maj
This commit is contained in:
43
system/src/Twig/DeferredExtension/DeferredBlockNode.php
Normal file
43
system/src/Twig/DeferredExtension/DeferredBlockNode.php
Normal file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the rybakit/twig-deferred-extension package.
|
||||
*
|
||||
* (c) Eugene Leonovich <gen.work@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Twig\DeferredExtension;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\BlockNode;
|
||||
|
||||
final class DeferredBlockNode extends BlockNode
|
||||
{
|
||||
public function compile(Compiler $compiler) : void
|
||||
{
|
||||
$name = $this->getAttribute('name');
|
||||
|
||||
$compiler
|
||||
->write("public function block_$name(\$context, array \$blocks = [])\n", "{\n")
|
||||
->indent()
|
||||
->write("\$this->deferred->defer(\$this, '$name');\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("public function block_{$name}_deferred(\$context, array \$blocks = [])\n", "{\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("\$this->deferred->resolve(\$this, \$context, \$blocks);\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
72
system/src/Twig/DeferredExtension/DeferredExtension.php
Normal file
72
system/src/Twig/DeferredExtension/DeferredExtension.php
Normal file
@@ -0,0 +1,72 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the rybakit/twig-deferred-extension package.
|
||||
*
|
||||
* (c) Eugene Leonovich <gen.work@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Twig\DeferredExtension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\AbstractExtension;
|
||||
use Twig\Template;
|
||||
|
||||
final class DeferredExtension extends AbstractExtension
|
||||
{
|
||||
private $blocks = [];
|
||||
|
||||
public function getTokenParsers() : array
|
||||
{
|
||||
return [new DeferredTokenParser()];
|
||||
}
|
||||
|
||||
public function getNodeVisitors() : array
|
||||
{
|
||||
if (Environment::VERSION_ID < 20000) {
|
||||
// Twig 1.x support
|
||||
return [new DeferredNodeVisitorCompat()];
|
||||
}
|
||||
|
||||
return [new DeferredNodeVisitor()];
|
||||
}
|
||||
|
||||
public function defer(Template $template, string $blockName) : void
|
||||
{
|
||||
$templateName = $template->getTemplateName();
|
||||
$this->blocks[$templateName][] = $blockName;
|
||||
$index = \count($this->blocks[$templateName]) - 1;
|
||||
|
||||
\ob_start(function (string $buffer) use ($index, $templateName) {
|
||||
unset($this->blocks[$templateName][$index]);
|
||||
|
||||
return $buffer;
|
||||
});
|
||||
}
|
||||
|
||||
public function resolve(Template $template, array $context, array $blocks) : void
|
||||
{
|
||||
$templateName = $template->getTemplateName();
|
||||
if (empty($this->blocks[$templateName])) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ($blockName = \array_pop($this->blocks[$templateName])) {
|
||||
$buffer = \ob_get_clean();
|
||||
|
||||
$blocks[$blockName] = [$template, 'block_'.$blockName.'_deferred'];
|
||||
$template->displayBlock($blockName, $context, $blocks);
|
||||
|
||||
echo $buffer;
|
||||
}
|
||||
|
||||
if ($parent = $template->getParent($context)) {
|
||||
$this->resolve($parent, $context, $blocks);
|
||||
}
|
||||
}
|
||||
}
|
||||
27
system/src/Twig/DeferredExtension/DeferredExtensionNode.php
Normal file
27
system/src/Twig/DeferredExtension/DeferredExtensionNode.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the rybakit/twig-deferred-extension package.
|
||||
*
|
||||
* (c) Eugene Leonovich <gen.work@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Twig\DeferredExtension;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Node;
|
||||
|
||||
final class DeferredExtensionNode extends Node
|
||||
{
|
||||
public function compile(Compiler $compiler) : void
|
||||
{
|
||||
$compiler
|
||||
->write("\$this->deferred = \$this->env->getExtension('".DeferredExtension::class."');\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
27
system/src/Twig/DeferredExtension/DeferredNode.php
Normal file
27
system/src/Twig/DeferredExtension/DeferredNode.php
Normal file
@@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the rybakit/twig-deferred-extension package.
|
||||
*
|
||||
* (c) Eugene Leonovich <gen.work@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Twig\DeferredExtension;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Node;
|
||||
|
||||
final class DeferredNode extends Node
|
||||
{
|
||||
public function compile(Compiler $compiler) : void
|
||||
{
|
||||
$compiler
|
||||
->write("\$this->deferred->resolve(\$this, \$context, \$blocks);\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
49
system/src/Twig/DeferredExtension/DeferredNodeVisitor.php
Normal file
49
system/src/Twig/DeferredExtension/DeferredNodeVisitor.php
Normal file
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the rybakit/twig-deferred-extension package.
|
||||
*
|
||||
* (c) Eugene Leonovich <gen.work@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Twig\DeferredExtension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
|
||||
final class DeferredNodeVisitor implements NodeVisitorInterface
|
||||
{
|
||||
private $hasDeferred = false;
|
||||
|
||||
public function enterNode(Node $node, Environment $env) : Node
|
||||
{
|
||||
if (!$this->hasDeferred && $node instanceof DeferredBlockNode) {
|
||||
$this->hasDeferred = true;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node, Environment $env) : ?Node
|
||||
{
|
||||
if ($this->hasDeferred && $node instanceof ModuleNode) {
|
||||
$node->setNode('constructor_end', new Node([new DeferredExtensionNode(), $node->getNode('constructor_end')]));
|
||||
$node->setNode('display_end', new Node([new DeferredNode(), $node->getNode('display_end')]));
|
||||
$this->hasDeferred = false;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getPriority() : int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the rybakit/twig-deferred-extension package.
|
||||
*
|
||||
* (c) Eugene Leonovich <gen.work@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Twig\DeferredExtension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
|
||||
final class DeferredNodeVisitorCompat implements NodeVisitorInterface
|
||||
{
|
||||
private $hasDeferred = false;
|
||||
|
||||
public function enterNode(\Twig_NodeInterface $node, Environment $env) : Node
|
||||
{
|
||||
if (!$this->hasDeferred && $node instanceof DeferredBlockNode) {
|
||||
$this->hasDeferred = true;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function leaveNode(\Twig_NodeInterface $node, Environment $env) : ?Node
|
||||
{
|
||||
if ($this->hasDeferred && $node instanceof ModuleNode) {
|
||||
$node->setNode('constructor_end', new Node([new DeferredExtensionNode(), $node->getNode('constructor_end')]));
|
||||
$node->setNode('display_end', new Node([new DeferredNode(), $node->getNode('display_end')]));
|
||||
$this->hasDeferred = false;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getPriority() : int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
77
system/src/Twig/DeferredExtension/DeferredTokenParser.php
Normal file
77
system/src/Twig/DeferredExtension/DeferredTokenParser.php
Normal file
@@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/**
|
||||
* This file is part of the rybakit/twig-deferred-extension package.
|
||||
*
|
||||
* (c) Eugene Leonovich <gen.work@gmail.com>
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace Twig\DeferredExtension;
|
||||
|
||||
use Twig\Node\BlockNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
use Twig\TokenParser\AbstractTokenParser;
|
||||
use Twig\TokenParser\BlockTokenParser;
|
||||
|
||||
final class DeferredTokenParser extends AbstractTokenParser
|
||||
{
|
||||
private $blockTokenParser;
|
||||
|
||||
public function setParser(Parser $parser) : void
|
||||
{
|
||||
parent::setParser($parser);
|
||||
|
||||
$this->blockTokenParser = new BlockTokenParser();
|
||||
$this->blockTokenParser->setParser($parser);
|
||||
}
|
||||
|
||||
public function parse(Token $token) : Node
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$nameToken = $stream->next();
|
||||
$deferredToken = $stream->nextIf(Token::NAME_TYPE, 'deferred');
|
||||
$stream->injectTokens([$nameToken]);
|
||||
|
||||
$node = $this->blockTokenParser->parse($token);
|
||||
|
||||
if ($deferredToken) {
|
||||
$this->replaceBlockNode($nameToken->getValue());
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getTag() : string
|
||||
{
|
||||
return 'block';
|
||||
}
|
||||
|
||||
private function replaceBlockNode(string $name) : void
|
||||
{
|
||||
$block = $this->parser->getBlock($name)->getNode('0');
|
||||
$this->parser->setBlock($name, $this->createDeferredBlockNode($block));
|
||||
}
|
||||
|
||||
private function createDeferredBlockNode(BlockNode $block) : DeferredBlockNode
|
||||
{
|
||||
$name = $block->getAttribute('name');
|
||||
$deferredBlock = new DeferredBlockNode($name, new Node([]), $block->getTemplateLine());
|
||||
|
||||
foreach ($block as $nodeName => $node) {
|
||||
$deferredBlock->setNode($nodeName, $node);
|
||||
}
|
||||
|
||||
if ($sourceContext = $block->getSourceContext()) {
|
||||
$deferredBlock->setSourceContext($sourceContext);
|
||||
}
|
||||
|
||||
return $deferredBlock;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user