Skip to content

Commit

Permalink
Merge branch '1.x'
Browse files Browse the repository at this point in the history
* 1.x:
  updated CHANGELOG
  reverted unexpected change
  fixed previous merge
  Organize and show value types
  Added label for "Whitespace Control" section
  Added note about case sensitivity of logic operators
  Fixed CS
  Fixed error guessing for nested templates
  Travis no longer supports hhvm-nightly
  fixed CS
  Added an example for "and" on if statment
  Updated exception messages for null vars in Twig_Template::getAttribute().
  Use stubs for profiles. Fixes #1660

Conflicts:
	doc/api.rst
  • Loading branch information
fabpot committed Jun 6, 2015
2 parents 56e515b + 3e3aa8e commit fe67c5b
Show file tree
Hide file tree
Showing 12 changed files with 209 additions and 65 deletions.
4 changes: 0 additions & 4 deletions .travis.yml
Expand Up @@ -5,11 +5,9 @@ php:
- 5.6
- hhvm
- nightly
- hhvm-nightly

allow_failures:
- php: nightly
- php: hhvm-nightly

env:
- TWIG_EXT=no
Expand All @@ -26,7 +24,5 @@ matrix:
exclude:
- php: hhvm
env: TWIG_EXT=yes
- php: hhvm-nightly
env: TWIG_EXT=yes
- php: nightly
env: TWIG_EXT=yes
1 change: 1 addition & 0 deletions CHANGELOG
Expand Up @@ -10,6 +10,7 @@

* 1.18.2 (2015-XX-XX)

* fixed template/line guessing in exceptions for nested templates
* optimized the number of inodes and the size of realpath cache when using the cache

* 1.18.1 (2015-04-19)
Expand Down
34 changes: 25 additions & 9 deletions doc/api.rst
Expand Up @@ -71,29 +71,43 @@ options as the constructor second argument::

The following options are available:

* ``debug``: When set to ``true``, the generated templates have a
* ``debug`` *boolean*

When set to ``true``, the generated templates have a
``__toString()`` method that you can use to display the generated nodes
(default to ``false``).

* ``charset``: The charset used by the templates (default to ``utf-8``).
* ``charset`` *string (default to ``utf-8``)*

The charset used by the templates.

* ``base_template_class`` *string (default to ``Twig_Template``)*

The base template class to use for generated
templates.

* ``base_template_class``: The base template class to use for generated
templates (default to ``Twig_Template``).
* ``cache`` *string|false*

* ``cache``: An absolute path where to store the compiled templates, or
An absolute path where to store the compiled templates, or
``false`` to disable caching (which is the default).

* ``auto_reload``: When developing with Twig, it's useful to recompile the
* ``auto_reload`` *boolean*

When developing with Twig, it's useful to recompile the
template whenever the source code changes. If you don't provide a value for
the ``auto_reload`` option, it will be determined automatically based on the
``debug`` value.

* ``strict_variables``: If set to ``false``, Twig will silently ignore invalid
* ``strict_variables`` *boolean*

If set to ``false``, Twig will silently ignore invalid
variables (variables and or attributes/methods that do not exist) and
replace them with a ``null`` value. When set to ``true``, Twig throws an
exception instead (default to ``false``).

* ``autoescape``: Sets the default auto-escaping strategy (``filename``,
* ``autoescape`` *string*

Sets the default auto-escaping strategy (``filename``,
``html``, ``js``, ``css``, ``url``, ``html_attr``, or a PHP callback that
takes the template "filename" and returns the escaping strategy to use -- the
callback cannot be a function name to avoid collision with built-in escaping
Expand All @@ -102,7 +116,9 @@ The following options are available:
based on the template filename extension (this strategy does not incur any
overhead at runtime as auto-escaping is done at compilation time.)

* ``optimizations``: A flag that indicates which optimizations to apply
* ``optimizations`` *integer*

A flag that indicates which optimizations to apply
(default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
disable).

Expand Down
12 changes: 10 additions & 2 deletions doc/tags/if.rst
Expand Up @@ -37,8 +37,16 @@ You can also use ``not`` to check for values that evaluate to ``false``:
<p>You are not subscribed to our mailing list.</p>
{% endif %}
For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can use
more complex ``expressions`` there too:
For multiple conditions, ``and`` and ``or`` can be used:

.. code-block:: jinja
{% if temperature > 18 and temperature < 27 %}
<p>It's a nice day for a walk in the park.</p>
{% endif %}
For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can
use more complex ``expressions`` there too:

.. code-block:: jinja
Expand Down
2 changes: 1 addition & 1 deletion doc/tags/spaceless.rst
Expand Up @@ -33,5 +33,5 @@ quirks under some circumstances.
.. tip::

For more information on whitespace control, read the
:doc:`dedicated<../templates>` section of the documentation and learn how
:ref:`dedicated section <templates-whitespace-control>` of the documentation and learn how
you can also use the whitespace control modifier on your tags.
6 changes: 6 additions & 0 deletions doc/templates.rst
Expand Up @@ -660,6 +660,10 @@ You can combine multiple expressions with the following operators:

Twig also support bitwise operators (``b-and``, ``b-xor``, and ``b-or``).

.. note::

Operators are case sensitive.

Comparisons
~~~~~~~~~~~

Expand Down Expand Up @@ -784,6 +788,8 @@ inserted into the string:
{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}
.. _templates-whitespace-control:

Whitespace Control
------------------

Expand Down
52 changes: 40 additions & 12 deletions ext/twig/twig.c
Expand Up @@ -779,12 +779,18 @@ PHP_FUNCTION(twig_template_get_attributes)
$message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
} elseif (is_array($object)) {
if (empty($object)) {
$message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
$message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
} else {
$message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
$message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
}
} elseif (Twig_Template::ARRAY_CALL === $type) {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
if (null === $object) {
$message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
} elseif (null === $object) {
$message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
Expand All @@ -807,12 +813,21 @@ PHP_FUNCTION(twig_template_get_attributes)
} else {
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));
if (Z_TYPE_P(object) == IS_NULL) {
convert_to_string(object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC,
(strcmp("array", type) == 0)
? "Impossible to access a key (\"%s\") on a %s variable"
: "Impossible to access an attribute (\"%s\") on a %s variable",
item, type_name);
} else {
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);
}
efree(item);
Expand All @@ -836,7 +851,14 @@ PHP_FUNCTION(twig_template_get_attributes)
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return null;
}
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 (null === $object) {
$message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
}
*/
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
Expand All @@ -846,9 +868,15 @@ PHP_FUNCTION(twig_template_get_attributes)

type_name = zend_zval_type_name(object);
Z_ADDREF_P(object);
convert_to_string_ex(&object);
if (Z_TYPE_P(object) == IS_NULL) {
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));
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable", item, type_name);
} else {
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);
efree(item);
Expand Down
2 changes: 1 addition & 1 deletion lib/Twig/Extension/Core.php
Expand Up @@ -1362,7 +1362,7 @@ function twig_test_iterable($value)
*
* @return string The rendered template
*/
function twig_include(Twig_Environment $env, array $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
{
$alreadySandboxed = false;
$sandbox = null;
Expand Down
39 changes: 29 additions & 10 deletions lib/Twig/Template.php
Expand Up @@ -79,7 +79,7 @@ public function getParent(array $context)
return false;
}

if ($parent instanceof Twig_Template) {
if ($parent instanceof self) {
return $this->parents[$parent->getTemplateName()] = $parent;
}

Expand Down Expand Up @@ -249,13 +249,20 @@ protected function loadTemplate($template, $templateName = null, $line = null, $
return $this->env->resolveTemplate($template);
}

if ($template instanceof Twig_Template) {
if ($template instanceof self) {
return $template;
}

return $this->env->loadTemplate($template, $index);
} catch (Twig_Error $e) {
$e->setTemplateFile($templateName ? $templateName : $this->getTemplateName());
if (!$e->getTemplateFile()) {
$e->setTemplateFile($templateName ? $templateName : $this->getTemplateName());
}

if ($e->getTemplateLine()) {
throw $e;
}

if (!$line) {
$e->guess();
} else {
Expand Down Expand Up @@ -385,10 +392,10 @@ final protected function getContext($context, $item, $ignoreStrictCheck = false)
*
* @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_Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
{
// array
if (Twig_Template::METHOD_CALL !== $type) {
if (self::METHOD_CALL !== $type) {
$arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;

if ((is_array($object) && array_key_exists($arrayItem, $object))
Expand All @@ -401,7 +408,7 @@ protected function getAttribute($object, $item, array $arguments = array(), $typ
return $object[$arrayItem];
}

if (Twig_Template::ARRAY_CALL === $type || !is_object($object)) {
if (self::ARRAY_CALL === $type || !is_object($object)) {
if ($isDefinedTest) {
return false;
}
Expand All @@ -420,8 +427,14 @@ protected function getAttribute($object, $item, array $arguments = array(), $typ
} else {
$message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
}
} elseif (Twig_Template::ARRAY_CALL === $type) {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
} elseif (self::ARRAY_CALL === $type) {
if (null === $object) {
$message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
} elseif (null === $object) {
$message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
Expand All @@ -439,11 +452,17 @@ protected function getAttribute($object, $item, array $arguments = array(), $typ
return;
}

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 (null === $object) {
$message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}

throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
}

// object property
if (Twig_Template::METHOD_CALL !== $type) {
if (self::METHOD_CALL !== $type) {
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
if ($isDefinedTest) {
return true;
Expand Down
@@ -0,0 +1,10 @@
--TEST--
Exception for syntax error in reused template
--TEMPLATE--
{% use 'foo.twig' %}
--TEMPLATE(foo.twig)--
{% block bar %}
{% do node.data = 5 %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Unexpected token "operator" of value "=" ("end of statement block" expected) in "foo.twig" at line 3

0 comments on commit fe67c5b

Please sign in to comment.