Merge branch '1.x' into 2.x

* 1.x:
  Update .travis.yml
  Fix possible array to string conversion concealing actual error
  Make variable names deterministic
  Be able to count on iterator
  made it clear that placeholders are free-forms for the replace filter
  contents -> content
This commit is contained in:
Fabien Potencier
2018-03-01 16:12:51 -08:00
11 changed files with 47 additions and 6 deletions
+1
View File
@@ -11,6 +11,7 @@ matrix:
include:
- php: 7.0
- php: 7.1
- php: 7.2
- php: nightly
fast_finish: true
+2
View File
@@ -14,6 +14,8 @@ return value of the ``count()`` method.
For objects that implement the ``__toString()`` magic method (and not ``Countable``),
it will return the length of the string provided by that method.
For objects that implement the ``IteratorAggregate`` interface, ``length`` will use the return value of the ``iterator_count()`` method.
.. code-block:: jinja
{% if users|length > 10 %}
+6
View File
@@ -11,6 +11,12 @@ The ``replace`` filter formats a given string by replacing the placeholders
{# outputs I like foo and bar
if the foo parameter equals to the foo string. #}
{# using % as a delimiter is purely conventional and optional #}
{{ "I like this and --that--."|replace({'this': foo, '--that--': "bar"}) }}
{# outputs I like foo and bar #}
Arguments
---------
+3 -1
View File
@@ -24,6 +24,7 @@ class Twig_Compiler
private $debugInfo = array();
private $sourceOffset;
private $sourceLine;
private $varNameSalt = 0;
public function __construct(Twig_Environment $env)
{
@@ -67,6 +68,7 @@ class Twig_Compiler
// source code starts at 1 (as we then increment it when we encounter new lines)
$this->sourceLine = 1;
$this->indentation = $indentation;
$this->varNameSalt = 0;
$node->compile($this);
@@ -233,7 +235,7 @@ class Twig_Compiler
public function getVarName()
{
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++));
}
}
+6
View File
@@ -1152,6 +1152,10 @@ function twig_length_filter(Twig_Environment $env, $thing)
return count($thing);
}
if ($thing instanceof \IteratorAggregate) {
return iterator_count($thing);
}
return 1;
}
@@ -1490,6 +1494,8 @@ function twig_get_attribute(Twig_Environment $env, Twig_Source $source, $object,
if (null === $object) {
$message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item);
} elseif (is_array($object)) {
$message = sprintf('Impossible to invoke a method ("%s") on an array.', $item);
} else {
$message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, gettype($object), $object);
}
+4 -2
View File
@@ -30,6 +30,7 @@ class Twig_Parser
private $importedSymbols;
private $traits;
private $embeddedTemplates = array();
private $varNameSalt = 0;
public function __construct(Twig_Environment $env)
{
@@ -38,7 +39,7 @@ class Twig_Parser
public function getVarName()
{
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++));
}
public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
@@ -74,6 +75,7 @@ class Twig_Parser
$this->blockStack = array();
$this->importedSymbols = array(array());
$this->embeddedTemplates = array();
$this->varNameSalt = 0;
try {
$body = $this->subparse($test, $dropNeedle);
@@ -323,7 +325,7 @@ class Twig_Parser
throw new Twig_Error_Syntax('A template that extends another one cannot start with a byte order mark (BOM); it must be removed.', $node->getTemplateLine(), $this->stream->getSourceContext());
}
throw new Twig_Error_Syntax('A template that extends another one cannot include contents outside Twig blocks. Did you forget to put the contents inside a {% block %} tag?', $node->getTemplateLine(), $this->stream->getSourceContext());
throw new Twig_Error_Syntax('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
+1 -1
View File
@@ -53,7 +53,7 @@ final class Twig_Profiler_NodeVisitor_Profiler extends Twig_BaseNodeVisitor
private function getVarName()
{
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
return sprintf('__internal_%s', hash('sha256', __METHOD__));
}
public function getPriority()
@@ -1,5 +1,5 @@
--TEST--
Exception for child templates defining contents outside blocks defined by parent
Exception for child templates defining content outside blocks defined by parent
--TEMPLATE--
{% extends 'base.twig' %}
@@ -12,4 +12,4 @@ Content outside a block.
{% block sidebar %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: A template that extends another one cannot include contents outside Twig blocks. Did you forget to put the contents inside a {% block %} tag in "index.twig" at line 3?
Twig_Error_Syntax: A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag in "index.twig" at line 3?
@@ -6,6 +6,7 @@
{{ number|length }}
{{ to_string_able|length }}
{{ countable|length }}
{{ iterator_aggregate|length }}
{{ null|length }}
{{ magic|length }}
{{ non_countable|length }}
@@ -16,6 +17,7 @@ return array(
'number' => 1000,
'to_string_able' => new ToStringStub('foobar'),
'countable' => new CountableStub(42), /* also asserts we do *not* call __toString() */
'iterator_aggregate' => new IteratorAggregateStub(array('a', 'b', 'c')), /* also asserts we do *not* call __toString() */
'null' => null,
'magic' => new MagicCallStub(), /* used to assert we do *not* call __call */
'non_countable' => new \StdClass(),
@@ -26,6 +28,7 @@ return array(
4
6
42
3
0
1
1
+18
View File
@@ -309,3 +309,21 @@ class CountableStub implements \Countable
throw new Exception('__toString shall not be called on \Countables');
}
}
/**
* This class is used in tests for the length filter
*/
class IteratorAggregateStub implements \IteratorAggregate
{
private $data;
public function __construct(array $data)
{
$this->data = $data;
}
public function getIterator()
{
return new ArrayIterator($this->data);
}
}
+1
View File
@@ -58,6 +58,7 @@ class Twig_Tests_TemplateTest extends \PHPUnit\Framework\TestCase
array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1.'),
array('{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1.'),
array('{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1.'),
array('{{ array.a() }}', 'Impossible to invoke a method ("a") on an array in "%s" at line 1.'),
array('{{ empty_array.a }}', 'Key "a" does not exist as the array is empty in "%s" at line 1.'),
array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1.'),
array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1.'),