Updating Twig to version v1.16.0 moving to vendor folder see #7320

1.9.x
Julio Montoya 11 years ago
parent ed2d3f0f7d
commit b1d261db6f
  1. 3
      composer.json
  2. 176
      main/inc/lib/symfony/Twig/Error.php
  3. 20
      main/inc/lib/symfony/Twig/Error/Loader.php
  4. 1010
      main/inc/lib/symfony/Twig/Extension/Core.php
  5. 77
      main/inc/lib/symfony/Twig/Extension/Escaper.php
  6. 38
      main/inc/lib/symfony/Twig/FilterInterface.php
  7. 100
      main/inc/lib/symfony/Twig/Loader/Chain.php
  8. 152
      main/inc/lib/symfony/Twig/Loader/Filesystem.php
  9. 61
      main/inc/lib/symfony/Twig/Node/Expression/Filter.php
  10. 66
      main/inc/lib/symfony/Twig/Node/Expression/Function.php
  11. 54
      main/inc/lib/symfony/Twig/Node/Expression/Test.php
  12. 89
      main/inc/lib/symfony/Twig/TokenParser/For.php
  13. 2
      vendor/autoload.php
  14. 11
      vendor/composer/ClassLoader.php
  15. 1
      vendor/composer/autoload_namespaces.php
  16. 11
      vendor/composer/autoload_real.php
  17. 59
      vendor/composer/installed.json
  18. 18
      vendor/twig/twig/.editorconfig
  19. 2
      vendor/twig/twig/.gitignore
  20. 21
      vendor/twig/twig/.travis.yml
  21. 684
      vendor/twig/twig/CHANGELOG
  22. 31
      vendor/twig/twig/LICENSE
  23. 15
      vendor/twig/twig/README.rst
  24. 42
      vendor/twig/twig/composer.json
  25. 30
      vendor/twig/twig/ext/twig/.gitignore
  26. 31
      vendor/twig/twig/ext/twig/LICENSE
  27. 8
      vendor/twig/twig/ext/twig/config.m4
  28. 8
      vendor/twig/twig/ext/twig/config.w32
  29. 31
      vendor/twig/twig/ext/twig/php_twig.h
  30. 1101
      vendor/twig/twig/ext/twig/twig.c
  31. 20
      vendor/twig/twig/lib/Twig/Autoloader.php
  32. 62
      vendor/twig/twig/lib/Twig/Compiler.php
  33. 11
      vendor/twig/twig/lib/Twig/CompilerInterface.php
  34. 474
      vendor/twig/twig/lib/Twig/Environment.php
  35. 248
      vendor/twig/twig/lib/Twig/Error.php
  36. 31
      vendor/twig/twig/lib/Twig/Error/Loader.php
  37. 3
      vendor/twig/twig/lib/Twig/Error/Runtime.php
  38. 3
      vendor/twig/twig/lib/Twig/Error/Syntax.php
  39. 29
      vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php
  40. 212
      vendor/twig/twig/lib/Twig/ExpressionParser.php
  41. 2
      vendor/twig/twig/lib/Twig/Extension.php
  42. 1462
      vendor/twig/twig/lib/Twig/Extension/Core.php
  43. 13
      vendor/twig/twig/lib/Twig/Extension/Debug.php
  44. 107
      vendor/twig/twig/lib/Twig/Extension/Escaper.php
  45. 0
      vendor/twig/twig/lib/Twig/Extension/Optimizer.php
  46. 4
      vendor/twig/twig/lib/Twig/Extension/Sandbox.php
  47. 113
      vendor/twig/twig/lib/Twig/Extension/Staging.php
  48. 64
      vendor/twig/twig/lib/Twig/Extension/StringLoader.php
  49. 23
      vendor/twig/twig/lib/Twig/ExtensionInterface.php
  50. 20
      vendor/twig/twig/lib/Twig/Filter.php
  51. 8
      vendor/twig/twig/lib/Twig/Filter/Function.php
  52. 11
      vendor/twig/twig/lib/Twig/Filter/Method.php
  53. 6
      vendor/twig/twig/lib/Twig/Filter/Node.php
  54. 23
      vendor/twig/twig/lib/Twig/FilterCallableInterface.php
  55. 42
      vendor/twig/twig/lib/Twig/FilterInterface.php
  56. 14
      vendor/twig/twig/lib/Twig/Function.php
  57. 8
      vendor/twig/twig/lib/Twig/Function/Function.php
  58. 11
      vendor/twig/twig/lib/Twig/Function/Method.php
  59. 8
      vendor/twig/twig/lib/Twig/Function/Node.php
  60. 23
      vendor/twig/twig/lib/Twig/FunctionCallableInterface.php
  61. 18
      vendor/twig/twig/lib/Twig/FunctionInterface.php
  62. 47
      vendor/twig/twig/lib/Twig/Lexer.php
  63. 13
      vendor/twig/twig/lib/Twig/LexerInterface.php
  64. 37
      vendor/twig/twig/lib/Twig/Loader/Array.php
  65. 138
      vendor/twig/twig/lib/Twig/Loader/Chain.php
  66. 236
      vendor/twig/twig/lib/Twig/Loader/Filesystem.php
  67. 34
      vendor/twig/twig/lib/Twig/Loader/String.php
  68. 15
      vendor/twig/twig/lib/Twig/LoaderInterface.php
  69. 3
      vendor/twig/twig/lib/Twig/Markup.php
  70. 15
      vendor/twig/twig/lib/Twig/Node.php
  71. 3
      vendor/twig/twig/lib/Twig/Node/AutoEscape.php
  72. 3
      vendor/twig/twig/lib/Twig/Node/Block.php
  73. 3
      vendor/twig/twig/lib/Twig/Node/BlockReference.php
  74. 3
      vendor/twig/twig/lib/Twig/Node/Body.php
  75. 3
      vendor/twig/twig/lib/Twig/Node/Do.php
  76. 38
      vendor/twig/twig/lib/Twig/Node/Embed.php
  77. 3
      vendor/twig/twig/lib/Twig/Node/Expression.php
  78. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Array.php
  79. 0
      vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php
  80. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary.php
  81. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php
  82. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php
  83. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php
  84. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php
  85. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php
  86. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php
  87. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php
  88. 30
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/EndsWith.php
  89. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php
  90. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php
  91. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php
  92. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php
  93. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php
  94. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php
  95. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php
  96. 28
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Matches.php
  97. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php
  98. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php
  99. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php
  100. 0
      vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php
  101. Some files were not shown because too many files have changed in this diff Show More

@ -2,6 +2,7 @@
"require": {
"php-ffmpeg/php-ffmpeg": "0.3.x-dev@dev",
"sabre/vobject": "~3.1",
"toin0u/digitalocean": "~1.4"
"toin0u/digitalocean": "~1.4",
"twig/twig": "1.*"
}
}

@ -1,176 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Twig base exception.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Error extends Exception
{
protected $lineno;
protected $filename;
protected $rawMessage;
protected $previous;
/**
* Constructor.
*
* @param string $message The error message
* @param integer $lineno The template line where the error occurred
* @param string $filename The template file name where the error occurred
* @param Exception $previous The previous exception
*/
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
{
if (-1 === $lineno || null === $filename) {
if ($trace = $this->getTemplateTrace()) {
if (-1 === $lineno) {
$lineno = $this->guessTemplateLine($trace);
}
if (null === $filename) {
$filename = $trace['object']->getTemplateName();
}
}
}
$this->lineno = $lineno;
$this->filename = $filename;
$this->rawMessage = $message;
$this->updateRepr();
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
$this->previous = $previous;
parent::__construct($this->message);
} else {
parent::__construct($this->message, 0, $previous);
}
}
/**
* Gets the raw message.
*
* @return string The raw message
*/
public function getRawMessage()
{
return $this->rawMessage;
}
/**
* Gets the filename where the error occurred.
*
* @return string The filename
*/
public function getTemplateFile()
{
return $this->filename;
}
/**
* Sets the filename where the error occurred.
*
* @param string $filename The filename
*/
public function setTemplateFile($filename)
{
$this->filename = $filename;
$this->updateRepr();
}
/**
* Gets the template line where the error occurred.
*
* @return integer The template line
*/
public function getTemplateLine()
{
return $this->lineno;
}
/**
* Sets the template line where the error occurred.
*
* @param integer $lineno The template line
*/
public function setTemplateLine($lineno)
{
$this->lineno = $lineno;
$this->updateRepr();
}
/**
* For PHP < 5.3.0, provides access to the getPrevious() method.
*
* @param string $method The method name
* @param array $arguments The parameters to be passed to the method
*
* @return Exception The previous exception or null
*/
public function __call($method, $arguments)
{
if ('getprevious' == strtolower($method)) {
return $this->previous;
}
throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
}
protected function updateRepr()
{
$this->message = $this->rawMessage;
$dot = false;
if ('.' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}
if (null !== $this->filename) {
$this->message .= sprintf(' in %s', is_string($this->filename) ? '"'.$this->filename.'"' : json_encode($this->filename));
}
if ($this->lineno >= 0) {
$this->message .= sprintf(' at line %d', $this->lineno);
}
if ($dot) {
$this->message .= '.';
}
}
protected function getTemplateTrace()
{
foreach (debug_backtrace() as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template) {
return $trace;
}
}
}
protected function guessTemplateLine($trace)
{
if (isset($trace['line'])) {
foreach ($trace['object']->getDebugInfo() as $codeLine => $templateLine) {
if ($codeLine <= $trace['line']) {
return $templateLine;
}
}
}
return -1;
}
}

@ -1,20 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2010 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Exception thrown when an error occurs during template loading.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Error_Loader extends Twig_Error
{
}

File diff suppressed because it is too large Load Diff

@ -1,77 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Twig_Extension_Escaper extends Twig_Extension
{
protected $autoescape;
public function __construct($autoescape = true)
{
$this->autoescape = $autoescape;
}
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
*/
public function getTokenParsers()
{
return array(new Twig_TokenParser_AutoEscape());
}
/**
* Returns the node visitor instances to add to the existing list.
*
* @return array An array of Twig_NodeVisitorInterface instances
*/
public function getNodeVisitors()
{
return array(new Twig_NodeVisitor_Escaper());
}
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
*/
public function getFilters()
{
return array(
'raw' => new Twig_Filter_Function('twig_raw_filter', array('is_safe' => array('all'))),
);
}
public function isGlobal()
{
return $this->autoescape;
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'escaper';
}
}
/**
* Marks a variable as being safe.
*
* @param string $string A PHP variable
*/
function twig_raw_filter($string)
{
return $string;
}

@ -1,38 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2010 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Represents a template filter.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
*/
interface Twig_FilterInterface
{
/**
* Compiles a filter.
*
* @return string The PHP code for the filter
*/
function compile();
function needsEnvironment();
function needsContext();
function getSafe(Twig_Node $filterArgs);
function getPreEscape();
function setArguments($arguments);
function getArguments();
}

@ -1,100 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2011 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Loads templates from other loaders.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Chain implements Twig_LoaderInterface
{
protected $loaders;
/**
* Constructor.
*
* @param Twig_LoaderInterface[] $loaders An array of loader instances
*/
public function __construct(array $loaders = array())
{
$this->loaders = array();
foreach ($loaders as $loader) {
$this->addLoader($loader);
}
}
/**
* Adds a loader instance.
*
* @param Twig_LoaderInterface $loader A Loader instance
*/
public function addLoader(Twig_LoaderInterface $loader)
{
$this->loaders[] = $loader;
}
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
*/
public function getSource($name)
{
foreach ($this->loaders as $loader) {
try {
return $loader->getSource($name);
} catch (Twig_Error_Loader $e) {
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
*/
public function getCacheKey($name)
{
foreach ($this->loaders as $loader) {
try {
return $loader->getCacheKey($name);
} catch (Twig_Error_Loader $e) {
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
/**
* Returns true if the template is still fresh.
*
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($name, $time)
{
foreach ($this->loaders as $loader) {
try {
return $loader->isFresh($name, $time);
} catch (Twig_Error_Loader $e) {
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
}

@ -1,152 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Loads template from the filesystem.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Filesystem implements Twig_LoaderInterface
{
protected $paths;
protected $cache;
/**
* Constructor.
*
* @param string|array $paths A path or an array of paths where to look for templates
*/
public function __construct($paths)
{
$this->setPaths($paths);
}
/**
* Returns the paths to the templates.
*
* @return array The array of paths where to look for templates
*/
public function getPaths()
{
return $this->paths;
}
/**
* Sets the paths where templates are stored.
*
* @param string|array $paths A path or an array of paths where to look for templates
*/
public function setPaths($paths)
{
if (!is_array($paths)) {
$paths = array($paths);
}
$this->paths = array();
foreach ($paths as $path) {
$this->addPath($path);
}
}
/**
* Adds a path where templates are stored.
*
* @param string $path A path where to look for templates
*/
public function addPath($path)
{
// invalidate the cache
$this->cache = array();
if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
}
$this->paths[] = $path;
}
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
*/
public function getSource($name)
{
return file_get_contents($this->findTemplate($name));
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
*/
public function getCacheKey($name)
{
return $this->findTemplate($name);
}
/**
* Returns true if the template is still fresh.
*
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
*/
public function isFresh($name, $time)
{
return filemtime($this->findTemplate($name)) < $time;
}
protected function findTemplate($name)
{
// normalize name
$name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
$this->validateName($name);
foreach ($this->paths as $path) {
if (is_file($path.'/'.$name)) {
return $this->cache[$name] = $path.'/'.$name;
}
}
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths)));
}
protected function validateName($name)
{
if (false !== strpos($name, "\0")) {
throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
}
$parts = explode('/', $name);
$level = 0;
foreach ($parts as $part) {
if ('..' === $part) {
--$level;
} elseif ('.' !== $part) {
++$level;
}
if ($level < 0) {
throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
}
}
}
}

@ -1,61 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
* (c) 2009 Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Twig_Node_Expression_Filter extends Twig_Node_Expression
{
public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
{
parent::__construct(array('node' => $node, 'filter' => $filterName, 'arguments' => $arguments), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getNode('filter')->getAttribute('value');
if (false === $filter = $compiler->getEnvironment()->getFilter($name)) {
$message = sprintf('The filter "%s" does not exist', $name);
if ($alternatives = $compiler->getEnvironment()->computeAlternatives($name, array_keys($compiler->getEnvironment()->getFilters()))) {
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
}
throw new Twig_Error_Syntax($message, $this->getLine());
}
$this->compileFilter($compiler, $filter);
}
protected function compileFilter(Twig_Compiler $compiler, Twig_FilterInterface $filter)
{
$compiler
->raw($filter->compile().'(')
->raw($filter->needsEnvironment() ? '$this->env, ' : '')
->raw($filter->needsContext() ? '$context, ' : '')
;
foreach ($filter->getArguments() as $argument) {
$compiler
->string($argument)
->raw(', ')
;
}
$compiler->subcompile($this->getNode('node'));
foreach ($this->getNode('arguments') as $node) {
$compiler
->raw(', ')
->subcompile($node)
;
}
$compiler->raw(')');
}
}

@ -1,66 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2010 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_Function extends Twig_Node_Expression
{
public function __construct($name, Twig_NodeInterface $arguments, $lineno)
{
parent::__construct(array('arguments' => $arguments), array('name' => $name), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getAttribute('name');
if (false === $function = $compiler->getEnvironment()->getFunction($name)) {
$message = sprintf('The function "%s" does not exist', $name);
if ($alternatives = $compiler->getEnvironment()->computeAlternatives($name, array_keys($compiler->getEnvironment()->getFunctions()))) {
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
}
throw new Twig_Error_Syntax($message, $this->getLine());
}
$compiler->raw($function->compile().'(');
$first = true;
if ($function->needsEnvironment()) {
$compiler->raw('$this->env');
$first = false;
}
if ($function->needsContext()) {
if (!$first) {
$compiler->raw(', ');
}
$compiler->raw('$context');
$first = false;
}
foreach ($function->getArguments() as $argument) {
if (!$first) {
$compiler->raw(', ');
}
$compiler->string($argument);
$first = false;
}
foreach ($this->getNode('arguments') as $node) {
if (!$first) {
$compiler->raw(', ');
}
$compiler->subcompile($node);
$first = false;
}
$compiler->raw(')');
}
}

@ -1,54 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2010 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_Test extends Twig_Node_Expression
{
public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
{
parent::__construct(array('node' => $node, 'arguments' => $arguments), array('name' => $name), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getAttribute('name');
$testMap = $compiler->getEnvironment()->getTests();
if (!isset($testMap[$name])) {
$message = sprintf('The test "%s" does not exist', $name);
if ($alternatives = $compiler->getEnvironment()->computeAlternatives($name, array_keys($compiler->getEnvironment()->getTests()))) {
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
}
throw new Twig_Error_Syntax($message, $this->getLine());
}
$name = $this->getAttribute('name');
$node = $this->getNode('node');
$compiler
->raw($testMap[$name]->compile().'(')
->subcompile($node)
;
if (null !== $this->getNode('arguments')) {
$compiler->raw(', ');
$max = count($this->getNode('arguments')) - 1;
foreach ($this->getNode('arguments') as $i => $arg) {
$compiler->subcompile($arg);
if ($i != $max) {
$compiler->raw(', ');
}
}
}
$compiler->raw(')');
}
}

@ -1,89 +0,0 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
* (c) 2009 Armin Ronacher
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Loops over each item of a sequence.
*
* <pre>
* <ul>
* {% for user in users %}
* <li>{{ user.username|e }}</li>
* {% endfor %}
* </ul>
* </pre>
*/
class Twig_TokenParser_For extends Twig_TokenParser
{
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*/
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, 'in');
$seq = $this->parser->getExpressionParser()->parseExpression();
$ifexpr = null;
if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'if')) {
$this->parser->getStream()->next();
$ifexpr = $this->parser->getExpressionParser()->parseExpression();
}
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideForFork'));
if ($this->parser->getStream()->next()->getValue() == 'else') {
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$else = $this->parser->subparse(array($this, 'decideForEnd'), true);
} else {
$else = null;
}
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
if (count($targets) > 1) {
$keyTarget = $targets->getNode(0);
$keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine());
$valueTarget = $targets->getNode(1);
$valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
} else {
$keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
$valueTarget = $targets->getNode(0);
$valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
}
return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
}
public function decideForFork(Twig_Token $token)
{
return $token->test(array('else', 'endfor'));
}
public function decideForEnd(Twig_Token $token)
{
return $token->test('endfor');
}
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag()
{
return 'for';
}
}

@ -4,4 +4,4 @@
require_once __DIR__ . '/composer' . '/autoload_real.php';
return ComposerAutoloaderInit0c93b82b258bec58c6d3606a1cb88d90::getLoader();
return ComposerAutoloaderInitc5ab5de8e41a56849cc6a794eb539925::getLoader();

@ -143,6 +143,8 @@ class ClassLoader
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-0 base directories
* @param bool $prepend Whether to prepend the directories
*
* @throws \InvalidArgumentException
*/
public function addPsr4($prefix, $paths, $prepend = false)
{
@ -202,10 +204,13 @@ class ClassLoader
* Registers a set of PSR-4 directories for a given namespace,
* replacing any others previously set for this namespace.
*
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
* @param string $prefix The prefix/namespace, with trailing '\\'
* @param array|string $paths The PSR-4 base directories
*
* @throws \InvalidArgumentException
*/
public function setPsr4($prefix, $paths) {
public function setPsr4($prefix, $paths)
{
if (!$prefix) {
$this->fallbackDirsPsr4 = (array) $paths;
} else {

@ -6,6 +6,7 @@ $vendorDir = dirname(dirname(__FILE__));
$baseDir = dirname($vendorDir);
return array(
'Twig_' => array($vendorDir . '/twig/twig/lib'),
'Symfony\\Component\\Yaml\\' => array($vendorDir . '/symfony/yaml'),
'Symfony\\Component\\Process\\' => array($vendorDir . '/symfony/process'),
'Symfony\\Component\\Filesystem\\' => array($vendorDir . '/symfony/filesystem'),

@ -2,7 +2,7 @@
// autoload_real.php @generated by Composer
class ComposerAutoloaderInit0c93b82b258bec58c6d3606a1cb88d90
class ComposerAutoloaderInitc5ab5de8e41a56849cc6a794eb539925
{
private static $loader;
@ -19,12 +19,9 @@ class ComposerAutoloaderInit0c93b82b258bec58c6d3606a1cb88d90
return self::$loader;
}
spl_autoload_register(array('ComposerAutoloaderInit0c93b82b258bec58c6d3606a1cb88d90', 'loadClassLoader'), true, true);
spl_autoload_register(array('ComposerAutoloaderInitc5ab5de8e41a56849cc6a794eb539925', 'loadClassLoader'), true, true);
self::$loader = $loader = new \Composer\Autoload\ClassLoader();
spl_autoload_unregister(array('ComposerAutoloaderInit0c93b82b258bec58c6d3606a1cb88d90', 'loadClassLoader'));
$vendorDir = dirname(__DIR__);
$baseDir = dirname($vendorDir);
spl_autoload_unregister(array('ComposerAutoloaderInitc5ab5de8e41a56849cc6a794eb539925', 'loadClassLoader'));
$map = require __DIR__ . '/autoload_namespaces.php';
foreach ($map as $namespace => $path) {
@ -47,7 +44,7 @@ class ComposerAutoloaderInit0c93b82b258bec58c6d3606a1cb88d90
}
}
function composerRequire0c93b82b258bec58c6d3606a1cb88d90($file)
function composerRequirec5ab5de8e41a56849cc6a794eb539925($file)
{
require $file;
}

@ -795,5 +795,64 @@
"digitalocean",
"vps"
]
},
{
"name": "twig/twig",
"version": "v1.16.0",
"version_normalized": "1.16.0.0",
"source": {
"type": "git",
"url": "https://github.com/fabpot/Twig.git",
"reference": "8ce37115802e257a984a82d38254884085060024"
},
"dist": {
"type": "zip",
"url": "https://api.github.com/repos/fabpot/Twig/zipball/8ce37115802e257a984a82d38254884085060024",
"reference": "8ce37115802e257a984a82d38254884085060024",
"shasum": ""
},
"require": {
"php": ">=5.2.4"
},
"time": "2014-07-05 12:19:05",
"type": "library",
"extra": {
"branch-alias": {
"dev-master": "1.16-dev"
}
},
"installation-source": "dist",
"autoload": {
"psr-0": {
"Twig_": "lib/"
}
},
"notification-url": "https://packagist.org/downloads/",
"license": [
"BSD-3-Clause"
],
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
},
{
"name": "Twig Team",
"homepage": "https://github.com/fabpot/Twig/graphs/contributors",
"role": "Contributors"
}
],
"description": "Twig, the flexible, fast, and secure template language for PHP",
"homepage": "http://twig.sensiolabs.org",
"keywords": [
"templating"
]
}
]

@ -0,0 +1,18 @@
; top-most EditorConfig file
root = true
; Unix-style newlines
[*]
end_of_line = LF
[*.php]
indent_style = space
indent_size = 4
[*.test]
indent_style = space
indent_size = 4
[*.rst]
indent_style = space
indent_size = 4

@ -0,0 +1,2 @@
/ext/twig/autom4te.cache/

@ -0,0 +1,21 @@
language: php
php:
- 5.2
- 5.3
- 5.4
- 5.5
- hhvm
env:
- TWIG_EXT=no
- TWIG_EXT=yes
before_script:
- if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && sudo make install"; fi
- if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
matrix:
exclude:
- php: hhvm
env: TWIG_EXT=yes

@ -0,0 +1,684 @@
* 1.16.0 (2014-07-05)
* changed url_encode to always encode according to RFC 3986
* fixed inheritance in a 'use'-hierarchy
* removed the __toString policy check when the sandbox is disabled
* fixed recursively calling blocks in templates with inheritance
* 1.15.1 (2014-02-13)
* fixed the conversion of the special '0000-00-00 00:00' date
* added an error message when trying to import an undefined block from a trait
* fixed a C extension crash when accessing defined but uninitialized property.
* 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
level above the configured path
* fixed fatal error that should be an exception when adding a filter/function/test too late
* added a batch filter
* added support for encoding an array as query string in the url_encode filter
* 1.12.2 (2013-02-09)
* fixed the timezone used by the date filter and function when the given date contains a timezone (like 2010-01-28T15:00:00+02:00)
* fixed globals when getGlobals is called early on
* added the first and last filter
* 1.12.1 (2013-01-15)
* added support for object instances as the second argument of the constant function
* relaxed globals management to avoid a BC break
* added support for {{ some_string[:2] }}
* 1.12.0 (2013-01-08)
* added verbatim as an alias for the raw tag to avoid confusion with the raw filter
* fixed registration of tests and functions as anonymous functions
* fixed globals management
* 1.12.0-RC1 (2012-12-29)
* added an include function (does the same as the include tag but in a more flexible way)
* added the ability to use any PHP callable to define filters, functions, and tests
* added a syntax error when using a loop variable that is not defined
* added the ability to set default values for macro arguments
* added support for named arguments for filters, tests, and functions
* moved filters/functions/tests syntax errors to the parser
* added support for extended ternary operator syntaxes
* 1.11.1 (2012-11-11)
* fixed debug info line numbering (was off by 2)
* fixed escaping when calling a macro inside another one (regression introduced in 1.9.1)
* optimized variable access on PHP 5.4
* fixed a crash of the C extension when an exception was thrown from a macro called without being imported (using _self.XXX)
* 1.11.0 (2012-11-07)
* fixed macro compilation when a variable name is a PHP reserved keyword
* changed the date filter behavior to always apply the default timezone, except if false is passed as the timezone
* fixed bitwise operator precedences
* added the template_from_string function
* fixed default timezone usage for the date function
* optimized the way Twig exceptions are managed (to make them faster)
* added Twig_ExistsLoaderInterface (implementing this interface in your loader make the chain loader much faster)
* 1.10.3 (2012-10-19)
* fixed wrong template location in some error messages
* reverted a BC break introduced in 1.10.2
* added a split filter
* 1.10.2 (2012-10-15)
* fixed macro calls on PHP 5.4
* 1.10.1 (2012-10-15)
* made a speed optimization to macro calls when imported via the "import" tag
* fixed C extension compilation on Windows
* fixed a segfault in the C extension when using DateTime objects
* 1.10.0 (2012-09-28)
* extracted functional tests framework to make it reusable for third-party extensions
* added namespaced templates support in Twig_Loader_Filesystem
* added Twig_Loader_Filesystem::prependPath()
* fixed an error when a token parser pass a closure as a test to the subparse() method
* 1.9.2 (2012-08-25)
* fixed the in operator for objects that contain circular references
* fixed the C extension when accessing a public property of an object implementing the \ArrayAccess interface
* 1.9.1 (2012-07-22)
* optimized macro calls when auto-escaping is on
* fixed wrong parent class for Twig_Function_Node
* made Twig_Loader_Chain more explicit about problems
* 1.9.0 (2012-07-13)
* made the parsing independent of the template loaders
* fixed exception trace when an error occurs when rendering a child template
* added escaping strategies for CSS, URL, and HTML attributes
* fixed nested embed tag calls
* added the date_modify filter
* 1.8.3 (2012-06-17)
* fixed paths in the filesystem loader when passing a path that ends with a slash or a backslash
* fixed escaping when a project defines a function named html or js
* fixed chmod mode to apply the umask correctly
* 1.8.2 (2012-05-30)
* added the abs filter
* fixed a regression when using a number in template attributes
* fixed compiler when mbstring.func_overload is set to 2
* fixed DateTimeZone support in date filter
* 1.8.1 (2012-05-17)
* fixed a regression when dealing with SimpleXMLElement instances in templates
* fixed "is_safe" value for the "dump" function when "html_errors" is not defined in php.ini
* switched to use mbstring whenever possible instead of iconv (you might need to update your encoding as mbstring and iconv encoding names sometimes differ)
* 1.8.0 (2012-05-08)
* enforced interface when adding tests, filters, functions, and node visitors from extensions
* fixed a side-effect of the date filter where the timezone might be changed
* simplified usage of the autoescape tag; the only (optional) argument is now the escaping strategy or false (with a BC layer)
* added a way to dynamically change the auto-escaping strategy according to the template "filename"
* changed the autoescape option to also accept a supported escaping strategy (for BC, true is equivalent to html)
* added an embed tag
* 1.7.0 (2012-04-24)
* fixed a PHP warning when using CIFS
* fixed template line number in some exceptions
* added an iterable test
* added an error when defining two blocks with the same name in a template
* added the preserves_safety option for filters
* fixed a PHP notice when trying to access a key on a non-object/array variable
* enhanced error reporting when the template file is an instance of SplFileInfo
* added Twig_Environment::mergeGlobals()
* added compilation checks to avoid misuses of the sandbox tag
* fixed filesystem loader freshness logic for high traffic websites
* fixed random function when charset is null
* 1.6.5 (2012-04-11)
* fixed a regression when a template only extends another one without defining any blocks
* 1.6.4 (2012-04-02)
* fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3
* fixed performance when compiling large files
* optimized parent template creation when the template does not use dynamic inheritance
* 1.6.3 (2012-03-22)
* fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension
* fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot
* made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate
* 1.6.2 (2012-03-18)
* fixed sandbox mode when used with inheritance
* added preserveKeys support for the slice filter
* fixed the date filter when a DateTime instance is passed with a specific timezone
* added a trim filter
* 1.6.1 (2012-02-29)
* fixed Twig C extension
* removed the creation of Twig_Markup instances when not needed
* added a way to set the default global timezone for dates
* fixed the slice filter on strings when the length is not specified
* fixed the creation of the cache directory in case of a race condition
* 1.6.0 (2012-02-04)
* fixed raw blocks when used with the whitespace trim option
* made a speed optimization to macro calls when imported via the "from" tag
* fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added
* fixed the attribute function when passing arguments
* added slice notation support for the [] operator (syntactic sugar for the slice operator)
* added a slice filter
* added string support for the reverse filter
* fixed the empty test and the length filter for Twig_Markup instances
* added a date function to ease date comparison
* fixed unary operators precedence
* added recursive parsing support in the parser
* added string and integer handling for the random function
* 1.5.1 (2012-01-05)
* fixed a regression when parsing strings
* 1.5.0 (2012-01-04)
* added Traversable objects support for the join filter
* 1.5.0-RC2 (2011-12-30)
* added a way to set the default global date interval format
* fixed the date filter for DateInterval instances (setTimezone() does not exist for them)
* refactored Twig_Template::display() to ease its extension
* added a number_format filter
* 1.5.0-RC1 (2011-12-26)
* removed the need to quote hash keys
* allowed hash keys to be any expression
* added a do tag
* added a flush tag
* added support for dynamically named filters and functions
* added a dump function to help debugging templates
* added a nl2br filter
* added a random function
* added a way to change the default format for the date filter
* fixed the lexer when an operator ending with a letter ends a line
* added string interpolation support
* enhanced exceptions for unknown filters, functions, tests, and tags
* 1.4.0 (2011-12-07)
* fixed lexer when using big numbers (> PHP_INT_MAX)
* added missing preserveKeys argument to the reverse filter
* fixed macros containing filter tag calls
* 1.4.0-RC2 (2011-11-27)
* removed usage of Reflection in Twig_Template::getAttribute()
* added a C extension that can optionally replace Twig_Template::getAttribute()
* added negative timestamp support to the date filter
* 1.4.0-RC1 (2011-11-20)
* optimized variable access when using PHP 5.4
* changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby
* added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders
* added Twig_Function_Node to allow more complex functions to have their own Node class
* added Twig_Filter_Node to allow more complex filters to have their own Node class
* added Twig_Test_Node to allow more complex tests to have their own Node class
* added a better error message when a template is empty but contain a BOM
* fixed "in" operator for empty strings
* fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option)
* changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order)
* added Twig_Environment::display()
* made the escape filter smarter when the encoding is not supported by PHP
* added a convert_encoding filter
* moved all node manipulations outside the compile() Node method
* made several speed optimizations
* 1.3.0 (2011-10-08)
no changes
* 1.3.0-RC1 (2011-10-04)
* added an optimization for the parent() function
* added cache reloading when auto_reload is true and an extension has been modified
* added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup)
* allowed empty templates to be used as traits
* added traits support for the "parent" function
* 1.2.0 (2011-09-13)
no changes
* 1.2.0-RC1 (2011-09-10)
* enhanced the exception when a tag remains unclosed
* added support for empty Countable objects for the "empty" test
* fixed algorithm that determines if a template using inheritance is valid (no output between block definitions)
* added better support for encoding problems when escaping a string (available as of PHP 5.4)
* added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %})
* added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %})
* added support for bitwise operators in expressions
* added the "attribute" function to allow getting dynamic attributes on variables
* added Twig_Loader_Chain
* added Twig_Loader_Array::setTemplate()
* added an optimization for the set tag when used to capture a large chunk of static text
* changed name regex to match PHP one "[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" (works for blocks, tags, functions, filters, and macros)
* removed the possibility to use the "extends" tag from a block
* added "if" modifier support to "for" loops
* 1.1.2 (2011-07-30)
* fixed json_encode filter on PHP 5.2
* fixed regression introduced in 1.1.1 ({{ block(foo|lower) }})
* fixed inheritance when using conditional parents
* fixed compilation of templates when the body of a child template is not empty
* fixed output when a macro throws an exception
* fixed a parsing problem when a large chunk of text is enclosed in a comment tag
* added PHPDoc for all Token parsers and Core extension functions
* 1.1.1 (2011-07-17)
* added a performance optimization in the Optimizer (also helps to lower the number of nested level calls)
* made some performance improvement for some edge cases
* 1.1.0 (2011-06-28)
* fixed json_encode filter
* 1.1.0-RC3 (2011-06-24)
* fixed method case-sensitivity when using the sandbox mode
* added timezone support for the date filter
* fixed possible security problems with NUL bytes
* 1.1.0-RC2 (2011-06-16)
* added an exception when the template passed to "use" is not a string
* made 'a.b is defined' not throw an exception if a is not defined (in strict mode)
* added {% line \d+ %} directive
* 1.1.0-RC1 (2011-05-28)
Flush your cache after upgrading.
* fixed date filter when using a timestamp
* fixed the defined test for some cases
* fixed a parsing problem when a large chunk of text is enclosed in a raw tag
* added support for horizontal reuse of template blocks (see docs for more information)
* added whitespace control modifier to all tags (see docs for more information)
* added null as an alias for none (the null test is also an alias for the none test now)
* made TRUE, FALSE, NONE equivalent to their lowercase counterparts
* wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line
* moved display() method to Twig_Template (generated templates should now use doDisplay() instead)
* 1.0.0 (2011-03-27)
* fixed output when using mbstring
* fixed duplicate call of methods when using the sandbox
* made the charset configurable for the escape filter
* 1.0.0-RC2 (2011-02-21)
* changed the way {% set %} works when capturing (the content is now marked as safe)
* added support for macro name in the endmacro tag
* make Twig_Error compatible with PHP 5.3.0 >
* fixed an infinite loop on some Windows configurations
* fixed the "length" filter for numbers
* fixed Template::getAttribute() as properties in PHP are case sensitive
* removed coupling between Twig_Node and Twig_Template
* fixed the ternary operator precedence rule
* 1.0.0-RC1 (2011-01-09)
Backward incompatibilities:
* the "items" filter, which has been deprecated for quite a long time now, has been removed
* the "range" filter has been converted to a function: 0|range(10) -> range(0, 10)
* the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }}
* the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }}
* the "for" tag does not support "joined by" anymore
* the "autoescape" first argument is now "true"/"false" (instead of "on"/"off")
* the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %})
* the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %})
* removed the grammar and simple token parser (moved to the Twig Extensions repository)
Changes:
* added "needs_context" option for filters and functions (the context is then passed as a first argument)
* added global variables support
* made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode)
* added the "from" tag to import macros as functions
* added support for functions (a function is just syntactic sugar for a getAttribute() call)
* made macros callable when sandbox mode is enabled
* added an exception when a macro uses a reserved name
* the "default" filter now uses the "empty" test instead of just checking for null
* added the "empty" test
* 0.9.10 (2010-12-16)
Backward incompatibilities:
* The Escaper extension is enabled by default, which means that all displayed
variables are now automatically escaped. You can revert to the previous
behavior by removing the extension via $env->removeExtension('escaper')
or just set the 'autoescape' option to 'false'.
* removed the "without loop" attribute for the "for" tag (not needed anymore
as the Optimizer take care of that for most cases)
* arrays and hashes have now a different syntax
* arrays keep the same syntax with square brackets: [1, 2]
* hashes now use curly braces (["a": "b"] should now be written as {"a": "b"})
* support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1})
* the i18n extension is now part of the Twig Extensions repository
Changes:
* added the merge filter
* removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead
* fixed usage of operators as method names (like is, in, and not)
* changed the order of execution for node visitors
* fixed default() filter behavior when used with strict_variables set to on
* fixed filesystem loader compatibility with PHAR files
* enhanced error messages when an unexpected token is parsed in an expression
* fixed filename not being added to syntax error messages
* added the autoescape option to enable/disable autoescaping
* removed the newline after a comment (mimics PHP behavior)
* added a syntax error exception when parent block is used on a template that does not extend another one
* made the Escaper extension enabled by default
* fixed sandbox extension when used with auto output escaping
* fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved)
* added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters)
* added priority to node visitors
* 0.9.9 (2010-11-28)
Backward incompatibilities:
* the self special variable has been renamed to _self
* the odd and even filters are now tests:
{{ foo|odd }} must now be written {{ foo is odd }}
* the "safe" filter has been renamed to "raw"
* in Node classes,
sub-nodes are now accessed via getNode() (instead of property access)
attributes via getAttribute() (instead of array access)
* the urlencode filter had been renamed to url_encode
* the include tag now merges the passed variables with the current context by default
(the old behavior is still possible by adding the "only" keyword)
* moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime)
* removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead)
* the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }})
Changes:
* added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template
* changed trans tag to accept any variable for the plural count
* fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements)
* added the ** (power) operator
* changed the algorithm used for parsing expressions
* added the spaceless tag
* removed trim_blocks option
* added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar())
* changed all exceptions to extend Twig_Error
* fixed unary expressions ({{ not(1 or 0) }})
* fixed child templates (with an extend tag) that uses one or more imports
* added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }})
* escaping has been rewritten
* the implementation of template inheritance has been rewritten
(blocks can now be called individually and still work with inheritance)
* fixed error handling for if tag when a syntax error occurs within a subparse process
* added a way to implement custom logic for resolving token parsers given a tag name
* fixed js escaper to be stricter (now uses a whilelist-based js escaper)
* added the following filers: "constant", "trans", "replace", "json_encode"
* added a "constant" test
* fixed objects with __toString() not being autoescaped
* fixed subscript expressions when calling __call() (methods now keep the case)
* added "test" feature (accessible via the "is" operator)
* removed the debug tag (should be done in an extension)
* fixed trans tag when no vars are used in plural form
* fixed race condition when writing template cache
* added the special _charset variable to reference the current charset
* added the special _context variable to reference the current context
* renamed self to _self (to avoid conflict)
* fixed Twig_Template::getAttribute() for protected properties
* 0.9.8 (2010-06-28)
Backward incompatibilities:
* the trans tag plural count is now attached to the plural tag:
old: `{% trans count %}...{% plural %}...{% endtrans %}`
new: `{% trans %}...{% plural count %}...{% endtrans %}`
* added a way to translate strings coming from a variable ({% trans var %})
* fixed trans tag when used with the Escaper extension
* fixed default cache umask
* removed Twig_Template instances from the debug tag output
* fixed objects with __isset() defined
* fixed set tag when used with a capture
* fixed type hinting for Twig_Environment::addFilter() method
* 0.9.7 (2010-06-12)
Backward incompatibilities:
* changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %})
* removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
* refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
* added self as a special variable that refers to the current template (useful for importing macros from the current template)
* added Twig_Template instance support to the include tag
* added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
* added a grammar sub-framework to ease the creation of custom tags
* fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface)
* removed the Twig_Resource::resolveMissingFilter() method
* fixed the filter tag which did not apply filtering to included files
* added a bunch of unit tests
* added a bunch of phpdoc
* added a sandbox tag in the sandbox extension
* changed the date filter to support any date format supported by DateTime
* added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default)
* added the lexer, parser, and compiler as arguments to the Twig_Environment constructor
* changed the cache option to only accepts an explicit path to a cache directory or false
* added a way to add token parsers, filters, and visitors without creating an extension
* added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
* changed the generated code to match the new coding standards
* fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
* added an exception when a child template has a non-empty body (as it is always ignored when rendering)
* 0.9.6 (2010-05-12)
* fixed variables defined outside a loop and for which the value changes in a for loop
* fixed the test suite for PHP 5.2 and older versions of PHPUnit
* added support for __call() in expression resolution
* fixed node visiting for macros (macros are now visited by visitors as any other node)
* fixed nested block definitions with a parent call (rarely useful but nonetheless supported now)
* added the cycle filter
* fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII
* added a long-syntax for the set tag ({% set foo %}...{% endset %})
* unit tests are now powered by PHPUnit
* added support for gettext via the `i18n` extension
* fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values
* added a more useful exception if an if tag is not closed properly
* added support for escaping strategy in the autoescape tag
* fixed lexer when a template has a big chunk of text between/in a block
* 0.9.5 (2010-01-20)
As for any new release, don't forget to remove all cached templates after
upgrading.
If you have defined custom filters, you MUST upgrade them for this release. To
upgrade, replace "array" with "new Twig_Filter_Function", and replace the
environment constant by the "needs_environment" option:
// before
'even' => array('twig_is_even_filter', false),
'escape' => array('twig_escape_filter', true),
// after
'even' => new Twig_Filter_Function('twig_is_even_filter'),
'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
If you have created NodeTransformer classes, you will need to upgrade them to
the new interface (please note that the interface is not yet considered
stable).
* fixed list nodes that did not extend the Twig_NodeListInterface
* added the "without loop" option to the for tag (it disables the generation of the loop variable)
* refactored node transformers to node visitors
* fixed automatic-escaping for blocks
* added a way to specify variables to pass to an included template
* changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
* improved the filter system to allow object methods to be used as filters
* changed the Array and String loaders to actually make use of the cache mechanism
* included the default filter function definitions in the extension class files directly (Core, Escaper)
* added the // operator (like the floor() PHP function)
* added the .. operator (as a syntactic sugar for the range filter when the step is 1)
* added the in operator (as a syntactic sugar for the in filter)
* added the following filters in the Core extension: in, range
* added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
* enhanced some error messages to provide better feedback in case of parsing errors
* 0.9.4 (2009-12-02)
If you have custom loaders, you MUST upgrade them for this release: The
Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
been changed (see the source code for more information or the documentation).
* added support for DateTime instances for the date filter
* fixed loop.last when the array only has one item
* made it possible to insert newlines in tag and variable blocks
* fixed a bug when a literal '\n' were present in a template text
* fixed bug when the filename of a template contains */
* refactored loaders
* 0.9.3 (2009-11-11)
This release is NOT backward compatible with the previous releases.
The loaders do not take the cache and autoReload arguments anymore. Instead,
the Twig_Environment class has two new options: cache and auto_reload.
Upgrading your code means changing this kind of code:
$loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true);
$twig = new Twig_Environment($loader);
to something like this:
$loader = new Twig_Loader_Filesystem('/path/to/templates');
$twig = new Twig_Environment($loader, array(
'cache' => '/path/to/compilation_cache',
'auto_reload' => true,
));
* deprecated the "items" filter as it is not needed anymore
* made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader
* optimized template loading speed
* removed output when an error occurs in a template and render() is used
* made major speed improvements for loops (up to 300% on even the smallest loops)
* added properties as part of the sandbox mode
* added public properties support (obj.item can now be the item property on the obj object)
* extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} )
* fixed bug when \ was used in HTML
* 0.9.2 (2009-10-29)
* made some speed optimizations
* changed the cache extension to .php
* added a js escaping strategy
* added support for short block tag
* changed the filter tag to allow chained filters
* made lexer more flexible as you can now change the default delimiters
* added set tag
* changed default directory permission when cache dir does not exist (more secure)
* added macro support
* changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance
* made Twig_Autoloader::autoload() a static method
* avoid writing template file if an error occurs
* added $ escaping when outputting raw strings
* enhanced some error messages to ease debugging
* fixed empty cache files when the template contains an error
* 0.9.1 (2009-10-14)
* fixed a bug in PHP 5.2.6
* fixed numbers with one than one decimal
* added support for method calls with arguments ({{ foo.bar('a', 43) }})
* made small speed optimizations
* made minor tweaks to allow better extensibility and flexibility
* 0.9.0 (2009-10-12)
* Initial release

@ -0,0 +1,31 @@
Copyright (c) 2009-2014 by the Twig Team.
Some rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* 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.

@ -0,0 +1,15 @@
Twig, the flexible, fast, and secure template language for PHP
==============================================================
Twig is a template language for PHP, released under the new BSD license (code
and documentation).
Twig uses a syntax similar to the Django and Jinja template languages which
inspired the Twig runtime environment.
More Information
----------------
Read the `documentation`_ for more information.
.. _documentation: http://twig.sensiolabs.org/documentation

@ -0,0 +1,42 @@
{
"name": "twig/twig",
"type": "library",
"description": "Twig, the flexible, fast, and secure template language for PHP",
"keywords": ["templating"],
"homepage": "http://twig.sensiolabs.org",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Twig Team",
"homepage": "https://github.com/fabpot/Twig/graphs/contributors",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"support": {
"forum": "https://groups.google.com/forum/#!forum/twig-users"
},
"require": {
"php": ">=5.2.4"
},
"autoload": {
"psr-0" : {
"Twig_" : "lib/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.16-dev"
}
}
}

@ -0,0 +1,30 @@
*.sw*
.deps
Makefile
Makefile.fragments
Makefile.global
Makefile.objects
acinclude.m4
aclocal.m4
build/
config.cache
config.guess
config.h
config.h.in
config.log
config.nice
config.status
config.sub
configure
configure.in
install-sh
libtool
ltmain.sh
missing
mkinstalldirs
run-tests.php
twig.loT
.libs/
modules/
twig.la
twig.lo

@ -0,0 +1,31 @@
Copyright (c) 2009-2013 by the Twig Team, see AUTHORS for more details.
Some rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* 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.

@ -0,0 +1,8 @@
dnl config.m4 for extension twig
PHP_ARG_ENABLE(twig, whether to enable twig support,
[ --enable-twig Enable twig support])
if test "$PHP_TWIG" != "no"; then
PHP_NEW_EXTENSION(twig, twig.c, $ext_shared)
fi

@ -0,0 +1,8 @@
// vim:ft=javascript
ARG_ENABLE("twig", "Twig support", "no");
if (PHP_TWIG != "no") {
AC_DEFINE('HAVE_TWIG', 1);
EXTENSION('twig', 'twig.c');
}

@ -0,0 +1,31 @@
/*
+----------------------------------------------------------------------+
| Twig Extension |
+----------------------------------------------------------------------+
| Copyright (c) 2011 Derick Rethans |
+----------------------------------------------------------------------+
| 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-3-Clause). |
+----------------------------------------------------------------------+
| Author: Derick Rethans <derick@derickrethans.nl> |
+----------------------------------------------------------------------+
*/
#ifndef PHP_TWIG_H
#define PHP_TWIG_H
#define PHP_TWIG_VERSION "1.16.0"
#include "php.h"
extern zend_module_entry twig_module_entry;
#define phpext_twig_ptr &twig_module_entry
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_FUNCTION(twig_template_get_attributes);
#endif

File diff suppressed because it is too large Load Diff

@ -12,28 +12,30 @@
/**
* Autoloads Twig classes.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Autoloader
{
/**
* Registers Twig_Autoloader as an SPL autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not.
*/
static public 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(__CLASS__, 'autoload'), true, $prepend);
} else {
spl_autoload_register(array(__CLASS__, 'autoload'));
}
}
/**
* Handles autoloading of classes.
*
* @param string $class A class name.
*
* @return boolean Returns true if the class has been loaded
* @param string $class A class name.
*/
static public function autoload($class)
public static function autoload($class)
{
if (0 !== strpos($class, 'Twig')) {
return;

@ -13,8 +13,7 @@
/**
* Compiles a node to PHP code.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Compiler implements Twig_CompilerInterface
{
@ -25,6 +24,7 @@ class Twig_Compiler implements Twig_CompilerInterface
protected $debugInfo;
protected $sourceOffset;
protected $sourceLine;
protected $filename;
/**
* Constructor.
@ -37,6 +37,11 @@ class Twig_Compiler implements Twig_CompilerInterface
$this->debugInfo = array();
}
public function getFilename()
{
return $this->filename;
}
/**
* Returns the environment instance related to this compiler.
*
@ -61,7 +66,7 @@ class Twig_Compiler implements Twig_CompilerInterface
* Compiles a node.
*
* @param Twig_NodeInterface $node The node to compile
* @param integer $indentation The current indentation
* @param int $indentation The current indentation
*
* @return Twig_Compiler The current compiler instance
*/
@ -70,9 +75,14 @@ class Twig_Compiler implements Twig_CompilerInterface
$this->lastLine = null;
$this->source = '';
$this->sourceOffset = 0;
$this->sourceLine = 0;
// source code starts at 1 (as we then increment it when we encounter new lines)
$this->sourceLine = 1;
$this->indentation = $indentation;
if ($node instanceof Twig_Node_Module) {
$this->filename = $node->getAttribute('filename');
}
$node->compile($this);
return $this;
@ -92,7 +102,7 @@ class Twig_Compiler implements Twig_CompilerInterface
/**
* Adds a raw string to the compiled code.
*
* @param string $string The string
* @param string $string The string
*
* @return Twig_Compiler The current compiler instance
*/
@ -119,6 +129,11 @@ class Twig_Compiler implements Twig_CompilerInterface
return $this;
}
/**
* Appends an indentation to the current PHP code after compilation.
*
* @return Twig_Compiler The current compiler instance
*/
public function addIndentation()
{
$this->source .= str_repeat(' ', $this->indentation * 4);
@ -129,7 +144,7 @@ class Twig_Compiler implements Twig_CompilerInterface
/**
* Adds a quoted string to the compiled code.
*
* @param string $value The string
* @param string $value The string
*
* @return Twig_Compiler The current compiler instance
*/
@ -143,7 +158,7 @@ class Twig_Compiler implements Twig_CompilerInterface
/**
* Returns a PHP representation of a given value.
*
* @param mixed $value The value to convert
* @param mixed $value The value to convert
*
* @return Twig_Compiler The current compiler instance
*/
@ -165,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);
@ -192,12 +208,21 @@ class Twig_Compiler implements Twig_CompilerInterface
public function addDebugInfo(Twig_NodeInterface $node)
{
if ($node->getLine() != $this->lastLine) {
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
$this->write(sprintf("// line %d\n", $node->getLine()));
// when mbstring.func_overload is set to 2
// mb_substr_count() replaces substr_count()
// but they have different signatures!
if (((int) ini_get('mbstring.func_overload')) & 2) {
// this is much slower than the "right" version
$this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
} else {
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
}
$this->sourceOffset = strlen($this->source);
$this->debugInfo[$this->sourceLine] = $node->getLine();
$this->lastLine = $node->getLine();
$this->write("// line {$node->getLine()}\n");
}
return $this;
@ -211,7 +236,7 @@ class Twig_Compiler implements Twig_CompilerInterface
/**
* Indents the generated code.
*
* @param integer $step The number of indentation to add
* @param int $step The number of indentation to add
*
* @return Twig_Compiler The current compiler instance
*/
@ -225,18 +250,21 @@ class Twig_Compiler implements Twig_CompilerInterface
/**
* Outdents the generated code.
*
* @param integer $step The number of indentation to remove
* @param int $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)
{
$this->indentation -= $step;
if ($this->indentation < 0) {
throw new Twig_Error('Unable to call outdent() as the indentation would become negative');
// can't outdent by more steps than the current indentation level
if ($this->indentation < $step) {
throw new LogicException('Unable to call outdent() as the indentation would become negative');
}
$this->indentation -= $step;
return $this;
}
}

@ -12,24 +12,25 @@
/**
* Interface implemented by compiler classes.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_CompilerInterface
{
/**
* Compiles a node.
*
* @param Twig_NodeInterface $node The node to compile
* @param Twig_NodeInterface $node The node to compile
*
* @return Twig_CompilerInterface The current compiler instance
*/
function compile(Twig_NodeInterface $node);
public function compile(Twig_NodeInterface $node);
/**
* Gets the current PHP code after compilation.
*
* @return string The PHP code
*/
function getSource();
public function getSource();
}

@ -12,12 +12,11 @@
/**
* Stores the Twig configuration.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Environment
{
const VERSION = '1.6.4';
const VERSION = '1.16.0';
protected $charset;
protected $loader;
@ -36,6 +35,7 @@ class Twig_Environment
protected $functions;
protected $globals;
protected $runtimeInitialized;
protected $extensionInitialized;
protected $loadedTemplates;
protected $strictVariables;
protected $unaryOperators;
@ -50,33 +50,36 @@ class Twig_Environment
*
* Available options:
*
* * debug: When set to `true`, the generated templates have a __toString()
* method that you can use to display the generated nodes (default to
* false).
* * 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).
*
* * cache: An absolute path where to store the compiled templates, or
* false to disable compilation cache (default)
* 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).
*
* * autoescape: Whether to enable auto-escaping (default to true);
* * autoescape: Whether to enable auto-escaping (default to html):
* * false: disable auto-escaping
* * true: equivalent to html
* * html, js: set the autoescaping to one of the supported strategies
* * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename"
*
* * optimizations: A flag that indicates which optimizations to apply
* (default to -1 which means that all optimizations are enabled;
* set it to 0 to disable)
* set it to 0 to disable).
*
* @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
* @param array $options An array of options
* @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
* @param array $options An array of options
*/
public function __construct(Twig_LoaderInterface $loader = null, $options = array())
{
@ -89,26 +92,27 @@ class Twig_Environment
'charset' => 'UTF-8',
'base_template_class' => 'Twig_Template',
'strict_variables' => false,
'autoescape' => true,
'autoescape' => 'html',
'cache' => false,
'auto_reload' => null,
'optimizations' => -1,
), $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->extensions = array(
'core' => new Twig_Extension_Core(),
'escaper' => new Twig_Extension_Escaper((bool) $options['autoescape']),
'optimizer' => new Twig_Extension_Optimizer($options['optimizations']),
);
$this->strictVariables = (bool) $options['strict_variables'];
$this->runtimeInitialized = false;
$this->setCache($options['cache']);
$this->functionCallbacks = array();
$this->filterCallbacks = array();
$this->addExtension(new Twig_Extension_Core());
$this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
$this->addExtension(new Twig_Extension_Optimizer($options['optimizations']));
$this->extensionInitialized = false;
$this->staging = new Twig_Extension_Staging();
}
/**
@ -150,7 +154,7 @@ class Twig_Environment
/**
* Checks if debug mode is enabled.
*
* @return Boolean true if debug mode is enabled, false otherwise
* @return bool true if debug mode is enabled, false otherwise
*/
public function isDebug()
{
@ -176,7 +180,7 @@ class Twig_Environment
/**
* Checks if the auto_reload option is enabled.
*
* @return Boolean true if auto_reload is enabled, false otherwise
* @return bool true if auto_reload is enabled, false otherwise
*/
public function isAutoReload()
{
@ -202,7 +206,7 @@ class Twig_Environment
/**
* Checks if the strict_variables option is enabled.
*
* @return Boolean true if strict_variables is enabled, false otherwise
* @return bool true if strict_variables is enabled, false otherwise
*/
public function isStrictVariables()
{
@ -235,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)
{
@ -251,13 +255,14 @@ class Twig_Environment
/**
* Gets the template class associated with the given string.
*
* @param string $name The name for which to calculate the template class name
* @param string $name The name for which to calculate the template class name
* @param int $index The index if it is an embedded template
*
* @return string The template class name
*/
public function getTemplateClass($name)
public function getTemplateClass($name, $index = null)
{
return $this->templateClassPrefix.md5($this->loader->getCacheKey($name));
return $this->templateClassPrefix.hash('sha256', $this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index);
}
/**
@ -277,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())
{
@ -288,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())
{
@ -297,13 +310,17 @@ class Twig_Environment
/**
* Loads a template by name.
*
* @param string $name The template name
* @param string $name The template name
* @param int $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)
public function loadTemplate($name, $index = null)
{
$cls = $this->getTemplateClass($name);
$cls = $this->getTemplateClass($name, $index);
if (isset($this->loadedTemplates[$cls])) {
return $this->loadedTemplates[$cls];
@ -311,10 +328,10 @@ class Twig_Environment
if (!class_exists($cls, false)) {
if (false === $cache = $this->getCacheFilename($name)) {
eval('?>'.$this->compileSource($this->loader->getSource($name), $name));
eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name));
} else {
if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
$this->writeCacheFile($cache, $this->compileSource($this->loader->getSource($name), $name));
$this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name));
}
require_once $cache;
@ -338,7 +355,7 @@ class Twig_Environment
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
*
* @return Boolean true if the template is fresh, false otherwise
* @return bool true if the template is fresh, false otherwise
*/
public function isTemplateFresh($name, $time)
{
@ -349,9 +366,22 @@ class Twig_Environment
}
}
return $this->loader->isFresh($name, $time);
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)) {
@ -431,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)
{
@ -462,15 +494,17 @@ class Twig_Environment
}
/**
* Parses a token stream.
* Converts a token stream to a node tree.
*
* @param Twig_TokenStream $stream A token stream instance
*
* @param Twig_TokenStream $tokens A Twig_TokenStream 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);
}
/**
@ -498,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
*
@ -516,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)
{
@ -525,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);
}
}
@ -546,6 +582,10 @@ class Twig_Environment
*/
public function getLoader()
{
if (null === $this->loader) {
throw new LogicException('You must set a loader first.');
}
return $this->loader;
}
@ -556,7 +596,7 @@ class Twig_Environment
*/
public function setCharset($charset)
{
$this->charset = $charset;
$this->charset = strtoupper($charset);
}
/**
@ -586,7 +626,7 @@ class Twig_Environment
*
* @param string $name The extension name
*
* @return Boolean Whether the extension is registered or not
* @return bool Whether the extension is registered or not
*/
public function hasExtension($name)
{
@ -616,29 +656,29 @@ class Twig_Environment
*/
public function addExtension(Twig_ExtensionInterface $extension)
{
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName()));
}
$this->extensions[$extension->getName()] = $extension;
$this->parsers = null;
$this->visitors = null;
$this->filters = null;
$this->tests = null;
$this->functions = null;
$this->globals = null;
}
/**
* Removes an extension by name.
*
* This method is deprecated and you should not use it.
*
* @param string $name The extension name
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
public function removeExtension($name)
{
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
}
unset($this->extensions[$name]);
$this->parsers = null;
$this->visitors = null;
$this->filters = null;
$this->tests = null;
$this->functions = null;
$this->globals = null;
}
/**
@ -670,8 +710,11 @@ class Twig_Environment
*/
public function addTokenParser(Twig_TokenParserInterface $parser)
{
$this->staging['token_parsers'][] = $parser;
$this->parsers = null;
if ($this->extensionInitialized) {
throw new LogicException('Unable to add a token parser as extensions have already been initialized.');
}
$this->staging->addTokenParser($parser);
}
/**
@ -681,27 +724,8 @@ class Twig_Environment
*/
public function getTokenParsers()
{
if (null === $this->parsers) {
$this->parsers = new Twig_TokenParserBroker();
if (isset($this->staging['token_parsers'])) {
foreach ($this->staging['token_parsers'] as $parser) {
$this->parsers->addTokenParser($parser);
}
}
foreach ($this->getExtensions() as $extension) {
$parsers = $extension->getTokenParsers();
foreach($parsers as $parser) {
if ($parser instanceof Twig_TokenParserInterface) {
$this->parsers->addTokenParser($parser);
} elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
$this->parsers->addTokenParserBroker($parser);
} else {
throw new Twig_Error_Runtime('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
}
}
}
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->parsers;
@ -733,8 +757,11 @@ class Twig_Environment
*/
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
$this->staging['visitors'][] = $visitor;
$this->visitors = null;
if ($this->extensionInitialized) {
throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
}
$this->staging->addNodeVisitor($visitor);
}
/**
@ -744,11 +771,8 @@ class Twig_Environment
*/
public function getNodeVisitors()
{
if (null === $this->visitors) {
$this->visitors = isset($this->staging['visitors']) ? $this->staging['visitors'] : array();
foreach ($this->getExtensions() as $extension) {
$this->visitors = array_merge($this->visitors, $extension->getNodeVisitors());
}
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->visitors;
@ -757,13 +781,25 @@ class Twig_Environment
/**
* Registers a Filter.
*
* @param string $name The filter name
* @param Twig_FilterInterface $filter A Twig_FilterInterface instance
* @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance
* @param Twig_FilterInterface|Twig_SimpleFilter $filter A Twig_FilterInterface instance or a Twig_SimpleFilter instance
*/
public function addFilter($name, Twig_FilterInterface $filter)
public function addFilter($name, $filter = null)
{
$this->staging['filters'][$name] = $filter;
$this->filters = null;
if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter');
}
if ($name instanceof Twig_SimpleFilter) {
$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);
}
/**
@ -774,12 +810,12 @@ class Twig_Environment
*
* @param string $name The filter name
*
* @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exists
* @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist
*/
public function getFilter($name)
{
if (null === $this->filters) {
$this->getFilters();
if (!$this->extensionInitialized) {
$this->initExtensions();
}
if (isset($this->filters[$name])) {
@ -824,11 +860,8 @@ class Twig_Environment
*/
public function getFilters()
{
if (null === $this->filters) {
$this->filters = isset($this->staging['filters']) ? $this->staging['filters'] : array();
foreach ($this->getExtensions() as $extension) {
$this->filters = array_merge($this->filters, $extension->getFilters());
}
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->filters;
@ -837,13 +870,25 @@ class Twig_Environment
/**
* Registers a Test.
*
* @param string $name The test name
* @param Twig_TestInterface $test A Twig_TestInterface instance
* @param string|Twig_SimpleTest $name The test name or a Twig_SimpleTest instance
* @param Twig_TestInterface|Twig_SimpleTest $test A Twig_TestInterface instance or a Twig_SimpleTest instance
*/
public function addTest($name, Twig_TestInterface $test)
public function addTest($name, $test = null)
{
$this->staging['tests'][$name] = $test;
$this->tests = null;
if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest');
}
if ($name instanceof Twig_SimpleTest) {
$test = $name;
$name = $test->getName();
}
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
}
$this->staging->addTest($name, $test);
}
/**
@ -853,26 +898,55 @@ class Twig_Environment
*/
public function getTests()
{
if (null === $this->tests) {
$this->tests = isset($this->staging['tests']) ? $this->staging['tests'] : array();
foreach ($this->getExtensions() as $extension) {
$this->tests = array_merge($this->tests, $extension->getTests());
}
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->tests;
}
/**
* Gets a test by name.
*
* @param string $name The test name
*
* @return Twig_Test|false A Twig_Test instance or false if the test does not exist
*/
public function getTest($name)
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
if (isset($this->tests[$name])) {
return $this->tests[$name];
}
return false;
}
/**
* Registers a Function.
*
* @param string $name The function name
* @param Twig_FunctionInterface $function A Twig_FunctionInterface instance
* @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance
* @param Twig_FunctionInterface|Twig_SimpleFunction $function A Twig_FunctionInterface instance or a Twig_SimpleFunction instance
*/
public function addFunction($name, Twig_FunctionInterface $function)
public function addFunction($name, $function = null)
{
$this->staging['functions'][$name] = $function;
$this->functions = null;
if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction');
}
if ($name instanceof Twig_SimpleFunction) {
$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);
}
/**
@ -883,12 +957,12 @@ class Twig_Environment
*
* @param string $name function name
*
* @return Twig_Function|false A Twig_Function instance or false if the function does not exists
* @return Twig_Function|false A Twig_Function instance or false if the function does not exist
*/
public function getFunction($name)
{
if (null === $this->functions) {
$this->getFunctions();
if (!$this->extensionInitialized) {
$this->initExtensions();
}
if (isset($this->functions[$name])) {
@ -933,11 +1007,8 @@ class Twig_Environment
*/
public function getFunctions()
{
if (null === $this->functions) {
$this->functions = isset($this->staging['functions']) ? $this->staging['functions'] : array();
foreach ($this->getExtensions() as $extension) {
$this->functions = array_merge($this->functions, $extension->getFunctions());
}
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->functions;
@ -946,13 +1017,32 @@ class Twig_Environment
/**
* Registers a Global.
*
* New globals can be added before compiling or rendering a template;
* but after, you can only update existing globals.
*
* @param string $name The global name
* @param mixed $value The global value
*/
public function addGlobal($name, $value)
{
$this->staging['globals'][$name] = $value;
$this->globals = null;
if ($this->extensionInitialized || $this->runtimeInitialized) {
if (null === $this->globals) {
$this->globals = $this->initGlobals();
}
/* This condition must be uncommented in Twig 2.0
if (!array_key_exists($name, $this->globals)) {
throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
}
*/
}
if ($this->extensionInitialized || $this->runtimeInitialized) {
// update the value
$this->globals[$name] = $value;
} else {
$this->staging->addGlobal($name, $value);
}
}
/**
@ -962,16 +1052,37 @@ class Twig_Environment
*/
public function getGlobals()
{
if (!$this->runtimeInitialized && !$this->extensionInitialized) {
return $this->initGlobals();
}
if (null === $this->globals) {
$this->globals = isset($this->staging['globals']) ? $this->staging['globals'] : array();
foreach ($this->getExtensions() as $extension) {
$this->globals = array_merge($this->globals, $extension->getGlobals());
}
$this->globals = $this->initGlobals();
}
return $this->globals;
}
/**
* Merges a context with the defined globals.
*
* @param array $context An array representing the context
*
* @return array The context merged with the globals
*/
public function mergeGlobals(array $context)
{
// we don't use array_merge as the context being generally
// bigger than globals, this code is faster.
foreach ($this->getGlobals() as $key => $value) {
if (!array_key_exists($key, $context)) {
$context[$key] = $value;
}
}
return $context;
}
/**
* Gets the registered unary Operators.
*
@ -979,8 +1090,8 @@ class Twig_Environment
*/
public function getUnaryOperators()
{
if (null === $this->unaryOperators) {
$this->initOperators();
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->unaryOperators;
@ -993,8 +1104,8 @@ class Twig_Environment
*/
public function getBinaryOperators()
{
if (null === $this->binaryOperators) {
$this->initOperators();
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->binaryOperators;
@ -1014,17 +1125,100 @@ class Twig_Environment
return array_keys($alternatives);
}
protected function initOperators()
protected function initGlobals()
{
$globals = array();
foreach ($this->extensions as $extension) {
$extGlob = $extension->getGlobals();
if (!is_array($extGlob)) {
throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
}
$globals[] = $extGlob;
}
$globals[] = $this->staging->getGlobals();
return call_user_func_array('array_merge', $globals);
}
protected function initExtensions()
{
if ($this->extensionInitialized) {
return;
}
$this->extensionInitialized = true;
$this->parsers = new Twig_TokenParserBroker();
$this->filters = array();
$this->functions = array();
$this->tests = array();
$this->visitors = array();
$this->unaryOperators = array();
$this->binaryOperators = array();
foreach ($this->getExtensions() as $extension) {
$operators = $extension->getOperators();
if (!$operators) {
continue;
foreach ($this->extensions as $extension) {
$this->initExtension($extension);
}
$this->initExtension($this->staging);
}
protected function initExtension(Twig_ExtensionInterface $extension)
{
// filters
foreach ($extension->getFilters() as $name => $filter) {
if ($name instanceof Twig_SimpleFilter) {
$filter = $name;
$name = $filter->getName();
} elseif ($filter instanceof Twig_SimpleFilter) {
$name = $filter->getName();
}
$this->filters[$name] = $filter;
}
// functions
foreach ($extension->getFunctions() as $name => $function) {
if ($name instanceof Twig_SimpleFunction) {
$function = $name;
$name = $function->getName();
} elseif ($function instanceof Twig_SimpleFunction) {
$name = $function->getName();
}
$this->functions[$name] = $function;
}
// tests
foreach ($extension->getTests() as $name => $test) {
if ($name instanceof Twig_SimpleTest) {
$test = $name;
$name = $test->getName();
} elseif ($test instanceof Twig_SimpleTest) {
$name = $test->getName();
}
$this->tests[$name] = $test;
}
// token parsers
foreach ($extension->getTokenParsers() as $parser) {
if ($parser instanceof Twig_TokenParserInterface) {
$this->parsers->addTokenParser($parser);
} elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
$this->parsers->addTokenParserBroker($parser);
} else {
throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
}
}
// node visitors
foreach ($extension->getNodeVisitors() as $visitor) {
$this->visitors[] = $visitor;
}
// operators
if ($operators = $extension->getOperators()) {
if (2 !== count($operators)) {
throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
}
@ -1045,16 +1239,16 @@ 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))) {
chmod($file, 0644);
@chmod($file, 0666 & ~umask());
return;
}
}
throw new Twig_Error_Runtime(sprintf('Failed to write cache file "%s".', $file));
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file));
}
}

@ -0,0 +1,248 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Twig base exception.
*
* This exception class and its children must only be used when
* an error occurs during the loading of a template, when a syntax error
* is detected in a template, or when rendering a template. Other
* errors must use regular PHP exception classes (like when the template
* cache directory is not writable for instance).
*
* To help debugging template issues, this class tracks the original template
* name and line where the error occurred.
*
* Whenever possible, you must set these information (original template name
* and line number) yourself by passing them to the constructor. If some or all
* these information are not available from where you throw the exception, then
* this class will guess them automatically (when the line number is set to -1
* and/or the filename is set to null). As this is a costly operation, this
* can be disabled by passing false for both the filename and the line number
* when creating a new instance of this class.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Error extends Exception
{
protected $lineno;
protected $filename;
protected $rawMessage;
protected $previous;
/**
* Constructor.
*
* Set both the line number and the filename to false to
* disable automatic guessing of the original template name
* and line number.
*
* Set the line number to -1 to enable its automatic guessing.
* Set the filename to null to enable its automatic guessing.
*
* By default, automatic guessing is enabled.
*
* @param string $message The error message
* @param int $lineno The template line where the error occurred
* @param string $filename The template file name where the error occurred
* @param Exception $previous The previous exception
*/
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
{
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
$this->previous = $previous;
parent::__construct('');
} else {
parent::__construct('', 0, $previous);
}
$this->lineno = $lineno;
$this->filename = $filename;
if (-1 === $this->lineno || null === $this->filename) {
$this->guessTemplateInfo();
}
$this->rawMessage = $message;
$this->updateRepr();
}
/**
* Gets the raw message.
*
* @return string The raw message
*/
public function getRawMessage()
{
return $this->rawMessage;
}
/**
* Gets the filename where the error occurred.
*
* @return string The filename
*/
public function getTemplateFile()
{
return $this->filename;
}
/**
* Sets the filename where the error occurred.
*
* @param string $filename The filename
*/
public function setTemplateFile($filename)
{
$this->filename = $filename;
$this->updateRepr();
}
/**
* Gets the template line where the error occurred.
*
* @return int The template line
*/
public function getTemplateLine()
{
return $this->lineno;
}
/**
* Sets the template line where the error occurred.
*
* @param int $lineno The template line
*/
public function setTemplateLine($lineno)
{
$this->lineno = $lineno;
$this->updateRepr();
}
public function guess()
{
$this->guessTemplateInfo();
$this->updateRepr();
}
/**
* For PHP < 5.3.0, provides access to the getPrevious() method.
*
* @param string $method The method name
* @param array $arguments The parameters to be passed to the method
*
* @return Exception The previous exception or null
*
* @throws BadMethodCallException
*/
public function __call($method, $arguments)
{
if ('getprevious' == strtolower($method)) {
return $this->previous;
}
throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
}
protected function updateRepr()
{
$this->message = $this->rawMessage;
$dot = false;
if ('.' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}
if ($this->filename) {
if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
$filename = sprintf('"%s"', $this->filename);
} else {
$filename = json_encode($this->filename);
}
$this->message .= sprintf(' in %s', $filename);
}
if ($this->lineno && $this->lineno >= 0) {
$this->message .= sprintf(' at line %d', $this->lineno);
}
if ($dot) {
$this->message .= '.';
}
}
protected function guessTemplateInfo()
{
$template = null;
$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'])) {
$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']);
}
}
}
// update template filename
if (null !== $template && null === $this->filename) {
$this->filename = $template->getTemplateName();
}
if (null === $template || $this->lineno > -1) {
return;
}
$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;
}
while ($e = array_pop($exceptions)) {
$traces = $e->getTrace();
while ($trace = array_shift($traces)) {
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
continue;
}
foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
if ($codeLine <= $trace['line']) {
// update template line
$this->lineno = $templateLine;
return;
}
}
}
}
}
}

@ -0,0 +1,31 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2010 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Exception thrown when an error occurs during template loading.
*
* Automatic template information guessing is always turned off as
* if a template cannot be loaded, there is nothing to guess.
* However, when a template is loaded from another one, then, we need
* to find the current context and this is automatically done by
* Twig_Template::displayWithErrorHandling().
*
* This strategy makes Twig_Environment::resolveTemplate() much faster.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Error_Loader extends Twig_Error
{
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
{
parent::__construct($message, false, false, $previous);
}
}

@ -13,8 +13,7 @@
/**
* Exception thrown when an error occurs at runtime.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Error_Runtime extends Twig_Error
{

@ -13,8 +13,7 @@
/**
* Exception thrown when a syntax error occurs during lexing or parsing of a template.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Error_Syntax extends Twig_Error
{

@ -0,0 +1,29 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Adds an exists() method for loaders.
*
* @author Florin Patan <florinpatan@gmail.com>
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_ExistsLoaderInterface
{
/**
* Check if we have the source code of a template, given its name.
*
* @param string $name The name of the template to check if we can load
*
* @return bool If the template source code is handled by this loader or not
*/
public function exists($name);
}

@ -18,8 +18,7 @@
* @see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
* @see http://en.wikipedia.org/wiki/Operator-precedence_parser
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_ExpressionParser
{
@ -87,11 +86,18 @@ class Twig_ExpressionParser
protected function parseConditionalExpression($expr)
{
while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) {
$this->parser->getStream()->next();
$expr2 = $this->parseExpression();
$this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'The ternary operator must have a default value');
$expr3 = $this->parseExpression();
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()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
$expr3 = $this->parseExpression();
} else {
$expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
}
} else {
$expr2 = $expr;
$expr3 = $this->parseExpression();
}
$expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
}
@ -152,13 +158,21 @@ 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();
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
$node = $this->parseHashExpression();
} else {
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine());
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
}
@ -173,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;
@ -244,15 +256,14 @@ 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();
} else {
$current = $stream->getCurrent();
throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType(), $current->getLine()), $current->getValue()), $current->getLine());
throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
@ -287,30 +298,31 @@ class Twig_ExpressionParser
public function getFunctionNode($name, $line)
{
$args = $this->parseArguments();
switch ($name) {
case 'parent':
$args = $this->parseArguments();
if (!count($this->parser->getBlockStack())) {
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line);
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename());
}
if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line);
throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line, $this->parser->getFilename());
}
return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
case 'block':
return new Twig_Node_Expression_BlockReference($args->getNode(0), false, $line);
return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line);
case 'attribute':
$args = $this->parseArguments();
if (count($args) < 2) {
throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line);
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->getImportedFunction($name)) {
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
$arguments = new Twig_Node_Expression_Array(array(), $line);
foreach ($args as $n) {
foreach ($this->parseArguments() as $n) {
$arguments->addElement($n);
}
@ -320,7 +332,8 @@ class Twig_ExpressionParser
return $node;
}
$class = $this->getFunctionNodeClass($name);
$args = $this->parseArguments(true);
$class = $this->getFunctionNodeClass($name, $line);
return new $class($name, $args, $line);
}
@ -332,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 (
@ -351,24 +364,43 @@ class Twig_ExpressionParser
}
}
} else {
throw new Twig_Error_Syntax('Expected name or number', $lineno);
throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
}
} else {
$type = Twig_TemplateInterface::ARRAY_CALL;
$arg = $this->parseExpression();
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());
}
$node = new Twig_Node_Expression_MethodCall($node, 'get'.$arg->getAttribute('value'), $arguments, $lineno);
$node->setAttribute('safe', true);
return $node;
}
} else {
$type = Twig_Template::ARRAY_CALL;
// slice?
$slice = false;
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
$stream->next();
$slice = true;
$arg = new Twig_Node_Expression_Constant(0, $token->getLine());
} else {
$arg = $this->parseExpression();
}
if ($stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
$slice = true;
}
if ($slice) {
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
$length = new Twig_Node_Expression_Constant(null, $token->getLine());
} else {
$length = $this->parseExpression();
}
$class = $this->getFilterNodeClass('slice');
$class = $this->getFilterNodeClass('slice', $token->getLine());
$arguments = new Twig_Node(array($arg, $length));
$filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine());
@ -399,10 +431,10 @@ class Twig_ExpressionParser
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
$arguments = new Twig_Node();
} else {
$arguments = $this->parseArguments();
$arguments = $this->parseArguments(true);
}
$class = $this->getFilterNodeClass($name->getAttribute('value'));
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
@ -416,17 +448,61 @@ class Twig_ExpressionParser
return $node;
}
public function parseArguments()
/**
* Parses arguments.
*
* @param bool $namedArguments Whether to allow named arguments or not
* @param bool $definition Whether we are parsing arguments for a function definition
*/
public function parseArguments($namedArguments = false, $definition = false)
{
$args = array();
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must be opened by a parenthesis');
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) {
if (!empty($args)) {
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
}
$args[] = $this->parseExpression();
if ($definition) {
$token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name');
$value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine());
} else {
$value = $this->parseExpression();
}
$name = null;
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());
}
$name = $value->getAttribute('name');
if ($definition) {
$value = $this->parsePrimaryExpression();
if (!$this->checkConstantExpression($value)) {
throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename());
}
} else {
$value = $this->parseExpression();
}
}
if ($definition) {
if (null === $name) {
$name = $value->getAttribute('name');
$value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
}
$args[$name] = $value;
} else {
if (null === $name) {
$args[] = $value;
} else {
$args[$name] = $value;
}
}
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
@ -439,14 +515,13 @@ class Twig_ExpressionParser
while (true) {
$token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
if (in_array($token->getValue(), array('true', 'false', 'none'))) {
throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine());
throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
$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);
@ -457,32 +532,67 @@ 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);
}
protected function getFunctionNodeClass($name)
protected function getFunctionNodeClass($name, $line)
{
$env = $this->parser->getEnvironment();
if (false === $function = $env->getFunction($name)) {
$message = sprintf('The function "%s" does not exist', $name);
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFunctions()))) {
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
}
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
}
if ($function instanceof Twig_SimpleFunction) {
return $function->getNodeClass();
}
return $function instanceof Twig_Function_Node ? $function->getClass() : 'Twig_Node_Expression_Function';
}
protected function getFilterNodeClass($name, $line)
{
$functionMap = $this->parser->getEnvironment()->getFunctions();
if (isset($functionMap[$name]) && $functionMap[$name] instanceof Twig_Function_Node) {
return $functionMap[$name]->getClass();
$env = $this->parser->getEnvironment();
if (false === $filter = $env->getFilter($name)) {
$message = sprintf('The filter "%s" does not exist', $name);
if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFilters()))) {
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
}
throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
}
if ($filter instanceof Twig_SimpleFilter) {
return $filter->getNodeClass();
}
return 'Twig_Node_Expression_Function';
return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter';
}
protected function getFilterNodeClass($name)
// checks that the node only contains "constant" elements
protected function checkConstantExpression(Twig_NodeInterface $node)
{
$filterMap = $this->parser->getEnvironment()->getFilters();
if (isset($filterMap[$name]) && $filterMap[$name] instanceof Twig_Filter_Node) {
return $filterMap[$name]->getClass();
if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array)) {
return false;
}
foreach ($node as $n) {
if (!$this->checkConstantExpression($n)) {
return false;
}
}
return 'Twig_Node_Expression_Filter';
return true;
}
}

@ -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()
{

File diff suppressed because it is too large Load Diff

@ -17,11 +17,18 @@ class Twig_Extension_Debug extends Twig_Extension
*/
public function getFunctions()
{
// dump is safe if var_dump is overriden by xdebug
$isDumpOutputHtmlSafe = extension_loaded('xdebug') && (false === get_cfg_var('xdebug.overload_var_dump') || get_cfg_var('xdebug.overload_var_dump')) && get_cfg_var('html_errors');
// dump is safe if var_dump is overridden by xdebug
$isDumpOutputHtmlSafe = extension_loaded('xdebug')
// false means that it was not set (and the default is on) or it explicitly enabled
&& (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump'))
// 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(
'dump' => new Twig_Function_Function('twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)),
new Twig_SimpleFunction('dump', 'twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)),
);
}

@ -0,0 +1,107 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Twig_Extension_Escaper extends Twig_Extension
{
protected $defaultStrategy;
public function __construct($defaultStrategy = 'html')
{
$this->setDefaultStrategy($defaultStrategy);
}
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
*/
public function getTokenParsers()
{
return array(new Twig_TokenParser_AutoEscape());
}
/**
* Returns the node visitor instances to add to the existing list.
*
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
*/
public function getNodeVisitors()
{
return array(new Twig_NodeVisitor_Escaper());
}
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
*/
public function getFilters()
{
return array(
new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))),
);
}
/**
* Sets the default strategy to use when not defined by the user.
*
* The strategy can be a valid PHP callback that takes the template
* "filename" as an argument and returns the strategy to use.
*
* @param mixed $defaultStrategy An escaping strategy
*/
public function setDefaultStrategy($defaultStrategy)
{
// for BC
if (true === $defaultStrategy) {
$defaultStrategy = 'html';
}
$this->defaultStrategy = $defaultStrategy;
}
/**
* Gets the default strategy to use when not defined by the user.
*
* @param string $filename The template "filename"
*
* @return string The default strategy to use for the template
*/
public function getDefaultStrategy($filename)
{
// disable string callables to avoid calling a function named html or js,
// or any other upcoming escaping strategy
if (!is_string($this->defaultStrategy) && is_callable($this->defaultStrategy)) {
return call_user_func($this->defaultStrategy, $filename);
}
return $this->defaultStrategy;
}
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName()
{
return 'escaper';
}
}
/**
* Marks a variable as being safe.
*
* @param string $string A PHP variable
*/
function twig_raw_filter($string)
{
return $string;
}

@ -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()
{
@ -93,7 +93,7 @@ class Twig_Extension_Sandbox extends Twig_Extension
public function ensureToStringAllowed($obj)
{
if (is_object($obj)) {
if ($this->isSandboxed() && is_object($obj)) {
$this->policy->checkMethodAllowed($obj, '__toString');
}

@ -0,0 +1,113 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2012 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Internal class.
*
* This class is used by Twig_Environment as a staging area and must not be used directly.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Extension_Staging extends Twig_Extension
{
protected $functions = array();
protected $filters = array();
protected $visitors = array();
protected $tokenParsers = array();
protected $globals = array();
protected $tests = array();
public function addFunction($name, $function)
{
$this->functions[$name] = $function;
}
/**
* {@inheritdoc}
*/
public function getFunctions()
{
return $this->functions;
}
public function addFilter($name, $filter)
{
$this->filters[$name] = $filter;
}
/**
* {@inheritdoc}
*/
public function getFilters()
{
return $this->filters;
}
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
$this->visitors[] = $visitor;
}
/**
* {@inheritdoc}
*/
public function getNodeVisitors()
{
return $this->visitors;
}
public function addTokenParser(Twig_TokenParserInterface $parser)
{
$this->tokenParsers[] = $parser;
}
/**
* {@inheritdoc}
*/
public function getTokenParsers()
{
return $this->tokenParsers;
}
public function addGlobal($name, $value)
{
$this->globals[$name] = $value;
}
/**
* {@inheritdoc}
*/
public function getGlobals()
{
return $this->globals;
}
public function addTest($name, $test)
{
$this->tests[$name] = $test;
}
/**
* {@inheritdoc}
*/
public function getTests()
{
return $this->tests;
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'staging';
}
}

@ -0,0 +1,64 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2012 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
class Twig_Extension_StringLoader extends Twig_Extension
{
/**
* {@inheritdoc}
*/
public function getFunctions()
{
return array(
new Twig_SimpleFunction('template_from_string', 'twig_template_from_string', array('needs_environment' => true)),
);
}
/**
* {@inheritdoc}
*/
public function getName()
{
return 'string_loader';
}
}
/**
* Loads a template from a string.
*
* <pre>
* {{ include(template_from_string("Hello {{ name }}")) }}
* </pre>
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $template A template as a string
*
* @return Twig_Template A Twig_Template instance
*/
function twig_template_from_string(Twig_Environment $env, $template)
{
$name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false));
$loader = new Twig_Loader_Chain(array(
new Twig_Loader_Array(array($name => $template)),
$current = $env->getLoader(),
));
$env->setLoader($loader);
try {
$template = $env->loadTemplate($name);
} catch (Exception $e) {
$env->setLoader($current);
throw $e;
}
$env->setLoader($current);
return $template;
}

@ -12,8 +12,7 @@
/**
* Interface implemented by extension classes.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
interface Twig_ExtensionInterface
{
@ -24,61 +23,61 @@ interface Twig_ExtensionInterface
*
* @param Twig_Environment $environment The current Twig_Environment instance
*/
function initRuntime(Twig_Environment $environment);
public function initRuntime(Twig_Environment $environment);
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
*/
function getTokenParsers();
public function getTokenParsers();
/**
* 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
*/
function getNodeVisitors();
public function getNodeVisitors();
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
*/
function getFilters();
public function getFilters();
/**
* Returns a list of tests to add to the existing list.
*
* @return array An array of tests
*/
function getTests();
public function getTests();
/**
* Returns a list of functions to add to the existing list.
*
* @return array An array of functions
*/
function getFunctions();
public function getFunctions();
/**
* Returns a list of operators to add to the existing list.
*
* @return array An array of operators
*/
function getOperators();
public function getOperators();
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*/
function getGlobals();
public function getGlobals();
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
function getName();
public function getName();
}

@ -12,10 +12,12 @@
/**
* Represents a template filter.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
abstract class Twig_Filter implements Twig_FilterInterface
abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface
{
protected $options;
protected $arguments = array();
@ -26,6 +28,8 @@ abstract class Twig_Filter implements Twig_FilterInterface
'needs_environment' => false,
'needs_context' => false,
'pre_escape' => null,
'preserves_safety' => null,
'callable' => null,
), $options);
}
@ -58,12 +62,20 @@ abstract class Twig_Filter implements Twig_FilterInterface
if (isset($this->options['is_safe_callback'])) {
return call_user_func($this->options['is_safe_callback'], $filterArgs);
}
}
return array();
public function getPreservesSafety()
{
return $this->options['preserves_safety'];
}
public function getPreEscape()
{
return $this->options['pre_escape'];
}
public function getCallable()
{
return $this->options['callable'];
}
}

@ -12,8 +12,10 @@
/**
* Represents a function template filter.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Function extends Twig_Filter
{
@ -21,6 +23,8 @@ class Twig_Filter_Function extends Twig_Filter
public function __construct($function, array $options = array())
{
$options['callable'] = $function;
parent::__construct($options);
$this->function = $function;

@ -12,15 +12,20 @@
/**
* Represents a method template filter.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Method extends Twig_Filter
{
protected $extension, $method;
protected $extension;
protected $method;
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
{
$options['callable'] = array($extension, $method);
parent::__construct($options);
$this->extension = $extension;

@ -12,8 +12,10 @@
/**
* Represents a template filter as a node.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Node extends Twig_Filter
{

@ -0,0 +1,23 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2012 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Represents a callable template filter.
*
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FilterCallableInterface
{
public function getCallable();
}

@ -0,0 +1,42 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2010 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Represents a template filter.
*
* Use Twig_SimpleFilter instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FilterInterface
{
/**
* Compiles a filter.
*
* @return string The PHP code for the filter
*/
public function compile();
public function needsEnvironment();
public function needsContext();
public function getSafe(Twig_Node $filterArgs);
public function getPreservesSafety();
public function getPreEscape();
public function setArguments($arguments);
public function getArguments();
}

@ -12,10 +12,12 @@
/**
* Represents a template function.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* Use Twig_SimpleFunction instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
abstract class Twig_Function implements Twig_FunctionInterface
abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface
{
protected $options;
protected $arguments = array();
@ -25,6 +27,7 @@ abstract class Twig_Function implements Twig_FunctionInterface
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'callable' => null,
), $options);
}
@ -60,4 +63,9 @@ abstract class Twig_Function implements Twig_FunctionInterface
return array();
}
public function getCallable()
{
return $this->options['callable'];
}
}

@ -13,8 +13,10 @@
/**
* Represents a function template function.
*
* @package twig
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
* Use Twig_SimpleFunction instead.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Function extends Twig_Function
{
@ -22,6 +24,8 @@ class Twig_Function_Function extends Twig_Function
public function __construct($function, array $options = array())
{
$options['callable'] = $function;
parent::__construct($options);
$this->function = $function;

@ -13,15 +13,20 @@
/**
* Represents a method template function.
*
* @package twig
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
* Use Twig_SimpleFunction instead.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Method extends Twig_Function
{
protected $extension, $method;
protected $extension;
protected $method;
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
{
$options['callable'] = array($extension, $method);
parent::__construct($options);
$this->extension = $extension;

@ -12,10 +12,12 @@
/**
* Represents a template function as a node.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* Use Twig_SimpleFunction instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Node extends Twig_Filter
class Twig_Function_Node extends Twig_Function
{
protected $class;

@ -0,0 +1,23 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2012 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Represents a callable template function.
*
* Use Twig_SimpleFunction instead.
*
* @author Fabien Potencier <fabien@symfony.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FunctionCallableInterface
{
public function getCallable();
}

@ -13,8 +13,10 @@
/**
* Represents a template function.
*
* @package twig
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
* Use Twig_SimpleFunction instead.
*
* @author Arnaud Le Blanc <arnaud.lb@gmail.com>
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FunctionInterface
{
@ -23,15 +25,15 @@ interface Twig_FunctionInterface
*
* @return string The PHP code for the function
*/
function compile();
public function compile();
function needsEnvironment();
public function needsEnvironment();
function needsContext();
public function needsContext();
function getSafe(Twig_Node $filterArgs);
public function getSafe(Twig_Node $filterArgs);
function setArguments($arguments);
public function setArguments($arguments);
function getArguments();
public function getArguments();
}

@ -13,8 +13,7 @@
/**
* Lexes a template string.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Lexer implements Twig_LexerInterface
{
@ -30,6 +29,9 @@ class Twig_Lexer implements Twig_LexerInterface
protected $filename;
protected $options;
protected $regexes;
protected $position;
protected $positions;
protected $currentVarBlockLine;
const STATE_DATA = 0;
const STATE_BLOCK = 1;
@ -59,10 +61,10 @@ class Twig_Lexer implements Twig_LexerInterface
$this->regexes = array(
'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*endraw\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
'operator' => $this->getOperatorRegex(),
'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
'lex_block_raw' => '/\s*raw\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
@ -71,18 +73,15 @@ 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)
{
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII');
} else {
$mbEncoding = null;
}
$this->code = str_replace(array("\r\n", "\r"), "\n", $code);
@ -133,7 +132,7 @@ class Twig_Lexer implements Twig_LexerInterface
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
}
if (isset($mbEncoding)) {
if ($mbEncoding) {
mb_internal_encoding($mbEncoding);
}
@ -176,7 +175,7 @@ class Twig_Lexer implements Twig_LexerInterface
// raw data?
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
$this->moveCursor($match[0]);
$this->lexRawData();
$this->lexRawData($match[1]);
// {% line \d+ %}
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
$this->moveCursor($match[0]);
@ -184,12 +183,14 @@ class Twig_Lexer implements Twig_LexerInterface
} else {
$this->pushToken(Twig_Token::BLOCK_START_TYPE);
$this->pushState(self::STATE_BLOCK);
$this->currentVarBlockLine = $this->lineno;
}
break;
case $this->options['tag_variable'][0]:
$this->pushToken(Twig_Token::VAR_START_TYPE);
$this->pushState(self::STATE_VAR);
$this->currentVarBlockLine = $this->lineno;
break;
}
}
@ -223,13 +224,13 @@ class Twig_Lexer implements Twig_LexerInterface
$this->moveCursor($match[0]);
if ($this->cursor >= $this->end) {
throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'));
throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->filename);
}
}
// 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
@ -284,10 +285,10 @@ class Twig_Lexer implements Twig_LexerInterface
}
}
protected function lexRawData()
protected function lexRawData($tag)
{
if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "block"'));
if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block', $tag), $this->lineno, $this->filename);
}
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
@ -322,7 +323,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);
@ -330,8 +330,6 @@ class Twig_Lexer implements Twig_LexerInterface
$this->popState();
++$this->cursor;
return;
}
}
@ -380,10 +378,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';

@ -12,18 +12,21 @@
/**
* Interface implemented by lexer classes.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_LexerInterface
{
/**
* Tokenizes a source code.
*
* @param string $code The source code
* @param string $filename A unique identifier for the 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
*
* @throws Twig_Error_Syntax When the code is syntactically wrong
*/
function tokenize($code, $filename = null);
public function tokenize($code, $filename = null);
}

@ -17,12 +17,11 @@
* source code of the template). If you don't want to see your cache grows out of
* control, you need to take care of clearing the old cache file by yourself.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Array implements Twig_LoaderInterface
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
protected $templates;
protected $templates = array();
/**
* Constructor.
@ -33,10 +32,7 @@ class Twig_Loader_Array implements Twig_LoaderInterface
*/
public function __construct(array $templates)
{
$this->templates = array();
foreach ($templates as $name => $template) {
$this->templates[$name] = $template;
}
$this->templates = $templates;
}
/**
@ -51,11 +47,7 @@ class Twig_Loader_Array implements Twig_LoaderInterface
}
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
* {@inheritdoc}
*/
public function getSource($name)
{
@ -68,11 +60,15 @@ class Twig_Loader_Array implements Twig_LoaderInterface
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
* {@inheritdoc}
*/
public function exists($name)
{
return isset($this->templates[(string) $name]);
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
@ -85,10 +81,7 @@ class Twig_Loader_Array implements Twig_LoaderInterface
}
/**
* Returns true if the template is still fresh.
*
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
* {@inheritdoc}
*/
public function isFresh($name, $time)
{

@ -0,0 +1,138 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2011 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Loads templates from other loaders.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
private $hasSourceCache = array();
protected $loaders = array();
/**
* Constructor.
*
* @param Twig_LoaderInterface[] $loaders An array of loader instances
*/
public function __construct(array $loaders = array())
{
foreach ($loaders as $loader) {
$this->addLoader($loader);
}
}
/**
* Adds a loader instance.
*
* @param Twig_LoaderInterface $loader A Loader instance
*/
public function addLoader(Twig_LoaderInterface $loader)
{
$this->loaders[] = $loader;
$this->hasSourceCache = array();
}
/**
* {@inheritdoc}
*/
public function getSource($name)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getSource($name);
} catch (Twig_Error_Loader $e) {
$exceptions[] = $e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions)));
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
$name = (string) $name;
if (isset($this->hasSourceCache[$name])) {
return $this->hasSourceCache[$name];
}
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface) {
if ($loader->exists($name)) {
return $this->hasSourceCache[$name] = true;
}
continue;
}
try {
$loader->getSource($name);
return $this->hasSourceCache[$name] = true;
} catch (Twig_Error_Loader $e) {
}
}
return $this->hasSourceCache[$name] = false;
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getCacheKey($name);
} catch (Twig_Error_Loader $e) {
$exceptions[] = get_class($loader).': '.$e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->isFresh($name, $time);
} catch (Twig_Error_Loader $e) {
$exceptions[] = get_class($loader).': '.$e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
}
}

@ -0,0 +1,236 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2009 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Loads template from the filesystem.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
/** 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 = array())
{
if ($paths) {
$this->setPaths($paths);
}
}
/**
* Returns the paths to the templates.
*
* @param string $namespace A path namespace
*
* @return array The array of paths where to look for templates
*/
public function getPaths($namespace = self::MAIN_NAMESPACE)
{
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
}
/**
* Returns the path namespaces.
*
* The main namespace is always defined.
*
* @return array The array of defined namespaces
*/
public function getNamespaces()
{
return array_keys($this->paths);
}
/**
* Sets the paths where templates are stored.
*
* @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 = self::MAIN_NAMESPACE)
{
if (!is_array($paths)) {
$paths = array($paths);
}
$this->paths[$namespace] = array();
foreach ($paths as $path) {
$this->addPath($path, $namespace);
}
}
/**
* Adds a path where templates are stored.
*
* @param string $path A path where to look for templates
* @param string $namespace A path name
*
* @throws Twig_Error_Loader
*/
public function addPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = array();
if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
}
$this->paths[$namespace][] = rtrim($path, '/\\');
}
/**
* Prepends a path where templates are stored.
*
* @param string $path A path where to look for templates
* @param string $namespace A path name
*
* @throws Twig_Error_Loader
*/
public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = array();
if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
}
$path = rtrim($path, '/\\');
if (!isset($this->paths[$namespace])) {
$this->paths[$namespace][] = $path;
} else {
array_unshift($this->paths[$namespace], $path);
}
}
/**
* {@inheritdoc}
*/
public function getSource($name)
{
return file_get_contents($this->findTemplate($name));
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
return $this->findTemplate($name);
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
$name = $this->normalizeName($name);
if (isset($this->cache[$name])) {
return true;
}
try {
$this->findTemplate($name);
return true;
} catch (Twig_Error_Loader $exception) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
return filemtime($this->findTemplate($name)) <= $time;
}
protected function findTemplate($name)
{
$name = $this->normalizeName($name);
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
$this->validateName($name);
list($namespace, $shortname) = $this->parseName($name);
if (!isset($this->paths[$namespace])) {
throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace));
}
foreach ($this->paths[$namespace] as $path) {
if (is_file($path.'/'.$shortname)) {
return $this->cache[$name] = $path.'/'.$shortname;
}
}
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])));
}
protected function parseName($name, $default = self::MAIN_NAMESPACE)
{
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);
$shortname = substr($name, $pos + 1);
return array($namespace, $shortname);
}
return array($default, $name);
}
protected function normalizeName($name)
{
return preg_replace('#/{2,}#', '/', strtr((string) $name, '\\', '/'));
}
protected function validateName($name)
{
if (false !== strpos($name, "\0")) {
throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
}
$name = ltrim($name, '/');
$parts = explode('/', $name);
$level = 0;
foreach ($parts as $part) {
if ('..' === $part) {
--$level;
} elseif ('.' !== $part) {
++$level;
}
if ($level < 0) {
throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
}
}
}
}

@ -12,22 +12,21 @@
/**
* Loads a template from a string.
*
* This loader should only be used for unit testing as it has many limitations
* (for instance, the include or extends tag does not make any sense for a string
* loader).
*
* When using this loader with a cache mechanism, you should know that a new cache
* key is generated each time a template content "changes" (the cache key being the
* source code of the template). If you don't want to see your cache grows out of
* control, you need to take care of clearing the old cache file by yourself.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Loader_String implements Twig_LoaderInterface
class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
* {@inheritdoc}
*/
public function getSource($name)
{
@ -35,11 +34,15 @@ class Twig_Loader_String implements Twig_LoaderInterface
}
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
* {@inheritdoc}
*/
public function exists($name)
{
return true;
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
@ -47,10 +50,7 @@ class Twig_Loader_String implements Twig_LoaderInterface
}
/**
* Returns true if the template is still fresh.
*
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
* {@inheritdoc}
*/
public function isFresh($name, $time)
{

@ -12,32 +12,31 @@
/**
* Interface all loaders must implement.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
interface Twig_LoaderInterface
{
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
* @param string $name The name of the template to load
*
* @return string The template source code
*
* @throws Twig_Error_Loader When $name is not found
*/
function getSource($name);
public function getSource($name);
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
* @param string $name The name of the template to load
*
* @return string The cache key
*
* @throws Twig_Error_Loader When $name is not found
*/
function getCacheKey($name);
public function getCacheKey($name);
/**
* Returns true if the template is still fresh.
@ -45,9 +44,9 @@ interface Twig_LoaderInterface
* @param string $name The template name
* @param timestamp $time The last modification time of the cached template
*
* @return Boolean true if the template is fresh, false otherwise
* @return bool true if the template is fresh, false otherwise
*
* @throws Twig_Error_Loader When $name is not found
*/
function isFresh($name, $time);
public function isFresh($name, $time);
}

@ -12,8 +12,7 @@
/**
* Marks a content as safe.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Markup implements Countable
{

@ -13,8 +13,7 @@
/**
* Represents a node in the AST.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Node implements Twig_NodeInterface
{
@ -31,7 +30,7 @@ class Twig_Node implements Twig_NodeInterface
*
* @param array $nodes An array of named nodes
* @param array $attributes An array of attributes (should not be nodes)
* @param integer $lineno The line number
* @param int $lineno The line number
* @param string $tag The tag name associated with the Node
*/
public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
@ -122,7 +121,7 @@ class Twig_Node implements Twig_NodeInterface
*
* @param string The attribute name
*
* @return Boolean true if the attribute is defined, false otherwise
* @return bool true if the attribute is defined, false otherwise
*/
public function hasAttribute($name)
{
@ -134,12 +133,12 @@ class Twig_Node implements Twig_NodeInterface
*
* @param string The attribute name
*
* @return mixed The attribute value
* @return mixed The attribute value
*/
public function getAttribute($name)
{
if (!array_key_exists($name, $this->attributes)) {
throw new Twig_Error_Runtime(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this)));
throw new LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this)));
}
return $this->attributes[$name];
@ -171,7 +170,7 @@ class Twig_Node implements Twig_NodeInterface
*
* @param string The node name
*
* @return Boolean true if the node with the given name exists, false otherwise
* @return bool true if the node with the given name exists, false otherwise
*/
public function hasNode($name)
{
@ -188,7 +187,7 @@ class Twig_Node implements Twig_NodeInterface
public function getNode($name)
{
if (!array_key_exists($name, $this->nodes)) {
throw new Twig_Error_Runtime(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this)));
throw new LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this)));
}
return $this->nodes[$name];

@ -18,8 +18,7 @@
*
* If autoescaping is disabled, then the value is false.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Node_AutoEscape extends Twig_Node
{

@ -13,8 +13,7 @@
/**
* Represents a block node.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Node_Block extends Twig_Node
{

@ -13,8 +13,7 @@
/**
* Represents a block call node.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInterface
{

@ -12,8 +12,7 @@
/**
* Represents a body node.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Node_Body extends Twig_Node
{

@ -12,8 +12,7 @@
/**
* Represents a do node.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Node_Do extends Twig_Node
{

@ -0,0 +1,38 @@
<?php
/*
* This file is part of Twig.
*
* (c) 2012 Fabien Potencier
*
* For the full copyright and license information, please view the LICENSE
* file that was distributed with this source code.
*/
/**
* Represents an embed node.
*
* @author Fabien Potencier <fabien@symfony.com>
*/
class Twig_Node_Embed extends Twig_Node_Include
{
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
{
parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
$this->setAttribute('filename', $filename);
$this->setAttribute('index', $index);
}
protected function addGetTemplate(Twig_Compiler $compiler)
{
$compiler
->write("\$this->env->loadTemplate(")
->string($this->getAttribute('filename'))
->raw(', ')
->string($this->getAttribute('index'))
->raw(")")
;
}
}

@ -13,8 +13,7 @@
/**
* Abstract class for all nodes that represents an expression.
*
* @package twig
* @author Fabien Potencier <fabien@symfony.com>
* @author Fabien Potencier <fabien@symfony.com>
*/
abstract class Twig_Node_Expression extends Twig_Node
{

@ -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('');
}
}

Some files were not shown because too many files have changed in this diff Show More

Loading…
Cancel
Save