Skip to content

Commit

Permalink
Merge pull request #33 from netgen-layouts/twig39
Browse files Browse the repository at this point in the history
Support yield rendering strategy in Twig 3.9+
  • Loading branch information
emodric authored Sep 6, 2024
2 parents 0c9f786 + 4098776 commit e22c56d
Show file tree
Hide file tree
Showing 15 changed files with 133 additions and 30 deletions.
6 changes: 4 additions & 2 deletions bundles/LayoutsBundle/Templating/Twig/Node/DefaultContext.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,17 @@

namespace Netgen\Bundle\LayoutsBundle\Templating\Twig\Node;

use Twig\Attribute\YieldReady;
use Twig\Compiler;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Node;

#[YieldReady]
final class DefaultContext extends Node
{
public function __construct(AbstractExpression $expr, int $line = 0, ?string $tag = null)
public function __construct(AbstractExpression $expr, int $line = 0)
{
parent::__construct(['expr' => $expr], [], $line, $tag);
parent::__construct(['expr' => $expr], [], $line);
}

public function compile(Compiler $compiler): void
Expand Down
21 changes: 17 additions & 4 deletions bundles/LayoutsBundle/Templating/Twig/Node/RenderZone.php
Original file line number Diff line number Diff line change
Expand Up @@ -8,22 +8,25 @@
use Netgen\Layouts\API\Values\Layout\Zone;
use Netgen\Layouts\View\Twig\ContextualizedTwigTemplate;
use Netgen\Layouts\View\ViewInterface;
use Twig\Attribute\YieldReady;
use Twig\Compiler;
use Twig\Environment;
use Twig\Node\Expression\AbstractExpression;
use Twig\Node\Node;

use const PHP_EOL;

#[YieldReady]
final class RenderZone extends Node
{
public function __construct(AbstractExpression $zone, ?AbstractExpression $context = null, int $line = 0, ?string $tag = null)
public function __construct(AbstractExpression $zone, ?AbstractExpression $context = null, int $line = 0)
{
$nodes = ['zone' => $zone];
if ($context instanceof AbstractExpression) {
$nodes['context'] = $context;
}

parent::__construct($nodes, [], $line, $tag);
parent::__construct($nodes, [], $line);
}

public function compile(Compiler $compiler): void
Expand All @@ -36,9 +39,8 @@ public function compile(Compiler $compiler): void
->write('$nglZoneIdentifier = $nglZone instanceof ' . Zone::class . ' ? $nglZone->getIdentifier() : $nglZone;' . PHP_EOL);

$this->compileContextNode($compiler);

$compiler->write('$nglTemplate = new ' . ContextualizedTwigTemplate::class . '($this, $context, $blocks);' . PHP_EOL);
$compiler->write('$this->env->getRuntime("' . RenderingRuntime::class . '")->displayZone($context["nglayouts"]->getLayout(), $nglZoneIdentifier, $nglContext, $nglTemplate);' . PHP_EOL);
$this->compileRenderNode($compiler);
}

/**
Expand All @@ -62,4 +64,15 @@ private function compileContextNode(Compiler $compiler): void

$compiler->write('$nglContext = ' . ViewInterface::class . '::CONTEXT_DEFAULT;' . PHP_EOL);
}

private function compileRenderNode(Compiler $compiler): void
{
if (Environment::VERSION_ID >= 30900) {
$compiler->write('yield $this->env->getRuntime("' . RenderingRuntime::class . '")->renderZone($context["nglayouts"]->getLayout(), $nglZoneIdentifier, $nglContext, $nglTemplate);' . PHP_EOL);

return;
}

$compiler->write('echo $this->env->getRuntime("' . RenderingRuntime::class . '")->renderZone($context["nglayouts"]->getLayout(), $nglZoneIdentifier, $nglContext, $nglTemplate);' . PHP_EOL);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,11 @@
use Ramsey\Uuid\Uuid;
use Throwable;
use Twig\Environment;
use Twig\Extension\CoreExtension;

use function array_unshift;
use function is_string;
use function method_exists;
use function twig_date_converter;

final class HelpersRuntime
Expand Down Expand Up @@ -121,7 +123,11 @@ public function getCountryFlag(string $countryCode): string
*/
public function formatDateTime(Environment $twig, $dateTime, string $dateFormat = 'medium', string $timeFormat = 'medium'): string
{
$dateTime = twig_date_converter($twig, $dateTime);
$coreExtension = $twig->getExtension(CoreExtension::class);

$dateTime = method_exists($coreExtension, 'convertDate') ?
$coreExtension->convertDate($dateTime) :
twig_date_converter($twig, $dateTime);

$formatValues = [
'none' => IntlDateFormatter::NONE,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,9 +159,9 @@ public function renderValue(array $context, $value, array $parameters = [], ?str
}

/**
* Displays the provided zone.
* Renders the provided zone.
*/
public function displayZone(Layout $layout, string $zoneIdentifier, string $viewContext, ContextualizedTwigTemplate $twigTemplate): void
public function renderZone(Layout $layout, string $zoneIdentifier, string $viewContext, ContextualizedTwigTemplate $twigTemplate): string
{
$locales = null;

Expand All @@ -178,7 +178,7 @@ public function displayZone(Layout $layout, string $zoneIdentifier, string $view
$locales,
);

echo $this->renderValue(
return $this->renderValue(
[],
new ZoneReference($layout, $zoneIdentifier),
[
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public function parse(Token $token): Node

$this->parser->getStream()->expect(Token::BLOCK_END_TYPE);

return new DefaultContextNode($expression, $token->getLine(), $this->getTag());
return new DefaultContextNode($expression, $token->getLine());
}

public function getTag(): string
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public function parse(Token $token): Node

$stream->expect(Token::BLOCK_END_TYPE);

return new RenderZoneNode($zone, $context, $token->getLine(), $this->getTag());
return new RenderZoneNode($zone, $context, $token->getLine());
}

public function getTag(): string
Expand Down
2 changes: 1 addition & 1 deletion composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@
"symfony/twig-bundle": "^3.4.47 || ^5.4 || ^6.2",
"symfony/validator": "^3.4.47 || ^5.4 || ^6.2",
"symfony/yaml": "^3.4.47 || ^5.4 || ^6.2",
"twig/twig": "^2.15 || ^3.4",
"twig/twig": "^2.15 || ^3.9",
"sensio/framework-extra-bundle": "^5.4 || ^6.2",
"doctrine/dbal": "^2.13 || ^3.5",
"doctrine/doctrine-bundle": "^1.12 || ^2.7",
Expand Down
5 changes: 1 addition & 4 deletions phpstan.neon
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ parameters:
treatPhpDocTypesAsCertain: false
dynamicConstantNames:
- Symfony\Component\HttpKernel\Kernel::VERSION_ID
- Twig\Environment::VERSION_ID

ignoreErrors:
# Doctrine DBAL
Expand Down Expand Up @@ -49,10 +50,6 @@ parameters:
- "#Call to function method_exists\\(\\) with Symfony\\\\Component\\\\HttpFoundation\\\\Request and 'getContentTypeFormat' will always evaluate to true.#"
- "#Call to function method_exists\\(\\) with Symfony\\\\Component\\\\DependencyInjection\\\\ContainerBuilder and 'registerAttributeFo…' will always evaluate to true.#"

-
message: '#twig_date_converter not found.#'
path: bundles/LayoutsBundle/Templating/Twig/Runtime/HelpersRuntime.php

# Netgen Layouts specifics

- "#Call to function method_exists\\(\\) with Netgen\\\\Layouts\\\\Layout\\\\Resolver\\\\(Condition|Target)TypeInterface and '(export|import)' will always evaluate to true.#"
Expand Down
3 changes: 3 additions & 0 deletions phpstan.tests.neon
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ parameters:
treatPhpDocTypesAsCertain: false
dynamicConstantNames:
- Symfony\Component\HttpKernel\Kernel::VERSION_ID
- Twig\Environment::MAJOR_VERSION

excludePaths:
- tests/application/public/bundles/
Expand Down Expand Up @@ -53,6 +54,8 @@ parameters:
message: '#Undefined variable: \$this#'
path: tests/lib/Transfer/Output/Visitor/Integration

- "#Call to function method_exists\\(\\) with .* and 'setNodeTag' will always evaluate to true.#"

-
message: "#Offset 'db' does not exist on array#"
path: tests/lib/Persistence/Doctrine/DatabaseTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ protected function setUp(): void
}

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::displayZone
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::renderZone
*
* @dataProvider getTests
*
Expand All @@ -74,7 +74,7 @@ public function testIntegration($file, $message, $condition, $templates, $except
}

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::displayZone
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::renderZone
*
* @dataProvider getTests
*
Expand All @@ -97,7 +97,7 @@ public function testIntegrationWithLocale($file, $message, $condition, $template
}

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::displayZone
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::renderZone
*
* @dataProvider getLegacyTests
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
namespace Netgen\Bundle\LayoutsBundle\Tests\Templating\Twig\Node;

use Netgen\Bundle\LayoutsBundle\Templating\Twig\Node\DefaultContext;
use Twig\Environment;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\NameExpression;

Expand All @@ -13,6 +14,13 @@
*/
final class DefaultContextTest extends NodeTestBase
{
protected function setUp(): void
{
if (Environment::MAJOR_VERSION === 2) {
self::markTestSkipped('Test requires twig/twig 3.9 to run');
}
}

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Node\DefaultContext::__construct
*/
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,24 @@
use Netgen\Layouts\API\Values\Layout\Zone;
use Netgen\Layouts\View\Twig\ContextualizedTwigTemplate;
use Netgen\Layouts\View\ViewInterface;
use Twig\Environment;
use Twig\Node\Expression\ConstantExpression;
use Twig\Node\Expression\NameExpression;

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Node\RenderZone::compile
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Node\RenderZone::compileContextNode
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Node\RenderZone::compileRenderNode
*/
final class RenderZoneTest extends NodeTestBase
{
protected function setUp(): void
{
if (Environment::MAJOR_VERSION === 2) {
self::markTestSkipped('Test requires twig/twig 3.9 to run');
}
}

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Node\RenderZone::__construct
*/
Expand Down Expand Up @@ -69,7 +78,7 @@ public static function getTests(): array
\$nglZoneIdentifier = \$nglZone instanceof {$zoneClass} ? \$nglZone->getIdentifier() : \$nglZone;
\$nglContext = {$contextNodeGetter};
\$nglTemplate = new {$templateClass}(\$this, \$context, \$blocks);
\$this->env->getRuntime("{$runtimeClass}")->displayZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
yield \$this->env->getRuntime("{$runtimeClass}")->renderZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
EOT,
$environment,
],
Expand All @@ -81,7 +90,7 @@ public static function getTests(): array
\$nglZoneIdentifier = \$nglZone instanceof {$zoneClass} ? \$nglZone->getIdentifier() : \$nglZone;
\$nglContext = {$contextNodeGetter};
\$nglTemplate = new {$templateClass}(\$this, \$context, \$blocks);
\$this->env->getRuntime("{$runtimeClass}")->displayZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
yield \$this->env->getRuntime("{$runtimeClass}")->renderZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
EOT,
$environment,
],
Expand All @@ -93,7 +102,7 @@ public static function getTests(): array
\$nglZoneIdentifier = \$nglZone instanceof {$zoneClass} ? \$nglZone->getIdentifier() : \$nglZone;
\$nglContext = {$viewInterface}::CONTEXT_DEFAULT;
\$nglTemplate = new {$templateClass}(\$this, \$context, \$blocks);
\$this->env->getRuntime("{$runtimeClass}")->displayZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
yield \$this->env->getRuntime("{$runtimeClass}")->renderZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
EOT,
$environment,
],
Expand All @@ -105,7 +114,7 @@ public static function getTests(): array
\$nglZoneIdentifier = \$nglZone instanceof {$zoneClass} ? \$nglZone->getIdentifier() : \$nglZone;
\$nglContext = {$viewInterface}::CONTEXT_DEFAULT;
\$nglTemplate = new {$templateClass}(\$this, \$context, \$blocks);
\$this->env->getRuntime("{$runtimeClass}")->displayZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
yield \$this->env->getRuntime("{$runtimeClass}")->renderZone(\$context["nglayouts"]->getLayout(), \$nglZoneIdentifier, \$nglContext, \$nglTemplate);
EOT,
$environment,
],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,17 @@

namespace Netgen\Bundle\LayoutsBundle\Tests\Templating\Twig\Runtime;

use Doctrine\Common\Collections\ArrayCollection;
use Exception;
use Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime;
use Netgen\Layouts\API\Service\BlockService;
use Netgen\Layouts\API\Values\Block\Block;
use Netgen\Layouts\API\Values\Block\BlockList;
use Netgen\Layouts\API\Values\Block\Placeholder;
use Netgen\Layouts\API\Values\Collection\Item;
use Netgen\Layouts\API\Values\Collection\Slot;
use Netgen\Layouts\API\Values\Layout\Layout;
use Netgen\Layouts\API\Values\Layout\Zone;
use Netgen\Layouts\API\Values\LayoutResolver\RuleCondition;
use Netgen\Layouts\Block\BlockDefinition;
use Netgen\Layouts\Collection\Result\ManualItem;
Expand All @@ -21,6 +25,7 @@
use Netgen\Layouts\Tests\Stubs\ErrorHandler;
use Netgen\Layouts\View\RendererInterface;
use Netgen\Layouts\View\Twig\ContextualizedTwigTemplate;
use Netgen\Layouts\View\View\ZoneView\ZoneReference;
use Netgen\Layouts\View\ViewInterface;
use PHPUnit\Framework\MockObject\MockObject;
use PHPUnit\Framework\TestCase;
Expand Down Expand Up @@ -60,6 +65,54 @@ protected function setUp(): void
);
}

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::__construct
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::getViewContext
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::renderZone
*/
public function testRenderZone(): void
{
$zone = Zone::fromArray([]);
$blocks = new BlockList();
$layout = Layout::fromArray(['zones' => new ArrayCollection(['zone' => $zone])]);

$twigTemplate = new ContextualizedTwigTemplate($this->createMock(Template::class));

$this->blockServiceMock
->expects(self::once())
->method('loadZoneBlocks')
->with(
self::identicalTo($zone),
self::isNull(),
)
->willReturn($blocks);

$this->rendererMock
->expects(self::once())
->method('renderValue')
->with(
self::isInstanceOf(ZoneReference::class),
self::identicalTo(ViewInterface::CONTEXT_DEFAULT),
self::identicalTo(
[
'blocks' => $blocks,
'twig_template' => $twigTemplate,
],
),
)
->willReturn('rendered zone');

self::assertSame(
'rendered zone',
$this->runtime->renderZone(
$layout,
'zone',
ViewInterface::CONTEXT_DEFAULT,
$twigTemplate,
),
);
}

/**
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::__construct
* @covers \Netgen\Bundle\LayoutsBundle\Templating\Twig\Runtime\RenderingRuntime::getViewContext
Expand Down
Loading

0 comments on commit e22c56d

Please sign in to comment.