diff --git a/src/Pug/PugSymfonyEngine.php b/src/Pug/PugSymfonyEngine.php index ea0a927..421e211 100644 --- a/src/Pug/PugSymfonyEngine.php +++ b/src/Pug/PugSymfonyEngine.php @@ -21,8 +21,10 @@ use Pug\Symfony\Traits\Options; use Symfony\Component\EventDispatcher\EventDispatcher; use Symfony\Component\Form\FormInterface; +use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\Response; use Symfony\Component\HttpKernel\KernelInterface; +use Symfony\Component\Routing\RequestContext; use Symfony\Component\Templating\EngineInterface; use Symfony\Component\Templating\TemplateReferenceInterface; use Twig\Environment as TwigEnvironment; @@ -50,11 +52,12 @@ class PugSymfonyEngine implements EngineInterface, InstallerInterface protected $defaultTemplateDirectory; public function __construct( - KernelInterface $kernel, + protected readonly KernelInterface $kernel, TwigEnvironment $twig, + private readonly ?RequestStack $stack = null, + private readonly ?RequestContext $context = null, ) { $container = $kernel->getContainer(); - $this->kernel = $kernel; $this->container = $container; $this->userOptions = ($this->container->hasParameter('pug') ? $this->container->getParameter('pug') : null) ?: []; $this->enhanceTwig($twig); diff --git a/src/Pug/Symfony/Traits/HelpersHandler.php b/src/Pug/Symfony/Traits/HelpersHandler.php index 2c8cf6a..e2e3764 100644 --- a/src/Pug/Symfony/Traits/HelpersHandler.php +++ b/src/Pug/Symfony/Traits/HelpersHandler.php @@ -21,8 +21,6 @@ use Symfony\Component\Filesystem\Filesystem; use Symfony\Component\HttpFoundation\RequestStack; use Symfony\Component\HttpFoundation\UrlHelper; -use Symfony\Component\HttpKernel\Kernel; -use Symfony\Component\HttpKernel\KernelInterface; use Symfony\Component\Routing\RequestContext; use Twig\Environment as TwigEnvironment; use Twig\Extension\ExtensionInterface; @@ -40,8 +38,6 @@ trait HelpersHandler protected Environment $twig; - protected Kernel|KernelInterface $kernel; - protected ?Pug $pug = null; protected array $userOptions = []; @@ -353,12 +349,13 @@ protected function copyTwigFunctions(): void protected function getHttpFoundationExtension(): HttpFoundationExtension { /* @var RequestStack $stack */ - $stack = $this->container->get('request_stack'); + $stack = $this->stack ?? $this->container->get('request_stack'); /* @var RequestContext $context */ - $context = $this->container->has('router.request_context') + $context = $this->context ?? ($this->container->has('router.request_context') ? $this->container->get('router.request_context') - : $this->container->get('router')->getContext(); + : $this->container->get('router')->getContext() + ); return new HttpFoundationExtension(new UrlHelper($stack, $context)); } diff --git a/src/Pug/Twig/Environment.php b/src/Pug/Twig/Environment.php index 0214fe3..90a78b3 100644 --- a/src/Pug/Twig/Environment.php +++ b/src/Pug/Twig/Environment.php @@ -59,7 +59,7 @@ public function getRuntime(string $class) try { return parent::getRuntime($class); } catch (RuntimeError $error) { - if (!$this->rootEnv) { + if (!($this->rootEnv ?? null)) { throw $error; } diff --git a/tests/Pug/AbstractController.php b/tests/Pug/AbstractController.php deleted file mode 100644 index f4ed673..0000000 --- a/tests/Pug/AbstractController.php +++ /dev/null @@ -1,7 +0,0 @@ -twig ??= new Environment(new FilesystemLoader( - __DIR__.'/../project-s5/templates/', - )); + if (!isset($this->twig)) { + $this->twig = new Environment(new FilesystemLoader( + __DIR__.'/../project-s5/templates/', + )); + $this->twig->addExtension(new CsrfExtension()); + $this->twig->addRuntimeLoader(new FactoryRuntimeLoader([ + CsrfRuntime::class => static fn () => new CsrfRuntime(new TestCsrfTokenManager()), + ])); + } + + return $this->twig; } protected function getPugSymfonyEngine(?KernelInterface $kernel = null): PugSymfonyEngine { $twig = $this->getTwigEnvironment(); - $this->pugSymfony ??= new PugSymfonyEngine($kernel ?? self::$kernel, $twig); + $this->pugSymfony ??= new PugSymfonyEngine( + $kernel ?? self::$kernel, + $twig, + new RequestStack(), + new RequestContext(), + ); $twig->setPugSymfonyEngine($this->pugSymfony); return $this->pugSymfony; @@ -100,19 +118,16 @@ public function setUp(): void $this->addFormRenderer(); } - protected function addFormRenderer(?ContainerInterface $container = null) + protected function addFormRenderer() { require_once __DIR__.'/TestCsrfTokenManager.php'; - /** @var Environment $twig */ - $twig = ($container ?? self::getContainer())->get('twig'); + $twig = $this->getTwigEnvironment(); $csrfManager = new TestCsrfTokenManager(); $formEngine = new TwigRendererEngine(['form_div_layout.html.twig'], $twig); $twig->addRuntimeLoader(new FactoryRuntimeLoader([ - FormRenderer::class => static function () use ($formEngine, $csrfManager) { - return new FormRenderer($formEngine, $csrfManager); - }, + FormRenderer::class => static fn () => new FormRenderer($formEngine, $csrfManager), ])); } } diff --git a/tests/Pug/PugSymfonyEngineTest.php b/tests/Pug/PugSymfonyEngineTest.php index aa8dcad..3ed99b9 100644 --- a/tests/Pug/PugSymfonyEngineTest.php +++ b/tests/Pug/PugSymfonyEngineTest.php @@ -5,23 +5,32 @@ use App\Service\PugInterceptor; use DateTime; use ErrorException; -use InvalidArgumentException; +use Phug\CompilerException; +use Phug\Util\SourceLocation; use Pug\Exceptions\ReservedVariable; use Pug\Filter\AbstractFilter; use Pug\Pug; use Pug\PugSymfonyEngine; use Pug\Symfony\MixedLoader; +use Pug\Symfony\Traits\HelpersHandler; use Pug\Symfony\Traits\PrivatePropertyAccessor; use Pug\Twig\Environment; use ReflectionException; use ReflectionProperty; use RuntimeException; use Symfony\Bridge\Twig\Extension\LogoutUrlExtension; -use Symfony\Bundle\FrameworkBundle\Controller\AbstractController; use Symfony\Component\DependencyInjection\Container; +use Symfony\Component\Form\Extension\Core\Type\DateType; +use Symfony\Component\Form\Extension\Core\Type\FormType; +use Symfony\Component\Form\Extension\Core\Type\SubmitType; +use Symfony\Component\Form\Extension\Core\Type\TextType; +use Symfony\Component\Form\FormFactory; +use Symfony\Component\Form\FormRegistry; +use Symfony\Component\Form\ResolvedFormTypeFactory; use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorage as BaseTokenStorage; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Http\Logout\LogoutUrlGenerator as BaseLogoutUrlGenerator; +use Symfony\Component\Translation\Translator; use Twig\Error\LoaderError; use Twig\Loader\ArrayLoader; use Twig\TwigFunction; @@ -83,27 +92,17 @@ public function setDueDate(DateTime $dueDate = null) } } -if (!class_exists('Symfony\Bundle\FrameworkBundle\Controller\AbstractController')) { - include __DIR__.'/AbstractController.php'; -} - -class TestController extends AbstractController +class TestController { public function index() { - try { - return $this->createFormBuilder(new Task()) - ->add('name', 'Symfony\Component\Form\Extension\Core\Type\TextType') - ->add('dueDate', 'Symfony\Component\Form\Extension\Core\Type\DateType') - ->add('save', 'Symfony\Component\Form\Extension\Core\Type\SubmitType', ['label' => 'Foo']) - ->getForm(); - } catch (InvalidArgumentException $e) { - return $this->createFormBuilder(new Task()) - ->add('name', 'text') - ->add('dueDate', 'date') - ->add('save', 'submit', ['label' => 'Foo']) - ->getForm(); - } + $factory = new FormFactory(new FormRegistry([], new ResolvedFormTypeFactory())); + + return $factory->createBuilder(FormType::class, new Task()) + ->add('name', TextType::class) + ->add('dueDate', DateType::class) + ->add('save', SubmitType::class, ['label' => 'Foo']) + ->getForm(); } } @@ -116,15 +115,16 @@ public function testRequireTwig() self::expectException(RuntimeException::class); self::expectExceptionMessage('Twig needs to be configured.'); - $container = self::$kernel->getContainer(); - foreach (['services', 'aliases', 'fileMap', 'methodMap'] as $name) { - /** @var ReflectionProperty $propertyAccessor */ - $services = static::getPrivateProperty($container, $name, $propertyAccessor); - unset($services['twig']); - $propertyAccessor->setValue($container, $services); - } + $object = new class () { + use HelpersHandler; - $this->getPugSymfonyEngine(); + public function wrongEnhance(): void + { + $this->enhanceTwig(new \stdClass()); + } + }; + + $object->wrongEnhance(); } /** @@ -139,11 +139,12 @@ public function testPreRenderPhp() }); $kernel->boot(); $pugSymfony = $this->getPugSymfonyEngine(); + $pugSymfony->setOption('prettyprint', false); - self::assertSame('

/foo

', $pugSymfony->renderString('p=asset("/foo")')); + self::assertSame('

/foo

', trim($pugSymfony->renderString('p=asset("/foo")'))); self::assertSame( 'My Site

/foo

', - $pugSymfony->render('asset.pug') + $pugSymfony->render('asset.pug'), ); } @@ -155,7 +156,7 @@ public function testMixin() self::assertSame( '', - $pugSymfony->render('mixin.pug') + $pugSymfony->render('mixin.pug'), ); } @@ -172,7 +173,7 @@ public function testPreRenderJs() $kernel->boot(); $pugSymfony = $this->getPugSymfonyEngine(); - self::assertSame('

/foo

', $pugSymfony->renderString('p=asset("/foo")')); + self::assertSame('

/foo

', trim($pugSymfony->renderString('p=asset("/foo")'))); } public function testPreRenderFile() @@ -208,7 +209,7 @@ public function testPreRenderCsrfToken() }); $kernel->boot(); $pugSymfony = $this->getPugSymfonyEngine($kernel); - $this->addFormRenderer($kernel->getContainer()); + $this->addFormRenderer(); self::assertSame('

Hello

', $pugSymfony->renderString('p Hello')); @@ -235,7 +236,7 @@ public function testSecurityToken() $services['security.token_storage'] = $tokenStorage; $reflectionProperty->setValue($container, $services); $pugSymfony = $this->getPugSymfonyEngine(); - $this->addFormRenderer($container); + $this->addFormRenderer(); self::assertSame('

the token

', trim($pugSymfony->render('token.pug'))); } @@ -247,8 +248,7 @@ public function testSecurityToken() public function testLogoutHelper() { $generator = new LogoutUrlGenerator(); - /* @var Environment $twig */ - $twig = self::$kernel->getContainer()->get('twig'); + $twig = $this->getTwigEnvironment(); foreach ($twig->getExtensions() as $extension) { if ($extension instanceof LogoutUrlExtension) { @@ -276,10 +276,8 @@ public function testLogoutHelper() public function testFormHelpers() { $pugSymfony = $this->getPugSymfonyEngine(); - $container = self::$kernel->getContainer(); - $this->addFormRenderer($container); + $this->addFormRenderer(); $controller = new TestController(); - $controller->setContainer($container); self::assertRegExp('/^'.implode('', [ '
', @@ -303,8 +301,8 @@ public function testRenderViaTwig() $container = self::$kernel->getContainer(); $controller = new TestController(); $controller->setContainer($container); - /** @var Environment $twig */ - $twig = $container->get('twig'); + $twig = $this->getTwigEnvironment(); + $this->getPugSymfonyEngine(); self::assertInstanceOf(Environment::class, $twig); self::assertInstanceOf(PugSymfonyEngine::class, $twig->getEngine()); @@ -325,9 +323,8 @@ public function testRenderViaTwig() */ public function testServicesSharing() { - /** @var Environment $twig */ - $twig = self::$kernel->getContainer()->get('twig'); - $twig->addGlobal('t', self::$kernel->getContainer()->get('translator')); + $twig = $this->getTwigEnvironment(); + $twig->addGlobal('t', new Translator('en_US')); $pugSymfony = $this->getPugSymfonyEngine(); self::assertSame('

Hello Bob

', trim($pugSymfony->renderString('p=t.trans("Hello %name%", {"%name%": "Bob"})'))); @@ -338,11 +335,9 @@ public function testServicesSharing() */ public function testTwigGlobals() { - $container = self::$kernel->getContainer(); - $pugSymfony = $this->getPugSymfonyEngine(); - /** @var Environment $twig */ - $twig = $container->get('twig'); + $twig = $this->getTwigEnvironment(); $twig->addGlobal('answer', 42); + $pugSymfony = $this->getPugSymfonyEngine(); self::assertSame('

42

', trim($pugSymfony->renderString('p=answer'))); } @@ -405,17 +400,20 @@ public function testAssetHelperJs() self::assertSame( '
'."\n". '
', preg_replace( '/<', - str_replace(['\'assets/', "\r"], ['\'/assets/', ''], trim($pugSymfony->render('style-js.pug'))) - ) + strtr(trim($pugSymfony->render('style-js.pug')), [ + "\r" => '', + ''' => "'", + ]), + ), ); } @@ -535,15 +533,16 @@ public function testCustomPaths() */ public function testMissingDir() { + self::expectExceptionObject(new CompilerException( + new SourceLocation('page.pug', 1,0), + 'Source file page.pug not found', + )); + $kernel = new TestKernel(); $kernel->boot(); $kernel->setProjectDirectory(__DIR__.'/../project'); - $pugSymfony = $this->getPugSymfonyEngine(); - self::assertSame( - '

Page

', - trim($pugSymfony->render('page.pug')) - ); + $this->getPugSymfonyEngine()->render('page.pug'); } public function testForbidThis() @@ -600,16 +599,14 @@ public function testCompileException() self::expectExceptionMessage('Unable to compile void function.'); $this->getPugSymfonyEngine(); - /** @var Environment $twig */ - $twig = self::$kernel->getContainer()->get('twig'); + $twig = $this->getTwigEnvironment(); $twig->compileCode(new TwigFunction('void'), '{# comment #}'); } public function testLoadTemplate() { $this->getPugSymfonyEngine(); - /** @var Environment $twig */ - $twig = self::$kernel->getContainer()->get('twig'); + $twig = $this->getTwigEnvironment(); try { $twig->loadTemplate('a', 'b', 1); @@ -652,8 +649,6 @@ public function testRenderInterceptor() $value = $property->getValue($container); $value['pug']['interceptors'] = [PugInterceptor::class]; $property->setValue($container, $value); - $controller = new TestController(); - $controller->setContainer($container); $twig = $this->getTwigEnvironment(); $pugSymfony = $this->getPugSymfonyEngine();