mirror of
https://github.com/slawkens/myaac.git
synced 2025-04-26 17:29:21 +02:00
Update Twig to v2.15.4
This commit is contained in:
parent
e552bcfe82
commit
130f7ba405
@ -18,7 +18,7 @@ namespace Twig\Cache;
|
||||
*/
|
||||
class FilesystemCache implements CacheInterface
|
||||
{
|
||||
const FORCE_BYTECODE_INVALIDATION = 1;
|
||||
public const FORCE_BYTECODE_INVALIDATION = 1;
|
||||
|
||||
private $directory;
|
||||
private $options;
|
||||
@ -35,7 +35,7 @@ class FilesystemCache implements CacheInterface
|
||||
|
||||
public function generateKey($name, $className)
|
||||
{
|
||||
$hash = hash('sha256', $className);
|
||||
$hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $className);
|
||||
|
||||
return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
|
||||
}
|
||||
@ -67,7 +67,7 @@ class FilesystemCache implements CacheInterface
|
||||
|
||||
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
|
||||
// Compile cached file into bytecode cache
|
||||
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) {
|
||||
if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) {
|
||||
@opcache_invalidate($key, true);
|
||||
} elseif (\function_exists('apc_compile_file')) {
|
||||
apc_compile_file($key);
|
||||
|
@ -14,11 +14,9 @@ namespace Twig\Cache;
|
||||
/**
|
||||
* Implements a no-cache strategy.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class NullCache implements CacheInterface
|
||||
final class NullCache implements CacheInterface
|
||||
{
|
||||
public function generateKey($name, $className)
|
||||
{
|
||||
|
@ -12,23 +12,22 @@
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Compiles a node to PHP code.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Compiler implements \Twig_CompilerInterface
|
||||
class Compiler
|
||||
{
|
||||
protected $lastLine;
|
||||
protected $source;
|
||||
protected $indentation;
|
||||
protected $env;
|
||||
protected $debugInfo = [];
|
||||
protected $sourceOffset;
|
||||
protected $sourceLine;
|
||||
protected $filename;
|
||||
private $lastLine;
|
||||
private $source;
|
||||
private $indentation;
|
||||
private $env;
|
||||
private $debugInfo = [];
|
||||
private $sourceOffset;
|
||||
private $sourceLine;
|
||||
private $varNameSalt = 0;
|
||||
|
||||
public function __construct(Environment $env)
|
||||
@ -36,16 +35,6 @@ class Compiler implements \Twig_CompilerInterface
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.25 (to be removed in 2.0)
|
||||
*/
|
||||
public function getFilename()
|
||||
{
|
||||
@trigger_error(sprintf('The %s() method is deprecated since version 1.25 and will be removed in 2.0.', __FUNCTION__), E_USER_DEPRECATED);
|
||||
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment instance related to this compiler.
|
||||
*
|
||||
@ -73,7 +62,7 @@ class Compiler implements \Twig_CompilerInterface
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function compile(\Twig_NodeInterface $node, $indentation = 0)
|
||||
public function compile(Node $node, $indentation = 0)
|
||||
{
|
||||
$this->lastLine = null;
|
||||
$this->source = '';
|
||||
@ -84,17 +73,12 @@ class Compiler implements \Twig_CompilerInterface
|
||||
$this->indentation = $indentation;
|
||||
$this->varNameSalt = 0;
|
||||
|
||||
if ($node instanceof ModuleNode) {
|
||||
// to be removed in 2.0
|
||||
$this->filename = $node->getTemplateName();
|
||||
}
|
||||
|
||||
$node->compile($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function subcompile(\Twig_NodeInterface $node, $raw = true)
|
||||
public function subcompile(Node $node, $raw = true)
|
||||
{
|
||||
if (false === $raw) {
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4);
|
||||
@ -124,9 +108,8 @@ class Compiler implements \Twig_CompilerInterface
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function write()
|
||||
public function write(...$strings)
|
||||
{
|
||||
$strings = \func_get_args();
|
||||
foreach ($strings as $string) {
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4).$string;
|
||||
}
|
||||
@ -134,22 +117,6 @@ class Compiler implements \Twig_CompilerInterface
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Appends an indentation to the current PHP code after compilation.
|
||||
*
|
||||
* @return $this
|
||||
*
|
||||
* @deprecated since 1.27 (to be removed in 2.0).
|
||||
*/
|
||||
public function addIndentation()
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use write(\'\') instead.', E_USER_DEPRECATED);
|
||||
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a quoted string to the compiled code.
|
||||
*
|
||||
@ -174,21 +141,21 @@ class Compiler implements \Twig_CompilerInterface
|
||||
public function repr($value)
|
||||
{
|
||||
if (\is_int($value) || \is_float($value)) {
|
||||
if (false !== $locale = setlocale(LC_NUMERIC, '0')) {
|
||||
setlocale(LC_NUMERIC, 'C');
|
||||
if (false !== $locale = setlocale(\LC_NUMERIC, '0')) {
|
||||
setlocale(\LC_NUMERIC, 'C');
|
||||
}
|
||||
|
||||
$this->raw(var_export($value, true));
|
||||
|
||||
if (false !== $locale) {
|
||||
setlocale(LC_NUMERIC, $locale);
|
||||
setlocale(\LC_NUMERIC, $locale);
|
||||
}
|
||||
} elseif (null === $value) {
|
||||
$this->raw('null');
|
||||
} elseif (\is_bool($value)) {
|
||||
$this->raw($value ? 'true' : 'false');
|
||||
} elseif (\is_array($value)) {
|
||||
$this->raw('[');
|
||||
$this->raw('array(');
|
||||
$first = true;
|
||||
foreach ($value as $key => $v) {
|
||||
if (!$first) {
|
||||
@ -199,7 +166,7 @@ class Compiler implements \Twig_CompilerInterface
|
||||
$this->raw(' => ');
|
||||
$this->repr($v);
|
||||
}
|
||||
$this->raw(']');
|
||||
$this->raw(')');
|
||||
} else {
|
||||
$this->string($value);
|
||||
}
|
||||
@ -212,22 +179,12 @@ class Compiler implements \Twig_CompilerInterface
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function addDebugInfo(\Twig_NodeInterface $node)
|
||||
public function addDebugInfo(Node $node)
|
||||
{
|
||||
if ($node->getTemplateLine() != $this->lastLine) {
|
||||
$this->write(sprintf("// line %d\n", $node->getTemplateLine()));
|
||||
|
||||
// when mbstring.func_overload is set to 2
|
||||
// mb_substr_count() replaces substr_count()
|
||||
// but they have different signatures!
|
||||
if (((int) ini_get('mbstring.func_overload')) & 2) {
|
||||
@trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED);
|
||||
|
||||
// this is much slower than the "right" version
|
||||
$this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
|
||||
} else {
|
||||
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
|
||||
}
|
||||
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
|
||||
$this->sourceOffset = \strlen($this->source);
|
||||
$this->debugInfo[$this->sourceLine] = $node->getTemplateLine();
|
||||
|
||||
@ -281,7 +238,7 @@ class Compiler implements \Twig_CompilerInterface
|
||||
|
||||
public function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++));
|
||||
return sprintf('__internal_compile_%d', $this->varNameSalt++);
|
||||
}
|
||||
}
|
||||
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -38,11 +38,9 @@ use Twig\Template;
|
||||
*/
|
||||
class Error extends \Exception
|
||||
{
|
||||
protected $lineno;
|
||||
// to be renamed to name in 2.0
|
||||
protected $filename;
|
||||
protected $rawMessage;
|
||||
|
||||
private $lineno;
|
||||
private $name;
|
||||
private $rawMessage;
|
||||
private $sourcePath;
|
||||
private $sourceCode;
|
||||
|
||||
@ -57,22 +55,23 @@ class Error extends \Exception
|
||||
* @param Source|string|null $source The source context where the error occurred
|
||||
* @param \Exception $previous The previous exception
|
||||
*/
|
||||
public function __construct($message, $lineno = -1, $source = null, \Exception $previous = null)
|
||||
public function __construct(string $message, int $lineno = -1, $source = null, \Exception $previous = null)
|
||||
{
|
||||
parent::__construct('', 0, $previous);
|
||||
|
||||
if (null === $source) {
|
||||
$name = null;
|
||||
} elseif (!$source instanceof Source) {
|
||||
// for compat with the Twig C ext., passing the template name as string is accepted
|
||||
} elseif (!$source instanceof Source && !$source instanceof \Twig_Source) {
|
||||
@trigger_error(sprintf('Passing a string as a source to %s is deprecated since Twig 2.6.1; pass a Twig\Source instance instead.', __CLASS__), \E_USER_DEPRECATED);
|
||||
$name = $source;
|
||||
} else {
|
||||
$name = $source->getName();
|
||||
$this->sourceCode = $source->getCode();
|
||||
$this->sourcePath = $source->getPath();
|
||||
}
|
||||
parent::__construct('', 0, $previous);
|
||||
|
||||
$this->lineno = $lineno;
|
||||
$this->filename = $name;
|
||||
$this->name = $name;
|
||||
$this->rawMessage = $message;
|
||||
$this->updateRepr();
|
||||
}
|
||||
@ -87,67 +86,6 @@ class Error extends \Exception
|
||||
return $this->rawMessage;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logical name where the error occurred.
|
||||
*
|
||||
* @return string The name
|
||||
*
|
||||
* @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead.
|
||||
*/
|
||||
public function getTemplateFile()
|
||||
{
|
||||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logical name where the error occurred.
|
||||
*
|
||||
* @param string $name The name
|
||||
*
|
||||
* @deprecated since 1.27 (to be removed in 2.0). Use setSourceContext() instead.
|
||||
*/
|
||||
public function setTemplateFile($name)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
$this->filename = $name;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the logical name where the error occurred.
|
||||
*
|
||||
* @return string The name
|
||||
*
|
||||
* @deprecated since 1.29 (to be removed in 2.0). Use getSourceContext() instead.
|
||||
*/
|
||||
public function getTemplateName()
|
||||
{
|
||||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use getSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the logical name where the error occurred.
|
||||
*
|
||||
* @param string $name The name
|
||||
*
|
||||
* @deprecated since 1.29 (to be removed in 2.0). Use setSourceContext() instead.
|
||||
*/
|
||||
public function setTemplateName($name)
|
||||
{
|
||||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.29 and will be removed in 2.0. Use setSourceContext() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
$this->filename = $name;
|
||||
$this->sourceCode = $this->sourcePath = null;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the template line where the error occurred.
|
||||
*
|
||||
@ -177,7 +115,7 @@ class Error extends \Exception
|
||||
*/
|
||||
public function getSourceContext()
|
||||
{
|
||||
return $this->filename ? new Source($this->sourceCode, $this->filename, $this->sourcePath) : null;
|
||||
return $this->name ? new Source($this->sourceCode, $this->name, $this->sourcePath) : null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,10 +124,10 @@ class Error extends \Exception
|
||||
public function setSourceContext(Source $source = null)
|
||||
{
|
||||
if (null === $source) {
|
||||
$this->sourceCode = $this->filename = $this->sourcePath = null;
|
||||
$this->sourceCode = $this->name = $this->sourcePath = null;
|
||||
} else {
|
||||
$this->sourceCode = $source->getCode();
|
||||
$this->filename = $source->getName();
|
||||
$this->name = $source->getName();
|
||||
$this->sourcePath = $source->getPath();
|
||||
}
|
||||
|
||||
@ -208,10 +146,7 @@ class Error extends \Exception
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function updateRepr()
|
||||
private function updateRepr()
|
||||
{
|
||||
$this->message = $this->rawMessage;
|
||||
|
||||
@ -234,11 +169,11 @@ class Error extends \Exception
|
||||
$questionMark = true;
|
||||
}
|
||||
|
||||
if ($this->filename) {
|
||||
if (\is_string($this->filename) || (\is_object($this->filename) && method_exists($this->filename, '__toString'))) {
|
||||
$name = sprintf('"%s"', $this->filename);
|
||||
if ($this->name) {
|
||||
if (\is_string($this->name) || (\is_object($this->name) && method_exists($this->name, '__toString'))) {
|
||||
$name = sprintf('"%s"', $this->name);
|
||||
} else {
|
||||
$name = json_encode($this->filename);
|
||||
$name = json_encode($this->name);
|
||||
}
|
||||
$this->message .= sprintf(' in %s', $name);
|
||||
}
|
||||
@ -256,20 +191,17 @@ class Error extends \Exception
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function guessTemplateInfo()
|
||||
private function guessTemplateInfo()
|
||||
{
|
||||
$template = null;
|
||||
$templateClass = null;
|
||||
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
$backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS | \DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig_Template' !== \get_class($trace['object'])) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig\Template' !== \get_class($trace['object'])) {
|
||||
$currentClass = \get_class($trace['object']);
|
||||
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
|
||||
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
|
||||
$isEmbedContainer = null === $templateClass ? false : 0 === strpos($templateClass, $currentClass);
|
||||
if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
|
||||
$template = $trace['object'];
|
||||
$templateClass = \get_class($trace['object']);
|
||||
}
|
||||
@ -277,8 +209,8 @@ class Error extends \Exception
|
||||
}
|
||||
|
||||
// update template name
|
||||
if (null !== $template && null === $this->filename) {
|
||||
$this->filename = $template->getTemplateName();
|
||||
if (null !== $template && null === $this->name) {
|
||||
$this->name = $template->getTemplateName();
|
||||
}
|
||||
|
||||
// update template path if any
|
||||
@ -296,7 +228,7 @@ class Error extends \Exception
|
||||
$file = $r->getFileName();
|
||||
|
||||
$exceptions = [$e = $this];
|
||||
while ($e instanceof self && $e = $e->getPrevious()) {
|
||||
while ($e = $e->getPrevious()) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
|
@ -26,20 +26,6 @@ class SyntaxError extends Error
|
||||
* @param array $items An array of possible items
|
||||
*/
|
||||
public function addSuggestions($name, array $items)
|
||||
{
|
||||
if (!$alternatives = self::computeAlternatives($name, $items)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* To be merged with the addSuggestions() method in 2.0.
|
||||
*/
|
||||
public static function computeAlternatives($name, $items)
|
||||
{
|
||||
$alternatives = [];
|
||||
foreach ($items as $item) {
|
||||
@ -48,9 +34,14 @@ class SyntaxError extends Error
|
||||
$alternatives[$item] = $lev;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$alternatives) {
|
||||
return;
|
||||
}
|
||||
|
||||
asort($alternatives);
|
||||
|
||||
return array_keys($alternatives);
|
||||
$this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', array_keys($alternatives))));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -13,6 +13,7 @@
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\ArrowFunctionExpression;
|
||||
use Twig\Node\Expression\AssignNameExpression;
|
||||
@ -24,6 +25,7 @@ use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\MethodCallExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\ParentExpression;
|
||||
use Twig\Node\Expression\TestExpression;
|
||||
use Twig\Node\Expression\Unary\NegUnary;
|
||||
use Twig\Node\Expression\Unary\NotUnary;
|
||||
use Twig\Node\Expression\Unary\PosUnary;
|
||||
@ -43,30 +45,20 @@ use Twig\Node\Node;
|
||||
*/
|
||||
class ExpressionParser
|
||||
{
|
||||
const OPERATOR_LEFT = 1;
|
||||
const OPERATOR_RIGHT = 2;
|
||||
|
||||
protected $parser;
|
||||
protected $unaryOperators;
|
||||
protected $binaryOperators;
|
||||
public const OPERATOR_LEFT = 1;
|
||||
public const OPERATOR_RIGHT = 2;
|
||||
|
||||
private $parser;
|
||||
private $env;
|
||||
private $unaryOperators;
|
||||
private $binaryOperators;
|
||||
|
||||
public function __construct(Parser $parser, $env = null)
|
||||
public function __construct(Parser $parser, Environment $env)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
|
||||
if ($env instanceof Environment) {
|
||||
$this->env = $env;
|
||||
$this->unaryOperators = $env->getUnaryOperators();
|
||||
$this->binaryOperators = $env->getBinaryOperators();
|
||||
} else {
|
||||
@trigger_error('Passing the operators as constructor arguments to '.__METHOD__.' is deprecated since version 1.27. Pass the environment instead.', E_USER_DEPRECATED);
|
||||
|
||||
$this->env = $parser->getEnvironment();
|
||||
$this->unaryOperators = func_get_arg(1);
|
||||
$this->binaryOperators = func_get_arg(2);
|
||||
}
|
||||
$this->env = $env;
|
||||
$this->unaryOperators = $env->getUnaryOperators();
|
||||
$this->binaryOperators = $env->getBinaryOperators();
|
||||
}
|
||||
|
||||
public function parseExpression($precedence = 0, $allowArrow = false)
|
||||
@ -86,7 +78,7 @@ class ExpressionParser
|
||||
} elseif ('is' === $token->getValue()) {
|
||||
$expr = $this->parseTestExpression($expr);
|
||||
} elseif (isset($op['callable'])) {
|
||||
$expr = \call_user_func($op['callable'], $this->parser, $expr);
|
||||
$expr = $op['callable']($this->parser, $expr);
|
||||
} else {
|
||||
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
|
||||
$class = $op['class'];
|
||||
@ -111,57 +103,57 @@ class ExpressionParser
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
// short array syntax (one argument, no parentheses)?
|
||||
if ($stream->look(1)->test(Token::ARROW_TYPE)) {
|
||||
if ($stream->look(1)->test(/* Token::ARROW_TYPE */ 12)) {
|
||||
$line = $stream->getCurrent()->getLine();
|
||||
$token = $stream->expect(Token::NAME_TYPE);
|
||||
$token = $stream->expect(/* Token::NAME_TYPE */ 5);
|
||||
$names = [new AssignNameExpression($token->getValue(), $token->getLine())];
|
||||
$stream->expect(Token::ARROW_TYPE);
|
||||
$stream->expect(/* Token::ARROW_TYPE */ 12);
|
||||
|
||||
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
|
||||
}
|
||||
|
||||
// first, determine if we are parsing an arrow function by finding => (long form)
|
||||
$i = 0;
|
||||
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
|
||||
return null;
|
||||
}
|
||||
++$i;
|
||||
while (true) {
|
||||
// variable name
|
||||
++$i;
|
||||
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
|
||||
break;
|
||||
}
|
||||
++$i;
|
||||
}
|
||||
if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {
|
||||
return null;
|
||||
}
|
||||
++$i;
|
||||
if (!$stream->look($i)->test(Token::ARROW_TYPE)) {
|
||||
if (!$stream->look($i)->test(/* Token::ARROW_TYPE */ 12)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// yes, let's parse it properly
|
||||
$token = $stream->expect(Token::PUNCTUATION_TYPE, '(');
|
||||
$token = $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(');
|
||||
$line = $token->getLine();
|
||||
|
||||
$names = [];
|
||||
while (true) {
|
||||
$token = $stream->expect(Token::NAME_TYPE);
|
||||
$token = $stream->expect(/* Token::NAME_TYPE */ 5);
|
||||
$names[] = new AssignNameExpression($token->getValue(), $token->getLine());
|
||||
|
||||
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')');
|
||||
$stream->expect(Token::ARROW_TYPE);
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')');
|
||||
$stream->expect(/* Token::ARROW_TYPE */ 12);
|
||||
|
||||
return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line);
|
||||
}
|
||||
|
||||
protected function getPrimary()
|
||||
private function getPrimary(): AbstractExpression
|
||||
{
|
||||
$token = $this->parser->getCurrentToken();
|
||||
|
||||
@ -172,10 +164,10 @@ class ExpressionParser
|
||||
$class = $operator['class'];
|
||||
|
||||
return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
|
||||
} elseif ($token->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
|
||||
$this->parser->getStream()->next();
|
||||
$expr = $this->parseExpression();
|
||||
$this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
|
||||
$this->parser->getStream()->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'An opened parenthesis is not properly closed');
|
||||
|
||||
return $this->parsePostfixExpression($expr);
|
||||
}
|
||||
@ -183,12 +175,12 @@ class ExpressionParser
|
||||
return $this->parsePrimaryExpression();
|
||||
}
|
||||
|
||||
protected function parseConditionalExpression($expr)
|
||||
private function parseConditionalExpression($expr): AbstractExpression
|
||||
{
|
||||
while ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, '?')) {
|
||||
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
while ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, '?')) {
|
||||
if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
if ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
$expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine());
|
||||
@ -204,21 +196,21 @@ class ExpressionParser
|
||||
return $expr;
|
||||
}
|
||||
|
||||
protected function isUnary(Token $token)
|
||||
private function isUnary(Token $token): bool
|
||||
{
|
||||
return $token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
|
||||
return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->unaryOperators[$token->getValue()]);
|
||||
}
|
||||
|
||||
protected function isBinary(Token $token)
|
||||
private function isBinary(Token $token): bool
|
||||
{
|
||||
return $token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
|
||||
return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->binaryOperators[$token->getValue()]);
|
||||
}
|
||||
|
||||
public function parsePrimaryExpression()
|
||||
{
|
||||
$token = $this->parser->getCurrentToken();
|
||||
switch ($token->getType()) {
|
||||
case Token::NAME_TYPE:
|
||||
case /* Token::NAME_TYPE */ 5:
|
||||
$this->parser->getStream()->next();
|
||||
switch ($token->getValue()) {
|
||||
case 'true':
|
||||
@ -247,17 +239,17 @@ class ExpressionParser
|
||||
}
|
||||
break;
|
||||
|
||||
case Token::NUMBER_TYPE:
|
||||
case /* Token::NUMBER_TYPE */ 6:
|
||||
$this->parser->getStream()->next();
|
||||
$node = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
break;
|
||||
|
||||
case Token::STRING_TYPE:
|
||||
case Token::INTERPOLATION_START_TYPE:
|
||||
case /* Token::STRING_TYPE */ 7:
|
||||
case /* Token::INTERPOLATION_START_TYPE */ 10:
|
||||
$node = $this->parseStringExpression();
|
||||
break;
|
||||
|
||||
case Token::OPERATOR_TYPE:
|
||||
case /* Token::OPERATOR_TYPE */ 8:
|
||||
if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
|
||||
// in this context, string operators are variable names
|
||||
$this->parser->getStream()->next();
|
||||
@ -267,10 +259,8 @@ class ExpressionParser
|
||||
$class = $this->unaryOperators[$token->getValue()]['class'];
|
||||
|
||||
$ref = new \ReflectionClass($class);
|
||||
$negClass = 'Twig\Node\Expression\Unary\NegUnary';
|
||||
$posClass = 'Twig\Node\Expression\Unary\PosUnary';
|
||||
if (!(\in_array($ref->getName(), [$negClass, $posClass, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos'])
|
||||
|| $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass)
|
||||
if (!(\in_array($ref->getName(), [NegUnary::class, PosUnary::class, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos'])
|
||||
|| $ref->isSubclassOf(NegUnary::class) || $ref->isSubclassOf(PosUnary::class)
|
||||
|| $ref->isSubclassOf('Twig_Node_Expression_Unary_Neg') || $ref->isSubclassOf('Twig_Node_Expression_Unary_Pos'))
|
||||
) {
|
||||
throw new SyntaxError(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
|
||||
@ -285,11 +275,11 @@ class ExpressionParser
|
||||
|
||||
// no break
|
||||
default:
|
||||
if ($token->test(Token::PUNCTUATION_TYPE, '[')) {
|
||||
if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) {
|
||||
$node = $this->parseArrayExpression();
|
||||
} elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) {
|
||||
} elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) {
|
||||
$node = $this->parseHashExpression();
|
||||
} elseif ($token->test(Token::OPERATOR_TYPE, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
|
||||
} elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) {
|
||||
throw new SyntaxError(sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
|
||||
} else {
|
||||
throw new SyntaxError(sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext());
|
||||
@ -307,12 +297,12 @@ class ExpressionParser
|
||||
// a string cannot be followed by another string in a single expression
|
||||
$nextCanBeString = true;
|
||||
while (true) {
|
||||
if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) {
|
||||
if ($nextCanBeString && $token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) {
|
||||
$nodes[] = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
$nextCanBeString = false;
|
||||
} elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) {
|
||||
} elseif ($stream->nextIf(/* Token::INTERPOLATION_START_TYPE */ 10)) {
|
||||
$nodes[] = $this->parseExpression();
|
||||
$stream->expect(Token::INTERPOLATION_END_TYPE);
|
||||
$stream->expect(/* Token::INTERPOLATION_END_TYPE */ 11);
|
||||
$nextCanBeString = true;
|
||||
} else {
|
||||
break;
|
||||
@ -330,16 +320,16 @@ class ExpressionParser
|
||||
public function parseArrayExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'An array element was expected');
|
||||
|
||||
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'An array element must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -347,7 +337,7 @@ class ExpressionParser
|
||||
|
||||
$node->addElement($this->parseExpression());
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened array is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
@ -355,16 +345,16 @@ class ExpressionParser
|
||||
public function parseHashExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '{', 'A hash element was expected');
|
||||
|
||||
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
||||
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A hash value must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -376,9 +366,18 @@ class ExpressionParser
|
||||
// * a string -- 'a'
|
||||
// * a name, which is equivalent to a string -- a
|
||||
// * an expression, which must be enclosed in parentheses -- (1 + 2)
|
||||
if (($token = $stream->nextIf(Token::STRING_TYPE)) || ($token = $stream->nextIf(Token::NAME_TYPE)) || $token = $stream->nextIf(Token::NUMBER_TYPE)) {
|
||||
if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) {
|
||||
$key = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
} elseif ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
|
||||
// {a} is a shortcut for {a:a}
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, [',', '}'])) {
|
||||
$value = new NameExpression($key->getAttribute('value'), $key->getTemplateLine());
|
||||
$node->addElement($value, $key);
|
||||
continue;
|
||||
}
|
||||
} elseif (($token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) || $token = $stream->nextIf(/* Token::NUMBER_TYPE */ 6)) {
|
||||
$key = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
} elseif ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
|
||||
$key = $this->parseExpression();
|
||||
} else {
|
||||
$current = $stream->getCurrent();
|
||||
@ -386,12 +385,12 @@ class ExpressionParser
|
||||
throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ':', 'A hash key must be followed by a colon (:)');
|
||||
$value = $this->parseExpression();
|
||||
|
||||
$node->addElement($value, $key);
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '}', 'An opened hash is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
@ -400,7 +399,7 @@ class ExpressionParser
|
||||
{
|
||||
while (true) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if (Token::PUNCTUATION_TYPE == $token->getType()) {
|
||||
if (/* Token::PUNCTUATION_TYPE */ 9 == $token->getType()) {
|
||||
if ('.' == $token->getValue() || '[' == $token->getValue()) {
|
||||
$node = $this->parseSubscriptExpression($node);
|
||||
} elseif ('|' == $token->getValue()) {
|
||||
@ -474,22 +473,22 @@ class ExpressionParser
|
||||
if ('.' == $token->getValue()) {
|
||||
$token = $stream->next();
|
||||
if (
|
||||
Token::NAME_TYPE == $token->getType()
|
||||
/* Token::NAME_TYPE */ 5 == $token->getType()
|
||||
||
|
||||
Token::NUMBER_TYPE == $token->getType()
|
||||
/* Token::NUMBER_TYPE */ 6 == $token->getType()
|
||||
||
|
||||
(Token::OPERATOR_TYPE == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue()))
|
||||
(/* Token::OPERATOR_TYPE */ 8 == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue()))
|
||||
) {
|
||||
$arg = new ConstantExpression($token->getValue(), $lineno);
|
||||
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
|
||||
$type = Template::METHOD_CALL;
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext());
|
||||
throw new SyntaxError(sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), Token::typeToEnglish($token->getType())), $lineno, $stream->getSourceContext());
|
||||
}
|
||||
|
||||
if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
||||
@ -499,11 +498,7 @@ class ExpressionParser
|
||||
|
||||
$name = $arg->getAttribute('value');
|
||||
|
||||
if ($this->parser->isReservedMacroName($name)) {
|
||||
throw new SyntaxError(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
$node = new MethodCallExpression($node, 'get'.$name, $arguments, $lineno);
|
||||
$node = new MethodCallExpression($node, 'macro_'.$name, $arguments, $lineno);
|
||||
$node->setAttribute('safe', true);
|
||||
|
||||
return $node;
|
||||
@ -513,19 +508,19 @@ class ExpressionParser
|
||||
|
||||
// slice?
|
||||
$slice = false;
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ':')) {
|
||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
|
||||
$slice = true;
|
||||
$arg = new ConstantExpression(0, $token->getLine());
|
||||
} else {
|
||||
$arg = $this->parseExpression();
|
||||
}
|
||||
|
||||
if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
if ($stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) {
|
||||
$slice = true;
|
||||
}
|
||||
|
||||
if ($slice) {
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) {
|
||||
$length = new ConstantExpression(null, $token->getLine());
|
||||
} else {
|
||||
$length = $this->parseExpression();
|
||||
@ -535,12 +530,12 @@ class ExpressionParser
|
||||
$arguments = new Node([$arg, $length]);
|
||||
$filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine());
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']');
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']');
|
||||
}
|
||||
|
||||
return new GetAttrExpression($node, $arg, $arguments, $type, $lineno);
|
||||
@ -556,10 +551,10 @@ class ExpressionParser
|
||||
public function parseFilterExpressionRaw($node, $tag = null)
|
||||
{
|
||||
while (true) {
|
||||
$token = $this->parser->getStream()->expect(Token::NAME_TYPE);
|
||||
$token = $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5);
|
||||
|
||||
$name = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
|
||||
$arguments = new Node();
|
||||
} else {
|
||||
$arguments = $this->parseArguments(true, false, true);
|
||||
@ -569,7 +564,7 @@ class ExpressionParser
|
||||
|
||||
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
|
||||
|
||||
if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '|')) {
|
||||
if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '|')) {
|
||||
break;
|
||||
}
|
||||
|
||||
@ -594,21 +589,26 @@ class ExpressionParser
|
||||
$args = [];
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(', 'A list of arguments must begin with an opening parenthesis');
|
||||
while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {
|
||||
if (!empty($args)) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'Arguments must be separated by a comma');
|
||||
|
||||
// if the comma above was a trailing comma, early exit the argument parse loop
|
||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition) {
|
||||
$token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name');
|
||||
$token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'An argument must be a name');
|
||||
$value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine());
|
||||
} else {
|
||||
$value = $this->parseExpression(0, $allowArrow);
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if ($namedArguments && $token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) {
|
||||
if ($namedArguments && $token = $stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) {
|
||||
if (!$value instanceof NameExpression) {
|
||||
throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
@ -618,7 +618,7 @@ class ExpressionParser
|
||||
$value = $this->parsePrimaryExpression();
|
||||
|
||||
if (!$this->checkConstantExpression($value)) {
|
||||
throw new SyntaxError(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext());
|
||||
throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, or an array).', $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
} else {
|
||||
$value = $this->parseExpression(0, $allowArrow);
|
||||
@ -639,7 +639,7 @@ class ExpressionParser
|
||||
}
|
||||
}
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
$stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
|
||||
return new Node($args);
|
||||
}
|
||||
@ -650,19 +650,19 @@ class ExpressionParser
|
||||
$targets = [];
|
||||
while (true) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if ($stream->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
|
||||
if ($stream->test(/* Token::OPERATOR_TYPE */ 8) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
|
||||
// in this context, string operators are variable names
|
||||
$this->parser->getStream()->next();
|
||||
} else {
|
||||
$stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to');
|
||||
$stream->expect(/* Token::NAME_TYPE */ 5, null, 'Only variables can be assigned to');
|
||||
}
|
||||
$value = $token->getValue();
|
||||
if (\in_array(strtolower($value), ['true', 'false', 'none', 'null'])) {
|
||||
if (\in_array(strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), ['true', 'false', 'none', 'null'])) {
|
||||
throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
$targets[] = new AssignNameExpression($value, $token->getLine());
|
||||
|
||||
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -675,7 +675,7 @@ class ExpressionParser
|
||||
$targets = [];
|
||||
while (true) {
|
||||
$targets[] = $this->parseExpression();
|
||||
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
@ -683,35 +683,42 @@ class ExpressionParser
|
||||
return new Node($targets);
|
||||
}
|
||||
|
||||
private function parseNotTestExpression(\Twig_NodeInterface $node)
|
||||
private function parseNotTestExpression(Node $node): NotUnary
|
||||
{
|
||||
return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
private function parseTestExpression(\Twig_NodeInterface $node)
|
||||
private function parseTestExpression(Node $node): TestExpression
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
list($name, $test) = $this->getTest($node->getTemplateLine());
|
||||
|
||||
$class = $this->getTestNodeClass($test);
|
||||
$arguments = null;
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, '(')) {
|
||||
if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) {
|
||||
$arguments = $this->parseArguments(true);
|
||||
} elseif ($test->hasOneMandatoryArgument()) {
|
||||
$arguments = new Node([0 => $this->parsePrimaryExpression()]);
|
||||
}
|
||||
|
||||
if ('defined' === $name && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) {
|
||||
$node = new MethodCallExpression($alias['node'], $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine());
|
||||
$node->setAttribute('safe', true);
|
||||
}
|
||||
|
||||
return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
private function getTest($line)
|
||||
private function getTest(int $line): array
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$name = $stream->expect(Token::NAME_TYPE)->getValue();
|
||||
$name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue();
|
||||
|
||||
if ($test = $this->env->getTest($name)) {
|
||||
return [$name, $test];
|
||||
}
|
||||
|
||||
if ($stream->test(Token::NAME_TYPE)) {
|
||||
if ($stream->test(/* Token::NAME_TYPE */ 5)) {
|
||||
// try 2-words tests
|
||||
$name = $name.' '.$this->parser->getCurrentToken()->getValue();
|
||||
|
||||
@ -728,11 +735,12 @@ class ExpressionParser
|
||||
throw $e;
|
||||
}
|
||||
|
||||
private function getTestNodeClass($test)
|
||||
private function getTestNodeClass(TwigTest $test): string
|
||||
{
|
||||
if ($test instanceof TwigTest && $test->isDeprecated()) {
|
||||
if ($test->isDeprecated()) {
|
||||
$stream = $this->parser->getStream();
|
||||
$message = sprintf('Twig Test "%s" is deprecated', $test->getName());
|
||||
|
||||
if (!\is_bool($test->getDeprecatedVersion())) {
|
||||
$message .= sprintf(' since version %s', $test->getDeprecatedVersion());
|
||||
}
|
||||
@ -740,19 +748,15 @@ class ExpressionParser
|
||||
$message .= sprintf('. Use "%s" instead', $test->getAlternative());
|
||||
}
|
||||
$src = $stream->getSourceContext();
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $stream->getCurrent()->getLine());
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $stream->getCurrent()->getLine());
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
@trigger_error($message, \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($test instanceof TwigTest) {
|
||||
return $test->getNodeClass();
|
||||
}
|
||||
|
||||
return $test instanceof \Twig_Test_Node ? $test->getClass() : 'Twig\Node\Expression\TestExpression';
|
||||
return $test->getNodeClass();
|
||||
}
|
||||
|
||||
protected function getFunctionNodeClass($name, $line)
|
||||
private function getFunctionNodeClass(string $name, int $line): string
|
||||
{
|
||||
if (false === $function = $this->env->getFunction($name)) {
|
||||
$e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext());
|
||||
@ -761,7 +765,7 @@ class ExpressionParser
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($function instanceof TwigFunction && $function->isDeprecated()) {
|
||||
if ($function->isDeprecated()) {
|
||||
$message = sprintf('Twig Function "%s" is deprecated', $function->getName());
|
||||
if (!\is_bool($function->getDeprecatedVersion())) {
|
||||
$message .= sprintf(' since version %s', $function->getDeprecatedVersion());
|
||||
@ -770,19 +774,15 @@ class ExpressionParser
|
||||
$message .= sprintf('. Use "%s" instead', $function->getAlternative());
|
||||
}
|
||||
$src = $this->parser->getStream()->getSourceContext();
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
@trigger_error($message, \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($function instanceof TwigFunction) {
|
||||
return $function->getNodeClass();
|
||||
}
|
||||
|
||||
return $function instanceof \Twig_Function_Node ? $function->getClass() : 'Twig\Node\Expression\FunctionExpression';
|
||||
return $function->getNodeClass();
|
||||
}
|
||||
|
||||
protected function getFilterNodeClass($name, $line)
|
||||
private function getFilterNodeClass(string $name, int $line): string
|
||||
{
|
||||
if (false === $filter = $this->env->getFilter($name)) {
|
||||
$e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext());
|
||||
@ -791,7 +791,7 @@ class ExpressionParser
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if ($filter instanceof TwigFilter && $filter->isDeprecated()) {
|
||||
if ($filter->isDeprecated()) {
|
||||
$message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
|
||||
if (!\is_bool($filter->getDeprecatedVersion())) {
|
||||
$message .= sprintf(' since version %s', $filter->getDeprecatedVersion());
|
||||
@ -800,20 +800,16 @@ class ExpressionParser
|
||||
$message .= sprintf('. Use "%s" instead', $filter->getAlternative());
|
||||
}
|
||||
$src = $this->parser->getStream()->getSourceContext();
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ? $src->getPath() : $src->getName(), $line);
|
||||
$message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line);
|
||||
|
||||
@trigger_error($message, E_USER_DEPRECATED);
|
||||
@trigger_error($message, \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if ($filter instanceof TwigFilter) {
|
||||
return $filter->getNodeClass();
|
||||
}
|
||||
|
||||
return $filter instanceof \Twig_Filter_Node ? $filter->getClass() : 'Twig\Node\Expression\FilterExpression';
|
||||
return $filter->getNodeClass();
|
||||
}
|
||||
|
||||
// checks that the node only contains "constant" elements
|
||||
protected function checkConstantExpression(\Twig_NodeInterface $node)
|
||||
private function checkConstantExpression(Node $node): bool
|
||||
{
|
||||
if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression
|
||||
|| $node instanceof NegUnary || $node instanceof PosUnary
|
||||
|
@ -11,17 +11,8 @@
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
|
||||
abstract class AbstractExtension implements ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_InitRuntimeInterface instead
|
||||
*/
|
||||
public function initRuntime(Environment $environment)
|
||||
{
|
||||
}
|
||||
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return [];
|
||||
@ -51,22 +42,6 @@ abstract class AbstractExtension implements ExtensionInterface
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead
|
||||
*/
|
||||
public function getGlobals()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return \get_class($this);
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\AbstractExtension', 'Twig_Extension');
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -12,10 +12,7 @@
|
||||
namespace Twig\Extension {
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class DebugExtension extends AbstractExtension
|
||||
final class DebugExtension extends AbstractExtension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
@ -33,11 +30,6 @@ class DebugExtension extends AbstractExtension
|
||||
new TwigFunction('dump', 'twig_var_dump', ['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [], 'needs_context' => true, 'needs_environment' => true, 'is_variadic' => true]),
|
||||
];
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'debug';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\DebugExtension', 'Twig_Extension_Debug');
|
||||
@ -48,7 +40,7 @@ use Twig\Environment;
|
||||
use Twig\Template;
|
||||
use Twig\TemplateWrapper;
|
||||
|
||||
function twig_var_dump(Environment $env, $context, array $vars = [])
|
||||
function twig_var_dump(Environment $env, $context, ...$vars)
|
||||
{
|
||||
if (!$env->isDebug()) {
|
||||
return;
|
||||
@ -66,9 +58,7 @@ function twig_var_dump(Environment $env, $context, array $vars = [])
|
||||
|
||||
var_dump($vars);
|
||||
} else {
|
||||
foreach ($vars as $var) {
|
||||
var_dump($var);
|
||||
}
|
||||
var_dump(...$vars);
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
|
@ -10,16 +10,21 @@
|
||||
*/
|
||||
|
||||
namespace Twig\Extension {
|
||||
use Twig\FileExtensionEscapingStrategy;
|
||||
use Twig\NodeVisitor\EscaperNodeVisitor;
|
||||
use Twig\TokenParser\AutoEscapeTokenParser;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class EscaperExtension extends AbstractExtension
|
||||
final class EscaperExtension extends AbstractExtension
|
||||
{
|
||||
protected $defaultStrategy;
|
||||
private $defaultStrategy;
|
||||
private $escapers = [];
|
||||
|
||||
/** @internal */
|
||||
public $safeClasses = [];
|
||||
|
||||
/** @internal */
|
||||
public $safeLookup = [];
|
||||
|
||||
/**
|
||||
* @param string|false|callable $defaultStrategy An escaping strategy
|
||||
@ -44,6 +49,8 @@ class EscaperExtension extends AbstractExtension
|
||||
public function getFilters()
|
||||
{
|
||||
return [
|
||||
new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
|
||||
new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']),
|
||||
new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]),
|
||||
];
|
||||
}
|
||||
@ -58,21 +65,8 @@ class EscaperExtension extends AbstractExtension
|
||||
*/
|
||||
public function setDefaultStrategy($defaultStrategy)
|
||||
{
|
||||
// for BC
|
||||
if (true === $defaultStrategy) {
|
||||
@trigger_error('Using "true" as the default strategy is deprecated since version 1.21. Use "html" instead.', E_USER_DEPRECATED);
|
||||
|
||||
$defaultStrategy = 'html';
|
||||
}
|
||||
|
||||
if ('filename' === $defaultStrategy) {
|
||||
@trigger_error('Using "filename" as the default strategy is deprecated since version 1.27. Use "name" instead.', E_USER_DEPRECATED);
|
||||
|
||||
$defaultStrategy = 'name';
|
||||
}
|
||||
|
||||
if ('name' === $defaultStrategy) {
|
||||
$defaultStrategy = ['\Twig\FileExtensionEscapingStrategy', 'guess'];
|
||||
$defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess'];
|
||||
}
|
||||
|
||||
$this->defaultStrategy = $defaultStrategy;
|
||||
@ -96,9 +90,47 @@ class EscaperExtension extends AbstractExtension
|
||||
return $this->defaultStrategy;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
/**
|
||||
* Defines a new escaper to be used via the escape filter.
|
||||
*
|
||||
* @param string $strategy The strategy name that should be used as a strategy in the escape call
|
||||
* @param callable $callable A valid PHP callable
|
||||
*/
|
||||
public function setEscaper($strategy, callable $callable)
|
||||
{
|
||||
return 'escaper';
|
||||
$this->escapers[$strategy] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all defined escapers.
|
||||
*
|
||||
* @return callable[] An array of escapers
|
||||
*/
|
||||
public function getEscapers()
|
||||
{
|
||||
return $this->escapers;
|
||||
}
|
||||
|
||||
public function setSafeClasses(array $safeClasses = [])
|
||||
{
|
||||
$this->safeClasses = [];
|
||||
$this->safeLookup = [];
|
||||
foreach ($safeClasses as $class => $strategies) {
|
||||
$this->addSafeClass($class, $strategies);
|
||||
}
|
||||
}
|
||||
|
||||
public function addSafeClass(string $class, array $strategies)
|
||||
{
|
||||
$class = ltrim($class, '\\');
|
||||
if (!isset($this->safeClasses[$class])) {
|
||||
$this->safeClasses[$class] = [];
|
||||
}
|
||||
$this->safeClasses[$class] = array_merge($this->safeClasses[$class], $strategies);
|
||||
|
||||
foreach ($strategies as $strategy) {
|
||||
$this->safeLookup[$strategy][$class] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -106,6 +138,14 @@ class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper');
|
||||
}
|
||||
|
||||
namespace {
|
||||
use Twig\Environment;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Extension\CoreExtension;
|
||||
use Twig\Extension\EscaperExtension;
|
||||
use Twig\Markup;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Marks a variable as being safe.
|
||||
*
|
||||
@ -117,4 +157,272 @@ function twig_raw_filter($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Escapes a string.
|
||||
*
|
||||
* @param mixed $string The value to be escaped
|
||||
* @param string $strategy The escaping strategy
|
||||
* @param string $charset The charset
|
||||
* @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
|
||||
{
|
||||
if ($autoescape && $string instanceof Markup) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
if (!\is_string($string)) {
|
||||
if (\is_object($string) && method_exists($string, '__toString')) {
|
||||
if ($autoescape) {
|
||||
$c = \get_class($string);
|
||||
$ext = $env->getExtension(EscaperExtension::class);
|
||||
if (!isset($ext->safeClasses[$c])) {
|
||||
$ext->safeClasses[$c] = [];
|
||||
foreach (class_parents($string) + class_implements($string) as $class) {
|
||||
if (isset($ext->safeClasses[$class])) {
|
||||
$ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class]));
|
||||
foreach ($ext->safeClasses[$class] as $s) {
|
||||
$ext->safeLookup[$s][$c] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) {
|
||||
return (string) $string;
|
||||
}
|
||||
}
|
||||
|
||||
$string = (string) $string;
|
||||
} elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) {
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
if ('' === $string) {
|
||||
return '';
|
||||
}
|
||||
|
||||
if (null === $charset) {
|
||||
$charset = $env->getCharset();
|
||||
}
|
||||
|
||||
switch ($strategy) {
|
||||
case 'html':
|
||||
// see https://www.php.net/htmlspecialchars
|
||||
|
||||
// Using a static variable to avoid initializing the array
|
||||
// each time the function is called. Moving the declaration on the
|
||||
// top of the function slow downs other escaping strategies.
|
||||
static $htmlspecialcharsCharsets = [
|
||||
'ISO-8859-1' => true, 'ISO8859-1' => true,
|
||||
'ISO-8859-15' => true, 'ISO8859-15' => true,
|
||||
'utf-8' => true, 'UTF-8' => true,
|
||||
'CP866' => true, 'IBM866' => true, '866' => true,
|
||||
'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
|
||||
'1251' => true,
|
||||
'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
|
||||
'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
|
||||
'BIG5' => true, '950' => true,
|
||||
'GB2312' => true, '936' => true,
|
||||
'BIG5-HKSCS' => true,
|
||||
'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
|
||||
'EUC-JP' => true, 'EUCJP' => true,
|
||||
'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
|
||||
];
|
||||
|
||||
if (isset($htmlspecialcharsCharsets[$charset])) {
|
||||
return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
|
||||
}
|
||||
|
||||
if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
|
||||
// cache the lowercase variant for future iterations
|
||||
$htmlspecialcharsCharsets[$charset] = true;
|
||||
|
||||
return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset);
|
||||
}
|
||||
|
||||
$string = twig_convert_encoding($string, 'UTF-8', $charset);
|
||||
$string = htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8');
|
||||
|
||||
return iconv('UTF-8', $charset, $string);
|
||||
|
||||
case 'js':
|
||||
// escape all non-alphanumeric characters
|
||||
// into their \x or \uHHHH representations
|
||||
if ('UTF-8' !== $charset) {
|
||||
$string = twig_convert_encoding($string, 'UTF-8', $charset);
|
||||
}
|
||||
|
||||
if (!preg_match('//u', $string)) {
|
||||
throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
|
||||
}
|
||||
|
||||
$string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) {
|
||||
$char = $matches[0];
|
||||
|
||||
/*
|
||||
* A few characters have short escape sequences in JSON and JavaScript.
|
||||
* Escape sequences supported only by JavaScript, not JSON, are omitted.
|
||||
* \" is also supported but omitted, because the resulting string is not HTML safe.
|
||||
*/
|
||||
static $shortMap = [
|
||||
'\\' => '\\\\',
|
||||
'/' => '\\/',
|
||||
"\x08" => '\b',
|
||||
"\x0C" => '\f',
|
||||
"\x0A" => '\n',
|
||||
"\x0D" => '\r',
|
||||
"\x09" => '\t',
|
||||
];
|
||||
|
||||
if (isset($shortMap[$char])) {
|
||||
return $shortMap[$char];
|
||||
}
|
||||
|
||||
$codepoint = mb_ord($char, 'UTF-8');
|
||||
if (0x10000 > $codepoint) {
|
||||
return sprintf('\u%04X', $codepoint);
|
||||
}
|
||||
|
||||
// Split characters outside the BMP into surrogate pairs
|
||||
// https://tools.ietf.org/html/rfc2781.html#section-2.1
|
||||
$u = $codepoint - 0x10000;
|
||||
$high = 0xD800 | ($u >> 10);
|
||||
$low = 0xDC00 | ($u & 0x3FF);
|
||||
|
||||
return sprintf('\u%04X\u%04X', $high, $low);
|
||||
}, $string);
|
||||
|
||||
if ('UTF-8' !== $charset) {
|
||||
$string = iconv('UTF-8', $charset, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
|
||||
case 'css':
|
||||
if ('UTF-8' !== $charset) {
|
||||
$string = twig_convert_encoding($string, 'UTF-8', $charset);
|
||||
}
|
||||
|
||||
if (!preg_match('//u', $string)) {
|
||||
throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
|
||||
}
|
||||
|
||||
$string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) {
|
||||
$char = $matches[0];
|
||||
|
||||
return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8'));
|
||||
}, $string);
|
||||
|
||||
if ('UTF-8' !== $charset) {
|
||||
$string = iconv('UTF-8', $charset, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
|
||||
case 'html_attr':
|
||||
if ('UTF-8' !== $charset) {
|
||||
$string = twig_convert_encoding($string, 'UTF-8', $charset);
|
||||
}
|
||||
|
||||
if (!preg_match('//u', $string)) {
|
||||
throw new RuntimeError('The string to escape is not a valid UTF-8 string.');
|
||||
}
|
||||
|
||||
$string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) {
|
||||
/**
|
||||
* This function is adapted from code coming from Zend Framework.
|
||||
*
|
||||
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com)
|
||||
* @license https://framework.zend.com/license/new-bsd New BSD License
|
||||
*/
|
||||
$chr = $matches[0];
|
||||
$ord = \ord($chr);
|
||||
|
||||
/*
|
||||
* The following replaces characters undefined in HTML with the
|
||||
* hex entity for the Unicode replacement character.
|
||||
*/
|
||||
if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) {
|
||||
return '�';
|
||||
}
|
||||
|
||||
/*
|
||||
* Check if the current character to escape has a name entity we should
|
||||
* replace it with while grabbing the hex value of the character.
|
||||
*/
|
||||
if (1 === \strlen($chr)) {
|
||||
/*
|
||||
* While HTML supports far more named entities, the lowest common denominator
|
||||
* has become HTML5's XML Serialisation which is restricted to the those named
|
||||
* entities that XML supports. Using HTML entities would result in this error:
|
||||
* XML Parsing Error: undefined entity
|
||||
*/
|
||||
static $entityMap = [
|
||||
34 => '"', /* quotation mark */
|
||||
38 => '&', /* ampersand */
|
||||
60 => '<', /* less-than sign */
|
||||
62 => '>', /* greater-than sign */
|
||||
];
|
||||
|
||||
if (isset($entityMap[$ord])) {
|
||||
return $entityMap[$ord];
|
||||
}
|
||||
|
||||
return sprintf('&#x%02X;', $ord);
|
||||
}
|
||||
|
||||
/*
|
||||
* Per OWASP recommendations, we'll use hex entities for any other
|
||||
* characters where a named entity does not exist.
|
||||
*/
|
||||
return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8'));
|
||||
}, $string);
|
||||
|
||||
if ('UTF-8' !== $charset) {
|
||||
$string = iconv('UTF-8', $charset, $string);
|
||||
}
|
||||
|
||||
return $string;
|
||||
|
||||
case 'url':
|
||||
return rawurlencode($string);
|
||||
|
||||
default:
|
||||
// check the ones set on CoreExtension for BC (to be removed in 3.0)
|
||||
$legacyEscapers = $env->getExtension(CoreExtension::class)->getEscapers(false);
|
||||
if (array_key_exists($strategy, $legacyEscapers)) {
|
||||
return $legacyEscapers[$strategy]($env, $string, $charset);
|
||||
}
|
||||
|
||||
$escapers = $env->getExtension(EscaperExtension::class)->getEscapers();
|
||||
if (array_key_exists($strategy, $escapers)) {
|
||||
return $escapers[$strategy]($env, $string, $charset);
|
||||
}
|
||||
|
||||
$escapers = array_merge($legacyEscapers, $escapers);
|
||||
$validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers)));
|
||||
|
||||
throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
function twig_escape_filter_is_safe(Node $filterArgs)
|
||||
{
|
||||
foreach ($filterArgs as $arg) {
|
||||
if ($arg instanceof ConstantExpression) {
|
||||
return [$arg->getAttribute('value')];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return ['html'];
|
||||
}
|
||||
}
|
||||
|
@ -11,7 +11,6 @@
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
use Twig\TwigFilter;
|
||||
@ -25,15 +24,6 @@ use Twig\TwigTest;
|
||||
*/
|
||||
interface ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* Initializes the runtime environment.
|
||||
*
|
||||
* This is where you can load some file that contains filter functions for instance.
|
||||
*
|
||||
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_InitRuntimeInterface instead
|
||||
*/
|
||||
public function initRuntime(Environment $environment);
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
@ -75,24 +65,6 @@ interface ExtensionInterface
|
||||
* @return array<array> First array of unary operators, second array of binary operators
|
||||
*/
|
||||
public function getOperators();
|
||||
|
||||
/**
|
||||
* Returns a list of global variables to add to the existing list.
|
||||
*
|
||||
* @return array An array of global variables
|
||||
*
|
||||
* @deprecated since 1.23 (to be removed in 2.0), implement \Twig_Extension_GlobalsInterface instead
|
||||
*/
|
||||
public function getGlobals();
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*
|
||||
* @deprecated since 1.26 (to be removed in 2.0), not used anymore internally
|
||||
*/
|
||||
public function getName();
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\ExtensionInterface', 'Twig_ExtensionInterface');
|
||||
|
@ -21,6 +21,12 @@ namespace Twig\Extension;
|
||||
*/
|
||||
interface GlobalsInterface
|
||||
{
|
||||
/**
|
||||
* Returns a list of global variables to add to the existing list.
|
||||
*
|
||||
* @return array An array of global variables
|
||||
*/
|
||||
public function getGlobals();
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface');
|
||||
|
@ -11,6 +11,8 @@
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
|
||||
/**
|
||||
* Enables usage of the deprecated Twig\Extension\AbstractExtension::initRuntime() method.
|
||||
*
|
||||
@ -18,9 +20,17 @@ namespace Twig\Extension;
|
||||
* deprecated initRuntime() method in your extensions.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since Twig 2.7, to be removed in 3.0
|
||||
*/
|
||||
interface InitRuntimeInterface
|
||||
{
|
||||
/**
|
||||
* Initializes the runtime environment.
|
||||
*
|
||||
* This is where you can load some file that contains filter functions for instance.
|
||||
*/
|
||||
public function initRuntime(Environment $environment);
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface');
|
||||
|
@ -13,12 +13,9 @@ namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\OptimizerNodeVisitor;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class OptimizerExtension extends AbstractExtension
|
||||
final class OptimizerExtension extends AbstractExtension
|
||||
{
|
||||
protected $optimizers;
|
||||
private $optimizers;
|
||||
|
||||
public function __construct($optimizers = -1)
|
||||
{
|
||||
@ -29,11 +26,6 @@ class OptimizerExtension extends AbstractExtension
|
||||
{
|
||||
return [new OptimizerNodeVisitor($this->optimizers)];
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'optimizer';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\OptimizerExtension', 'Twig_Extension_Optimizer');
|
||||
|
@ -41,12 +41,7 @@ class ProfilerExtension extends AbstractExtension
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return [new ProfilerNodeVisitor(\get_class($this))];
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'profiler';
|
||||
return [new ProfilerNodeVisitor(static::class)];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -12,17 +12,17 @@
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\SandboxNodeVisitor;
|
||||
use Twig\Sandbox\SecurityNotAllowedMethodError;
|
||||
use Twig\Sandbox\SecurityNotAllowedPropertyError;
|
||||
use Twig\Sandbox\SecurityPolicyInterface;
|
||||
use Twig\Source;
|
||||
use Twig\TokenParser\SandboxTokenParser;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SandboxExtension extends AbstractExtension
|
||||
final class SandboxExtension extends AbstractExtension
|
||||
{
|
||||
protected $sandboxedGlobally;
|
||||
protected $sandboxed;
|
||||
protected $policy;
|
||||
private $sandboxedGlobally;
|
||||
private $sandboxed;
|
||||
private $policy;
|
||||
|
||||
public function __construct(SecurityPolicyInterface $policy, $sandboxed = false)
|
||||
{
|
||||
@ -77,33 +77,49 @@ class SandboxExtension extends AbstractExtension
|
||||
}
|
||||
}
|
||||
|
||||
public function checkMethodAllowed($obj, $method)
|
||||
public function checkMethodAllowed($obj, $method, int $lineno = -1, Source $source = null)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkMethodAllowed($obj, $method);
|
||||
try {
|
||||
$this->policy->checkMethodAllowed($obj, $method);
|
||||
} catch (SecurityNotAllowedMethodError $e) {
|
||||
$e->setSourceContext($source);
|
||||
$e->setTemplateLine($lineno);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPropertyAllowed($obj, $method)
|
||||
public function checkPropertyAllowed($obj, $property, int $lineno = -1, Source $source = null)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkPropertyAllowed($obj, $method);
|
||||
try {
|
||||
$this->policy->checkPropertyAllowed($obj, $property);
|
||||
} catch (SecurityNotAllowedPropertyError $e) {
|
||||
$e->setSourceContext($source);
|
||||
$e->setTemplateLine($lineno);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function ensureToStringAllowed($obj)
|
||||
public function ensureToStringAllowed($obj, int $lineno = -1, Source $source = null)
|
||||
{
|
||||
if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) {
|
||||
$this->policy->checkMethodAllowed($obj, '__toString');
|
||||
try {
|
||||
$this->policy->checkMethodAllowed($obj, '__toString');
|
||||
} catch (SecurityNotAllowedMethodError $e) {
|
||||
$e->setSourceContext($source);
|
||||
$e->setTemplateLine($lineno);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'sandbox';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox');
|
||||
|
@ -13,32 +13,32 @@ namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* Internal class.
|
||||
*
|
||||
* This class is used by \Twig\Environment as a staging area and must not be used directly.
|
||||
* Used by \Twig\Environment as a staging area.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
class StagingExtension extends AbstractExtension
|
||||
final class StagingExtension extends AbstractExtension
|
||||
{
|
||||
protected $functions = [];
|
||||
protected $filters = [];
|
||||
protected $visitors = [];
|
||||
protected $tokenParsers = [];
|
||||
protected $globals = [];
|
||||
protected $tests = [];
|
||||
private $functions = [];
|
||||
private $filters = [];
|
||||
private $visitors = [];
|
||||
private $tokenParsers = [];
|
||||
private $tests = [];
|
||||
|
||||
public function addFunction($name, $function)
|
||||
public function addFunction(TwigFunction $function)
|
||||
{
|
||||
if (isset($this->functions[$name])) {
|
||||
@trigger_error(sprintf('Overriding function "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
|
||||
if (isset($this->functions[$function->getName()])) {
|
||||
throw new \LogicException(sprintf('Function "%s" is already registered.', $function->getName()));
|
||||
}
|
||||
|
||||
$this->functions[$name] = $function;
|
||||
$this->functions[$function->getName()] = $function;
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
@ -46,13 +46,13 @@ class StagingExtension extends AbstractExtension
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
public function addFilter($name, $filter)
|
||||
public function addFilter(TwigFilter $filter)
|
||||
{
|
||||
if (isset($this->filters[$name])) {
|
||||
@trigger_error(sprintf('Overriding filter "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
|
||||
if (isset($this->filters[$filter->getName()])) {
|
||||
throw new \LogicException(sprintf('Filter "%s" is already registered.', $filter->getName()));
|
||||
}
|
||||
|
||||
$this->filters[$name] = $filter;
|
||||
$this->filters[$filter->getName()] = $filter;
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
@ -73,7 +73,7 @@ class StagingExtension extends AbstractExtension
|
||||
public function addTokenParser(TokenParserInterface $parser)
|
||||
{
|
||||
if (isset($this->tokenParsers[$parser->getTag()])) {
|
||||
@trigger_error(sprintf('Overriding tag "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $parser->getTag()), E_USER_DEPRECATED);
|
||||
throw new \LogicException(sprintf('Tag "%s" is already registered.', $parser->getTag()));
|
||||
}
|
||||
|
||||
$this->tokenParsers[$parser->getTag()] = $parser;
|
||||
@ -84,34 +84,19 @@ class StagingExtension extends AbstractExtension
|
||||
return $this->tokenParsers;
|
||||
}
|
||||
|
||||
public function addGlobal($name, $value)
|
||||
public function addTest(TwigTest $test)
|
||||
{
|
||||
$this->globals[$name] = $value;
|
||||
}
|
||||
|
||||
public function getGlobals()
|
||||
{
|
||||
return $this->globals;
|
||||
}
|
||||
|
||||
public function addTest($name, $test)
|
||||
{
|
||||
if (isset($this->tests[$name])) {
|
||||
@trigger_error(sprintf('Overriding test "%s" that is already registered is deprecated since version 1.30 and won\'t be possible anymore in 2.0.', $name), E_USER_DEPRECATED);
|
||||
if (isset($this->tests[$test->getName()])) {
|
||||
throw new \LogicException(sprintf('Test "%s" is already registered.', $test->getName()));
|
||||
}
|
||||
|
||||
$this->tests[$name] = $test;
|
||||
$this->tests[$test->getName()] = $test;
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'staging';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\StagingExtension', 'Twig_Extension_Staging');
|
||||
|
@ -12,10 +12,7 @@
|
||||
namespace Twig\Extension {
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class StringLoaderExtension extends AbstractExtension
|
||||
final class StringLoaderExtension extends AbstractExtension
|
||||
{
|
||||
public function getFunctions()
|
||||
{
|
||||
@ -23,11 +20,6 @@ class StringLoaderExtension extends AbstractExtension
|
||||
new TwigFunction('template_from_string', 'twig_template_from_string', ['needs_environment' => true]),
|
||||
];
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'string_loader';
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Extension\StringLoaderExtension', 'Twig_Extension_StringLoader');
|
||||
@ -47,7 +39,7 @@ use Twig\TemplateWrapper;
|
||||
*
|
||||
* @return TemplateWrapper
|
||||
*/
|
||||
function twig_template_from_string(Environment $env, $template, $name = null)
|
||||
function twig_template_from_string(Environment $env, $template, string $name = null)
|
||||
{
|
||||
return $env->createTemplate((string) $template, $name);
|
||||
}
|
||||
|
475
system/libs/Twig/ExtensionSet.php
Normal file
475
system/libs/Twig/ExtensionSet.php
Normal file
@ -0,0 +1,475 @@
|
||||
<?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;
|
||||
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Extension\ExtensionInterface;
|
||||
use Twig\Extension\GlobalsInterface;
|
||||
use Twig\Extension\InitRuntimeInterface;
|
||||
use Twig\Extension\StagingExtension;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class ExtensionSet
|
||||
{
|
||||
private $extensions;
|
||||
private $initialized = false;
|
||||
private $runtimeInitialized = false;
|
||||
private $staging;
|
||||
private $parsers;
|
||||
private $visitors;
|
||||
private $filters;
|
||||
private $tests;
|
||||
private $functions;
|
||||
private $unaryOperators;
|
||||
private $binaryOperators;
|
||||
private $globals;
|
||||
private $functionCallbacks = [];
|
||||
private $filterCallbacks = [];
|
||||
private $lastModified = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->staging = new StagingExtension();
|
||||
}
|
||||
|
||||
/**
|
||||
* Initializes the runtime environment.
|
||||
*
|
||||
* @deprecated since Twig 2.7
|
||||
*/
|
||||
public function initRuntime(Environment $env)
|
||||
{
|
||||
if ($this->runtimeInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->runtimeInitialized = true;
|
||||
|
||||
foreach ($this->extensions as $extension) {
|
||||
if ($extension instanceof InitRuntimeInterface) {
|
||||
$extension->initRuntime($env);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function hasExtension(string $class): bool
|
||||
{
|
||||
$class = ltrim($class, '\\');
|
||||
if (!isset($this->extensions[$class]) && class_exists($class, false)) {
|
||||
// For BC/FC with namespaced aliases
|
||||
$class = (new \ReflectionClass($class))->name;
|
||||
}
|
||||
|
||||
return isset($this->extensions[$class]);
|
||||
}
|
||||
|
||||
public function getExtension(string $class): ExtensionInterface
|
||||
{
|
||||
$class = ltrim($class, '\\');
|
||||
if (!isset($this->extensions[$class]) && class_exists($class, false)) {
|
||||
// For BC/FC with namespaced aliases
|
||||
$class = (new \ReflectionClass($class))->name;
|
||||
}
|
||||
|
||||
if (!isset($this->extensions[$class])) {
|
||||
throw new RuntimeError(sprintf('The "%s" extension is not enabled.', $class));
|
||||
}
|
||||
|
||||
return $this->extensions[$class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ExtensionInterface[] $extensions
|
||||
*/
|
||||
public function setExtensions(array $extensions)
|
||||
{
|
||||
foreach ($extensions as $extension) {
|
||||
$this->addExtension($extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExtensionInterface[]
|
||||
*/
|
||||
public function getExtensions(): array
|
||||
{
|
||||
return $this->extensions;
|
||||
}
|
||||
|
||||
public function getSignature(): string
|
||||
{
|
||||
return json_encode(array_keys($this->extensions));
|
||||
}
|
||||
|
||||
public function isInitialized(): bool
|
||||
{
|
||||
return $this->initialized || $this->runtimeInitialized;
|
||||
}
|
||||
|
||||
public function getLastModified(): int
|
||||
{
|
||||
if (0 !== $this->lastModified) {
|
||||
return $this->lastModified;
|
||||
}
|
||||
|
||||
foreach ($this->extensions as $extension) {
|
||||
$r = new \ReflectionObject($extension);
|
||||
if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModified) {
|
||||
$this->lastModified = $extensionTime;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->lastModified;
|
||||
}
|
||||
|
||||
public function addExtension(ExtensionInterface $extension)
|
||||
{
|
||||
$class = \get_class($extension);
|
||||
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class));
|
||||
}
|
||||
|
||||
if (isset($this->extensions[$class])) {
|
||||
throw new \LogicException(sprintf('Unable to register extension "%s" as it is already registered.', $class));
|
||||
}
|
||||
|
||||
$this->extensions[$class] = $extension;
|
||||
}
|
||||
|
||||
public function addFunction(TwigFunction $function)
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName()));
|
||||
}
|
||||
|
||||
$this->staging->addFunction($function);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFunction[]
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFunction|false
|
||||
*/
|
||||
public function getFunction(string $name)
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->functions[$name])) {
|
||||
return $this->functions[$name];
|
||||
}
|
||||
|
||||
foreach ($this->functions as $pattern => $function) {
|
||||
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
|
||||
|
||||
if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) {
|
||||
array_shift($matches);
|
||||
$function->setArguments($matches);
|
||||
|
||||
return $function;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->functionCallbacks as $callback) {
|
||||
if (false !== $function = $callback($name)) {
|
||||
return $function;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function registerUndefinedFunctionCallback(callable $callable)
|
||||
{
|
||||
$this->functionCallbacks[] = $callable;
|
||||
}
|
||||
|
||||
public function addFilter(TwigFilter $filter)
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $filter->getName()));
|
||||
}
|
||||
|
||||
$this->staging->addFilter($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFilter[]
|
||||
*/
|
||||
public function getFilters(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFilter|false
|
||||
*/
|
||||
public function getFilter(string $name)
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->filters[$name])) {
|
||||
return $this->filters[$name];
|
||||
}
|
||||
|
||||
foreach ($this->filters as $pattern => $filter) {
|
||||
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
|
||||
|
||||
if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) {
|
||||
array_shift($matches);
|
||||
$filter->setArguments($matches);
|
||||
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->filterCallbacks as $callback) {
|
||||
if (false !== $filter = $callback($name)) {
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function registerUndefinedFilterCallback(callable $callable)
|
||||
{
|
||||
$this->filterCallbacks[] = $callable;
|
||||
}
|
||||
|
||||
public function addNodeVisitor(NodeVisitorInterface $visitor)
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.');
|
||||
}
|
||||
|
||||
$this->staging->addNodeVisitor($visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NodeVisitorInterface[]
|
||||
*/
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->visitors;
|
||||
}
|
||||
|
||||
public function addTokenParser(TokenParserInterface $parser)
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException('Unable to add a token parser as extensions have already been initialized.');
|
||||
}
|
||||
|
||||
$this->staging->addTokenParser($parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TokenParserInterface[]
|
||||
*/
|
||||
public function getTokenParsers(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->parsers;
|
||||
}
|
||||
|
||||
public function getGlobals(): array
|
||||
{
|
||||
if (null !== $this->globals) {
|
||||
return $this->globals;
|
||||
}
|
||||
|
||||
$globals = [];
|
||||
foreach ($this->extensions as $extension) {
|
||||
if (!$extension instanceof GlobalsInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$extGlobals = $extension->getGlobals();
|
||||
if (!\is_array($extGlobals)) {
|
||||
throw new \UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', \get_class($extension)));
|
||||
}
|
||||
|
||||
$globals = array_merge($globals, $extGlobals);
|
||||
}
|
||||
|
||||
if ($this->initialized) {
|
||||
$this->globals = $globals;
|
||||
}
|
||||
|
||||
return $globals;
|
||||
}
|
||||
|
||||
public function addTest(TwigTest $test)
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName()));
|
||||
}
|
||||
|
||||
$this->staging->addTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigTest[]
|
||||
*/
|
||||
public function getTests(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigTest|false
|
||||
*/
|
||||
public function getTest(string $name)
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->tests[$name])) {
|
||||
return $this->tests[$name];
|
||||
}
|
||||
|
||||
foreach ($this->tests as $pattern => $test) {
|
||||
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
|
||||
|
||||
if ($count) {
|
||||
if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
|
||||
array_shift($matches);
|
||||
$test->setArguments($matches);
|
||||
|
||||
return $test;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getUnaryOperators(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->unaryOperators;
|
||||
}
|
||||
|
||||
public function getBinaryOperators(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->binaryOperators;
|
||||
}
|
||||
|
||||
private function initExtensions()
|
||||
{
|
||||
$this->parsers = [];
|
||||
$this->filters = [];
|
||||
$this->functions = [];
|
||||
$this->tests = [];
|
||||
$this->visitors = [];
|
||||
$this->unaryOperators = [];
|
||||
$this->binaryOperators = [];
|
||||
|
||||
foreach ($this->extensions as $extension) {
|
||||
$this->initExtension($extension);
|
||||
}
|
||||
$this->initExtension($this->staging);
|
||||
// Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
private function initExtension(ExtensionInterface $extension)
|
||||
{
|
||||
// filters
|
||||
foreach ($extension->getFilters() as $filter) {
|
||||
$this->filters[$filter->getName()] = $filter;
|
||||
}
|
||||
|
||||
// functions
|
||||
foreach ($extension->getFunctions() as $function) {
|
||||
$this->functions[$function->getName()] = $function;
|
||||
}
|
||||
|
||||
// tests
|
||||
foreach ($extension->getTests() as $test) {
|
||||
$this->tests[$test->getName()] = $test;
|
||||
}
|
||||
|
||||
// token parsers
|
||||
foreach ($extension->getTokenParsers() as $parser) {
|
||||
if (!$parser instanceof TokenParserInterface) {
|
||||
throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.');
|
||||
}
|
||||
|
||||
$this->parsers[] = $parser;
|
||||
}
|
||||
|
||||
// node visitors
|
||||
foreach ($extension->getNodeVisitors() as $visitor) {
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
// operators
|
||||
if ($operators = $extension->getOperators()) {
|
||||
if (!\is_array($operators)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', \get_class($extension), \is_object($operators) ? \get_class($operators) : \gettype($operators).(\is_resource($operators) ? '' : '#'.$operators)));
|
||||
}
|
||||
|
||||
if (2 !== \count($operators)) {
|
||||
throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', \get_class($extension), \count($operators)));
|
||||
}
|
||||
|
||||
$this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
|
||||
$this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\ExtensionSet', 'Twig_ExtensionSet');
|
@ -41,7 +41,7 @@ class FileExtensionEscapingStrategy
|
||||
$name = substr($name, 0, -5);
|
||||
}
|
||||
|
||||
$extension = pathinfo($name, PATHINFO_EXTENSION);
|
||||
$extension = pathinfo($name, \PATHINFO_EXTENSION);
|
||||
|
||||
switch ($extension) {
|
||||
case 'js':
|
||||
|
@ -19,39 +19,36 @@ use Twig\Error\SyntaxError;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Lexer implements \Twig_LexerInterface
|
||||
class Lexer
|
||||
{
|
||||
protected $tokens;
|
||||
protected $code;
|
||||
protected $cursor;
|
||||
protected $lineno;
|
||||
protected $end;
|
||||
protected $state;
|
||||
protected $states;
|
||||
protected $brackets;
|
||||
protected $env;
|
||||
// to be renamed to $name in 2.0 (where it is private)
|
||||
protected $filename;
|
||||
protected $options;
|
||||
protected $regexes;
|
||||
protected $position;
|
||||
protected $positions;
|
||||
protected $currentVarBlockLine;
|
||||
|
||||
private $tokens;
|
||||
private $code;
|
||||
private $cursor;
|
||||
private $lineno;
|
||||
private $end;
|
||||
private $state;
|
||||
private $states;
|
||||
private $brackets;
|
||||
private $env;
|
||||
private $source;
|
||||
private $options;
|
||||
private $regexes;
|
||||
private $position;
|
||||
private $positions;
|
||||
private $currentVarBlockLine;
|
||||
|
||||
const STATE_DATA = 0;
|
||||
const STATE_BLOCK = 1;
|
||||
const STATE_VAR = 2;
|
||||
const STATE_STRING = 3;
|
||||
const STATE_INTERPOLATION = 4;
|
||||
public const STATE_DATA = 0;
|
||||
public const STATE_BLOCK = 1;
|
||||
public const STATE_VAR = 2;
|
||||
public const STATE_STRING = 3;
|
||||
public const STATE_INTERPOLATION = 4;
|
||||
|
||||
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
|
||||
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A';
|
||||
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
||||
const REGEX_DQ_STRING_DELIM = '/"/A';
|
||||
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
|
||||
const PUNCTUATION = '()[]{}?:.,|';
|
||||
public const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
|
||||
public const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A';
|
||||
public const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
||||
public const REGEX_DQ_STRING_DELIM = '/"/A';
|
||||
public const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
|
||||
public const PUNCTUATION = '()[]{}?:.,|';
|
||||
|
||||
public function __construct(Environment $env, array $options = [])
|
||||
{
|
||||
@ -100,9 +97,7 @@ class Lexer implements \Twig_LexerInterface
|
||||
$this->options['whitespace_trim']. // -
|
||||
'|'.
|
||||
$this->options['whitespace_line_trim']. // ~
|
||||
')?\s*'.
|
||||
'(?:end%s)'. // endraw or endverbatim
|
||||
'\s*'.
|
||||
')?\s*endverbatim\s*'.
|
||||
'(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}
|
||||
'|'.
|
||||
@ -117,7 +112,7 @@ class Lexer implements \Twig_LexerInterface
|
||||
// #}
|
||||
'lex_comment' => '{
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim']).preg_quote($this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n?
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n?
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~#}[ \t\0\x0B]*
|
||||
'|'.
|
||||
@ -127,9 +122,7 @@ class Lexer implements \Twig_LexerInterface
|
||||
|
||||
// verbatim %}
|
||||
'lex_block_raw' => '{
|
||||
\s*
|
||||
(raw|verbatim)
|
||||
\s*
|
||||
\s*verbatim\s*
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}\s*
|
||||
'|'.
|
||||
@ -160,28 +153,10 @@ class Lexer implements \Twig_LexerInterface
|
||||
];
|
||||
}
|
||||
|
||||
public function tokenize($code, $name = null)
|
||||
public function tokenize(Source $source)
|
||||
{
|
||||
if (!$code instanceof Source) {
|
||||
@trigger_error(sprintf('Passing a string as the $code argument of %s() is deprecated since version 1.27 and will be removed in 2.0. Pass a \Twig\Source instance instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
$this->source = new Source($code, $name);
|
||||
} else {
|
||||
$this->source = $code;
|
||||
}
|
||||
|
||||
if (((int) ini_get('mbstring.func_overload')) & 2) {
|
||||
@trigger_error('Support for having "mbstring.func_overload" different from 0 is deprecated version 1.29 and will be removed in 2.0.', E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if (\function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
|
||||
$mbEncoding = mb_internal_encoding();
|
||||
mb_internal_encoding('ASCII');
|
||||
} else {
|
||||
$mbEncoding = null;
|
||||
}
|
||||
|
||||
$this->code = str_replace(["\r\n", "\r"], "\n", $this->source->getCode());
|
||||
$this->filename = $this->source->getName();
|
||||
$this->source = $source;
|
||||
$this->code = str_replace(["\r\n", "\r"], "\n", $source->getCode());
|
||||
$this->cursor = 0;
|
||||
$this->lineno = 1;
|
||||
$this->end = \strlen($this->code);
|
||||
@ -192,7 +167,7 @@ class Lexer implements \Twig_LexerInterface
|
||||
$this->position = -1;
|
||||
|
||||
// find all token starts in one go
|
||||
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
|
||||
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, \PREG_OFFSET_CAPTURE);
|
||||
$this->positions = $matches;
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
@ -221,25 +196,21 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::EOF_TYPE);
|
||||
$this->pushToken(/* Token::EOF_TYPE */ -1);
|
||||
|
||||
if (!empty($this->brackets)) {
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
|
||||
}
|
||||
|
||||
if ($mbEncoding) {
|
||||
mb_internal_encoding($mbEncoding);
|
||||
}
|
||||
|
||||
return new TokenStream($this->tokens, $this->source);
|
||||
}
|
||||
|
||||
protected function lexData()
|
||||
private function lexData()
|
||||
{
|
||||
// if no matches are left we return the rest of the template as simple text token
|
||||
if ($this->position == \count($this->positions[0]) - 1) {
|
||||
$this->pushToken(Token::TEXT_TYPE, substr($this->code, $this->cursor));
|
||||
$this->pushToken(/* Token::TEXT_TYPE */ 0, substr($this->code, $this->cursor));
|
||||
$this->cursor = $this->end;
|
||||
|
||||
return;
|
||||
@ -268,7 +239,7 @@ class Lexer implements \Twig_LexerInterface
|
||||
$text = rtrim($text, " \t\0\x0B");
|
||||
}
|
||||
}
|
||||
$this->pushToken(Token::TEXT_TYPE, $text);
|
||||
$this->pushToken(/* Token::TEXT_TYPE */ 0, $text);
|
||||
$this->moveCursor($textContent.$position[0]);
|
||||
|
||||
switch ($this->positions[1][$this->position][0]) {
|
||||
@ -280,30 +251,30 @@ class Lexer implements \Twig_LexerInterface
|
||||
// raw data?
|
||||
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lexRawData($match[1]);
|
||||
$this->lexRawData();
|
||||
// {% line \d+ %}
|
||||
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lineno = (int) $match[1];
|
||||
} else {
|
||||
$this->pushToken(Token::BLOCK_START_TYPE);
|
||||
$this->pushToken(/* Token::BLOCK_START_TYPE */ 1);
|
||||
$this->pushState(self::STATE_BLOCK);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
}
|
||||
break;
|
||||
|
||||
case $this->options['tag_variable'][0]:
|
||||
$this->pushToken(Token::VAR_START_TYPE);
|
||||
$this->pushToken(/* Token::VAR_START_TYPE */ 2);
|
||||
$this->pushState(self::STATE_VAR);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexBlock()
|
||||
private function lexBlock()
|
||||
{
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::BLOCK_END_TYPE);
|
||||
$this->pushToken(/* Token::BLOCK_END_TYPE */ 3);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
@ -311,10 +282,10 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexVar()
|
||||
private function lexVar()
|
||||
{
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::VAR_END_TYPE);
|
||||
$this->pushToken(/* Token::VAR_END_TYPE */ 4);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
@ -322,7 +293,7 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexExpression()
|
||||
private function lexExpression()
|
||||
{
|
||||
// whitespace
|
||||
if (preg_match('/\s+/A', $this->code, $match, 0, $this->cursor)) {
|
||||
@ -340,21 +311,21 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
// operators
|
||||
elseif (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
|
||||
$this->pushToken(/* Token::OPERATOR_TYPE */ 8, preg_replace('/\s+/', ' ', $match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// names
|
||||
elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::NAME_TYPE, $match[0]);
|
||||
$this->pushToken(/* Token::NAME_TYPE */ 5, $match[0]);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// numbers
|
||||
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) {
|
||||
$number = (float) $match[0]; // floats
|
||||
if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
|
||||
if (ctype_digit($match[0]) && $number <= \PHP_INT_MAX) {
|
||||
$number = (int) $match[0]; // integers lower than the maximum
|
||||
}
|
||||
$this->pushToken(Token::NUMBER_TYPE, $number);
|
||||
$this->pushToken(/* Token::NUMBER_TYPE */ 6, $number);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// punctuation
|
||||
@ -375,12 +346,12 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
|
||||
$this->pushToken(/* Token::PUNCTUATION_TYPE */ 9, $this->code[$this->cursor]);
|
||||
++$this->cursor;
|
||||
}
|
||||
// strings
|
||||
elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
|
||||
$this->pushToken(/* Token::STRING_TYPE */ 7, stripcslashes(substr($match[0], 1, -1)));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// opening double quoted string
|
||||
@ -395,14 +366,10 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexRawData($tag)
|
||||
private function lexRawData()
|
||||
{
|
||||
if ('raw' === $tag) {
|
||||
@trigger_error(sprintf('Twig Tag "raw" is deprecated since version 1.21. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new SyntaxError(sprintf('Unexpected end of file: Unclosed "%s" block.', $tag), $this->lineno, $this->source);
|
||||
if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new SyntaxError('Unexpected end of file: Unclosed "verbatim" block.', $this->lineno, $this->source);
|
||||
}
|
||||
|
||||
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
|
||||
@ -420,27 +387,27 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::TEXT_TYPE, $text);
|
||||
$this->pushToken(/* Token::TEXT_TYPE */ 0, $text);
|
||||
}
|
||||
|
||||
protected function lexComment()
|
||||
private function lexComment()
|
||||
{
|
||||
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source);
|
||||
}
|
||||
|
||||
$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
|
||||
}
|
||||
|
||||
protected function lexString()
|
||||
private function lexString()
|
||||
{
|
||||
if (preg_match($this->regexes['interpolation_start'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->brackets[] = [$this->options['interpolation'][0], $this->lineno];
|
||||
$this->pushToken(Token::INTERPOLATION_START_TYPE);
|
||||
$this->pushToken(/* Token::INTERPOLATION_START_TYPE */ 10);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->pushState(self::STATE_INTERPOLATION);
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && \strlen($match[0]) > 0) {
|
||||
$this->pushToken(Token::STRING_TYPE, stripcslashes($match[0]));
|
||||
$this->pushToken(/* Token::STRING_TYPE */ 7, stripcslashes($match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
@ -456,12 +423,12 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexInterpolation()
|
||||
private function lexInterpolation()
|
||||
{
|
||||
$bracket = end($this->brackets);
|
||||
if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, 0, $this->cursor)) {
|
||||
array_pop($this->brackets);
|
||||
$this->pushToken(Token::INTERPOLATION_END_TYPE);
|
||||
$this->pushToken(/* Token::INTERPOLATION_END_TYPE */ 11);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
@ -469,23 +436,23 @@ class Lexer implements \Twig_LexerInterface
|
||||
}
|
||||
}
|
||||
|
||||
protected function pushToken($type, $value = '')
|
||||
private function pushToken($type, $value = '')
|
||||
{
|
||||
// do not push empty text tokens
|
||||
if (Token::TEXT_TYPE === $type && '' === $value) {
|
||||
if (/* Token::TEXT_TYPE */ 0 === $type && '' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tokens[] = new Token($type, $value, $this->lineno);
|
||||
}
|
||||
|
||||
protected function moveCursor($text)
|
||||
private function moveCursor($text)
|
||||
{
|
||||
$this->cursor += \strlen($text);
|
||||
$this->lineno += substr_count($text, "\n");
|
||||
}
|
||||
|
||||
protected function getOperatorRegex()
|
||||
private function getOperatorRegex()
|
||||
{
|
||||
$operators = array_merge(
|
||||
['='],
|
||||
@ -499,11 +466,15 @@ class Lexer implements \Twig_LexerInterface
|
||||
$regex = [];
|
||||
foreach ($operators as $operator => $length) {
|
||||
// an operator that ends with a character must be followed by
|
||||
// a whitespace or a parenthesis
|
||||
// a whitespace, a parenthesis, an opening map [ or sequence {
|
||||
$r = preg_quote($operator, '/');
|
||||
if (ctype_alpha($operator[$length - 1])) {
|
||||
$r = preg_quote($operator, '/').'(?=[\s()])';
|
||||
} else {
|
||||
$r = preg_quote($operator, '/');
|
||||
$r .= '(?=[\s()\[{])';
|
||||
}
|
||||
|
||||
// an operator that begins with a character must not have a dot or pipe before
|
||||
if (ctype_alpha($operator[0])) {
|
||||
$r = '(?<![\.\|])'.$r;
|
||||
}
|
||||
|
||||
// an operator with a space can be any amount of whitespaces
|
||||
@ -515,13 +486,13 @@ class Lexer implements \Twig_LexerInterface
|
||||
return '/'.implode('|', $regex).'/A';
|
||||
}
|
||||
|
||||
protected function pushState($state)
|
||||
private function pushState($state)
|
||||
{
|
||||
$this->states[] = $this->state;
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
protected function popState()
|
||||
private function popState()
|
||||
{
|
||||
if (0 === \count($this->states)) {
|
||||
throw new \LogicException('Cannot pop state without a previous state.');
|
||||
|
@ -24,13 +24,11 @@ use Twig\Source;
|
||||
*
|
||||
* This loader should only be used for unit testing.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
final class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
{
|
||||
protected $templates = [];
|
||||
private $templates = [];
|
||||
|
||||
/**
|
||||
* @param array $templates An array of templates (keys are the names, and values are the source code)
|
||||
@ -48,19 +46,7 @@ class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceConte
|
||||
*/
|
||||
public function setTemplate($name, $template)
|
||||
{
|
||||
$this->templates[(string) $name] = $template;
|
||||
}
|
||||
|
||||
public function getSource($name)
|
||||
{
|
||||
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $this->templates[$name];
|
||||
$this->templates[$name] = $template;
|
||||
}
|
||||
|
||||
public function getSourceContext($name)
|
||||
@ -75,12 +61,11 @@ class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceConte
|
||||
|
||||
public function exists($name)
|
||||
{
|
||||
return isset($this->templates[(string) $name]);
|
||||
return isset($this->templates[$name]);
|
||||
}
|
||||
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
@ -90,7 +75,6 @@ class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceConte
|
||||
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
@ -12,19 +12,16 @@
|
||||
namespace Twig\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Loads templates from other loaders.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
final class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
{
|
||||
private $hasSourceCache = [];
|
||||
protected $loaders = [];
|
||||
private $loaders = [];
|
||||
|
||||
/**
|
||||
* @param LoaderInterface[] $loaders
|
||||
@ -50,40 +47,16 @@ class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceConte
|
||||
return $this->loaders;
|
||||
}
|
||||
|
||||
public function getSource($name)
|
||||
{
|
||||
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->getSource($name);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
|
||||
public function getSourceContext($name)
|
||||
{
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
if (!$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
return $loader->getSourceContext($name);
|
||||
}
|
||||
|
||||
return new Source($loader->getSource($name), $name);
|
||||
return $loader->getSourceContext($name);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = $e->getMessage();
|
||||
}
|
||||
@ -94,30 +67,13 @@ class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceConte
|
||||
|
||||
public function exists($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
if (isset($this->hasSourceCache[$name])) {
|
||||
return $this->hasSourceCache[$name];
|
||||
}
|
||||
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface) {
|
||||
if ($loader->exists($name)) {
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
if ($loader instanceof SourceContextLoaderInterface) {
|
||||
$loader->getSourceContext($name);
|
||||
} else {
|
||||
$loader->getSource($name);
|
||||
}
|
||||
|
||||
if ($loader->exists($name)) {
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
} catch (LoaderError $e) {
|
||||
}
|
||||
}
|
||||
|
||||
@ -128,7 +84,7 @@ class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceConte
|
||||
{
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
if (!$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -146,7 +102,7 @@ class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceConte
|
||||
{
|
||||
$exceptions = [];
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof ExistsLoaderInterface && !$loader->exists($name)) {
|
||||
if (!$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -12,22 +12,12 @@
|
||||
namespace Twig\Loader;
|
||||
|
||||
/**
|
||||
* Adds an exists() method for loaders.
|
||||
* Empty interface for Twig 1.x compatibility.
|
||||
*
|
||||
* @author Florin Patan <florinpatan@gmail.com>
|
||||
*
|
||||
* @deprecated since 1.12 (to be removed in 3.0)
|
||||
* @deprecated since Twig 2.7, to be removed in 3.0
|
||||
*/
|
||||
interface ExistsLoaderInterface
|
||||
interface ExistsLoaderInterface extends LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Check if we have the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name The name of the template to check if we can load
|
||||
*
|
||||
* @return bool If the template source code is handled by this loader or not
|
||||
*/
|
||||
public function exists($name);
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\ExistsLoaderInterface', 'Twig_ExistsLoaderInterface');
|
||||
|
@ -22,7 +22,7 @@ use Twig\Source;
|
||||
class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface
|
||||
{
|
||||
/** Identifier of the main namespace. */
|
||||
const MAIN_NAMESPACE = '__main__';
|
||||
public const MAIN_NAMESPACE = '__main__';
|
||||
|
||||
protected $paths = [];
|
||||
protected $cache = [];
|
||||
@ -34,10 +34,10 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
* @param string|null $rootPath The root path common to all relative paths (null for getcwd())
|
||||
*/
|
||||
public function __construct($paths = [], $rootPath = null)
|
||||
public function __construct($paths = [], string $rootPath = null)
|
||||
{
|
||||
$this->rootPath = (null === $rootPath ? getcwd() : $rootPath).\DIRECTORY_SEPARATOR;
|
||||
if (false !== $realPath = realpath($rootPath)) {
|
||||
if (null !== $rootPath && false !== ($realPath = realpath($rootPath))) {
|
||||
$this->rootPath = $realPath.\DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
@ -136,17 +136,6 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
}
|
||||
}
|
||||
|
||||
public function getSource($name)
|
||||
{
|
||||
@trigger_error(sprintf('Calling "getSource" on "%s" is deprecated since 1.27. Use getSourceContext() instead.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
if (null === ($path = $this->findTemplate($name)) || false === $path) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return file_get_contents($path);
|
||||
}
|
||||
|
||||
public function getSourceContext($name)
|
||||
{
|
||||
if (null === ($path = $this->findTemplate($name)) || false === $path) {
|
||||
@ -177,13 +166,7 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
return null !== ($path = $this->findTemplate($name, false)) && false !== $path;
|
||||
} catch (LoaderError $e) {
|
||||
@trigger_error(sprintf('In %s::findTemplate(), you must accept a second argument that when set to "false" returns "false" instead of throwing an exception. Not supporting this argument is deprecated since version 1.27.', \get_class($this)), E_USER_DEPRECATED);
|
||||
|
||||
return false;
|
||||
}
|
||||
return null !== ($path = $this->findTemplate($name, false)) && false !== $path;
|
||||
}
|
||||
|
||||
public function isFresh($name, $time)
|
||||
@ -199,13 +182,15 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
/**
|
||||
* Checks if the template can be found.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* In Twig 3.0, findTemplate must return a string or null (returning false won't work anymore).
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param bool $throw Whether to throw an exception when an error occurs
|
||||
*
|
||||
* @return string|false|null The template name or false/null
|
||||
*/
|
||||
protected function findTemplate($name)
|
||||
protected function findTemplate($name, $throw = true)
|
||||
{
|
||||
$throw = \func_num_args() > 1 ? func_get_arg(1) : true;
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
@ -221,9 +206,9 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
}
|
||||
|
||||
try {
|
||||
$this->validateName($name);
|
||||
|
||||
list($namespace, $shortname) = $this->parseName($name);
|
||||
|
||||
$this->validateName($shortname);
|
||||
} catch (LoaderError $e) {
|
||||
if (!$throw) {
|
||||
return false;
|
||||
@ -265,7 +250,12 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
throw new LoaderError($this->errorCache[$name]);
|
||||
}
|
||||
|
||||
protected function parseName($name, $default = self::MAIN_NAMESPACE)
|
||||
private function normalizeName($name)
|
||||
{
|
||||
return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
|
||||
}
|
||||
|
||||
private function parseName($name, $default = self::MAIN_NAMESPACE)
|
||||
{
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
@ -281,12 +271,7 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
return [$default, $name];
|
||||
}
|
||||
|
||||
protected function normalizeName($name)
|
||||
{
|
||||
return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
|
||||
}
|
||||
|
||||
protected function validateName($name)
|
||||
private function validateName($name)
|
||||
{
|
||||
if (false !== strpos($name, "\0")) {
|
||||
throw new LoaderError('A template name cannot contain NUL bytes.');
|
||||
@ -312,10 +297,10 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source
|
||||
{
|
||||
return strspn($file, '/\\', 0, 1)
|
||||
|| (\strlen($file) > 3 && ctype_alpha($file[0])
|
||||
&& ':' === substr($file, 1, 1)
|
||||
&& ':' === $file[1]
|
||||
&& strspn($file, '/\\', 2, 1)
|
||||
)
|
||||
|| null !== parse_url($file, PHP_URL_SCHEME)
|
||||
|| null !== parse_url($file, \PHP_URL_SCHEME)
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Twig\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Interface all loaders must implement.
|
||||
@ -21,17 +22,15 @@ use Twig\Error\LoaderError;
|
||||
interface LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
* Returns the source context for a given template logical name.
|
||||
*
|
||||
* @param string $name The name of the template to load
|
||||
* @param string $name The template logical name
|
||||
*
|
||||
* @return string The template source code
|
||||
* @return Source
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*
|
||||
* @deprecated since 1.27 (to be removed in 2.0), implement Twig\Loader\SourceContextLoaderInterface
|
||||
*/
|
||||
public function getSource($name);
|
||||
public function getSourceContext($name);
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
@ -56,6 +55,15 @@ interface LoaderInterface
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function isFresh($name, $time);
|
||||
|
||||
/**
|
||||
* Check if we have the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name The name of the template to check if we can load
|
||||
*
|
||||
* @return bool If the template source code is handled by this loader or not
|
||||
*/
|
||||
public function exists($name);
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\LoaderInterface', 'Twig_LoaderInterface');
|
||||
|
@ -11,28 +11,11 @@
|
||||
|
||||
namespace Twig\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Adds a getSourceContext() method for loaders.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since 1.27 (to be removed in 3.0)
|
||||
* Empty interface for Twig 1.x compatibility.
|
||||
*/
|
||||
interface SourceContextLoaderInterface
|
||||
interface SourceContextLoaderInterface extends LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Returns the source context for a given template logical name.
|
||||
*
|
||||
* @param string $name The template logical name
|
||||
*
|
||||
* @return Source
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function getSourceContext($name);
|
||||
}
|
||||
|
||||
class_alias('Twig\Loader\SourceContextLoaderInterface', 'Twig_SourceContextLoaderInterface');
|
||||
|
@ -16,10 +16,10 @@ namespace Twig;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Markup implements \Countable
|
||||
class Markup implements \Countable, \JsonSerializable
|
||||
{
|
||||
protected $content;
|
||||
protected $charset;
|
||||
private $content;
|
||||
private $charset;
|
||||
|
||||
public function __construct($content, $charset)
|
||||
{
|
||||
@ -32,9 +32,22 @@ class Markup implements \Countable
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : \strlen($this->content);
|
||||
return mb_strlen($this->content, $this->charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -26,7 +26,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class AutoEscapeNode extends Node
|
||||
{
|
||||
public function __construct($value, \Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
|
||||
public function __construct($value, Node $body, int $lineno, string $tag = 'autoescape')
|
||||
{
|
||||
parent::__construct(['body' => $body], ['value' => $value], $lineno, $tag);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class BlockNode extends Node
|
||||
{
|
||||
public function __construct($name, \Twig_NodeInterface $body, $lineno, $tag = null)
|
||||
public function __construct(string $name, Node $body, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['body' => $body], ['name' => $name], $lineno, $tag);
|
||||
}
|
||||
@ -32,6 +32,7 @@ class BlockNode extends Node
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function block_%s(\$context, array \$blocks = [])\n", $this->getAttribute('name')), "{\n")
|
||||
->indent()
|
||||
->write("\$macros = \$this->macros;\n")
|
||||
;
|
||||
|
||||
$compiler
|
||||
|
@ -21,7 +21,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class BlockReferenceNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
public function __construct(string $name, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct([], ['name' => $name], $lineno, $tag);
|
||||
}
|
||||
|
28
system/libs/Twig/Node/CheckSecurityCallNode.php
Normal file
28
system/libs/Twig/Node/CheckSecurityCallNode.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?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\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class CheckSecurityCallNode extends Node
|
||||
{
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n")
|
||||
->write("\$this->checkSecurity();\n")
|
||||
;
|
||||
}
|
||||
}
|
@ -18,9 +18,9 @@ use Twig\Compiler;
|
||||
*/
|
||||
class CheckSecurityNode extends Node
|
||||
{
|
||||
protected $usedFilters;
|
||||
protected $usedTags;
|
||||
protected $usedFunctions;
|
||||
private $usedFilters;
|
||||
private $usedTags;
|
||||
private $usedFunctions;
|
||||
|
||||
public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
|
||||
{
|
||||
@ -45,10 +45,13 @@ class CheckSecurityNode extends Node
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n")
|
||||
->write('$tags = ')->repr(array_filter($tags))->raw(";\n")
|
||||
->write('$filters = ')->repr(array_filter($filters))->raw(";\n")
|
||||
->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n")
|
||||
->write("\n")
|
||||
->write("public function checkSecurity()\n")
|
||||
->write("{\n")
|
||||
->indent()
|
||||
->write('static $tags = ')->repr(array_filter($tags))->raw(";\n")
|
||||
->write('static $filters = ')->repr(array_filter($filters))->raw(";\n")
|
||||
->write('static $functions = ')->repr(array_filter($functions))->raw(";\n\n")
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->write("\$this->sandbox->checkSecurity(\n")
|
||||
@ -61,7 +64,7 @@ class CheckSecurityNode extends Node
|
||||
->outdent()
|
||||
->write("} catch (SecurityError \$e) {\n")
|
||||
->indent()
|
||||
->write("\$e->setSourceContext(\$this->getSourceContext());\n\n")
|
||||
->write("\$e->setSourceContext(\$this->source);\n\n")
|
||||
->write("if (\$e instanceof SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
|
||||
@ -78,6 +81,8 @@ class CheckSecurityNode extends Node
|
||||
->write("throw \$e;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -33,10 +33,13 @@ class CheckToStringNode extends AbstractExpression
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$expr = $this->getNode('expr');
|
||||
$compiler
|
||||
->raw('$this->sandbox->ensureToStringAllowed(')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(')')
|
||||
->subcompile($expr)
|
||||
->raw(', ')
|
||||
->repr($expr->getTemplateLine())
|
||||
->raw(', $this->source)')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -22,7 +22,7 @@ use Twig\Node\Expression\ConstantExpression;
|
||||
*/
|
||||
class DeprecatedNode extends Node
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
|
||||
public function __construct(AbstractExpression $expr, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $lineno, $tag);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use Twig\Node\Expression\AbstractExpression;
|
||||
*/
|
||||
class DoNode extends Node
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
|
||||
public function __construct(AbstractExpression $expr, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $lineno, $tag);
|
||||
}
|
||||
|
@ -23,13 +23,11 @@ use Twig\Node\Expression\ConstantExpression;
|
||||
class EmbedNode extends IncludeNode
|
||||
{
|
||||
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
|
||||
public function __construct($name, $index, AbstractExpression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
|
||||
public function __construct(string $name, int $index, ?AbstractExpression $variables, bool $only, bool $ignoreMissing, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(new ConstantExpression('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
// to be removed in 2.0, used name instead
|
||||
$this->setAttribute('filename', $name);
|
||||
$this->setAttribute('index', $index);
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,9 @@ use Twig\Compiler;
|
||||
|
||||
class ArrayExpression extends AbstractExpression
|
||||
{
|
||||
protected $index;
|
||||
private $index;
|
||||
|
||||
public function __construct(array $elements, $lineno)
|
||||
public function __construct(array $elements, int $lineno)
|
||||
{
|
||||
parent::__construct($elements, [], $lineno);
|
||||
|
||||
|
@ -44,7 +44,7 @@ class ArrowFunctionExpression extends AbstractExpression
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
->raw(') use ($context) { ')
|
||||
->raw(') use ($context, $macros) { ')
|
||||
;
|
||||
foreach ($this->getNode('names') as $name) {
|
||||
$compiler
|
||||
|
@ -14,10 +14,11 @@ namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
abstract class AbstractBinary extends AbstractExpression
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $left, \Twig_NodeInterface $right, $lineno)
|
||||
public function __construct(Node $left, Node $right, int $lineno)
|
||||
{
|
||||
parent::__construct(['left' => $left, 'right' => $right], [], $lineno);
|
||||
}
|
||||
|
@ -15,21 +15,6 @@ use Twig\Compiler;
|
||||
|
||||
class PowerBinary extends AbstractBinary
|
||||
{
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 50600) {
|
||||
return parent::compile($compiler);
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('pow(')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('**');
|
||||
|
22
system/libs/Twig/Node/Expression/Binary/SpaceshipBinary.php
Normal file
22
system/libs/Twig/Node/Expression/Binary/SpaceshipBinary.php
Normal file
@ -0,0 +1,22 @@
|
||||
<?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\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
class SpaceshipBinary extends AbstractBinary
|
||||
{
|
||||
public function operator(Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('<=>');
|
||||
}
|
||||
}
|
@ -22,17 +22,8 @@ use Twig\Node\Node;
|
||||
*/
|
||||
class BlockReferenceExpression extends AbstractExpression
|
||||
{
|
||||
/**
|
||||
* @param Node|null $template
|
||||
*/
|
||||
public function __construct(\Twig_NodeInterface $name, $template = null, $lineno, $tag = null)
|
||||
public function __construct(Node $name, ?Node $template, int $lineno, string $tag = null)
|
||||
{
|
||||
if (\is_bool($template)) {
|
||||
@trigger_error(sprintf('The %s method "$asString" argument is deprecated since version 1.28 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
$template = null;
|
||||
}
|
||||
|
||||
$nodes = ['name' => $name];
|
||||
if (null !== $template) {
|
||||
$nodes['template'] = $template;
|
||||
@ -58,7 +49,7 @@ class BlockReferenceExpression extends AbstractExpression
|
||||
}
|
||||
}
|
||||
|
||||
private function compileTemplateCall(Compiler $compiler, $method)
|
||||
private function compileTemplateCall(Compiler $compiler, string $method): Compiler
|
||||
{
|
||||
if (!$this->hasNode('template')) {
|
||||
$compiler->write('$this');
|
||||
@ -75,12 +66,11 @@ class BlockReferenceExpression extends AbstractExpression
|
||||
}
|
||||
|
||||
$compiler->raw(sprintf('->%s', $method));
|
||||
$this->compileBlockArguments($compiler);
|
||||
|
||||
return $compiler;
|
||||
return $this->compileBlockArguments($compiler);
|
||||
}
|
||||
|
||||
private function compileBlockArguments(Compiler $compiler)
|
||||
private function compileBlockArguments(Compiler $compiler): Compiler
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
|
@ -22,37 +22,37 @@ abstract class CallExpression extends AbstractExpression
|
||||
|
||||
protected function compileCallable(Compiler $compiler)
|
||||
{
|
||||
$closingParenthesis = false;
|
||||
$isArray = false;
|
||||
if ($this->hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
|
||||
if (\is_string($callable) && false === strpos($callable, '::')) {
|
||||
$compiler->raw($callable);
|
||||
} else {
|
||||
list($r, $callable) = $this->reflectCallable($callable);
|
||||
if ($r instanceof \ReflectionMethod && \is_string($callable[0])) {
|
||||
if ($r->isStatic()) {
|
||||
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
|
||||
} else {
|
||||
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
|
||||
}
|
||||
} elseif ($r instanceof \ReflectionMethod && $callable[0] instanceof ExtensionInterface) {
|
||||
$compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', \get_class($callable[0]), $callable[1]));
|
||||
} else {
|
||||
$type = ucfirst($this->getAttribute('type'));
|
||||
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), ', $type, $this->getAttribute('name')));
|
||||
$closingParenthesis = true;
|
||||
$isArray = true;
|
||||
}
|
||||
}
|
||||
$callable = $this->getAttribute('callable');
|
||||
|
||||
if (\is_string($callable) && false === strpos($callable, '::')) {
|
||||
$compiler->raw($callable);
|
||||
} else {
|
||||
$compiler->raw($this->getAttribute('thing')->compile());
|
||||
[$r, $callable] = $this->reflectCallable($callable);
|
||||
|
||||
if (\is_string($callable)) {
|
||||
$compiler->raw($callable);
|
||||
} elseif (\is_array($callable) && \is_string($callable[0])) {
|
||||
if (!$r instanceof \ReflectionMethod || $r->isStatic()) {
|
||||
$compiler->raw(sprintf('%s::%s', $callable[0], $callable[1]));
|
||||
} else {
|
||||
$compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1]));
|
||||
}
|
||||
} elseif (\is_array($callable) && $callable[0] instanceof ExtensionInterface) {
|
||||
$class = \get_class($callable[0]);
|
||||
if (!$compiler->getEnvironment()->hasExtension($class)) {
|
||||
// Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error
|
||||
$compiler->raw(sprintf('$this->env->getExtension(\'%s\')', $class));
|
||||
} else {
|
||||
$compiler->raw(sprintf('$this->extensions[\'%s\']', ltrim($class, '\\')));
|
||||
}
|
||||
|
||||
$compiler->raw(sprintf('->%s', $callable[1]));
|
||||
} else {
|
||||
$compiler->raw(sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $this->getAttribute('name')));
|
||||
}
|
||||
}
|
||||
|
||||
$this->compileArguments($compiler, $isArray);
|
||||
|
||||
if ($closingParenthesis) {
|
||||
$compiler->raw(')');
|
||||
}
|
||||
$this->compileArguments($compiler);
|
||||
}
|
||||
|
||||
protected function compileArguments(Compiler $compiler, $isArray = false)
|
||||
@ -93,10 +93,8 @@ abstract class CallExpression extends AbstractExpression
|
||||
}
|
||||
|
||||
if ($this->hasNode('arguments')) {
|
||||
$callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
|
||||
|
||||
$callable = $this->getAttribute('callable');
|
||||
$arguments = $this->getArguments($callable, $this->getNode('arguments'));
|
||||
|
||||
foreach ($arguments as $node) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
@ -142,14 +140,23 @@ abstract class CallExpression extends AbstractExpression
|
||||
throw new \LogicException($message);
|
||||
}
|
||||
|
||||
$callableParameters = $this->getCallableParameters($callable, $isVariadic);
|
||||
list($callableParameters, $isPhpVariadic) = $this->getCallableParameters($callable, $isVariadic);
|
||||
$arguments = [];
|
||||
$names = [];
|
||||
$missingArguments = [];
|
||||
$optionalArguments = [];
|
||||
$pos = 0;
|
||||
foreach ($callableParameters as $callableParameter) {
|
||||
$names[] = $name = $this->normalizeName($callableParameter->name);
|
||||
$name = $this->normalizeName($callableParameter->name);
|
||||
if (\PHP_VERSION_ID >= 80000 && 'range' === $callable) {
|
||||
if ('start' === $name) {
|
||||
$name = 'low';
|
||||
} elseif ('end' === $name) {
|
||||
$name = 'high';
|
||||
}
|
||||
}
|
||||
|
||||
$names[] = $name;
|
||||
|
||||
if (\array_key_exists($name, $parameters)) {
|
||||
if (\array_key_exists($pos, $parameters)) {
|
||||
@ -187,7 +194,7 @@ abstract class CallExpression extends AbstractExpression
|
||||
}
|
||||
|
||||
if ($isVariadic) {
|
||||
$arbitraryArguments = new ArrayExpression([], -1);
|
||||
$arbitraryArguments = $isPhpVariadic ? new VariadicExpression([], -1) : new ArrayExpression([], -1);
|
||||
foreach ($parameters as $key => $value) {
|
||||
if (\is_int($key)) {
|
||||
$arbitraryArguments->addElement($value);
|
||||
@ -230,12 +237,9 @@ abstract class CallExpression extends AbstractExpression
|
||||
return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], $name));
|
||||
}
|
||||
|
||||
private function getCallableParameters($callable, $isVariadic)
|
||||
private function getCallableParameters($callable, bool $isVariadic): array
|
||||
{
|
||||
list($r) = $this->reflectCallable($callable);
|
||||
if (null === $r) {
|
||||
return [];
|
||||
}
|
||||
[$r, , $callableName] = $this->reflectCallable($callable);
|
||||
|
||||
$parameters = $r->getParameters();
|
||||
if ($this->hasNode('node')) {
|
||||
@ -252,21 +256,21 @@ abstract class CallExpression extends AbstractExpression
|
||||
array_shift($parameters);
|
||||
}
|
||||
}
|
||||
$isPhpVariadic = false;
|
||||
if ($isVariadic) {
|
||||
$argument = end($parameters);
|
||||
if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && [] === $argument->getDefaultValue()) {
|
||||
$isArray = $argument && $argument->hasType() && 'array' === $argument->getType()->getName();
|
||||
if ($isArray && $argument->isDefaultValueAvailable() && [] === $argument->getDefaultValue()) {
|
||||
array_pop($parameters);
|
||||
} elseif ($argument && $argument->isVariadic()) {
|
||||
array_pop($parameters);
|
||||
$isPhpVariadic = true;
|
||||
} else {
|
||||
$callableName = $r->name;
|
||||
if ($r instanceof \ReflectionMethod) {
|
||||
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
|
||||
}
|
||||
|
||||
throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
}
|
||||
|
||||
return $parameters;
|
||||
return [$parameters, $isPhpVariadic];
|
||||
}
|
||||
|
||||
private function reflectCallable($callable)
|
||||
@ -275,30 +279,44 @@ abstract class CallExpression extends AbstractExpression
|
||||
return $this->reflector;
|
||||
}
|
||||
|
||||
if (\is_array($callable)) {
|
||||
if (!method_exists($callable[0], $callable[1])) {
|
||||
// __call()
|
||||
return [null, []];
|
||||
}
|
||||
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
||||
} elseif (\is_object($callable) && !$callable instanceof \Closure) {
|
||||
$r = new \ReflectionObject($callable);
|
||||
$r = $r->getMethod('__invoke');
|
||||
$callable = [$callable, '__invoke'];
|
||||
} elseif (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
||||
$class = substr($callable, 0, $pos);
|
||||
$method = substr($callable, $pos + 2);
|
||||
if (!method_exists($class, $method)) {
|
||||
// __staticCall()
|
||||
return [null, []];
|
||||
}
|
||||
$r = new \ReflectionMethod($callable);
|
||||
$callable = [$class, $method];
|
||||
} else {
|
||||
$r = new \ReflectionFunction($callable);
|
||||
if (\is_string($callable) && false !== $pos = strpos($callable, '::')) {
|
||||
$callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)];
|
||||
}
|
||||
|
||||
return $this->reflector = [$r, $callable];
|
||||
if (\is_array($callable) && method_exists($callable[0], $callable[1])) {
|
||||
$r = new \ReflectionMethod($callable[0], $callable[1]);
|
||||
|
||||
return $this->reflector = [$r, $callable, $r->class.'::'.$r->name];
|
||||
}
|
||||
|
||||
$checkVisibility = $callable instanceof \Closure;
|
||||
try {
|
||||
$closure = \Closure::fromCallable($callable);
|
||||
} catch (\TypeError $e) {
|
||||
throw new \LogicException(sprintf('Callback for %s "%s" is not callable in the current scope.', $this->getAttribute('type'), $this->getAttribute('name')), 0, $e);
|
||||
}
|
||||
$r = new \ReflectionFunction($closure);
|
||||
|
||||
if (false !== strpos($r->name, '{closure}')) {
|
||||
return $this->reflector = [$r, $callable, 'Closure'];
|
||||
}
|
||||
|
||||
if ($object = $r->getClosureThis()) {
|
||||
$callable = [$object, $r->name];
|
||||
$callableName = (\function_exists('get_debug_type') ? get_debug_type($object) : \get_class($object)).'::'.$r->name;
|
||||
} elseif (\PHP_VERSION_ID >= 80111 && $class = $r->getClosureCalledClass()) {
|
||||
$callableName = $class->name.'::'.$r->name;
|
||||
} elseif (\PHP_VERSION_ID < 80111 && $class = $r->getClosureScopeClass()) {
|
||||
$callableName = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name;
|
||||
} else {
|
||||
$callable = $callableName = $r->name;
|
||||
}
|
||||
|
||||
if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) {
|
||||
$callable = $r->getClosure();
|
||||
}
|
||||
|
||||
return $this->reflector = [$r, $callable, $callableName];
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,7 +16,7 @@ use Twig\Compiler;
|
||||
|
||||
class ConditionalExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $expr1, AbstractExpression $expr2, AbstractExpression $expr3, $lineno)
|
||||
public function __construct(AbstractExpression $expr1, AbstractExpression $expr2, AbstractExpression $expr3, int $lineno)
|
||||
{
|
||||
parent::__construct(['expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3], [], $lineno);
|
||||
}
|
||||
|
@ -16,7 +16,7 @@ use Twig\Compiler;
|
||||
|
||||
class ConstantExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct($value, $lineno)
|
||||
public function __construct($value, int $lineno)
|
||||
{
|
||||
parent::__construct([], ['value' => $value], $lineno);
|
||||
}
|
||||
|
@ -29,7 +29,7 @@ use Twig\Node\Node;
|
||||
*/
|
||||
class DefaultFilter extends FilterExpression
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $node, ConstantExpression $filterName, \Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
public function __construct(Node $node, ConstantExpression $filterName, Node $arguments, int $lineno, string $tag = null)
|
||||
{
|
||||
$default = new FilterExpression($node, new ConstantExpression('default', $node->getTemplateLine()), $arguments, $node->getTemplateLine());
|
||||
|
||||
|
@ -13,11 +13,11 @@
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\Node\Node;
|
||||
|
||||
class FilterExpression extends CallExpression
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $node, ConstantExpression $filterName, \Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
public function __construct(Node $node, ConstantExpression $filterName, Node $arguments, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['node' => $node, 'filter' => $filterName, 'arguments' => $arguments], [], $lineno, $tag);
|
||||
}
|
||||
@ -29,16 +29,11 @@ class FilterExpression extends CallExpression
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'filter');
|
||||
$this->setAttribute('thing', $filter);
|
||||
$this->setAttribute('needs_environment', $filter->needsEnvironment());
|
||||
$this->setAttribute('needs_context', $filter->needsContext());
|
||||
$this->setAttribute('arguments', $filter->getArguments());
|
||||
if ($filter instanceof \Twig_FilterCallableInterface || $filter instanceof TwigFilter) {
|
||||
$this->setAttribute('callable', $filter->getCallable());
|
||||
}
|
||||
if ($filter instanceof TwigFilter) {
|
||||
$this->setAttribute('is_variadic', $filter->isVariadic());
|
||||
}
|
||||
$this->setAttribute('callable', $filter->getCallable());
|
||||
$this->setAttribute('is_variadic', $filter->isVariadic());
|
||||
|
||||
$this->compileCallable($compiler);
|
||||
}
|
||||
|
@ -12,11 +12,11 @@
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\Node\Node;
|
||||
|
||||
class FunctionExpression extends CallExpression
|
||||
{
|
||||
public function __construct($name, \Twig_NodeInterface $arguments, $lineno)
|
||||
public function __construct(string $name, Node $arguments, int $lineno)
|
||||
{
|
||||
parent::__construct(['arguments' => $arguments], ['name' => $name, 'is_defined_test' => false], $lineno);
|
||||
}
|
||||
@ -28,21 +28,15 @@ class FunctionExpression extends CallExpression
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'function');
|
||||
$this->setAttribute('thing', $function);
|
||||
$this->setAttribute('needs_environment', $function->needsEnvironment());
|
||||
$this->setAttribute('needs_context', $function->needsContext());
|
||||
$this->setAttribute('arguments', $function->getArguments());
|
||||
if ($function instanceof \Twig_FunctionCallableInterface || $function instanceof TwigFunction) {
|
||||
$callable = $function->getCallable();
|
||||
if ('constant' === $name && $this->getAttribute('is_defined_test')) {
|
||||
$callable = 'twig_constant_is_defined';
|
||||
}
|
||||
|
||||
$this->setAttribute('callable', $callable);
|
||||
}
|
||||
if ($function instanceof TwigFunction) {
|
||||
$this->setAttribute('is_variadic', $function->isVariadic());
|
||||
$callable = $function->getCallable();
|
||||
if ('constant' === $name && $this->getAttribute('is_defined_test')) {
|
||||
$callable = 'twig_constant_is_defined';
|
||||
}
|
||||
$this->setAttribute('callable', $callable);
|
||||
$this->setAttribute('is_variadic', $function->isVariadic());
|
||||
|
||||
$this->compileCallable($compiler);
|
||||
}
|
||||
|
@ -13,67 +13,76 @@
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Extension\SandboxExtension;
|
||||
use Twig\Template;
|
||||
|
||||
class GetAttrExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $node, AbstractExpression $attribute, AbstractExpression $arguments = null, $type, $lineno)
|
||||
public function __construct(AbstractExpression $node, AbstractExpression $attribute, ?AbstractExpression $arguments, string $type, int $lineno)
|
||||
{
|
||||
$nodes = ['node' => $node, 'attribute' => $attribute];
|
||||
if (null !== $arguments) {
|
||||
$nodes['arguments'] = $arguments;
|
||||
}
|
||||
|
||||
parent::__construct($nodes, ['type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false], $lineno);
|
||||
parent::__construct($nodes, ['type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'optimizable' => true], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
if ($this->getAttribute('disable_c_ext')) {
|
||||
@trigger_error(sprintf('Using the "disable_c_ext" attribute on %s is deprecated since version 1.30 and will be removed in 2.0.', __CLASS__), E_USER_DEPRECATED);
|
||||
$env = $compiler->getEnvironment();
|
||||
|
||||
// optimize array calls
|
||||
if (
|
||||
$this->getAttribute('optimizable')
|
||||
&& (!$env->isStrictVariables() || $this->getAttribute('ignore_strict_check'))
|
||||
&& !$this->getAttribute('is_defined_test')
|
||||
&& Template::ARRAY_CALL === $this->getAttribute('type')
|
||||
) {
|
||||
$var = '$'.$compiler->getVarName();
|
||||
$compiler
|
||||
->raw('(('.$var.' = ')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(') && is_array(')
|
||||
->raw($var)
|
||||
->raw(') || ')
|
||||
->raw($var)
|
||||
->raw(' instanceof ArrayAccess ? (')
|
||||
->raw($var)
|
||||
->raw('[')
|
||||
->subcompile($this->getNode('attribute'))
|
||||
->raw('] ?? null) : null)')
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (\function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) {
|
||||
$compiler->raw('twig_template_get_attributes($this, ');
|
||||
} else {
|
||||
$compiler->raw('$this->getAttribute(');
|
||||
}
|
||||
$compiler->raw('twig_get_attribute($this->env, $this->source, ');
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check')) {
|
||||
$this->getNode('node')->setAttribute('ignore_strict_check', true);
|
||||
}
|
||||
|
||||
$compiler->subcompile($this->getNode('node'));
|
||||
$compiler
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('attribute'))
|
||||
;
|
||||
|
||||
$compiler->raw(', ')->subcompile($this->getNode('attribute'));
|
||||
|
||||
// only generate optional arguments when needed (to make generated code more readable)
|
||||
$needFourth = $this->getAttribute('ignore_strict_check');
|
||||
$needThird = $needFourth || $this->getAttribute('is_defined_test');
|
||||
$needSecond = $needThird || Template::ANY_CALL !== $this->getAttribute('type');
|
||||
$needFirst = $needSecond || $this->hasNode('arguments');
|
||||
|
||||
if ($needFirst) {
|
||||
if ($this->hasNode('arguments')) {
|
||||
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
|
||||
} else {
|
||||
$compiler->raw(', []');
|
||||
}
|
||||
if ($this->hasNode('arguments')) {
|
||||
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
|
||||
} else {
|
||||
$compiler->raw(', []');
|
||||
}
|
||||
|
||||
if ($needSecond) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('type'));
|
||||
}
|
||||
|
||||
if ($needThird) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('is_defined_test'));
|
||||
}
|
||||
|
||||
if ($needFourth) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('ignore_strict_check'));
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
$compiler->raw(', ')
|
||||
->repr($this->getAttribute('type'))
|
||||
->raw(', ')->repr($this->getAttribute('is_defined_test'))
|
||||
->raw(', ')->repr($this->getAttribute('ignore_strict_check'))
|
||||
->raw(', ')->repr($env->hasExtension(SandboxExtension::class))
|
||||
->raw(', ')->repr($this->getNode('node')->getTemplateLine())
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -15,9 +15,9 @@ use Twig\Compiler;
|
||||
|
||||
class MethodCallExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $node, $method, ArrayExpression $arguments, $lineno)
|
||||
public function __construct(AbstractExpression $node, string $method, ArrayExpression $arguments, int $lineno)
|
||||
{
|
||||
parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false], $lineno);
|
||||
parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false, 'is_defined_test' => false], $lineno);
|
||||
|
||||
if ($node instanceof NameExpression) {
|
||||
$node->setAttribute('always_defined', true);
|
||||
@ -26,11 +26,24 @@ class MethodCallExpression extends AbstractExpression
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
if ($this->getAttribute('is_defined_test')) {
|
||||
$compiler
|
||||
->raw('method_exists($macros[')
|
||||
->repr($this->getNode('node')->getAttribute('name'))
|
||||
->raw('], ')
|
||||
->repr($this->getAttribute('method'))
|
||||
->raw(')')
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw('->')
|
||||
->raw($this->getAttribute('method'))
|
||||
->raw('(')
|
||||
->raw('twig_call_macro($macros[')
|
||||
->repr($this->getNode('node')->getAttribute('name'))
|
||||
->raw('], ')
|
||||
->repr($this->getAttribute('method'))
|
||||
->raw(', [')
|
||||
;
|
||||
$first = true;
|
||||
foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
|
||||
@ -41,7 +54,10 @@ class MethodCallExpression extends AbstractExpression
|
||||
|
||||
$compiler->subcompile($pair['value']);
|
||||
}
|
||||
$compiler->raw(')');
|
||||
$compiler
|
||||
->raw('], ')
|
||||
->repr($this->getTemplateLine())
|
||||
->raw(', $context, $this->getSourceContext())');
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -16,13 +16,13 @@ use Twig\Compiler;
|
||||
|
||||
class NameExpression extends AbstractExpression
|
||||
{
|
||||
protected $specialVars = [
|
||||
'_self' => '$this',
|
||||
private $specialVars = [
|
||||
'_self' => '$this->getTemplateName()',
|
||||
'_context' => '$context',
|
||||
'_charset' => '$this->env->getCharset()',
|
||||
];
|
||||
|
||||
public function __construct($name, $lineno)
|
||||
public function __construct(string $name, int $lineno)
|
||||
{
|
||||
parent::__construct([], ['name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false], $lineno);
|
||||
}
|
||||
@ -36,7 +36,7 @@ class NameExpression extends AbstractExpression
|
||||
if ($this->getAttribute('is_defined_test')) {
|
||||
if ($this->isSpecial()) {
|
||||
$compiler->repr(true);
|
||||
} elseif (\PHP_VERSION_ID >= 700400) {
|
||||
} elseif (\PHP_VERSION_ID >= 70400) {
|
||||
$compiler
|
||||
->raw('array_key_exists(')
|
||||
->string($name)
|
||||
@ -60,45 +60,25 @@ class NameExpression extends AbstractExpression
|
||||
->raw(']')
|
||||
;
|
||||
} else {
|
||||
if (\PHP_VERSION_ID >= 70000) {
|
||||
// use PHP 7 null coalescing operator
|
||||
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
|
||||
$compiler
|
||||
->raw('($context[')
|
||||
->string($name)
|
||||
->raw('] ?? ')
|
||||
->raw('] ?? null)')
|
||||
;
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
|
||||
$compiler->raw('null)');
|
||||
} else {
|
||||
$compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
|
||||
}
|
||||
} elseif (\PHP_VERSION_ID >= 50400) {
|
||||
// PHP 5.4 ternary operator performance was optimized
|
||||
} else {
|
||||
$compiler
|
||||
->raw('(isset($context[')
|
||||
->string($name)
|
||||
->raw(']) ? $context[')
|
||||
->raw(']) || array_key_exists(')
|
||||
->string($name)
|
||||
->raw('] : ')
|
||||
;
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
|
||||
$compiler->raw('null)');
|
||||
} else {
|
||||
$compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
|
||||
}
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->getContext($context, ')
|
||||
->raw(', $context) ? $context[')
|
||||
->string($name)
|
||||
;
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', true');
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('] : (function () { throw new RuntimeError(\'Variable ')
|
||||
->string($name)
|
||||
->raw(' does not exist.\', ')
|
||||
->repr($this->lineno)
|
||||
->raw(', $this->source); })()')
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use Twig\Node\Node;
|
||||
|
||||
class NullCoalesceExpression extends ConditionalExpression
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $left, \Twig_NodeInterface $right, $lineno)
|
||||
public function __construct(Node $left, Node $right, int $lineno)
|
||||
{
|
||||
$test = new DefinedTest(clone $left, 'defined', new Node(), $left->getTemplateLine());
|
||||
// for "block()", we don't need the null test as the return value is always a string
|
||||
@ -44,7 +44,7 @@ class NullCoalesceExpression extends ConditionalExpression
|
||||
* cases might be implemented as an optimizer node visitor, but has not been done
|
||||
* as benefits are probably not worth the added complexity.
|
||||
*/
|
||||
if (\PHP_VERSION_ID >= 70000 && $this->getNode('expr2') instanceof NameExpression) {
|
||||
if ($this->getNode('expr2') instanceof NameExpression) {
|
||||
$this->getNode('expr2')->setAttribute('always_defined', true);
|
||||
$compiler
|
||||
->raw('((')
|
||||
|
@ -21,7 +21,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class ParentExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
public function __construct(string $name, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct([], ['output' => false, 'name' => $name], $lineno, $tag);
|
||||
}
|
||||
|
@ -15,7 +15,7 @@ use Twig\Compiler;
|
||||
|
||||
class TempNameExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct($name, $lineno)
|
||||
public function __construct(string $name, int $lineno)
|
||||
{
|
||||
parent::__construct([], ['name' => $name], $lineno);
|
||||
}
|
||||
|
@ -18,8 +18,10 @@ use Twig\Node\Expression\BlockReferenceExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FunctionExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\MethodCallExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\TestExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Checks if a variable is defined in the current context.
|
||||
@ -33,7 +35,7 @@ use Twig\Node\Expression\TestExpression;
|
||||
*/
|
||||
class DefinedTest extends TestExpression
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $node, $name, \Twig_NodeInterface $arguments = null, $lineno)
|
||||
public function __construct(Node $node, string $name, ?Node $arguments, int $lineno)
|
||||
{
|
||||
if ($node instanceof NameExpression) {
|
||||
$node->setAttribute('is_defined_test', true);
|
||||
@ -46,6 +48,8 @@ class DefinedTest extends TestExpression
|
||||
$node->setAttribute('is_defined_test', true);
|
||||
} elseif ($node instanceof ConstantExpression || $node instanceof ArrayExpression) {
|
||||
$node = new ConstantExpression(true, $node->getTemplateLine());
|
||||
} elseif ($node instanceof MethodCallExpression) {
|
||||
$node->setAttribute('is_defined_test', true);
|
||||
} else {
|
||||
throw new SyntaxError('The "defined" test only works with simple variables.', $lineno);
|
||||
}
|
||||
@ -53,8 +57,9 @@ class DefinedTest extends TestExpression
|
||||
parent::__construct($node, $name, $arguments, $lineno);
|
||||
}
|
||||
|
||||
protected function changeIgnoreStrictCheck(GetAttrExpression $node)
|
||||
private function changeIgnoreStrictCheck(GetAttrExpression $node)
|
||||
{
|
||||
$node->setAttribute('optimizable', false);
|
||||
$node->setAttribute('ignore_strict_check', true);
|
||||
|
||||
if ($node->getNode('node') instanceof GetAttrExpression) {
|
||||
|
@ -28,7 +28,7 @@ class OddTest extends TestExpression
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' % 2 == 1')
|
||||
->raw(' % 2 != 0')
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
@ -12,11 +12,11 @@
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\TwigTest;
|
||||
use Twig\Node\Node;
|
||||
|
||||
class TestExpression extends CallExpression
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $node, $name, \Twig_NodeInterface $arguments = null, $lineno)
|
||||
public function __construct(Node $node, string $name, ?Node $arguments, int $lineno)
|
||||
{
|
||||
$nodes = ['node' => $node];
|
||||
if (null !== $arguments) {
|
||||
@ -33,16 +33,9 @@ class TestExpression extends CallExpression
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'test');
|
||||
$this->setAttribute('thing', $test);
|
||||
if ($test instanceof TwigTest) {
|
||||
$this->setAttribute('arguments', $test->getArguments());
|
||||
}
|
||||
if ($test instanceof \Twig_TestCallableInterface || $test instanceof TwigTest) {
|
||||
$this->setAttribute('callable', $test->getCallable());
|
||||
}
|
||||
if ($test instanceof TwigTest) {
|
||||
$this->setAttribute('is_variadic', $test->isVariadic());
|
||||
}
|
||||
$this->setAttribute('arguments', $test->getArguments());
|
||||
$this->setAttribute('callable', $test->getCallable());
|
||||
$this->setAttribute('is_variadic', $test->isVariadic());
|
||||
|
||||
$this->compileCallable($compiler);
|
||||
}
|
||||
|
@ -14,10 +14,11 @@ namespace Twig\Node\Expression\Unary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
abstract class AbstractUnary extends AbstractExpression
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $node, $lineno)
|
||||
public function __construct(Node $node, int $lineno)
|
||||
{
|
||||
parent::__construct(['node' => $node], [], $lineno);
|
||||
}
|
||||
|
24
system/libs/Twig/Node/Expression/VariadicExpression.php
Normal file
24
system/libs/Twig/Node/Expression/VariadicExpression.php
Normal file
@ -0,0 +1,24 @@
|
||||
<?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\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
|
||||
class VariadicExpression extends ArrayExpression
|
||||
{
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('...');
|
||||
|
||||
parent::compile($compiler);
|
||||
}
|
||||
}
|
@ -20,7 +20,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class FlushNode extends Node
|
||||
{
|
||||
public function __construct($lineno, $tag)
|
||||
public function __construct(int $lineno, string $tag)
|
||||
{
|
||||
parent::__construct([], [], $lineno, $tag);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class ForLoopNode extends Node
|
||||
{
|
||||
public function __construct($lineno, $tag = null)
|
||||
public function __construct(int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct([], ['with_loop' => false, 'ifexpr' => false, 'else' => false], $lineno, $tag);
|
||||
}
|
||||
|
@ -23,9 +23,9 @@ use Twig\Node\Expression\AssignNameExpression;
|
||||
*/
|
||||
class ForNode extends Node
|
||||
{
|
||||
protected $loop;
|
||||
private $loop;
|
||||
|
||||
public function __construct(AssignNameExpression $keyTarget, AssignNameExpression $valueTarget, AbstractExpression $seq, AbstractExpression $ifexpr = null, \Twig_NodeInterface $body, \Twig_NodeInterface $else = null, $lineno, $tag = null)
|
||||
public function __construct(AssignNameExpression $keyTarget, AssignNameExpression $valueTarget, AbstractExpression $seq, ?AbstractExpression $ifexpr, Node $body, ?Node $else, int $lineno, string $tag = null)
|
||||
{
|
||||
$body = new Node([$body, $this->loop = new ForLoopNode($lineno, $tag)]);
|
||||
|
||||
|
@ -21,7 +21,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class IfNode extends Node
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $tests, \Twig_NodeInterface $else = null, $lineno, $tag = null)
|
||||
public function __construct(Node $tests, ?Node $else, int $lineno, string $tag = null)
|
||||
{
|
||||
$nodes = ['tests' => $tests];
|
||||
if (null !== $else) {
|
||||
@ -50,8 +50,11 @@ class IfNode extends Node
|
||||
->subcompile($this->getNode('tests')->getNode($i))
|
||||
->raw(") {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('tests')->getNode($i + 1))
|
||||
;
|
||||
// The node might not exists if the content is empty
|
||||
if ($this->getNode('tests')->hasNode($i + 1)) {
|
||||
$compiler->subcompile($this->getNode('tests')->getNode($i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hasNode('else')) {
|
||||
|
@ -22,20 +22,28 @@ use Twig\Node\Expression\NameExpression;
|
||||
*/
|
||||
class ImportNode extends Node
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, AbstractExpression $var, $lineno, $tag = null)
|
||||
public function __construct(AbstractExpression $expr, AbstractExpression $var, int $lineno, string $tag = null, bool $global = true)
|
||||
{
|
||||
parent::__construct(['expr' => $expr, 'var' => $var], [], $lineno, $tag);
|
||||
parent::__construct(['expr' => $expr, 'var' => $var], ['global' => $global], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
->subcompile($this->getNode('var'))
|
||||
->raw(' = ')
|
||||
->write('$macros[')
|
||||
->repr($this->getNode('var')->getAttribute('name'))
|
||||
->raw('] = ')
|
||||
;
|
||||
|
||||
if ($this->getAttribute('global')) {
|
||||
$compiler
|
||||
->raw('$this->macros[')
|
||||
->repr($this->getNode('var')->getAttribute('name'))
|
||||
->raw('] = ')
|
||||
;
|
||||
}
|
||||
|
||||
if ($this->getNode('expr') instanceof NameExpression && '_self' === $this->getNode('expr')->getAttribute('name')) {
|
||||
$compiler->raw('$this');
|
||||
} else {
|
||||
|
@ -22,7 +22,7 @@ use Twig\Node\Expression\AbstractExpression;
|
||||
*/
|
||||
class IncludeNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, AbstractExpression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
|
||||
public function __construct(AbstractExpression $expr, ?AbstractExpression $variables, bool $only, bool $ignoreMissing, int $lineno, string $tag = null)
|
||||
{
|
||||
$nodes = ['expr' => $expr];
|
||||
if (null !== $variables) {
|
||||
|
@ -21,9 +21,9 @@ use Twig\Error\SyntaxError;
|
||||
*/
|
||||
class MacroNode extends Node
|
||||
{
|
||||
const VARARGS_NAME = 'varargs';
|
||||
public const VARARGS_NAME = 'varargs';
|
||||
|
||||
public function __construct($name, \Twig_NodeInterface $body, \Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
public function __construct(string $name, Node $body, Node $arguments, int $lineno, string $tag = null)
|
||||
{
|
||||
foreach ($arguments as $argumentName => $argument) {
|
||||
if (self::VARARGS_NAME === $argumentName) {
|
||||
@ -38,7 +38,7 @@ class MacroNode extends Node
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf('public function get%s(', $this->getAttribute('name')))
|
||||
->write(sprintf('public function macro_%s(', $this->getAttribute('name')))
|
||||
;
|
||||
|
||||
$count = \count($this->getNode('arguments'));
|
||||
@ -54,21 +54,16 @@ class MacroNode extends Node
|
||||
}
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID >= 50600) {
|
||||
if ($count) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
|
||||
$compiler->raw('...$__varargs__');
|
||||
if ($count) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('...$__varargs__')
|
||||
->raw(")\n")
|
||||
->write("{\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
$compiler
|
||||
->write("\$macros = \$this->macros;\n")
|
||||
->write("\$context = \$this->env->mergeGlobals([\n")
|
||||
->indent()
|
||||
;
|
||||
@ -88,19 +83,8 @@ class MacroNode extends Node
|
||||
->raw(' => ')
|
||||
;
|
||||
|
||||
if (\PHP_VERSION_ID >= 50600) {
|
||||
$compiler->raw("\$__varargs__,\n");
|
||||
} else {
|
||||
$compiler
|
||||
->raw('func_num_args() > ')
|
||||
->repr($count)
|
||||
->raw(' ? array_slice(func_get_args(), ')
|
||||
->repr($count)
|
||||
->raw(") : [],\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw("\$__varargs__,\n")
|
||||
->outdent()
|
||||
->write("]);\n\n")
|
||||
->write("\$blocks = [];\n\n")
|
||||
@ -114,19 +98,14 @@ class MacroNode extends Node
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('body'))
|
||||
->raw("\n")
|
||||
->write("return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n")
|
||||
->outdent()
|
||||
->write("} catch (\Exception \$e) {\n")
|
||||
->write("} finally {\n")
|
||||
->indent()
|
||||
->write("ob_end_clean();\n\n")
|
||||
->write("throw \$e;\n")
|
||||
->write("ob_end_clean();\n")
|
||||
->outdent()
|
||||
->write("} catch (\Throwable \$e) {\n")
|
||||
->indent()
|
||||
->write("ob_end_clean();\n\n")
|
||||
->write("throw \$e;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n")
|
||||
->write("}\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
|
@ -25,16 +25,15 @@ use Twig\Source;
|
||||
* display_end, constructor_start, constructor_end, and class_end.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final since Twig 2.4.0
|
||||
*/
|
||||
class ModuleNode extends Node
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $body, AbstractExpression $parent = null, \Twig_NodeInterface $blocks, \Twig_NodeInterface $macros, \Twig_NodeInterface $traits, $embeddedTemplates, $name, $source = '')
|
||||
public function __construct(Node $body, ?AbstractExpression $parent, Node $blocks, Node $macros, Node $traits, $embeddedTemplates, Source $source)
|
||||
{
|
||||
if (!$name instanceof Source) {
|
||||
@trigger_error(sprintf('Passing a string as the $name argument of %s() is deprecated since version 1.27. Pass a \Twig\Source instance instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
$source = new Source($source, $name);
|
||||
} else {
|
||||
$source = $name;
|
||||
if (__CLASS__ !== static::class) {
|
||||
@trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$nodes = [
|
||||
@ -54,16 +53,11 @@ class ModuleNode extends Node
|
||||
|
||||
// embedded templates are set as attributes so that they are only visited once by the visitors
|
||||
parent::__construct($nodes, [
|
||||
// source to be remove in 2.0
|
||||
'source' => $source->getCode(),
|
||||
// filename to be remove in 2.0 (use getTemplateName() instead)
|
||||
'filename' => $source->getName(),
|
||||
'index' => null,
|
||||
'embedded_templates' => $embeddedTemplates,
|
||||
], 1);
|
||||
|
||||
// populate the template name of all node children
|
||||
$this->setTemplateName($source->getName());
|
||||
$this->setSourceContext($source);
|
||||
}
|
||||
|
||||
@ -89,16 +83,7 @@ class ModuleNode extends Node
|
||||
|
||||
$this->compileClassHeader($compiler);
|
||||
|
||||
if (
|
||||
\count($this->getNode('blocks'))
|
||||
|| \count($this->getNode('traits'))
|
||||
|| !$this->hasNode('parent')
|
||||
|| $this->getNode('parent') instanceof ConstantExpression
|
||||
|| \count($this->getNode('constructor_start'))
|
||||
|| \count($this->getNode('constructor_end'))
|
||||
) {
|
||||
$this->compileConstructor($compiler);
|
||||
}
|
||||
$this->compileConstructor($compiler);
|
||||
|
||||
$this->compileGetParent($compiler);
|
||||
|
||||
@ -114,8 +99,6 @@ class ModuleNode extends Node
|
||||
|
||||
$this->compileDebugInfo($compiler);
|
||||
|
||||
$this->compileGetSource($compiler);
|
||||
|
||||
$this->compileGetSourceContext($compiler);
|
||||
|
||||
$this->compileClassFooter($compiler);
|
||||
@ -166,6 +149,7 @@ class ModuleNode extends Node
|
||||
->write("use Twig\Environment;\n")
|
||||
->write("use Twig\Error\LoaderError;\n")
|
||||
->write("use Twig\Error\RuntimeError;\n")
|
||||
->write("use Twig\Extension\SandboxExtension;\n")
|
||||
->write("use Twig\Markup;\n")
|
||||
->write("use Twig\Sandbox\SecurityError;\n")
|
||||
->write("use Twig\Sandbox\SecurityNotAllowedTagError;\n")
|
||||
@ -179,9 +163,11 @@ class ModuleNode extends Node
|
||||
// if the template name contains */, add a blank to avoid a PHP parse error
|
||||
->write('/* '.str_replace('*/', '* /', $this->getSourceContext()->getName())." */\n")
|
||||
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getSourceContext()->getName(), $this->getAttribute('index')))
|
||||
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
|
||||
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass(false)))
|
||||
->write("{\n")
|
||||
->indent()
|
||||
->write("private \$source;\n")
|
||||
->write("private \$macros = [];\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
@ -192,6 +178,7 @@ class ModuleNode extends Node
|
||||
->indent()
|
||||
->subcompile($this->getNode('constructor_start'))
|
||||
->write("parent::__construct(\$env);\n\n")
|
||||
->write("\$this->source = \$this->getSourceContext();\n\n")
|
||||
;
|
||||
|
||||
// parent
|
||||
@ -203,18 +190,24 @@ class ModuleNode extends Node
|
||||
if ($countTraits) {
|
||||
// traits
|
||||
foreach ($this->getNode('traits') as $i => $trait) {
|
||||
$this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
|
||||
|
||||
$node = $trait->getNode('template');
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($node)
|
||||
->write(sprintf('$_trait_%s = $this->loadTemplate(', $i))
|
||||
->subcompile($node)
|
||||
->raw(', ')
|
||||
->repr($node->getTemplateName())
|
||||
->raw(', ')
|
||||
->repr($node->getTemplateLine())
|
||||
->raw(");\n")
|
||||
->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
|
||||
->indent()
|
||||
->write("throw new RuntimeError('Template \"'.")
|
||||
->subcompile($trait->getNode('template'))
|
||||
->raw(".'\" cannot be used as a trait.', ")
|
||||
->repr($node->getTemplateLine())
|
||||
->raw(", \$this->getSourceContext());\n")
|
||||
->raw(", \$this->source);\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
|
||||
@ -226,13 +219,13 @@ class ModuleNode extends Node
|
||||
->string($key)
|
||||
->raw("])) {\n")
|
||||
->indent()
|
||||
->write("throw new RuntimeError(sprintf('Block ")
|
||||
->write("throw new RuntimeError('Block ")
|
||||
->string($key)
|
||||
->raw(' is not defined in trait ')
|
||||
->subcompile($trait->getNode('template'))
|
||||
->raw(".'), ")
|
||||
->raw(".', ")
|
||||
->repr($node->getTemplateLine())
|
||||
->raw(", \$this->getSourceContext());\n")
|
||||
->raw(", \$this->source);\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
|
||||
@ -318,6 +311,7 @@ class ModuleNode extends Node
|
||||
$compiler
|
||||
->write("protected function doDisplay(array \$context, array \$blocks = [])\n", "{\n")
|
||||
->indent()
|
||||
->write("\$macros = \$this->macros;\n")
|
||||
->subcompile($this->getNode('display_start'))
|
||||
->subcompile($this->getNode('body'))
|
||||
;
|
||||
@ -440,20 +434,6 @@ class ModuleNode extends Node
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileGetSource(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("/** @deprecated since 1.27 (to be removed in 2.0). Use getSourceContext() instead */\n")
|
||||
->write("public function getSource()\n", "{\n")
|
||||
->indent()
|
||||
->write("@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getSourceContext() instead.', E_USER_DEPRECATED);\n\n")
|
||||
->write('return $this->getSourceContext()->getCode();')
|
||||
->raw("\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileGetSourceContext(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
|
@ -20,7 +20,7 @@ use Twig\Source;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Node implements \Twig_NodeInterface
|
||||
class Node implements \Countable, \IteratorAggregate
|
||||
{
|
||||
protected $nodes;
|
||||
protected $attributes;
|
||||
@ -36,11 +36,11 @@ class Node implements \Twig_NodeInterface
|
||||
* @param int $lineno The line number
|
||||
* @param string $tag The tag name associated with the Node
|
||||
*/
|
||||
public function __construct(array $nodes = [], array $attributes = [], $lineno = 0, $tag = null)
|
||||
public function __construct(array $nodes = [], array $attributes = [], int $lineno = 0, string $tag = null)
|
||||
{
|
||||
foreach ($nodes as $name => $node) {
|
||||
if (!$node instanceof \Twig_NodeInterface) {
|
||||
@trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, \get_class($this)), E_USER_DEPRECATED);
|
||||
if (!$node instanceof self) {
|
||||
throw new \InvalidArgumentException(sprintf('Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a \Twig\Node\Node instance.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, static::class));
|
||||
}
|
||||
}
|
||||
$this->nodes = $nodes;
|
||||
@ -56,7 +56,7 @@ class Node implements \Twig_NodeInterface
|
||||
$attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true)));
|
||||
}
|
||||
|
||||
$repr = [\get_class($this).'('.implode(', ', $attributes)];
|
||||
$repr = [static::class.'('.implode(', ', $attributes)];
|
||||
|
||||
if (\count($this->nodes)) {
|
||||
foreach ($this->nodes as $name => $node) {
|
||||
@ -77,41 +77,6 @@ class Node implements \Twig_NodeInterface
|
||||
return implode("\n", $repr);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.16.1 (to be removed in 2.0)
|
||||
*/
|
||||
public function toXml($asDom = false)
|
||||
{
|
||||
@trigger_error(sprintf('%s is deprecated since version 1.16.1 and will be removed in 2.0.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
$dom = new \DOMDocument('1.0', 'UTF-8');
|
||||
$dom->formatOutput = true;
|
||||
$dom->appendChild($xml = $dom->createElement('twig'));
|
||||
|
||||
$xml->appendChild($node = $dom->createElement('node'));
|
||||
$node->setAttribute('class', \get_class($this));
|
||||
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
$node->appendChild($attribute = $dom->createElement('attribute'));
|
||||
$attribute->setAttribute('name', $name);
|
||||
$attribute->appendChild($dom->createTextNode($value));
|
||||
}
|
||||
|
||||
foreach ($this->nodes as $name => $n) {
|
||||
if (null === $n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$child = $n->toXml(true)->getElementsByTagName('node')->item(0);
|
||||
$child = $dom->importNode($child, true);
|
||||
$child->setAttribute('name', $name);
|
||||
|
||||
$node->appendChild($child);
|
||||
}
|
||||
|
||||
return $asDom ? $dom : $dom->saveXML();
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
foreach ($this->nodes as $node) {
|
||||
@ -124,16 +89,6 @@ class Node implements \Twig_NodeInterface
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.27 (to be removed in 2.0)
|
||||
*/
|
||||
public function getLine()
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateLine() instead.', E_USER_DEPRECATED);
|
||||
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
public function getNodeTag()
|
||||
{
|
||||
return $this->tag;
|
||||
@ -153,7 +108,7 @@ class Node implements \Twig_NodeInterface
|
||||
public function getAttribute($name)
|
||||
{
|
||||
if (!\array_key_exists($name, $this->attributes)) {
|
||||
throw new \LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, \get_class($this)));
|
||||
throw new \LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, static::class));
|
||||
}
|
||||
|
||||
return $this->attributes[$name];
|
||||
@ -178,7 +133,7 @@ class Node implements \Twig_NodeInterface
|
||||
*/
|
||||
public function hasNode($name)
|
||||
{
|
||||
return \array_key_exists($name, $this->nodes);
|
||||
return isset($this->nodes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -186,19 +141,15 @@ class Node implements \Twig_NodeInterface
|
||||
*/
|
||||
public function getNode($name)
|
||||
{
|
||||
if (!\array_key_exists($name, $this->nodes)) {
|
||||
throw new \LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, \get_class($this)));
|
||||
if (!isset($this->nodes[$name])) {
|
||||
throw new \LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, static::class));
|
||||
}
|
||||
|
||||
return $this->nodes[$name];
|
||||
}
|
||||
|
||||
public function setNode($name, $node = null)
|
||||
public function setNode($name, self $node)
|
||||
{
|
||||
if (!$node instanceof \Twig_NodeInterface) {
|
||||
@trigger_error(sprintf('Using "%s" for the value of node "%s" of "%s" is deprecated since version 1.25 and will be removed in 2.0.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, \get_class($this)), E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$this->nodes[$name] = $node;
|
||||
}
|
||||
|
||||
@ -207,65 +158,59 @@ class Node implements \Twig_NodeInterface
|
||||
unset($this->nodes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return \count($this->nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return \Traversable
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator()
|
||||
{
|
||||
return new \ArrayIterator($this->nodes);
|
||||
}
|
||||
|
||||
public function setTemplateName($name)
|
||||
/**
|
||||
* @deprecated since 2.8 (to be removed in 3.0)
|
||||
*/
|
||||
public function setTemplateName($name/*, $triggerDeprecation = true */)
|
||||
{
|
||||
$triggerDeprecation = 2 > \func_num_args() || \func_get_arg(1);
|
||||
if ($triggerDeprecation) {
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use setSourceContext() instead.', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$this->name = $name;
|
||||
foreach ($this->nodes as $node) {
|
||||
if (null !== $node) {
|
||||
$node->setTemplateName($name);
|
||||
}
|
||||
$node->setTemplateName($name, $triggerDeprecation);
|
||||
}
|
||||
}
|
||||
|
||||
public function getTemplateName()
|
||||
{
|
||||
return $this->name;
|
||||
return $this->sourceContext ? $this->sourceContext->getName() : null;
|
||||
}
|
||||
|
||||
public function setSourceContext(Source $source)
|
||||
{
|
||||
$this->sourceContext = $source;
|
||||
foreach ($this->nodes as $node) {
|
||||
if ($node instanceof Node) {
|
||||
$node->setSourceContext($source);
|
||||
}
|
||||
$node->setSourceContext($source);
|
||||
}
|
||||
|
||||
$this->setTemplateName($source->getName(), false);
|
||||
}
|
||||
|
||||
public function getSourceContext()
|
||||
{
|
||||
return $this->sourceContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.27 (to be removed in 2.0)
|
||||
*/
|
||||
public function setFilename($name)
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use setTemplateName() instead.', E_USER_DEPRECATED);
|
||||
|
||||
$this->setTemplateName($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.27 (to be removed in 2.0)
|
||||
*/
|
||||
public function getFilename()
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0. Use getTemplateName() instead.', E_USER_DEPRECATED);
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\Node', 'Twig_Node');
|
||||
|
@ -22,7 +22,7 @@ use Twig\Node\Expression\AbstractExpression;
|
||||
*/
|
||||
class PrintNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, $lineno, $tag = null)
|
||||
public function __construct(AbstractExpression $expr, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $lineno, $tag);
|
||||
}
|
||||
|
@ -20,7 +20,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class SandboxNode extends Node
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $body, $lineno, $tag = null)
|
||||
public function __construct(Node $body, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['body' => $body], [], $lineno, $tag);
|
||||
}
|
||||
@ -34,12 +34,19 @@ class SandboxNode extends Node
|
||||
->write("\$this->sandbox->enableSandbox();\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("} finally {\n")
|
||||
->indent()
|
||||
->write("if (!\$alreadySandboxed) {\n")
|
||||
->indent()
|
||||
->write("\$this->sandbox->disableSandbox();\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -13,7 +13,6 @@ namespace Twig\Node;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
|
||||
/**
|
||||
* Adds a check for the __toString() method when the variable is an object and the sandbox is activated.
|
||||
@ -42,28 +41,14 @@ class SandboxedPrintNode extends PrintNode
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write('$this->env->getExtension(\'\Twig\Extension\SandboxExtension\')->ensureToStringAllowed(')
|
||||
->write('$this->extensions[SandboxExtension::class]->ensureToStringAllowed(')
|
||||
->subcompile($expr)
|
||||
->raw(");\n")
|
||||
->raw(', ')
|
||||
->repr($expr->getTemplateLine())
|
||||
->raw(", \$this->source);\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes node filters.
|
||||
*
|
||||
* This is mostly needed when another visitor adds filters (like the escaper one).
|
||||
*
|
||||
* @return Node
|
||||
*/
|
||||
protected function removeNodeFilter(Node $node)
|
||||
{
|
||||
if ($node instanceof FilterExpression) {
|
||||
return $this->removeNodeFilter($node->getNode('node'));
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
|
||||
class_alias('Twig\Node\SandboxedPrintNode', 'Twig_Node_SandboxedPrint');
|
||||
|
@ -21,7 +21,7 @@ use Twig\Node\Expression\ConstantExpression;
|
||||
*/
|
||||
class SetNode extends Node implements NodeCaptureInterface
|
||||
{
|
||||
public function __construct($capture, \Twig_NodeInterface $names, \Twig_NodeInterface $values, $lineno, $tag = null)
|
||||
public function __construct(bool $capture, Node $names, Node $values, int $lineno, string $tag = null)
|
||||
{
|
||||
parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => false], $lineno, $tag);
|
||||
|
||||
|
@ -18,11 +18,13 @@ use Twig\Compiler;
|
||||
*
|
||||
* It removes spaces between HTML tags.
|
||||
*
|
||||
* @deprecated since Twig 2.7, to be removed in 3.0
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SpacelessNode extends Node
|
||||
class SpacelessNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
public function __construct(\Twig_NodeInterface $body, $lineno, $tag = 'spaceless')
|
||||
public function __construct(Node $body, int $lineno, string $tag = 'spaceless')
|
||||
{
|
||||
parent::__construct(['body' => $body], [], $lineno, $tag);
|
||||
}
|
||||
|
@ -21,7 +21,7 @@ use Twig\Compiler;
|
||||
*/
|
||||
class TextNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
public function __construct($data, $lineno)
|
||||
public function __construct(string $data, int $lineno)
|
||||
{
|
||||
parent::__construct([], ['data' => $data], $lineno);
|
||||
}
|
||||
|
@ -20,14 +20,14 @@ use Twig\Compiler;
|
||||
*/
|
||||
class WithNode extends Node
|
||||
{
|
||||
public function __construct(Node $body, Node $variables = null, $only = false, $lineno, $tag = null)
|
||||
public function __construct(Node $body, ?Node $variables, bool $only, int $lineno, string $tag = null)
|
||||
{
|
||||
$nodes = ['body' => $body];
|
||||
if (null !== $variables) {
|
||||
$nodes['variables'] = $variables;
|
||||
}
|
||||
|
||||
parent::__construct($nodes, ['only' => (bool) $only], $lineno, $tag);
|
||||
parent::__construct($nodes, ['only' => $only], $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler)
|
||||
|
@ -11,6 +11,7 @@
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Node\Node;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
|
||||
/**
|
||||
@ -18,14 +19,12 @@ use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
*
|
||||
* It visits all nodes and their children and calls the given visitor for each.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class NodeTraverser
|
||||
final class NodeTraverser
|
||||
{
|
||||
protected $env;
|
||||
protected $visitors = [];
|
||||
private $env;
|
||||
private $visitors = [];
|
||||
|
||||
/**
|
||||
* @param NodeVisitorInterface[] $visitors
|
||||
@ -45,10 +44,8 @@ class NodeTraverser
|
||||
|
||||
/**
|
||||
* Traverses a node and calls the registered visitors.
|
||||
*
|
||||
* @return \Twig_NodeInterface
|
||||
*/
|
||||
public function traverse(\Twig_NodeInterface $node)
|
||||
public function traverse(Node $node): Node
|
||||
{
|
||||
ksort($this->visitors);
|
||||
foreach ($this->visitors as $visitors) {
|
||||
@ -60,24 +57,23 @@ class NodeTraverser
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function traverseForVisitor(NodeVisitorInterface $visitor, \Twig_NodeInterface $node = null)
|
||||
/**
|
||||
* @return Node|null
|
||||
*/
|
||||
private function traverseForVisitor(NodeVisitorInterface $visitor, Node $node)
|
||||
{
|
||||
if (null === $node) {
|
||||
return;
|
||||
}
|
||||
|
||||
$node = $visitor->enterNode($node, $this->env);
|
||||
|
||||
foreach ($node as $k => $n) {
|
||||
if (null === $n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (false !== ($m = $this->traverseForVisitor($visitor, $n)) && null !== $m) {
|
||||
if ($m !== $n) {
|
||||
$node->setNode($k, $m);
|
||||
}
|
||||
} else {
|
||||
if (false === $m) {
|
||||
@trigger_error('Returning "false" to remove a Node from NodeVisitorInterface::leaveNode() is deprecated since Twig version 2.9; return "null" instead.', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$node->removeNode($k);
|
||||
}
|
||||
}
|
||||
|
@ -23,21 +23,13 @@ use Twig\Node\Node;
|
||||
*/
|
||||
abstract class AbstractNodeVisitor implements NodeVisitorInterface
|
||||
{
|
||||
final public function enterNode(\Twig_NodeInterface $node, Environment $env)
|
||||
final public function enterNode(Node $node, Environment $env)
|
||||
{
|
||||
if (!$node instanceof Node) {
|
||||
throw new \LogicException(sprintf('%s only supports \Twig\Node\Node instances.', __CLASS__));
|
||||
}
|
||||
|
||||
return $this->doEnterNode($node, $env);
|
||||
}
|
||||
|
||||
final public function leaveNode(\Twig_NodeInterface $node, Environment $env)
|
||||
final public function leaveNode(Node $node, Environment $env)
|
||||
{
|
||||
if (!$node instanceof Node) {
|
||||
throw new \LogicException(sprintf('%s only supports \Twig\Node\Node instances.', __CLASS__));
|
||||
}
|
||||
|
||||
return $this->doLeaveNode($node, $env);
|
||||
}
|
||||
|
||||
@ -51,7 +43,7 @@ abstract class AbstractNodeVisitor implements NodeVisitorInterface
|
||||
/**
|
||||
* Called after child nodes are visited.
|
||||
*
|
||||
* @return Node|false|null The modified node or null if the node must be removed
|
||||
* @return Node|null The modified node or null if the node must be removed
|
||||
*/
|
||||
abstract protected function doLeaveNode(Node $node, Environment $env);
|
||||
}
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Twig\NodeVisitor;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Extension\EscaperExtension;
|
||||
use Twig\Node\AutoEscapeNode;
|
||||
use Twig\Node\BlockNode;
|
||||
use Twig\Node\BlockReferenceNode;
|
||||
@ -27,18 +28,16 @@ use Twig\Node\PrintNode;
|
||||
use Twig\NodeTraverser;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
final class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
{
|
||||
protected $statusStack = [];
|
||||
protected $blocks = [];
|
||||
protected $safeAnalysis;
|
||||
protected $traverser;
|
||||
protected $defaultStrategy = false;
|
||||
protected $safeVars = [];
|
||||
private $statusStack = [];
|
||||
private $blocks = [];
|
||||
private $safeAnalysis;
|
||||
private $traverser;
|
||||
private $defaultStrategy = false;
|
||||
private $safeVars = [];
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
@ -48,7 +47,7 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
protected function doEnterNode(Node $node, Environment $env)
|
||||
{
|
||||
if ($node instanceof ModuleNode) {
|
||||
if ($env->hasExtension('\Twig\Extension\EscaperExtension') && $defaultStrategy = $env->getExtension('\Twig\Extension\EscaperExtension')->getDefaultStrategy($node->getTemplateName())) {
|
||||
if ($env->hasExtension(EscaperExtension::class) && $defaultStrategy = $env->getExtension(EscaperExtension::class)->getDefaultStrategy($node->getTemplateName())) {
|
||||
$this->defaultStrategy = $defaultStrategy;
|
||||
}
|
||||
$this->safeVars = [];
|
||||
@ -128,7 +127,7 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
return new InlinePrint($this->getEscaperFilter($type, $expression), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
protected function escapePrintNode(PrintNode $node, Environment $env, $type)
|
||||
private function escapePrintNode(PrintNode $node, Environment $env, $type)
|
||||
{
|
||||
if (false === $type) {
|
||||
return $node;
|
||||
@ -145,7 +144,7 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
return new $class($this->getEscaperFilter($type, $expression), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
protected function preEscapeFilterNode(FilterExpression $filter, Environment $env)
|
||||
private function preEscapeFilterNode(FilterExpression $filter, Environment $env)
|
||||
{
|
||||
$name = $filter->getNode('filter')->getAttribute('value');
|
||||
|
||||
@ -164,7 +163,7 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
return $filter;
|
||||
}
|
||||
|
||||
protected function isSafeFor($type, \Twig_NodeInterface $expression, $env)
|
||||
private function isSafeFor($type, Node $expression, $env)
|
||||
{
|
||||
$safe = $this->safeAnalysis->getSafe($expression);
|
||||
|
||||
@ -182,7 +181,7 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
return \in_array($type, $safe) || \in_array('all', $safe);
|
||||
}
|
||||
|
||||
protected function needEscaping(Environment $env)
|
||||
private function needEscaping(Environment $env)
|
||||
{
|
||||
if (\count($this->statusStack)) {
|
||||
return $this->statusStack[\count($this->statusStack) - 1];
|
||||
@ -191,7 +190,7 @@ class EscaperNodeVisitor extends AbstractNodeVisitor
|
||||
return $this->defaultStrategy ? $this->defaultStrategy : false;
|
||||
}
|
||||
|
||||
protected function getEscaperFilter($type, \Twig_NodeInterface $node)
|
||||
private function getEscaperFilter(string $type, Node $node): FilterExpression
|
||||
{
|
||||
$line = $node->getTemplateLine();
|
||||
$name = new ConstantExpression('escape', $line);
|
||||
|
72
system/libs/Twig/NodeVisitor/MacroAutoImportNodeVisitor.php
Normal file
72
system/libs/Twig/NodeVisitor/MacroAutoImportNodeVisitor.php
Normal file
@ -0,0 +1,72 @@
|
||||
<?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\NodeVisitor;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\Expression\AssignNameExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\MethodCallExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\ImportNode;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class MacroAutoImportNodeVisitor implements NodeVisitorInterface
|
||||
{
|
||||
private $inAModule = false;
|
||||
private $hasMacroCalls = false;
|
||||
|
||||
public function enterNode(Node $node, Environment $env)
|
||||
{
|
||||
if ($node instanceof ModuleNode) {
|
||||
$this->inAModule = true;
|
||||
$this->hasMacroCalls = false;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function leaveNode(Node $node, Environment $env)
|
||||
{
|
||||
if ($node instanceof ModuleNode) {
|
||||
$this->inAModule = false;
|
||||
if ($this->hasMacroCalls) {
|
||||
$node->getNode('constructor_end')->setNode('_auto_macro_import', new ImportNode(new NameExpression('_self', 0), new AssignNameExpression('_self', 0), 0, 'import', true));
|
||||
}
|
||||
} elseif ($this->inAModule) {
|
||||
if (
|
||||
$node instanceof GetAttrExpression &&
|
||||
$node->getNode('node') instanceof NameExpression &&
|
||||
'_self' === $node->getNode('node')->getAttribute('name') &&
|
||||
$node->getNode('attribute') instanceof ConstantExpression
|
||||
) {
|
||||
$this->hasMacroCalls = true;
|
||||
|
||||
$name = $node->getNode('attribute')->getAttribute('value');
|
||||
$node = new MethodCallExpression($node->getNode('node'), 'macro_'.$name, $node->getNode('arguments'), $node->getTemplateLine());
|
||||
$node->setAttribute('safe', true);
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function getPriority()
|
||||
{
|
||||
// we must be ran before auto-escaping
|
||||
return -10;
|
||||
}
|
||||
}
|
@ -12,6 +12,7 @@
|
||||
namespace Twig\NodeVisitor;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Interface for node visitor classes.
|
||||
@ -23,16 +24,16 @@ interface NodeVisitorInterface
|
||||
/**
|
||||
* Called before child nodes are visited.
|
||||
*
|
||||
* @return \Twig_NodeInterface The modified node
|
||||
* @return Node The modified node
|
||||
*/
|
||||
public function enterNode(\Twig_NodeInterface $node, Environment $env);
|
||||
public function enterNode(Node $node, Environment $env);
|
||||
|
||||
/**
|
||||
* Called after child nodes are visited.
|
||||
*
|
||||
* @return \Twig_NodeInterface|false|null The modified node or null if the node must be removed
|
||||
* @return Node|null The modified node or null if the node must be removed
|
||||
*/
|
||||
public function leaveNode(\Twig_NodeInterface $node, Environment $env);
|
||||
public function leaveNode(Node $node, Environment $env);
|
||||
|
||||
/**
|
||||
* Returns the priority for this visitor.
|
||||
|
@ -13,8 +13,6 @@ namespace Twig\NodeVisitor;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\BlockReferenceNode;
|
||||
use Twig\Node\BodyNode;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\BlockReferenceExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\FilterExpression;
|
||||
@ -22,12 +20,10 @@ use Twig\Node\Expression\FunctionExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\ParentExpression;
|
||||
use Twig\Node\Expression\TempNameExpression;
|
||||
use Twig\Node\ForNode;
|
||||
use Twig\Node\IncludeNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\SetTempNode;
|
||||
|
||||
/**
|
||||
* Tries to optimize the AST.
|
||||
@ -37,28 +33,25 @@ use Twig\Node\SetTempNode;
|
||||
* You can configure which optimizations you want to activate via the
|
||||
* optimizer mode.
|
||||
*
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
final class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
{
|
||||
const OPTIMIZE_ALL = -1;
|
||||
const OPTIMIZE_NONE = 0;
|
||||
const OPTIMIZE_FOR = 2;
|
||||
const OPTIMIZE_RAW_FILTER = 4;
|
||||
const OPTIMIZE_VAR_ACCESS = 8;
|
||||
public const OPTIMIZE_ALL = -1;
|
||||
public const OPTIMIZE_NONE = 0;
|
||||
public const OPTIMIZE_FOR = 2;
|
||||
public const OPTIMIZE_RAW_FILTER = 4;
|
||||
// obsolete, does not do anything
|
||||
public const OPTIMIZE_VAR_ACCESS = 8;
|
||||
|
||||
protected $loops = [];
|
||||
protected $loopsTargets = [];
|
||||
protected $optimizers;
|
||||
protected $prependedNodes = [];
|
||||
protected $inABody = false;
|
||||
private $loops = [];
|
||||
private $loopsTargets = [];
|
||||
private $optimizers;
|
||||
|
||||
/**
|
||||
* @param int $optimizers The optimizer mode
|
||||
*/
|
||||
public function __construct($optimizers = -1)
|
||||
public function __construct(int $optimizers = -1)
|
||||
{
|
||||
if (!\is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
|
||||
throw new \InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
|
||||
@ -73,27 +66,11 @@ class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
$this->enterOptimizeFor($node, $env);
|
||||
}
|
||||
|
||||
if (\PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('\Twig\Extension\SandboxExtension')) {
|
||||
if ($this->inABody) {
|
||||
if (!$node instanceof AbstractExpression) {
|
||||
if ('Twig_Node' !== \get_class($node)) {
|
||||
array_unshift($this->prependedNodes, []);
|
||||
}
|
||||
} else {
|
||||
$node = $this->optimizeVariables($node, $env);
|
||||
}
|
||||
} elseif ($node instanceof BodyNode) {
|
||||
$this->inABody = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function doLeaveNode(Node $node, Environment $env)
|
||||
{
|
||||
$expression = $node instanceof AbstractExpression;
|
||||
|
||||
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
|
||||
$this->leaveOptimizeFor($node, $env);
|
||||
}
|
||||
@ -104,33 +81,6 @@ class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
|
||||
$node = $this->optimizePrintNode($node, $env);
|
||||
|
||||
if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('\Twig\Extension\SandboxExtension')) {
|
||||
if ($node instanceof BodyNode) {
|
||||
$this->inABody = false;
|
||||
} elseif ($this->inABody) {
|
||||
if (!$expression && 'Twig_Node' !== \get_class($node) && $prependedNodes = array_shift($this->prependedNodes)) {
|
||||
$nodes = [];
|
||||
foreach (array_unique($prependedNodes) as $name) {
|
||||
$nodes[] = new SetTempNode($name, $node->getTemplateLine());
|
||||
}
|
||||
|
||||
$nodes[] = $node;
|
||||
$node = new Node($nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function optimizeVariables(\Twig_NodeInterface $node, Environment $env)
|
||||
{
|
||||
if ('Twig_Node_Expression_Name' === \get_class($node) && $node->isSimple()) {
|
||||
$this->prependedNodes[0][] = $node->getAttribute('name');
|
||||
|
||||
return new TempNameExpression($node->getAttribute('name'), $node->getTemplateLine());
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
@ -140,10 +90,8 @@ class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
* It replaces:
|
||||
*
|
||||
* * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
|
||||
*
|
||||
* @return \Twig_NodeInterface
|
||||
*/
|
||||
protected function optimizePrintNode(\Twig_NodeInterface $node, Environment $env)
|
||||
private function optimizePrintNode(Node $node, Environment $env): Node
|
||||
{
|
||||
if (!$node instanceof PrintNode) {
|
||||
return $node;
|
||||
@ -164,10 +112,8 @@ class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
|
||||
/**
|
||||
* Removes "raw" filters.
|
||||
*
|
||||
* @return \Twig_NodeInterface
|
||||
*/
|
||||
protected function optimizeRawFilter(\Twig_NodeInterface $node, Environment $env)
|
||||
private function optimizeRawFilter(Node $node, Environment $env): Node
|
||||
{
|
||||
if ($node instanceof FilterExpression && 'raw' == $node->getNode('filter')->getAttribute('value')) {
|
||||
return $node->getNode('node');
|
||||
@ -179,7 +125,7 @@ class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
/**
|
||||
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
|
||||
*/
|
||||
protected function enterOptimizeFor(\Twig_NodeInterface $node, Environment $env)
|
||||
private function enterOptimizeFor(Node $node, Environment $env)
|
||||
{
|
||||
if ($node instanceof ForNode) {
|
||||
// disable the loop variable by default
|
||||
@ -243,7 +189,7 @@ class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
/**
|
||||
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
|
||||
*/
|
||||
protected function leaveOptimizeFor(\Twig_NodeInterface $node, Environment $env)
|
||||
private function leaveOptimizeFor(Node $node, Environment $env)
|
||||
{
|
||||
if ($node instanceof ForNode) {
|
||||
array_shift($this->loops);
|
||||
@ -252,12 +198,12 @@ class OptimizerNodeVisitor extends AbstractNodeVisitor
|
||||
}
|
||||
}
|
||||
|
||||
protected function addLoopToCurrent()
|
||||
private function addLoopToCurrent()
|
||||
{
|
||||
$this->loops[0]->setAttribute('with_loop', true);
|
||||
}
|
||||
|
||||
protected function addLoopToAll()
|
||||
private function addLoopToAll()
|
||||
{
|
||||
foreach ($this->loops as $loop) {
|
||||
$loop->setAttribute('with_loop', true);
|
||||
|
@ -23,20 +23,17 @@ use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\ParentExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*/
|
||||
class SafeAnalysisNodeVisitor extends AbstractNodeVisitor
|
||||
final class SafeAnalysisNodeVisitor extends AbstractNodeVisitor
|
||||
{
|
||||
protected $data = [];
|
||||
protected $safeVars = [];
|
||||
private $data = [];
|
||||
private $safeVars = [];
|
||||
|
||||
public function setSafeVars($safeVars)
|
||||
{
|
||||
$this->safeVars = $safeVars;
|
||||
}
|
||||
|
||||
public function getSafe(\Twig_NodeInterface $node)
|
||||
public function getSafe(Node $node)
|
||||
{
|
||||
$hash = spl_object_hash($node);
|
||||
if (!isset($this->data[$hash])) {
|
||||
@ -56,7 +53,7 @@ class SafeAnalysisNodeVisitor extends AbstractNodeVisitor
|
||||
}
|
||||
}
|
||||
|
||||
protected function setSafe(\Twig_NodeInterface $node, array $safe)
|
||||
private function setSafe(Node $node, array $safe)
|
||||
{
|
||||
$hash = spl_object_hash($node);
|
||||
if (isset($this->data[$hash])) {
|
||||
@ -125,8 +122,7 @@ class SafeAnalysisNodeVisitor extends AbstractNodeVisitor
|
||||
}
|
||||
} elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression) {
|
||||
$name = $node->getNode('node')->getAttribute('name');
|
||||
// attributes on template instances are safe
|
||||
if ('_self' == $name || \in_array($name, $this->safeVars)) {
|
||||
if (\in_array($name, $this->safeVars)) {
|
||||
$this->setSafe($node, ['all']);
|
||||
} else {
|
||||
$this->setSafe($node, []);
|
||||
@ -138,7 +134,7 @@ class SafeAnalysisNodeVisitor extends AbstractNodeVisitor
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function intersectSafe(array $a = null, array $b = null)
|
||||
private function intersectSafe(array $a = null, array $b = null): array
|
||||
{
|
||||
if (null === $a || null === $b) {
|
||||
return [];
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Twig\NodeVisitor;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Node\CheckSecurityCallNode;
|
||||
use Twig\Node\CheckSecurityNode;
|
||||
use Twig\Node\CheckToStringNode;
|
||||
use Twig\Node\Expression\Binary\ConcatBinary;
|
||||
@ -26,16 +27,14 @@ use Twig\Node\PrintNode;
|
||||
use Twig\Node\SetNode;
|
||||
|
||||
/**
|
||||
* @final
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SandboxNodeVisitor extends AbstractNodeVisitor
|
||||
final class SandboxNodeVisitor extends AbstractNodeVisitor
|
||||
{
|
||||
protected $inAModule = false;
|
||||
protected $tags;
|
||||
protected $filters;
|
||||
protected $functions;
|
||||
private $inAModule = false;
|
||||
private $tags;
|
||||
private $filters;
|
||||
private $functions;
|
||||
|
||||
private $needsToStringWrap = false;
|
||||
|
||||
@ -102,7 +101,8 @@ class SandboxNodeVisitor extends AbstractNodeVisitor
|
||||
if ($node instanceof ModuleNode) {
|
||||
$this->inAModule = false;
|
||||
|
||||
$node->getNode('constructor_end')->setNode('_security_check', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('display_start')]));
|
||||
$node->setNode('constructor_end', new Node([new CheckSecurityCallNode(), $node->getNode('constructor_end')]));
|
||||
$node->setNode('class_end', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('class_end')]));
|
||||
} elseif ($this->inAModule) {
|
||||
if ($node instanceof PrintNode || $node instanceof SetNode) {
|
||||
$this->needsToStringWrap = false;
|
||||
@ -112,7 +112,7 @@ class SandboxNodeVisitor extends AbstractNodeVisitor
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function wrapNode(Node $node, $name)
|
||||
private function wrapNode(Node $node, string $name)
|
||||
{
|
||||
$expr = $node->getNode($name);
|
||||
if ($expr instanceof NameExpression || $expr instanceof GetAttrExpression) {
|
||||
@ -120,7 +120,7 @@ class SandboxNodeVisitor extends AbstractNodeVisitor
|
||||
}
|
||||
}
|
||||
|
||||
private function wrapArrayNode(Node $node, $name)
|
||||
private function wrapArrayNode(Node $node, string $name)
|
||||
{
|
||||
$args = $node->getNode($name);
|
||||
foreach ($args as $name => $_) {
|
||||
|
@ -23,8 +23,8 @@ use Twig\Node\Node;
|
||||
use Twig\Node\NodeCaptureInterface;
|
||||
use Twig\Node\NodeOutputInterface;
|
||||
use Twig\Node\PrintNode;
|
||||
use Twig\Node\SpacelessNode;
|
||||
use Twig\Node\TextNode;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
|
||||
/**
|
||||
@ -32,22 +32,21 @@ use Twig\TokenParser\TokenParserInterface;
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Parser implements \Twig_ParserInterface
|
||||
class Parser
|
||||
{
|
||||
protected $stack = [];
|
||||
protected $stream;
|
||||
protected $parent;
|
||||
protected $handlers;
|
||||
protected $visitors;
|
||||
protected $expressionParser;
|
||||
protected $blocks;
|
||||
protected $blockStack;
|
||||
protected $macros;
|
||||
protected $env;
|
||||
protected $reservedMacroNames;
|
||||
protected $importedSymbols;
|
||||
protected $traits;
|
||||
protected $embeddedTemplates = [];
|
||||
private $stack = [];
|
||||
private $stream;
|
||||
private $parent;
|
||||
private $handlers;
|
||||
private $visitors;
|
||||
private $expressionParser;
|
||||
private $blocks;
|
||||
private $blockStack;
|
||||
private $macros;
|
||||
private $env;
|
||||
private $importedSymbols;
|
||||
private $traits;
|
||||
private $embeddedTemplates = [];
|
||||
private $varNameSalt = 0;
|
||||
|
||||
public function __construct(Environment $env)
|
||||
@ -55,48 +54,25 @@ class Parser implements \Twig_ParserInterface
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.27 (to be removed in 2.0)
|
||||
*/
|
||||
public function getEnvironment()
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED);
|
||||
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
public function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->stream->getSourceContext()->getCode().$this->varNameSalt++));
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.27 (to be removed in 2.0). Use $parser->getStream()->getSourceContext()->getPath() instead.
|
||||
*/
|
||||
public function getFilename()
|
||||
{
|
||||
@trigger_error(sprintf('The "%s" method is deprecated since version 1.27 and will be removed in 2.0. Use $parser->getStream()->getSourceContext()->getPath() instead.', __METHOD__), E_USER_DEPRECATED);
|
||||
|
||||
return $this->stream->getSourceContext()->getName();
|
||||
return sprintf('__internal_parse_%d', $this->varNameSalt++);
|
||||
}
|
||||
|
||||
public function parse(TokenStream $stream, $test = null, $dropNeedle = false)
|
||||
{
|
||||
// push all variables into the stack to keep the current state of the parser
|
||||
// using get_object_vars() instead of foreach would lead to https://bugs.php.net/71336
|
||||
// This hack can be removed when min version if PHP 7.0
|
||||
$vars = [];
|
||||
foreach ($this as $k => $v) {
|
||||
$vars[$k] = $v;
|
||||
}
|
||||
|
||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
|
||||
$vars = get_object_vars($this);
|
||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']);
|
||||
$this->stack[] = $vars;
|
||||
|
||||
// tag handlers
|
||||
if (null === $this->handlers) {
|
||||
$this->handlers = $this->env->getTokenParsers();
|
||||
$this->handlers->setParser($this);
|
||||
$this->handlers = [];
|
||||
foreach ($this->env->getTokenParsers() as $handler) {
|
||||
$handler->setParser($this);
|
||||
|
||||
$this->handlers[$handler->getTag()] = $handler;
|
||||
}
|
||||
}
|
||||
|
||||
// node visitors
|
||||
@ -116,7 +92,6 @@ class Parser implements \Twig_ParserInterface
|
||||
$this->blockStack = [];
|
||||
$this->importedSymbols = [[]];
|
||||
$this->embeddedTemplates = [];
|
||||
$this->varNameSalt = 0;
|
||||
|
||||
try {
|
||||
$body = $this->subparse($test, $dropNeedle);
|
||||
@ -156,27 +131,27 @@ class Parser implements \Twig_ParserInterface
|
||||
$rv = [];
|
||||
while (!$this->stream->isEOF()) {
|
||||
switch ($this->getCurrentToken()->getType()) {
|
||||
case Token::TEXT_TYPE:
|
||||
case /* Token::TEXT_TYPE */ 0:
|
||||
$token = $this->stream->next();
|
||||
$rv[] = new TextNode($token->getValue(), $token->getLine());
|
||||
break;
|
||||
|
||||
case Token::VAR_START_TYPE:
|
||||
case /* Token::VAR_START_TYPE */ 2:
|
||||
$token = $this->stream->next();
|
||||
$expr = $this->expressionParser->parseExpression();
|
||||
$this->stream->expect(Token::VAR_END_TYPE);
|
||||
$this->stream->expect(/* Token::VAR_END_TYPE */ 4);
|
||||
$rv[] = new PrintNode($expr, $token->getLine());
|
||||
break;
|
||||
|
||||
case Token::BLOCK_START_TYPE:
|
||||
case /* Token::BLOCK_START_TYPE */ 1:
|
||||
$this->stream->next();
|
||||
$token = $this->getCurrentToken();
|
||||
|
||||
if (Token::NAME_TYPE !== $token->getType()) {
|
||||
if (/* Token::NAME_TYPE */ 5 !== $token->getType()) {
|
||||
throw new SyntaxError('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext());
|
||||
}
|
||||
|
||||
if (null !== $test && \call_user_func($test, $token)) {
|
||||
if (null !== $test && $test($token)) {
|
||||
if ($dropNeedle) {
|
||||
$this->stream->next();
|
||||
}
|
||||
@ -188,8 +163,7 @@ class Parser implements \Twig_ParserInterface
|
||||
return new Node($rv, [], $lineno);
|
||||
}
|
||||
|
||||
$subparser = $this->handlers->getTokenParser($token->getValue());
|
||||
if (null === $subparser) {
|
||||
if (!isset($this->handlers[$token->getValue()])) {
|
||||
if (null !== $test) {
|
||||
$e = new SyntaxError(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext());
|
||||
|
||||
@ -206,6 +180,7 @@ class Parser implements \Twig_ParserInterface
|
||||
|
||||
$this->stream->next();
|
||||
|
||||
$subparser = $this->handlers[$token->getValue()];
|
||||
$node = $subparser->parse($token);
|
||||
if (null !== $node) {
|
||||
$rv[] = $node;
|
||||
@ -224,26 +199,6 @@ class Parser implements \Twig_ParserInterface
|
||||
return new Node($rv, [], $lineno);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.27 (to be removed in 2.0)
|
||||
*/
|
||||
public function addHandler($name, $class)
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED);
|
||||
|
||||
$this->handlers[$name] = $class;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since 1.27 (to be removed in 2.0)
|
||||
*/
|
||||
public function addNodeVisitor(NodeVisitorInterface $visitor)
|
||||
{
|
||||
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.27 and will be removed in 2.0.', E_USER_DEPRECATED);
|
||||
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
public function getBlockStack()
|
||||
{
|
||||
return $this->blockStack;
|
||||
@ -286,28 +241,17 @@ class Parser implements \Twig_ParserInterface
|
||||
|
||||
public function setMacro($name, MacroNode $node)
|
||||
{
|
||||
if ($this->isReservedMacroName($name)) {
|
||||
throw new SyntaxError(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword.', $name), $node->getTemplateLine(), $this->stream->getSourceContext());
|
||||
}
|
||||
|
||||
$this->macros[$name] = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 2.7 as there are no reserved macro names anymore, will be removed in 3.0.
|
||||
*/
|
||||
public function isReservedMacroName($name)
|
||||
{
|
||||
if (null === $this->reservedMacroNames) {
|
||||
$this->reservedMacroNames = [];
|
||||
$r = new \ReflectionClass($this->env->getBaseTemplateClass());
|
||||
foreach ($r->getMethods() as $method) {
|
||||
$methodName = strtolower($method->getName());
|
||||
@trigger_error(sprintf('The "%s" method is deprecated since Twig 2.7 and will be removed in 3.0.', __METHOD__), \E_USER_DEPRECATED);
|
||||
|
||||
if ('get' === substr($methodName, 0, 3) && isset($methodName[3])) {
|
||||
$this->reservedMacroNames[] = substr($methodName, 3);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return \in_array(strtolower($name), $this->reservedMacroNames);
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addTrait($trait)
|
||||
@ -334,19 +278,8 @@ class Parser implements \Twig_ParserInterface
|
||||
|
||||
public function getImportedSymbol($type, $alias)
|
||||
{
|
||||
if (null !== $this->peekBlockStack()) {
|
||||
foreach ($this->importedSymbols as $functions) {
|
||||
if (isset($functions[$type][$alias])) {
|
||||
if (\count($this->blockStack) > 1) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return $functions[$type][$alias];
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return isset($this->importedSymbols[0][$type][$alias]) ? $this->importedSymbols[0][$type][$alias] : null;
|
||||
}
|
||||
// if the symbol does not exist in the current scope (0), try in the main/global scope (last index)
|
||||
return $this->importedSymbols[0][$type][$alias] ?? ($this->importedSymbols[\count($this->importedSymbols) - 1][$type][$alias] ?? null);
|
||||
}
|
||||
|
||||
public function isMainScope()
|
||||
@ -398,13 +331,14 @@ class Parser implements \Twig_ParserInterface
|
||||
return $this->stream->getCurrent();
|
||||
}
|
||||
|
||||
protected function filterBodyNodes(\Twig_NodeInterface $node)
|
||||
private function filterBodyNodes(Node $node, bool $nested = false)
|
||||
{
|
||||
// check that the body does not contain non-empty output nodes
|
||||
if (
|
||||
($node instanceof TextNode && !ctype_space($node->getAttribute('data')))
|
||||
||
|
||||
(!$node instanceof TextNode && !$node instanceof BlockReferenceNode && $node instanceof NodeOutputInterface)
|
||||
// the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
|
||||
(!$node instanceof TextNode && !$node instanceof BlockReferenceNode && ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode))
|
||||
) {
|
||||
if (false !== strpos((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) {
|
||||
$t = substr($node->getAttribute('data'), 3);
|
||||
@ -417,17 +351,37 @@ class Parser implements \Twig_ParserInterface
|
||||
throw new SyntaxError('A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag?', $node->getTemplateLine(), $this->stream->getSourceContext());
|
||||
}
|
||||
|
||||
// bypass nodes that will "capture" the output
|
||||
// bypass nodes that "capture" the output
|
||||
if ($node instanceof NodeCaptureInterface) {
|
||||
// a "block" tag in such a node will serve as a block definition AND be displayed in place as well
|
||||
return $node;
|
||||
}
|
||||
|
||||
if ($node instanceof NodeOutputInterface) {
|
||||
// to be removed completely in Twig 3.0
|
||||
if (!$nested && $node instanceof SpacelessNode) {
|
||||
@trigger_error(sprintf('Using the spaceless tag at the root level of a child template in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
// "block" tags that are not captured (see above) are only used for defining
|
||||
// the content of the block. In such a case, nesting it does not work as
|
||||
// expected as the definition is not part of the default template code flow.
|
||||
if ($nested && ($node instanceof BlockReferenceNode || $node instanceof \Twig_Node_BlockReference)) {
|
||||
//throw new SyntaxError('A block definition cannot be nested under non-capturing nodes.', $node->getTemplateLine(), $this->stream->getSourceContext());
|
||||
@trigger_error(sprintf('Nesting a block definition under a non-capturing node in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), \E_USER_DEPRECATED);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0
|
||||
if ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode) {
|
||||
return;
|
||||
}
|
||||
|
||||
// here, $nested means "being at the root level of a child template"
|
||||
// we need to discard the wrapping "Twig_Node" for the "body" node
|
||||
$nested = $nested || ('Twig_Node' !== \get_class($node) && Node::class !== \get_class($node));
|
||||
foreach ($node as $k => $n) {
|
||||
if (null !== $n && null === $this->filterBodyNodes($n)) {
|
||||
if (null !== $n && null === $this->filterBodyNodes($n, $nested)) {
|
||||
$node->removeNode($k);
|
||||
}
|
||||
}
|
||||
|
@ -31,7 +31,7 @@ abstract class BaseDumper
|
||||
|
||||
abstract protected function formatTime(Profile $profile, $percent);
|
||||
|
||||
private function dumpProfile(Profile $profile, $prefix = '', $sibling = false)
|
||||
private function dumpProfile(Profile $profile, $prefix = '', $sibling = false): string
|
||||
{
|
||||
if ($profile->isRoot()) {
|
||||
$this->root = $profile->getDuration();
|
||||
|
@ -15,10 +15,8 @@ use Twig\Profiler\Profile;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class BlackfireDumper
|
||||
final class BlackfireDumper
|
||||
{
|
||||
public function dump(Profile $profile)
|
||||
{
|
||||
@ -42,7 +40,7 @@ EOF;
|
||||
return $str;
|
||||
}
|
||||
|
||||
private function dumpChildren($parent, Profile $profile, &$data)
|
||||
private function dumpChildren(string $parent, Profile $profile, &$data)
|
||||
{
|
||||
foreach ($profile as $p) {
|
||||
if ($p->isTemplate()) {
|
||||
@ -55,7 +53,7 @@ EOF;
|
||||
}
|
||||
}
|
||||
|
||||
private function dumpProfile($edge, Profile $profile, &$data)
|
||||
private function dumpProfile(string $edge, Profile $profile, &$data)
|
||||
{
|
||||
if (isset($data[$edge])) {
|
||||
++$data[$edge]['ct'];
|
||||
|
@ -15,10 +15,8 @@ use Twig\Profiler\Profile;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class HtmlDumper extends BaseDumper
|
||||
final class HtmlDumper extends BaseDumper
|
||||
{
|
||||
private static $colors = [
|
||||
'block' => '#dfd',
|
||||
|
@ -15,10 +15,8 @@ use Twig\Profiler\Profile;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class TextDumper extends BaseDumper
|
||||
final class TextDumper extends BaseDumper
|
||||
{
|
||||
protected function formatTemplate(Profile $profile, $prefix)
|
||||
{
|
||||
|
@ -21,7 +21,7 @@ use Twig\Node\Node;
|
||||
*/
|
||||
class EnterProfileNode extends Node
|
||||
{
|
||||
public function __construct($extensionName, $type, $name, $varName)
|
||||
public function __construct(string $extensionName, string $type, string $name, string $varName)
|
||||
{
|
||||
parent::__construct([], ['extension_name' => $extensionName, 'name' => $name, 'type' => $type, 'var_name' => $varName]);
|
||||
}
|
||||
@ -29,9 +29,9 @@ class EnterProfileNode extends Node
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write(sprintf('$%s = $this->env->getExtension(', $this->getAttribute('var_name')))
|
||||
->write(sprintf('$%s = $this->extensions[', $this->getAttribute('var_name')))
|
||||
->repr($this->getAttribute('extension_name'))
|
||||
->raw(");\n")
|
||||
->raw("];\n")
|
||||
->write(sprintf('$%s->enter($%s = new \Twig\Profiler\Profile($this->getTemplateName(), ', $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof'))
|
||||
->repr($this->getAttribute('type'))
|
||||
->raw(', ')
|
||||
|
@ -21,7 +21,7 @@ use Twig\Node\Node;
|
||||
*/
|
||||
class LeaveProfileNode extends Node
|
||||
{
|
||||
public function __construct($varName)
|
||||
public function __construct(string $varName)
|
||||
{
|
||||
parent::__construct([], ['var_name' => $varName]);
|
||||
}
|
||||
|
@ -24,16 +24,16 @@ use Twig\Profiler\Profile;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class ProfilerNodeVisitor extends AbstractNodeVisitor
|
||||
final class ProfilerNodeVisitor extends AbstractNodeVisitor
|
||||
{
|
||||
private $extensionName;
|
||||
private $varName;
|
||||
|
||||
public function __construct($extensionName)
|
||||
public function __construct(string $extensionName)
|
||||
{
|
||||
$this->extensionName = $extensionName;
|
||||
$this->varName = sprintf('__internal_%s', hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $extensionName));
|
||||
}
|
||||
|
||||
protected function doEnterNode(Node $node, Environment $env)
|
||||
@ -44,33 +44,25 @@ class ProfilerNodeVisitor extends AbstractNodeVisitor
|
||||
protected function doLeaveNode(Node $node, Environment $env)
|
||||
{
|
||||
if ($node instanceof ModuleNode) {
|
||||
$varName = $this->getVarName();
|
||||
$node->setNode('display_start', new Node([new EnterProfileNode($this->extensionName, Profile::TEMPLATE, $node->getTemplateName(), $varName), $node->getNode('display_start')]));
|
||||
$node->setNode('display_end', new Node([new LeaveProfileNode($varName), $node->getNode('display_end')]));
|
||||
$node->setNode('display_start', new Node([new EnterProfileNode($this->extensionName, Profile::TEMPLATE, $node->getTemplateName(), $this->varName), $node->getNode('display_start')]));
|
||||
$node->setNode('display_end', new Node([new LeaveProfileNode($this->varName), $node->getNode('display_end')]));
|
||||
} elseif ($node instanceof BlockNode) {
|
||||
$varName = $this->getVarName();
|
||||
$node->setNode('body', new BodyNode([
|
||||
new EnterProfileNode($this->extensionName, Profile::BLOCK, $node->getAttribute('name'), $varName),
|
||||
new EnterProfileNode($this->extensionName, Profile::BLOCK, $node->getAttribute('name'), $this->varName),
|
||||
$node->getNode('body'),
|
||||
new LeaveProfileNode($varName),
|
||||
new LeaveProfileNode($this->varName),
|
||||
]));
|
||||
} elseif ($node instanceof MacroNode) {
|
||||
$varName = $this->getVarName();
|
||||
$node->setNode('body', new BodyNode([
|
||||
new EnterProfileNode($this->extensionName, Profile::MACRO, $node->getAttribute('name'), $varName),
|
||||
new EnterProfileNode($this->extensionName, Profile::MACRO, $node->getAttribute('name'), $this->varName),
|
||||
$node->getNode('body'),
|
||||
new LeaveProfileNode($varName),
|
||||
new LeaveProfileNode($this->varName),
|
||||
]));
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s', hash('sha256', $this->extensionName));
|
||||
}
|
||||
|
||||
public function getPriority()
|
||||
{
|
||||
return 0;
|
||||
|
@ -14,14 +14,14 @@ namespace Twig\Profiler;
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @final
|
||||
* @final since Twig 2.4.0
|
||||
*/
|
||||
class Profile implements \IteratorAggregate, \Serializable
|
||||
{
|
||||
const ROOT = 'ROOT';
|
||||
const BLOCK = 'block';
|
||||
const TEMPLATE = 'template';
|
||||
const MACRO = 'macro';
|
||||
public const ROOT = 'ROOT';
|
||||
public const BLOCK = 'block';
|
||||
public const TEMPLATE = 'template';
|
||||
public const MACRO = 'macro';
|
||||
|
||||
private $template;
|
||||
private $name;
|
||||
@ -30,8 +30,12 @@ class Profile implements \IteratorAggregate, \Serializable
|
||||
private $ends = [];
|
||||
private $profiles = [];
|
||||
|
||||
public function __construct($template = 'main', $type = self::ROOT, $name = 'main')
|
||||
public function __construct(string $template = 'main', string $type = self::ROOT, string $name = 'main')
|
||||
{
|
||||
if (__CLASS__ !== static::class) {
|
||||
@trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', \E_USER_DEPRECATED);
|
||||
}
|
||||
|
||||
$this->template = $template;
|
||||
$this->type = $type;
|
||||
$this->name = 0 === strpos($name, '__internal_') ? 'INTERNAL' : $name;
|
||||
@ -153,17 +157,18 @@ class Profile implements \IteratorAggregate, \Serializable
|
||||
$this->enter();
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
#[\ReturnTypeWillChange]
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
return new \ArrayIterator($this->profiles);
|
||||
}
|
||||
|
||||
public function serialize()
|
||||
public function serialize(): string
|
||||
{
|
||||
return serialize($this->__serialize());
|
||||
}
|
||||
|
||||
public function unserialize($data)
|
||||
public function unserialize($data): void
|
||||
{
|
||||
$this->__unserialize(unserialize($data));
|
||||
}
|
||||
@ -171,7 +176,7 @@ class Profile implements \IteratorAggregate, \Serializable
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function __serialize()
|
||||
public function __serialize(): array
|
||||
{
|
||||
return [$this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles];
|
||||
}
|
||||
@ -179,7 +184,7 @@ class Profile implements \IteratorAggregate, \Serializable
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function __unserialize(array $data)
|
||||
public function __unserialize(array $data): void
|
||||
{
|
||||
list($this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles) = $data;
|
||||
}
|
||||
|
@ -23,7 +23,7 @@ class FactoryRuntimeLoader implements RuntimeLoaderInterface
|
||||
/**
|
||||
* @param array $map An array where keys are class names and values factory callables
|
||||
*/
|
||||
public function __construct($map = [])
|
||||
public function __construct(array $map = [])
|
||||
{
|
||||
$this->map = $map;
|
||||
}
|
||||
|
@ -15,13 +15,24 @@ namespace Twig\Sandbox;
|
||||
* Exception thrown when a not allowed filter is used in a template.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class SecurityNotAllowedFilterError extends SecurityError
|
||||
{
|
||||
private $filterName;
|
||||
|
||||
public function __construct($message, $functionName, $lineno = -1, $filename = null, \Exception $previous = null)
|
||||
public function __construct(string $message, string $functionName, int $lineno = -1, string $filename = null, \Exception $previous = null)
|
||||
{
|
||||
if (-1 !== $lineno) {
|
||||
@trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $filename) {
|
||||
@trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $previous) {
|
||||
@trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->filterName = $functionName;
|
||||
}
|
||||
|
@ -15,13 +15,24 @@ namespace Twig\Sandbox;
|
||||
* Exception thrown when a not allowed function is used in a template.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class SecurityNotAllowedFunctionError extends SecurityError
|
||||
{
|
||||
private $functionName;
|
||||
|
||||
public function __construct($message, $functionName, $lineno = -1, $filename = null, \Exception $previous = null)
|
||||
public function __construct(string $message, string $functionName, int $lineno = -1, string $filename = null, \Exception $previous = null)
|
||||
{
|
||||
if (-1 !== $lineno) {
|
||||
@trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $filename) {
|
||||
@trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $previous) {
|
||||
@trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->functionName = $functionName;
|
||||
}
|
||||
|
@ -15,14 +15,25 @@ namespace Twig\Sandbox;
|
||||
* Exception thrown when a not allowed class method is used in a template.
|
||||
*
|
||||
* @author Kit Burton-Senior <mail@kitbs.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class SecurityNotAllowedMethodError extends SecurityError
|
||||
{
|
||||
private $className;
|
||||
private $methodName;
|
||||
|
||||
public function __construct($message, $className, $methodName, $lineno = -1, $filename = null, \Exception $previous = null)
|
||||
public function __construct(string $message, string $className, string $methodName, int $lineno = -1, string $filename = null, \Exception $previous = null)
|
||||
{
|
||||
if (-1 !== $lineno) {
|
||||
@trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $filename) {
|
||||
@trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $previous) {
|
||||
@trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->className = $className;
|
||||
$this->methodName = $methodName;
|
||||
|
@ -15,14 +15,25 @@ namespace Twig\Sandbox;
|
||||
* Exception thrown when a not allowed class property is used in a template.
|
||||
*
|
||||
* @author Kit Burton-Senior <mail@kitbs.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class SecurityNotAllowedPropertyError extends SecurityError
|
||||
{
|
||||
private $className;
|
||||
private $propertyName;
|
||||
|
||||
public function __construct($message, $className, $propertyName, $lineno = -1, $filename = null, \Exception $previous = null)
|
||||
public function __construct(string $message, string $className, string $propertyName, int $lineno = -1, string $filename = null, \Exception $previous = null)
|
||||
{
|
||||
if (-1 !== $lineno) {
|
||||
@trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $filename) {
|
||||
@trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $previous) {
|
||||
@trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->className = $className;
|
||||
$this->propertyName = $propertyName;
|
||||
|
@ -15,13 +15,24 @@ namespace Twig\Sandbox;
|
||||
* Exception thrown when a not allowed tag is used in a template.
|
||||
*
|
||||
* @author Martin Hasoň <martin.hason@gmail.com>
|
||||
*
|
||||
* @final
|
||||
*/
|
||||
class SecurityNotAllowedTagError extends SecurityError
|
||||
{
|
||||
private $tagName;
|
||||
|
||||
public function __construct($message, $tagName, $lineno = -1, $filename = null, \Exception $previous = null)
|
||||
public function __construct(string $message, string $tagName, int $lineno = -1, string $filename = null, \Exception $previous = null)
|
||||
{
|
||||
if (-1 !== $lineno) {
|
||||
@trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $filename) {
|
||||
@trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
if (null !== $previous) {
|
||||
@trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), \E_USER_DEPRECATED);
|
||||
}
|
||||
parent::__construct($message, $lineno, $filename, $previous);
|
||||
$this->tagName = $tagName;
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user