Skip to content

Commit

Permalink
Fix #154: Support for Twig 3.9
Browse files Browse the repository at this point in the history
  • Loading branch information
diffy0712 authored May 7, 2024
1 parent 34d28fc commit 5f15bc2
Show file tree
Hide file tree
Showing 5 changed files with 62 additions and 10 deletions.
8 changes: 6 additions & 2 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
Yii Framework 2 twig extension Change Log
=========================================

2.4.4 under development
2.5.0 under development
-----------------------

- no changes in this release.
- Bug #154: Support for Twig 3.9
In twig 3.9 there were many internal changes that might affect and break codebases using twig:
- Internal functions (including twig_get_attribute) has been moved and renamed.
- The internal working of twig does not store template state in the output buffer anymore.
This means if a custom twig function reads and modifies the output buffer, it might not work as expected.


2.4.3 April 29, 2024
Expand Down
5 changes: 1 addition & 4 deletions composer.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,7 @@
"require": {
"php": "^7.2.5|^8.0|^8.1",
"yiisoft/yii2": "~2.0.4",
"twig/twig": "~3.0"
},
"conflict": {
"twig/twig": ">=3.9"
"twig/twig": "~3.9"
},
"require-dev": {
"cweagans/composer-patches": "^1.7",
Expand Down
18 changes: 17 additions & 1 deletion src/Extension.php
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,18 @@ class Extension extends AbstractExtension
*/
protected $widgets = [];

/**
* Little hack to work with twig 3.9
* see explanation at the end of yii\twig\ViewRenderer::render function
*
* @var bool
*/
protected $viewEndPage = false;

public function withViewEndPage(): bool
{
return $this->viewEndPage;
}

/**
* Creates new instance
Expand Down Expand Up @@ -194,7 +206,11 @@ public function widget($widget, $config = [])
public function viewHelper($context, $name = null)
{
if ($name !== null && isset($context['this'])) {
$this->call($context['this'], Inflector::variablize($name));
if ($name === 'end_page') {
$this->viewEndPage = true;
} else {
$this->call($context['this'], Inflector::variablize($name));
}
}
}

Expand Down
4 changes: 3 additions & 1 deletion src/Template.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
use Twig\Error\RuntimeError;
use Twig\Template as TwigTemplate;
use Twig\Markup;
use Twig\Extension\CoreExtension;

/**
* Template helper
Expand Down Expand Up @@ -51,6 +52,7 @@ public static function attribute(Environment $env, Source $source, $object, $ite
$arguments[$key] = (string)$value;
}
}
return \twig_get_attribute($env, $source, $object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);

return CoreExtension::getAttribute($env, $source, $object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);
}
}
37 changes: 35 additions & 2 deletions src/ViewRenderer.php
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,14 @@ class ViewRenderer extends BaseViewRenderer
*/
public $twigFallbackPaths = [];

/**
* Custom Extension for twig
* Need this in the render function for twig3.9 fix.
*
* @var Extension
*/
protected $extension;


public function init()
{
Expand All @@ -121,6 +129,7 @@ public function init()
$this->twig = new Environment($loader, array_merge([
'cache' => Yii::getAlias($this->cachePath),
'charset' => Yii::$app->charset,
'use_yield' => false
], $this->options));

// Adding custom globals (objects or static classes)
Expand All @@ -138,7 +147,8 @@ public function init()
$this->addFilters($this->filters);
}

$this->addExtensions([new Extension($this->uses)]);
$this->extension = new Extension($this->uses);
$this->addExtensions([$this->extension]);

// Adding custom extensions
if (!empty($this->extensions)) {
Expand Down Expand Up @@ -176,7 +186,30 @@ public function render($view, $file, $params)
$this->setLexerOptions($this->lexerOptions);
}

return $this->twig->render(pathinfo($file, PATHINFO_BASENAME), $params);
$content = $this->twig->render(pathinfo($file, PATHINFO_BASENAME), $params);
/**
* Hack to work with twig3.9.
* Explanation:
* Twig 3.9 does not hold the contents of the template in the output buffer anymore,
* but it still reads the contents from it (if we stick to use `use_yield` false)
* (it was an internal implementation and this package relied on this fact)
* This means that when the endPage method in the yii2 View class wants to read the output buffer
* to replace the placeholders it will be empty, because twig has already emptied it (and does not hold its state anymore).
*
* By not doing anything in the twig function call yet (see yii\twig\Extension::viewHelper), we can work around this limitation
* by calling the endPage function with the twig render results in the buffer after twig has already done its work.
*/
if ($this->extension->withViewEndPage()) {
// $view->endPage will end the current buffer when calling ob_get_clean and echo the modified(replaced placeholders) contents.
// this means that we need 2 levels deep output buffer.
ob_start();
ob_start();
echo $content;
$view->endPage();
$content = ob_get_clean();
}

return $content;
}

/**
Expand Down

0 comments on commit 5f15bc2

Please sign in to comment.