diff --git a/CHANGELOG b/CHANGELOG index 1a0807c9f..4d2c72fc3 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,6 +1,6 @@ * 1.28.0 (2016-XX-XX) - * added "is defined" support for block() + * added "is defined" support for block() and constant() * optimized the way attributes are fetched * 1.27.0 (2016-10-25) diff --git a/doc/functions/constant.rst b/doc/functions/constant.rst index bea0e9fc0..9c1ed23fd 100644 --- a/doc/functions/constant.rst +++ b/doc/functions/constant.rst @@ -16,3 +16,11 @@ As of 1.12.1 you can read constants from object instances as well: .. code-block:: jinja {{ constant('RSS', date) }} + +Use the ``defined`` test to check if a constant is defined: + +.. code-block:: jinja + + {% if constant('SOME_CONST') is defined %} + ... + {% endif %} diff --git a/lib/Twig/Extension/Core.php b/lib/Twig/Extension/Core.php index 901a3058f..9ddb627cc 100644 --- a/lib/Twig/Extension/Core.php +++ b/lib/Twig/Extension/Core.php @@ -1452,6 +1452,23 @@ function twig_constant($constant, $object = null) return constant($constant); } +/** + * Checks if a constant exists. + * + * @param string $constant The name of the constant + * @param null|object $object The object to get the constant from + * + * @return bool + */ +function twig_constant_is_defined($constant, $object = null) +{ + if (null !== $object) { + $constant = get_class($object).'::'.$constant; + } + + return defined($constant); +} + /** * Batches item. * diff --git a/lib/Twig/Node/Expression/Function.php b/lib/Twig/Node/Expression/Function.php index 7326ede26..207f6552b 100644 --- a/lib/Twig/Node/Expression/Function.php +++ b/lib/Twig/Node/Expression/Function.php @@ -12,7 +12,7 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call { public function __construct($name, Twig_NodeInterface $arguments, $lineno) { - parent::__construct(array('arguments' => $arguments), array('name' => $name), $lineno); + parent::__construct(array('arguments' => $arguments), array('name' => $name, 'is_defined_test' => false), $lineno); } public function compile(Twig_Compiler $compiler) @@ -27,7 +27,12 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression_Call $this->setAttribute('needs_context', $function->needsContext()); $this->setAttribute('arguments', $function->getArguments()); if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) { - $this->setAttribute('callable', $function->getCallable()); + $callable = $function->getCallable(); + if ('constant' === $name && $this->getAttribute('is_defined_test')) { + $callable = 'twig_constant_is_defined'; + } + + $this->setAttribute('callable', $callable); } if ($function instanceof Twig_SimpleFunction) { $this->setAttribute('is_variadic', $function->isVariadic()); diff --git a/lib/Twig/Node/Expression/Test/Defined.php b/lib/Twig/Node/Expression/Test/Defined.php index a8fd74db9..9d2d7ca72 100644 --- a/lib/Twig/Node/Expression/Test/Defined.php +++ b/lib/Twig/Node/Expression/Test/Defined.php @@ -32,6 +32,8 @@ class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test $this->changeIgnoreStrictCheck($node); } elseif ($node instanceof Twig_Node_Expression_BlockReference) { $node->setAttribute('is_defined_test', true); + } elseif ($node instanceof Twig_Node_Expression_Function && 'constant' === $node->getAttribute('name')) { + $node->setAttribute('is_defined_test', true); } elseif ($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array) { $node = new Twig_Node_Expression_Constant(true, $node->getTemplateLine()); } else { diff --git a/test/Twig/Tests/Fixtures/tests/defined_for_constants.test b/test/Twig/Tests/Fixtures/tests/defined_for_constants.test new file mode 100644 index 000000000..2fa9929d3 --- /dev/null +++ b/test/Twig/Tests/Fixtures/tests/defined_for_constants.test @@ -0,0 +1,14 @@ +--TEST-- +"defined" support for constants +--TEMPLATE-- +{{ constant('DATE_W3C') is defined ? 'ok' : 'ko' }} +{{ constant('ARRAY_AS_PROPS', object) is defined ? 'ok' : 'ko' }} +{{ constant('FOOBAR') is not defined ? 'ok' : 'ko' }} +{{ constant('FOOBAR', object) is not defined ? 'ok' : 'ko' }} +--DATA-- +return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi'))); +--EXPECT-- +ok +ok +ok +ok