Update to customized version of Twig DeferredExtension, improve Twig 2 compatibility

This commit is contained in:
Matias Griese
2021-09-17 15:39:16 +03:00
parent 76d881bac1
commit 243053659c
12 changed files with 350 additions and 187 deletions

View File

@@ -3,7 +3,10 @@
1. [](#new)
* Register plugin autoloaders into plugin objects
1. [](#bugfix)
2. [](#improved)
* Improve Twig 2 compatibility
* Update to customized version of Twig DeferredExtension (Twig 1/2 compatible)
3. [](#bugfix)
* Fixed conflicting `$_original` variable in `Flex Pages`
# v1.7.21

View File

@@ -55,7 +55,6 @@
"miljar/php-exif": "^0.6",
"composer/ca-bundle": "^1.2",
"dragonmantank/cron-expression": "^1.2",
"phive/twig-extensions-deferred": "^1.0",
"willdurand/negotiation": "^3.0",
"itsgoingd/clockwork": "^5.0",
"enshrined/svg-sanitize": "~0.13",
@@ -93,7 +92,8 @@
},
"autoload": {
"psr-4": {
"Grav\\": "system/src/Grav"
"Grav\\": "system/src/Grav",
"Twig\\": "system/src/Twig"
},
"files": [
"system/defines.php"

126
composer.lock generated
View File

@@ -4,7 +4,7 @@
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
"content-hash": "36375d8a5daf3aef0341f9a2b023f4e9",
"content-hash": "a777a09c8efd3109a03df1d9c9c20b87",
"packages": [
{
"name": "antoligy/dom-string-iterators",
@@ -380,20 +380,20 @@
},
{
"name": "donatj/phpuseragentparser",
"version": "v1.4.0",
"version": "v1.5.0",
"source": {
"type": "git",
"url": "https://github.com/donatj/PhpUserAgent.git",
"reference": "246c1cf0a44f07168c702203bf30d5f48f17bab0"
"reference": "cc9d872cddfc180c52d084d0dff1e4aad653d37f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/246c1cf0a44f07168c702203bf30d5f48f17bab0",
"reference": "246c1cf0a44f07168c702203bf30d5f48f17bab0",
"url": "https://api.github.com/repos/donatj/PhpUserAgent/zipball/cc9d872cddfc180c52d084d0dff1e4aad653d37f",
"reference": "cc9d872cddfc180c52d084d0dff1e4aad653d37f",
"shasum": ""
},
"require": {
"php": ">=5.3.0"
"php": ">=5.4.0"
},
"require-dev": {
"camspiers/json-pretty": "~1.0",
@@ -432,7 +432,7 @@
],
"support": {
"issues": "https://github.com/donatj/PhpUserAgent/issues",
"source": "https://github.com/donatj/PhpUserAgent/tree/v1.4.0"
"source": "https://github.com/donatj/PhpUserAgent/tree/v1.5.0"
},
"funding": [
{
@@ -444,7 +444,7 @@
"type": "github"
}
],
"time": "2021-03-16T16:25:14+00:00"
"time": "2021-09-16T17:05:03+00:00"
},
{
"name": "dragonmantank/cron-expression",
@@ -1516,59 +1516,6 @@
],
"time": "2021-05-12T11:11:27+00:00"
},
{
"name": "phive/twig-extensions-deferred",
"version": "v1.0.2",
"source": {
"type": "git",
"url": "https://github.com/rybakit/twig-extensions-deferred-legacy.git",
"reference": "5a2426d622afa74034e754ca5ea1d1ff7887627f"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/rybakit/twig-extensions-deferred-legacy/zipball/5a2426d622afa74034e754ca5ea1d1ff7887627f",
"reference": "5a2426d622afa74034e754ca5ea1d1ff7887627f",
"shasum": ""
},
"require": {
"twig/twig": "~1.18"
},
"type": "library",
"autoload": {
"psr-4": {
"Phive\\Twig\\Extensions\\Deferred\\": "src/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"MIT"
],
"authors": [
{
"name": "Eugene Leonovich",
"email": "gen.work@gmail.com"
}
],
"description": "An extension for Twig that allows to defer block rendering",
"homepage": "https://github.com/rybakit/twig-extensions-deferred",
"keywords": [
"defer",
"extension",
"lazy",
"twig"
],
"support": {
"issues": "https://github.com/rybakit/twig-extensions-deferred-legacy/issues",
"source": "https://github.com/rybakit/twig-extensions-deferred-legacy/tree/v1.0.2"
},
"funding": [
{
"url": "https://github.com/rybakit",
"type": "github"
}
],
"time": "2017-03-17T21:39:21+00:00"
},
{
"name": "php-http/message-factory",
"version": "v1.0.2",
@@ -3231,16 +3178,16 @@
},
{
"name": "twig/twig",
"version": "v1.44.4",
"version": "v1.44.5",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "4d400421528e9fa40caaffcf7824c172526dd99d"
"reference": "dd4353357c5a116322e92a00d16043a31881a81e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/4d400421528e9fa40caaffcf7824c172526dd99d",
"reference": "4d400421528e9fa40caaffcf7824c172526dd99d",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/dd4353357c5a116322e92a00d16043a31881a81e",
"reference": "dd4353357c5a116322e92a00d16043a31881a81e",
"shasum": ""
},
"require": {
@@ -3293,7 +3240,7 @@
],
"support": {
"issues": "https://github.com/twigphp/Twig/issues",
"source": "https://github.com/twigphp/Twig/tree/v1.44.4"
"source": "https://github.com/twigphp/Twig/tree/v1.44.5"
},
"funding": [
{
@@ -3305,7 +3252,7 @@
"type": "tidelift"
}
],
"time": "2021-05-16T12:11:20+00:00"
"time": "2021-09-17T08:35:19+00:00"
},
{
"name": "willdurand/negotiation",
@@ -4502,33 +4449,33 @@
},
{
"name": "phpspec/prophecy",
"version": "1.13.0",
"version": "1.14.0",
"source": {
"type": "git",
"url": "https://github.com/phpspec/prophecy.git",
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea"
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/be1996ed8adc35c3fd795488a653f4b518be70ea",
"reference": "be1996ed8adc35c3fd795488a653f4b518be70ea",
"url": "https://api.github.com/repos/phpspec/prophecy/zipball/d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
"reference": "d86dfc2e2a3cd366cee475e52c6bb3bbc371aa0e",
"shasum": ""
},
"require": {
"doctrine/instantiator": "^1.2",
"php": "^7.2 || ~8.0, <8.1",
"php": "^7.2 || ~8.0, <8.2",
"phpdocumentor/reflection-docblock": "^5.2",
"sebastian/comparator": "^3.0 || ^4.0",
"sebastian/recursion-context": "^3.0 || ^4.0"
},
"require-dev": {
"phpspec/phpspec": "^6.0",
"phpspec/phpspec": "^6.0 || ^7.0",
"phpunit/phpunit": "^8.0 || ^9.0"
},
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.11.x-dev"
"dev-master": "1.x-dev"
}
},
"autoload": {
@@ -4563,22 +4510,22 @@
],
"support": {
"issues": "https://github.com/phpspec/prophecy/issues",
"source": "https://github.com/phpspec/prophecy/tree/1.13.0"
"source": "https://github.com/phpspec/prophecy/tree/1.14.0"
},
"time": "2021-03-17T13:42:18+00:00"
"time": "2021-09-10T09:02:12+00:00"
},
{
"name": "phpstan/phpstan",
"version": "0.12.98",
"version": "0.12.99",
"source": {
"type": "git",
"url": "https://github.com/phpstan/phpstan.git",
"reference": "3bb7cc246c057405dd5e290c3ecc62ab51d57e00"
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/3bb7cc246c057405dd5e290c3ecc62ab51d57e00",
"reference": "3bb7cc246c057405dd5e290c3ecc62ab51d57e00",
"url": "https://api.github.com/repos/phpstan/phpstan/zipball/b4d40f1d759942f523be267a1bab6884f46ca3f7",
"reference": "b4d40f1d759942f523be267a1bab6884f46ca3f7",
"shasum": ""
},
"require": {
@@ -4609,7 +4556,7 @@
"description": "PHPStan - PHP Static Analysis Tool",
"support": {
"issues": "https://github.com/phpstan/phpstan/issues",
"source": "https://github.com/phpstan/phpstan/tree/0.12.98"
"source": "https://github.com/phpstan/phpstan/tree/0.12.99"
},
"funding": [
{
@@ -4629,7 +4576,7 @@
"type": "tidelift"
}
],
"time": "2021-09-02T12:33:01+00:00"
"time": "2021-09-12T20:09:55+00:00"
},
{
"name": "phpstan/phpstan-deprecation-rules",
@@ -4684,23 +4631,23 @@
},
{
"name": "phpunit/php-code-coverage",
"version": "9.2.6",
"version": "9.2.7",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/php-code-coverage.git",
"reference": "f6293e1b30a2354e8428e004689671b83871edde"
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/f6293e1b30a2354e8428e004689671b83871edde",
"reference": "f6293e1b30a2354e8428e004689671b83871edde",
"url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/d4c798ed8d51506800b441f7a13ecb0f76f12218",
"reference": "d4c798ed8d51506800b441f7a13ecb0f76f12218",
"shasum": ""
},
"require": {
"ext-dom": "*",
"ext-libxml": "*",
"ext-xmlwriter": "*",
"nikic/php-parser": "^4.10.2",
"nikic/php-parser": "^4.12.0",
"php": ">=7.3",
"phpunit/php-file-iterator": "^3.0.3",
"phpunit/php-text-template": "^2.0.2",
@@ -4749,7 +4696,7 @@
],
"support": {
"issues": "https://github.com/sebastianbergmann/php-code-coverage/issues",
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.6"
"source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.7"
},
"funding": [
{
@@ -4757,7 +4704,7 @@
"type": "github"
}
],
"time": "2021-03-28T07:26:59+00:00"
"time": "2021-09-17T05:39:03+00:00"
},
{
"name": "phpunit/php-file-iterator",
@@ -6008,7 +5955,6 @@
"type": "github"
}
],
"abandoned": true,
"time": "2020-09-28T06:45:17+00:00"
},
{

View File

@@ -90,7 +90,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
*
* @return array
*/
public function getGlobals()
public function getGlobals(): array
{
return [
'grav' => $this->grav,
@@ -102,7 +102,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
*
* @return array
*/
public function getFilters()
public function getFilters(): array
{
return [
new TwigFilter('*ize', [$this, 'inflectorFilter']),
@@ -172,7 +172,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
*
* @return array
*/
public function getFunctions()
public function getFunctions(): array
{
return [
new TwigFunction('array', [$this, 'arrayFilter']),
@@ -243,7 +243,7 @@ class GravExtension extends AbstractExtension implements GlobalsInterface
/**
* @return array
*/
public function getTokenParsers()
public function getTokenParsers(): array
{
return [
new TwigTokenParserRender(),

View File

@@ -22,9 +22,9 @@ use Grav\Common\Twig\Extension\GravExtension;
use Grav\Common\Utils;
use RocketTheme\Toolbox\ResourceLocator\UniformResourceLocator;
use RocketTheme\Toolbox\Event\Event;
use Phive\Twig\Extensions\Deferred\DeferredExtension;
use RuntimeException;
use Twig\Cache\FilesystemCache;
use Twig\DeferredExtension\DeferredExtension;
use Twig\Environment;
use Twig\Error\LoaderError;
use Twig\Error\RuntimeError;
@@ -33,7 +33,6 @@ use Twig\Extension\DebugExtension;
use Twig\Extension\StringLoaderExtension;
use Twig\Loader\ArrayLoader;
use Twig\Loader\ChainLoader;
use Twig\Loader\ExistsLoaderInterface;
use Twig\Loader\FilesystemLoader;
use Twig\Profiler\Profile;
use Twig\TwigFilter;
@@ -515,25 +514,21 @@ class Twig
$twig_extension = $extension ? '.'. $extension .TWIG_EXT : TEMPLATE_EXT;
$template_file = $this->template($page->template() . $twig_extension);
$page_template = null;
$loader = $this->twig->getLoader();
if ($loader instanceof ExistsLoaderInterface) {
if ($loader->exists($template_file)) {
// template.xxx.twig
$page_template = $template_file;
} elseif ($twig_extension !== TEMPLATE_EXT && $loader->exists($template . TEMPLATE_EXT)) {
// template.html.twig
$page_template = $template . TEMPLATE_EXT;
$format = 'html';
} elseif ($loader->exists($default . $twig_extension)) {
// default.xxx.twig
$page_template = $default . $twig_extension;
} else {
// default.html.twig
$page_template = $default . TEMPLATE_EXT;
$format = 'html';
}
if ($loader->exists($template_file)) {
// template.xxx.twig
$page_template = $template_file;
} elseif ($twig_extension !== TEMPLATE_EXT && $loader->exists($template . TEMPLATE_EXT)) {
// template.html.twig
$page_template = $template . TEMPLATE_EXT;
$format = 'html';
} elseif ($loader->exists($default . $twig_extension)) {
// default.xxx.twig
$page_template = $default . $twig_extension;
} else {
// default.html.twig
$page_template = $default . TEMPLATE_EXT;
$format = 'html';
}
return $page_template;

View File

@@ -1,70 +0,0 @@
<?php
// Fix too many ob_get_clean() calls when exception is thrown inside the template.
namespace Phive\Twig\Extensions\Deferred;
class DeferredExtension extends \Twig_Extension
{
/**
* @var array
*/
private $blocks = array();
/**
* {@inheritdoc}
*/
public function getTokenParsers()
{
return array(new DeferredTokenParser());
}
/**
* {@inheritdoc}
*/
public function getNodeVisitors()
{
return array(new DeferredNodeVisitor());
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'deferred';
}
public function defer(\Twig_Template $template, $blockName)
{
ob_start();
$templateName = $template->getTemplateName();
$this->blocks[$templateName][] = [ob_get_level(), $blockName];
}
public function resolve(\Twig_Template $template, array $context, array $blocks)
{
$templateName = $template->getTemplateName();
if (empty($this->blocks[$templateName])) {
return;
}
while ($block = array_pop($this->blocks[$templateName])) {
[$level, $blockName] = $block;
if (ob_get_level() !== $level) {
continue;
}
$buffer = ob_get_clean();
$blocks[$blockName] = array($template, 'block_'.$blockName.'_deferred');
$template->displayBlock($blockName, $context, $blocks);
echo $buffer;
}
if ($parent = $template->getParent($context)) {
$this->resolve($parent, $context, $blocks);
}
}
}

View 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")
;
}
}

View File

@@ -0,0 +1,66 @@
<?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\Extension\AbstractExtension;
use Twig\Template;
final class DeferredExtension extends AbstractExtension
{
private $blocks = [];
public function getTokenParsers() : array
{
return [new DeferredTokenParser()];
}
public function getNodeVisitors() : array
{
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);
}
}
}

View 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")
;
}
}

View 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")
;
}
}

View 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(\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;
}
}

View 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;
}
}