Commit 6caf112d authored by catch's avatar catch

Issue #2609110 by andypost, hussainweb: Update Twig to 1.23.1

parent af813036
......@@ -1919,16 +1919,16 @@
},
{
"name": "twig/twig",
"version": "v1.22.2",
"version": "v1.23.1",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a"
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
"reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
"shasum": ""
},
"require": {
......@@ -1941,7 +1941,7 @@
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.22-dev"
"dev-master": "1.23-dev"
}
},
"autoload": {
......@@ -1976,7 +1976,7 @@
"keywords": [
"templating"
],
"time": "2015-09-22 13:59:32"
"time": "2015-11-05 12:49:06"
},
{
"name": "wikimedia/composer-merge-plugin",
......@@ -1988,7 +1988,7 @@
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/ef66d8b92b741d6278dace3658afc35416831ff1",
"url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/f770a4dd9771041f713f5ba3d8e4ba702015653d",
"reference": "47bb3388cfeae41a38087ac8465a7d08fa92ea2e",
"shasum": ""
},
......
......@@ -17,7 +17,7 @@
"symfony/validator": "2.7.*",
"symfony/process": "2.7.*",
"symfony/yaml": "2.7.*",
"twig/twig": "^1.22.2",
"twig/twig": "^1.23.1",
"doctrine/common": "2.5.*",
"doctrine/annotations": "1.2.*",
"guzzlehttp/guzzle": "~6.1",
......
......@@ -107,7 +107,7 @@
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/ef66d8b92b741d6278dace3658afc35416831ff1",
"url": "https://api.github.com/repos/wikimedia/composer-merge-plugin/zipball/f770a4dd9771041f713f5ba3d8e4ba702015653d",
"reference": "47bb3388cfeae41a38087ac8465a7d08fa92ea2e",
"shasum": ""
},
......@@ -1177,69 +1177,6 @@
"psr-7"
]
},
{
"name": "twig/twig",
"version": "v1.22.2",
"version_normalized": "1.22.2.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
"reference": "79249fc8c9ff62e41e217e0c630e2e00bcadda6a",
"shasum": ""
},
"require": {
"php": ">=5.2.7"
},
"require-dev": {
"symfony/debug": "~2.7",
"symfony/phpunit-bridge": "~2.7"
},
"time": "2015-09-22 13:59:32",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.22-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "http://twig.sensiolabs.org/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
]
},
{
"name": "zendframework/zend-diactoros",
"version": "1.1.3",
......@@ -3858,5 +3795,68 @@
],
"description": "Symfony BrowserKit Component",
"homepage": "https://symfony.com"
},
{
"name": "twig/twig",
"version": "v1.23.1",
"version_normalized": "1.23.1.0",
"source": {
"type": "git",
"url": "https://github.com/twigphp/Twig.git",
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/twigphp/Twig/zipball/d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
"reference": "d9b6333ae8dd2c8e3fd256e127548def0bc614c6",
"shasum": ""
},
"require": {
"php": ">=5.2.7"
},
"require-dev": {
"symfony/debug": "~2.7",
"symfony/phpunit-bridge": "~2.7"
},
"time": "2015-11-05 12:49:06",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.23-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "http://twig.sensiolabs.org/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
]
}
]
* 1.23.1 (2015-XX-XX)
* fixed some exception messages which triggered PHP warnings
* fixed BC on Twig_Test_NodeTestCase
* 1.23.0 (2015-10-29)
* deprecated the possibility to override an extension by registering another one with the same name
* deprecated Twig_ExtensionInterface::getGlobals() (added Twig_Extension_GlobalsInterface for BC)
* deprecated Twig_ExtensionInterface::initRuntime() (added Twig_Extension_InitRuntimeInterface for BC)
* deprecated Twig_Environment::computeAlternatives()
* 1.22.3 (2015-10-13)
* fixed regression when using null as a cache strategy
* improved performance when checking template freshness
* fixed warnings when loaded templates do not exist
* fixed template class name generation to prevent possible collisions
* fixed logic for custom escapers to call them even on integers and null values
* changed template cache names to take into account the Twig C extension
* 1.22.2 (2015-09-22)
* fixed a race condition in template loading
......
......@@ -40,7 +40,7 @@
},
"extra": {
"branch-alias": {
"dev-master": "1.22-dev"
"dev-master": "1.23-dev"
}
}
}
......@@ -38,7 +38,7 @@ public function generateKey($name, $className)
{
$hash = hash('sha256', $className);
return $this->directory.'/'.$hash[0].'/'.$hash[1].'/'.$hash.'.php';
return $this->directory.'/'.$hash[0].$hash[1].'/'.$hash.'.php';
}
/**
......@@ -87,6 +87,10 @@ public function write($key, $content)
*/
public function getTimestamp($key)
{
if (!file_exists($key)) {
return 0;
}
return (int) @filemtime($key);
}
}
......@@ -16,7 +16,7 @@
*/
class Twig_Environment
{
const VERSION = '1.22.2';
const VERSION = '1.23.1';
protected $charset;
protected $loader;
......@@ -48,6 +48,7 @@ class Twig_Environment
private $originalCache;
private $bcWriteCacheFile = false;
private $bcGetCacheFilename = false;
private $lastModifiedExtension = 0;
/**
* Constructor.
......@@ -263,6 +264,10 @@ public function setCache($cache)
} elseif (false === $cache) {
$this->originalCache = $cache;
$this->cache = new Twig_Cache_Null();
} elseif (null === $cache) {
@trigger_error('Using "null" as the cache strategy is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED);
$this->originalCache = false;
$this->cache = new Twig_Cache_Null();
} elseif ($cache instanceof Twig_CacheInterface) {
$this->originalCache = $this->cache = $cache;
} else {
......@@ -291,14 +296,22 @@ public function getCacheFilename($name)
/**
* Gets the template class associated with the given string.
*
* @param string $name The name for which to calculate the template class name
* @param int $index The index if it is an embedded template
* The generated template class is based on the following parameters:
*
* * The cache key for the given template;
* * The currently enabled extensions;
* * Whether the Twig C extension is available or not.
*
* @param string $name The name for which to calculate the template class name
* @param int|null $index The index if it is an embedded template
*
* @return string The template class name
*/
public function getTemplateClass($name, $index = null)
{
$key = $this->getLoader()->getCacheKey($name).'__'.implode('__', array_keys($this->extensions));
$key = $this->getLoader()->getCacheKey($name);
$key .= json_encode(array_keys($this->extensions));
$key .= function_exists('twig_template_get_attributes');
return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index);
}
......@@ -446,14 +459,16 @@ public function createTemplate($template)
*/
public function isTemplateFresh($name, $time)
{
foreach ($this->extensions as $extension) {
$r = new ReflectionObject($extension);
if (filemtime($r->getFileName()) > $time) {
return false;
if (0 === $this->lastModifiedExtension) {
foreach ($this->extensions as $extension) {
$r = new ReflectionObject($extension);
if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModifiedExtension) {
$this->lastModifiedExtension = $extensionTime;
}
}
}
return $this->getLoader()->isFresh($name, $time);
return $this->lastModifiedExtension <= $time && $this->getLoader()->isFresh($name, $time);
}
/**
......@@ -710,12 +725,22 @@ public function getCharset()
/**
* Initializes the runtime environment.
*
* @deprecated since 1.23 (to be removed in 2.0)
*/
public function initRuntime()
{
$this->runtimeInitialized = true;
foreach ($this->getExtensions() as $extension) {
foreach ($this->getExtensions() as $name => $extension) {
if (!$extension instanceof Twig_Extension_InitRuntimeInterface) {
$m = new ReflectionMethod($extension, 'initRuntime');
if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Defining the initRuntime() method in the "%s" extension is deprecated. Use the `needs_environment` option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended).', $name), E_USER_DEPRECATED);
}
}
$extension->initRuntime($this);
}
}
......@@ -755,11 +780,19 @@ public function getExtension($name)
*/
public function addExtension(Twig_ExtensionInterface $extension)
{
$name = $extension->getName();
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName()));
throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $name));
}
$this->extensions[$extension->getName()] = $extension;
if (isset($this->extensions[$name])) {
@trigger_error(sprintf('The possibility to register the same extension twice ("%s") is deprecated and will be removed in Twig 2.0. Use proper PHP inheritance instead.', $name), E_USER_DEPRECATED);
}
$this->lastModifiedExtension = 0;
$this->extensions[$name] = $extension;
}
/**
......@@ -1218,24 +1251,28 @@ public function getBinaryOperators()
return $this->binaryOperators;
}
/**
* @deprecated since 1.23 (to be removed in 2.0)
*/
public function computeAlternatives($name, $items)
{
$alternatives = array();
foreach ($items as $item) {
$lev = levenshtein($name, $item);
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = $lev;
}
}
asort($alternatives);
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
return array_keys($alternatives);
return Twig_Error_Syntax::computeAlternatives($name, $items);
}
protected function initGlobals()
{
$globals = array();
foreach ($this->extensions as $extension) {
foreach ($this->extensions as $name => $extension) {
if (!$extension instanceof Twig_Extension_GlobalsInterface) {
$m = new ReflectionMethod($extension, 'getGlobals');
if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension is deprecated without explicitly implementing Twig_Extension_GlobalsInterface.', $name), E_USER_DEPRECATED);
}
}
$extGlob = $extension->getGlobals();
if (!is_array($extGlob)) {
throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
......
......@@ -155,6 +155,15 @@ public function __call($method, $arguments)
throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
}
public function appendMessage($rawMessage)
{
$this->rawMessage .= $rawMessage;
$this->updateRepr();
}
/**
* @internal
*/
protected function updateRepr()
{
$this->message = $this->rawMessage;
......@@ -165,6 +174,12 @@ protected function updateRepr()
$dot = true;
}
$questionMark = false;
if ('?' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$questionMark = true;
}
if ($this->filename) {
if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
$filename = sprintf('"%s"', $this->filename);
......@@ -181,8 +196,15 @@ protected function updateRepr()
if ($dot) {
$this->message .= '.';
}
if ($questionMark) {
$this->message .= '?';
}
}
/**
* @internal
*/
protected function guessTemplateInfo()
{
$template = null;
......
......@@ -17,4 +17,37 @@
*/
class Twig_Error_Syntax extends Twig_Error
{
/**
* Tweaks the error message to include suggestions.
*
* @param string $name The original name of the item that does not exist
* @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 = array();
foreach ($items as $item) {
$lev = levenshtein($name, $item);
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = $lev;
}
}
asort($alternatives);
return array_keys($alternatives);
}
}
......@@ -171,7 +171,7 @@ public function parsePrimaryExpression()
$negClass = 'Twig_Node_Expression_Unary_Neg';
$posClass = 'Twig_Node_Expression_Unary_Pos';
if (!(in_array($ref->getName(), array($negClass, $posClass)) || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass))) {
throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename());
throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
$this->parser->getStream()->next();
......@@ -187,7 +187,7 @@ public function parsePrimaryExpression()
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
$node = $this->parseHashExpression();
} else {
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
}
......@@ -278,7 +278,7 @@ public function parseHashExpression()
} else {
$current = $stream->getCurrent();
throw new Twig_Error_Syntax(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"', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
throw new Twig_Error_Syntax(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".', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
......@@ -317,11 +317,11 @@ public function getFunctionNode($name, $line)
case 'parent':
$this->parseArguments();
if (!count($this->parser->getBlockStack())) {
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename());
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden.', $line, $this->parser->getFilename());
}
if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line, $this->parser->getFilename());
throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getFilename());
}
return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
......@@ -330,7 +330,7 @@ public function getFunctionNode($name, $line)
case 'attribute':
$args = $this->parseArguments();
if (count($args) < 2) {
throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename());
throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getFilename());
}
return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line);
......@@ -384,13 +384,13 @@ public function parseSubscriptExpression($node)
if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
if (!$arg instanceof Twig_Node_Expression_Constant) {
throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
}
$name = $arg->getAttribute('value');
if ($this->parser->isReservedMacroName($name)) {
throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword', $name), $token->getLine(), $this->parser->getFilename());
throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $this->parser->getFilename());
}
$node = new Twig_Node_Expression_MethodCall($node, 'get'.$name, $arguments, $lineno);
......@@ -500,7 +500,7 @@ public function parseArguments($namedArguments = false, $definition = false)
$name = null;
if ($namedArguments && $token = $stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
if (!$value instanceof Twig_Node_Expression_Name) {
throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename());
throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given.', get_class($value)), $token->getLine(), $this->parser->getFilename());
}
$name = $value->getAttribute('name');
......@@ -540,7 +540,7 @@ public function parseAssignmentExpression()
while (true) {
$token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
if (in_array($token->getValue(), array('true', 'false', 'none'))) {
throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename());
throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s".', $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
$targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine());
......@@ -570,12 +570,10 @@ protected function getFunctionNodeClass($name, $line)
$env = $this->parser->getEnvironment();
if (false === $function = $env->getFunction($name)) {
$message = sprintf('The function "%s" does not exist', $name);
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFunctions()))) {
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
}
$e = new Twig_Error_Syntax(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getFilename());
$e->addSuggestions($name, array_keys($env->getFunctions()));
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
throw $e;
}
if ($function instanceof Twig_SimpleFunction && $function->isDeprecated()) {
......@@ -600,12 +598,10 @@ protected function getFilterNodeClass($name, $line)
$env = $this->parser->getEnvironment();
if (false === $filter = $env->getFilter($name)) {
$message = sprintf('The filter "%s" does not exist', $name);
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFilters()))) {
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
}
$e = new Twig_Error_Syntax(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getFilename());
$e->addSuggestions($name, array_keys($env->getFilters()));
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
throw $e;
}
if ($filter instanceof Twig_SimpleFilter && $filter->isDeprecated()) {
......
......@@ -11,20 +11,16 @@
abstract class Twig_Extension implements Twig_ExtensionInterface
{
/**
* Initializes the runtime environment.
* {@inheritdoc}
*
* This is where you can load some file that contains filter functions for instance.
*
* @param Twig_Environment $environment The current Twig_Environment instance
* @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterface instead
*/
public function initRuntime(Twig_Environment $environment)
{
}
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
* {@inheritdoc}
*/
public function getTokenParsers()
{
......@@ -32,9 +28,7 @@ public function getTokenParsers()
}
/**
* Returns the node visitor instances to add to the existing list.
*
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
* {@inheritdoc}
*/
public function getNodeVisitors()
{
......@@ -42,9 +36,7 @@ public function getNodeVisitors()
}
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
* {@inheritdoc}
*/
public function getFilters()
{
......@@ -52,9 +44,7 @@ public function getFilters()
}
/**
* Returns a list of tests to add to the existing list.
*
* @return array An array of tests
* {@inheritdoc}
*/
public function getTests()
{
......@@ -62,9 +52,7 @@ public function getTests()
}
/**
* Returns a list of functions to add to the existing list.