Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Prevent calling twig_get_attribute by generating the guessed PHP code instead #3928

Open
wants to merge 8 commits into
base: 3.x
Choose a base branch
from
9 changes: 9 additions & 0 deletions doc/tags/type.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
``type``
==============

Twig can perform more performant access, when type of variables can be predicted.
To support prediction you can provide type hints, that you already know from PHP:

.. code-block:: twig

{% type interval \DateInterval %}
9 changes: 9 additions & 0 deletions src/Environment.php
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,8 @@
use Twig\NodeVisitor\NodeVisitorInterface;
use Twig\RuntimeLoader\RuntimeLoaderInterface;
use Twig\TokenParser\TokenParserInterface;
use Twig\TypeHint\ContextStack;
use Twig\TypeHint\TypeInterface;

/**
* Stores the Twig configuration and renders templates.
Expand Down Expand Up @@ -69,6 +71,7 @@ class Environment
private $optionsHash;
/** @var bool */
private $useYield;
private $typeHintStack;

/**
* Constructor.
Expand Down Expand Up @@ -127,6 +130,7 @@ public function __construct(LoaderInterface $loader, $options = [])
$this->strictVariables = (bool) $options['strict_variables'];
$this->setCache($options['cache']);
$this->extensionSet = new ExtensionSet();
$this->typeHintStack = new ContextStack();

$this->addExtension(new CoreExtension());
$this->addExtension(new EscaperExtension($options['autoescape']));
Expand Down Expand Up @@ -845,6 +849,11 @@ public function getBinaryOperators(): array
return $this->extensionSet->getBinaryOperators();
}

public function getTypeHintStack(): ContextStack
{
return $this->typeHintStack;
}

private function updateOptionsHash(): void
{
$this->optionsHash = implode(':', [
Expand Down
2 changes: 2 additions & 0 deletions src/Extension/CoreExtension.php
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,7 @@
use Twig\TokenParser\IncludeTokenParser;
use Twig\TokenParser\MacroTokenParser;
use Twig\TokenParser\SetTokenParser;
use Twig\TokenParser\TypeHintTokenParser;
use Twig\TokenParser\UseTokenParser;
use Twig\TokenParser\WithTokenParser;
use Twig\TwigFilter;
Expand Down Expand Up @@ -178,6 +179,7 @@ public function getTokenParsers(): array
new EmbedTokenParser(),
new WithTokenParser(),
new DeprecatedTokenParser(),
new TypeHintTokenParser(),
];
}

Expand Down
32 changes: 32 additions & 0 deletions src/Extension/TypeOptimizerExtension.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

/*
* This file is part of Twig.
*
* (c) Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/

namespace Twig\Extension;

use Twig\NodeVisitor\TypeEvaluateNodeVisitor;

/**
* Extension to improve PHP code compiling by predicting variable types of expressions.
*
* @author Joshua Behrens <[email protected]>
*/
final class TypeOptimizerExtension extends AbstractExtension
{
public function getTokenParsers(): array
{
return [];
}

public function getNodeVisitors(): array
{
return [new TypeEvaluateNodeVisitor()];
}
}
22 changes: 21 additions & 1 deletion src/Lexer.php
Original file line number Diff line number Diff line change
Expand Up @@ -412,7 +412,27 @@ private function lexComment(): void
throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source);
}

$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor) . $match[0][0];

if (preg_match('/(.*@var\s+)([a-z][a-z0-9]*)(\s+)(\S+)(.*)$/is', $text, $hintMatch) === 1) {
$this->pushToken(Token::BLOCK_START_TYPE);
$this->moveCursor($hintMatch[1]);

$this->pushToken(Token::NAME_TYPE, 'type');

$this->pushToken(Token::NAME_TYPE, $hintMatch[2]);
$this->moveCursor($hintMatch[2]);

$this->moveCursor($hintMatch[3]);

$this->pushToken(Token::NAME_TYPE, $hintMatch[4]);
$this->moveCursor($hintMatch[4]);

$this->pushToken(Token::BLOCK_END_TYPE);
$this->moveCursor($hintMatch[5]);
} else {
$this->moveCursor($text);
}
}

private function lexString(): void
Expand Down
Loading