mirror of
https://github.com/vichan-devel/vichan.git
synced 2024-11-23 23:20:57 +01:00
Merge branch 'master' of https://github.com/savetheinternet/Tinyboard
Conflicts: stylesheets/dark.css stylesheets/futaba+vichan.css stylesheets/miku.css stylesheets/stripes.css stylesheets/testorange.css stylesheets/wasabi.css
This commit is contained in:
commit
1ed45ebe9e
@ -417,9 +417,9 @@
|
||||
// Thumbnail extension, empty for inherited (png recommended)
|
||||
$config['thumb_ext'] = 'png';
|
||||
|
||||
// EXPERIMENTAL:
|
||||
// Maximum amount of frames to resize (more frames means more processing power). "1" means no animated thumbnails.
|
||||
// Requires $config['thumb_ext'] to be 'gif' $config['imagick'] to be enabled.
|
||||
// Requires $config['thumb_ext'] to be 'gif' and $config['thumb_method'] to be 'imagick', 'convert', or 'convert+gifsicle'.
|
||||
// Not respected by 'convert'; will just resize all frames if this is > 1.
|
||||
$config['thumb_keep_animation_frames'] = 1;
|
||||
|
||||
// Thumbnailing method:
|
||||
@ -431,8 +431,7 @@
|
||||
// PHP Imagick.
|
||||
// - 'convert+gifsicle' Same as above, with the exception of using `gifsicle` (command line application)
|
||||
// instead of `convert` for resizing gifs. It's faster and resulting animated gifs
|
||||
// have less artifacts than if resized with ImageMagick.
|
||||
|
||||
// have less artifacts than if resized with ImageMagick.
|
||||
$config['thumb_method'] = 'gd';
|
||||
|
||||
// Strip EXIF metadata from JPEG files
|
||||
@ -939,6 +938,9 @@
|
||||
// Automatically dismiss all reports regarding a thread when it is locked
|
||||
$config['mod']['dismiss_reports_on_lock'] = true;
|
||||
|
||||
// Replace ?/config with a simple text editor for editing inc/instance-config.php
|
||||
$config['mod']['config_editor_php'] = false;
|
||||
|
||||
// Probably best not to change these:
|
||||
if (!defined('JANITOR')) {
|
||||
define('JANITOR', 0, true);
|
||||
@ -1009,7 +1011,7 @@
|
||||
// Bypass flood check
|
||||
$config['mod']['flood'] = ADMIN;
|
||||
// Raw HTML posting
|
||||
$config['mod']['rawhtml'] = MOD;
|
||||
$config['mod']['rawhtml'] = ADMIN;
|
||||
|
||||
/* Administration */
|
||||
// View the report queue
|
||||
@ -1087,8 +1089,6 @@
|
||||
// Delete news entries
|
||||
$config['mod']['news_delete'] = ADMIN;
|
||||
|
||||
// View the current configuration
|
||||
$config['mod']['show_config'] = ADMIN;
|
||||
// Edit the current configuration (via web interface)
|
||||
$config['mod']['edit_config'] = ADMIN;
|
||||
|
||||
|
@ -173,6 +173,7 @@ class ImageImagick extends ImageBase {
|
||||
}
|
||||
}
|
||||
public function to($src) {
|
||||
global $config;
|
||||
if ($config['strip_exif']) {
|
||||
$this->image->stripImage();
|
||||
}
|
||||
@ -250,9 +251,9 @@ class ImageConvert extends ImageBase {
|
||||
|
||||
if (!$this->temp) {
|
||||
if ($config['strip_exif']) {
|
||||
shell_exec('convert ' . escapeshellarg($this->src) . ' -strip ' . escapeshellarg($src));
|
||||
shell_exec('convert ' . escapeshellarg($this->src) . ' -auto-orient -strip ' . escapeshellarg($src));
|
||||
} else {
|
||||
shell_exec('convert ' . escapeshellarg($this->src) . ' ' . escapeshellarg($src));
|
||||
shell_exec('convert ' . escapeshellarg($this->src) . ' -auto-orient ' . escapeshellarg($src));
|
||||
}
|
||||
} else {
|
||||
rename($this->temp, $src);
|
||||
@ -281,10 +282,13 @@ class ImageConvert extends ImageBase {
|
||||
|
||||
$quality = $config['thumb_quality'] * 10;
|
||||
|
||||
$config['thumb_keep_animation_frames'] = (int) $config['thumb_keep_animation_frames'];
|
||||
|
||||
if ($this->format == 'gif' && ($config['thumb_ext'] == 'gif' || $config['thumb_ext'] == '') && $config['thumb_keep_animation_frames'] > 1) {
|
||||
if ($this->gifsicle) {
|
||||
if (shell_exec("gifsicle --unoptimize -O2 --resize {$this->width}x{$this->height} < " .
|
||||
escapeshellarg($this->src . '') . " > " . escapeshellarg($this->temp)) || !file_exists($this->temp))
|
||||
escapeshellarg($this->src . '') . " \"#0-{$config['thumb_keep_animation_frames']}\" > " .
|
||||
escapeshellarg($this->temp)) || !file_exists($this->temp))
|
||||
error('Failed to resize image!');
|
||||
} else {
|
||||
if (shell_exec("convert -background transparent -filter Point -sample {$this->width}x{$this->height} +antialias -quality {$quality} " .
|
||||
|
@ -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 Boolean $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(new self, 'autoload'), true, $prepend);
|
||||
} else {
|
||||
spl_autoload_register(array(new self, '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
|
||||
{
|
||||
@ -22,6 +21,10 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
protected $source;
|
||||
protected $indentation;
|
||||
protected $env;
|
||||
protected $debugInfo;
|
||||
protected $sourceOffset;
|
||||
protected $sourceLine;
|
||||
protected $filename;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -31,6 +34,12 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
public function __construct(Twig_Environment $env)
|
||||
{
|
||||
$this->env = $env;
|
||||
$this->debugInfo = array();
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -56,8 +65,8 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
/**
|
||||
* Compiles a node.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to compile
|
||||
* @param integer $indent The current indentation
|
||||
* @param Twig_NodeInterface $node The node to compile
|
||||
* @param integer $indentation The current indentation
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
@ -65,8 +74,15 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
{
|
||||
$this->lastLine = null;
|
||||
$this->source = '';
|
||||
$this->sourceOffset = 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;
|
||||
@ -86,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
|
||||
*/
|
||||
@ -113,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);
|
||||
@ -123,7 +144,7 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
/**
|
||||
* Adds a quoted string to the compiled code.
|
||||
*
|
||||
* @param string $string The string
|
||||
* @param string $value The string
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
@ -137,19 +158,27 @@ 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
|
||||
*/
|
||||
public function repr($value)
|
||||
{
|
||||
if (is_int($value) || is_float($value)) {
|
||||
if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
|
||||
setlocale(LC_NUMERIC, 'C');
|
||||
}
|
||||
|
||||
$this->raw($value);
|
||||
} else if (null === $value) {
|
||||
|
||||
if (false !== $locale) {
|
||||
setlocale(LC_NUMERIC, $locale);
|
||||
}
|
||||
} elseif (null === $value) {
|
||||
$this->raw('null');
|
||||
} else if (is_bool($value)) {
|
||||
} elseif (is_bool($value)) {
|
||||
$this->raw($value ? 'true' : 'false');
|
||||
} else if (is_array($value)) {
|
||||
} elseif (is_array($value)) {
|
||||
$this->raw('array(');
|
||||
$i = 0;
|
||||
foreach ($value as $key => $value) {
|
||||
@ -178,17 +207,35 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
public function addDebugInfo(Twig_NodeInterface $node)
|
||||
{
|
||||
if ($node->getLine() != $this->lastLine) {
|
||||
$this->lastLine = $node->getLine();
|
||||
$this->write("// line {$node->getLine()}\n");
|
||||
|
||||
// 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();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDebugInfo()
|
||||
{
|
||||
return $this->debugInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indents the generated code.
|
||||
*
|
||||
* @param integer $indent The number of indentation to add
|
||||
* @param integer $step The number of indentation to add
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
@ -202,18 +249,19 @@ class Twig_Compiler implements Twig_CompilerInterface
|
||||
/**
|
||||
* Outdents the generated code.
|
||||
*
|
||||
* @param integer $indent The number of indentation to remove
|
||||
* @param integer $step The number of indentation to remove
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
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,24 @@
|
||||
/**
|
||||
* 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 2.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.2.0';
|
||||
const VERSION = '1.13.1';
|
||||
|
||||
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;
|
||||
@ -43,23 +43,23 @@ class Twig_Environment
|
||||
protected $templateClassPrefix = '__TwigTemplate_';
|
||||
protected $functionCallbacks;
|
||||
protected $filterCallbacks;
|
||||
protected $staging;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* 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.
|
||||
* If you don't provide the auto_reload option, it will be
|
||||
@ -68,14 +68,18 @@ class Twig_Environment
|
||||
* * 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())
|
||||
{
|
||||
@ -88,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();
|
||||
}
|
||||
|
||||
/**
|
||||
@ -250,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 integer $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.md5($this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -282,16 +288,28 @@ class Twig_Environment
|
||||
return $this->loadTemplate($name)->render($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a template.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param array $context An array of parameters to pass to the template
|
||||
*/
|
||||
public function display($name, array $context = array())
|
||||
{
|
||||
$this->loadTemplate($name)->display($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a template by name.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param string $name The template name
|
||||
* @param integer $index The index if it is an embedded template
|
||||
*
|
||||
* @return Twig_TemplateInterface A template instance representing the given template name
|
||||
*/
|
||||
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];
|
||||
@ -299,10 +317,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->loader->isFresh($name, filemtime($cache)))) {
|
||||
$this->writeCacheFile($cache, $this->compileSource($this->loader->getSource($name), $name));
|
||||
if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
|
||||
$this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name));
|
||||
}
|
||||
|
||||
require_once $cache;
|
||||
@ -316,6 +334,30 @@ class Twig_Environment
|
||||
return $this->loadedTemplates[$cls] = new $cls($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
* Besides checking the loader for freshness information,
|
||||
* this method also checks if the enabled extensions have
|
||||
* not changed.
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
public function isTemplateFresh($name, $time)
|
||||
{
|
||||
foreach ($this->extensions as $extension) {
|
||||
$r = new ReflectionObject($extension);
|
||||
if (filemtime($r->getFileName()) > $time) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->getLoader()->isFresh($name, $time);
|
||||
}
|
||||
|
||||
public function resolveTemplate($names)
|
||||
{
|
||||
if (!is_array($names)) {
|
||||
@ -510,6 +552,10 @@ class Twig_Environment
|
||||
*/
|
||||
public function getLoader()
|
||||
{
|
||||
if (null === $this->loader) {
|
||||
throw new LogicException('You must set a loader first.');
|
||||
}
|
||||
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
@ -520,7 +566,7 @@ class Twig_Environment
|
||||
*/
|
||||
public function setCharset($charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
$this->charset = strtoupper($charset);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -580,16 +626,28 @@ 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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]);
|
||||
}
|
||||
|
||||
@ -622,39 +680,46 @@ class Twig_Environment
|
||||
*/
|
||||
public function addTokenParser(Twig_TokenParserInterface $parser)
|
||||
{
|
||||
if (null === $this->parsers) {
|
||||
$this->getTokenParsers();
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException('Unable to add a token parser as extensions have already been initialized.');
|
||||
}
|
||||
|
||||
$this->parsers->addTokenParser($parser);
|
||||
$this->staging->addTokenParser($parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the registered Token Parsers.
|
||||
*
|
||||
* @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
|
||||
* @return Twig_TokenParserBrokerInterface A broker containing token parsers
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
if (null === $this->parsers) {
|
||||
$this->parsers = new Twig_TokenParserBroker;
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$parsers = $extension->getTokenParsers();
|
||||
foreach($parsers as $parser) {
|
||||
if ($parser instanceof Twig_TokenParserInterface) {
|
||||
$this->parsers->addTokenParser($parser);
|
||||
} else if ($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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets registered tags.
|
||||
*
|
||||
* Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
|
||||
*
|
||||
* @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
|
||||
*/
|
||||
public function getTags()
|
||||
{
|
||||
$tags = array();
|
||||
foreach ($this->getTokenParsers()->getParsers() as $parser) {
|
||||
if ($parser instanceof Twig_TokenParserInterface) {
|
||||
$tags[$parser->getTag()] = $parser;
|
||||
}
|
||||
}
|
||||
|
||||
return $tags;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Node Visitor.
|
||||
*
|
||||
@ -662,11 +727,11 @@ class Twig_Environment
|
||||
*/
|
||||
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
|
||||
{
|
||||
if (null === $this->visitors) {
|
||||
$this->getNodeVisitors();
|
||||
if ($this->extensionInitialized) {
|
||||
throw new LogicException('Unable to add a node visitor as extensions have already been initialized.', $extension->getName());
|
||||
}
|
||||
|
||||
$this->visitors[] = $visitor;
|
||||
$this->staging->addNodeVisitor($visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -676,11 +741,8 @@ class Twig_Environment
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
if (null === $this->visitors) {
|
||||
$this->visitors = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->visitors = array_merge($this->visitors, $extension->getNodeVisitors());
|
||||
}
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->visitors;
|
||||
@ -689,16 +751,25 @@ class Twig_Environment
|
||||
/**
|
||||
* Registers a Filter.
|
||||
*
|
||||
* @param string $name The filter name
|
||||
* @param Twig_FilterInterface $visitor 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)
|
||||
{
|
||||
if (null === $this->filters) {
|
||||
$this->loadFilters();
|
||||
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');
|
||||
}
|
||||
|
||||
$this->filters[$name] = $filter;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -709,18 +780,31 @@ 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->loadFilters();
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->filters[$name])) {
|
||||
return $this->filters[$name];
|
||||
}
|
||||
|
||||
foreach ($this->filters as $pattern => $filter) {
|
||||
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
|
||||
|
||||
if ($count) {
|
||||
if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
|
||||
array_shift($matches);
|
||||
$filter->setArguments($matches);
|
||||
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->filterCallbacks as $callback) {
|
||||
if (false !== $filter = call_user_func($callback, $name)) {
|
||||
return $filter;
|
||||
@ -738,29 +822,43 @@ class Twig_Environment
|
||||
/**
|
||||
* Gets the registered Filters.
|
||||
*
|
||||
* Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback.
|
||||
*
|
||||
* @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
|
||||
*
|
||||
* @see registerUndefinedFilterCallback
|
||||
*/
|
||||
protected function loadFilters()
|
||||
public function getFilters()
|
||||
{
|
||||
$this->filters = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->filters = array_merge($this->filters, $extension->getFilters());
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Test.
|
||||
*
|
||||
* @param string $name The test name
|
||||
* @param Twig_TestInterface $visitor 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)
|
||||
{
|
||||
if (null === $this->tests) {
|
||||
$this->getTests();
|
||||
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');
|
||||
}
|
||||
|
||||
$this->tests[$name] = $test;
|
||||
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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -770,29 +868,55 @@ class Twig_Environment
|
||||
*/
|
||||
public function getTests()
|
||||
{
|
||||
if (null === $this->tests) {
|
||||
$this->tests = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->tests = array_merge($this->tests, $extension->getTests());
|
||||
}
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Function.
|
||||
* Gets a test by name.
|
||||
*
|
||||
* @param string $name The function name
|
||||
* @param Twig_FunctionInterface $function A Twig_FunctionInterface instance
|
||||
* @param string $name The test name
|
||||
*
|
||||
* @return Twig_Test|false A Twig_Test instance or false if the test does not exist
|
||||
*/
|
||||
public function addFunction($name, Twig_FunctionInterface $function)
|
||||
public function getTest($name)
|
||||
{
|
||||
if (null === $this->functions) {
|
||||
$this->loadFunctions();
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
$this->functions[$name] = $function;
|
||||
if (isset($this->tests[$name])) {
|
||||
return $this->tests[$name];
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Function.
|
||||
*
|
||||
* @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, $function = 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);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -803,18 +927,31 @@ 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->loadFunctions();
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->functions[$name])) {
|
||||
return $this->functions[$name];
|
||||
}
|
||||
|
||||
foreach ($this->functions as $pattern => $function) {
|
||||
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
|
||||
|
||||
if ($count) {
|
||||
if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
|
||||
array_shift($matches);
|
||||
$function->setArguments($matches);
|
||||
|
||||
return $function;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->functionCallbacks as $callback) {
|
||||
if (false !== $function = call_user_func($callback, $name)) {
|
||||
return $function;
|
||||
@ -829,27 +966,53 @@ class Twig_Environment
|
||||
$this->functionCallbacks[] = $callable;
|
||||
}
|
||||
|
||||
protected function loadFunctions()
|
||||
/**
|
||||
* Gets registered functions.
|
||||
*
|
||||
* Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
|
||||
*
|
||||
* @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances
|
||||
*
|
||||
* @see registerUndefinedFunctionCallback
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
$this->functions = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->functions = array_merge($this->functions, $extension->getFunctions());
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
if (null === $this->globals) {
|
||||
$this->getGlobals();
|
||||
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));
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
$this->globals[$name] = $value;
|
||||
if ($this->extensionInitialized || $this->runtimeInitialized) {
|
||||
// update the value
|
||||
$this->globals[$name] = $value;
|
||||
} else {
|
||||
$this->staging->addGlobal($name, $value);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -859,16 +1022,37 @@ class Twig_Environment
|
||||
*/
|
||||
public function getGlobals()
|
||||
{
|
||||
if (!$this->runtimeInitialized && !$this->extensionInitialized) {
|
||||
return $this->initGlobals();
|
||||
}
|
||||
|
||||
if (null === $this->globals) {
|
||||
$this->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.
|
||||
*
|
||||
@ -876,8 +1060,8 @@ class Twig_Environment
|
||||
*/
|
||||
public function getUnaryOperators()
|
||||
{
|
||||
if (null === $this->unaryOperators) {
|
||||
$this->initOperators();
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->unaryOperators;
|
||||
@ -890,24 +1074,121 @@ class Twig_Environment
|
||||
*/
|
||||
public function getBinaryOperators()
|
||||
{
|
||||
if (null === $this->binaryOperators) {
|
||||
$this->initOperators();
|
||||
if (!$this->extensionInitialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->binaryOperators;
|
||||
}
|
||||
|
||||
protected function initOperators()
|
||||
public function computeAlternatives($name, $items)
|
||||
{
|
||||
$this->unaryOperators = array();
|
||||
$this->binaryOperators = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$operators = $extension->getOperators();
|
||||
$alternatives = array();
|
||||
foreach ($items as $item) {
|
||||
$lev = levenshtein($name, $item);
|
||||
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
|
||||
$alternatives[$item] = $lev;
|
||||
}
|
||||
}
|
||||
asort($alternatives);
|
||||
|
||||
if (!$operators) {
|
||||
continue;
|
||||
return array_keys($alternatives);
|
||||
}
|
||||
|
||||
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->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)));
|
||||
}
|
||||
@ -919,20 +1200,25 @@ class Twig_Environment
|
||||
|
||||
protected function writeCacheFile($file, $content)
|
||||
{
|
||||
if (!is_dir(dirname($file))) {
|
||||
mkdir(dirname($file), 0777, true);
|
||||
$dir = dirname($file);
|
||||
if (!is_dir($dir)) {
|
||||
if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
|
||||
throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir));
|
||||
}
|
||||
} elseif (!is_writable($dir)) {
|
||||
throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir));
|
||||
}
|
||||
|
||||
$tmpFile = tempnam(dirname($file), 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));
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,24 @@
|
||||
/**
|
||||
* Twig base exception.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* 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
|
||||
{
|
||||
@ -25,6 +41,15 @@ class Twig_Error extends Exception
|
||||
/**
|
||||
* 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 integer $lineno The template line where the error occurred
|
||||
* @param string $filename The template file name where the error occurred
|
||||
@ -32,22 +57,23 @@ class Twig_Error extends Exception
|
||||
*/
|
||||
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
|
||||
{
|
||||
if (-1 === $lineno || null === $filename) {
|
||||
list($lineno, $filename) = $this->findTemplateInfo(null !== $previous ? $previous : $this, $lineno, $filename);
|
||||
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();
|
||||
|
||||
if (version_compare(PHP_VERSION, '5.3.0', '<')) {
|
||||
$this->previous = $previous;
|
||||
parent::__construct($this->message);
|
||||
} else {
|
||||
parent::__construct($this->message, 0, $previous);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -104,13 +130,21 @@ class Twig_Error extends Exception
|
||||
$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
|
||||
* @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)
|
||||
{
|
||||
@ -131,11 +165,16 @@ class Twig_Error extends Exception
|
||||
$dot = true;
|
||||
}
|
||||
|
||||
if (null !== $this->filename) {
|
||||
$this->message .= sprintf(' in %s', json_encode($this->filename));
|
||||
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 >= 0) {
|
||||
if ($this->lineno && $this->lineno >= 0) {
|
||||
$this->message .= sprintf(' at line %d', $this->lineno);
|
||||
}
|
||||
|
||||
@ -144,52 +183,57 @@ class Twig_Error extends Exception
|
||||
}
|
||||
}
|
||||
|
||||
protected function findTemplateInfo(Exception $e, $currentLine, $currentFile)
|
||||
protected function guessTemplateInfo()
|
||||
{
|
||||
if (!function_exists('token_get_all')) {
|
||||
return array($currentLine, $currentFile);
|
||||
$template = null;
|
||||
|
||||
if (version_compare(phpversion(), '5.3.6', '>=')) {
|
||||
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
} else {
|
||||
$backtrace = debug_backtrace();
|
||||
}
|
||||
|
||||
$traces = $e->getTrace();
|
||||
foreach ($traces as $i => $trace) {
|
||||
if (!isset($trace['class']) || 'Twig_Template' === $trace['class']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$r = new ReflectionClass($trace['class']);
|
||||
if (!$r->implementsInterface('Twig_TemplateInterface')) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!is_file($r->getFilename())) {
|
||||
// probably an eval()'d code
|
||||
return array($currentLine, $currentFile);
|
||||
}
|
||||
|
||||
if (0 === $i) {
|
||||
$line = $e->getLine();
|
||||
} else {
|
||||
$line = isset($traces[$i - 1]['line']) ? $traces[$i - 1]['line'] : -log(0);
|
||||
}
|
||||
|
||||
$tokens = token_get_all(file_get_contents($r->getFilename()));
|
||||
$templateline = -1;
|
||||
$template = null;
|
||||
foreach ($tokens as $token) {
|
||||
if (isset($token[2]) && $token[2] >= $line) {
|
||||
return array($templateline, $template);
|
||||
}
|
||||
|
||||
if (T_COMMENT === $token[0] && null === $template && preg_match('#/\* +(.+) +\*/#', $token[1], $match)) {
|
||||
$template = $match[1];
|
||||
} elseif (T_COMMENT === $token[0] && preg_match('#^//\s*line (\d+)\s*$#', $token[1], $match)) {
|
||||
$templateline = $match[1];
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
|
||||
if (null === $this->filename || $this->filename == $trace['object']->getTemplateName()) {
|
||||
$template = $trace['object'];
|
||||
}
|
||||
}
|
||||
|
||||
return array($currentLine, $template);
|
||||
}
|
||||
|
||||
return array($currentLine, $currentFile);
|
||||
// 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();
|
||||
|
||||
$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;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,20 @@
|
||||
/**
|
||||
* Exception thrown when an error occurs during template loading.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* 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
|
||||
{
|
||||
|
28
inc/lib/Twig/ExistsLoaderInterface.php
Normal file
28
inc/lib/Twig/ExistsLoaderInterface.php
Normal file
@ -0,0 +1,28 @@
|
||||
<?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 2.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 boolean 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
|
||||
{
|
||||
@ -89,9 +88,19 @@ class Twig_ExpressionParser
|
||||
{
|
||||
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();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$expr2 = $this->parseExpression();
|
||||
if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$this->parser->getStream()->next();
|
||||
$expr3 = $this->parseExpression();
|
||||
} else {
|
||||
$expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
} else {
|
||||
$this->parser->getStream()->next();
|
||||
$expr2 = $expr;
|
||||
$expr3 = $this->parseExpression();
|
||||
}
|
||||
|
||||
$expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
@ -143,31 +152,67 @@ class Twig_ExpressionParser
|
||||
break;
|
||||
|
||||
case Twig_Token::NUMBER_TYPE:
|
||||
case Twig_Token::STRING_TYPE:
|
||||
$this->parser->getStream()->next();
|
||||
$node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
break;
|
||||
|
||||
case Twig_Token::STRING_TYPE:
|
||||
case Twig_Token::INTERPOLATION_START_TYPE:
|
||||
$node = $this->parseStringExpression();
|
||||
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->getLine()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parsePostfixExpression($node);
|
||||
}
|
||||
|
||||
public function parseStringExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$nodes = array();
|
||||
// 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();
|
||||
$nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
$nextCanBeString = false;
|
||||
} elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) {
|
||||
$stream->next();
|
||||
$nodes[] = $this->parseExpression();
|
||||
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
|
||||
$nextCanBeString = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$expr = array_shift($nodes);
|
||||
foreach ($nodes as $node) {
|
||||
$expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine());
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
public function parseArrayExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
|
||||
$elements = array();
|
||||
|
||||
$node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
|
||||
if (!empty($elements)) {
|
||||
if (!$first) {
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
@ -175,21 +220,24 @@ class Twig_ExpressionParser
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$elements[] = $this->parseExpression();
|
||||
$node->addElement($this->parseExpression());
|
||||
}
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
|
||||
|
||||
return new Twig_Node_Expression_Array($elements, $stream->getCurrent()->getLine());
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseHashExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
|
||||
$elements = array();
|
||||
|
||||
$node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
|
||||
if (!empty($elements)) {
|
||||
if (!$first) {
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
@ -197,19 +245,33 @@ class Twig_ExpressionParser
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
if (!$stream->test(Twig_Token::STRING_TYPE) && !$stream->test(Twig_Token::NUMBER_TYPE)) {
|
||||
// a hash key can be:
|
||||
//
|
||||
// * a number -- 12
|
||||
// * 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();
|
||||
$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 or a number (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->getLine()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
|
||||
}
|
||||
|
||||
$key = $stream->next()->getValue();
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
|
||||
$elements[$key] = $this->parseExpression();
|
||||
$value = $this->parseExpression();
|
||||
|
||||
$node->addElement($value, $key);
|
||||
}
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
|
||||
|
||||
return new Twig_Node_Expression_Array($elements, $stream->getCurrent()->getLine());
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parsePostfixExpression($node)
|
||||
@ -234,43 +296,56 @@ 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()) {
|
||||
throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend another one is forbidden', $line);
|
||||
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, $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 attribute)', $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);
|
||||
default:
|
||||
if (null !== $alias = $this->parser->getImportedFunction($name)) {
|
||||
return new Twig_Node_Expression_GetAttr($alias['node'], new Twig_Node_Expression_Constant($alias['name'], $line), $args, Twig_TemplateInterface::METHOD_CALL, $line);
|
||||
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
|
||||
$arguments = new Twig_Node_Expression_Array(array(), $line);
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
|
||||
$node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line);
|
||||
$node->setAttribute('safe', true);
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_Function($name, $args, $line);
|
||||
$args = $this->parseArguments(true);
|
||||
$class = $this->getFunctionNodeClass($name, $line);
|
||||
|
||||
return new $class($name, $args, $line);
|
||||
}
|
||||
}
|
||||
|
||||
public function parseSubscriptExpression($node)
|
||||
{
|
||||
$token = $this->parser->getStream()->next();
|
||||
$stream = $this->parser->getStream();
|
||||
$token = $stream->next();
|
||||
$lineno = $token->getLine();
|
||||
$arguments = new Twig_Node();
|
||||
$arguments = new Twig_Node_Expression_Array(array(), $lineno);
|
||||
$type = Twig_TemplateInterface::ANY_CALL;
|
||||
if ($token->getValue() == '.') {
|
||||
$token = $this->parser->getStream()->next();
|
||||
$token = $stream->next();
|
||||
if (
|
||||
$token->getType() == Twig_Token::NAME_TYPE
|
||||
||
|
||||
@ -280,20 +355,60 @@ class Twig_ExpressionParser
|
||||
) {
|
||||
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
|
||||
|
||||
if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$type = Twig_TemplateInterface::METHOD_CALL;
|
||||
$arguments = $this->parseArguments();
|
||||
} else {
|
||||
$arguments = new Twig_Node();
|
||||
foreach ($this->parseArguments() as $n) {
|
||||
$arguments->addElement($n);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throw new Twig_Error_Syntax('Expected name or number', $lineno);
|
||||
throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
|
||||
}
|
||||
|
||||
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_TemplateInterface::ARRAY_CALL;
|
||||
|
||||
$arg = $this->parseExpression();
|
||||
$this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ']');
|
||||
// slice?
|
||||
$slice = false;
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$arg = new Twig_Node_Expression_Constant(0, $token->getLine());
|
||||
} else {
|
||||
$arg = $this->parseExpression();
|
||||
}
|
||||
|
||||
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$stream->next();
|
||||
}
|
||||
|
||||
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', $token->getLine());
|
||||
$arguments = new Twig_Node(array($arg, $length));
|
||||
$filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine());
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
|
||||
@ -315,10 +430,12 @@ class Twig_ExpressionParser
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
|
||||
$arguments = new Twig_Node();
|
||||
} else {
|
||||
$arguments = $this->parseArguments();
|
||||
$arguments = $this->parseArguments(true);
|
||||
}
|
||||
|
||||
$node = new Twig_Node_Expression_Filter($node, $name, $arguments, $token->getLine(), $tag);
|
||||
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
|
||||
|
||||
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
|
||||
|
||||
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) {
|
||||
break;
|
||||
@ -330,17 +447,62 @@ class Twig_ExpressionParser
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseArguments()
|
||||
/**
|
||||
* Parses arguments.
|
||||
*
|
||||
* @param Boolean $namedArguments Whether to allow named arguments or not
|
||||
* @param Boolean $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 && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
|
||||
$token = $stream->next();
|
||||
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');
|
||||
|
||||
@ -353,7 +515,7 @@ 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());
|
||||
|
||||
@ -379,4 +541,60 @@ class Twig_ExpressionParser
|
||||
|
||||
return new Twig_Node($targets);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
$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 $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter';
|
||||
}
|
||||
|
||||
// checks that the node only contains "constant" elements
|
||||
protected function checkConstantExpression(Twig_NodeInterface $node)
|
||||
{
|
||||
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 true;
|
||||
}
|
||||
}
|
||||
|
@ -82,9 +82,9 @@ abstract class Twig_Extension implements Twig_ExtensionInterface
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of global functions to add to the existing list.
|
||||
* Returns a list of global variables to add to the existing list.
|
||||
*
|
||||
* @return array An array of global functions
|
||||
* @return array An array of global variables
|
||||
*/
|
||||
public function getGlobals()
|
||||
{
|
||||
|
File diff suppressed because it is too large
Load Diff
71
inc/lib/Twig/Extension/Debug.php
Normal file
71
inc/lib/Twig/Extension/Debug.php
Normal file
@ -0,0 +1,71 @@
|
||||
<?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.
|
||||
*/
|
||||
class Twig_Extension_Debug extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Returns a list of global functions to add to the existing list.
|
||||
*
|
||||
* @return array An array of global functions
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
// 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(
|
||||
new Twig_SimpleFunction('dump', 'twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'debug';
|
||||
}
|
||||
}
|
||||
|
||||
function twig_var_dump(Twig_Environment $env, $context)
|
||||
{
|
||||
if (!$env->isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
$count = func_num_args();
|
||||
if (2 === $count) {
|
||||
$vars = array();
|
||||
foreach ($context as $key => $value) {
|
||||
if (!$value instanceof Twig_Template) {
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
var_dump($vars);
|
||||
} else {
|
||||
for ($i = 2; $i < $count; $i++) {
|
||||
var_dump(func_get_arg($i));
|
||||
}
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
@ -10,11 +10,11 @@
|
||||
*/
|
||||
class Twig_Extension_Escaper extends Twig_Extension
|
||||
{
|
||||
protected $autoescape;
|
||||
protected $defaultStrategy;
|
||||
|
||||
public function __construct($autoescape = true)
|
||||
public function __construct($defaultStrategy = 'html')
|
||||
{
|
||||
$this->autoescape = $autoescape;
|
||||
$this->setDefaultStrategy($defaultStrategy);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -45,13 +45,44 @@ class Twig_Extension_Escaper extends Twig_Extension
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
'raw' => new Twig_Filter_Function('twig_raw_filter', array('is_safe' => array('all'))),
|
||||
new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))),
|
||||
);
|
||||
}
|
||||
|
||||
public function isGlobal()
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
return $this->autoescape;
|
||||
// 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;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -74,4 +105,3 @@ function twig_raw_filter($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
||||
|
113
inc/lib/Twig/Extension/Staging.php
Normal file
113
inc/lib/Twig/Extension/Staging.php
Normal file
@ -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';
|
||||
}
|
||||
}
|
64
inc/lib/Twig/Extension/StringLoader.php
Normal file
64
inc/lib/Twig/Extension/StringLoader.php
Normal file
@ -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)
|
||||
{
|
||||
static $loader;
|
||||
|
||||
if (null === $loader) {
|
||||
$loader = new Twig_Loader_String();
|
||||
}
|
||||
|
||||
$current = $env->getLoader();
|
||||
$env->setLoader($loader);
|
||||
try {
|
||||
$template = $env->loadTemplate($template);
|
||||
} 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
|
||||
*/
|
||||
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 functions to add to the existing list.
|
||||
* Returns a list of global variables to add to the existing list.
|
||||
*
|
||||
* @return array An array of global functions
|
||||
* @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();
|
||||
}
|
||||
|
@ -28,7 +28,7 @@ class Twig_Extensions_Extension_I18n extends Twig_Extension
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
'trans' => new Twig_Filter_Function('gettext'),
|
||||
new Twig_SimpleFilter('trans', 'gettext'),
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -9,24 +9,23 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return Array(
|
||||
'filesize' => new Twig_Filter_Function('format_bytes'),
|
||||
'truncate' => new Twig_Filter_Function('twig_truncate_filter'),
|
||||
'truncate_body' => new Twig_Filter_Function('truncate'),
|
||||
'extension' => new Twig_Filter_Function('twig_extension_filter'),
|
||||
'sprintf' => new Twig_Filter_Function('sprintf'),
|
||||
'capcode' => new Twig_Filter_Function('capcode'),
|
||||
'hasPermission' => new Twig_Filter_Function('twig_hasPermission_filter'),
|
||||
'date' => new Twig_Filter_Function('twig_date_filter'),
|
||||
'poster_id' => new Twig_Filter_Function('poster_id'),
|
||||
'remove_whitespace' => new Twig_Filter_Function('twig_remove_whitespace_filter'),
|
||||
'count' => new Twig_Filter_Function('count'),
|
||||
'ago' => new Twig_Filter_Function('ago'),
|
||||
'until' => new Twig_Filter_Function('until'),
|
||||
'split' => new Twig_Filter_Function('twig_split_filter'),
|
||||
'push' => new Twig_Filter_Function('twig_push_filter'),
|
||||
'bidi_cleanup' => new Twig_Filter_Function('bidi_cleanup'),
|
||||
'addslashes' => new Twig_Filter_Function('addslashes')
|
||||
return array(
|
||||
new Twig_SimpleFilter('filesize', 'format_bytes'),
|
||||
new Twig_SimpleFilter('truncate', 'twig_truncate_filter'),
|
||||
new Twig_SimpleFilter('truncate_body', 'truncate'),
|
||||
new Twig_SimpleFilter('extension', 'twig_extension_filter'),
|
||||
new Twig_SimpleFilter('sprintf', 'sprintf'),
|
||||
new Twig_SimpleFilter('capcode', 'capcode'),
|
||||
new Twig_SimpleFilter('hasPermission', 'twig_hasPermission_filter'),
|
||||
new Twig_SimpleFilter('date', 'twig_date_filter'),
|
||||
new Twig_SimpleFilter('poster_id', 'poster_id'),
|
||||
new Twig_SimpleFilter('remove_whitespace', 'twig_remove_whitespace_filter'),
|
||||
new Twig_SimpleFilter('count', 'count'),
|
||||
new Twig_SimpleFilter('ago', 'ago'),
|
||||
new Twig_SimpleFilter('until', 'until'),
|
||||
new Twig_SimpleFilter('push', 'twig_push_filter'),
|
||||
new Twig_SimpleFilter('bidi_cleanup', 'bidi_cleanup'),
|
||||
new Twig_SimpleFilter('addslashes', 'addslashes')
|
||||
);
|
||||
}
|
||||
|
||||
@ -37,12 +36,12 @@ class Twig_Extensions_Extension_Tinyboard extends Twig_Extension
|
||||
*/
|
||||
public function getFunctions()
|
||||
{
|
||||
return Array(
|
||||
'time' => new Twig_Filter_Function('time'),
|
||||
'floor' => new Twig_Filter_Function('floor'),
|
||||
'timezone' => new Twig_Filter_Function('twig_timezone_function'),
|
||||
'hiddenInputs' => new Twig_Filter_Function('hiddenInputs'),
|
||||
'hiddenInputsHash' => new Twig_Filter_Function('hiddenInputsHash'),
|
||||
return array(
|
||||
new Twig_SimpleFunction('time', 'time'),
|
||||
new Twig_SimpleFunction('floor', 'floor'),
|
||||
new Twig_SimpleFunction('timezone', 'twig_timezone_function'),
|
||||
new Twig_SimpleFunction('hiddenInputs', 'hiddenInputs'),
|
||||
new Twig_SimpleFunction('hiddenInputsHash', 'hiddenInputsHash'),
|
||||
);
|
||||
}
|
||||
|
||||
@ -61,10 +60,6 @@ function twig_timezone_function() {
|
||||
return 'Z';
|
||||
}
|
||||
|
||||
function twig_split_filter($str, $delim) {
|
||||
return explode($delim, $str);
|
||||
}
|
||||
|
||||
function twig_push_filter($array, $value) {
|
||||
array_push($array, $value);
|
||||
return $array;
|
||||
|
@ -94,22 +94,25 @@ class Twig_Extensions_Node_Trans extends Twig_Node
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw(');');
|
||||
$compiler->raw(");\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileString(Twig_NodeInterface $body)
|
||||
{
|
||||
if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant) {
|
||||
if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant || $body instanceof Twig_Node_Expression_TempName) {
|
||||
return array($body, array());
|
||||
}
|
||||
|
||||
$vars = array();
|
||||
|
||||
if (count($body)) {
|
||||
$msg = '';
|
||||
|
||||
foreach ($body as $node) {
|
||||
if (get_class($node) === 'Twig_Node' && $node->getNode(0) instanceof Twig_Node_SetTemp) {
|
||||
$node = $node->getNode(1);
|
||||
}
|
||||
|
||||
if ($node instanceof Twig_Node_Print) {
|
||||
$n = $node->getNode('expr');
|
||||
while ($n instanceof Twig_Node_Expression_Filter) {
|
||||
@ -127,4 +130,4 @@ class Twig_Extensions_Node_Trans extends Twig_Node
|
||||
|
||||
return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $body->getLine()))), $vars);
|
||||
}
|
||||
}
|
||||
}
|
@ -43,12 +43,12 @@ class Twig_Extensions_TokenParser_Trans extends Twig_TokenParser
|
||||
return new Twig_Extensions_Node_Trans($body, $plural, $count, $lineno, $this->getTag());
|
||||
}
|
||||
|
||||
public function decideForFork($token)
|
||||
public function decideForFork(Twig_Token $token)
|
||||
{
|
||||
return $token->test(array('plural', 'endtrans'));
|
||||
}
|
||||
|
||||
public function decideForEnd($token)
|
||||
public function decideForEnd(Twig_Token $token)
|
||||
{
|
||||
return $token->test('endtrans');
|
||||
}
|
||||
@ -77,4 +77,4 @@ class Twig_Extensions_TokenParser_Trans extends Twig_TokenParser
|
||||
throw new Twig_Error_Syntax(sprintf('The text to be translated with "trans" can only contain references to simple variables'), $lineno);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -12,12 +12,15 @@
|
||||
/**
|
||||
* 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();
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
@ -25,9 +28,21 @@ abstract class Twig_Filter implements Twig_FilterInterface
|
||||
'needs_environment' => false,
|
||||
'needs_context' => false,
|
||||
'pre_escape' => null,
|
||||
'preserves_safety' => null,
|
||||
'callable' => null,
|
||||
), $options);
|
||||
}
|
||||
|
||||
public function setArguments($arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function needsEnvironment()
|
||||
{
|
||||
return $this->options['needs_environment'];
|
||||
@ -47,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;
|
||||
|
39
inc/lib/Twig/Filter/Node.php
Normal file
39
inc/lib/Twig/Filter/Node.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template filter as a node.
|
||||
*
|
||||
* 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
|
||||
{
|
||||
protected $class;
|
||||
|
||||
public function __construct($class, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
}
|
||||
}
|
23
inc/lib/Twig/FilterCallableInterface.php
Normal file
23
inc/lib/Twig/FilterCallableInterface.php
Normal file
@ -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();
|
||||
}
|
@ -12,8 +12,10 @@
|
||||
/**
|
||||
* 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)
|
||||
*/
|
||||
interface Twig_FilterInterface
|
||||
{
|
||||
@ -22,13 +24,19 @@ interface Twig_FilterInterface
|
||||
*
|
||||
* @return string The PHP code for the filter
|
||||
*/
|
||||
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 getPreEscape();
|
||||
public function getPreservesSafety();
|
||||
|
||||
public function getPreEscape();
|
||||
|
||||
public function setArguments($arguments);
|
||||
|
||||
public function getArguments();
|
||||
}
|
||||
|
@ -12,21 +12,35 @@
|
||||
/**
|
||||
* 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();
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = array_merge(array(
|
||||
'needs_environment' => false,
|
||||
'needs_context' => false,
|
||||
'callable' => null,
|
||||
), $options);
|
||||
}
|
||||
|
||||
public function setArguments($arguments)
|
||||
{
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
public function getArguments()
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function needsEnvironment()
|
||||
{
|
||||
return $this->options['needs_environment'];
|
||||
@ -49,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;
|
||||
|
39
inc/lib/Twig/Function/Node.php
Normal file
39
inc/lib/Twig/Function/Node.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template function as a node.
|
||||
*
|
||||
* 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_Function
|
||||
{
|
||||
protected $class;
|
||||
|
||||
public function __construct($class, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->class = $class;
|
||||
}
|
||||
|
||||
public function getClass()
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
}
|
||||
}
|
23
inc/lib/Twig/FunctionCallableInterface.php
Normal file
23
inc/lib/Twig/FunctionCallableInterface.php
Normal file
@ -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,11 +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);
|
||||
|
||||
public function setArguments($arguments);
|
||||
|
||||
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
|
||||
{
|
||||
@ -24,21 +23,28 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
protected $lineno;
|
||||
protected $end;
|
||||
protected $state;
|
||||
protected $states;
|
||||
protected $brackets;
|
||||
|
||||
protected $env;
|
||||
protected $filename;
|
||||
protected $options;
|
||||
protected $operatorRegex;
|
||||
protected $regexes;
|
||||
protected $position;
|
||||
protected $positions;
|
||||
protected $currentVarBlockLine;
|
||||
|
||||
const STATE_DATA = 0;
|
||||
const STATE_BLOCK = 1;
|
||||
const STATE_VAR = 2;
|
||||
const STATE_DATA = 0;
|
||||
const STATE_BLOCK = 1;
|
||||
const STATE_VAR = 2;
|
||||
const STATE_STRING = 3;
|
||||
const STATE_INTERPOLATION = 4;
|
||||
|
||||
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
|
||||
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
|
||||
const REGEX_STRING = '/"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
||||
const PUNCTUATION = '()[]{}?:.,|';
|
||||
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
|
||||
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
|
||||
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
||||
const REGEX_DQ_STRING_DELIM = '/"/A';
|
||||
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
|
||||
const PUNCTUATION = '()[]{}?:.,|';
|
||||
|
||||
public function __construct(Twig_Environment $env, array $options = array())
|
||||
{
|
||||
@ -49,14 +55,28 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
'tag_block' => array('{%', '%}'),
|
||||
'tag_variable' => array('{{', '}}'),
|
||||
'whitespace_trim' => '-',
|
||||
'interpolation' => array('#{', '}'),
|
||||
), $options);
|
||||
|
||||
$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*(?: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|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',
|
||||
'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@ -74,7 +94,13 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
$this->end = strlen($this->code);
|
||||
$this->tokens = array();
|
||||
$this->state = self::STATE_DATA;
|
||||
$this->states = array();
|
||||
$this->brackets = array();
|
||||
$this->position = -1;
|
||||
|
||||
// find all token starts in one go
|
||||
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
|
||||
$this->positions = $matches;
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
// dispatch to the lexing functions depending
|
||||
@ -91,6 +117,14 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
case self::STATE_VAR:
|
||||
$this->lexVar();
|
||||
break;
|
||||
|
||||
case self::STATE_STRING:
|
||||
$this->lexString();
|
||||
break;
|
||||
|
||||
case self::STATE_INTERPOLATION:
|
||||
$this->lexInterpolation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@ -110,77 +144,66 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
|
||||
protected function lexData()
|
||||
{
|
||||
$pos = $this->end;
|
||||
$append = '';
|
||||
|
||||
// Find the first token after the cursor
|
||||
foreach (array('tag_comment', 'tag_variable', 'tag_block') as $type) {
|
||||
$tmpPos = strpos($this->code, $this->options[$type][0], $this->cursor);
|
||||
if (false !== $tmpPos && $tmpPos < $pos) {
|
||||
$trimBlock = false;
|
||||
$append = '';
|
||||
$pos = $tmpPos;
|
||||
$token = $this->options[$type][0];
|
||||
if (strpos($this->code, $this->options['whitespace_trim'], $pos) === ($pos + strlen($token))) {
|
||||
$trimBlock = true;
|
||||
$append = $this->options['whitespace_trim'];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// if no matches are left we return the rest of the template as simple text token
|
||||
if ($pos === $this->end) {
|
||||
if ($this->position == count($this->positions[0]) - 1) {
|
||||
$this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor));
|
||||
$this->cursor = $this->end;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first token after the current cursor
|
||||
$position = $this->positions[0][++$this->position];
|
||||
while ($position[1] < $this->cursor) {
|
||||
if ($this->position == count($this->positions[0]) - 1) {
|
||||
return;
|
||||
}
|
||||
$position = $this->positions[0][++$this->position];
|
||||
}
|
||||
|
||||
// push the template text first
|
||||
$text = $textContent = substr($this->code, $this->cursor, $pos - $this->cursor);
|
||||
if (true === $trimBlock) {
|
||||
$text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
|
||||
if (isset($this->positions[2][$this->position][0])) {
|
||||
$text = rtrim($text);
|
||||
}
|
||||
$this->pushToken(Twig_Token::TEXT_TYPE, $text);
|
||||
$this->moveCursor($textContent.$token.$append);
|
||||
$this->moveCursor($textContent.$position[0]);
|
||||
|
||||
switch ($token) {
|
||||
switch ($this->positions[1][$this->position][0]) {
|
||||
case $this->options['tag_comment'][0]:
|
||||
$this->lexComment();
|
||||
break;
|
||||
|
||||
case $this->options['tag_block'][0]:
|
||||
// raw data?
|
||||
if (preg_match('/\s*raw\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', $this->code, $match, null, $this->cursor)) {
|
||||
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lexRawData();
|
||||
$this->state = self::STATE_DATA;
|
||||
$this->lexRawData($match[1]);
|
||||
// {% line \d+ %}
|
||||
} else if (preg_match('/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', $this->code, $match, null, $this->cursor)) {
|
||||
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lineno = (int) $match[1];
|
||||
$this->state = self::STATE_DATA;
|
||||
} else {
|
||||
$this->pushToken(Twig_Token::BLOCK_START_TYPE);
|
||||
$this->state = self::STATE_BLOCK;
|
||||
$this->pushState(self::STATE_BLOCK);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
}
|
||||
break;
|
||||
|
||||
case $this->options['tag_variable'][0]:
|
||||
$this->pushToken(Twig_Token::VAR_START_TYPE);
|
||||
$this->state = self::STATE_VAR;
|
||||
$this->pushState(self::STATE_VAR);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexBlock()
|
||||
{
|
||||
$trimTag = preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/');
|
||||
$endTag = preg_quote($this->options['tag_block'][1], '/');
|
||||
|
||||
if (empty($this->brackets) && preg_match('/\s*(?:'.$trimTag.'\s*|\s*'.$endTag.')\n?/A', $this->code, $match, null, $this->cursor)) {
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::BLOCK_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->state = self::STATE_DATA;
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
@ -188,13 +211,10 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
|
||||
protected function lexVar()
|
||||
{
|
||||
$trimTag = preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/');
|
||||
$endTag = preg_quote($this->options['tag_variable'][1], '/');
|
||||
|
||||
if (empty($this->brackets) && preg_match('/\s*'.$trimTag.'\s*|\s*'.$endTag.'/A', $this->code, $match, null, $this->cursor)) {
|
||||
if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::VAR_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->state = self::STATE_DATA;
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
@ -207,12 +227,12 @@ 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->getOperatorRegex(), $this->code, $match, null, $this->cursor)) {
|
||||
if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
@ -223,7 +243,11 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
}
|
||||
// numbers
|
||||
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
|
||||
$this->pushToken(Twig_Token::NUMBER_TYPE, ctype_digit($match[0]) ? (int) $match[0] : (float) $match[0]);
|
||||
$number = (float) $match[0]; // floats
|
||||
if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
|
||||
$number = (int) $match[0]; // integers lower than the maximum
|
||||
}
|
||||
$this->pushToken(Twig_Token::NUMBER_TYPE, $number);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// punctuation
|
||||
@ -252,35 +276,80 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
$this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// opening double quoted string
|
||||
elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
|
||||
$this->brackets[] = array('"', $this->lineno);
|
||||
$this->pushState(self::STATE_STRING);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// unlexable
|
||||
else {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected character "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename);
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexRawData()
|
||||
protected function lexRawData($tag)
|
||||
{
|
||||
if (!preg_match('/'.preg_quote($this->options['tag_block'][0], '/').'\s*endraw\s*'.preg_quote($this->options['tag_block'][1], '/').'/s', $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);
|
||||
$this->pushToken(Twig_Token::TEXT_TYPE, $text);
|
||||
$this->moveCursor($text.$match[0][0]);
|
||||
|
||||
if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
|
||||
$text = rtrim($text);
|
||||
}
|
||||
|
||||
$this->pushToken(Twig_Token::TEXT_TYPE, $text);
|
||||
}
|
||||
|
||||
protected function lexComment()
|
||||
{
|
||||
$commentEndRegex = '/(?:'.preg_quote($this->options['whitespace_trim'], '/')
|
||||
.preg_quote($this->options['tag_comment'][1], '/').'\s*|'
|
||||
.preg_quote($this->options['tag_comment'][1], '/').')\n?/s';
|
||||
|
||||
if (!preg_match($commentEndRegex, $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename);
|
||||
}
|
||||
|
||||
$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
|
||||
}
|
||||
|
||||
protected function lexString()
|
||||
{
|
||||
if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) {
|
||||
$this->brackets[] = array($this->options['interpolation'][0], $this->lineno);
|
||||
$this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->pushState(self::STATE_INTERPOLATION);
|
||||
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
|
||||
$this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
|
||||
$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);
|
||||
}
|
||||
|
||||
$this->popState();
|
||||
++$this->cursor;
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexInterpolation()
|
||||
{
|
||||
$bracket = end($this->brackets);
|
||||
if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) {
|
||||
array_pop($this->brackets);
|
||||
$this->pushToken(Twig_Token::INTERPOLATION_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
protected function pushToken($type, $value = '')
|
||||
{
|
||||
// do not push empty text tokens
|
||||
@ -299,10 +368,6 @@ class Twig_Lexer implements Twig_LexerInterface
|
||||
|
||||
protected function getOperatorRegex()
|
||||
{
|
||||
if (null !== $this->operatorRegex) {
|
||||
return $this->operatorRegex;
|
||||
}
|
||||
|
||||
$operators = array_merge(
|
||||
array('='),
|
||||
array_keys($this->env->getUnaryOperators()),
|
||||
@ -317,12 +382,27 @@ 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, '/').'(?=[ ()])';
|
||||
$regex[] = preg_quote($operator, '/').'(?=[\s()])';
|
||||
} else {
|
||||
$regex[] = preg_quote($operator, '/');
|
||||
}
|
||||
}
|
||||
|
||||
return $this->operatorRegex = '/'.implode('|', $regex).'/A';
|
||||
return '/'.implode('|', $regex).'/A';
|
||||
}
|
||||
|
||||
protected function pushState($state)
|
||||
{
|
||||
$this->states[] = $this->state;
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
protected function popState()
|
||||
{
|
||||
if (0 === count($this->states)) {
|
||||
throw new Exception('Cannot pop state without a previous state');
|
||||
}
|
||||
|
||||
$this->state = array_pop($this->states);
|
||||
}
|
||||
}
|
||||
|
@ -12,18 +12,18 @@
|
||||
/**
|
||||
* 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 2.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
|
||||
*/
|
||||
function tokenize($code, $filename = null);
|
||||
public function tokenize($code, $filename = null);
|
||||
}
|
||||
|
@ -17,10 +17,9 @@
|
||||
* 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;
|
||||
|
||||
@ -47,18 +46,15 @@ class Twig_Loader_Array implements Twig_LoaderInterface
|
||||
*/
|
||||
public function setTemplate($name, $template)
|
||||
{
|
||||
$this->templates[$name] = $template;
|
||||
$this->templates[(string) $name] = $template;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
@ -67,14 +63,19 @@ 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)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
@ -83,13 +84,15 @@ 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)
|
||||
{
|
||||
$name = (string) $name;
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
@ -12,11 +12,11 @@
|
||||
/**
|
||||
* Loads templates from other loaders.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_Chain implements Twig_LoaderInterface
|
||||
class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
private $hasSourceCache = array();
|
||||
protected $loaders;
|
||||
|
||||
/**
|
||||
@ -40,61 +40,100 @@ class Twig_Loader_Chain implements Twig_LoaderInterface
|
||||
public function addLoader(Twig_LoaderInterface $loader)
|
||||
{
|
||||
$this->loaders[] = $loader;
|
||||
$this->hasSourceCache = array();
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$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.', $name));
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$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.', $name));
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
|
||||
}
|
||||
|
||||
/**
|
||||
* 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)
|
||||
{
|
||||
$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.', $name));
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
|
||||
}
|
||||
}
|
||||
|
@ -12,10 +12,9 @@
|
||||
/**
|
||||
* Loads template from the filesystem.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Loader_Filesystem implements Twig_LoaderInterface
|
||||
class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
|
||||
{
|
||||
protected $paths;
|
||||
protected $cache;
|
||||
@ -25,44 +24,64 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
|
||||
*
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
*/
|
||||
public function __construct($paths)
|
||||
public function __construct($paths = array())
|
||||
{
|
||||
$this->setPaths($paths);
|
||||
if ($paths) {
|
||||
$this->setPaths($paths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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()
|
||||
public function getPaths($namespace = '__main__')
|
||||
{
|
||||
return $this->paths;
|
||||
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|array $paths A path or an array of paths where to look for templates
|
||||
* @param string $namespace A path namespace
|
||||
*/
|
||||
public function setPaths($paths)
|
||||
public function setPaths($paths, $namespace = '__main__')
|
||||
{
|
||||
if (!is_array($paths)) {
|
||||
$paths = array($paths);
|
||||
}
|
||||
|
||||
$this->paths = array();
|
||||
$this->paths[$namespace] = array();
|
||||
foreach ($paths as $path) {
|
||||
$this->addPath($path);
|
||||
$this->addPath($path, $namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a path where templates are stored.
|
||||
*
|
||||
* @param string $path A path where to look for templates
|
||||
* @param string $path A path where to look for templates
|
||||
* @param string $namespace A path name
|
||||
*
|
||||
* @throws Twig_Error_Loader
|
||||
*/
|
||||
public function addPath($path)
|
||||
public function addPath($path, $namespace = '__main__')
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = array();
|
||||
@ -71,15 +90,37 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
|
||||
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
|
||||
}
|
||||
|
||||
$this->paths[] = $path;
|
||||
$this->paths[$namespace][] = rtrim($path, '/\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
* Prepends a path where templates are stored.
|
||||
*
|
||||
* @param string $name The name of the template to load
|
||||
* @param string $path A path where to look for templates
|
||||
* @param string $namespace A path name
|
||||
*
|
||||
* @return string The template source code
|
||||
* @throws Twig_Error_Loader
|
||||
*/
|
||||
public function prependPath($path, $namespace = '__main__')
|
||||
{
|
||||
// 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)
|
||||
{
|
||||
@ -87,11 +128,7 @@ class Twig_Loader_Filesystem 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 getCacheKey($name)
|
||||
{
|
||||
@ -99,18 +136,36 @@ class Twig_Loader_Filesystem 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 exists($name)
|
||||
{
|
||||
$name = (string) $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;
|
||||
return filemtime($this->findTemplate($name)) <= $time;
|
||||
}
|
||||
|
||||
protected function findTemplate($name)
|
||||
{
|
||||
$name = (string) $name;
|
||||
|
||||
// normalize name
|
||||
$name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
|
||||
|
||||
@ -120,13 +175,28 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
|
||||
|
||||
$this->validateName($name);
|
||||
|
||||
foreach ($this->paths as $path) {
|
||||
$namespace = '__main__';
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
|
||||
}
|
||||
|
||||
$namespace = substr($name, 1, $pos - 1);
|
||||
|
||||
$name = substr($name, $pos + 1);
|
||||
}
|
||||
|
||||
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.'/'.$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)));
|
||||
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])));
|
||||
}
|
||||
|
||||
protected function validateName($name)
|
||||
@ -135,6 +205,7 @@ class Twig_Loader_Filesystem implements Twig_LoaderInterface
|
||||
throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
|
||||
}
|
||||
|
||||
$name = ltrim($name, '/');
|
||||
$parts = explode('/', $name);
|
||||
$level = 0;
|
||||
foreach ($parts as $part) {
|
||||
|
@ -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,34 +12,41 @@
|
||||
/**
|
||||
* 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.
|
||||
*
|
||||
* @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
|
||||
*
|
||||
* @throws Twig_Error_Loader When $name is not found
|
||||
*/
|
||||
function isFresh($name, $time);
|
||||
public function isFresh($name, $time);
|
||||
}
|
||||
|
@ -12,20 +12,26 @@
|
||||
/**
|
||||
* Marks a content as safe.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Markup
|
||||
class Twig_Markup implements Countable
|
||||
{
|
||||
protected $content;
|
||||
protected $charset;
|
||||
|
||||
public function __construct($content)
|
||||
public function __construct($content, $charset)
|
||||
{
|
||||
$this->content = (string) $content;
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content);
|
||||
}
|
||||
}
|
||||
|
@ -13,10 +13,9 @@
|
||||
/**
|
||||
* 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, Countable, IteratorAggregate
|
||||
class Twig_Node implements Twig_NodeInterface
|
||||
{
|
||||
protected $nodes;
|
||||
protected $attributes;
|
||||
@ -134,12 +133,12 @@ class Twig_Node implements Twig_NodeInterface, Countable, IteratorAggregate
|
||||
*
|
||||
* @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];
|
||||
@ -188,7 +187,7 @@ class Twig_Node implements Twig_NodeInterface, Countable, IteratorAggregate
|
||||
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
|
||||
{
|
||||
|
19
inc/lib/Twig/Node/Body.php
Normal file
19
inc/lib/Twig/Node/Body.php
Normal file
@ -0,0 +1,19 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a body node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Body extends Twig_Node
|
||||
{
|
||||
}
|
38
inc/lib/Twig/Node/Do.php
Normal file
38
inc/lib/Twig/Node/Do.php
Normal file
@ -0,0 +1,38 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a do node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Do extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
38
inc/lib/Twig/Node/Embed.php
Normal file
38
inc/lib/Twig/Node/Embed.php
Normal file
@ -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
|
||||
{
|
||||
|
@ -10,9 +10,54 @@
|
||||
*/
|
||||
class Twig_Node_Expression_Array extends Twig_Node_Expression
|
||||
{
|
||||
protected $index;
|
||||
|
||||
public function __construct(array $elements, $lineno)
|
||||
{
|
||||
parent::__construct($elements, array(), $lineno);
|
||||
|
||||
$this->index = -1;
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
if ($pair['key'] instanceof Twig_Node_Expression_Constant && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) {
|
||||
$this->index = $pair['key']->getAttribute('value');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getKeyValuePairs()
|
||||
{
|
||||
$pairs = array();
|
||||
|
||||
foreach (array_chunk($this->nodes, 2) as $pair) {
|
||||
$pairs[] = array(
|
||||
'key' => $pair[0],
|
||||
'value' => $pair[1],
|
||||
);
|
||||
}
|
||||
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
public function hasElement(Twig_Node_Expression $key)
|
||||
{
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
// we compare the string representation of the keys
|
||||
// to avoid comparing the line numbers which are not relevant here.
|
||||
if ((string) $key == (string) $pair['key']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null)
|
||||
{
|
||||
if (null === $key) {
|
||||
$key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine());
|
||||
}
|
||||
|
||||
array_push($this->nodes, $key, $value);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -24,16 +69,16 @@ class Twig_Node_Expression_Array extends Twig_Node_Expression
|
||||
{
|
||||
$compiler->raw('array(');
|
||||
$first = true;
|
||||
foreach ($this->nodes as $name => $node) {
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$compiler
|
||||
->repr($name)
|
||||
->subcompile($pair['key'])
|
||||
->raw(' => ')
|
||||
->subcompile($node)
|
||||
->subcompile($pair['value'])
|
||||
;
|
||||
}
|
||||
$compiler->raw(')');
|
||||
|
@ -19,6 +19,10 @@ class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->raw(sprintf('$context[\'%s\']', $this->getAttribute('name')));
|
||||
$compiler
|
||||
->raw('$context[')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(']')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
@ -17,9 +17,9 @@ class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('floor(');
|
||||
$compiler->raw('intval(floor(');
|
||||
parent::compile($compiler);
|
||||
$compiler->raw(')');
|
||||
$compiler->raw('))');
|
||||
}
|
||||
|
||||
public function operator(Twig_Compiler $compiler)
|
||||
|
@ -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_Expression_BlockReference extends Twig_Node_Expression
|
||||
{
|
||||
|
178
inc/lib/Twig/Node/Expression/Call.php
Normal file
178
inc/lib/Twig/Node/Expression/Call.php
Normal file
@ -0,0 +1,178 @@
|
||||
<?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.
|
||||
*/
|
||||
abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
|
||||
{
|
||||
protected function compileCallable(Twig_Compiler $compiler)
|
||||
{
|
||||
$callable = $this->getAttribute('callable');
|
||||
|
||||
$closingParenthesis = false;
|
||||
if ($callable) {
|
||||
if (is_string($callable)) {
|
||||
$compiler->raw($callable);
|
||||
} elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) {
|
||||
$compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1]));
|
||||
} else {
|
||||
$type = ucfirst($this->getAttribute('type'));
|
||||
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
|
||||
$closingParenthesis = true;
|
||||
}
|
||||
} else {
|
||||
$compiler->raw($this->getAttribute('thing')->compile());
|
||||
}
|
||||
|
||||
$this->compileArguments($compiler);
|
||||
|
||||
if ($closingParenthesis) {
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileArguments(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('(');
|
||||
|
||||
$first = true;
|
||||
|
||||
if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
|
||||
$compiler->raw('$this->env');
|
||||
$first = false;
|
||||
}
|
||||
|
||||
if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$compiler->raw('$context');
|
||||
$first = false;
|
||||
}
|
||||
|
||||
if ($this->hasAttribute('arguments')) {
|
||||
foreach ($this->getAttribute('arguments') as $argument) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$compiler->string($argument);
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
|
||||
if ($this->hasNode('node')) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$compiler->subcompile($this->getNode('node'));
|
||||
$first = false;
|
||||
}
|
||||
|
||||
if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) {
|
||||
$callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
|
||||
|
||||
$arguments = $this->getArguments($callable, $this->getNode('arguments'));
|
||||
|
||||
foreach ($arguments as $node) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$compiler->subcompile($node);
|
||||
$first = false;
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
}
|
||||
|
||||
protected function getArguments($callable, $arguments)
|
||||
{
|
||||
$parameters = array();
|
||||
$named = false;
|
||||
foreach ($arguments as $name => $node) {
|
||||
if (!is_int($name)) {
|
||||
$named = true;
|
||||
$name = $this->normalizeName($name);
|
||||
} elseif ($named) {
|
||||
throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
$parameters[$name] = $node;
|
||||
}
|
||||
|
||||
if (!$named) {
|
||||
return $parameters;
|
||||
}
|
||||
|
||||
if (!$callable) {
|
||||
throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
// manage named arguments
|
||||
if (is_array($callable)) {
|
||||
$r = new ReflectionMethod($callable[0], $callable[1]);
|
||||
} elseif (is_object($callable) && !$callable instanceof Closure) {
|
||||
$r = new ReflectionObject($callable);
|
||||
$r = $r->getMethod('__invoke');
|
||||
} else {
|
||||
$r = new ReflectionFunction($callable);
|
||||
}
|
||||
|
||||
$definition = $r->getParameters();
|
||||
if ($this->hasNode('node')) {
|
||||
array_shift($definition);
|
||||
}
|
||||
if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
|
||||
array_shift($definition);
|
||||
}
|
||||
if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
|
||||
array_shift($definition);
|
||||
}
|
||||
if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
|
||||
foreach ($this->getAttribute('arguments') as $argument) {
|
||||
array_shift($definition);
|
||||
}
|
||||
}
|
||||
|
||||
$arguments = array();
|
||||
$pos = 0;
|
||||
foreach ($definition as $param) {
|
||||
$name = $this->normalizeName($param->name);
|
||||
|
||||
if (array_key_exists($name, $parameters)) {
|
||||
if (array_key_exists($pos, $parameters)) {
|
||||
throw new Twig_Error_Syntax(sprintf('Arguments "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
$arguments[] = $parameters[$name];
|
||||
unset($parameters[$name]);
|
||||
} elseif (array_key_exists($pos, $parameters)) {
|
||||
$arguments[] = $parameters[$pos];
|
||||
unset($parameters[$pos]);
|
||||
++$pos;
|
||||
} elseif ($param->isDefaultValueAvailable()) {
|
||||
$arguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
|
||||
} elseif ($param->isOptional()) {
|
||||
break;
|
||||
} else {
|
||||
throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
}
|
||||
|
||||
foreach (array_keys($parameters) as $name) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unknown argument "%s" for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
protected function normalizeName($name)
|
||||
{
|
||||
return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
|
||||
}
|
||||
}
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Represents an extension call node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
|
||||
{
|
||||
|
@ -9,7 +9,7 @@
|
||||
* 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
|
||||
class Twig_Node_Expression_Filter extends Twig_Node_Expression_Call
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
{
|
||||
@ -19,54 +19,18 @@ class Twig_Node_Expression_Filter extends Twig_Node_Expression
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$name = $this->getNode('filter')->getAttribute('value');
|
||||
if (false === $filter = $compiler->getEnvironment()->getFilter($name)) {
|
||||
throw new Twig_Error_Syntax(sprintf('The filter "%s" does not exist', $name), $this->getLine());
|
||||
$filter = $compiler->getEnvironment()->getFilter($name);
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'filter');
|
||||
$this->setAttribute('thing', $filter);
|
||||
$this->setAttribute('needs_environment', $filter->needsEnvironment());
|
||||
$this->setAttribute('needs_context', $filter->needsContext());
|
||||
$this->setAttribute('arguments', $filter->getArguments());
|
||||
if ($filter instanceof Twig_FilterCallableInterface || $filter instanceof Twig_SimpleFilter) {
|
||||
$this->setAttribute('callable', $filter->getCallable());
|
||||
}
|
||||
|
||||
$node = $this->getNode('node');
|
||||
|
||||
// The default filter is intercepted when the filtered value
|
||||
// is a name (like obj) or an attribute (like obj.attr)
|
||||
// In such a case, it's compiled to {{ obj is defined ? obj|default('bar') : 'bar' }}
|
||||
if ('default' === $name && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
|
||||
$compiler
|
||||
->raw('((')
|
||||
->subcompile(new Twig_Node_Expression_Test($node, 'defined', new Twig_Node(), $this->getLine()))
|
||||
->raw(') ? (')
|
||||
;
|
||||
|
||||
$this->compileFilter($compiler, $filter);
|
||||
|
||||
$compiler->raw(') : (');
|
||||
|
||||
if ($this->getNode('arguments')->hasNode(0)) {
|
||||
$compiler->subcompile($this->getNode('arguments')->getNode(0));
|
||||
} else {
|
||||
$compiler->string('');
|
||||
}
|
||||
|
||||
$compiler->raw('))');
|
||||
} else {
|
||||
$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, ' : '')
|
||||
->subcompile($this->getNode('node'))
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $node) {
|
||||
$compiler
|
||||
->raw(', ')
|
||||
->subcompile($node)
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
$this->compileCallable($compiler);
|
||||
}
|
||||
}
|
||||
|
43
inc/lib/Twig/Node/Expression/Filter/Default.php
Normal file
43
inc/lib/Twig/Node/Expression/Filter/Default.php
Normal file
@ -0,0 +1,43 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Returns the value or the default value when it is undefined or empty.
|
||||
*
|
||||
* <pre>
|
||||
* {{ var.foo|default('foo item on var is not defined') }}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
{
|
||||
$default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getLine()), $arguments, $node->getLine());
|
||||
|
||||
if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
|
||||
$test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine());
|
||||
$false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine());
|
||||
|
||||
$node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine());
|
||||
} else {
|
||||
$node = $default;
|
||||
}
|
||||
|
||||
parent::__construct($node, $filterName, $arguments, $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->subcompile($this->getNode('node'));
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
* 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
|
||||
class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
|
||||
{
|
||||
public function __construct($name, Twig_NodeInterface $arguments, $lineno)
|
||||
{
|
||||
@ -17,33 +17,19 @@ class Twig_Node_Expression_Function extends Twig_Node_Expression
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$function = $compiler->getEnvironment()->getFunction($this->getAttribute('name'));
|
||||
if (false === $function) {
|
||||
throw new Twig_Error_Syntax(sprintf('The function "%s" does not exist', $this->getAttribute('name')), $this->getLine());
|
||||
$name = $this->getAttribute('name');
|
||||
$function = $compiler->getEnvironment()->getFunction($name);
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'function');
|
||||
$this->setAttribute('thing', $function);
|
||||
$this->setAttribute('needs_environment', $function->needsEnvironment());
|
||||
$this->setAttribute('needs_context', $function->needsContext());
|
||||
$this->setAttribute('arguments', $function->getArguments());
|
||||
if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
|
||||
$this->setAttribute('callable', $function->getCallable());
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw($function->compile().'(')
|
||||
->raw($function->needsEnvironment() ? '$this->env' : '')
|
||||
;
|
||||
|
||||
if ($function->needsContext()) {
|
||||
$compiler->raw($function->needsEnvironment() ? ', $context' : '$context');
|
||||
}
|
||||
|
||||
$first = true;
|
||||
foreach ($this->getNode('arguments') as $node) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
} else {
|
||||
if ($function->needsEnvironment() || $function->needsContext()) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
}
|
||||
$compiler->subcompile($node);
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
$this->compileCallable($compiler);
|
||||
}
|
||||
}
|
||||
|
@ -11,43 +11,43 @@
|
||||
*/
|
||||
class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_NodeInterface $arguments, $type, $lineno)
|
||||
public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression_Array $arguments, $type, $lineno)
|
||||
{
|
||||
parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type), $lineno);
|
||||
parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno);
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->raw('$this->getAttribute(');
|
||||
|
||||
if ($this->hasAttribute('is_defined_test') && $compiler->getEnvironment()->isStrictVariables()) {
|
||||
$compiler->subcompile(new Twig_Node_Expression_Filter(
|
||||
$this->getNode('node'),
|
||||
new Twig_Node_Expression_Constant('default', $this->getLine()),
|
||||
new Twig_Node(),
|
||||
$this->getLine()
|
||||
));
|
||||
if (function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) {
|
||||
$compiler->raw('twig_template_get_attributes($this, ');
|
||||
} else {
|
||||
$compiler->subcompile($this->getNode('node'));
|
||||
$compiler->raw('$this->getAttribute(');
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('attribute'))
|
||||
->raw(', array(')
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $node) {
|
||||
$compiler
|
||||
->subcompile($node)
|
||||
->raw(', ')
|
||||
;
|
||||
if ($this->getAttribute('ignore_strict_check')) {
|
||||
$this->getNode('node')->setAttribute('ignore_strict_check', true);
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('), ')
|
||||
->repr($this->getAttribute('type'))
|
||||
->raw($this->hasAttribute('is_defined_test') ? ', true' : ', false')
|
||||
->raw(')');
|
||||
$compiler->subcompile($this->getNode('node'));
|
||||
|
||||
$compiler->raw(', ')->subcompile($this->getNode('attribute'));
|
||||
|
||||
if (count($this->getNode('arguments')) || Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
|
||||
|
||||
if (Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', ')->repr($this->getAttribute('type'));
|
||||
}
|
||||
|
||||
if ($this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', '.($this->getAttribute('is_defined_test') ? 'true' : 'false'));
|
||||
}
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', '.($this->getAttribute('ignore_strict_check') ? 'true' : 'false'));
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
||||
|
41
inc/lib/Twig/Node/Expression/MethodCall.php
Normal file
41
inc/lib/Twig/Node/Expression/MethodCall.php
Normal file
@ -0,0 +1,41 @@
|
||||
<?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_Node_Expression_MethodCall extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $node, $method, Twig_Node_Expression_Array $arguments, $lineno)
|
||||
{
|
||||
parent::__construct(array('node' => $node, 'arguments' => $arguments), array('method' => $method, 'safe' => false), $lineno);
|
||||
|
||||
if ($node instanceof Twig_Node_Expression_Name) {
|
||||
$node->setAttribute('always_defined', true);
|
||||
}
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw('->')
|
||||
->raw($this->getAttribute('method'))
|
||||
->raw('(')
|
||||
;
|
||||
$first = true;
|
||||
foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$compiler->subcompile($pair['value']);
|
||||
}
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
@ -11,31 +11,78 @@
|
||||
*/
|
||||
class Twig_Node_Expression_Name extends Twig_Node_Expression
|
||||
{
|
||||
protected $specialVars = array(
|
||||
'_self' => '$this',
|
||||
'_context' => '$context',
|
||||
'_charset' => '$this->env->getCharset()',
|
||||
);
|
||||
|
||||
public function __construct($name, $lineno)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno);
|
||||
parent::__construct(array(), array('name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false), $lineno);
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
static $specialVars = array(
|
||||
'_self' => '$this',
|
||||
'_context' => '$context',
|
||||
'_charset' => '$this->env->getCharset()',
|
||||
);
|
||||
|
||||
$name = $this->getAttribute('name');
|
||||
|
||||
if ($this->hasAttribute('is_defined_test')) {
|
||||
if (isset($specialVars[$name])) {
|
||||
if ($this->getAttribute('is_defined_test')) {
|
||||
if ($this->isSpecial()) {
|
||||
$compiler->repr(true);
|
||||
} else {
|
||||
$compiler->raw('array_key_exists(')->repr($name)->raw(', $context)');
|
||||
}
|
||||
} elseif (isset($specialVars[$name])) {
|
||||
$compiler->raw($specialVars[$name]);
|
||||
} elseif ($this->isSpecial()) {
|
||||
$compiler->raw($this->specialVars[$name]);
|
||||
} elseif ($this->getAttribute('always_defined')) {
|
||||
$compiler
|
||||
->raw('$context[')
|
||||
->string($name)
|
||||
->raw(']')
|
||||
;
|
||||
} else {
|
||||
$compiler->raw(sprintf('$this->getContext($context, \'%s\')', $name));
|
||||
// remove the non-PHP 5.4 version when PHP 5.3 support is dropped
|
||||
// as the non-optimized version is just a workaround for slow ternary operator
|
||||
// when the context has a lot of variables
|
||||
if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
|
||||
// PHP 5.4 ternary operator performance was optimized
|
||||
$compiler
|
||||
->raw('(isset($context[')
|
||||
->string($name)
|
||||
->raw(']) ? $context[')
|
||||
->string($name)
|
||||
->raw('] : ')
|
||||
;
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
|
||||
$compiler->raw('null)');
|
||||
} else {
|
||||
$compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
|
||||
}
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->getContext($context, ')
|
||||
->string($name)
|
||||
;
|
||||
|
||||
if ($this->getAttribute('ignore_strict_check')) {
|
||||
$compiler->raw(', true');
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function isSpecial()
|
||||
{
|
||||
return isset($this->specialVars[$this->getAttribute('name')]);
|
||||
}
|
||||
|
||||
public function isSimple()
|
||||
{
|
||||
return !$this->isSpecial() && !$this->getAttribute('is_defined_test');
|
||||
}
|
||||
}
|
||||
|
@ -13,14 +13,13 @@
|
||||
/**
|
||||
* Represents a parent node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Parent extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno, $tag);
|
||||
parent::__construct(array(), array('output' => false, 'name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -30,10 +29,19 @@ class Twig_Node_Expression_Parent extends Twig_Node_Expression
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw("\$this->renderParentBlock(")
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(", \$context, \$blocks)")
|
||||
;
|
||||
if ($this->getAttribute('output')) {
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("\$this->displayParentBlock(")
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(", \$context, \$blocks);\n")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->raw("\$this->renderParentBlock(")
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(", \$context, \$blocks)")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
26
inc/lib/Twig/Node/Expression/TempName.php
Normal file
26
inc/lib/Twig/Node/Expression/TempName.php
Normal file
@ -0,0 +1,26 @@
|
||||
<?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.
|
||||
*/
|
||||
class Twig_Node_Expression_TempName extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct($name, $lineno)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno);
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('$_')
|
||||
->raw($this->getAttribute('name'))
|
||||
->raw('_')
|
||||
;
|
||||
}
|
||||
}
|
@ -8,7 +8,7 @@
|
||||
* 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
|
||||
class Twig_Node_Expression_Test extends Twig_Node_Expression_Call
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
|
||||
{
|
||||
@ -17,44 +17,16 @@ class Twig_Node_Expression_Test extends Twig_Node_Expression
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$testMap = $compiler->getEnvironment()->getTests();
|
||||
if (!isset($testMap[$this->getAttribute('name')])) {
|
||||
throw new Twig_Error_Syntax(sprintf('The test "%s" does not exist', $this->getAttribute('name')), $this->getLine());
|
||||
}
|
||||
|
||||
$name = $this->getAttribute('name');
|
||||
$node = $this->getNode('node');
|
||||
$test = $compiler->getEnvironment()->getTest($name);
|
||||
|
||||
// defined is a special case
|
||||
if ('defined' === $name) {
|
||||
if ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr) {
|
||||
$node->setAttribute('is_defined_test', true);
|
||||
$compiler->subcompile($node);
|
||||
$node->removeAttribute('is_defined_test');
|
||||
} else {
|
||||
throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine());
|
||||
}
|
||||
return;
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('type', 'test');
|
||||
$this->setAttribute('thing', $test);
|
||||
if ($test instanceof Twig_TestCallableInterface || $test instanceof Twig_SimpleTest) {
|
||||
$this->setAttribute('callable', $test->getCallable());
|
||||
}
|
||||
|
||||
$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(')');
|
||||
$this->compileCallable($compiler);
|
||||
}
|
||||
}
|
||||
|
46
inc/lib/Twig/Node/Expression/Test/Constant.php
Normal file
46
inc/lib/Twig/Node/Expression/Test/Constant.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a variable is the exact same value as a constant.
|
||||
*
|
||||
* <pre>
|
||||
* {% if post.status is constant('Post::PUBLISHED') %}
|
||||
* the status attribute is exactly the same as Post::PUBLISHED
|
||||
* {% endif %}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' === constant(')
|
||||
;
|
||||
|
||||
if ($this->getNode('arguments')->hasNode(1)) {
|
||||
$compiler
|
||||
->raw('get_class(')
|
||||
->subcompile($this->getNode('arguments')->getNode(1))
|
||||
->raw(')."::".')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('arguments')->getNode(0))
|
||||
->raw('))')
|
||||
;
|
||||
}
|
||||
}
|
54
inc/lib/Twig/Node/Expression/Test/Defined.php
Normal file
54
inc/lib/Twig/Node/Expression/Test/Defined.php
Normal file
@ -0,0 +1,54 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a variable is defined in the current context.
|
||||
*
|
||||
* <pre>
|
||||
* {# defined works with variable names and variable attributes #}
|
||||
* {% if foo is defined %}
|
||||
* {# ... #}
|
||||
* {% endif %}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
|
||||
{
|
||||
parent::__construct($node, $name, $arguments, $lineno);
|
||||
|
||||
if ($node instanceof Twig_Node_Expression_Name) {
|
||||
$node->setAttribute('is_defined_test', true);
|
||||
} elseif ($node instanceof Twig_Node_Expression_GetAttr) {
|
||||
$node->setAttribute('is_defined_test', true);
|
||||
|
||||
$this->changeIgnoreStrictCheck($node);
|
||||
} else {
|
||||
throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node)
|
||||
{
|
||||
$node->setAttribute('ignore_strict_check', true);
|
||||
|
||||
if ($node->getNode('node') instanceof Twig_Node_Expression_GetAttr) {
|
||||
$this->changeIgnoreStrictCheck($node->getNode('node'));
|
||||
}
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->subcompile($this->getNode('node'));
|
||||
}
|
||||
}
|
33
inc/lib/Twig/Node/Expression/Test/Divisibleby.php
Normal file
33
inc/lib/Twig/Node/Expression/Test/Divisibleby.php
Normal file
@ -0,0 +1,33 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a variable is divisible by a number.
|
||||
*
|
||||
* <pre>
|
||||
* {% if loop.index is divisibleby(3) %}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Test_Divisibleby extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(0 == ')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' % ')
|
||||
->subcompile($this->getNode('arguments')->getNode(0))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
32
inc/lib/Twig/Node/Expression/Test/Even.php
Normal file
32
inc/lib/Twig/Node/Expression/Test/Even.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a number is even.
|
||||
*
|
||||
* <pre>
|
||||
* {{ var is even }}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Test_Even extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' % 2 == 0')
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
31
inc/lib/Twig/Node/Expression/Test/Null.php
Normal file
31
inc/lib/Twig/Node/Expression/Test/Null.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks that a variable is null.
|
||||
*
|
||||
* <pre>
|
||||
* {{ var is none }}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Test_Null extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(null === ')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
32
inc/lib/Twig/Node/Expression/Test/Odd.php
Normal file
32
inc/lib/Twig/Node/Expression/Test/Odd.php
Normal file
@ -0,0 +1,32 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a number is odd.
|
||||
*
|
||||
* <pre>
|
||||
* {{ var is odd }}
|
||||
* </pre>
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' % 2 == 1')
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
29
inc/lib/Twig/Node/Expression/Test/Sameas.php
Normal file
29
inc/lib/Twig/Node/Expression/Test/Sameas.php
Normal file
@ -0,0 +1,29 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Checks if a variable is the same as another one (=== in PHP).
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Expression_Test_Sameas extends Twig_Node_Expression_Test
|
||||
{
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(' === ')
|
||||
->subcompile($this->getNode('arguments')->getNode(0))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
36
inc/lib/Twig/Node/Flush.php
Normal file
36
inc/lib/Twig/Node/Flush.php
Normal file
@ -0,0 +1,36 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a flush node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Flush extends Twig_Node
|
||||
{
|
||||
public function __construct($lineno, $tag)
|
||||
{
|
||||
parent::__construct(array(), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("flush();\n")
|
||||
;
|
||||
}
|
||||
}
|
@ -13,14 +13,21 @@
|
||||
/**
|
||||
* Represents a for node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_For extends Twig_Node
|
||||
{
|
||||
protected $loop;
|
||||
|
||||
public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'ifexpr' => $ifexpr, 'body' => $body, 'else' => $else), array('with_loop' => true), $lineno, $tag);
|
||||
$body = new Twig_Node(array($body, $this->loop = new Twig_Node_ForLoop($lineno, $tag)));
|
||||
|
||||
if (null !== $ifexpr) {
|
||||
$body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag);
|
||||
}
|
||||
|
||||
parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -53,7 +60,7 @@ class Twig_Node_For extends Twig_Node
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
if (null === $this->getNode('ifexpr')) {
|
||||
if (!$this->getAttribute('ifexpr')) {
|
||||
$compiler
|
||||
->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n")
|
||||
->indent()
|
||||
@ -68,6 +75,10 @@ class Twig_Node_For extends Twig_Node
|
||||
}
|
||||
}
|
||||
|
||||
$this->loop->setAttribute('else', null !== $this->getNode('else'));
|
||||
$this->loop->setAttribute('with_loop', $this->getAttribute('with_loop'));
|
||||
$this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr'));
|
||||
|
||||
$compiler
|
||||
->write("foreach (\$context['_seq'] as ")
|
||||
->subcompile($this->getNode('key_target'))
|
||||
@ -75,47 +86,7 @@ class Twig_Node_For extends Twig_Node
|
||||
->subcompile($this->getNode('value_target'))
|
||||
->raw(") {\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('ifexpr')) {
|
||||
$compiler
|
||||
->write("if (!(")
|
||||
->subcompile($this->getNode('ifexpr'))
|
||||
->raw(")) {\n")
|
||||
->indent()
|
||||
->write("continue;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
|
||||
if (null !== $this->getNode('else')) {
|
||||
$compiler->write("\$context['_iterated'] = true;\n");
|
||||
}
|
||||
|
||||
if ($this->getAttribute('with_loop')) {
|
||||
$compiler
|
||||
->write("++\$context['loop']['index0'];\n")
|
||||
->write("++\$context['loop']['index'];\n")
|
||||
->write("\$context['loop']['first'] = false;\n")
|
||||
;
|
||||
|
||||
if (null === $this->getNode('ifexpr')) {
|
||||
$compiler
|
||||
->write("if (isset(\$context['loop']['length'])) {\n")
|
||||
->indent()
|
||||
->write("--\$context['loop']['revindex0'];\n")
|
||||
->write("--\$context['loop']['revindex'];\n")
|
||||
->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
@ -136,6 +107,6 @@ class Twig_Node_For extends Twig_Node
|
||||
$compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n");
|
||||
|
||||
// keep the values set in the inner context for variables defined in the outer context
|
||||
$compiler->write("\$context = array_merge(\$_parent, array_intersect_key(\$context, \$_parent));\n");
|
||||
$compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n");
|
||||
}
|
||||
}
|
||||
|
55
inc/lib/Twig/Node/ForLoop.php
Normal file
55
inc/lib/Twig/Node/ForLoop.php
Normal file
@ -0,0 +1,55 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Internal node used by the for node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_ForLoop extends Twig_Node
|
||||
{
|
||||
public function __construct($lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array(), array('with_loop' => false, 'ifexpr' => false, 'else' => false), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
if ($this->getAttribute('else')) {
|
||||
$compiler->write("\$context['_iterated'] = true;\n");
|
||||
}
|
||||
|
||||
if ($this->getAttribute('with_loop')) {
|
||||
$compiler
|
||||
->write("++\$context['loop']['index0'];\n")
|
||||
->write("++\$context['loop']['index'];\n")
|
||||
->write("\$context['loop']['first'] = false;\n")
|
||||
;
|
||||
|
||||
if (!$this->getAttribute('ifexpr')) {
|
||||
$compiler
|
||||
->write("if (isset(\$context['loop']['length'])) {\n")
|
||||
->indent()
|
||||
->write("--\$context['loop']['revindex0'];\n")
|
||||
->write("--\$context['loop']['revindex'];\n")
|
||||
->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -13,8 +13,7 @@
|
||||
/**
|
||||
* Represents an if node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_If extends Twig_Node
|
||||
{
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Represents an import node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Import extends Twig_Node
|
||||
{
|
||||
|
@ -13,8 +13,7 @@
|
||||
/**
|
||||
* Represents an include node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
|
||||
{
|
||||
@ -39,21 +38,46 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
|
||||
;
|
||||
}
|
||||
|
||||
$this->addGetTemplate($compiler);
|
||||
|
||||
$compiler->raw('->display(');
|
||||
|
||||
$this->addTemplateArguments($compiler);
|
||||
|
||||
$compiler->raw(");\n");
|
||||
|
||||
if ($this->getAttribute('ignore_missing')) {
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("} catch (Twig_Error_Loader \$e) {\n")
|
||||
->indent()
|
||||
->write("// ignore missing template\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
protected function addGetTemplate(Twig_Compiler $compiler)
|
||||
{
|
||||
if ($this->getNode('expr') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
->write("\$this->env->loadTemplate(")
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(")->display(")
|
||||
->raw(")")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write("\$template = \$this->env->resolveTemplate(")
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(");\n")
|
||||
->write('$template->display(')
|
||||
->write('$template')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
protected function addTemplateArguments(Twig_Compiler $compiler)
|
||||
{
|
||||
if (false === $this->getAttribute('only')) {
|
||||
if (null === $this->getNode('variables')) {
|
||||
$compiler->raw('$context');
|
||||
@ -71,18 +95,5 @@ class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
|
||||
$compiler->subcompile($this->getNode('variables'));
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw(");\n");
|
||||
|
||||
if ($this->getAttribute('ignore_missing')) {
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("} catch (Twig_Error_Loader \$e) {\n")
|
||||
->indent()
|
||||
->write("// ignore missing template\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Represents a macro node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Macro extends Twig_Node
|
||||
{
|
||||
@ -29,43 +28,67 @@ class Twig_Node_Macro extends Twig_Node
|
||||
*/
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$arguments = array();
|
||||
foreach ($this->getNode('arguments') as $argument) {
|
||||
$arguments[] = '$'.$argument->getAttribute('name').' = null';
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function get%s(", $this->getAttribute('name')))
|
||||
;
|
||||
|
||||
$count = count($this->getNode('arguments'));
|
||||
$pos = 0;
|
||||
foreach ($this->getNode('arguments') as $name => $default) {
|
||||
$compiler
|
||||
->raw('$_'.$name.' = ')
|
||||
->subcompile($default)
|
||||
;
|
||||
|
||||
if (++$pos < $count) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function get%s(%s)\n", $this->getAttribute('name'), implode(', ', $arguments)), "{\n")
|
||||
->indent()
|
||||
->write("\$context = array_merge(\$this->env->getGlobals(), array(\n")
|
||||
->raw(")\n")
|
||||
->write("{\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $argument) {
|
||||
if (!count($this->getNode('arguments'))) {
|
||||
$compiler->write("\$context = \$this->env->getGlobals();\n\n");
|
||||
} else {
|
||||
$compiler
|
||||
->write('')
|
||||
->string($argument->getAttribute('name'))
|
||||
->raw(' => $'.$argument->getAttribute('name'))
|
||||
->raw(",\n")
|
||||
->write("\$context = \$this->env->mergeGlobals(array(\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $name => $default) {
|
||||
$compiler
|
||||
->write('')
|
||||
->string($name)
|
||||
->raw(' => $_'.$name)
|
||||
->raw(",\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("));\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("));\n\n")
|
||||
->write("\$blocks = array();\n\n")
|
||||
->write("ob_start();\n")
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("} catch(Exception \$e) {\n")
|
||||
->write("} catch (Exception \$e) {\n")
|
||||
->indent()
|
||||
->write("ob_end_clean();\n\n")
|
||||
->write("throw \$e;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->write("return ob_get_clean();\n")
|
||||
->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
|
@ -13,14 +13,19 @@
|
||||
/**
|
||||
* Represents a module node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Module extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $filename)
|
||||
public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename)
|
||||
{
|
||||
parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename), 1);
|
||||
// embedded templates are set as attributes so that they are only visited once by the visitors
|
||||
parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1);
|
||||
}
|
||||
|
||||
public function setIndex($index)
|
||||
{
|
||||
$this->setAttribute('index', $index);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -31,13 +36,21 @@ class Twig_Node_Module extends Twig_Node
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$this->compileTemplate($compiler);
|
||||
|
||||
foreach ($this->getAttribute('embedded_templates') as $template) {
|
||||
$compiler->subcompile($template);
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileTemplate(Twig_Compiler $compiler)
|
||||
{
|
||||
if (!$this->getAttribute('index')) {
|
||||
$compiler->write('<?php');
|
||||
}
|
||||
|
||||
$this->compileClassHeader($compiler);
|
||||
|
||||
if (count($this->getNode('blocks')) || count($this->getNode('traits'))) {
|
||||
if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$this->compileConstructor($compiler);
|
||||
}
|
||||
|
||||
@ -57,29 +70,31 @@ class Twig_Node_Module extends Twig_Node
|
||||
|
||||
$this->compileIsTraitable($compiler);
|
||||
|
||||
$this->compileDebugInfo($compiler);
|
||||
|
||||
$this->compileClassFooter($compiler);
|
||||
}
|
||||
|
||||
protected function compileGetParent(Twig_Compiler $compiler)
|
||||
{
|
||||
if (null === $this->getNode('parent')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("protected function doGetParent(array \$context)\n", "{\n")
|
||||
->indent()
|
||||
->write("return ")
|
||||
;
|
||||
|
||||
if (null === $this->getNode('parent')) {
|
||||
$compiler->raw("false");
|
||||
if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler->subcompile($this->getNode('parent'));
|
||||
} else {
|
||||
if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler->subcompile($this->getNode('parent'));
|
||||
} else {
|
||||
$compiler
|
||||
->raw("\$this->env->resolveTemplate(")
|
||||
->subcompile($this->getNode('parent'))
|
||||
->raw(")")
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
->raw("\$this->env->resolveTemplate(")
|
||||
->subcompile($this->getNode('parent'))
|
||||
->raw(")")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
@ -91,21 +106,25 @@ class Twig_Node_Module extends Twig_Node
|
||||
|
||||
protected function compileDisplayBody(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler->write("\$context = array_merge(\$this->env->getGlobals(), \$context);\n\n");
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
|
||||
if (null !== $this->getNode('parent')) {
|
||||
$compiler->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
|
||||
if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler->write("\$this->parent");
|
||||
} else {
|
||||
$compiler->write("\$this->getParent(\$context)");
|
||||
}
|
||||
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileClassHeader(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("<?php\n\n")
|
||||
->write("\n\n")
|
||||
// if the filename contains */, add a blank to avoid a PHP parse error
|
||||
->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
|
||||
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename')))
|
||||
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index')))
|
||||
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
|
||||
->write("{\n")
|
||||
->indent()
|
||||
@ -120,6 +139,17 @@ class Twig_Node_Module extends Twig_Node
|
||||
->write("parent::__construct(\$env);\n\n")
|
||||
;
|
||||
|
||||
// parent
|
||||
if (null === $this->getNode('parent')) {
|
||||
$compiler->write("\$this->parent = false;\n\n");
|
||||
} elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
->write("\$this->parent = \$this->env->loadTemplate(")
|
||||
->subcompile($this->getNode('parent'))
|
||||
->raw(");\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
$countTraits = count($this->getNode('traits'));
|
||||
if ($countTraits) {
|
||||
// traits
|
||||
@ -151,18 +181,32 @@ class Twig_Node_Module extends Twig_Node
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("\$this->blocks = array_merge(\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
for ($i = 0; $i < $countTraits; $i++) {
|
||||
if ($countTraits > 1) {
|
||||
$compiler
|
||||
->write(sprintf("\$_trait_%s_blocks,\n", $i))
|
||||
->write("\$this->traits = array_merge(\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
for ($i = 0; $i < $countTraits; $i++) {
|
||||
$compiler
|
||||
->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i))
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write(");\n\n")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write("\$this->traits = \$_trait_0_blocks;\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("\$this->blocks = array_merge(\n")
|
||||
->indent()
|
||||
->write("\$this->traits,\n")
|
||||
->write("array(\n")
|
||||
;
|
||||
} else {
|
||||
@ -250,11 +294,21 @@ class Twig_Node_Module extends Twig_Node
|
||||
// only contains blocks and use statements.
|
||||
$traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
|
||||
if ($traitable) {
|
||||
if (!count($nodes = $this->getNode('body'))) {
|
||||
$nodes = new Twig_Node(array($this->getNode('body')));
|
||||
if ($this->getNode('body') instanceof Twig_Node_Body) {
|
||||
$nodes = $this->getNode('body')->getNode(0);
|
||||
} else {
|
||||
$nodes = $this->getNode('body');
|
||||
}
|
||||
|
||||
if (!count($nodes)) {
|
||||
$nodes = new Twig_Node(array($nodes));
|
||||
}
|
||||
|
||||
foreach ($nodes as $node) {
|
||||
if (!count($node)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
|
||||
continue;
|
||||
}
|
||||
@ -268,16 +322,31 @@ class Twig_Node_Module extends Twig_Node
|
||||
}
|
||||
}
|
||||
|
||||
if ($traitable) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("public function isTraitable()\n", "{\n")
|
||||
->indent()
|
||||
->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileDebugInfo(Twig_Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("public function getDebugInfo()\n", "{\n")
|
||||
->indent()
|
||||
->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
|
||||
public function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
|
||||
protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
|
@ -13,8 +13,7 @@
|
||||
/**
|
||||
* Represents a node that outputs an expression.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface
|
||||
{
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Represents a sandbox node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Sandbox extends Twig_Node
|
||||
{
|
||||
|
@ -13,8 +13,7 @@
|
||||
/**
|
||||
* Represents a module node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_SandboxedModule extends Twig_Node_Module
|
||||
{
|
||||
@ -24,7 +23,9 @@ class Twig_Node_SandboxedModule extends Twig_Node_Module
|
||||
|
||||
public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags, array $usedFunctions)
|
||||
{
|
||||
parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getNode('traits'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
|
||||
parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getNode('traits'), $node->getAttribute('embedded_templates'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
|
||||
|
||||
$this->setAttribute('index', $node->getAttribute('index'));
|
||||
|
||||
$this->usedFilters = $usedFilters;
|
||||
$this->usedTags = $usedTags;
|
||||
@ -33,9 +34,7 @@ class Twig_Node_SandboxedModule extends Twig_Node_Module
|
||||
|
||||
protected function compileDisplayBody(Twig_Compiler $compiler)
|
||||
{
|
||||
if (null === $this->getNode('parent')) {
|
||||
$compiler->write("\$this->checkSecurity();\n");
|
||||
}
|
||||
$compiler->write("\$this->checkSecurity();\n");
|
||||
|
||||
parent::compileDisplayBody($compiler);
|
||||
}
|
||||
@ -45,7 +44,7 @@ class Twig_Node_SandboxedModule extends Twig_Node_Module
|
||||
parent::compileDisplayFooter($compiler);
|
||||
|
||||
$compiler
|
||||
->write("protected function checkSecurity() {\n")
|
||||
->write("protected function checkSecurity()\n", "{\n")
|
||||
->indent()
|
||||
->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
|
||||
->indent()
|
||||
@ -54,16 +53,6 @@ class Twig_Node_SandboxedModule extends Twig_Node_Module
|
||||
->write(!$this->usedFunctions ? "array()\n" : "array('".implode('\', \'', $this->usedFunctions)."')\n")
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('parent')) {
|
||||
$compiler
|
||||
->raw("\n")
|
||||
->write("\$this->parent->checkSecurity();\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
|
@ -17,8 +17,7 @@
|
||||
* and if the sandbox is enabled, we need to check that the __toString()
|
||||
* method is allowed if 'article' is an object.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_SandboxedPrint extends Twig_Node_Print
|
||||
{
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Represents a set node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Set extends Twig_Node
|
||||
{
|
||||
@ -67,7 +66,7 @@ class Twig_Node_Set extends Twig_Node
|
||||
$compiler->subcompile($this->getNode('names'), false);
|
||||
|
||||
if ($this->getAttribute('capture')) {
|
||||
$compiler->raw(" = new Twig_Markup(ob_get_clean())");
|
||||
$compiler->raw(" = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())");
|
||||
}
|
||||
}
|
||||
|
||||
@ -87,9 +86,9 @@ class Twig_Node_Set extends Twig_Node
|
||||
} else {
|
||||
if ($this->getAttribute('safe')) {
|
||||
$compiler
|
||||
->raw("new Twig_Markup(")
|
||||
->raw("('' === \$tmp = ")
|
||||
->subcompile($this->getNode('values'))
|
||||
->raw(")")
|
||||
->raw(") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())")
|
||||
;
|
||||
} else {
|
||||
$compiler->subcompile($this->getNode('values'));
|
||||
|
35
inc/lib/Twig/Node/SetTemp.php
Normal file
35
inc/lib/Twig/Node/SetTemp.php
Normal file
@ -0,0 +1,35 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
class Twig_Node_SetTemp extends Twig_Node
|
||||
{
|
||||
public function __construct($name, $lineno)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno);
|
||||
}
|
||||
|
||||
public function compile(Twig_Compiler $compiler)
|
||||
{
|
||||
$name = $this->getAttribute('name');
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('if (isset($context[')
|
||||
->string($name)
|
||||
->raw('])) { $_')
|
||||
->raw($name)
|
||||
->raw('_ = $context[')
|
||||
->repr($name)
|
||||
->raw(']; } else { $_')
|
||||
->raw($name)
|
||||
->raw("_ = null; }\n")
|
||||
;
|
||||
}
|
||||
}
|
@ -14,8 +14,7 @@
|
||||
*
|
||||
* It removes spaces between HTML tags.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Spaceless extends Twig_Node
|
||||
{
|
||||
|
@ -13,8 +13,7 @@
|
||||
/**
|
||||
* Represents a text node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface
|
||||
{
|
||||
|
@ -12,19 +12,19 @@
|
||||
/**
|
||||
* Represents a node in the AST.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_NodeInterface
|
||||
interface Twig_NodeInterface extends Countable, IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
function compile(Twig_Compiler $compiler);
|
||||
public function compile(Twig_Compiler $compiler);
|
||||
|
||||
function getLine();
|
||||
public function getLine();
|
||||
|
||||
function getNodeTag();
|
||||
public function getNodeTag();
|
||||
}
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Represents a displayable node in the AST.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface Twig_NodeOutputInterface
|
||||
{
|
||||
|
@ -14,8 +14,7 @@
|
||||
*
|
||||
* It visits all nodes and their children and call the given visitor for each.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_NodeTraverser
|
||||
{
|
||||
|
@ -12,18 +12,18 @@
|
||||
/**
|
||||
* Twig_NodeVisitor_Escaper implements output escaping.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
|
||||
{
|
||||
protected $statusStack = array();
|
||||
protected $blocks = array();
|
||||
|
||||
protected $safeAnalysis;
|
||||
protected $traverser;
|
||||
protected $defaultStrategy = false;
|
||||
protected $safeVars = array();
|
||||
|
||||
function __construct()
|
||||
public function __construct()
|
||||
{
|
||||
$this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis();
|
||||
}
|
||||
@ -34,14 +34,21 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
* @return Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_AutoEscape) {
|
||||
if ($node instanceof Twig_Node_Module) {
|
||||
if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) {
|
||||
$this->defaultStrategy = $defaultStrategy;
|
||||
}
|
||||
$this->safeVars = array();
|
||||
} elseif ($node instanceof Twig_Node_AutoEscape) {
|
||||
$this->statusStack[] = $node->getAttribute('value');
|
||||
} elseif ($node instanceof Twig_Node_Block) {
|
||||
$this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
|
||||
} elseif ($node instanceof Twig_Node_Import) {
|
||||
$this->safeVars[] = $node->getNode('var')->getAttribute('name');
|
||||
}
|
||||
|
||||
return $node;
|
||||
@ -53,11 +60,14 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
* @return Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_Filter) {
|
||||
if ($node instanceof Twig_Node_Module) {
|
||||
$this->defaultStrategy = false;
|
||||
$this->safeVars = array();
|
||||
} elseif ($node instanceof Twig_Node_Expression_Filter) {
|
||||
return $this->preEscapeFilterNode($node, $env);
|
||||
} elseif ($node instanceof Twig_Node_Print) {
|
||||
return $this->escapePrintNode($node, $env, $this->needEscaping($env));
|
||||
@ -96,22 +106,18 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
|
||||
{
|
||||
$name = $filter->getNode('filter')->getAttribute('value');
|
||||
|
||||
if (false !== $f = $env->getFilter($name)) {
|
||||
$type = $f->getPreEscape();
|
||||
if (null === $type) {
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$node = $filter->getNode('node');
|
||||
if ($this->isSafeFor($type, $node, $env)) {
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$filter->setNode('node', $this->getEscaperFilter($type, $node));
|
||||
|
||||
$type = $env->getFilter($name)->getPreEscape();
|
||||
if (null === $type) {
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$node = $filter->getNode('node');
|
||||
if ($this->isSafeFor($type, $node, $env)) {
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$filter->setNode('node', $this->getEscaperFilter($type, $node));
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
@ -123,6 +129,9 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
|
||||
if (null === $this->traverser) {
|
||||
$this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis));
|
||||
}
|
||||
|
||||
$this->safeAnalysis->setSafeVars($this->safeVars);
|
||||
|
||||
$this->traverser->traverse($expression);
|
||||
$safe = $this->safeAnalysis->getSafe($expression);
|
||||
}
|
||||
@ -136,18 +145,15 @@ class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
|
||||
return $this->statusStack[count($this->statusStack) - 1];
|
||||
}
|
||||
|
||||
if ($env->hasExtension('escaper') && $env->getExtension('escaper')->isGlobal()) {
|
||||
return 'html';
|
||||
}
|
||||
|
||||
return false;
|
||||
return $this->defaultStrategy ? $this->defaultStrategy : false;
|
||||
}
|
||||
|
||||
protected function getEscaperFilter($type, Twig_NodeInterface $node)
|
||||
{
|
||||
$line = $node->getLine();
|
||||
$name = new Twig_Node_Expression_Constant('escape', $line);
|
||||
$args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line)));
|
||||
$args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line)));
|
||||
|
||||
return new Twig_Node_Expression_Filter($node, $name, $args, $line);
|
||||
}
|
||||
|
||||
|
@ -17,8 +17,7 @@
|
||||
* You can configure which optimizations you want to activate via the
|
||||
* optimizer mode.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
{
|
||||
@ -26,9 +25,12 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
const OPTIMIZE_NONE = 0;
|
||||
const OPTIMIZE_FOR = 2;
|
||||
const OPTIMIZE_RAW_FILTER = 4;
|
||||
const OPTIMIZE_VAR_ACCESS = 8;
|
||||
|
||||
protected $loops = array();
|
||||
protected $optimizers;
|
||||
protected $prependedNodes = array();
|
||||
protected $inABody = false;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -53,6 +55,20 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
$this->enterOptimizeFor($node, $env);
|
||||
}
|
||||
|
||||
if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
|
||||
if ($this->inABody) {
|
||||
if (!$node instanceof Twig_Node_Expression) {
|
||||
if (get_class($node) !== 'Twig_Node') {
|
||||
array_unshift($this->prependedNodes, array());
|
||||
}
|
||||
} else {
|
||||
$node = $this->optimizeVariables($node, $env);
|
||||
}
|
||||
} elseif ($node instanceof Twig_Node_Body) {
|
||||
$this->inABody = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
@ -61,6 +77,8 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
*/
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
$expression = $node instanceof Twig_Node_Expression;
|
||||
|
||||
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
|
||||
$this->leaveOptimizeFor($node, $env);
|
||||
}
|
||||
@ -69,20 +87,58 @@ class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
|
||||
$node = $this->optimizeRawFilter($node, $env);
|
||||
}
|
||||
|
||||
$node = $this->optimizeRenderBlock($node, $env);
|
||||
$node = $this->optimizePrintNode($node, $env);
|
||||
|
||||
if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
|
||||
if ($node instanceof Twig_Node_Body) {
|
||||
$this->inABody = false;
|
||||
} elseif ($this->inABody) {
|
||||
if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
|
||||
$nodes = array();
|
||||
foreach (array_unique($prependedNodes) as $name) {
|
||||
$nodes[] = new Twig_Node_SetTemp($name, $node->getLine());
|
||||
}
|
||||
|
||||
$nodes[] = $node;
|
||||
$node = new Twig_Node($nodes);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function optimizeVariables($node, $env)
|
||||
{
|
||||
if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
|
||||
$this->prependedNodes[0][] = $node->getAttribute('name');
|
||||
|
||||
return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine());
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Replaces "echo $this->renderBlock()" with "$this->displayBlock()".
|
||||
* Optimizes print nodes.
|
||||
*
|
||||
* It replaces:
|
||||
*
|
||||
* * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
|
||||
*
|
||||
* @param Twig_NodeInterface $node A Node
|
||||
* @param Twig_Environment $env The current Twig environment
|
||||
*/
|
||||
protected function optimizeRenderBlock($node, $env)
|
||||
protected function optimizePrintNode($node, $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference) {
|
||||
if (!$node instanceof Twig_Node_Print) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
if (
|
||||
$node->getNode('expr') instanceof Twig_Node_Expression_BlockReference ||
|
||||
$node->getNode('expr') instanceof Twig_Node_Expression_Parent
|
||||
) {
|
||||
$node->getNode('expr')->setAttribute('output', true);
|
||||
|
||||
return $node->getNode('expr');
|
||||
|
@ -3,27 +3,33 @@
|
||||
class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
|
||||
{
|
||||
protected $data = array();
|
||||
protected $safeVars = array();
|
||||
|
||||
public function setSafeVars($safeVars)
|
||||
{
|
||||
$this->safeVars = $safeVars;
|
||||
}
|
||||
|
||||
public function getSafe(Twig_NodeInterface $node)
|
||||
{
|
||||
$hash = spl_object_hash($node);
|
||||
if (isset($this->data[$hash])) {
|
||||
foreach($this->data[$hash] as $bucket) {
|
||||
foreach ($this->data[$hash] as $bucket) {
|
||||
if ($bucket['key'] === $node) {
|
||||
return $bucket['value'];
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
protected function setSafe(Twig_NodeInterface $node, array $safe)
|
||||
{
|
||||
$hash = spl_object_hash($node);
|
||||
if (isset($this->data[$hash])) {
|
||||
foreach($this->data[$hash] as &$bucket) {
|
||||
foreach ($this->data[$hash] as &$bucket) {
|
||||
if ($bucket['key'] === $node) {
|
||||
$bucket['value'] = $safe;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
@ -59,7 +65,11 @@ class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
|
||||
$name = $node->getNode('filter')->getAttribute('value');
|
||||
$args = $node->getNode('arguments');
|
||||
if (false !== $filter = $env->getFilter($name)) {
|
||||
$this->setSafe($node, $filter->getSafe($args));
|
||||
$safe = $filter->getSafe($args);
|
||||
if (null === $safe) {
|
||||
$safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety());
|
||||
}
|
||||
$this->setSafe($node, $safe);
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
@ -73,6 +83,20 @@ class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
} elseif ($node instanceof Twig_Node_Expression_MethodCall) {
|
||||
if ($node->getAttribute('safe')) {
|
||||
$this->setSafe($node, array('all'));
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
} elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) {
|
||||
$name = $node->getNode('node')->getAttribute('name');
|
||||
// attributes on template instances are safe
|
||||
if ('_self' == $name || in_array($name, $this->safeVars)) {
|
||||
$this->setSafe($node, array('all'));
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Twig_NodeVisitor_Sandbox implements sandboxing.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
|
||||
{
|
||||
@ -28,7 +27,7 @@ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
* @return Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
@ -70,7 +69,7 @@ class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
* @return Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Twig_NodeVisitorInterface is the interface the all node visitor classes must implement.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface Twig_NodeVisitorInterface
|
||||
{
|
||||
@ -23,9 +22,9 @@ interface Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
* @return Twig_NodeInterface The modified node
|
||||
*/
|
||||
function enterNode(Twig_NodeInterface $node, Twig_Environment $env);
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env);
|
||||
|
||||
/**
|
||||
* Called after child nodes are visited.
|
||||
@ -33,9 +32,9 @@ interface Twig_NodeVisitorInterface
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
* @return Twig_NodeInterface|false The modified node or false if the node must be removed
|
||||
*/
|
||||
function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
|
||||
|
||||
/**
|
||||
* Returns the priority for this visitor.
|
||||
@ -44,5 +43,5 @@ interface Twig_NodeVisitorInterface
|
||||
*
|
||||
* @return integer The priority level
|
||||
*/
|
||||
function getPriority();
|
||||
public function getPriority();
|
||||
}
|
||||
|
@ -13,11 +13,11 @@
|
||||
/**
|
||||
* Default parser implementation.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Parser implements Twig_ParserInterface
|
||||
{
|
||||
protected $stack = array();
|
||||
protected $stream;
|
||||
protected $parent;
|
||||
protected $handlers;
|
||||
@ -28,9 +28,9 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
protected $macros;
|
||||
protected $env;
|
||||
protected $reservedMacroNames;
|
||||
protected $importedFunctions;
|
||||
protected $tmpVarCount;
|
||||
protected $importedSymbols;
|
||||
protected $traits;
|
||||
protected $embeddedTemplates = array();
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
@ -42,28 +42,45 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
public function getEnvironment()
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
public function getVarName()
|
||||
{
|
||||
return sprintf('__internal_%s_%d', substr($this->env->getTemplateClass($this->stream->getFilename()), strlen($this->env->getTemplateClassPrefix())), ++$this->tmpVarCount);
|
||||
return sprintf('__internal_%s', hash('sha1', uniqid(mt_rand(), true), false));
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->stream->getFilename();
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
*/
|
||||
public function parse(Twig_TokenStream $stream)
|
||||
public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
|
||||
{
|
||||
$this->tmpVarCount = 0;
|
||||
// push all variables into the stack to keep the current state of the parser
|
||||
$vars = get_object_vars($this);
|
||||
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser']);
|
||||
$this->stack[] = $vars;
|
||||
|
||||
// tag handlers
|
||||
$this->handlers = $this->env->getTokenParsers();
|
||||
$this->handlers->setParser($this);
|
||||
if (null === $this->handlers) {
|
||||
$this->handlers = $this->env->getTokenParsers();
|
||||
$this->handlers->setParser($this);
|
||||
}
|
||||
|
||||
// node visitors
|
||||
$this->visitors = $this->env->getNodeVisitors();
|
||||
if (null === $this->visitors) {
|
||||
$this->visitors = $this->env->getNodeVisitors();
|
||||
}
|
||||
|
||||
if (null === $this->expressionParser) {
|
||||
$this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators());
|
||||
@ -75,10 +92,11 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
$this->macros = array();
|
||||
$this->traits = array();
|
||||
$this->blockStack = array();
|
||||
$this->importedFunctions = array(array());
|
||||
$this->importedSymbols = array(array());
|
||||
$this->embeddedTemplates = array();
|
||||
|
||||
try {
|
||||
$body = $this->subparse(null);
|
||||
$body = $this->subparse($test, $dropNeedle);
|
||||
|
||||
if (null !== $this->parent) {
|
||||
if (null === $body = $this->filterBodyNodes($body)) {
|
||||
@ -86,18 +104,29 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
}
|
||||
}
|
||||
} catch (Twig_Error_Syntax $e) {
|
||||
if (null === $e->getTemplateFile()) {
|
||||
$e->setTemplateFile($this->stream->getFilename());
|
||||
if (!$e->getTemplateFile()) {
|
||||
$e->setTemplateFile($this->getFilename());
|
||||
}
|
||||
|
||||
if (!$e->getTemplateLine()) {
|
||||
$e->setTemplateLine($this->stream->getCurrent()->getLine());
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
$node = new Twig_Node_Module($body, $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->stream->getFilename());
|
||||
$node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->getFilename());
|
||||
|
||||
$traverser = new Twig_NodeTraverser($this->env, $this->visitors);
|
||||
|
||||
return $traverser->traverse($node);
|
||||
$node = $traverser->traverse($node);
|
||||
|
||||
// restore previous stack so previous parse() call can resume working
|
||||
foreach (array_pop($this->stack) as $key => $val) {
|
||||
$this->$key = $val;
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function subparse($test, $dropNeedle = false)
|
||||
@ -123,7 +152,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
$token = $this->getCurrentToken();
|
||||
|
||||
if ($token->getType() !== Twig_Token::NAME_TYPE) {
|
||||
throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->stream->getFilename());
|
||||
throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->getFilename());
|
||||
}
|
||||
|
||||
if (null !== $test && call_user_func($test, $token)) {
|
||||
@ -141,10 +170,20 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
$subparser = $this->handlers->getTokenParser($token->getValue());
|
||||
if (null === $subparser) {
|
||||
if (null !== $test) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected tag name "%s" (expecting closing tag for the "%s" tag defined near line %s)', $token->getValue(), $test[0]->getTag(), $lineno), $token->getLine(), $this->stream->getFilename());
|
||||
$error = sprintf('Unexpected tag name "%s"', $token->getValue());
|
||||
if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) {
|
||||
$error .= sprintf(' (expecting closing tag for the "%s" tag defined near line %s)', $test[0]->getTag(), $lineno);
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax($error, $token->getLine(), $this->getFilename());
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax(sprintf('Unknown tag name "%s"', $token->getValue()), $token->getLine(), $this->stream->getFilename());
|
||||
$message = sprintf('Unknown tag name "%s"', $token->getValue());
|
||||
if ($alternatives = $this->env->computeAlternatives($token->getValue(), array_keys($this->env->getTags()))) {
|
||||
$message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax($message, $token->getLine(), $this->getFilename());
|
||||
}
|
||||
|
||||
$this->stream->next();
|
||||
@ -156,7 +195,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', -1, $this->stream->getFilename());
|
||||
throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->getFilename());
|
||||
}
|
||||
}
|
||||
|
||||
@ -202,9 +241,14 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
return isset($this->blocks[$name]);
|
||||
}
|
||||
|
||||
public function getBlock($name)
|
||||
{
|
||||
return $this->blocks[$name];
|
||||
}
|
||||
|
||||
public function setBlock($name, $value)
|
||||
{
|
||||
$this->blocks[$name] = $value;
|
||||
$this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine());
|
||||
}
|
||||
|
||||
public function hasMacro($name)
|
||||
@ -223,7 +267,7 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
}
|
||||
|
||||
if (in_array($name, $this->reservedMacroNames)) {
|
||||
throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword', $name), $node->getLine());
|
||||
throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword', $name), $node->getLine(), $this->getFilename());
|
||||
}
|
||||
|
||||
$this->macros[$name] = $node;
|
||||
@ -234,33 +278,45 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
$this->traits[] = $trait;
|
||||
}
|
||||
|
||||
public function addImportedFunction($alias, $name, Twig_Node_Expression $node)
|
||||
public function hasTraits()
|
||||
{
|
||||
$this->importedFunctions[0][$alias] = array('name' => $name, 'node' => $node);
|
||||
return count($this->traits) > 0;
|
||||
}
|
||||
|
||||
public function getImportedFunction($alias)
|
||||
public function embedTemplate(Twig_Node_Module $template)
|
||||
{
|
||||
foreach ($this->importedFunctions as $functions) {
|
||||
if (isset($functions[$alias])) {
|
||||
return $functions[$alias];
|
||||
$template->setIndex(mt_rand());
|
||||
|
||||
$this->embeddedTemplates[] = $template;
|
||||
}
|
||||
|
||||
public function addImportedSymbol($type, $alias, $name = null, Twig_Node_Expression $node = null)
|
||||
{
|
||||
$this->importedSymbols[0][$type][$alias] = array('name' => $name, 'node' => $node);
|
||||
}
|
||||
|
||||
public function getImportedSymbol($type, $alias)
|
||||
{
|
||||
foreach ($this->importedSymbols as $functions) {
|
||||
if (isset($functions[$type][$alias])) {
|
||||
return $functions[$type][$alias];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function isMainScope()
|
||||
{
|
||||
return 1 === count($this->importedFunctions);
|
||||
return 1 === count($this->importedSymbols);
|
||||
}
|
||||
|
||||
public function pushLocalScope()
|
||||
{
|
||||
array_unshift($this->importedFunctions, array());
|
||||
array_unshift($this->importedSymbols, array());
|
||||
}
|
||||
|
||||
public function popLocalScope()
|
||||
{
|
||||
array_shift($this->importedFunctions);
|
||||
array_shift($this->importedSymbols);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -311,7 +367,11 @@ class Twig_Parser implements Twig_ParserInterface
|
||||
||
|
||||
(!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface)
|
||||
) {
|
||||
throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->stream->getFilename());
|
||||
if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) {
|
||||
throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->getFilename());
|
||||
}
|
||||
|
||||
throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->getFilename());
|
||||
}
|
||||
|
||||
// bypass "set" nodes as they "capture" the output
|
||||
|
@ -12,17 +12,17 @@
|
||||
/**
|
||||
* Interface implemented by parser classes.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @deprecated since 1.12 (to be removed in 2.0)
|
||||
*/
|
||||
interface Twig_ParserInterface
|
||||
{
|
||||
/**
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
*/
|
||||
function parse(Twig_TokenStream $code);
|
||||
public function parse(Twig_TokenStream $stream);
|
||||
}
|
||||
|
@ -12,8 +12,7 @@
|
||||
/**
|
||||
* Exception thrown when a security error occurs at runtime.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Twig_Sandbox_SecurityError extends Twig_Error
|
||||
{
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user