Issue #2172235 by joelpittet: Upgrade Twig to 1.15.* from 1.12.*.
parent
56d3a638f4
commit
af1ca73aca
|
@ -19,7 +19,7 @@
|
|||
"symfony/serializer": "2.3.*",
|
||||
"symfony/validator": "2.3.*",
|
||||
"symfony/yaml": "2.3.*",
|
||||
"twig/twig": "1.12.*",
|
||||
"twig/twig": "1.15.*",
|
||||
"doctrine/common": "dev-bmaster#99b44f52a1b844f9c4c34e618b160664d5c27daf",
|
||||
"doctrine/annotations": "dev-master#463d926a8dcc49271cb7db5a08364a70ed6e3cd3",
|
||||
"guzzle/http": "3.7.*",
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
"This file locks the dependencies of your project to a known state",
|
||||
"Read more about it at http://getcomposer.org/doc/01-basic-usage.md#composer-lock-the-lock-file"
|
||||
],
|
||||
"hash": "06006c1512fb829fcdb498b7e515901d",
|
||||
"hash": "14b55ea7402f04abe36b677892e442f3",
|
||||
"packages": [
|
||||
{
|
||||
"name": "doctrine/annotations",
|
||||
|
@ -1877,16 +1877,16 @@
|
|||
},
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"version": "v1.12.3",
|
||||
"version": "v1.15.0",
|
||||
"source": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/fabpot/Twig.git",
|
||||
"reference": "v1.12.3"
|
||||
"reference": "85e4ff98000157ff753d934b9f13659a953f5666"
|
||||
},
|
||||
"dist": {
|
||||
"type": "zip",
|
||||
"url": "https://api.github.com/repos/fabpot/Twig/zipball/v1.12.3",
|
||||
"reference": "v1.12.3",
|
||||
"url": "https://api.github.com/repos/fabpot/Twig/zipball/85e4ff98000157ff753d934b9f13659a953f5666",
|
||||
"reference": "85e4ff98000157ff753d934b9f13659a953f5666",
|
||||
"shasum": ""
|
||||
},
|
||||
"require": {
|
||||
|
@ -1895,7 +1895,7 @@
|
|||
"type": "library",
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.12-dev"
|
||||
"dev-master": "1.15-dev"
|
||||
}
|
||||
},
|
||||
"autoload": {
|
||||
|
@ -1905,7 +1905,7 @@
|
|||
},
|
||||
"notification-url": "https://packagist.org/downloads/",
|
||||
"license": [
|
||||
"BSD-3"
|
||||
"BSD-3-Clause"
|
||||
],
|
||||
"authors": [
|
||||
{
|
||||
|
@ -1922,7 +1922,7 @@
|
|||
"keywords": [
|
||||
"templating"
|
||||
],
|
||||
"time": "2013-04-08 12:40:11"
|
||||
"time": "2013-12-06 07:47:10"
|
||||
},
|
||||
{
|
||||
"name": "zendframework/zend-escaper",
|
||||
|
|
|
@ -4,6 +4,7 @@ php:
|
|||
- 5.2
|
||||
- 5.3
|
||||
- 5.4
|
||||
- 5.5
|
||||
|
||||
env:
|
||||
- TWIG_EXT=no
|
||||
|
|
|
@ -4,6 +4,10 @@ Lead Developer:
|
|||
|
||||
- Fabien Potencier <fabien.potencier@symfony-project.org>
|
||||
|
||||
C Extension Developer:
|
||||
|
||||
- Derick Rethans <derick@derickrethans.nl>
|
||||
|
||||
Project Founder:
|
||||
|
||||
- Armin Ronacher <armin.ronacher@active-4.com>
|
||||
|
|
|
@ -1,3 +1,59 @@
|
|||
* 1.15.0 (2013-12-06)
|
||||
|
||||
* made ignoreStrictCheck in Template::getAttribute() works with __call() methods throwing BadMethodCallException
|
||||
* added min and max functions
|
||||
* added the round filter
|
||||
* fixed a bug that prevented the optimizers to be enabled/disabled selectively
|
||||
* fixed first and last filters for UTF-8 strings
|
||||
* added a source function to include the content of a template without rendering it
|
||||
* fixed the C extension sandbox behavior when get or set is prepend to method name
|
||||
|
||||
* 1.14.2 (2013-10-30)
|
||||
|
||||
* fixed error filename/line when an error occurs in an included file
|
||||
* allowed operators that contain whitespaces to have more than one whitespace
|
||||
* allowed tests to be made of 1 or 2 words (like "same as" or "divisible by")
|
||||
|
||||
* 1.14.1 (2013-10-15)
|
||||
|
||||
* made it possible to use named operators as variables
|
||||
* fixed the possibility to have a variable named 'matches'
|
||||
* added support for PHP 5.5 DateTimeInterface
|
||||
|
||||
* 1.14.0 (2013-10-03)
|
||||
|
||||
* fixed usage of the html_attr escaping strategy to avoid double-escaping with the html strategy
|
||||
* added new operators: ends with, starts with, and matches
|
||||
* fixed some compatibility issues with HHVM
|
||||
* added a way to add custom escaping strategies
|
||||
* fixed the C extension compilation on Windows
|
||||
* fixed the batch filter when using a fill argument with an exact match of elements to batch
|
||||
* fixed the filesystem loader cache when a template name exists in several namespaces
|
||||
* fixed template_from_string when the template includes or extends other ones
|
||||
* fixed a crash of the C extension on an edge case
|
||||
|
||||
* 1.13.2 (2013-08-03)
|
||||
|
||||
* fixed the error line number for an error occurs in and embedded template
|
||||
* fixed crashes of the C extension on some edge cases
|
||||
|
||||
* 1.13.1 (2013-06-06)
|
||||
|
||||
* added the possibility to ignore the filesystem constructor argument in Twig_Loader_Filesystem
|
||||
* fixed Twig_Loader_Chain::exists() for a loader which implements Twig_ExistsLoaderInterface
|
||||
* adjusted backtrace call to reduce memory usage when an error occurs
|
||||
* added support for object instances as the second argument of the constant test
|
||||
* fixed the include function when used in an assignment
|
||||
|
||||
* 1.13.0 (2013-05-10)
|
||||
|
||||
* fixed getting a numeric-like item on a variable ('09' for instance)
|
||||
* fixed getting a boolean or float key on an array, so it is consistent with PHP's array access:
|
||||
`{{ array[false] }}` behaves the same as `echo $array[false];` (equals `$array[0]`)
|
||||
* made the escape filter 20% faster for happy path (escaping string for html with UTF-8)
|
||||
* changed ☃ to § in tests
|
||||
* enforced usage of named arguments after positional ones
|
||||
|
||||
* 1.12.3 (2013-04-08)
|
||||
|
||||
* fixed a security issue in the filesystem loader where it was possible to include a template one
|
||||
|
|
|
@ -1,8 +1,6 @@
|
|||
Twig, the flexible, fast, and secure template language for PHP
|
||||
==============================================================
|
||||
|
||||
[![Build Status](https://secure.travis-ci.org/fabpot/Twig.png?branch=master)](http://travis-ci.org/fabpot/Twig)
|
||||
|
||||
Twig is a template language for PHP, released under the new BSD license (code
|
||||
and documentation).
|
||||
|
||||
|
@ -12,6 +10,6 @@ inspired the Twig runtime environment.
|
|||
More Information
|
||||
----------------
|
||||
|
||||
Read the [documentation][1] for more information.
|
||||
Read the `documentation`_ for more information.
|
||||
|
||||
[1]: http://twig.sensiolabs.org/documentation
|
||||
.. _documentation: http://twig.sensiolabs.org/documentation
|
|
@ -4,7 +4,7 @@
|
|||
"description": "Twig, the flexible, fast, and secure template language for PHP",
|
||||
"keywords": ["templating"],
|
||||
"homepage": "http://twig.sensiolabs.org",
|
||||
"license": "BSD-3",
|
||||
"license": "BSD-3-Clause",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
|
@ -25,7 +25,7 @@
|
|||
},
|
||||
"extra": {
|
||||
"branch-alias": {
|
||||
"dev-master": "1.12-dev"
|
||||
"dev-master": "1.15-dev"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -177,7 +177,7 @@ argument::
|
|||
|
||||
$filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
|
||||
|
||||
Environment aware Filters
|
||||
Environment-aware Filters
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to access the current environment instance in your filter, set the
|
||||
|
@ -191,7 +191,7 @@ environment as the first argument to the filter call::
|
|||
return str_rot13($string);
|
||||
}, array('needs_environment' => true));
|
||||
|
||||
Context aware Filters
|
||||
Context-aware Filters
|
||||
~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If you want to access the current context in your filter, set the
|
||||
|
@ -211,14 +211,14 @@ Automatic Escaping
|
|||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If automatic escaping is enabled, the output of the filter may be escaped
|
||||
before printing. If your filter acts as an escaper (or explicitly outputs html
|
||||
before printing. If your filter acts as an escaper (or explicitly outputs HTML
|
||||
or JavaScript code), you will want the raw output to be printed. In such a
|
||||
case, set the ``is_safe`` option::
|
||||
|
||||
$filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
|
||||
|
||||
Some filters may need to work on input that is already escaped or safe, for
|
||||
example when adding (safe) html tags to originally unsafe output. In such a
|
||||
example when adding (safe) HTML tags to originally unsafe output. In such a
|
||||
case, set the ``pre_escape`` option to escape the input data before it is run
|
||||
through your filter::
|
||||
|
||||
|
@ -241,11 +241,11 @@ The following filters will be matched by the above defined dynamic filter:
|
|||
|
||||
A dynamic filter can define more than one dynamic parts::
|
||||
|
||||
$filter = new Twig_SimpleFilter('*_path', function ($name, $suffix, $arguments) {
|
||||
$filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
|
||||
// ...
|
||||
});
|
||||
|
||||
The filter will receive all dynamic part values before the normal filters
|
||||
The filter will receive all dynamic part values before the normal filter
|
||||
arguments, but after the environment and the context. For instance, a call to
|
||||
``'foo'|a_path_b()`` will result in the following arguments to be passed to
|
||||
the filter: ``('a', 'b', 'foo')``.
|
||||
|
@ -277,12 +277,64 @@ to create an instance of ``Twig_SimpleTest``::
|
|||
});
|
||||
$twig->addTest($test);
|
||||
|
||||
Tests do not support any options.
|
||||
Tests allow you to create custom application specific logic for evaluating
|
||||
boolean conditions. As a simple example, let's create a Twig test that checks if
|
||||
objects are 'red'::
|
||||
|
||||
$twig = new Twig_Environment($loader)
|
||||
$test = new Twig_SimpleTest('red', function ($value) {
|
||||
if (isset($value->color) && $value->color == 'red') {
|
||||
return true;
|
||||
}
|
||||
if (isset($value->paint) && $value->paint == 'red') {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
});
|
||||
$twig->addTest($test);
|
||||
|
||||
Test functions should always return true/false.
|
||||
|
||||
When creating tests you can use the ``node_class`` option to provide custom test
|
||||
compilation. This is useful if your test can be compiled into PHP primitives.
|
||||
This is used by many of the tests built into Twig::
|
||||
|
||||
$twig = new Twig_Environment($loader)
|
||||
$test = new Twig_SimpleTest(
|
||||
'odd',
|
||||
null,
|
||||
array('node_class' => 'Twig_Node_Expression_Test_Odd'));
|
||||
$twig->addTest($test);
|
||||
|
||||
class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' % 2 == 1')
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
The above example shows how you can create tests that use a node class. The
|
||||
node class has access to one sub-node called 'node'. This sub-node contains the
|
||||
value that is being tested. When the ``odd`` filter is used in code such as:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if my_value is odd %}
|
||||
|
||||
The ``node`` sub-node will contain an expression of ``my_value``. Node-based
|
||||
tests also have access to the ``arguments`` node. This node will contain the
|
||||
various other arguments that have been provided to your test.
|
||||
|
||||
Tags
|
||||
----
|
||||
|
||||
One of the most exciting feature of a template engine like Twig is the
|
||||
One of the most exciting features of a template engine like Twig is the
|
||||
possibility to define new language constructs. This is also the most complex
|
||||
feature as you need to understand how Twig's internals work.
|
||||
|
||||
|
@ -330,14 +382,15 @@ Now, let's see the actual code of this class::
|
|||
{
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$lineno = $token->getLine();
|
||||
$name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
|
||||
$value = $this->parser->getExpressionParser()->parseExpression();
|
||||
$parser = $this->parser;
|
||||
$stream = $parser->getStream();
|
||||
|
||||
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
$stream->expect(Twig_Token::OPERATOR_TYPE, '=');
|
||||
$value = $parser->getExpressionParser()->parseExpression();
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
|
||||
return new Project_Set_Node($name, $value, $lineno, $this->getTag());
|
||||
return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
|
||||
}
|
||||
|
||||
public function getTag()
|
||||
|
@ -384,9 +437,9 @@ The ``Project_Set_Node`` class itself is rather simple::
|
|||
|
||||
class Project_Set_Node extends Twig_Node
|
||||
{
|
||||
public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null)
|
||||
public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
|
||||
{
|
||||
parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag);
|
||||
parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
|
@ -640,7 +693,7 @@ responsible for parsing the tag and compiling it to PHP.
|
|||
Operators
|
||||
~~~~~~~~~
|
||||
|
||||
The ``getOperators()`` methods allows to add new operators. Here is how to add
|
||||
The ``getOperators()`` methods lets you add new operators. Here is how to add
|
||||
``!``, ``||``, and ``&&`` operators::
|
||||
|
||||
class Project_Twig_Extension extends Twig_Extension
|
||||
|
@ -664,7 +717,7 @@ The ``getOperators()`` methods allows to add new operators. Here is how to add
|
|||
Tests
|
||||
~~~~~
|
||||
|
||||
The ``getTests()`` methods allows to add new test functions::
|
||||
The ``getTests()`` method lets you add new test functions::
|
||||
|
||||
class Project_Twig_Extension extends Twig_Extension
|
||||
{
|
||||
|
@ -682,16 +735,8 @@ Overloading
|
|||
-----------
|
||||
|
||||
To overload an already defined filter, test, operator, global variable, or
|
||||
function, define it again **as late as possible**::
|
||||
|
||||
$twig = new Twig_Environment($loader);
|
||||
$twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
|
||||
// do something different from the built-in date filter
|
||||
}));
|
||||
|
||||
Here, we have overloaded the built-in ``date`` filter with a custom one.
|
||||
|
||||
That also works with an extension::
|
||||
function, re-define it in an extension and register it **as late as
|
||||
possible** (order matters)::
|
||||
|
||||
class MyCoreExtension extends Twig_Extension
|
||||
{
|
||||
|
@ -716,6 +761,19 @@ That also works with an extension::
|
|||
$twig = new Twig_Environment($loader);
|
||||
$twig->addExtension(new MyCoreExtension());
|
||||
|
||||
Here, we have overloaded the built-in ``date`` filter with a custom one.
|
||||
|
||||
If you do the same on the Twig_Environment itself, beware that it takes
|
||||
precedence over any other registered extensions::
|
||||
|
||||
$twig = new Twig_Environment($loader);
|
||||
$twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
|
||||
// do something different from the built-in date filter
|
||||
}));
|
||||
// the date filter will come from the above registration, not
|
||||
// from the registered extension below
|
||||
$twig->addExtension(new MyCoreExtension());
|
||||
|
||||
.. caution::
|
||||
|
||||
Note that overloading the built-in Twig elements is not recommended as it
|
||||
|
|
|
@ -244,14 +244,14 @@ Automatic Escaping
|
|||
~~~~~~~~~~~~~~~~~~
|
||||
|
||||
If automatic escaping is enabled, the output of the filter may be escaped
|
||||
before printing. If your filter acts as an escaper (or explicitly outputs html
|
||||
or javascript code), you will want the raw output to be printed. In such a
|
||||
before printing. If your filter acts as an escaper (or explicitly outputs HTML
|
||||
or JavaScript code), you will want the raw output to be printed. In such a
|
||||
case, set the ``is_safe`` option::
|
||||
|
||||
$filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
|
||||
|
||||
Some filters may need to work on input that is already escaped or safe, for
|
||||
example when adding (safe) html tags to originally unsafe output. In such a
|
||||
example when adding (safe) HTML tags to originally unsafe output. In such a
|
||||
case, set the ``pre_escape`` option to escape the input data before it is run
|
||||
through your filter::
|
||||
|
||||
|
@ -266,7 +266,7 @@ Dynamic Filters
|
|||
A filter name containing the special ``*`` character is a dynamic filter as
|
||||
the ``*`` can be any string::
|
||||
|
||||
$twig->addFilter('*_path', new Twig_Filter_Function('twig_path'));
|
||||
$twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
|
||||
|
||||
function twig_path($name, $arguments)
|
||||
{
|
||||
|
|
|
@ -348,10 +348,10 @@ tag, ``autoescape``, and a filter, ``raw``.
|
|||
When creating the escaper extension, you can switch on or off the global
|
||||
output escaping strategy::
|
||||
|
||||
$escaper = new Twig_Extension_Escaper(true);
|
||||
$escaper = new Twig_Extension_Escaper('html');
|
||||
$twig->addExtension($escaper);
|
||||
|
||||
If set to ``true``, all variables in templates are escaped (using the ``html``
|
||||
If set to ``html``, all variables in templates are escaped (using the ``html``
|
||||
escaping strategy), except those using the ``raw`` filter:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
@ -417,15 +417,15 @@ The escaping rules are implemented as follows:
|
|||
{{ var|upper|raw }} {# won't be escaped #}
|
||||
|
||||
* Automatic escaping is not applied if the last filter in the chain is marked
|
||||
safe for the current context (e.g. ``html`` or ``js``). ``escaper`` and
|
||||
``escaper('html')`` are marked safe for html, ``escaper('js')`` is marked
|
||||
safe for javascript, ``raw`` is marked safe for everything.
|
||||
safe for the current context (e.g. ``html`` or ``js``). ``escape`` and
|
||||
``escape('html')`` are marked safe for HTML, ``escape('js')`` is marked
|
||||
safe for JavaScript, ``raw`` is marked safe for everything.
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% autoescape 'js' %}
|
||||
{{ var|escape('html') }} {# will be escaped for html and javascript #}
|
||||
{{ var }} {# will be escaped for javascript #}
|
||||
{{ var|escape('html') }} {# will be escaped for HTML and JavaScript #}
|
||||
{{ var }} {# will be escaped for JavaScript #}
|
||||
{{ var|escape('js') }} {# won't be double-escaped #}
|
||||
{% endautoescape %}
|
||||
|
||||
|
@ -499,16 +499,16 @@ to enable by passing them to the constructor::
|
|||
Twig supports the following optimizations:
|
||||
|
||||
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_ALL``, enables all optimizations
|
||||
(this is the default value).
|
||||
(this is the default value).
|
||||
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_NONE``, disables all optimizations.
|
||||
This reduces the compilation time, but it can increase the execution time
|
||||
and the consumed memory.
|
||||
This reduces the compilation time, but it can increase the execution time
|
||||
and the consumed memory.
|
||||
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR``, optimizes the ``for`` tag by
|
||||
removing the ``loop`` variable creation whenever possible.
|
||||
removing the ``loop`` variable creation whenever possible.
|
||||
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_RAW_FILTER``, removes the ``raw``
|
||||
filter whenever possible.
|
||||
filter whenever possible.
|
||||
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_VAR_ACCESS``, simplifies the creation
|
||||
and access of variables in the compiled templates whenever possible.
|
||||
and access of variables in the compiled templates whenever possible.
|
||||
|
||||
Exceptions
|
||||
----------
|
||||
|
|
|
@ -90,7 +90,7 @@ standards:
|
|||
{% set foo_bar = 'foo' %}
|
||||
|
||||
* Indent your code inside tags (use the same indentation as the one used for
|
||||
the main language of the file):
|
||||
the target language of the rendered template):
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
|
|
|
@ -77,6 +77,9 @@ Tests
|
|||
removed in Twig 3.x (use ``Twig_Test`` instead). In Twig 2.x,
|
||||
``Twig_SimpleTest`` is just an alias for ``Twig_Test``.
|
||||
|
||||
* The ``sameas`` and ``divisibleby`` tests are deprecated in favor of ``same
|
||||
as`` and ``divisible by`` respectively.
|
||||
|
||||
Interfaces
|
||||
----------
|
||||
|
||||
|
@ -88,7 +91,9 @@ Interfaces
|
|||
* ``Twig_NodeInterface`` (use ``Twig_Node`` instead)
|
||||
* ``Twig_ParserInterface`` (use ``Twig_Parser`` instead)
|
||||
* ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
|
||||
* ``Twig_TemplateInterface`` (use ``Twig_Template`` instead)
|
||||
* ``Twig_TemplateInterface`` (use ``Twig_Template`` instead, and use
|
||||
those constants Twig_Template::ANY_CALL, Twig_Template::ARRAY_CALL,
|
||||
Twig_Template::METHOD_CALL)
|
||||
|
||||
Globals
|
||||
-------
|
||||
|
|
|
@ -6,9 +6,9 @@ The ``abs`` filter returns the absolute value.
|
|||
.. code-block:: jinja
|
||||
|
||||
{# number = -5 #}
|
||||
|
||||
|
||||
{{ number|abs }}
|
||||
|
||||
|
||||
{# outputs 5 #}
|
||||
|
||||
.. note::
|
||||
|
|
|
@ -14,11 +14,11 @@ missing items:
|
|||
|
||||
<table>
|
||||
{% for row in items|batch(3, 'No item') %}
|
||||
<tr>
|
||||
{% for column in row %}
|
||||
<td>{{ column }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
<tr>
|
||||
{% for column in row %}
|
||||
<td>{{ column }}</td>
|
||||
{% endfor %}
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</table>
|
||||
|
||||
|
@ -27,19 +27,19 @@ The above example will be rendered as:
|
|||
.. code-block:: jinja
|
||||
|
||||
<table>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
<tr>
|
||||
<td>a</td>
|
||||
<td>b</td>
|
||||
<td>c</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
<tr>
|
||||
<td>d</td>
|
||||
<td>e</td>
|
||||
<td>f</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>No item</td>
|
||||
<td>No item</td>
|
||||
<tr>
|
||||
<td>g</td>
|
||||
<td>No item</td>
|
||||
<td>No item</td>
|
||||
</tr>
|
||||
</table>
|
||||
|
|
|
@ -21,8 +21,8 @@ is the input charset:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``from``: The input charset
|
||||
* ``to``: The output charset
|
||||
* ``from``: The input charset
|
||||
* ``to``: The output charset
|
||||
|
||||
.. _`iconv`: http://php.net/iconv
|
||||
.. _`mbstring`: http://php.net/mbstring
|
||||
|
|
|
@ -19,6 +19,10 @@ The ``date`` filter formats a date to a given format:
|
|||
|
||||
{{ post.published_at|date("m/d/Y") }}
|
||||
|
||||
The format specifier is the same as supported by `date`_,
|
||||
except when the filtered data is of type `DateInterval`_, when the format must conform to
|
||||
`DateInterval::format`_ instead.
|
||||
|
||||
The ``date`` filter accepts strings (it must be in a format supported by the
|
||||
`strtotime`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For
|
||||
instance, to display the current date, filter the word "now":
|
||||
|
@ -80,9 +84,11 @@ The default timezone can also be set globally by calling ``setTimezone()``:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``format``: The date format
|
||||
* ``timezone``: The date timezone
|
||||
* ``format``: The date format
|
||||
* ``timezone``: The date timezone
|
||||
|
||||
.. _`strtotime`: http://www.php.net/strtotime
|
||||
.. _`DateTime`: http://www.php.net/DateTime
|
||||
.. _`DateInterval`: http://www.php.net/DateInterval
|
||||
.. _`strtotime`: http://www.php.net/strtotime
|
||||
.. _`DateTime`: http://www.php.net/DateTime
|
||||
.. _`DateInterval`: http://www.php.net/DateInterval
|
||||
.. _`date`: http://www.php.net/date
|
||||
.. _`DateInterval::format`: http://www.php.net/DateInterval.format
|
||||
|
|
|
@ -17,7 +17,7 @@ it with the :doc:`date<date>` filter for formatting.
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``modifier``: The modifier
|
||||
* ``modifier``: The modifier
|
||||
|
||||
.. _`strtotime`: http://www.php.net/strtotime
|
||||
.. _`DateTime`: http://www.php.net/DateTime
|
||||
|
|
|
@ -30,4 +30,4 @@ undefined:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``default``: The default value
|
||||
* ``default``: The default value
|
||||
|
|
|
@ -5,6 +5,9 @@
|
|||
The ``css``, ``url``, and ``html_attr`` strategies were added in Twig
|
||||
1.9.0.
|
||||
|
||||
.. versionadded:: 1.14.0
|
||||
The ability to define custom escapers was added in Twig 1.14.0.
|
||||
|
||||
The ``escape`` filter escapes a string for safe insertion into the final
|
||||
output. It supports different escaping strategies depending on the template
|
||||
context.
|
||||
|
@ -84,10 +87,30 @@ The ``escape`` filter supports the following escaping strategies:
|
|||
{{ var|escape(strategy)|raw }} {# won't be double-escaped #}
|
||||
{% endautoescape %}
|
||||
|
||||
Custom Escapers
|
||||
---------------
|
||||
|
||||
You can define custom escapers by calling the ``setEscaper()`` method on the
|
||||
``core`` extension instance. The first argument is the escaper name (to be
|
||||
used in the ``escape`` call) and the second one must be a valid PHP callable:
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
$twig = new Twig_Environment($loader);
|
||||
$twig->getExtension('core')->setEscaper('csv', 'csv_escaper'));
|
||||
|
||||
When called by Twig, the callable receives the Twig environment instance, the
|
||||
string to escape, and the charset.
|
||||
|
||||
.. note::
|
||||
|
||||
Built-in escapers cannot be overridden mainly they should be considered as
|
||||
the final implementation and also for better performance.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``strategy``: The escaping strategy
|
||||
* ``charset``: The string charset
|
||||
* ``strategy``: The escaping strategy
|
||||
* ``charset``: The string charset
|
||||
|
||||
.. _`htmlspecialchars`: http://php.net/htmlspecialchars
|
||||
|
|
|
@ -2,15 +2,15 @@
|
|||
==========
|
||||
|
||||
The ``format`` filter formats a given string by replacing the placeholders
|
||||
(placeholders follows the `printf`_ notation):
|
||||
(placeholders follows the `sprintf`_ notation):
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ "I like %s and %s."|format(foo, "bar") }}
|
||||
|
||||
{# returns I like foo and bar
|
||||
{# outputs I like foo and bar
|
||||
if the foo parameter equals to the foo string. #}
|
||||
|
||||
.. _`printf`: http://www.php.net/printf
|
||||
.. _`sprintf`: http://www.php.net/sprintf
|
||||
|
||||
.. seealso:: :doc:`replace<replace>`
|
||||
|
|
|
@ -27,6 +27,7 @@ Filters
|
|||
raw
|
||||
replace
|
||||
reverse
|
||||
round
|
||||
slice
|
||||
sort
|
||||
split
|
||||
|
|
|
@ -15,9 +15,9 @@ define it with the optional first parameter:
|
|||
.. code-block:: jinja
|
||||
|
||||
{{ [1, 2, 3]|join('|') }}
|
||||
{# returns 1|2|3 #}
|
||||
{# outputs 1|2|3 #}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``glue``: The separator
|
||||
* ``glue``: The separator
|
||||
|
|
|
@ -14,8 +14,8 @@ The ``json_encode`` filter returns the JSON representation of a string:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``options``: A bitmask of `json_encode options`_ (``{{
|
||||
data|json_encode(constant(JSON_PRETTY_PRINT)) }}``)
|
||||
* ``options``: A bitmask of `json_encode options`_ (``{{
|
||||
data|json_encode(constant('JSON_PRETTY_PRINT')) }}``)
|
||||
|
||||
.. _`json_encode`: http://php.net/json_encode
|
||||
.. _`json_encode options`: http://www.php.net/manual/en/json.constants.php
|
||||
|
|
|
@ -9,4 +9,3 @@ the length of a string:
|
|||
{% if users|length > 10 %}
|
||||
...
|
||||
{% endif %}
|
||||
|
||||
|
|
|
@ -21,9 +21,9 @@ separator using the additional arguments:
|
|||
If no formatting options are provided then Twig will use the default formatting
|
||||
options of:
|
||||
|
||||
- 0 decimal places.
|
||||
- ``.`` as the decimal point.
|
||||
- ``,`` as the thousands separator.
|
||||
* 0 decimal places.
|
||||
* ``.`` as the decimal point.
|
||||
* ``,`` as the thousands separator.
|
||||
|
||||
These defaults can be easily changed through the core extension:
|
||||
|
||||
|
@ -38,8 +38,8 @@ additional parameters.
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``decimal``: The number of decimal points to display
|
||||
* ``decimal_point``: The character(s) to use for the decimal point
|
||||
* ``decimal_sep``: The character(s) to use for the thousands separator
|
||||
* ``decimal``: The number of decimal points to display
|
||||
* ``decimal_point``: The character(s) to use for the decimal point
|
||||
* ``thousand_sep``: The character(s) to use for the thousands separator
|
||||
|
||||
.. _`number_format`: http://php.net/number_format
|
||||
|
|
|
@ -7,6 +7,6 @@ if ``raw`` is the last filter applied to it:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% autoescape true %}
|
||||
{% autoescape %}
|
||||
{{ var|raw }} {# var won't be escaped #}
|
||||
{% endautoescape %}
|
||||
|
|
|
@ -8,12 +8,12 @@ The ``replace`` filter formats a given string by replacing the placeholders
|
|||
|
||||
{{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }}
|
||||
|
||||
{# returns I like foo and bar
|
||||
{# outputs I like foo and bar
|
||||
if the foo parameter equals to the foo string. #}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``replace_pairs``: The placeholder values
|
||||
* ``replace_pairs``: The placeholder values
|
||||
|
||||
.. seealso:: :doc:`format<format>`
|
||||
|
|
|
@ -8,7 +8,7 @@ The ``reverse`` filter reverses a sequence, a mapping, or a string:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for use in users|reverse %}
|
||||
{% for user in users|reverse %}
|
||||
...
|
||||
{% endfor %}
|
||||
|
||||
|
@ -16,8 +16,32 @@ The ``reverse`` filter reverses a sequence, a mapping, or a string:
|
|||
|
||||
{# outputs 4321 #}
|
||||
|
||||
.. tip::
|
||||
|
||||
For sequences and mappings, numeric keys are not preserved. To reverse
|
||||
them as well, pass ``true`` as an argument to the ``reverse`` filter:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for key, value in {1: "a", 2: "b", 3: "c"}|reverse %}
|
||||
{{ key }}: {{ value }}
|
||||
{%- endfor %}
|
||||
|
||||
{# output: 0: c 1: b 2: a #}
|
||||
|
||||
{% for key, value in {1: "a", 2: "b", 3: "c"}|reverse(true) %}
|
||||
{{ key }}: {{ value }}
|
||||
{%- endfor %}
|
||||
|
||||
{# output: 3: c 2: b 1: a #}
|
||||
|
||||
.. note::
|
||||
|
||||
It also works with objects implementing the `Traversable`_ interface.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``preserve_keys``: Preserve keys when reversing a mapping or a sequence.
|
||||
|
||||
.. _`Traversable`: http://php.net/Traversable
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
``round``
|
||||
=========
|
||||
|
||||
.. versionadded:: 1.15.0
|
||||
The ``round`` filter was added in Twig 1.15.0.
|
||||
|
||||
The ``round`` filter rounds a number to a given precision:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ 42.55|round }}
|
||||
{# outputs 43 #}
|
||||
|
||||
{{ 42.55|round(1, 'floor') }}
|
||||
{# outputs 42.5 #}
|
||||
|
||||
The ``round`` filter takes two optional arguments; the first one specifies the
|
||||
precision (default is ``0``) and the second the rounding method (default is
|
||||
``common``):
|
||||
|
||||
* ``common`` rounds either up or down (rounds the value up to precision decimal
|
||||
places away from zero, when it is half way there -- making 1.5 into 2 and
|
||||
-1.5 into -2);
|
||||
|
||||
* ``ceil`` always rounds up;
|
||||
|
||||
* ``floor`` always rounds down.
|
||||
|
||||
.. note::
|
||||
|
||||
The ``//`` operator is equivalent to ``|round(0, 'floor')``.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``precision``: The rounding precision
|
||||
* ``method``: The rounding method
|
|
@ -8,11 +8,11 @@ The ``slice`` filter extracts a slice of a sequence, a mapping, or a string:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for i in [1, 2, 3, 4]|slice(1, 2) %}
|
||||
{% for i in [1, 2, 3, 4, 5]|slice(1, 2) %}
|
||||
{# will iterate over 2 and 3 #}
|
||||
{% endfor %}
|
||||
|
||||
{{ '1234'|slice(1, 2) }}
|
||||
{{ '12345'|slice(1, 2) }}
|
||||
|
||||
{# outputs 23 #}
|
||||
|
||||
|
@ -20,7 +20,7 @@ You can use any valid expression for both the start and the length:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for i in [1, 2, 3, 4]|slice(start, length) %}
|
||||
{% for i in [1, 2, 3, 4, 5]|slice(start, length) %}
|
||||
{# ... #}
|
||||
{% endfor %}
|
||||
|
||||
|
@ -28,17 +28,17 @@ As syntactic sugar, you can also use the ``[]`` notation:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for i in [1, 2, 3, 4][start:length] %}
|
||||
{% for i in [1, 2, 3, 4, 5][start:length] %}
|
||||
{# ... #}
|
||||
{% endfor %}
|
||||
|
||||
{{ '1234'[1:2] }}
|
||||
{{ '12345'[1:2] }}
|
||||
|
||||
{# you can omit the first argument -- which is the same as 0 #}
|
||||
{{ '1234'[:2] }} {# will display "12" #}
|
||||
{{ '12345'[:2] }} {# will display "12" #}
|
||||
|
||||
{# you can omit the last argument -- which will select everything till the end #}
|
||||
{{ '1234'[2:] }} {# will display "34 #}
|
||||
{{ '12345'[2:] }} {# will display "345" #}
|
||||
|
||||
The ``slice`` filter works as the `array_slice`_ PHP function for arrays and
|
||||
`substr`_ for strings.
|
||||
|
@ -61,9 +61,9 @@ up until the end of the variable.
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``start``: The start of the slice
|
||||
* ``length``: The size of the slice
|
||||
* ``preserve_keys``: Whether to preserve key or not (when the input is an array)
|
||||
* ``start``: The start of the slice
|
||||
* ``length``: The size of the slice
|
||||
* ``preserve_keys``: Whether to preserve key or not (when the input is an array)
|
||||
|
||||
.. _`Traversable`: http://php.net/manual/en/class.traversable.php
|
||||
.. _`array_slice`: http://php.net/array_slice
|
||||
|
|
|
@ -46,8 +46,8 @@ chunks. Length is set by the ``limit`` argument (one character by default).
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``delimiter``: The delimiter
|
||||
* ``limit``: The limit argument
|
||||
* ``delimiter``: The delimiter
|
||||
* ``limit``: The limit argument
|
||||
|
||||
.. _`explode`: http://php.net/explode
|
||||
.. _`str_split`: http://php.net/str_split
|
||||
|
|
|
@ -24,6 +24,6 @@ and end of a string:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``character_mask``: The characters to strip
|
||||
* ``character_mask``: The characters to strip
|
||||
|
||||
.. _`trim`: http://php.net/trim
|
||||
|
|
|
@ -12,12 +12,17 @@ or an array as query string:
|
|||
{{ "path-seg*ment"|url_encode }}
|
||||
{# outputs "path-seg%2Ament" #}
|
||||
|
||||
{{ "string with spaces"|url_encode(true) }}
|
||||
{# outputs "string%20with%20spaces" #}
|
||||
|
||||
{{ {'param': 'value', 'foo': 'bar'}|url_encode }}
|
||||
{# outputs "param=value&foo=bar" #}
|
||||
|
||||
.. note::
|
||||
|
||||
Internally, Twig uses the PHP `urlencode`_ or the `http_build_query`_ function.
|
||||
Internally, Twig uses the PHP `urlencode`_ (or `rawurlencode`_ if you pass
|
||||
``true`` as the first parameter) or the `http_build_query`_ function.
|
||||
|
||||
.. _`urlencode`: http://php.net/urlencode
|
||||
.. _`urlencode`: http://php.net/urlencode
|
||||
.. _`rawurlencode`: http://php.net/rawurlencode
|
||||
.. _`http_build_query`: http://php.net/http_build_query
|
||||
|
|
|
@ -22,4 +22,4 @@ The array can contain any number of values:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``position``: The cycle position
|
||||
* ``position``: The cycle position
|
||||
|
|
|
@ -11,7 +11,7 @@ Converts an argument to a date to allow date comparison:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if date(user.created_at) < date('+2days') %}
|
||||
{% if date(user.created_at) < date('-2days') %}
|
||||
{# do something #}
|
||||
{% endif %}
|
||||
|
||||
|
@ -21,7 +21,7 @@ You can pass a timezone as the second argument:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if date(user.created_at) < date('+2days', 'Europe/Paris') %}
|
||||
{% if date(user.created_at) < date('-2days', 'Europe/Paris') %}
|
||||
{# do something #}
|
||||
{% endif %}
|
||||
|
||||
|
@ -46,7 +46,7 @@ If no argument is passed, the function returns the current date:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``date``: The date
|
||||
* ``timezone``: The timezone
|
||||
* ``date``: The date
|
||||
* ``timezone``: The timezone
|
||||
|
||||
.. _`date`: http://www.php.net/date
|
||||
|
|
|
@ -63,7 +63,7 @@ dumped:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``context``: The context to dump
|
||||
* ``context``: The context to dump
|
||||
|
||||
.. _`XDebug`: http://xdebug.org/docs/display
|
||||
.. _`var_dump`: http://php.net/var_dump
|
||||
|
|
|
@ -73,8 +73,8 @@ sandboxing it:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``template``: The template to render
|
||||
* ``variables``: The variables to pass to the template
|
||||
* ``with_context``: Whether to pass the current context variables or not
|
||||
* ``ignore_missing``: Whether to ignore missing templates or not
|
||||
* ``sandboxed``: Whether to sandbox the template or not
|
||||
* ``template``: The template to render
|
||||
* ``variables``: The variables to pass to the template
|
||||
* ``with_context``: Whether to pass the current context variables or not
|
||||
* ``ignore_missing``: Whether to ignore missing templates or not
|
||||
* ``sandboxed``: Whether to sandbox the template or not
|
||||
|
|
|
@ -11,7 +11,10 @@ Functions
|
|||
date
|
||||
dump
|
||||
include
|
||||
max
|
||||
min
|
||||
parent
|
||||
random
|
||||
range
|
||||
source
|
||||
template_from_string
|
||||
|
|
|
@ -0,0 +1,19 @@
|
|||
``max``
|
||||
=======
|
||||
|
||||
.. versionadded:: 1.15
|
||||
The ``max`` function was added in Twig 1.15.
|
||||
|
||||
``max`` returns the biggest value of a sequence or a set of values:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ max(1, 3, 2) }}
|
||||
{{ max([1, 3, 2]) }}
|
||||
|
||||
When called with a mapping, max ignores keys and only compares values:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ max({2: "two", 1: "one", 3: "three", 5: "five", 4: "for"}) }}
|
||||
{# return "two" #}
|
|
@ -0,0 +1,19 @@
|
|||
``min``
|
||||
=======
|
||||
|
||||
.. versionadded:: 1.15
|
||||
The ``min`` function was added in Twig 1.15.
|
||||
|
||||
``min`` returns the lowest value of a sequence or a set of values:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ min(1, 3, 2) }}
|
||||
{{ min([1, 3, 2]) }}
|
||||
|
||||
When called with a mapping, min ignores keys and only compares values:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ min({2: "two", 1: "one", 3: "three", 5: "five", 4: "for"}) }}
|
||||
{# return "five" #}
|
|
@ -18,12 +18,12 @@ parameter type:
|
|||
|
||||
{{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #}
|
||||
{{ random('ABC') }} {# example output: C #}
|
||||
{{ random() }} {# example output: 15386094 (works as native PHP `mt_rand`_ function) #}
|
||||
{{ random() }} {# example output: 15386094 (works as the native PHP mt_rand function) #}
|
||||
{{ random(5) }} {# example output: 3 #}
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``values``: The values
|
||||
* ``values``: The values
|
||||
|
||||
.. _`mt_rand`: http://php.net/mt_rand
|
||||
|
|
|
@ -9,7 +9,7 @@ Returns a list containing an arithmetic progression of integers:
|
|||
{{ i }},
|
||||
{% endfor %}
|
||||
|
||||
{# returns 0, 1, 2, 3 #}
|
||||
{# outputs 0, 1, 2, 3, #}
|
||||
|
||||
When step is given (as the third parameter), it specifies the increment (or
|
||||
decrement):
|
||||
|
@ -20,7 +20,7 @@ decrement):
|
|||
{{ i }},
|
||||
{% endfor %}
|
||||
|
||||
{# returns 0, 2, 4, 6 #}
|
||||
{# outputs 0, 2, 4, 6, #}
|
||||
|
||||
The Twig built-in ``..`` operator is just syntactic sugar for the ``range``
|
||||
function (with a step of 1):
|
||||
|
@ -38,8 +38,8 @@ function (with a step of 1):
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``low``: The first value of the sequence.
|
||||
* ``high``: The highest possible value of the sequence.
|
||||
* ``step``: The increment between elements of the sequence.
|
||||
* ``low``: The first value of the sequence.
|
||||
* ``high``: The highest possible value of the sequence.
|
||||
* ``step``: The increment between elements of the sequence.
|
||||
|
||||
.. _`range`: http://php.net/range
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
``source``
|
||||
==========
|
||||
|
||||
.. versionadded:: 1.15
|
||||
The source function was added in Twig 1.15.
|
||||
|
||||
The ``source`` function returns the content of a template without rendering it:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ source('template.html') }}
|
||||
{{ source(some_var) }}
|
||||
|
||||
The function uses the same template loaders as the ones used to include
|
||||
templates. So, if you are using the filesystem loader, the templates are looked
|
||||
for in the paths defined by it.
|
||||
|
||||
Arguments
|
||||
---------
|
||||
|
||||
* ``name``: The name of the template to read
|
|
@ -8,7 +8,7 @@ The ``template_from_string`` function loads a template from a string:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{{ include(template_from_string("Hello {{ name }}") }}
|
||||
{{ include(template_from_string("Hello {{ name }}")) }}
|
||||
{{ include(template_from_string(page.template)) }}
|
||||
|
||||
.. note::
|
||||
|
@ -29,4 +29,4 @@ The ``template_from_string`` function loads a template from a string:
|
|||
Arguments
|
||||
---------
|
||||
|
||||
* ``template``: The template
|
||||
* ``template``: The template
|
||||
|
|
|
@ -15,4 +15,5 @@ Twig
|
|||
filters/index
|
||||
functions/index
|
||||
tests/index
|
||||
installation
|
||||
deprecated
|
||||
|
|
|
@ -0,0 +1,118 @@
|
|||
Installation
|
||||
============
|
||||
|
||||
You have multiple ways to install Twig.
|
||||
|
||||
Installing the Twig PHP package
|
||||
-------------------------------
|
||||
|
||||
Installing via Composer (recommended)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Install Composer in your project:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -s http://getcomposer.org/installer | php
|
||||
|
||||
2. Create a ``composer.json`` file in your project root:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"require": {
|
||||
"twig/twig": "1.*"
|
||||
}
|
||||
}
|
||||
|
||||
3. Install via Composer
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
php composer.phar install
|
||||
|
||||
.. note::
|
||||
If you want to learn more about Composer, the ``composer.json`` file syntax
|
||||
and its usage, you can read the `online documentation`_.
|
||||
|
||||
Installing from the tarball release
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Download the most recent tarball from the `download page`_
|
||||
2. Unpack the tarball
|
||||
3. Move the files somewhere in your project
|
||||
|
||||
Installing the development version
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Install Git
|
||||
2. ``git clone git://github.com/fabpot/Twig.git``
|
||||
|
||||
Installing the PEAR package
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Install PEAR
|
||||
2. ``pear channel-discover pear.twig-project.org``
|
||||
3. ``pear install twig/Twig`` (or ``pear install twig/Twig-beta``)
|
||||
|
||||
Installing the C extension
|
||||
--------------------------
|
||||
|
||||
.. versionadded:: 1.4
|
||||
The C extension was added in Twig 1.4.
|
||||
|
||||
Twig comes with a C extension that enhances the performance of the Twig
|
||||
runtime engine.
|
||||
|
||||
You can install it via PEAR:
|
||||
|
||||
1. Install PEAR
|
||||
2. ``pear channel-discover pear.twig-project.org``
|
||||
3. ``pear install twig/CTwig`` (or ``pear install twig/CTwig-beta``)
|
||||
|
||||
Or manually like any other PHP extension:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd ext/twig
|
||||
$ phpize
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
For Windows:
|
||||
|
||||
1. Setup the build environment following the `PHP documentation`_
|
||||
2. Put Twig's C extension source code into ``C:\php-sdk\phpdev\vcXX\x86\php-source-directory\ext\twig``
|
||||
3. Use the ``configure --disable-all --enable-cli --enable-twig=shared`` command instead of step 14
|
||||
4. ``nmake``
|
||||
5. Copy the ``C:\php-sdk\phpdev\vcXX\x86\php-source-directory\Release_TS\php_twig.dll`` file to your PHP setup.
|
||||
|
||||
.. tip::
|
||||
|
||||
For Windows ZendServer, TS is not enabled as mentionned in `Zend Server
|
||||
FAQ`_.
|
||||
|
||||
You have to use `configure --disable-all --disable-zts --enable-cli
|
||||
--enable-twig=shared` to be able to build the twig C extension for
|
||||
ZendServer.
|
||||
|
||||
The built DLL will be available in
|
||||
C:\\php-sdk\\phpdev\\vcXX\\x86\\php-source-directory\\Release
|
||||
|
||||
Finally, enable the extension in your ``php.ini`` configuration file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
extension=twig.so #For Unix systems
|
||||
extension=php_twig.dll #For Windows systems
|
||||
|
||||
And from now on, Twig will automatically compile your templates to take
|
||||
advantage of the C extension. Note that this extension does not replace the
|
||||
PHP code but only provides an optimized version of the
|
||||
``Twig_Template::getAttribute()`` method.
|
||||
|
||||
.. _`download page`: https://github.com/fabpot/Twig/tags
|
||||
.. _`online documentation`: http://getcomposer.org/doc
|
||||
.. _`PHP documentation`: https://wiki.php.net/internals/windows/stepbystepbuild
|
||||
.. _`Zend Server FAQ`: http://www.zend.com/en/products/server/faq#faqD6
|
|
@ -3,11 +3,11 @@ Twig Internals
|
|||
|
||||
Twig is very extensible and you can easily hack it. Keep in mind that you
|
||||
should probably try to create an extension before hacking the core, as most
|
||||
features and enhancements can be done with extensions. This chapter is also
|
||||
features and enhancements can be handled with extensions. This chapter is also
|
||||
useful for people who want to understand how Twig works under the hood.
|
||||
|
||||
How Twig works?
|
||||
---------------
|
||||
How does Twig work?
|
||||
-------------------
|
||||
|
||||
The rendering of a Twig template can be summarized into four key steps:
|
||||
|
||||
|
@ -18,7 +18,7 @@ The rendering of a Twig template can be summarized into four key steps:
|
|||
for easier processing;
|
||||
* Then, the **parser** converts the token stream into a meaningful tree
|
||||
of nodes (the Abstract Syntax Tree);
|
||||
* Eventually, the *compiler* transforms the AST into PHP code;
|
||||
* Eventually, the *compiler* transforms the AST into PHP code.
|
||||
|
||||
* **Evaluate** the template: It basically means calling the ``display()``
|
||||
method of the compiled template and passing it the context.
|
||||
|
@ -42,7 +42,7 @@ an instance of ``Twig_Token``, and the stream is an instance of
|
|||
* ``Twig_Token::EOF_TYPE``: Ends of template.
|
||||
|
||||
You can manually convert a source code into a token stream by calling the
|
||||
``tokenize()`` of an environment::
|
||||
``tokenize()`` method of an environment::
|
||||
|
||||
$stream = $twig->tokenize($source, $identifier);
|
||||
|
||||
|
@ -63,7 +63,7 @@ Here is the output for the ``Hello {{ name }}`` template:
|
|||
|
||||
.. note::
|
||||
|
||||
You can change the default lexer use by Twig (``Twig_Lexer``) by calling
|
||||
The default lexer (``Twig_Lexer``) can be changed by calling
|
||||
the ``setLexer()`` method::
|
||||
|
||||
$twig->setLexer($lexer);
|
||||
|
@ -97,7 +97,7 @@ Here is the output for the ``Hello {{ name }}`` template:
|
|||
|
||||
.. note::
|
||||
|
||||
The default parser (``Twig_TokenParser``) can be also changed by calling the
|
||||
The default parser (``Twig_TokenParser``) can be changed by calling the
|
||||
``setParser()`` method::
|
||||
|
||||
$twig->setParser($parser);
|
||||
|
@ -108,13 +108,11 @@ The Compiler
|
|||
The last step is done by the compiler. It takes a node tree as an input and
|
||||
generates PHP code usable for runtime execution of the template.
|
||||
|
||||
You can call the compiler by hand with the ``compile()`` method of an
|
||||
environment::
|
||||
You can manually compile a node tree to PHP code with the ``compile()`` method
|
||||
of an environment::
|
||||
|
||||
$php = $twig->compile($nodes);
|
||||
|
||||
The ``compile()`` method returns the PHP source code representing the node.
|
||||
|
||||
The generated template for a ``Hello {{ name }}`` template reads as follows
|
||||
(the actual output can differ depending on the version of Twig you are
|
||||
using)::
|
||||
|
@ -134,7 +132,7 @@ using)::
|
|||
|
||||
.. note::
|
||||
|
||||
As for the lexer and the parser, the default compiler (``Twig_Compiler``) can
|
||||
be changed by calling the ``setCompiler()`` method::
|
||||
The default compiler (``Twig_Compiler``) can be changed by calling the
|
||||
``setCompiler()`` method::
|
||||
|
||||
$twig->setCompiler($compiler);
|
||||
|
|
|
@ -29,115 +29,27 @@ Twig needs at least **PHP 5.2.4** to run.
|
|||
Installation
|
||||
------------
|
||||
|
||||
You have multiple ways to install Twig.
|
||||
|
||||
Installing via Composer (recommended)
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Install composer in your project:
|
||||
The recommended way to install Twig is via Composer:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
curl -s http://getcomposer.org/installer | php
|
||||
|
||||
2. Create a ``composer.json`` file in your project root:
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
{
|
||||
"require": {
|
||||
"twig/twig": "1.*"
|
||||
}
|
||||
}
|
||||
|
||||
3. Install via composer
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
php composer.phar install
|
||||
composer require twig/twig:1.*
|
||||
|
||||
.. note::
|
||||
If you want to learn more about Composer, the ``composer.json`` file syntax
|
||||
and its usage, you can read the `online documentation`_.
|
||||
|
||||
Installing from the tarball release
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Download the most recent tarball from the `download page`_
|
||||
2. Unpack the tarball
|
||||
3. Move the files somewhere in your project
|
||||
|
||||
Installing the development version
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Install Git
|
||||
2. ``git clone git://github.com/fabpot/Twig.git``
|
||||
|
||||
Installing the PEAR package
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
1. Install PEAR
|
||||
2. ``pear channel-discover pear.twig-project.org``
|
||||
3. ``pear install twig/Twig`` (or ``pear install twig/Twig-beta``)
|
||||
|
||||
|
||||
Installing the C extension
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
.. versionadded:: 1.4
|
||||
The C extension was added in Twig 1.4.
|
||||
|
||||
Twig comes with a C extension that enhances the performance of the Twig
|
||||
runtime engine. You can install it like any other PHP extension:
|
||||
|
||||
.. code-block:: bash
|
||||
|
||||
$ cd ext/twig
|
||||
$ phpize
|
||||
$ ./configure
|
||||
$ make
|
||||
$ make install
|
||||
|
||||
Finally, enable the extension in your ``php.ini`` configuration file:
|
||||
|
||||
.. code-block:: ini
|
||||
|
||||
extension=twig.so
|
||||
|
||||
And from now on, Twig will automatically compile your templates to take
|
||||
advantage of the C extension. Note that this extension does not replace the
|
||||
PHP code but only provides an optimized version of the
|
||||
``Twig_Template::getAttribute()`` method.
|
||||
|
||||
.. tip::
|
||||
|
||||
On Windows, you can also simply download and install a `pre-built DLL`_.
|
||||
To learn more about the other installation methods, read the
|
||||
:doc:`installation<installation>` chapter; it also explains how to install
|
||||
the Twig C extension.
|
||||
|
||||
Basic API Usage
|
||||
---------------
|
||||
|
||||
This section gives you a brief introduction to the PHP API for Twig.
|
||||
|
||||
The first step to use Twig is to register its autoloader::
|
||||
|
||||
require_once '/path/to/lib/Twig/Autoloader.php';
|
||||
Twig_Autoloader::register();
|
||||
|
||||
Replace the ``/path/to/lib/`` path with the path you used for Twig
|
||||
installation.
|
||||
|
||||
If you have installed Twig via Composer you can take advantage of Composer's
|
||||
autoload mechanism by replacing the previous snippet for::
|
||||
|
||||
require_once '/path/to/vendor/autoload.php'
|
||||
|
||||
.. note::
|
||||
|
||||
Twig follows the PEAR convention names for its classes, which means you
|
||||
can easily integrate Twig classes loading in your own autoloader.
|
||||
|
||||
.. code-block:: php
|
||||
|
||||
require_once '/path/to/vendor/autoload.php';
|
||||
|
||||
$loader = new Twig_Loader_String();
|
||||
$twig = new Twig_Environment($loader);
|
||||
|
||||
|
@ -159,6 +71,9 @@ filesystem loader::
|
|||
|
||||
echo $twig->render('index.html', array('name' => 'Fabien'));
|
||||
|
||||
.. _`download page`: https://github.com/fabpot/Twig/tags
|
||||
.. _`online documentation`: http://getcomposer.org/doc
|
||||
.. _`pre-build DLL`: https://github.com/stealth35/stealth35.github.com/downloads
|
||||
.. tip::
|
||||
|
||||
If you are not using Composer, use the Twig built-in autoloader::
|
||||
|
||||
require_once '/path/to/lib/Twig/Autoloader.php';
|
||||
Twig_Autoloader::register();
|
||||
|
|
|
@ -335,7 +335,7 @@ you have some dynamic JavaScript files thanks to the ``autoescape`` tag:
|
|||
|
||||
But if you have many HTML and JS files, and if your template names follow some
|
||||
conventions, you can instead determine the default escaping strategy to use
|
||||
based on the template name. Let's say that your template names always ends
|
||||
based on the template name. Let's say that your template names always end
|
||||
with ``.html`` for HTML files, ``.js`` for JavaScript ones, and ``.css`` for
|
||||
stylesheets, here is how you can configure Twig::
|
||||
|
||||
|
|
|
@ -192,7 +192,7 @@ How blocks work?
|
|||
A block provides a way to change how a certain part of a template is rendered
|
||||
but it does not interfere in any way with the logic around it.
|
||||
|
||||
Let's take the following example to illustrate how a block work and more
|
||||
Let's take the following example to illustrate how a block works and more
|
||||
importantly, how it does not work:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
``from``
|
||||
========
|
||||
|
||||
The ``from`` tags import :doc:`macro<../tags/macro>` names into the current
|
||||
The ``from`` tag imports :doc:`macro<../tags/macro>` names into the current
|
||||
namespace. The tag is documented in detail in the documentation for the
|
||||
:doc:`import<../tags/import>` tag.
|
||||
|
||||
|
|
|
@ -37,7 +37,7 @@ more complex ``expressions`` there too:
|
|||
{% if kenny.sick %}
|
||||
Kenny is sick.
|
||||
{% elseif kenny.dead %}
|
||||
You killed Kenny! You bastard!!!
|
||||
You killed Kenny! You bastard!!!
|
||||
{% else %}
|
||||
Kenny looks okay --- so far
|
||||
{% endif %}
|
||||
|
|
|
@ -15,7 +15,7 @@ Imagine we have a helper module that renders forms (called ``forms.html``):
|
|||
<input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
|
||||
{% endmacro %}
|
||||
|
||||
{% macro textarea(name, value, rows) %}
|
||||
{% macro textarea(name, value, rows, cols) %}
|
||||
<textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
|
||||
{% endmacro %}
|
||||
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
``include``
|
||||
===========
|
||||
|
||||
The ``include`` statement includes a template and return the rendered content
|
||||
The ``include`` statement includes a template and returns the rendered content
|
||||
of that file into the current namespace:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
@ -63,7 +63,7 @@ directly::
|
|||
The ``ignore missing`` feature has been added in Twig 1.2.
|
||||
|
||||
You can mark an include with ``ignore missing`` in which case Twig will ignore
|
||||
the statement if the template to be ignored does not exist. It has to be
|
||||
the statement if the template to be included does not exist. It has to be
|
||||
placed just after the template name. Here some valid examples:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
|
|
@ -20,7 +20,7 @@ Macros differs from native PHP functions in a few ways:
|
|||
|
||||
* Arguments of a macro are always optional.
|
||||
|
||||
But as PHP functions, macros don't have access to the current template
|
||||
But as with PHP functions, macros don't have access to the current template
|
||||
variables.
|
||||
|
||||
.. tip::
|
||||
|
|
|
@ -2,31 +2,77 @@
|
|||
=======
|
||||
|
||||
Inside code blocks you can also assign values to variables. Assignments use
|
||||
the ``set`` tag and can have multiple targets:
|
||||
the ``set`` tag and can have multiple targets.
|
||||
|
||||
Here is how you can assign the ``bar`` value to the ``foo`` variable:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% set foo = 'foo' %}
|
||||
{% set foo = 'bar' %}
|
||||
|
||||
After the ``set`` call, the ``foo`` variable is available in the template like
|
||||
any other ones:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{# displays bar #}
|
||||
{{ foo }}
|
||||
|
||||
The assigned value can be any valid :ref:`Twig expressions
|
||||
<twig-expressions>`:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% set foo = [1, 2] %}
|
||||
|
||||
{% set foo = {'foo': 'bar'} %}
|
||||
|
||||
{% set foo = 'foo' ~ 'bar' %}
|
||||
|
||||
Several variables can be assigned in one block:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% set foo, bar = 'foo', 'bar' %}
|
||||
|
||||
{# is equivalent to #}
|
||||
|
||||
{% set foo = 'foo' %}
|
||||
{% set bar = 'bar' %}
|
||||
|
||||
The ``set`` tag can also be used to 'capture' chunks of text:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% set foo %}
|
||||
<div id="pagination">
|
||||
...
|
||||
</div>
|
||||
<div id="pagination">
|
||||
...
|
||||
</div>
|
||||
{% endset %}
|
||||
|
||||
.. caution::
|
||||
|
||||
If you enable automatic output escaping, Twig will only consider the
|
||||
content to be safe when capturing chunks of text.
|
||||
|
||||
.. note::
|
||||
|
||||
Note that loops are scoped in Twig; therefore a variable declared inside a
|
||||
``for`` loop is not accessible outside the loop itself:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for item in list %}
|
||||
{% set foo = item %}
|
||||
{% endfor %}
|
||||
|
||||
{# foo is NOT available #}
|
||||
|
||||
If you want to access the variable, just declare it before the loop:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% set foo = "" %}
|
||||
{% for item in list %}
|
||||
{% set foo = item %}
|
||||
{% endfor %}
|
||||
|
||||
{# foo is available #}
|
||||
|
|
|
@ -35,7 +35,7 @@ but without the associated complexity:
|
|||
{% block content %}{% endblock %}
|
||||
|
||||
The ``use`` statement tells Twig to import the blocks defined in
|
||||
```blocks.html`` into the current template (it's like macros, but for blocks):
|
||||
``blocks.html`` into the current template (it's like macros, but for blocks):
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
|
|
|
@ -47,7 +47,7 @@ IDEs Integration
|
|||
Many IDEs support syntax highlighting and auto-completion for Twig:
|
||||
|
||||
* *Textmate* via the `Twig bundle`_
|
||||
* *Vim* via the `Jinja syntax plugin`_
|
||||
* *Vim* via the `Jinja syntax plugin`_ or the `vim-twig plugin`_
|
||||
* *Netbeans* via the `Twig syntax plugin`_ (until 7.1, native as of 7.2)
|
||||
* *PhpStorm* (native as of 2.1)
|
||||
* *Eclipse* via the `Twig plugin`_
|
||||
|
@ -167,7 +167,7 @@ To apply a filter on a section of code, wrap it with the
|
|||
.. code-block:: jinja
|
||||
|
||||
{% filter upper %}
|
||||
This text becomes uppercase
|
||||
This text becomes uppercase
|
||||
{% endfilter %}
|
||||
|
||||
Go to the :doc:`filters<filters/index>` page to learn more about the built-in
|
||||
|
@ -197,8 +197,6 @@ Named Arguments
|
|||
.. versionadded:: 1.12
|
||||
Support for named arguments was added in Twig 1.12.
|
||||
|
||||
Arguments for filters and functions can also be passed as *named arguments*:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% for i in range(low=1, high=10, step=2) %}
|
||||
|
@ -227,14 +225,12 @@ to change the default value:
|
|||
{# or skip the format value by using a named argument for the timezone #}
|
||||
{{ "now"|date(timezone="Europe/Paris") }}
|
||||
|
||||
You can also use both positional and named arguments in one call, which is not
|
||||
recommended as it can be confusing:
|
||||
You can also use both positional and named arguments in one call, in which
|
||||
case positional arguments must always come before named arguments:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{# both work #}
|
||||
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
|
||||
{{ "now"|date(timezone="Europe/Paris", 'd/m/Y H:i') }}
|
||||
|
||||
.. tip::
|
||||
|
||||
|
@ -544,6 +540,8 @@ macro call:
|
|||
<input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
|
||||
{% endmacro %}
|
||||
|
||||
.. _twig-expressions:
|
||||
|
||||
Expressions
|
||||
-----------
|
||||
|
||||
|
@ -554,8 +552,19 @@ even if you're not working with PHP you should feel comfortable with it.
|
|||
|
||||
The operator precedence is as follows, with the lowest-precedence
|
||||
operators listed first: ``b-and``, ``b-xor``, ``b-or``, ``or``, ``and``,
|
||||
``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``..``, ``+``,
|
||||
``-``, ``~``, ``*``, ``/``, ``//``, ``%``, ``is``, and ``**``.
|
||||
``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``matches``,
|
||||
``starts with``, ``ends with``, ``..``, ``+``, ``-``, ``~``, ``*``, ``/``,
|
||||
``//``, ``%``, ``is``, ``**``, ``|``, ``[]``, and ``.``:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% set greeting = 'Hello' %}
|
||||
{% set name = 'Fabien' %}
|
||||
|
||||
{{ greeting ~ name|lower }} {# Hello fabien #}
|
||||
|
||||
{# use parenthesis to change precedence #}
|
||||
{{ (greeting ~ name)|lower }} {# hello fabien #}
|
||||
|
||||
Literals
|
||||
~~~~~~~~
|
||||
|
@ -632,8 +641,9 @@ but exists for completeness' sake. The following operators are supported:
|
|||
* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is
|
||||
``4``.
|
||||
|
||||
* ``//``: Divides two numbers and returns the truncated integer result. ``{{
|
||||
20 // 7 }}`` is ``2``.
|
||||
* ``//``: Divides two numbers and returns the floored integer result. ``{{ 20
|
||||
// 7 }}`` is ``2``, ``{{ -20 // 7 }}`` is ``-3``(this is just syntactic
|
||||
sugar for the :doc:`round<filters/round>` filter).
|
||||
|
||||
* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would
|
||||
return ``4``.
|
||||
|
@ -664,6 +674,27 @@ Comparisons
|
|||
The following comparison operators are supported in any expression: ``==``,
|
||||
``!=``, ``<``, ``>``, ``>=``, and ``<=``.
|
||||
|
||||
You can also check if a string ``starts with`` or ``ends with`` another
|
||||
string:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if 'Fabien' starts with 'F' %}
|
||||
{% endif %}
|
||||
|
||||
{% if 'Fabien' ends with 'n' %}
|
||||
{% endif %}
|
||||
|
||||
.. note::
|
||||
|
||||
For complex string comparisons, the ``matches`` operator allows you to use
|
||||
`regular expressions`_:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if phone matches '{^[\d\.]+$}' %}
|
||||
{% endif %}
|
||||
|
||||
Containment Operator
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
|
@ -709,16 +740,16 @@ Tests can accept arguments too:
|
|||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if loop.index is divisibleby(3) %}
|
||||
{% if post.status is constant('Post::PUBLISHED') %}
|
||||
|
||||
Tests can be negated by using the ``is not`` operator:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if loop.index is not divisibleby(3) %}
|
||||
{% if post.status is not constant('Post::PUBLISHED') %}
|
||||
|
||||
{# is equivalent to #}
|
||||
{% if not (loop.index is divisibleby(3)) %}
|
||||
{% if not (post.status is constant('Post::PUBLISHED')) %}
|
||||
|
||||
Go to the :doc:`tests<tests/index>` page to learn more about the built-in
|
||||
tests.
|
||||
|
@ -751,8 +782,8 @@ categories:
|
|||
{{ foo ? 'yes' : 'no' }}
|
||||
|
||||
{# as of Twig 1.12.0 #}
|
||||
{{ foo ?: 'no' }} == {{ foo ? foo : 'no' }}
|
||||
{{ foo ? 'yes' }} == {{ foo ? 'yes' : '' }}
|
||||
{{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }}
|
||||
{{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
|
||||
|
||||
String Interpolation
|
||||
~~~~~~~~~~~~~~~~~~~~
|
||||
|
@ -785,11 +816,11 @@ Use the ``spaceless`` tag to remove whitespace *between HTML tags*:
|
|||
|
||||
{% spaceless %}
|
||||
<div>
|
||||
<strong>foo</strong>
|
||||
<strong>foo bar</strong>
|
||||
</div>
|
||||
{% endspaceless %}
|
||||
|
||||
{# output will be <div><strong>foo</strong></div> #}
|
||||
{# output will be <div><strong>foo bar</strong></div> #}
|
||||
|
||||
In addition to the spaceless tag you can also control whitespace on a per tag
|
||||
level. By using the whitespace control modifier on your tags, you can trim
|
||||
|
@ -829,7 +860,8 @@ If you want to create your own, read the :ref:`Creating an
|
|||
Extension<creating_extensions>` chapter.
|
||||
|
||||
.. _`Twig bundle`: https://github.com/Anomareh/PHP-Twig.tmbundle
|
||||
.. _`Jinja syntax plugin`: http://jinja.pocoo.org/2/documentation/integration
|
||||
.. _`Jinja syntax plugin`: http://jinja.pocoo.org/docs/integration/#vim
|
||||
.. _`vim-twig plugin`: https://github.com/evidens/vim-twig
|
||||
.. _`Twig syntax plugin`: http://plugins.netbeans.org/plugin/37069/php-twig
|
||||
.. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin
|
||||
.. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language
|
||||
|
@ -838,3 +870,4 @@ Extension<creating_extensions>` chapter.
|
|||
.. _`other Twig syntax mode`: https://github.com/muxx/Twig-HTML.mode
|
||||
.. _`Notepad++ Twig Highlighter`: https://github.com/Banane9/notepadplusplus-twig
|
||||
.. _`web-mode.el`: http://web-mode.org/
|
||||
.. _`regular expressions`: http://php.net/manual/en/pcre.pattern.php
|
||||
|
|
|
@ -1,6 +1,9 @@
|
|||
``constant``
|
||||
============
|
||||
|
||||
.. versionadded: 1.13.1
|
||||
constant now accepts object instances as the second argument.
|
||||
|
||||
``constant`` checks if a variable has the exact same value as a constant. You
|
||||
can use either global constants or class constants:
|
||||
|
||||
|
@ -9,3 +12,11 @@ can use either global constants or class constants:
|
|||
{% if post.status is constant('Post::PUBLISHED') %}
|
||||
the status attribute is exactly the same as Post::PUBLISHED
|
||||
{% endif %}
|
||||
|
||||
You can test constants from object instances as well:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if post.status is constant('PUBLISHED', post) %}
|
||||
the status attribute is exactly the same as Post::PUBLISHED
|
||||
{% endif %}
|
||||
|
|
|
@ -1,10 +1,14 @@
|
|||
``divisibleby``
|
||||
===============
|
||||
``divisible by``
|
||||
================
|
||||
|
||||
``divisibleby`` checks if a variable is divisible by a number:
|
||||
.. versionadded:: 1.14.2
|
||||
The ``divisible by`` test was added in Twig 1.14.2 as an alias for
|
||||
``divisibleby``.
|
||||
|
||||
``divisible by`` checks if a variable is divisible by a number:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if loop.index is divisibleby(3) %}
|
||||
{% if loop.index is divisible by(3) %}
|
||||
...
|
||||
{% endif %}
|
||||
|
|
|
@ -1,11 +1,14 @@
|
|||
``sameas``
|
||||
==========
|
||||
``same as``
|
||||
===========
|
||||
|
||||
``sameas`` checks if a variable points to the same memory address than another
|
||||
variable:
|
||||
.. versionadded:: 1.14.2
|
||||
The ``same as`` test was added in Twig 1.14.2 as an alias for ``sameas``.
|
||||
|
||||
``same as`` checks if a variable points to the same memory address than
|
||||
another variable:
|
||||
|
||||
.. code-block:: jinja
|
||||
|
||||
{% if foo.attribute is sameas(false) %}
|
||||
the foo attribute really is the ``false`` PHP value
|
||||
{% if foo.attribute is same as(false) %}
|
||||
the foo attribute really is the 'false' PHP value
|
||||
{% endif %}
|
||||
|
|
|
@ -1,22 +1,31 @@
|
|||
Copyright (c) 2011, Derick Rethans <derick@derickrethans.nl>
|
||||
All rights reserved.
|
||||
Copyright (c) 2009-2013 by the Twig Team, see AUTHORS for more details.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
Some rights reserved.
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
|
||||
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
+----------------------------------------------------------------------+
|
||||
| Redistribution and use in source and binary forms, with or without |
|
||||
| modification, are permitted provided that the conditions mentioned |
|
||||
| in the accompanying LICENSE file are met (BSD, revised). |
|
||||
| in the accompanying LICENSE file are met (BSD-3-Clause). |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Derick Rethans <derick@derickrethans.nl> |
|
||||
+----------------------------------------------------------------------+
|
||||
|
@ -15,7 +15,7 @@
|
|||
#ifndef PHP_TWIG_H
|
||||
#define PHP_TWIG_H
|
||||
|
||||
#define PHP_TWIG_VERSION "1.12.3"
|
||||
#define PHP_TWIG_VERSION "1.15.0"
|
||||
|
||||
#include "php.h"
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
+----------------------------------------------------------------------+
|
||||
| Redistribution and use in source and binary forms, with or without |
|
||||
| modification, are permitted provided that the conditions mentioned |
|
||||
| in the accompanying LICENSE file are met (BSD, revised). |
|
||||
| in the accompanying LICENSE file are met (BSD-3-Clause). |
|
||||
+----------------------------------------------------------------------+
|
||||
| Author: Derick Rethans <derick@derickrethans.nl> |
|
||||
+----------------------------------------------------------------------+
|
||||
|
@ -18,8 +18,10 @@
|
|||
|
||||
#include "php.h"
|
||||
#include "php_twig.h"
|
||||
#include "ext/standard/php_var.h"
|
||||
#include "ext/standard/php_string.h"
|
||||
#include "ext/standard/php_smart_str.h"
|
||||
#include "ext/spl/spl_exceptions.h"
|
||||
|
||||
#include "Zend/zend_object_handlers.h"
|
||||
#include "Zend/zend_interfaces.h"
|
||||
|
@ -76,12 +78,26 @@ zend_module_entry twig_module_entry = {
|
|||
ZEND_GET_MODULE(twig)
|
||||
#endif
|
||||
|
||||
int TWIG_ARRAY_KEY_EXISTS(zval *array, char* key, int key_len)
|
||||
int TWIG_ARRAY_KEY_EXISTS(zval *array, zval *key)
|
||||
{
|
||||
if (Z_TYPE_P(array) != IS_ARRAY) {
|
||||
return 0;
|
||||
}
|
||||
return zend_symtable_exists(Z_ARRVAL_P(array), key, key_len + 1);
|
||||
|
||||
switch (Z_TYPE_P(key)) {
|
||||
case IS_NULL:
|
||||
return zend_hash_exists(Z_ARRVAL_P(array), "", 1);
|
||||
|
||||
case IS_BOOL:
|
||||
case IS_DOUBLE:
|
||||
convert_to_long(key);
|
||||
case IS_LONG:
|
||||
return zend_hash_index_exists(Z_ARRVAL_P(array), Z_LVAL_P(key));
|
||||
|
||||
default:
|
||||
convert_to_string(key);
|
||||
return zend_symtable_exists(Z_ARRVAL_P(array), Z_STRVAL_P(key), Z_STRLEN_P(key) + 1);
|
||||
}
|
||||
}
|
||||
|
||||
int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
|
||||
|
@ -106,23 +122,23 @@ int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
|
|||
|
||||
zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
|
||||
{
|
||||
zend_class_entry *ce = Z_OBJCE_P(object);
|
||||
zval *retval;
|
||||
zend_class_entry *ce = Z_OBJCE_P(object);
|
||||
zval *retval;
|
||||
|
||||
if (Z_TYPE_P(object) == IS_OBJECT) {
|
||||
SEPARATE_ARG_IF_REF(offset);
|
||||
zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
|
||||
|
||||
zval_ptr_dtor(&offset);
|
||||
zval_ptr_dtor(&offset);
|
||||
|
||||
if (!retval) {
|
||||
if (!EG(exception)) {
|
||||
zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
if (!retval) {
|
||||
if (!EG(exception)) {
|
||||
zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return retval;
|
||||
return retval;
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
@ -200,8 +216,8 @@ zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TS
|
|||
fci.no_separation = 0;
|
||||
|
||||
if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
|
||||
FREE_DTOR(zfunction)
|
||||
zend_throw_exception_ex(zend_exception_get_default(TSRMLS_C), 0 TSRMLS_CC, "Could not execute %s::%s()", zend_get_class_entry(object TSRMLS_CC)->name, function TSRMLS_CC);
|
||||
ALLOC_INIT_ZVAL(retval_ptr);
|
||||
ZVAL_BOOL(retval_ptr, 0);
|
||||
}
|
||||
|
||||
if (args) {
|
||||
|
@ -243,9 +259,8 @@ zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
|
|||
zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
|
||||
{
|
||||
zval **tmp_zval;
|
||||
char *tmp_name;
|
||||
|
||||
if (class == NULL || Z_TYPE_P(class) != IS_ARRAY || Z_TYPE_P(prop_name) != IS_STRING) {
|
||||
if (class == NULL || Z_TYPE_P(class) != IS_ARRAY) {
|
||||
if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
|
||||
// array access object
|
||||
return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
|
||||
|
@ -253,11 +268,23 @@ zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
|
|||
return NULL;
|
||||
}
|
||||
|
||||
convert_to_string(prop_name);
|
||||
tmp_name = Z_STRVAL_P(prop_name);
|
||||
if (zend_symtable_find(HASH_OF(class), tmp_name, strlen(tmp_name)+1, (void**) &tmp_zval) == SUCCESS) {
|
||||
return *tmp_zval;
|
||||
switch(Z_TYPE_P(prop_name)) {
|
||||
case IS_NULL:
|
||||
zend_hash_find(HASH_OF(class), "", 1, (void**) &tmp_zval);
|
||||
return *tmp_zval;
|
||||
|
||||
case IS_BOOL:
|
||||
case IS_DOUBLE:
|
||||
convert_to_long(prop_name);
|
||||
case IS_LONG:
|
||||
zend_hash_index_find(HASH_OF(class), Z_LVAL_P(prop_name), (void **) &tmp_zval);
|
||||
return *tmp_zval;
|
||||
|
||||
case IS_STRING:
|
||||
zend_symtable_find(HASH_OF(class), Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name) + 1, (void**) &tmp_zval);
|
||||
return *tmp_zval;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
@ -669,7 +696,7 @@ static int twig_add_property_to_class(void *pDest APPLY_TSRMLS_DC, int num_args,
|
|||
zend_property_info *pptr = (zend_property_info *) pDest;
|
||||
APPLY_TSRMLS_FETCH();
|
||||
|
||||
if (!(pptr->flags & ZEND_ACC_PUBLIC)) {
|
||||
if (!(pptr->flags & ZEND_ACC_PUBLIC) || (pptr->flags & ZEND_ACC_STATIC)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -717,7 +744,7 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
zval *object;
|
||||
char *item;
|
||||
int item_len;
|
||||
zval zitem;
|
||||
zval *zitem, ztmpitem;
|
||||
zval *arguments = NULL;
|
||||
zval *ret = NULL;
|
||||
char *type = NULL;
|
||||
|
@ -726,24 +753,21 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
zend_bool ignoreStrictCheck = 0;
|
||||
int free_ret = 0;
|
||||
zval *tmp_self_cache;
|
||||
char *class_name = NULL;
|
||||
zval *tmp_class;
|
||||
char *type_name;
|
||||
|
||||
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozs|asbb", &template, &object, &item, &item_len, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
|
||||
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozz|asbb", &template, &object, &zitem, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
|
||||
return;
|
||||
}
|
||||
|
||||
INIT_PZVAL(&zitem);
|
||||
ZVAL_STRINGL(&zitem, item, item_len, 0);
|
||||
|
||||
switch (is_numeric_string(item, item_len, &Z_LVAL(zitem), &Z_DVAL(zitem), 0)) {
|
||||
case IS_LONG:
|
||||
Z_TYPE(zitem) = IS_LONG;
|
||||
break;
|
||||
case IS_DOUBLE:
|
||||
Z_TYPE(zitem) = IS_DOUBLE;
|
||||
convert_to_long(&zitem);
|
||||
break;
|
||||
}
|
||||
// convert the item to a string
|
||||
ztmpitem = *zitem;
|
||||
zval_copy_ctor(&ztmpitem);
|
||||
convert_to_string(&ztmpitem);
|
||||
item_len = Z_STRLEN(ztmpitem);
|
||||
item = estrndup(Z_STRVAL(ztmpitem), item_len);
|
||||
zval_dtor(&ztmpitem);
|
||||
|
||||
if (!type) {
|
||||
type = "any";
|
||||
|
@ -751,29 +775,32 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
|
||||
/*
|
||||
// array
|
||||
if (Twig_TemplateInterface::METHOD_CALL !== $type) {
|
||||
if ((is_array($object) && array_key_exists($item, $object))
|
||||
|| ($object instanceof ArrayAccess && isset($object[$item]))
|
||||
if (Twig_Template::METHOD_CALL !== $type) {
|
||||
$arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
|
||||
|
||||
if ((is_array($object) && array_key_exists($arrayItem, $object))
|
||||
|| ($object instanceof ArrayAccess && isset($object[$arrayItem]))
|
||||
) {
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $object[$item];
|
||||
return $object[$arrayItem];
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
if (strcmp("method", type) != 0) {
|
||||
// printf("XXXmethod: %s\n", type);
|
||||
if ((TWIG_ARRAY_KEY_EXISTS(object, item, item_len))
|
||||
|| (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, &zitem TSRMLS_CC))
|
||||
if ((TWIG_ARRAY_KEY_EXISTS(object, zitem))
|
||||
|| (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, zitem TSRMLS_CC))
|
||||
) {
|
||||
zval *ret;
|
||||
|
||||
if (isDefinedTest) {
|
||||
RETURN_TRUE;
|
||||
}
|
||||
|
||||
ret = TWIG_GET_ARRAY_ELEMENT(object, item, item_len TSRMLS_CC);
|
||||
ret = TWIG_GET_ARRAY_ELEMENT_ZVAL(object, zitem TSRMLS_CC);
|
||||
|
||||
if (!ret) {
|
||||
ret = &EG(uninitialized_zval);
|
||||
}
|
||||
|
@ -784,7 +811,7 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
return;
|
||||
}
|
||||
/*
|
||||
if (Twig_TemplateInterface::ARRAY_CALL === $type) {
|
||||
if (Twig_Template::ARRAY_CALL === $type) {
|
||||
if ($isDefinedTest) {
|
||||
return false;
|
||||
}
|
||||
|
@ -792,7 +819,7 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
return null;
|
||||
}
|
||||
*/
|
||||
if (strcmp("array", type) == 0) {
|
||||
if (strcmp("array", type) == 0 || Z_TYPE_P(object) != IS_OBJECT) {
|
||||
if (isDefinedTest) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
|
@ -801,11 +828,13 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
}
|
||||
/*
|
||||
if (is_object($object)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
|
||||
} elseif (is_array($object)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))), -1, $this->getTemplateName());
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
|
||||
} elseif (Twig_Template::ARRAY_CALL === $type) {
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
} else {
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a "%s" variable', $item, gettype($object)), -1, $this->getTemplateName());
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -815,7 +844,15 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
} else if (Z_TYPE_P(object) == IS_ARRAY) {
|
||||
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC));
|
||||
} else {
|
||||
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to access a key (\"%s\") on a \"%s\" variable", item, zend_zval_type_name(object));
|
||||
char *type_name = zend_zval_type_name(object);
|
||||
Z_ADDREF_P(object);
|
||||
convert_to_string(object);
|
||||
TWIG_RUNTIME_ERROR(template TSRMLS_CC,
|
||||
(strcmp("array", type) == 0)
|
||||
? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
|
||||
: "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
|
||||
item, type_name, Z_STRVAL_P(object));
|
||||
zval_ptr_dtor(&object);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
@ -836,58 +873,45 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
}
|
||||
throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, implode(', ', array_keys($object))));
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
}
|
||||
*/
|
||||
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
|
||||
RETURN_FALSE;
|
||||
}
|
||||
if (Z_TYPE_P(object) == IS_ARRAY) {
|
||||
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Item \"%s\" for \"Array\" does not exist", item);
|
||||
} else {
|
||||
Z_ADDREF_P(object);
|
||||
convert_to_string_ex(&object);
|
||||
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Item \"%s\" for \"%s\" does not exist", item, Z_STRVAL_P(object));
|
||||
zval_ptr_dtor(&object);
|
||||
return;
|
||||
}
|
||||
|
||||
type_name = zend_zval_type_name(object);
|
||||
Z_ADDREF_P(object);
|
||||
convert_to_string_ex(&object);
|
||||
|
||||
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
|
||||
|
||||
zval_ptr_dtor(&object);
|
||||
|
||||
return;
|
||||
}
|
||||
/*
|
||||
// get some information about the object
|
||||
$class = get_class($object);
|
||||
if (!isset(self::$cache[$class])) {
|
||||
$r = new ReflectionClass($class);
|
||||
self::$cache[$class] = array('methods' => array(), 'properties' => array());
|
||||
foreach ($r->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
self::$cache[$class]['methods'][strtolower($method->getName())] = true;
|
||||
}
|
||||
|
||||
foreach ($r->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
|
||||
self::$cache[$class]['properties'][$property->getName()] = true;
|
||||
}
|
||||
}
|
||||
*/
|
||||
if (Z_TYPE_P(object) == IS_OBJECT) {
|
||||
char *class_name = NULL;
|
||||
|
||||
class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
|
||||
tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
|
||||
class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
|
||||
tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
|
||||
tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
|
||||
|
||||
if (!TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC)) {
|
||||
twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
|
||||
}
|
||||
efree(class_name);
|
||||
if (!tmp_class) {
|
||||
twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
|
||||
tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
|
||||
}
|
||||
efree(class_name);
|
||||
|
||||
/*
|
||||
// object property
|
||||
if (Twig_TemplateInterface::METHOD_CALL !== $type) {
|
||||
if (isset(self::$cache[$class]['properties'][$item])
|
||||
|| isset($object->$item) || array_key_exists($item, $object)
|
||||
) {
|
||||
if (Twig_Template::METHOD_CALL !== $type) {
|
||||
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if ($this->env->hasExtension('sandbox')) {
|
||||
$this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
|
||||
}
|
||||
|
@ -897,53 +921,54 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
}
|
||||
*/
|
||||
if (strcmp("method", type) != 0) {
|
||||
zval *tmp_class, *tmp_properties, *tmp_item;
|
||||
char *class_name = NULL;
|
||||
zval *tmp_properties, *tmp_item;
|
||||
|
||||
class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
|
||||
tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
|
||||
tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
|
||||
tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
|
||||
|
||||
efree(class_name);
|
||||
|
||||
if (tmp_item || TWIG_HAS_PROPERTY(object, &zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
|
||||
if (tmp_item || TWIG_HAS_PROPERTY(object, zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
|
||||
if (isDefinedTest) {
|
||||
RETURN_TRUE;
|
||||
}
|
||||
if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
|
||||
TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, &zitem TSRMLS_CC);
|
||||
TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
|
||||
}
|
||||
if (EG(exception)) {
|
||||
return;
|
||||
}
|
||||
|
||||
ret = TWIG_PROPERTY(object, &zitem TSRMLS_CC);
|
||||
ret = TWIG_PROPERTY(object, zitem TSRMLS_CC);
|
||||
RETURN_ZVAL(ret, 1, 0);
|
||||
}
|
||||
}
|
||||
/*
|
||||
// object method
|
||||
if (!isset(self::$cache[$class]['methods'])) {
|
||||
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
|
||||
}
|
||||
|
||||
$call = false;
|
||||
$lcItem = strtolower($item);
|
||||
if (isset(self::$cache[$class]['methods'][$lcItem])) {
|
||||
$method = $item;
|
||||
$method = (string) $item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
|
||||
$method = 'get'.$item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
|
||||
$method = 'is'.$item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['__call'])) {
|
||||
$method = $item;
|
||||
$method = (string) $item;
|
||||
$call = true;
|
||||
*/
|
||||
{
|
||||
int call = 0;
|
||||
char *lcItem = TWIG_STRTOLOWER(item, item_len);
|
||||
int lcItem_length;
|
||||
char *method = NULL;
|
||||
char *tmp_method_name_get;
|
||||
char *tmp_method_name_is;
|
||||
zval *tmp_class, *tmp_methods;
|
||||
char *class_name = NULL;
|
||||
zval *zmethod;
|
||||
zval *tmp_methods;
|
||||
|
||||
class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
|
||||
lcItem_length = strlen(lcItem);
|
||||
tmp_method_name_get = emalloc(4 + lcItem_length);
|
||||
tmp_method_name_is = emalloc(3 + lcItem_length);
|
||||
|
@ -951,9 +976,7 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
sprintf(tmp_method_name_get, "get%s", lcItem);
|
||||
sprintf(tmp_method_name_is, "is%s", lcItem);
|
||||
|
||||
tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
|
||||
tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
|
||||
efree(class_name);
|
||||
|
||||
if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
|
||||
method = item;
|
||||
|
@ -963,16 +986,20 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
method = tmp_method_name_is;
|
||||
} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
|
||||
method = item;
|
||||
call = 1;
|
||||
/*
|
||||
} else {
|
||||
if ($isDefinedTest) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
}
|
||||
throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)));
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
|
||||
}
|
||||
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
}
|
||||
|
@ -1003,27 +1030,46 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
|
||||
}
|
||||
*/
|
||||
MAKE_STD_ZVAL(zmethod);
|
||||
ZVAL_STRING(zmethod, method, 1);
|
||||
if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
|
||||
TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, &zitem TSRMLS_CC);
|
||||
TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, zmethod TSRMLS_CC);
|
||||
}
|
||||
if (EG(exception)) {
|
||||
efree(tmp_method_name_get);
|
||||
efree(tmp_method_name_is);
|
||||
efree(lcItem);
|
||||
zval_ptr_dtor(&zmethod);
|
||||
return;
|
||||
}
|
||||
/*
|
||||
$ret = call_user_func_array(array($object, $method), $arguments);
|
||||
// Some objects throw exceptions when they have __call, and the method we try
|
||||
// to call is not supported. If ignoreStrictCheck is true, we should return null.
|
||||
try {
|
||||
$ret = call_user_func_array(array($object, $method), $arguments);
|
||||
} catch (BadMethodCallException $e) {
|
||||
if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
|
||||
return null;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
*/
|
||||
if (Z_TYPE_P(object) == IS_OBJECT) {
|
||||
ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
|
||||
free_ret = 1;
|
||||
ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
|
||||
if (EG(exception) && TWIG_INSTANCE_OF(EG(exception), spl_ce_BadMethodCallException TSRMLS_CC)) {
|
||||
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
|
||||
zend_clear_exception(TSRMLS_C);
|
||||
return;
|
||||
}
|
||||
}
|
||||
free_ret = 1;
|
||||
efree(tmp_method_name_get);
|
||||
efree(tmp_method_name_is);
|
||||
efree(lcItem);
|
||||
zval_ptr_dtor(&zmethod);
|
||||
}
|
||||
/*
|
||||
// useful when calling a template method from a template
|
||||
// this is not supported but unfortunately heavily used in the Symfony profiler
|
||||
if ($object instanceof Twig_TemplateInterface) {
|
||||
return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
|
||||
}
|
||||
|
@ -1033,9 +1079,7 @@ PHP_FUNCTION(twig_template_get_attributes)
|
|||
// ret can be null, if e.g. the called method throws an exception
|
||||
if (ret) {
|
||||
if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
|
||||
if (Z_STRLEN_P(ret) == 0) {
|
||||
free_ret = 1;
|
||||
} else {
|
||||
if (Z_STRLEN_P(ret) != 0) {
|
||||
zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
|
||||
TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
|
||||
zval_ptr_dtor(&charset);
|
||||
|
|
|
@ -18,11 +18,16 @@ class Twig_Autoloader
|
|||
{
|
||||
/**
|
||||
* Registers Twig_Autoloader as an SPL autoloader.
|
||||
*
|
||||
* @param Boolean $prepend Whether to prepend the autoloader or not.
|
||||
*/
|
||||
public static function register()
|
||||
public static function register($prepend = false)
|
||||
{
|
||||
ini_set('unserialize_callback_func', 'spl_autoload_call');
|
||||
spl_autoload_register(array(new self, 'autoload'));
|
||||
if (version_compare(phpversion(), '5.3.0', '>=')) {
|
||||
spl_autoload_register(array(new self, 'autoload'), true, $prepend);
|
||||
} else {
|
||||
spl_autoload_register(array(new self, 'autoload'));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -180,11 +180,12 @@ class Twig_Compiler implements Twig_CompilerInterface
|
|||
$this->raw($value ? 'true' : 'false');
|
||||
} elseif (is_array($value)) {
|
||||
$this->raw('array(');
|
||||
$i = 0;
|
||||
$first = true;
|
||||
foreach ($value as $key => $value) {
|
||||
if ($i++) {
|
||||
if (!$first) {
|
||||
$this->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
$this->repr($key);
|
||||
$this->raw(' => ');
|
||||
$this->repr($value);
|
||||
|
@ -252,6 +253,8 @@ class Twig_Compiler implements Twig_CompilerInterface
|
|||
* @param integer $step The number of indentation to remove
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*
|
||||
* @throws LogicException When trying to outdent too much so the indentation would become negative
|
||||
*/
|
||||
public function outdent($step = 1)
|
||||
{
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
*/
|
||||
class Twig_Environment
|
||||
{
|
||||
const VERSION = '1.12.3';
|
||||
const VERSION = '1.15.0';
|
||||
|
||||
protected $charset;
|
||||
protected $loader;
|
||||
|
@ -53,7 +53,7 @@ class Twig_Environment
|
|||
* * debug: When set to true, it automatically set "auto_reload" to true as
|
||||
* well (default to false).
|
||||
*
|
||||
* * charset: The charset used by the templates (default to utf-8).
|
||||
* * charset: The charset used by the templates (default to UTF-8).
|
||||
*
|
||||
* * base_template_class: The base template class to use for generated
|
||||
* templates (default to Twig_Template).
|
||||
|
@ -61,9 +61,9 @@ class Twig_Environment
|
|||
* * cache: An absolute path where to store the compiled templates, or
|
||||
* false to disable compilation cache (default).
|
||||
*
|
||||
* * auto_reload: Whether to reload the template is the original source changed.
|
||||
* * auto_reload: Whether to reload the template if the original source changed.
|
||||
* If you don't provide the auto_reload option, it will be
|
||||
* determined automatically base on the debug value.
|
||||
* determined automatically based on the debug value.
|
||||
*
|
||||
* * strict_variables: Whether to ignore invalid variables in templates
|
||||
* (default to false).
|
||||
|
@ -99,7 +99,7 @@ class Twig_Environment
|
|||
), $options);
|
||||
|
||||
$this->debug = (bool) $options['debug'];
|
||||
$this->charset = $options['charset'];
|
||||
$this->charset = strtoupper($options['charset']);
|
||||
$this->baseTemplateClass = $options['base_template_class'];
|
||||
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
|
||||
$this->strictVariables = (bool) $options['strict_variables'];
|
||||
|
@ -239,7 +239,7 @@ class Twig_Environment
|
|||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string The cache file name
|
||||
* @return string|false The cache file name or false when caching is disabled
|
||||
*/
|
||||
public function getCacheFilename($name)
|
||||
{
|
||||
|
@ -262,7 +262,7 @@ class Twig_Environment
|
|||
*/
|
||||
public function getTemplateClass($name, $index = null)
|
||||
{
|
||||
return $this->templateClassPrefix.md5($this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index);
|
||||
return $this->templateClassPrefix.hash('sha256', $this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -282,6 +282,10 @@ class Twig_Environment
|
|||
* @param array $context An array of parameters to pass to the template
|
||||
*
|
||||
* @return string The rendered template
|
||||
*
|
||||
* @throws Twig_Error_Loader When the template cannot be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
* @throws Twig_Error_Runtime When an error occurred during rendering
|
||||
*/
|
||||
public function render($name, array $context = array())
|
||||
{
|
||||
|
@ -293,6 +297,10 @@ class Twig_Environment
|
|||
*
|
||||
* @param string $name The template name
|
||||
* @param array $context An array of parameters to pass to the template
|
||||
*
|
||||
* @throws Twig_Error_Loader When the template cannot be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
* @throws Twig_Error_Runtime When an error occurred during rendering
|
||||
*/
|
||||
public function display($name, array $context = array())
|
||||
{
|
||||
|
@ -306,6 +314,9 @@ class Twig_Environment
|
|||
* @param integer $index The index if it is an embedded template
|
||||
*
|
||||
* @return Twig_TemplateInterface A template instance representing the given template name
|
||||
*
|
||||
* @throws Twig_Error_Loader When the template cannot be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
*/
|
||||
public function loadTemplate($name, $index = null)
|
||||
{
|
||||
|
@ -358,6 +369,19 @@ class Twig_Environment
|
|||
return $this->getLoader()->isFresh($name, $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load a template consecutively from an array.
|
||||
*
|
||||
* Similar to loadTemplate() but it also accepts Twig_TemplateInterface instances and an array
|
||||
* of templates where each is tried to be loaded.
|
||||
*
|
||||
* @param string|Twig_Template|array $names A template or an array of templates to try consecutively
|
||||
*
|
||||
* @return Twig_Template
|
||||
*
|
||||
* @throws Twig_Error_Loader When none of the templates can be found
|
||||
* @throws Twig_Error_Syntax When an error occurred during compilation
|
||||
*/
|
||||
public function resolveTemplate($names)
|
||||
{
|
||||
if (!is_array($names)) {
|
||||
|
@ -437,6 +461,8 @@ class Twig_Environment
|
|||
* @param string $name The template name
|
||||
*
|
||||
* @return Twig_TokenStream A Twig_TokenStream instance
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the code is syntactically wrong
|
||||
*/
|
||||
public function tokenize($source, $name = null)
|
||||
{
|
||||
|
@ -468,15 +494,17 @@ class Twig_Environment
|
|||
}
|
||||
|
||||
/**
|
||||
* Parses a token stream.
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $tokens A Twig_TokenStream instance
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A Node tree
|
||||
* @return Twig_Node_Module A node tree
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
|
||||
*/
|
||||
public function parse(Twig_TokenStream $tokens)
|
||||
public function parse(Twig_TokenStream $stream)
|
||||
{
|
||||
return $this->getParser()->parse($tokens);
|
||||
return $this->getParser()->parse($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -504,7 +532,7 @@ class Twig_Environment
|
|||
}
|
||||
|
||||
/**
|
||||
* Compiles a Node.
|
||||
* Compiles a node and returns the PHP code.
|
||||
*
|
||||
* @param Twig_NodeInterface $node A Twig_NodeInterface instance
|
||||
*
|
||||
|
@ -522,6 +550,8 @@ class Twig_Environment
|
|||
* @param string $name The template name
|
||||
*
|
||||
* @return string The compiled PHP source code
|
||||
*
|
||||
* @throws Twig_Error_Syntax When there was an error during tokenizing, parsing or compiling
|
||||
*/
|
||||
public function compileSource($source, $name = null)
|
||||
{
|
||||
|
@ -531,7 +561,7 @@ class Twig_Environment
|
|||
$e->setTemplateFile($name);
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
|
||||
throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -566,7 +596,7 @@ class Twig_Environment
|
|||
*/
|
||||
public function setCharset($charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
$this->charset = strtoupper($charset);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -728,7 +758,7 @@ class Twig_Environment
|
|||
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
|
||||
{
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException('Unable to add a node visitor as extensions have already been initialized.', $extension->getName());
|
||||
throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
|
||||
}
|
||||
|
||||
$this->staging->addNodeVisitor($visitor);
|
||||
|
@ -764,11 +794,11 @@ class Twig_Environment
|
|||
$filter = $name;
|
||||
$name = $filter->getName();
|
||||
}
|
||||
|
||||
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name));
|
||||
}
|
||||
|
||||
|
||||
$this->staging->addFilter($name, $filter);
|
||||
}
|
||||
|
||||
|
@ -853,7 +883,7 @@ class Twig_Environment
|
|||
$test = $name;
|
||||
$name = $test->getName();
|
||||
}
|
||||
|
||||
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
|
||||
}
|
||||
|
@ -911,11 +941,11 @@ class Twig_Environment
|
|||
$function = $name;
|
||||
$name = $function->getName();
|
||||
}
|
||||
|
||||
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name));
|
||||
}
|
||||
|
||||
|
||||
$this->staging->addFunction($name, $function);
|
||||
}
|
||||
|
||||
|
@ -1099,10 +1129,17 @@ class Twig_Environment
|
|||
{
|
||||
$globals = array();
|
||||
foreach ($this->extensions as $extension) {
|
||||
$globals = array_merge($globals, $extension->getGlobals());
|
||||
$extGlob = $extension->getGlobals();
|
||||
if (!is_array($extGlob)) {
|
||||
throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
|
||||
}
|
||||
|
||||
$globals[] = $extGlob;
|
||||
}
|
||||
|
||||
return array_merge($globals, $this->staging->getGlobals());
|
||||
$globals[] = $this->staging->getGlobals();
|
||||
|
||||
return call_user_func_array('array_merge', $globals);
|
||||
}
|
||||
|
||||
protected function initExtensions()
|
||||
|
@ -1202,7 +1239,7 @@ class Twig_Environment
|
|||
throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir));
|
||||
}
|
||||
|
||||
$tmpFile = tempnam(dirname($file), basename($file));
|
||||
$tmpFile = tempnam($dir, basename($file));
|
||||
if (false !== @file_put_contents($tmpFile, $content)) {
|
||||
// rename does not work on Win32 before 5.2.6
|
||||
if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) {
|
||||
|
|
|
@ -186,10 +186,21 @@ class Twig_Error extends Exception
|
|||
protected function guessTemplateInfo()
|
||||
{
|
||||
$template = null;
|
||||
foreach (debug_backtrace() as $trace) {
|
||||
$templateClass = null;
|
||||
|
||||
if (version_compare(phpversion(), '5.3.6', '>=')) {
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
} else {
|
||||
$backtrace = debug_backtrace();
|
||||
}
|
||||
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
|
||||
if (null === $this->filename || $this->filename == $trace['object']->getTemplateName()) {
|
||||
$currentClass = get_class($trace['object']);
|
||||
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
|
||||
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
|
||||
$template = $trace['object'];
|
||||
$templateClass = get_class($trace['object']);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -206,6 +217,11 @@ class Twig_Error extends Exception
|
|||
$r = new ReflectionObject($template);
|
||||
$file = $r->getFileName();
|
||||
|
||||
// hhvm has a bug where eval'ed files comes out as the current directory
|
||||
if (is_dir($file)) {
|
||||
$file = '';
|
||||
}
|
||||
|
||||
$exceptions = array($e = $this);
|
||||
while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) {
|
||||
$exceptions[] = $e;
|
||||
|
|
|
@ -22,7 +22,7 @@ interface Twig_ExistsLoaderInterface
|
|||
*
|
||||
* @param string $name The name of the template to check if we can load
|
||||
*
|
||||
* @return boolean If the template source code is handled by this loader or not
|
||||
* @return Boolean If the template source code is handled by this loader or not
|
||||
*/
|
||||
public function exists($name);
|
||||
}
|
||||
|
|
|
@ -86,18 +86,15 @@ class Twig_ExpressionParser
|
|||
|
||||
protected function parseConditionalExpression($expr)
|
||||
{
|
||||
while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) {
|
||||
$this->parser->getStream()->next();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
while ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, '?')) {
|
||||
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$this->parser->getStream()->next();
|
||||
if ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
$expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
} else {
|
||||
$this->parser->getStream()->next();
|
||||
$expr2 = $expr;
|
||||
$expr3 = $this->parseExpression();
|
||||
}
|
||||
|
@ -161,6 +158,14 @@ class Twig_ExpressionParser
|
|||
$node = $this->parseStringExpression();
|
||||
break;
|
||||
|
||||
case Twig_Token::OPERATOR_TYPE:
|
||||
if (preg_match(Twig_Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
|
||||
// in this context, string operators are variable names
|
||||
$this->parser->getStream()->next();
|
||||
$node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) {
|
||||
$node = $this->parseArrayExpression();
|
||||
|
@ -182,12 +187,10 @@ class Twig_ExpressionParser
|
|||
// a string cannot be followed by another string in a single expression
|
||||
$nextCanBeString = true;
|
||||
while (true) {
|
||||
if ($stream->test(Twig_Token::STRING_TYPE) && $nextCanBeString) {
|
||||
$token = $stream->next();
|
||||
if ($nextCanBeString && $token = $stream->nextIf(Twig_Token::STRING_TYPE)) {
|
||||
$nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
$nextCanBeString = false;
|
||||
} elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) {
|
||||
$stream->next();
|
||||
} elseif ($stream->nextIf(Twig_Token::INTERPOLATION_START_TYPE)) {
|
||||
$nodes[] = $this->parseExpression();
|
||||
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
|
||||
$nextCanBeString = true;
|
||||
|
@ -253,8 +256,7 @@ class Twig_ExpressionParser
|
|||
// * a string -- 'a'
|
||||
// * a name, which is equivalent to a string -- a
|
||||
// * an expression, which must be enclosed in parentheses -- (1 + 2)
|
||||
if ($stream->test(Twig_Token::STRING_TYPE) || $stream->test(Twig_Token::NAME_TYPE) || $stream->test(Twig_Token::NUMBER_TYPE)) {
|
||||
$token = $stream->next();
|
||||
if (($token = $stream->nextIf(Twig_Token::STRING_TYPE)) || ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) || $token = $stream->nextIf(Twig_Token::NUMBER_TYPE)) {
|
||||
$key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
} elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$key = $this->parseExpression();
|
||||
|
@ -316,7 +318,7 @@ class Twig_ExpressionParser
|
|||
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) : new Twig_Node_Expression_Array(array(), $line), Twig_TemplateInterface::ANY_CALL, $line);
|
||||
return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_Template::ANY_CALL, $line);
|
||||
default:
|
||||
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
|
||||
$arguments = new Twig_Node_Expression_Array(array(), $line);
|
||||
|
@ -343,7 +345,7 @@ class Twig_ExpressionParser
|
|||
$token = $stream->next();
|
||||
$lineno = $token->getLine();
|
||||
$arguments = new Twig_Node_Expression_Array(array(), $lineno);
|
||||
$type = Twig_TemplateInterface::ANY_CALL;
|
||||
$type = Twig_Template::ANY_CALL;
|
||||
if ($token->getValue() == '.') {
|
||||
$token = $stream->next();
|
||||
if (
|
||||
|
@ -365,7 +367,7 @@ class Twig_ExpressionParser
|
|||
throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
if ($node instanceof Twig_Node_Expression_Name && null !== $alias = $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
|
||||
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());
|
||||
}
|
||||
|
@ -376,7 +378,7 @@ class Twig_ExpressionParser
|
|||
return $node;
|
||||
}
|
||||
} else {
|
||||
$type = Twig_TemplateInterface::ARRAY_CALL;
|
||||
$type = Twig_Template::ARRAY_CALL;
|
||||
|
||||
// slice?
|
||||
$slice = false;
|
||||
|
@ -387,9 +389,8 @@ class Twig_ExpressionParser
|
|||
$arg = $this->parseExpression();
|
||||
}
|
||||
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
if ($stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$stream->next();
|
||||
}
|
||||
|
||||
if ($slice) {
|
||||
|
@ -472,8 +473,7 @@ class Twig_ExpressionParser
|
|||
}
|
||||
|
||||
$name = null;
|
||||
if ($namedArguments && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
$token = $stream->next();
|
||||
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());
|
||||
}
|
||||
|
@ -519,10 +519,9 @@ class Twig_ExpressionParser
|
|||
}
|
||||
$targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine());
|
||||
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return new Twig_Node($targets);
|
||||
|
@ -533,10 +532,9 @@ class Twig_ExpressionParser
|
|||
$targets = array();
|
||||
while (true) {
|
||||
$targets[] = $this->parseExpression();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return new Twig_Node($targets);
|
||||
|
|
|
@ -34,7 +34,7 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
|
|||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
<?php
|
||||
|
||||
if (!defined('ENT_SUBSTITUTE')) {
|
||||
define('ENT_SUBSTITUTE', 8);
|
||||
// use 0 as hhvm does not support several flags yet
|
||||
define('ENT_SUBSTITUTE', 0);
|
||||
}
|
||||
|
||||
/*
|
||||
|
@ -17,6 +18,28 @@ class Twig_Extension_Core extends Twig_Extension
|
|||
protected $dateFormats = array('F j, Y H:i', '%d days');
|
||||
protected $numberFormat = array(0, '.', ',');
|
||||
protected $timezone = null;
|
||||
protected $escapers = array();
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$this->escapers[$strategy] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all defined escapers.
|
||||
*
|
||||
* @return array An array of escapers
|
||||
*/
|
||||
public function getEscapers()
|
||||
{
|
||||
return $this->escapers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default format to be used by the date filter.
|
||||
|
@ -94,7 +117,7 @@ class Twig_Extension_Core extends Twig_Extension
|
|||
/**
|
||||
* Returns the token parser instance to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParser instances
|
||||
* @return Twig_TokenParser[] An array of Twig_TokenParser instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
|
@ -132,6 +155,7 @@ class Twig_Extension_Core extends Twig_Extension
|
|||
new Twig_SimpleFilter('replace', 'strtr'),
|
||||
new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)),
|
||||
new Twig_SimpleFilter('abs', 'abs'),
|
||||
new Twig_SimpleFilter('round', 'twig_round'),
|
||||
|
||||
// encoding
|
||||
new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'),
|
||||
|
@ -186,12 +210,15 @@ class Twig_Extension_Core extends Twig_Extension
|
|||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
new Twig_SimpleFunction('max', 'max'),
|
||||
new Twig_SimpleFunction('min', 'min'),
|
||||
new Twig_SimpleFunction('range', 'range'),
|
||||
new Twig_SimpleFunction('constant', 'twig_constant'),
|
||||
new Twig_SimpleFunction('cycle', 'twig_cycle'),
|
||||
new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
|
||||
new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
|
||||
new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true)),
|
||||
new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))),
|
||||
new Twig_SimpleFunction('source', 'twig_source', array('needs_environment' => true, 'is_safe' => array('all'))),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -207,9 +234,11 @@ class Twig_Extension_Core extends Twig_Extension
|
|||
new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
|
||||
new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
|
||||
new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
|
||||
new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
|
||||
new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
|
||||
new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
|
||||
new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
|
||||
new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
|
||||
new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
|
||||
new Twig_SimpleTest('empty', 'twig_test_empty'),
|
||||
new Twig_SimpleTest('iterable', 'twig_test_iterable'),
|
||||
|
@ -230,50 +259,52 @@ class Twig_Extension_Core extends Twig_Extension
|
|||
'+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
|
||||
),
|
||||
array(
|
||||
'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
|
||||
'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'matches' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Matches', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'starts with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_StartsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'ends with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_EndsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
|
||||
'**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
public function parseNotTestExpression(Twig_Parser $parser, $node)
|
||||
public function parseNotTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
|
||||
{
|
||||
return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
public function parseTestExpression(Twig_Parser $parser, $node)
|
||||
public function parseTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
|
||||
$class = $this->getTestNodeClass($parser, $name, $node->getLine());
|
||||
$arguments = null;
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = $parser->getExpressionParser()->parseArguments(true);
|
||||
}
|
||||
|
||||
$class = $this->getTestNodeClass($parser, $name, $node->getLine());
|
||||
|
||||
return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
|
@ -281,7 +312,21 @@ class Twig_Extension_Core extends Twig_Extension
|
|||
{
|
||||
$env = $parser->getEnvironment();
|
||||
$testMap = $env->getTests();
|
||||
if (!isset($testMap[$name])) {
|
||||
$testName = null;
|
||||
if (isset($testMap[$name])) {
|
||||
$testName = $name;
|
||||
} elseif ($parser->getStream()->test(Twig_Token::NAME_TYPE)) {
|
||||
// try 2-words tests
|
||||
$name = $name.' '.$parser->getCurrentToken()->getValue();
|
||||
|
||||
if (isset($testMap[$name])) {
|
||||
$parser->getStream()->next();
|
||||
|
||||
$testName = $name;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $testName) {
|
||||
$message = sprintf('The test "%s" does not exist', $name);
|
||||
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getTests()))) {
|
||||
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
|
||||
|
@ -348,7 +393,7 @@ function twig_random(Twig_Environment $env, $values = null)
|
|||
return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values);
|
||||
}
|
||||
|
||||
if ($values instanceof Traversable) {
|
||||
if (is_object($values) && $values instanceof Traversable) {
|
||||
$values = iterator_to_array($values);
|
||||
} elseif (is_string($values)) {
|
||||
if ('' === $values) {
|
||||
|
@ -459,13 +504,15 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu
|
|||
$defaultTimezone = $timezone;
|
||||
}
|
||||
|
||||
if ($date instanceof DateTime) {
|
||||
$date = clone $date;
|
||||
if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
|
||||
$returningDate = new DateTime($date->format('c'));
|
||||
if (false !== $timezone) {
|
||||
$date->setTimezone($defaultTimezone);
|
||||
$returningDate->setTimezone($defaultTimezone);
|
||||
} else {
|
||||
$returningDate->setTimezone($date->getTimezone());
|
||||
}
|
||||
|
||||
return $date;
|
||||
return $returningDate;
|
||||
}
|
||||
|
||||
$asString = (string) $date;
|
||||
|
@ -481,6 +528,28 @@ function twig_date_converter(Twig_Environment $env, $date = null, $timezone = nu
|
|||
return $date;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rounds a number.
|
||||
*
|
||||
* @param integer|float $value The value to round
|
||||
* @param integer|float $precision The rounding precision
|
||||
* @param string $method The method to use for rounding
|
||||
*
|
||||
* @return integer|float The rounded number
|
||||
*/
|
||||
function twig_round($value, $precision = 0, $method = 'common')
|
||||
{
|
||||
if ('common' == $method) {
|
||||
return round($value, $precision);
|
||||
}
|
||||
|
||||
if ('ceil' != $method && 'floor' != $method) {
|
||||
throw new Twig_Error_Runtime('The round filter only supports the "common", "ceil", and "floor" methods.');
|
||||
}
|
||||
|
||||
return $method($value * pow(10, $precision)) / pow(10, $precision);
|
||||
}
|
||||
|
||||
/**
|
||||
* Number format filter.
|
||||
*
|
||||
|
@ -518,7 +587,7 @@ function twig_number_format_filter(Twig_Environment $env, $number, $decimal = nu
|
|||
* URL encodes a string as a path segment or an array as a query string.
|
||||
*
|
||||
* @param string|array $url A URL or an array of query parameters
|
||||
* @param bool $raw true to use rawurlencode() instead of urlencode
|
||||
* @param Boolean $raw true to use rawurlencode() instead of urlencode
|
||||
*
|
||||
* @return string The URL encoded value
|
||||
*/
|
||||
|
@ -620,7 +689,7 @@ function twig_array_merge($arr1, $arr2)
|
|||
*/
|
||||
function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false)
|
||||
{
|
||||
if ($item instanceof Traversable) {
|
||||
if (is_object($item) && $item instanceof Traversable) {
|
||||
$item = iterator_to_array($item, false);
|
||||
}
|
||||
|
||||
|
@ -649,7 +718,7 @@ function twig_first(Twig_Environment $env, $item)
|
|||
{
|
||||
$elements = twig_slice($env, $item, 0, 1, false);
|
||||
|
||||
return is_string($elements) ? $elements[0] : current($elements);
|
||||
return is_string($elements) ? $elements : current($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -664,7 +733,7 @@ function twig_last(Twig_Environment $env, $item)
|
|||
{
|
||||
$elements = twig_slice($env, $item, -1, 1, false);
|
||||
|
||||
return is_string($elements) ? $elements[0] : current($elements);
|
||||
return is_string($elements) ? $elements : current($elements);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -687,7 +756,7 @@ function twig_last(Twig_Environment $env, $item)
|
|||
*/
|
||||
function twig_join_filter($value, $glue = '')
|
||||
{
|
||||
if ($value instanceof Traversable) {
|
||||
if (is_object($value) && $value instanceof Traversable) {
|
||||
$value = iterator_to_array($value, false);
|
||||
}
|
||||
|
||||
|
@ -829,7 +898,7 @@ function twig_in_filter($value, $compare)
|
|||
}
|
||||
|
||||
return false !== strpos($compare, (string) $value);
|
||||
} elseif ($compare instanceof Traversable) {
|
||||
} elseif (is_object($compare) && $compare instanceof Traversable) {
|
||||
return in_array($value, iterator_to_array($compare, false), is_object($value));
|
||||
}
|
||||
|
||||
|
@ -847,21 +916,70 @@ function twig_in_filter($value, $compare)
|
|||
*/
|
||||
function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
|
||||
{
|
||||
if ($autoescape && is_object($string) && $string instanceof Twig_Markup) {
|
||||
if ($autoescape && $string instanceof Twig_Markup) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) {
|
||||
return $string;
|
||||
if (!is_string($string)) {
|
||||
if (is_object($string) && method_exists($string, '__toString')) {
|
||||
$string = (string) $string;
|
||||
} else {
|
||||
return $string;
|
||||
}
|
||||
}
|
||||
|
||||
if (null === $charset) {
|
||||
$charset = $env->getCharset();
|
||||
}
|
||||
|
||||
$string = (string) $string;
|
||||
|
||||
switch ($strategy) {
|
||||
case 'html':
|
||||
// see http://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;
|
||||
|
||||
if (null === $htmlspecialcharsCharsets) {
|
||||
if ('hiphop' === substr(PHP_VERSION, -6)) {
|
||||
$htmlspecialcharsCharsets = array('utf-8' => true, 'UTF-8' => true);
|
||||
} else {
|
||||
$htmlspecialcharsCharsets = array(
|
||||
'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 twig_convert_encoding($string, $charset, 'UTF-8');
|
||||
|
||||
case 'js':
|
||||
// escape all non-alphanumeric characters
|
||||
// into their \xHH or \uHHHH representations
|
||||
|
@ -915,47 +1033,29 @@ function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html',
|
|||
|
||||
return $string;
|
||||
|
||||
case 'html':
|
||||
// see http://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 = array(
|
||||
'iso-8859-1' => true, 'iso8859-1' => true,
|
||||
'iso-8859-15' => true, 'iso8859-15' => 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[strtolower($charset)])) {
|
||||
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 twig_convert_encoding($string, $charset, 'UTF-8');
|
||||
|
||||
case 'url':
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
// hackish test to avoid version_compare that is much slower, this works unless PHP releases a 5.10.*
|
||||
// at that point however PHP 5.2.* support can be removed
|
||||
if (PHP_VERSION < '5.3.0') {
|
||||
return str_replace('%7E', '~', rawurlencode($string));
|
||||
}
|
||||
|
||||
return rawurlencode($string);
|
||||
|
||||
default:
|
||||
throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: html, js, url, css, and html_attr).', $strategy));
|
||||
static $escapers;
|
||||
|
||||
if (null === $escapers) {
|
||||
$escapers = $env->getExtension('core')->getEscapers();
|
||||
}
|
||||
|
||||
if (isset($escapers[$strategy])) {
|
||||
return call_user_func($escapers[$strategy], $env, $string, $charset);
|
||||
}
|
||||
|
||||
$validStrategies = implode(', ', array_merge(array('html', 'js', 'url', 'css', 'html_attr'), array_keys($escapers)));
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1262,11 +1362,11 @@ function twig_test_iterable($value)
|
|||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* @param string template The template to render
|
||||
* @param array variables The variables to pass to the template
|
||||
* @param Boolean with_context Whether to pass the current context variables or not
|
||||
* @param Boolean ignore_missing Whether to ignore missing templates or not
|
||||
* @param Boolean sandboxed Whether to sandbox the template or not
|
||||
* @param string|array $template The template to render or an array of templates to try consecutively
|
||||
* @param array $variables The variables to pass to the template
|
||||
* @param Boolean $with_context Whether to pass the current context variables or not
|
||||
* @param Boolean $ignore_missing Whether to ignore missing templates or not
|
||||
* @param Boolean $sandboxed Whether to sandbox the template or not
|
||||
*
|
||||
* @return string The rendered template
|
||||
*/
|
||||
|
@ -1284,7 +1384,7 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
|
|||
}
|
||||
|
||||
try {
|
||||
return $env->resolveTemplate($template)->display($variables);
|
||||
return $env->resolveTemplate($template)->render($variables);
|
||||
} catch (Twig_Error_Loader $e) {
|
||||
if (!$ignoreMissing) {
|
||||
throw $e;
|
||||
|
@ -1296,6 +1396,18 @@ function twig_include(Twig_Environment $env, $context, $template, $variables = a
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a template content without rendering it.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string The template source
|
||||
*/
|
||||
function twig_source(Twig_Environment $env, $name)
|
||||
{
|
||||
return $env->getLoader()->getSource($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Provides the ability to get constants from instances as well as class/global constants.
|
||||
*
|
||||
|
@ -1318,13 +1430,13 @@ function twig_constant($constant, $object = null)
|
|||
*
|
||||
* @param array $items An array of items
|
||||
* @param integer $size The size of the batch
|
||||
* @param string $fill A string to fill missing items
|
||||
* @param mixed $fill A value used to fill missing items
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
function twig_array_batch($items, $size, $fill = null)
|
||||
{
|
||||
if ($items instanceof Traversable) {
|
||||
if (is_object($items) && $items instanceof Traversable) {
|
||||
$items = iterator_to_array($items, false);
|
||||
}
|
||||
|
||||
|
@ -1334,10 +1446,12 @@ function twig_array_batch($items, $size, $fill = null)
|
|||
|
||||
if (null !== $fill) {
|
||||
$last = count($result) - 1;
|
||||
$result[$last] = array_merge(
|
||||
$result[$last],
|
||||
array_fill(0, $size - count($result[$last]), $fill)
|
||||
);
|
||||
if ($fillCount = $size - count($result[$last])) {
|
||||
$result[$last] = array_merge(
|
||||
$result[$last],
|
||||
array_fill(0, $fillCount, $fill)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
|
|
|
@ -24,6 +24,7 @@ class Twig_Extension_Debug extends Twig_Extension
|
|||
// false means that it was not set (and the default is on) or it explicitly enabled
|
||||
// xdebug.overload_var_dump produces HTML only when html_errors is also enabled
|
||||
&& (false === ini_get('html_errors') || ini_get('html_errors'))
|
||||
|| 'cli' === php_sapi_name()
|
||||
;
|
||||
|
||||
return array(
|
||||
|
|
|
@ -30,7 +30,7 @@ class Twig_Extension_Escaper extends Twig_Extension
|
|||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
|
|
|
@ -33,7 +33,7 @@ class Twig_Extension_Sandbox extends Twig_Extension
|
|||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
|
|
|
@ -33,7 +33,7 @@ class Twig_Extension_StringLoader extends Twig_Extension
|
|||
* Loads a template from a string.
|
||||
*
|
||||
* <pre>
|
||||
* {% include template_from_string("Hello {{ name }}") }}
|
||||
* {{ include(template_from_string("Hello {{ name }}")) }}
|
||||
* </pre>
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
|
@ -43,16 +43,16 @@ class Twig_Extension_StringLoader extends Twig_Extension
|
|||
*/
|
||||
function twig_template_from_string(Twig_Environment $env, $template)
|
||||
{
|
||||
static $loader;
|
||||
$name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false));
|
||||
|
||||
if (null === $loader) {
|
||||
$loader = new Twig_Loader_String();
|
||||
}
|
||||
$loader = new Twig_Loader_Chain(array(
|
||||
new Twig_Loader_Array(array($name => $template)),
|
||||
$current = $env->getLoader(),
|
||||
));
|
||||
|
||||
$current = $env->getLoader();
|
||||
$env->setLoader($loader);
|
||||
try {
|
||||
$template = $env->loadTemplate($template);
|
||||
$template = $env->loadTemplate($name);
|
||||
} catch (Exception $e) {
|
||||
$env->setLoader($current);
|
||||
|
||||
|
|
|
@ -35,7 +35,7 @@ interface Twig_ExtensionInterface
|
|||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors();
|
||||
|
||||
|
|
|
@ -62,8 +62,6 @@ abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableI
|
|||
if (isset($this->options['is_safe_callback'])) {
|
||||
return call_user_func($this->options['is_safe_callback'], $filterArgs);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPreservesSafety()
|
||||
|
|
|
@ -73,12 +73,7 @@ class Twig_Lexer implements Twig_LexerInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Tokenizes a source code.
|
||||
*
|
||||
* @param string $code The source code
|
||||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function tokenize($code, $filename = null)
|
||||
{
|
||||
|
@ -233,7 +228,7 @@ class Twig_Lexer implements Twig_LexerInterface
|
|||
|
||||
// operators
|
||||
if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]);
|
||||
$this->pushToken(Twig_Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// names
|
||||
|
@ -326,7 +321,6 @@ class Twig_Lexer implements Twig_LexerInterface
|
|||
$this->moveCursor($match[0]);
|
||||
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
|
||||
|
||||
list($expect, $lineno) = array_pop($this->brackets);
|
||||
if ($this->code[$this->cursor] != '"') {
|
||||
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
|
||||
|
@ -382,10 +376,15 @@ class Twig_Lexer implements Twig_LexerInterface
|
|||
// an operator that ends with a character must be followed by
|
||||
// a whitespace or a parenthesis
|
||||
if (ctype_alpha($operator[$length - 1])) {
|
||||
$regex[] = preg_quote($operator, '/').'(?=[\s()])';
|
||||
$r = preg_quote($operator, '/').'(?=[\s()])';
|
||||
} else {
|
||||
$regex[] = preg_quote($operator, '/');
|
||||
$r = preg_quote($operator, '/');
|
||||
}
|
||||
|
||||
// an operator with a space can be any amount of whitespaces
|
||||
$r = preg_replace('/\s+/', '\s+', $r);
|
||||
|
||||
$regex[] = $r;
|
||||
}
|
||||
|
||||
return '/'.implode('|', $regex).'/A';
|
||||
|
|
|
@ -24,6 +24,8 @@ interface Twig_LexerInterface
|
|||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the code is syntactically wrong
|
||||
*/
|
||||
public function tokenize($code, $filename = null);
|
||||
}
|
||||
|
|
|
@ -21,7 +21,7 @@
|
|||
*/
|
||||
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
protected $templates;
|
||||
protected $templates = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -32,10 +32,7 @@ class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
|
|||
*/
|
||||
public function __construct(array $templates)
|
||||
{
|
||||
$this->templates = array();
|
||||
foreach ($templates as $name => $template) {
|
||||
$this->templates[$name] = $template;
|
||||
}
|
||||
$this->templates = $templates;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -17,7 +17,7 @@
|
|||
class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
private $hasSourceCache = array();
|
||||
protected $loaders;
|
||||
protected $loaders = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
|
@ -26,7 +26,6 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
|
|||
*/
|
||||
public function __construct(array $loaders = array())
|
||||
{
|
||||
$this->loaders = array();
|
||||
foreach ($loaders as $loader) {
|
||||
$this->addLoader($loader);
|
||||
}
|
||||
|
@ -76,8 +75,12 @@ class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterf
|
|||
}
|
||||
|
||||
foreach ($this->loaders as $loader) {
|
||||
if ($loader instanceof Twig_ExistsLoaderInterface && $loader->exists($name)) {
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
if ($loader instanceof Twig_ExistsLoaderInterface) {
|
||||
if ($loader->exists($name)) {
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
}
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
|
|
|
@ -16,17 +16,22 @@
|
|||
*/
|
||||
class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
protected $paths;
|
||||
protected $cache;
|
||||
/** Identifier of the main namespace. */
|
||||
const MAIN_NAMESPACE = '__main__';
|
||||
|
||||
protected $paths = array();
|
||||
protected $cache = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
*/
|
||||
public function __construct($paths)
|
||||
public function __construct($paths = array())
|
||||
{
|
||||
$this->setPaths($paths);
|
||||
if ($paths) {
|
||||
$this->setPaths($paths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -36,7 +41,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
|||
*
|
||||
* @return array The array of paths where to look for templates
|
||||
*/
|
||||
public function getPaths($namespace = '__main__')
|
||||
public function getPaths($namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
|
||||
}
|
||||
|
@ -44,7 +49,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
|||
/**
|
||||
* Returns the path namespaces.
|
||||
*
|
||||
* The "__main__" namespace is always defined.
|
||||
* The main namespace is always defined.
|
||||
*
|
||||
* @return array The array of defined namespaces
|
||||
*/
|
||||
|
@ -59,7 +64,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
|||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
* @param string $namespace A path namespace
|
||||
*/
|
||||
public function setPaths($paths, $namespace = '__main__')
|
||||
public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
if (!is_array($paths)) {
|
||||
$paths = array($paths);
|
||||
|
@ -79,7 +84,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
|||
*
|
||||
* @throws Twig_Error_Loader
|
||||
*/
|
||||
public function addPath($path, $namespace = '__main__')
|
||||
public function addPath($path, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = array();
|
||||
|
@ -99,7 +104,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
|||
*
|
||||
* @throws Twig_Error_Loader
|
||||
*/
|
||||
public function prependPath($path, $namespace = '__main__')
|
||||
public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = array();
|
||||
|
@ -173,15 +178,15 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
|||
|
||||
$this->validateName($name);
|
||||
|
||||
$namespace = '__main__';
|
||||
$namespace = self::MAIN_NAMESPACE;
|
||||
$shortname = $name;
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
|
||||
}
|
||||
|
||||
$namespace = substr($name, 1, $pos - 1);
|
||||
|
||||
$name = substr($name, $pos + 1);
|
||||
$shortname = substr($name, $pos + 1);
|
||||
}
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
|
@ -189,8 +194,8 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderI
|
|||
}
|
||||
|
||||
foreach ($this->paths[$namespace] as $path) {
|
||||
if (is_file($path.'/'.$name)) {
|
||||
return $this->cache[$name] = $path.'/'.$name;
|
||||
if (is_file($path.'/'.$shortname)) {
|
||||
return $this->cache[$name] = $path.'/'.$shortname;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,30 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2013 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_EndsWith extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(0 === substr_compare(')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(', -strlen(')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(')))')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2013 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Matches extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('preg_match(')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('');
|
||||
}
|
||||
}
|
|
@ -0,0 +1,28 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2013 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_StartsWith extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(0 === strpos(')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw('))')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
{
|
||||
return $compiler->raw('');
|
||||
}
|
||||
}
|
|
@ -98,7 +98,10 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
|||
if (!is_int($name)) {
|
||||
$named = true;
|
||||
$name = $this->normalizeName($name);
|
||||
} elseif ($named) {
|
||||
throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
$parameters[$name] = $node;
|
||||
}
|
||||
|
||||
|
@ -142,6 +145,10 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
|||
$name = $this->normalizeName($param->name);
|
||||
|
||||
if (array_key_exists($name, $parameters)) {
|
||||
if (array_key_exists($pos, $parameters)) {
|
||||
throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
$arguments[] = $parameters[$name];
|
||||
unset($parameters[$name]);
|
||||
} elseif (array_key_exists($pos, $parameters)) {
|
||||
|
@ -157,8 +164,8 @@ abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
|||
}
|
||||
}
|
||||
|
||||
foreach (array_keys($parameters) as $name) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unknown argument "%s" for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
if (!empty($parameters)) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unknown argument%s "%s" for %s "%s".', count($parameters) > 1 ? 's' : '' , implode('", "', array_keys($parameters)), $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
|
|
|
@ -32,10 +32,10 @@ class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
|
|||
|
||||
$compiler->raw(', ')->subcompile($this->getNode('attribute'));
|
||||
|
||||
if (count($this->getNode('arguments')) || Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
if (count($this->getNode('arguments')) || Twig_Template::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
|
||||
|
||||
if (Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
if (Twig_Template::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('type'));
|
||||
}
|
||||
|
||||
|
|
|
@ -28,6 +28,17 @@ class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test
|
|||
->raw('(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' === constant(')
|
||||
;
|
||||
|
||||
if ($this->getNode('arguments')->hasNode(1)) {
|
||||
$compiler
|
||||
->raw('get_class(')
|
||||
->subcompile($this->getNode('arguments')->getNode(1))
|
||||
->raw(')."::".')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('arguments')->getNode(0))
|
||||
->raw('))')
|
||||
;
|
||||
|
|
|
@ -34,7 +34,7 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
|
|||
|
||||
$this->changeIgnoreStrictCheck($node);
|
||||
} else {
|
||||
throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine(), $compiler->getFilename());
|
||||
throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -107,6 +107,6 @@ class Twig_Node_For extends Twig_Node
|
|||
$compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
|
||||
|
||||
// keep the values set in the inner context for variables defined in the outer context
|
||||
$compiler->write("\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent));\n");
|
||||
$compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n");
|
||||
}
|
||||
}
|
||||
|
|
|
@ -30,7 +30,7 @@ class Twig_Node_If extends Twig_Node
|
|||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
for ($i = 0; $i < count($this->getNode('tests')); $i += 2) {
|
||||
for ($i = 0, $count = count($this->getNode('tests')); $i < $count; $i += 2) {
|
||||
if ($i > 0) {
|
||||
$compiler
|
||||
->outdent()
|
||||
|
|
|
@ -12,7 +12,7 @@
|
|||
/**
|
||||
* Twig_NodeTraverser is a node traverser.
|
||||
*
|
||||
* It visits all nodes and their children and call the given visitor for each.
|
||||
* It visits all nodes and their children and calls the given visitor for each.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
|
@ -24,8 +24,8 @@ class Twig_NodeTraverser
|
|||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param array $visitors An array of Twig_NodeVisitorInterface instances
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param Twig_NodeVisitorInterface[] $visitors An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function __construct(Twig_Environment $env, array $visitors = array())
|
||||
{
|
||||
|
|
|
@ -39,7 +39,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
|||
*/
|
||||
public function __construct($optimizers = -1)
|
||||
{
|
||||
if (!is_int($optimizers) || $optimizers > 2) {
|
||||
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));
|
||||
}
|
||||
|
||||
|
@ -108,7 +108,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
|||
return $node;
|
||||
}
|
||||
|
||||
protected function optimizeVariables($node, $env)
|
||||
protected function optimizeVariables(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
|
||||
$this->prependedNodes[0][] = $node->getAttribute('name');
|
||||
|
@ -129,7 +129,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
|||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function optimizePrintNode($node, $env)
|
||||
protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if (!$node instanceof Twig_Node_Print) {
|
||||
return $node;
|
||||
|
@ -153,7 +153,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
|||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function optimizeRawFilter($node, $env)
|
||||
protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
|
||||
return $node->getNode('node');
|
||||
|
@ -168,7 +168,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
|||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function enterOptimizeFor($node, $env)
|
||||
protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_For) {
|
||||
// disable the loop variable by default
|
||||
|
@ -217,7 +217,7 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
|||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function leaveOptimizeFor($node, $env)
|
||||
protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_For) {
|
||||
array_shift($this->loops);
|
||||
|
|
|
@ -13,15 +13,21 @@ class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
|
|||
public function getSafe(Twig_NodeInterface $node)
|
||||
{
|
||||
$hash = spl_object_hash($node);
|
||||
if (isset($this->data[$hash])) {
|
||||
foreach ($this->data[$hash] as $bucket) {
|
||||
if ($bucket['key'] === $node) {
|
||||
return $bucket['value'];
|
||||
}
|
||||
}
|
||||
if (!isset($this->data[$hash])) {
|
||||
return;
|
||||
}
|
||||
|
||||
return null;
|
||||
foreach ($this->data[$hash] as $bucket) {
|
||||
if ($bucket['key'] !== $node) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (in_array('html_attr', $bucket['value'])) {
|
||||
$bucket['value'][] = 'html';
|
||||
}
|
||||
|
||||
return $bucket['value'];
|
||||
}
|
||||
}
|
||||
|
||||
protected function setSafe(Twig_NodeInterface $node, array $safe)
|
||||
|
|
|
@ -49,7 +49,7 @@ class Twig_Parser implements Twig_ParserInterface
|
|||
|
||||
public function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s', hash('sha1', uniqid(mt_rand(), true), false));
|
||||
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
|
@ -58,11 +58,7 @@ class Twig_Parser implements Twig_ParserInterface
|
|||
}
|
||||
|
||||
/**
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
|
||||
{
|
||||
|
@ -246,7 +242,7 @@ class Twig_Parser implements Twig_ParserInterface
|
|||
return $this->blocks[$name];
|
||||
}
|
||||
|
||||
public function setBlock($name, $value)
|
||||
public function setBlock($name, Twig_Node_Block $value)
|
||||
{
|
||||
$this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine());
|
||||
}
|
||||
|
@ -384,7 +380,7 @@ class Twig_Parser implements Twig_ParserInterface
|
|||
}
|
||||
|
||||
foreach ($node as $k => $n) {
|
||||
if (null !== $n && null === $n = $this->filterBodyNodes($n)) {
|
||||
if (null !== $n && null === $this->filterBodyNodes($n)) {
|
||||
$node->removeNode($k);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,8 @@ interface Twig_ParserInterface
|
|||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
*
|
||||
* @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
|
||||
*/
|
||||
public function parse(Twig_TokenStream $stream);
|
||||
}
|
||||
|
|
|
@ -80,8 +80,6 @@ class Twig_SimpleFilter
|
|||
if (null !== $this->options['is_safe_callback']) {
|
||||
return call_user_func($this->options['is_safe_callback'], $filterArgs);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getPreservesSafety()
|
||||
|
|
|
@ -127,12 +127,24 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
{
|
||||
$name = (string) $name;
|
||||
|
||||
$template = null;
|
||||
if (isset($blocks[$name])) {
|
||||
$b = $blocks;
|
||||
unset($b[$name]);
|
||||
call_user_func($blocks[$name], $context, $b);
|
||||
$template = $blocks[$name][0];
|
||||
$block = $blocks[$name][1];
|
||||
unset($blocks[$name]);
|
||||
} elseif (isset($this->blocks[$name])) {
|
||||
call_user_func($this->blocks[$name], $context, $blocks);
|
||||
$template = $this->blocks[$name][0];
|
||||
$block = $this->blocks[$name][1];
|
||||
}
|
||||
|
||||
if (null !== $template) {
|
||||
try {
|
||||
$template->$block($context, $blocks);
|
||||
} catch (Twig_Error $e) {
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getTemplateName(), $e);
|
||||
}
|
||||
} elseif (false !== $parent = $this->getParent($context)) {
|
||||
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks));
|
||||
}
|
||||
|
@ -276,7 +288,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
|
||||
throw $e;
|
||||
} catch (Exception $e) {
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e);
|
||||
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getTemplateName(), $e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -326,7 +338,7 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
* @param mixed $object The object or array from where to get the item
|
||||
* @param mixed $item The item to get from the array or object
|
||||
* @param array $arguments An array of arguments to pass if the item is an object method
|
||||
* @param string $type The type of attribute (@see Twig_TemplateInterface)
|
||||
* @param string $type The type of attribute (@see Twig_Template constants)
|
||||
* @param Boolean $isDefinedTest Whether this is only a defined check
|
||||
* @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not
|
||||
*
|
||||
|
@ -334,23 +346,23 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
*
|
||||
* @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
|
||||
*/
|
||||
protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
|
||||
protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
|
||||
{
|
||||
$item = ctype_digit((string) $item) ? (int) $item : (string) $item;
|
||||
|
||||
// array
|
||||
if (Twig_TemplateInterface::METHOD_CALL !== $type) {
|
||||
if ((is_array($object) && array_key_exists($item, $object))
|
||||
|| ($object instanceof ArrayAccess && isset($object[$item]))
|
||||
if (Twig_Template::METHOD_CALL !== $type) {
|
||||
$arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
|
||||
|
||||
if ((is_array($object) && array_key_exists($arrayItem, $object))
|
||||
|| ($object instanceof ArrayAccess && isset($object[$arrayItem]))
|
||||
) {
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return $object[$item];
|
||||
return $object[$arrayItem];
|
||||
}
|
||||
|
||||
if (Twig_TemplateInterface::ARRAY_CALL === $type) {
|
||||
if (Twig_Template::ARRAY_CALL === $type || !is_object($object)) {
|
||||
if ($isDefinedTest) {
|
||||
return false;
|
||||
}
|
||||
|
@ -360,11 +372,13 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
}
|
||||
|
||||
if (is_object($object)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
|
||||
} elseif (is_array($object)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $item, implode(', ', array_keys($object))), -1, $this->getTemplateName());
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
|
||||
} elseif (Twig_Template::ARRAY_CALL === $type) {
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
} else {
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a "%s" variable', $item, gettype($object)), -1, $this->getTemplateName());
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -378,14 +392,14 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
return null;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist', $item, is_array($object) ? 'Array' : $object), -1, $this->getTemplateName());
|
||||
throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
|
||||
}
|
||||
|
||||
$class = get_class($object);
|
||||
|
||||
// object property
|
||||
if (Twig_TemplateInterface::METHOD_CALL !== $type) {
|
||||
if (isset($object->$item) || array_key_exists($item, $object)) {
|
||||
if (Twig_Template::METHOD_CALL !== $type) {
|
||||
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
|
||||
if ($isDefinedTest) {
|
||||
return true;
|
||||
}
|
||||
|
@ -403,15 +417,17 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
|
||||
}
|
||||
|
||||
$call = false;
|
||||
$lcItem = strtolower($item);
|
||||
if (isset(self::$cache[$class]['methods'][$lcItem])) {
|
||||
$method = $item;
|
||||
$method = (string) $item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
|
||||
$method = 'get'.$item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
|
||||
$method = 'is'.$item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['__call'])) {
|
||||
$method = $item;
|
||||
$method = (string) $item;
|
||||
$call = true;
|
||||
} else {
|
||||
if ($isDefinedTest) {
|
||||
return false;
|
||||
|
@ -432,7 +448,16 @@ abstract class Twig_Template implements Twig_TemplateInterface
|
|||
$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
|
||||
}
|
||||
|
||||
$ret = call_user_func_array(array($object, $method), $arguments);
|
||||
// Some objects throw exceptions when they have __call, and the method we try
|
||||
// to call is not supported. If ignoreStrictCheck is true, we should return null.
|
||||
try {
|
||||
$ret = call_user_func_array(array($object, $method), $arguments);
|
||||
} catch (BadMethodCallException $e) {
|
||||
if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
|
||||
return null;
|
||||
}
|
||||
throw $e;
|
||||
}
|
||||
|
||||
// useful when calling a template method from a template
|
||||
// this is not supported but unfortunately heavily used in the Symfony profiler
|
||||
|
|
|
@ -121,11 +121,10 @@ class Twig_Token
|
|||
*
|
||||
* @param integer $type The type as an integer
|
||||
* @param Boolean $short Whether to return a short representation or not
|
||||
* @param integer $line The code line
|
||||
*
|
||||
* @return string The string representation
|
||||
*/
|
||||
public static function typeToString($type, $short = false, $line = -1)
|
||||
public static function typeToString($type, $short = false)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::EOF_TYPE:
|
||||
|
@ -178,11 +177,10 @@ class Twig_Token
|
|||
* Returns the english representation of a given type.
|
||||
*
|
||||
* @param integer $type The type as an integer
|
||||
* @param integer $line The code line
|
||||
*
|
||||
* @return string The string representation
|
||||
*/
|
||||
public static function typeToEnglish($type, $line = -1)
|
||||
public static function typeToEnglish($type)
|
||||
{
|
||||
switch ($type) {
|
||||
case self::EOF_TYPE:
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue