mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-13 10:56:38 +00:00
Handle functions like filters
Functions are not global variables anymore. They are resolved at compile time, and Twig_Function objects are instanciated only when compiling.
This commit is contained in:
committed by
Fabien Potencier
parent
b247452ec5
commit
c225a5bed9
@@ -27,6 +27,7 @@ class Twig_Environment
|
||||
protected $visitors;
|
||||
protected $filters;
|
||||
protected $tests;
|
||||
protected $functions;
|
||||
protected $globals;
|
||||
protected $runtimeInitialized;
|
||||
protected $loadedTemplates;
|
||||
@@ -455,6 +456,43 @@ class Twig_Environment
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
public function addFunction($name, Twig_Function $function)
|
||||
{
|
||||
if (null === $this->functions) {
|
||||
$this->loadFunctions();
|
||||
}
|
||||
$this->functions[$name] = $function;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get a function by name
|
||||
*
|
||||
* Subclasses may override getFunction($name) and load functions differently;
|
||||
* so no list of functions is available.
|
||||
*
|
||||
* @param string $name function name
|
||||
* @return Twig_Function|null A Twig_Function instance or null if the function does not exists
|
||||
*/
|
||||
public function getFunction($name)
|
||||
{
|
||||
if (null === $this->functions) {
|
||||
$this->loadFunctions();
|
||||
}
|
||||
|
||||
if (isset($this->functions[$name])) {
|
||||
return $this->functions[$name];
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function loadFunctions() {
|
||||
$this->functions = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->functions = array_merge($this->functions, $extension->getFunctions());
|
||||
}
|
||||
}
|
||||
|
||||
public function addGlobal($name, $value)
|
||||
{
|
||||
if (null === $this->globals) {
|
||||
|
||||
@@ -60,6 +60,16 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return array An array of functions
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of operators to add to the existing list.
|
||||
|
||||
@@ -87,12 +87,12 @@ class Twig_Extension_Core extends Twig_Extension
|
||||
*
|
||||
* @return array An array of global functions
|
||||
*/
|
||||
public function getGlobals()
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
'fn_range' => new Twig_Function($this, 'getRange'),
|
||||
'fn_constant' => new Twig_Function($this, 'getConstant'),
|
||||
'fn_cycle' => new Twig_Function($this, 'getCycle'),
|
||||
'range' => new Twig_Function_Method($this, 'getRange'),
|
||||
'constant' => new Twig_Function_Method($this, 'getConstant'),
|
||||
'cycle' => new Twig_Function_Method($this, 'getCycle'),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -54,6 +54,13 @@ interface Twig_ExtensionInterface
|
||||
*/
|
||||
public function getTests();
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return array An array of functions
|
||||
*/
|
||||
public function getFunctions();
|
||||
|
||||
/**
|
||||
* Returns a list of operators to add to the existing list.
|
||||
*
|
||||
|
||||
+19
-11
@@ -10,29 +10,37 @@
|
||||
*/
|
||||
|
||||
/**
|
||||
* Defines a new Twig function.
|
||||
* Represents a template function.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Function extends Exception
|
||||
abstract class Twig_Function implements Twig_FunctionInterface
|
||||
{
|
||||
protected $object;
|
||||
protected $method;
|
||||
protected $options;
|
||||
|
||||
public function __construct($object, $method)
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->object = $object;
|
||||
$this->method = $method;
|
||||
$this->options = array_merge(array(
|
||||
'needs_environment' => false,
|
||||
), $options);
|
||||
}
|
||||
|
||||
public function getObject()
|
||||
public function needsEnvironment()
|
||||
{
|
||||
return $this->object;
|
||||
return $this->options['needs_environment'];
|
||||
}
|
||||
|
||||
public function getMethod()
|
||||
public function getSafe(Twig_Node $functionArgs)
|
||||
{
|
||||
return $this->method;
|
||||
if (isset($this->options['is_safe'])) {
|
||||
return $this->options['is_safe'];
|
||||
}
|
||||
|
||||
if (isset($this->options['is_safe_callback'])) {
|
||||
return call_user_func($this->options['is_safe_callback'], $functionArgs);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,34 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2010 Arnaud Le Blanc
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a function template function.
|
||||
*
|
||||
* @package twig
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
*/
|
||||
class Twig_Function_Function extends Twig_Function
|
||||
{
|
||||
protected $function;
|
||||
|
||||
public function __construct($function, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->function = $function;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return $this->function;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2010 Arnaud Le Blanc
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a method template function.
|
||||
*
|
||||
* @package twig
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
*/
|
||||
class Twig_Function_Method extends Twig_Filter
|
||||
{
|
||||
protected $extension, $method;
|
||||
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->extension = $extension;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
* (c) 2010 Arnaud Le Blanc
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template function.
|
||||
*
|
||||
* @package twig
|
||||
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
|
||||
*/
|
||||
interface Twig_FunctionInterface
|
||||
{
|
||||
public function compile();
|
||||
public function needsEnvironment();
|
||||
public function getSafe(Twig_Node $filterArgs);
|
||||
}
|
||||
@@ -17,22 +17,23 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
// functions must be prefixed with fn_
|
||||
$this->getNode('name')->setAttribute('name', 'fn_'.$this->getNode('name')->getAttribute('name'));
|
||||
|
||||
$compiler
|
||||
->raw('$this->callFunction($context, ')
|
||||
->subcompile($this->getNode('name'))
|
||||
->raw(', array(')
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $node) {
|
||||
$compiler
|
||||
->subcompile($node)
|
||||
->raw(', ')
|
||||
;
|
||||
$function = $compiler->getEnvironment()->getFunction($this->getNode('name')->getAttribute('name'));
|
||||
if (!$function) {
|
||||
throw new Twig_Error_Syntax(sprintf('The function "%s" does not exist', $this->getNode('name')->getAttribute('name')), $this->getLine());
|
||||
}
|
||||
|
||||
$compiler->raw('))');
|
||||
$compiler->raw($function->compile().($function->needsEnvironment() ? '($this->env, ' : '('));
|
||||
|
||||
$first = true;
|
||||
foreach ($this->getNode('arguments') as $node) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
} else {
|
||||
$first = false;
|
||||
}
|
||||
$compiler->subcompile($node);
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
--TEST--
|
||||
Twig supports calling variables
|
||||
--TEMPLATE--
|
||||
{{ lower('FOO') }}
|
||||
{{ lower1('FOO') }}
|
||||
--DATA--
|
||||
return array(
|
||||
'foo' => new Foo(),
|
||||
'fn_lower' => new Twig_Function('foo', 'strToLower'),
|
||||
'fn_lower1' => new Twig_Function(new Foo(), 'strToLower'))
|
||||
--EXPECT--
|
||||
foo
|
||||
foo
|
||||
@@ -1,25 +0,0 @@
|
||||
--TEST--
|
||||
global variables
|
||||
--TEMPLATE--
|
||||
{{ foo() }}
|
||||
{{ bar() }}
|
||||
{% include "included.twig" %}
|
||||
{% from "included.twig" import foobar %}
|
||||
{{ foobar() }}
|
||||
--TEMPLATE(included.twig)--
|
||||
{% macro foobar() %}
|
||||
{{ foo() }}
|
||||
|
||||
{% endmacro %}
|
||||
{{ foo() }}
|
||||
|
||||
--DATA--
|
||||
$twig->addGlobal('fn_foo', new Twig_Function(new Foo(), 'getFoo'));
|
||||
$twig->addGlobal('fn_bar', new Twig_Function('barObj', 'getFoo'));
|
||||
return array('barObj' => new Foo());
|
||||
--EXPECT--
|
||||
foo
|
||||
foo
|
||||
|
||||
foo
|
||||
foo
|
||||
@@ -0,0 +1,14 @@
|
||||
--TEST--
|
||||
global variables
|
||||
--TEMPLATE--
|
||||
{% include "included.twig" %}
|
||||
{% from "included.twig" import foobar %}
|
||||
{{ foobar() }}
|
||||
--TEMPLATE(included.twig)--
|
||||
{% macro foobar() %}
|
||||
called foobar
|
||||
{% endmacro %}
|
||||
--DATA--
|
||||
return array();
|
||||
--EXPECT--
|
||||
called foobar
|
||||
@@ -10,7 +10,7 @@
|
||||
--DATA--
|
||||
return array('foo' => 'bar')
|
||||
--EXPECT--
|
||||
fn_range,fn_constant,fn_cycle,foo,_parent,
|
||||
fn_range,fn_constant,fn_cycle,_parent,
|
||||
fn_range,fn_constant,fn_cycle,foo,foo1,_parent,
|
||||
fn_range,fn_constant,fn_cycle,foo1,_parent,
|
||||
foo,_parent,
|
||||
_parent,
|
||||
foo,foo1,_parent,
|
||||
foo1,_parent,
|
||||
|
||||
@@ -135,6 +135,14 @@ class TestExtension extends Twig_Extension
|
||||
);
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return array(
|
||||
'safe_br' => new Twig_Function_Method($this, 'br', array('is_safe' => array('html'))),
|
||||
'unsafe_br' => new Twig_Function_Method($this, 'br'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* nl2br which also escapes, for testing escaper filters
|
||||
*/
|
||||
@@ -158,6 +166,11 @@ class TestExtension extends Twig_Extension
|
||||
return strtoupper($value);
|
||||
}
|
||||
|
||||
public function br()
|
||||
{
|
||||
return '<br />';
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return 'test';
|
||||
|
||||
Reference in New Issue
Block a user