mirror of
https://github.com/alchemy-fr/Phraseanet.git
synced 2025-10-17 23:13:15 +00:00
initial import
This commit is contained in:
9
lib/classes/Twig/AUTHORS
Normal file
9
lib/classes/Twig/AUTHORS
Normal file
@@ -0,0 +1,9 @@
|
||||
Twig is written and maintained by the Twig Team:
|
||||
|
||||
Lead Developer:
|
||||
|
||||
- Fabien Potencier <fabien.potencier@symfony-project.org>
|
||||
|
||||
Project Founder:
|
||||
|
||||
- Armin Ronacher <armin.ronacher@active-4.com>
|
46
lib/classes/Twig/Autoloader.php
Executable file
46
lib/classes/Twig/Autoloader.php
Executable file
@@ -0,0 +1,46 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Autoloads Twig classes.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Autoloader
|
||||
{
|
||||
/**
|
||||
* Registers Twig_Autoloader as an SPL autoloader.
|
||||
*/
|
||||
static public function register()
|
||||
{
|
||||
ini_set('unserialize_callback_func', 'spl_autoload_call');
|
||||
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
|
||||
*/
|
||||
static public function autoload($class)
|
||||
{
|
||||
if (0 !== strpos($class, 'Twig')) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (file_exists($file = dirname(__FILE__).'/../'.str_replace('_', '/', $class).'.php')) {
|
||||
require $file;
|
||||
}
|
||||
}
|
||||
}
|
208
lib/classes/Twig/CHANGELOG
Normal file
208
lib/classes/Twig/CHANGELOG
Normal file
@@ -0,0 +1,208 @@
|
||||
* 0.9.9
|
||||
|
||||
Backward incompatibilities:
|
||||
* the self special variable has been renamed to _self
|
||||
* the odd and even filters are now tests:
|
||||
{{ foo|odd }} must now be written {{ foo is odd }}
|
||||
* the "safe" filter has been renamed to "raw"
|
||||
* in Node classes,
|
||||
sub-nodes are now accessed via getNode() (instead of property access)
|
||||
attributes via getAttribute() (instead of array access)
|
||||
* the urlencode filter had been renamed to url_encode
|
||||
* the include tag now merges the passed variables with the current context by default
|
||||
(the old behavior is still possible by adding the "only" keyword)
|
||||
* Moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime)
|
||||
|
||||
* added the spaceless tag
|
||||
* removed trim_blocks option
|
||||
* added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar())
|
||||
* changed all exceptions to extend Twig_Error
|
||||
* fixed unary expressions ({{ not(1 or 0) }})
|
||||
* fixed child templates (with an extend tag) that uses one or more imports
|
||||
* added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }})
|
||||
* escaping has been rewritten (from pre-escaping to post-escaping)
|
||||
* the implementation of template inheritance has been rewritten
|
||||
(blocks can now be called individually and still work with inheritance)
|
||||
* fixed error handling for if tag when a syntax error occurs within a subparse process
|
||||
* added a way to implement custom logic for resolving token parsers given a tag name
|
||||
* fixed js escaper to be stricter (now uses a whilelist-based js escaper)
|
||||
* added the following filers: "constant", "trans", "replace", "json_encode"
|
||||
* added a "constant" test
|
||||
* fixed objects with __toString() not being autoescaped
|
||||
* fixed subscript expressions when calling __call() (methods now keep the case)
|
||||
* added "test" feature (accessible via the "is" operator)
|
||||
* removed the debug tag (should be done in an extension)
|
||||
* fixed trans tag when no vars are used in plural form
|
||||
* fixed race condition when writing template cache
|
||||
* added the special _charset variable to reference the current charset
|
||||
* added the special _context variable to reference the current context
|
||||
* renamed self to _self (to avoid conflict)
|
||||
* fixed Twig_Template::getAttribute() for protected properties
|
||||
|
||||
* 0.9.8 (2010-06-28)
|
||||
|
||||
Backward incompatibilities:
|
||||
* the trans tag plural count is now attached to the plural tag:
|
||||
old: `{% trans count %}...{% plural %}...{% endtrans %}`
|
||||
new: `{% trans %}...{% plural count %}...{% endtrans %}`
|
||||
|
||||
* added a way to translate strings coming from a variable ({% trans var %})
|
||||
* fixed trans tag when used with the Escaper extension
|
||||
* fixed default cache umask
|
||||
* removed Twig_Template instances from the debug tag output
|
||||
* fixed objects with __isset() defined
|
||||
* fixed set tag when used with a capture
|
||||
* fixed type hinting for Twig_Environment::addFilter() method
|
||||
|
||||
* 0.9.7 (2010-06-12)
|
||||
|
||||
Backward incompatibilities:
|
||||
* changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %})
|
||||
* removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
|
||||
* refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
|
||||
|
||||
* added self as a special variable that refers to the current template (useful for importing macros from the current template)
|
||||
* added Twig_Template instance support to the include tag
|
||||
* added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
|
||||
* added a grammar sub-framework to ease the creation of custom tags
|
||||
* fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface)
|
||||
* removed the Twig_Resource::resolveMissingFilter() method
|
||||
* fixed the filter tag which did not apply filtering to included files
|
||||
* added a bunch of unit tests
|
||||
* added a bunch of phpdoc
|
||||
* added a sandbox tag in the sandbox extension
|
||||
* changed the date filter to support any date format supported by DateTime
|
||||
* added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default)
|
||||
* added the lexer, parser, and compiler as arguments to the Twig_Environment constructor
|
||||
* changed the cache option to only accepts an explicit path to a cache directory or false
|
||||
* added a way to add token parsers, filters, and visitors without creating an extension
|
||||
* added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
|
||||
* changed the generated code to match the new coding standards
|
||||
* fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
|
||||
* added an exception when a child template has a non-empty body (as it is always ignored when rendering)
|
||||
|
||||
* 0.9.6 (2010-05-12)
|
||||
|
||||
* fixed variables defined outside a loop and for which the value changes in a for loop
|
||||
* fixed the test suite for PHP 5.2 and older versions of PHPUnit
|
||||
* added support for __call() in expression resolution
|
||||
* fixed node visiting for macros (macros are now visited by visitors as any other node)
|
||||
* fixed nested block definitions with a parent call (rarely useful but nonetheless supported now)
|
||||
* added the cycle filter
|
||||
* fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII
|
||||
* added a long-syntax for the set tag ({% set foo %}...{% endset %})
|
||||
* unit tests are now powered by PHPUnit
|
||||
* added support for gettext via the `i18n` extension
|
||||
* fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values
|
||||
* added a more useful exception if an if tag is not closed properly
|
||||
* added support for escaping strategy in the autoescape tag
|
||||
* fixed lexer when a template has a big chunk of text between/in a block
|
||||
|
||||
* 0.9.5 (2010-01-20)
|
||||
|
||||
As for any new release, don't forget to remove all cached templates after
|
||||
upgrading.
|
||||
|
||||
If you have defined custom filters, you MUST upgrade them for this release. To
|
||||
upgrade, replace "array" with "new Twig_Filter_Function", and replace the
|
||||
environment constant by the "needs_environment" option:
|
||||
|
||||
// before
|
||||
'even' => array('twig_is_even_filter', false),
|
||||
'escape' => array('twig_escape_filter', true),
|
||||
|
||||
// after
|
||||
'even' => new Twig_Filter_Function('twig_is_even_filter'),
|
||||
'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
|
||||
|
||||
If you have created NodeTransformer classes, you will need to upgrade them to
|
||||
the new interface (please note that the interface is not yet considered
|
||||
stable).
|
||||
|
||||
* fixed list nodes that did not extend the Twig_NodeListInterface
|
||||
* added the "without loop" option to the for tag (it disables the generation of the loop variable)
|
||||
* refactored node transformers to node visitors
|
||||
* fixed automatic-escaping for blocks
|
||||
* added a way to specify variables to pass to an included template
|
||||
* changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
|
||||
* improved the filter system to allow object methods to be used as filters
|
||||
* changed the Array and String loaders to actually make use of the cache mechanism
|
||||
* included the default filter function definitions in the extension class files directly (Core, Escaper)
|
||||
* added the // operator (like the floor() PHP function)
|
||||
* added the .. operator (as a syntactic sugar for the range filter when the step is 1)
|
||||
* added the in operator (as a syntactic sugar for the in filter)
|
||||
* added the following filters in the Core extension: in, range
|
||||
* added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
|
||||
* enhanced some error messages to provide better feedback in case of parsing errors
|
||||
|
||||
* 0.9.4 (2009-12-02)
|
||||
|
||||
If you have custom loaders, you MUST upgrade them for this release: The
|
||||
Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
|
||||
been changed (see the source code for more information or the documentation).
|
||||
|
||||
* added support for DateTime instances for the date filter
|
||||
* fixed loop.last when the array only has one item
|
||||
* made it possible to insert newlines in tag and variable blocks
|
||||
* fixed a bug when a literal '\n' were present in a template text
|
||||
* fixed bug when the filename of a template contains */
|
||||
* refactored loaders
|
||||
|
||||
* 0.9.3 (2009-11-11)
|
||||
|
||||
This release is NOT backward compatible with the previous releases.
|
||||
|
||||
The loaders do not take the cache and autoReload arguments anymore. Instead,
|
||||
the Twig_Environment class has two new options: cache and auto_reload.
|
||||
Upgrading your code means changing this kind of code:
|
||||
|
||||
$loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true);
|
||||
$twig = new Twig_Environment($loader);
|
||||
|
||||
to something like this:
|
||||
|
||||
$loader = new Twig_Loader_Filesystem('/path/to/templates');
|
||||
$twig = new Twig_Environment($loader, array(
|
||||
'cache' => '/path/to/compilation_cache',
|
||||
'auto_reload' => true,
|
||||
));
|
||||
|
||||
* deprecated the "items" filter as it is not needed anymore
|
||||
* made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader
|
||||
* optimized template loading speed
|
||||
* removed output when an error occurs in a template and render() is used
|
||||
* made major speed improvements for loops (up to 300% on even the smallest loops)
|
||||
* added properties as part of the sandbox mode
|
||||
* added public properties support (obj.item can now be the item property on the obj object)
|
||||
* extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} )
|
||||
* fixed bug when \ was used in HTML
|
||||
|
||||
* 0.9.2 (2009-10-29)
|
||||
|
||||
* made some speed optimizations
|
||||
* changed the cache extension to .php
|
||||
* added a js escaping strategy
|
||||
* added support for short block tag
|
||||
* changed the filter tag to allow chained filters
|
||||
* made lexer more flexible as you can now change the default delimiters
|
||||
* added set tag
|
||||
* changed default directory permission when cache dir does not exist (more secure)
|
||||
* added macro support
|
||||
* changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance
|
||||
* made Twig_Autoloader::autoload() a static method
|
||||
* avoid writing template file if an error occurs
|
||||
* added $ escaping when outputting raw strings
|
||||
* enhanced some error messages to ease debugging
|
||||
* fixed empty cache files when the template contains an error
|
||||
|
||||
* 0.9.1 (2009-10-14)
|
||||
|
||||
* fixed a bug in PHP 5.2.6
|
||||
* fixed numbers with one than one decimal
|
||||
* added support for method calls with arguments ({{ foo.bar('a', 43) }})
|
||||
* made small speed optimizations
|
||||
* made minor tweaks to allow better extensibility and flexibility
|
||||
|
||||
* 0.9.0 (2009-10-12)
|
||||
|
||||
* Initial release
|
223
lib/classes/Twig/Compiler.php
Executable file
223
lib/classes/Twig/Compiler.php
Executable file
@@ -0,0 +1,223 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Compiles a node to PHP code.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Compiler implements Twig_CompilerInterface
|
||||
{
|
||||
protected $lastLine;
|
||||
protected $source;
|
||||
protected $indentation;
|
||||
protected $env;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Twig_Environment $env The twig environment instance
|
||||
*/
|
||||
public function __construct(Twig_Environment $env = null)
|
||||
{
|
||||
if (null !== $env) {
|
||||
$this->setEnvironment($env);
|
||||
}
|
||||
}
|
||||
|
||||
public function setEnvironment(Twig_Environment $env)
|
||||
{
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the environment instance related to this compiler.
|
||||
*
|
||||
* @return Twig_Environment The environment instance
|
||||
*/
|
||||
public function getEnvironment()
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current PHP code after compilation.
|
||||
*
|
||||
* @return string The PHP code
|
||||
*/
|
||||
public function getSource()
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a node.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to compile
|
||||
* @param integer $indent The current indentation
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function compile(Twig_NodeInterface $node, $indentation = 0)
|
||||
{
|
||||
$this->lastLine = null;
|
||||
$this->source = '';
|
||||
$this->indentation = $indentation;
|
||||
|
||||
$node->compile($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function subcompile(Twig_NodeInterface $node, $raw = true)
|
||||
{
|
||||
if (false === $raw)
|
||||
{
|
||||
$this->addIndentation();
|
||||
}
|
||||
|
||||
$node->compile($this);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a raw string to the compiled code.
|
||||
*
|
||||
* @param string $string The string
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function raw($string)
|
||||
{
|
||||
$this->source .= $string;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the compiled code by adding indentation.
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function write()
|
||||
{
|
||||
$strings = func_get_args();
|
||||
foreach ($strings as $string) {
|
||||
$this->addIndentation();
|
||||
$this->source .= $string;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function addIndentation()
|
||||
{
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a quoted string to the compiled code.
|
||||
*
|
||||
* @param string $string The string
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function string($value)
|
||||
{
|
||||
$this->source .= sprintf('"%s"', addcslashes($value, "\t\"\$\\"));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PHP representation of a given value.
|
||||
*
|
||||
* @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)) {
|
||||
$this->raw($value);
|
||||
} else if (null === $value) {
|
||||
$this->raw('null');
|
||||
} else if (is_bool($value)) {
|
||||
$this->raw($value ? 'true' : 'false');
|
||||
} else if (is_array($value)) {
|
||||
$this->raw('array(');
|
||||
$i = 0;
|
||||
foreach ($value as $key => $value) {
|
||||
if ($i++) {
|
||||
$this->raw(', ');
|
||||
}
|
||||
$this->repr($key);
|
||||
$this->raw(' => ');
|
||||
$this->repr($value);
|
||||
}
|
||||
$this->raw(')');
|
||||
} else {
|
||||
$this->string($value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds debugging information.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The related twig node
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function addDebugInfo(Twig_NodeInterface $node)
|
||||
{
|
||||
if ($node->getLine() != $this->lastLine) {
|
||||
$this->lastLine = $node->getLine();
|
||||
$this->write("// line {$node->getLine()}\n");
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Indents the generated code.
|
||||
*
|
||||
* @param integer $indent The number of indentation to add
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function indent($step = 1)
|
||||
{
|
||||
$this->indentation += $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Outdents the generated code.
|
||||
*
|
||||
* @param integer $indent The number of indentation to remove
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function outdent($step = 1)
|
||||
{
|
||||
$this->indentation -= $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
}
|
35
lib/classes/Twig/CompilerInterface.php
Executable file
35
lib/classes/Twig/CompilerInterface.php
Executable file
@@ -0,0 +1,35 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface implemented by compiler classes.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_CompilerInterface
|
||||
{
|
||||
/**
|
||||
* Compiles a node.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to compile
|
||||
*
|
||||
* @return Twig_Compiler The current compiler instance
|
||||
*/
|
||||
public function compile(Twig_NodeInterface $node);
|
||||
|
||||
/**
|
||||
* Gets the current PHP code after compilation.
|
||||
*
|
||||
* @return string The PHP code
|
||||
*/
|
||||
public function getSource();
|
||||
}
|
431
lib/classes/Twig/Environment.php
Executable file
431
lib/classes/Twig/Environment.php
Executable file
@@ -0,0 +1,431 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
class Twig_Environment
|
||||
{
|
||||
const VERSION = '0.9.9-DEV';
|
||||
|
||||
protected $charset;
|
||||
protected $loader;
|
||||
protected $debug;
|
||||
protected $autoReload;
|
||||
protected $cache;
|
||||
protected $lexer;
|
||||
protected $parser;
|
||||
protected $compiler;
|
||||
protected $baseTemplateClass;
|
||||
protected $extensions;
|
||||
protected $parsers;
|
||||
protected $visitors;
|
||||
protected $filters;
|
||||
protected $tests;
|
||||
protected $runtimeInitialized;
|
||||
protected $loadedTemplates;
|
||||
protected $strictVariables;
|
||||
|
||||
/**
|
||||
* 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).
|
||||
*
|
||||
* * 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)
|
||||
*
|
||||
* * auto_reload: Whether to reload the template is the original source changed.
|
||||
* If you don't provide the auto_reload option, it will be
|
||||
* determined automatically base on the debug value.
|
||||
*
|
||||
* * strict_variables: Whether to ignore invalid variables in templates
|
||||
* (default to false).
|
||||
*
|
||||
* @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
|
||||
* @param array $options An array of options
|
||||
* @param Twig_LexerInterface $lexer A Twig_LexerInterface instance
|
||||
* @param Twig_ParserInterface $parser A Twig_ParserInterface instance
|
||||
* @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance
|
||||
*/
|
||||
public function __construct(Twig_LoaderInterface $loader = null, $options = array(), Twig_LexerInterface $lexer = null, Twig_ParserInterface $parser = null, Twig_CompilerInterface $compiler = null)
|
||||
{
|
||||
if (null !== $loader) {
|
||||
$this->setLoader($loader);
|
||||
}
|
||||
|
||||
$this->setLexer(null !== $lexer ? $lexer : new Twig_Lexer());
|
||||
$this->setParser(null !== $parser ? $parser : new Twig_Parser());
|
||||
$this->setCompiler(null !== $compiler ? $compiler : new Twig_Compiler());
|
||||
|
||||
$this->debug = isset($options['debug']) ? (bool) $options['debug'] : false;
|
||||
$this->charset = isset($options['charset']) ? $options['charset'] : 'UTF-8';
|
||||
$this->baseTemplateClass = isset($options['base_template_class']) ? $options['base_template_class'] : 'Twig_Template';
|
||||
$this->autoReload = isset($options['auto_reload']) ? (bool) $options['auto_reload'] : $this->debug;
|
||||
$this->extensions = array('core' => new Twig_Extension_Core());
|
||||
$this->strictVariables = isset($options['strict_variables']) ? (bool) $options['strict_variables'] : false;
|
||||
$this->runtimeInitialized = false;
|
||||
if (isset($options['cache']) && $options['cache']) {
|
||||
$this->setCache($options['cache']);
|
||||
}
|
||||
}
|
||||
|
||||
public function getBaseTemplateClass()
|
||||
{
|
||||
return $this->baseTemplateClass;
|
||||
}
|
||||
|
||||
public function setBaseTemplateClass($class)
|
||||
{
|
||||
$this->baseTemplateClass = $class;
|
||||
}
|
||||
|
||||
public function enableDebug()
|
||||
{
|
||||
$this->debug = true;
|
||||
}
|
||||
|
||||
public function disableDebug()
|
||||
{
|
||||
$this->debug = false;
|
||||
}
|
||||
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->debug;
|
||||
}
|
||||
|
||||
public function isAutoReload()
|
||||
{
|
||||
return $this->autoReload;
|
||||
}
|
||||
|
||||
public function setAutoReload($autoReload)
|
||||
{
|
||||
$this->autoReload = (Boolean) $autoReload;
|
||||
}
|
||||
|
||||
public function enableStrictVariables()
|
||||
{
|
||||
$this->strictVariables = true;
|
||||
}
|
||||
|
||||
public function disableStrictVariables()
|
||||
{
|
||||
$this->strictVariables = false;
|
||||
}
|
||||
|
||||
public function isStrictVariables()
|
||||
{
|
||||
return $this->strictVariables;
|
||||
}
|
||||
|
||||
public function getCache()
|
||||
{
|
||||
return $this->cache;
|
||||
}
|
||||
|
||||
public function setCache($cache)
|
||||
{
|
||||
$this->cache = $cache;
|
||||
|
||||
if ($this->cache && !is_dir($this->cache)) {
|
||||
mkdir($this->cache, 0777, true);
|
||||
}
|
||||
}
|
||||
|
||||
public function getCacheFilename($name)
|
||||
{
|
||||
return $this->getCache() ? $this->getCache().'/'.$this->getTemplateClass($name).'.php' : false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the template class associated with the given string.
|
||||
*
|
||||
* @param string $name The name for which to calculate the template class name
|
||||
*
|
||||
* @return string The template class name
|
||||
*/
|
||||
public function getTemplateClass($name)
|
||||
{
|
||||
return '__TwigTemplate_'.md5($this->loader->getCacheKey($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a template by name.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return Twig_TemplateInterface A template instance representing the given template name
|
||||
*/
|
||||
public function loadTemplate($name)
|
||||
{
|
||||
$cls = $this->getTemplateClass($name);
|
||||
|
||||
if (isset($this->loadedTemplates[$cls])) {
|
||||
return $this->loadedTemplates[$cls];
|
||||
}
|
||||
|
||||
if (!class_exists($cls, false)) {
|
||||
if (false === $cache = $this->getCacheFilename($name)) {
|
||||
eval('?>'.$this->compileSource($this->loader->getSource($name), $name));
|
||||
} else {
|
||||
if (!file_exists($cache) || ($this->isAutoReload() && !$this->loader->isFresh($name, filemtime($cache)))) {
|
||||
$this->writeCacheFile($cache, $this->compileSource($this->loader->getSource($name), $name));
|
||||
}
|
||||
|
||||
require_once $cache;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->runtimeInitialized) {
|
||||
$this->initRuntime();
|
||||
}
|
||||
|
||||
return $this->loadedTemplates[$cls] = new $cls($this);
|
||||
}
|
||||
|
||||
public function clearTemplateCache()
|
||||
{
|
||||
$this->loadedTemplates = array();
|
||||
}
|
||||
|
||||
public function getLexer()
|
||||
{
|
||||
return $this->lexer;
|
||||
}
|
||||
|
||||
public function setLexer(Twig_LexerInterface $lexer)
|
||||
{
|
||||
$this->lexer = $lexer;
|
||||
$lexer->setEnvironment($this);
|
||||
}
|
||||
|
||||
public function tokenize($source, $name)
|
||||
{
|
||||
return $this->getLexer()->tokenize($source, $name);
|
||||
}
|
||||
|
||||
public function getParser()
|
||||
{
|
||||
return $this->parser;
|
||||
}
|
||||
|
||||
public function setParser(Twig_ParserInterface $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
$parser->setEnvironment($this);
|
||||
}
|
||||
|
||||
public function parse(Twig_TokenStream $tokens)
|
||||
{
|
||||
return $this->getParser()->parse($tokens);
|
||||
}
|
||||
|
||||
public function getCompiler()
|
||||
{
|
||||
return $this->compiler;
|
||||
}
|
||||
|
||||
public function setCompiler(Twig_CompilerInterface $compiler)
|
||||
{
|
||||
$this->compiler = $compiler;
|
||||
$compiler->setEnvironment($this);
|
||||
}
|
||||
|
||||
public function compile(Twig_NodeInterface $node)
|
||||
{
|
||||
return $this->getCompiler()->compile($node)->getSource();
|
||||
}
|
||||
|
||||
public function compileSource($source, $name)
|
||||
{
|
||||
return $this->compile($this->parse($this->tokenize($source, $name)));
|
||||
}
|
||||
|
||||
public function setLoader(Twig_LoaderInterface $loader)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
}
|
||||
|
||||
public function getLoader()
|
||||
{
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
public function setCharset($charset)
|
||||
{
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
public function getCharset()
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
public function initRuntime()
|
||||
{
|
||||
$this->runtimeInitialized = true;
|
||||
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$extension->initRuntime($this);
|
||||
}
|
||||
}
|
||||
|
||||
public function hasExtension($name)
|
||||
{
|
||||
return isset($this->extensions[$name]);
|
||||
}
|
||||
|
||||
public function getExtension($name)
|
||||
{
|
||||
if (!isset($this->extensions[$name])) {
|
||||
throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name));
|
||||
}
|
||||
|
||||
return $this->extensions[$name];
|
||||
}
|
||||
|
||||
public function addExtension(Twig_ExtensionInterface $extension)
|
||||
{
|
||||
$this->extensions[$extension->getName()] = $extension;
|
||||
}
|
||||
|
||||
public function removeExtension($name)
|
||||
{
|
||||
unset($this->extensions[$name]);
|
||||
}
|
||||
|
||||
public function setExtensions(array $extensions)
|
||||
{
|
||||
foreach ($extensions as $extension) {
|
||||
$this->addExtension($extension);
|
||||
}
|
||||
}
|
||||
|
||||
public function getExtensions()
|
||||
{
|
||||
return $this->extensions;
|
||||
}
|
||||
|
||||
public function addTokenParser(Twig_TokenParserInterface $parser)
|
||||
{
|
||||
if (null === $this->parsers) {
|
||||
$this->getTokenParsers();
|
||||
}
|
||||
|
||||
$this->parsers->addTokenParser($parser);
|
||||
}
|
||||
|
||||
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');
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parsers;
|
||||
}
|
||||
|
||||
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
|
||||
{
|
||||
if (null === $this->visitors) {
|
||||
$this->getNodeVisitors();
|
||||
}
|
||||
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
if (null === $this->visitors) {
|
||||
$this->visitors = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->visitors = array_merge($this->visitors, $extension->getNodeVisitors());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->visitors;
|
||||
}
|
||||
|
||||
public function addFilter($name, Twig_FilterInterface $filter)
|
||||
{
|
||||
if (null === $this->filters) {
|
||||
$this->getFilters();
|
||||
}
|
||||
|
||||
$this->filters[$name] = $filter;
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
if (null === $this->filters) {
|
||||
$this->filters = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->filters = array_merge($this->filters, $extension->getFilters());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function addTest($name, Twig_TestInterface $test)
|
||||
{
|
||||
if (null === $this->tests) {
|
||||
$this->getTests();
|
||||
}
|
||||
|
||||
$this->tests[$name] = $test;
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
if (null === $this->tests) {
|
||||
$this->tests = array();
|
||||
foreach ($this->getExtensions() as $extension) {
|
||||
$this->tests = array_merge($this->tests, $extension->getTests());
|
||||
}
|
||||
}
|
||||
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
protected function writeCacheFile($file, $content)
|
||||
{
|
||||
$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);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Failed to write cache file "%s".', $file));
|
||||
}
|
||||
}
|
20
lib/classes/Twig/Error.php
Executable file
20
lib/classes/Twig/Error.php
Executable file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Twig base exception.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Error extends Exception
|
||||
{
|
||||
}
|
20
lib/classes/Twig/Error/Loader.php
Normal file
20
lib/classes/Twig/Error/Loader.php
Normal file
@@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs during template loading.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Error_Loader extends Twig_Error
|
||||
{
|
||||
}
|
21
lib/classes/Twig/Error/Runtime.php
Normal file
21
lib/classes/Twig/Error/Runtime.php
Normal file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs at runtime.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Error_Runtime extends Twig_Error
|
||||
{
|
||||
}
|
52
lib/classes/Twig/Error/Syntax.php
Normal file
52
lib/classes/Twig/Error/Syntax.php
Normal file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when a syntax error occurs during lexing or parsing of a template.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Error_Syntax extends Twig_Error
|
||||
{
|
||||
protected $lineno;
|
||||
protected $filename;
|
||||
protected $rawMessage;
|
||||
|
||||
public function __construct($message, $lineno, $filename = null)
|
||||
{
|
||||
$this->lineno = $lineno;
|
||||
$this->filename = $filename;
|
||||
$this->rawMessage = $message;
|
||||
|
||||
$this->updateRepr();
|
||||
|
||||
parent::__construct($this->message, $lineno);
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function setFilename($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
protected function updateRepr()
|
||||
{
|
||||
$this->message = $this->rawMessage.' in '.($this->filename ? $this->filename : 'n/a').' at line '.$this->lineno;
|
||||
}
|
||||
}
|
538
lib/classes/Twig/ExpressionParser.php
Executable file
538
lib/classes/Twig/ExpressionParser.php
Executable file
@@ -0,0 +1,538 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Parses expressions.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_ExpressionParser
|
||||
{
|
||||
protected $parser;
|
||||
|
||||
public function __construct(Twig_Parser $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
public function parseExpression()
|
||||
{
|
||||
return $this->parseConditionalExpression();
|
||||
}
|
||||
|
||||
public function parseConditionalExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$expr1 = $this->parseOrExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '?')) {
|
||||
$this->parser->getStream()->next();
|
||||
$expr2 = $this->parseOrExpression();
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ':');
|
||||
$expr3 = $this->parseConditionalExpression();
|
||||
$expr1 = new Twig_Node_Expression_Conditional($expr1, $expr2, $expr3, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $expr1;
|
||||
}
|
||||
|
||||
public function parseOrExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseAndExpression();
|
||||
while ($this->parser->getStream()->test('or')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseAndExpression();
|
||||
$left = new Twig_Node_Expression_Binary_Or($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseAndExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseCompareExpression();
|
||||
while ($this->parser->getStream()->test('and')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseCompareExpression();
|
||||
$left = new Twig_Node_Expression_Binary_And($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseCompareExpression()
|
||||
{
|
||||
static $operators = array('==', '!=', '<', '>', '>=', '<=');
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$expr = $this->parseAddExpression();
|
||||
$ops = array();
|
||||
$negated = false;
|
||||
while (
|
||||
$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, $operators)
|
||||
||
|
||||
($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'not') && $this->parser->getStream()->look()->test(Twig_Token::NAME_TYPE, 'in'))
|
||||
||
|
||||
$this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'in')
|
||||
) {
|
||||
$this->parser->getStream()->rewind();
|
||||
if ($this->parser->getStream()->test(Twig_Token::NAME_TYPE, 'not')) {
|
||||
$negated = true;
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
$ops[] = new Twig_Node_Expression_Constant($this->parser->getStream()->next()->getValue(), $lineno);
|
||||
$ops[] = $this->parseAddExpression();
|
||||
}
|
||||
|
||||
if (empty($ops)) {
|
||||
return $expr;
|
||||
}
|
||||
|
||||
$node = new Twig_Node_Expression_Compare($expr, new Twig_Node($ops), $lineno);
|
||||
|
||||
if ($negated) {
|
||||
$node = new Twig_Node_Expression_Unary_Not($node, $lineno);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseAddExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseSubExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '+')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseSubExpression();
|
||||
$left = new Twig_Node_Expression_Binary_Add($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseSubExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseConcatExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '-')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseConcatExpression();
|
||||
$left = new Twig_Node_Expression_Binary_Sub($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseConcatExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseMulExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '~')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseMulExpression();
|
||||
$left = new Twig_Node_Expression_Binary_Concat($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseMulExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseDivExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '*')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseDivExpression();
|
||||
$left = new Twig_Node_Expression_Binary_Mul($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseDivExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseFloorDivExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '/')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseModExpression();
|
||||
$left = new Twig_Node_Expression_Binary_Div($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseFloorDivExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseModExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '//')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseModExpression();
|
||||
$left = new Twig_Node_Expression_Binary_FloorDiv($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseModExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$left = $this->parseUnaryExpression();
|
||||
while ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '%')) {
|
||||
$this->parser->getStream()->next();
|
||||
$right = $this->parseUnaryExpression();
|
||||
$left = new Twig_Node_Expression_Binary_Mod($left, $right, $lineno);
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
}
|
||||
|
||||
return $left;
|
||||
}
|
||||
|
||||
public function parseUnaryExpression()
|
||||
{
|
||||
if ($this->parser->getStream()->test('not')) {
|
||||
return $this->parseNotExpression();
|
||||
}
|
||||
if ($this->parser->getCurrentToken()->getType() == Twig_Token::OPERATOR_TYPE) {
|
||||
switch ($this->parser->getCurrentToken()->getValue()) {
|
||||
case '-':
|
||||
return $this->parseNegExpression();
|
||||
case '+':
|
||||
return $this->parsePosExpression();
|
||||
}
|
||||
}
|
||||
|
||||
return $this->parsePrimaryExpression();
|
||||
}
|
||||
|
||||
public function parseNotExpression()
|
||||
{
|
||||
$token = $this->parser->getStream()->next();
|
||||
$node = $this->parseUnaryExpression();
|
||||
|
||||
return new Twig_Node_Expression_Unary_Not($node, $token->getLine());
|
||||
}
|
||||
|
||||
public function parseNegExpression()
|
||||
{
|
||||
$token = $this->parser->getStream()->next();
|
||||
$node = $this->parseUnaryExpression();
|
||||
|
||||
return new Twig_Node_Expression_Unary_Neg($node, $token->getLine());
|
||||
}
|
||||
|
||||
public function parsePosExpression()
|
||||
{
|
||||
$token = $this->parser->getStream()->next();
|
||||
$node = $this->parseUnaryExpression();
|
||||
|
||||
return new Twig_Node_Expression_Unary_Pos($node, $token->getLine());
|
||||
}
|
||||
|
||||
public function parsePrimaryExpression($assignment = false)
|
||||
{
|
||||
$token = $this->parser->getCurrentToken();
|
||||
switch ($token->getType()) {
|
||||
case Twig_Token::NAME_TYPE:
|
||||
$this->parser->getStream()->next();
|
||||
switch ($token->getValue()) {
|
||||
case 'true':
|
||||
$node = new Twig_Node_Expression_Constant(true, $token->getLine());
|
||||
break;
|
||||
|
||||
case 'false':
|
||||
$node = new Twig_Node_Expression_Constant(false, $token->getLine());
|
||||
break;
|
||||
|
||||
case 'none':
|
||||
$node = new Twig_Node_Expression_Constant(null, $token->getLine());
|
||||
break;
|
||||
|
||||
default:
|
||||
$cls = $assignment ? 'Twig_Node_Expression_AssignName' : 'Twig_Node_Expression_Name';
|
||||
$node = new $cls($token->getValue(), $token->getLine());
|
||||
}
|
||||
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;
|
||||
|
||||
default:
|
||||
if ($token->test(Twig_Token::OPERATOR_TYPE, '[')) {
|
||||
$node = $this->parseArrayExpression();
|
||||
} elseif ($token->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
||||
$this->parser->getStream()->next();
|
||||
$node = $this->parseExpression();
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ')');
|
||||
} else {
|
||||
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::getTypeAsString($token->getType()), $token->getValue()), $token->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
if (!$assignment) {
|
||||
$node = $this->parsePostfixExpression($node);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseArrayExpression()
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Twig_Token::OPERATOR_TYPE, '[');
|
||||
$elements = array();
|
||||
while (!$stream->test(Twig_Token::OPERATOR_TYPE, ']')) {
|
||||
if (!empty($elements)) {
|
||||
$stream->expect(Twig_Token::OPERATOR_TYPE, ',');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Twig_Token::OPERATOR_TYPE, ']')) {
|
||||
$stream->expect(Twig_Token::OPERATOR_TYPE, ']');
|
||||
|
||||
return new Twig_Node_Expression_Array($elements, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
}
|
||||
|
||||
// hash or array element?
|
||||
if (
|
||||
$stream->test(Twig_Token::STRING_TYPE)
|
||||
||
|
||||
$stream->test(Twig_Token::NUMBER_TYPE)
|
||||
)
|
||||
{
|
||||
if ($stream->look()->test(Twig_Token::OPERATOR_TYPE, ':')) {
|
||||
// hash
|
||||
$key = $stream->next()->getValue();
|
||||
$stream->next();
|
||||
|
||||
$elements[$key] = $this->parseExpression();
|
||||
|
||||
continue;
|
||||
}
|
||||
$stream->rewind();
|
||||
}
|
||||
|
||||
$elements[] = $this->parseExpression();
|
||||
}
|
||||
$stream->expect(Twig_Token::OPERATOR_TYPE, ']');
|
||||
|
||||
return new Twig_Node_Expression_Array($elements, $this->parser->getCurrentToken()->getLine());
|
||||
}
|
||||
|
||||
public function parsePostfixExpression($node)
|
||||
{
|
||||
while (1) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if ($token->getType() == Twig_Token::OPERATOR_TYPE) {
|
||||
if ('..' == $token->getValue()) {
|
||||
$node = $this->parseRangeExpression($node);
|
||||
} elseif ('.' == $token->getValue() || '[' == $token->getValue()) {
|
||||
$node = $this->parseSubscriptExpression($node);
|
||||
} elseif ('|' == $token->getValue()) {
|
||||
$node = $this->parseFilterExpression($node);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} elseif ($token->getType() == Twig_Token::NAME_TYPE && 'is' == $token->getValue()) {
|
||||
$node = $this->parseTestExpression($node);
|
||||
break;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseTestExpression($node)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$token = $stream->next();
|
||||
$lineno = $token->getLine();
|
||||
|
||||
$negated = false;
|
||||
if ($stream->test('not')) {
|
||||
$stream->next();
|
||||
$negated = true;
|
||||
}
|
||||
|
||||
$name = $stream->expect(Twig_Token::NAME_TYPE);
|
||||
|
||||
$arguments = null;
|
||||
if ($stream->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
||||
$arguments = $this->parseArguments($node);
|
||||
}
|
||||
$test = new Twig_Node_Expression_Test($node, $name->getValue(), $arguments, $lineno);
|
||||
|
||||
if ($negated) {
|
||||
$test = new Twig_Node_Expression_Unary_Not($test, $lineno);
|
||||
}
|
||||
|
||||
return $test;
|
||||
}
|
||||
|
||||
public function parseRangeExpression($node)
|
||||
{
|
||||
$token = $this->parser->getStream()->next();
|
||||
$lineno = $token->getLine();
|
||||
|
||||
$end = $this->parseExpression();
|
||||
|
||||
$name = new Twig_Node_Expression_Constant('range', $lineno);
|
||||
$arguments = new Twig_Node(array($end));
|
||||
|
||||
return new Twig_Node_Expression_Filter($node, $name, $arguments, $lineno);
|
||||
}
|
||||
|
||||
public function parseSubscriptExpression($node)
|
||||
{
|
||||
$token = $this->parser->getStream()->next();
|
||||
$lineno = $token->getLine();
|
||||
$arguments = new Twig_Node();
|
||||
$type = Twig_Node_Expression_GetAttr::TYPE_ANY;
|
||||
if ($token->getValue() == '.') {
|
||||
$token = $this->parser->getStream()->next();
|
||||
if ($token->getType() == Twig_Token::NAME_TYPE || $token->getType() == Twig_Token::NUMBER_TYPE) {
|
||||
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
|
||||
|
||||
if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
||||
$type = Twig_Node_Expression_GetAttr::TYPE_METHOD;
|
||||
$arguments = $this->parseArguments();
|
||||
} else {
|
||||
$arguments = new Twig_Node();
|
||||
}
|
||||
} else {
|
||||
throw new Twig_Error_Syntax('Expected name or number', $lineno);
|
||||
}
|
||||
} else {
|
||||
$type = Twig_Node_Expression_GetAttr::TYPE_ARRAY;
|
||||
|
||||
$arg = $this->parseExpression();
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ']');
|
||||
}
|
||||
|
||||
return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
|
||||
}
|
||||
|
||||
public function parseFilterExpression($node)
|
||||
{
|
||||
$this->parser->getStream()->next();
|
||||
|
||||
return $this->parseFilterExpressionRaw($node);
|
||||
}
|
||||
|
||||
public function parseFilterExpressionRaw($node, $tag = null)
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
|
||||
while (true) {
|
||||
$token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
|
||||
|
||||
$name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '(')) {
|
||||
$arguments = new Twig_Node();
|
||||
} else {
|
||||
$arguments = $this->parseArguments();
|
||||
}
|
||||
|
||||
$node = new Twig_Node_Expression_Filter($node, $name, $arguments, $token->getLine(), $tag);
|
||||
|
||||
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, '|')) {
|
||||
break;
|
||||
}
|
||||
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function parseArguments()
|
||||
{
|
||||
$parser = $this->parser->getStream();
|
||||
$parser->expect(Twig_Token::OPERATOR_TYPE, '(');
|
||||
|
||||
$args = array();
|
||||
while (!$parser->test(Twig_Token::OPERATOR_TYPE, ')')) {
|
||||
if (!empty($args)) {
|
||||
$parser->expect(Twig_Token::OPERATOR_TYPE, ',');
|
||||
}
|
||||
$args[] = $this->parseExpression();
|
||||
}
|
||||
$parser->expect(Twig_Token::OPERATOR_TYPE, ')');
|
||||
|
||||
return new Twig_Node($args);
|
||||
}
|
||||
|
||||
public function parseAssignmentExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$targets = array();
|
||||
while (true) {
|
||||
if (!empty($targets)) {
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ',');
|
||||
}
|
||||
if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ')') ||
|
||||
$this->parser->getStream()->test(Twig_Token::VAR_END_TYPE) ||
|
||||
$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE) ||
|
||||
$this->parser->getStream()->test('in'))
|
||||
{
|
||||
break;
|
||||
}
|
||||
$targets[] = $this->parsePrimaryExpression(true);
|
||||
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Twig_Node($targets);
|
||||
}
|
||||
|
||||
public function parseMultitargetExpression()
|
||||
{
|
||||
$lineno = $this->parser->getCurrentToken()->getLine();
|
||||
$targets = array();
|
||||
$is_multitarget = false;
|
||||
while (true) {
|
||||
if (!empty($targets)) {
|
||||
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, ',');
|
||||
}
|
||||
if ($this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ')') ||
|
||||
$this->parser->getStream()->test(Twig_Token::VAR_END_TYPE) ||
|
||||
$this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE))
|
||||
{
|
||||
break;
|
||||
}
|
||||
$targets[] = $this->parseExpression();
|
||||
if (!$this->parser->getStream()->test(Twig_Token::OPERATOR_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
$is_multitarget = true;
|
||||
}
|
||||
|
||||
return array($is_multitarget, new Twig_Node($targets));
|
||||
}
|
||||
}
|
63
lib/classes/Twig/Extension.php
Executable file
63
lib/classes/Twig/Extension.php
Executable file
@@ -0,0 +1,63 @@
|
||||
<?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.
|
||||
*/
|
||||
abstract class Twig_Extension implements Twig_ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* Initializes the runtime environment.
|
||||
*
|
||||
* This is where you can load some file that contains filter functions for instance.
|
||||
*
|
||||
* @param Twig_Environment $environement The current Twig_Environment instance
|
||||
*/
|
||||
public function initRuntime(Twig_Environment $environement)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of tests to add to the existing list.
|
||||
*
|
||||
* @return array An array of tests
|
||||
*/
|
||||
public function getTests()
|
||||
{
|
||||
return array();
|
||||
}
|
||||
}
|
417
lib/classes/Twig/Extension/Core.php
Executable file
417
lib/classes/Twig/Extension/Core.php
Executable file
@@ -0,0 +1,417 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_Core extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Returns the token parser instance to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParser instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(
|
||||
new Twig_TokenParser_For(),
|
||||
new Twig_TokenParser_If(),
|
||||
new Twig_TokenParser_Extends(),
|
||||
new Twig_TokenParser_Include(),
|
||||
new Twig_TokenParser_Block(),
|
||||
new Twig_TokenParser_Parent(),
|
||||
new Twig_TokenParser_Display(),
|
||||
new Twig_TokenParser_Filter(),
|
||||
new Twig_TokenParser_Macro(),
|
||||
new Twig_TokenParser_Import(),
|
||||
new Twig_TokenParser_Set(),
|
||||
new Twig_TokenParser_Spaceless(),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
$filters = array(
|
||||
// formatting filters
|
||||
'date' => new Twig_Filter_Function('twig_date_format_filter'),
|
||||
'format' => new Twig_Filter_Function('sprintf'),
|
||||
'replace' => new Twig_Filter_Function('twig_strtr'),
|
||||
|
||||
// encoding
|
||||
'url_encode' => new Twig_Filter_Function('twig_urlencode_filter'),
|
||||
'json_encode' => new Twig_Filter_Function('json_encode'),
|
||||
|
||||
// string filters
|
||||
'title' => new Twig_Filter_Function('twig_title_string_filter', array('needs_environment' => true)),
|
||||
'capitalize' => new Twig_Filter_Function('twig_capitalize_string_filter', array('needs_environment' => true)),
|
||||
'upper' => new Twig_Filter_Function('strtoupper'),
|
||||
'lower' => new Twig_Filter_Function('strtolower'),
|
||||
'striptags' => new Twig_Filter_Function('strip_tags'),
|
||||
'constant' => new Twig_Filter_Function('constant'),
|
||||
|
||||
// array helpers
|
||||
'join' => new Twig_Filter_Function('twig_join_filter'),
|
||||
'reverse' => new Twig_Filter_Function('twig_reverse_filter'),
|
||||
'length' => new Twig_Filter_Function('twig_length_filter', array('needs_environment' => true)),
|
||||
'sort' => new Twig_Filter_Function('twig_sort_filter'),
|
||||
'in' => new Twig_Filter_Function('twig_in_filter'),
|
||||
'range' => new Twig_Filter_Function('twig_range_filter'),
|
||||
'cycle' => new Twig_Filter_Function('twig_cycle_filter'),
|
||||
|
||||
// iteration and runtime
|
||||
'default' => new Twig_Filter_Function('twig_default_filter'),
|
||||
'keys' => new Twig_Filter_Function('twig_get_array_keys_filter'),
|
||||
'items' => new Twig_Filter_Function('twig_get_array_items_filter'),
|
||||
|
||||
// escaping
|
||||
'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
|
||||
'e' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
|
||||
);
|
||||
|
||||
if (function_exists('mb_get_info')) {
|
||||
$filters['upper'] = new Twig_Filter_Function('twig_upper_filter', array('needs_environment' => true));
|
||||
$filters['lower'] = new Twig_Filter_Function('twig_lower_filter', array('needs_environment' => true));
|
||||
}
|
||||
|
||||
return $filters;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getTests()
|
||||
{
|
||||
return array(
|
||||
'even' => new Twig_Test_Function('twig_test_even'),
|
||||
'odd' => new Twig_Test_Function('twig_test_odd'),
|
||||
'defined' => new Twig_Test_Function('twig_test_defined'),
|
||||
'sameas' => new Twig_Test_Function('twig_test_sameas'),
|
||||
'none' => new Twig_Test_Function('twig_test_none'),
|
||||
'divisibleby' => new Twig_Test_Function('twig_test_divisibleby'),
|
||||
'constant' => new Twig_Test_Function('twig_test_constant'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'core';
|
||||
}
|
||||
}
|
||||
|
||||
function twig_date_format_filter($date, $format = 'F j, Y H:i')
|
||||
{
|
||||
if (!$date instanceof DateTime) {
|
||||
$date = new DateTime((ctype_digit($date) ? '@' : '').$date);
|
||||
}
|
||||
|
||||
return $date->format($format);
|
||||
}
|
||||
|
||||
function twig_urlencode_filter($url, $raw = false)
|
||||
{
|
||||
if ($raw) {
|
||||
return rawurlencode($url);
|
||||
}
|
||||
|
||||
return urlencode($url);
|
||||
}
|
||||
|
||||
function twig_join_filter($value, $glue = '')
|
||||
{
|
||||
return implode($glue, (array) $value);
|
||||
}
|
||||
|
||||
function twig_default_filter($value, $default = '')
|
||||
{
|
||||
return null === $value ? $default : $value;
|
||||
}
|
||||
|
||||
function twig_get_array_keys_filter($array)
|
||||
{
|
||||
if (is_object($array) && $array instanceof Traversable) {
|
||||
return array_keys(iterator_to_array($array));
|
||||
}
|
||||
|
||||
if (!is_array($array)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_keys($array);
|
||||
}
|
||||
|
||||
function twig_reverse_filter($array)
|
||||
{
|
||||
if (is_object($array) && $array instanceof Traversable) {
|
||||
return array_reverse(iterator_to_array($array));
|
||||
}
|
||||
|
||||
if (!is_array($array)) {
|
||||
return array();
|
||||
}
|
||||
|
||||
return array_reverse($array);
|
||||
}
|
||||
|
||||
function twig_sort_filter($array)
|
||||
{
|
||||
asort($array);
|
||||
|
||||
return $array;
|
||||
}
|
||||
|
||||
function twig_in_filter($value, $compare)
|
||||
{
|
||||
if (is_array($compare)) {
|
||||
return in_array($value, $compare);
|
||||
} elseif (is_string($compare)) {
|
||||
return false !== strpos($compare, (string) $value);
|
||||
} elseif (is_object($compare) && $compare instanceof Traversable) {
|
||||
return in_array($value, iterator_to_array($compare, false));
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function twig_range_filter($start, $end, $step = 1)
|
||||
{
|
||||
return range($start, $end, $step);
|
||||
}
|
||||
|
||||
function twig_cycle_filter($values, $i)
|
||||
{
|
||||
if (!is_array($values) && !$values instanceof ArrayAccess) {
|
||||
return $values;
|
||||
}
|
||||
|
||||
return $values[$i % count($values)];
|
||||
}
|
||||
|
||||
function twig_strtr($pattern, $replacements)
|
||||
{
|
||||
return str_replace(array_keys($replacements), array_values($replacements), $pattern);
|
||||
}
|
||||
|
||||
/*
|
||||
* Each type specifies a way for applying a transformation to a string
|
||||
* The purpose is for the string to be "escaped" so it is suitable for
|
||||
* the format it is being displayed in.
|
||||
*
|
||||
* For example, the string: "It's required that you enter a username & password.\n"
|
||||
* If this were to be displayed as HTML it would be sensible to turn the
|
||||
* ampersand into '&' and the apostrophe into '&aps;'. However if it were
|
||||
* going to be used as a string in JavaScript to be displayed in an alert box
|
||||
* it would be right to leave the string as-is, but c-escape the apostrophe and
|
||||
* the new line.
|
||||
*/
|
||||
function twig_escape_filter(Twig_Environment $env, $string, $type = 'html')
|
||||
{
|
||||
if (!is_string($string) && !(is_object($string) && method_exists($string, '__toString'))) {
|
||||
return $string;
|
||||
}
|
||||
|
||||
switch ($type) {
|
||||
case 'js':
|
||||
// escape all non-alphanumeric characters
|
||||
// into their \xHH or \uHHHH representations
|
||||
$charset = $env->getCharset();
|
||||
|
||||
if ('UTF-8' != $charset) {
|
||||
$string = _twig_convert_encoding($string, 'UTF-8', $charset);
|
||||
}
|
||||
|
||||
if (null === $string = preg_replace_callback('#[^\p{L}\p{N} ]#u', '_twig_escape_js_callback', $string)) {
|
||||
throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
|
||||
}
|
||||
|
||||
if ('UTF-8' != $charset) {
|
||||
$string = _twig_convert_encoding($string, $charset, 'UTF-8');
|
||||
}
|
||||
|
||||
return $string;
|
||||
|
||||
case 'html':
|
||||
return htmlspecialchars($string, ENT_QUOTES, $env->getCharset());
|
||||
|
||||
default:
|
||||
throw new Twig_Error_Runtime(sprintf('Invalid escape type "%s".', $type));
|
||||
}
|
||||
}
|
||||
|
||||
function twig_escape_filter_is_safe(Twig_Node $filterArgs)
|
||||
{
|
||||
foreach ($filterArgs as $arg) {
|
||||
if ($arg instanceof Twig_Node_Expression_Constant) {
|
||||
return array($arg->getAttribute('value'));
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
return array('html');
|
||||
}
|
||||
|
||||
if (function_exists('iconv')) {
|
||||
function _twig_convert_encoding($string, $to, $from)
|
||||
{
|
||||
return iconv($from, $to, $string);
|
||||
}
|
||||
} elseif (function_exists('mb_convert_encoding')) {
|
||||
function _twig_convert_encoding($string, $to, $from)
|
||||
{
|
||||
return mb_convert_encoding($string, $to, $from);
|
||||
}
|
||||
} else {
|
||||
function _twig_convert_encoding($string, $to, $from)
|
||||
{
|
||||
throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
|
||||
}
|
||||
}
|
||||
|
||||
function _twig_escape_js_callback($matches)
|
||||
{
|
||||
$char = $matches[0];
|
||||
|
||||
// \xHH
|
||||
if (!isset($char[1])) {
|
||||
return '\\x'.substr('00'.bin2hex($char), -2);
|
||||
}
|
||||
|
||||
// \uHHHH
|
||||
$char = _twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
|
||||
|
||||
return '\\u'.substr('0000'.bin2hex($char), -4);
|
||||
}
|
||||
|
||||
// add multibyte extensions if possible
|
||||
if (function_exists('mb_get_info')) {
|
||||
function twig_length_filter(Twig_Environment $env, $thing)
|
||||
{
|
||||
return is_string($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
|
||||
}
|
||||
|
||||
function twig_upper_filter(Twig_Environment $env, $string)
|
||||
{
|
||||
if (null !== ($charset = $env->getCharset())) {
|
||||
return mb_strtoupper($string, $charset);
|
||||
}
|
||||
|
||||
return strtoupper($string);
|
||||
}
|
||||
|
||||
function twig_lower_filter(Twig_Environment $env, $string)
|
||||
{
|
||||
if (null !== ($charset = $env->getCharset())) {
|
||||
return mb_strtolower($string, $charset);
|
||||
}
|
||||
|
||||
return strtolower($string);
|
||||
}
|
||||
|
||||
function twig_title_string_filter(Twig_Environment $env, $string)
|
||||
{
|
||||
if (null !== ($charset = $env->getCharset())) {
|
||||
return mb_convert_case($string, MB_CASE_TITLE, $charset);
|
||||
}
|
||||
|
||||
return ucwords(strtolower($string));
|
||||
}
|
||||
|
||||
function twig_capitalize_string_filter(Twig_Environment $env, $string)
|
||||
{
|
||||
if (null !== ($charset = $env->getCharset())) {
|
||||
return mb_strtoupper(mb_substr($string, 0, 1, $charset)).
|
||||
mb_strtolower(mb_substr($string, 1, mb_strlen($string), $charset), $charset);
|
||||
}
|
||||
|
||||
return ucfirst(strtolower($string));
|
||||
}
|
||||
}
|
||||
// and byte fallback
|
||||
else
|
||||
{
|
||||
function twig_length_filter(Twig_Environment $env, $thing)
|
||||
{
|
||||
return is_string($thing) ? strlen($thing) : count($thing);
|
||||
}
|
||||
|
||||
function twig_title_string_filter(Twig_Environment $env, $string)
|
||||
{
|
||||
return ucwords(strtolower($string));
|
||||
}
|
||||
|
||||
function twig_capitalize_string_filter(Twig_Environment $env, $string)
|
||||
{
|
||||
return ucfirst(strtolower($string));
|
||||
}
|
||||
}
|
||||
|
||||
function twig_iterator_to_array($seq, $useKeys = true)
|
||||
{
|
||||
if (is_array($seq)) {
|
||||
return $seq;
|
||||
} elseif (is_object($seq) && $seq instanceof Traversable) {
|
||||
return $seq;
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
// only for backward compatibility
|
||||
function twig_get_array_items_filter($array)
|
||||
{
|
||||
// noop
|
||||
return $array;
|
||||
}
|
||||
|
||||
function twig_test_sameas($value, $test)
|
||||
{
|
||||
return $value === $test;
|
||||
}
|
||||
|
||||
function twig_test_none($value)
|
||||
{
|
||||
return null === $value;
|
||||
}
|
||||
|
||||
function twig_test_divisibleby($value, $num)
|
||||
{
|
||||
return 0 == $value % $num;
|
||||
}
|
||||
|
||||
function twig_test_even($value)
|
||||
{
|
||||
return $value % 2 == 0;
|
||||
}
|
||||
|
||||
function twig_test_odd($value)
|
||||
{
|
||||
return $value % 2 == 1;
|
||||
}
|
||||
|
||||
function twig_test_constant($value, $constant)
|
||||
{
|
||||
return constant($constant) === $value;
|
||||
}
|
||||
|
||||
function twig_test_defined($name, $context)
|
||||
{
|
||||
return array_key_exists($name, $context);
|
||||
}
|
73
lib/classes/Twig/Extension/Escaper.php
Executable file
73
lib/classes/Twig/Extension/Escaper.php
Executable file
@@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_Escaper extends Twig_Extension
|
||||
{
|
||||
protected $autoescape;
|
||||
|
||||
public function __construct($autoescape = true)
|
||||
{
|
||||
$this->autoescape = $autoescape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(new Twig_TokenParser_AutoEscape());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array(new Twig_NodeVisitor_Escaper());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
'raw' => new Twig_Filter_Function('twig_raw_filter', array('is_safe' => array('all'))),
|
||||
);
|
||||
}
|
||||
|
||||
public function isGlobal()
|
||||
{
|
||||
return $this->autoescape;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'escaper';
|
||||
}
|
||||
}
|
||||
|
||||
// tells the escaper node visitor that the string is safe
|
||||
function twig_raw_filter($string)
|
||||
{
|
||||
return $string;
|
||||
}
|
||||
|
44
lib/classes/Twig/Extension/I18n.php
Executable file
44
lib/classes/Twig/Extension/I18n.php
Executable file
@@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_I18n extends Twig_Extension
|
||||
{
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(new Twig_TokenParser_Trans());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters()
|
||||
{
|
||||
return array(
|
||||
'trans' => new Twig_Filter_Function('gettext'),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'i18n';
|
||||
}
|
||||
}
|
103
lib/classes/Twig/Extension/Sandbox.php
Executable file
103
lib/classes/Twig/Extension/Sandbox.php
Executable file
@@ -0,0 +1,103 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Extension_Sandbox extends Twig_Extension
|
||||
{
|
||||
protected $sandboxedGlobally;
|
||||
protected $sandboxed;
|
||||
protected $policy;
|
||||
|
||||
public function __construct(Twig_Sandbox_SecurityPolicyInterface $policy, $sandboxed = false)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
$this->sandboxedGlobally = $sandboxed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return array(new Twig_TokenParser_Sandbox());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return array(new Twig_NodeVisitor_Sandbox());
|
||||
}
|
||||
|
||||
public function enableSandbox()
|
||||
{
|
||||
$this->sandboxed = true;
|
||||
}
|
||||
|
||||
public function disableSandbox()
|
||||
{
|
||||
$this->sandboxed = false;
|
||||
}
|
||||
|
||||
public function isSandboxed()
|
||||
{
|
||||
return $this->sandboxedGlobally || $this->sandboxed;
|
||||
}
|
||||
|
||||
public function isSandboxedGlobally()
|
||||
{
|
||||
return $this->sandboxedGlobally;
|
||||
}
|
||||
|
||||
public function setSecurityPolicy(Twig_Sandbox_SecurityPolicyInterface $policy)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
}
|
||||
|
||||
public function getSecurityPolicy()
|
||||
{
|
||||
return $this->policy;
|
||||
}
|
||||
|
||||
public function checkSecurity($tags, $filters)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkSecurity($tags, $filters);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkMethodAllowed($obj, $method)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkMethodAllowed($obj, $method);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPropertyAllowed($obj, $method)
|
||||
{
|
||||
if ($this->isSandboxed()) {
|
||||
$this->policy->checkPropertyAllowed($obj, $method);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName()
|
||||
{
|
||||
return 'sandbox';
|
||||
}
|
||||
}
|
63
lib/classes/Twig/ExtensionInterface.php
Executable file
63
lib/classes/Twig/ExtensionInterface.php
Executable file
@@ -0,0 +1,63 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface implemented by extension classes.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* Initializes the runtime environment.
|
||||
*
|
||||
* This is where you can load some file that contains filter functions for instance.
|
||||
*
|
||||
* @param Twig_Environment $environement The current Twig_Environment instance
|
||||
*/
|
||||
public function initRuntime(Twig_Environment $environement);
|
||||
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
|
||||
*/
|
||||
public function getTokenParsers();
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return array An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function getNodeVisitors();
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return array An array of filters
|
||||
*/
|
||||
public function getFilters();
|
||||
|
||||
/**
|
||||
* Returns a list of tests to add to the existing list.
|
||||
*
|
||||
* @return array An array of tests
|
||||
*/
|
||||
public function getTests();
|
||||
|
||||
/**
|
||||
* Returns the name of the extension.
|
||||
*
|
||||
* @return string The extension name
|
||||
*/
|
||||
public function getName();
|
||||
}
|
51
lib/classes/Twig/Filter.php
Executable file
51
lib/classes/Twig/Filter.php
Executable file
@@ -0,0 +1,51 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template filter.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
abstract class Twig_Filter implements Twig_FilterInterface
|
||||
{
|
||||
protected $options;
|
||||
|
||||
public function __construct(array $options = array())
|
||||
{
|
||||
$this->options = array_merge(array(
|
||||
'needs_environment' => false,
|
||||
), $options);
|
||||
|
||||
if (isset($this->options['is_escaper'])) {
|
||||
$this->options['is_safe'] = array('html');
|
||||
unset($this->options['is_escaper']);
|
||||
}
|
||||
}
|
||||
|
||||
public function needsEnvironment()
|
||||
{
|
||||
return $this->options['needs_environment'];
|
||||
}
|
||||
|
||||
public function getSafe(Twig_Node $filterArgs)
|
||||
{
|
||||
if (isset($this->options['is_safe'])) {
|
||||
return $this->options['is_safe'];
|
||||
}
|
||||
|
||||
if (isset($this->options['is_safe_callback'])) {
|
||||
return call_user_func($this->options['is_safe_callback'], $filterArgs);
|
||||
}
|
||||
|
||||
return array();
|
||||
}
|
||||
}
|
33
lib/classes/Twig/Filter/Function.php
Executable file
33
lib/classes/Twig/Filter/Function.php
Executable file
@@ -0,0 +1,33 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a function template filter.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Filter_Function extends Twig_Filter
|
||||
{
|
||||
protected $function;
|
||||
|
||||
public function __construct($function, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->function = $function;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return $this->function;
|
||||
}
|
||||
}
|
34
lib/classes/Twig/Filter/Method.php
Executable file
34
lib/classes/Twig/Filter/Method.php
Executable file
@@ -0,0 +1,34 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a method template filter.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Filter_Method extends Twig_Filter
|
||||
{
|
||||
protected $extension, $method;
|
||||
|
||||
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
|
||||
{
|
||||
parent::__construct($options);
|
||||
|
||||
$this->extension = $extension;
|
||||
$this->method = $method;
|
||||
}
|
||||
|
||||
public function compile()
|
||||
{
|
||||
return sprintf('$this->getEnvironment()->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
|
||||
}
|
||||
}
|
21
lib/classes/Twig/FilterInterface.php
Executable file
21
lib/classes/Twig/FilterInterface.php
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a template filter.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_FilterInterface
|
||||
{
|
||||
public function compile();
|
||||
}
|
30
lib/classes/Twig/Grammar.php
Executable file
30
lib/classes/Twig/Grammar.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
abstract class Twig_Grammar implements Twig_GrammarInterface
|
||||
{
|
||||
protected $name;
|
||||
protected $parser;
|
||||
|
||||
public function __construct($name)
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function setParser(Twig_ParserInterface $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
public function getName()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
}
|
22
lib/classes/Twig/Grammar/Arguments.php
Executable file
22
lib/classes/Twig/Grammar/Arguments.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Arguments extends Twig_Grammar
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('<%s:arguments>', $this->name);
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
return $this->parser->getExpressionParser()->parseArguments();
|
||||
}
|
||||
}
|
22
lib/classes/Twig/Grammar/Array.php
Executable file
22
lib/classes/Twig/Grammar/Array.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Array extends Twig_Grammar
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('<%s:array>', $this->name);
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
return $this->parser->getExpressionParser()->parseArrayExpression();
|
||||
}
|
||||
}
|
39
lib/classes/Twig/Grammar/Body.php
Executable file
39
lib/classes/Twig/Grammar/Body.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Body extends Twig_Grammar
|
||||
{
|
||||
protected $end;
|
||||
|
||||
public function __construct($name, $end = null)
|
||||
{
|
||||
parent::__construct($name);
|
||||
|
||||
$this->end = null === $end ? 'end'.$name : $end;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('<%s:body>', $this->name);
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$stream = $this->parser->getStream();
|
||||
$stream->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
|
||||
return $this->parser->subparse(array($this, 'decideBlockEnd'), true);
|
||||
}
|
||||
|
||||
public function decideBlockEnd($token)
|
||||
{
|
||||
return $token->test($this->end);
|
||||
}
|
||||
}
|
24
lib/classes/Twig/Grammar/Boolean.php
Executable file
24
lib/classes/Twig/Grammar/Boolean.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Boolean extends Twig_Grammar
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('<%s:boolean>', $this->name);
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$this->parser->getStream()->expect(Twig_Token::NAME_TYPE, array('true', 'false'));
|
||||
|
||||
return new Twig_Node_Expression_Constant('true' === $token->getValue() ? true : false, $token->getLine());
|
||||
}
|
||||
}
|
24
lib/classes/Twig/Grammar/Constant.php
Executable file
24
lib/classes/Twig/Grammar/Constant.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Constant extends Twig_Grammar
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$this->parser->getStream()->expect($this->name);
|
||||
|
||||
return $this->name;
|
||||
}
|
||||
}
|
22
lib/classes/Twig/Grammar/Expression.php
Executable file
22
lib/classes/Twig/Grammar/Expression.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Expression extends Twig_Grammar
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('<%s>', $this->name);
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
return $this->parser->getExpressionParser()->parseExpression();
|
||||
}
|
||||
}
|
24
lib/classes/Twig/Grammar/Number.php
Executable file
24
lib/classes/Twig/Grammar/Number.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Number extends Twig_Grammar
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('<%s:number>', $this->name);
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$this->parser->getStream()->expect(Twig_Token::NUMBER_TYPE);
|
||||
|
||||
return new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
|
||||
}
|
||||
}
|
64
lib/classes/Twig/Grammar/Optional.php
Executable file
64
lib/classes/Twig/Grammar/Optional.php
Executable file
@@ -0,0 +1,64 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Optional extends Twig_Grammar
|
||||
{
|
||||
protected $grammar;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->grammar = array();
|
||||
foreach (func_get_args() as $grammar) {
|
||||
$this->addGrammar($grammar);
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$repr = array();
|
||||
foreach ($this->grammar as $grammar) {
|
||||
$repr[] = (string) $grammar;
|
||||
}
|
||||
|
||||
return sprintf('[%s]', implode(' ', $repr));
|
||||
}
|
||||
|
||||
public function addGrammar(Twig_GrammarInterface $grammar)
|
||||
{
|
||||
$this->grammar[] = $grammar;
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
// test if we have the optional element before consuming it
|
||||
if ($this->grammar[0] instanceof Twig_Grammar_Constant) {
|
||||
if (!$this->parser->getStream()->test($this->grammar[0]->getName())) {
|
||||
return array();
|
||||
}
|
||||
} elseif ($this->grammar[0] instanceof Twig_Grammar_Name) {
|
||||
if (!$this->parser->getStream()->test(Twig_Token::NAME_TYPE)) {
|
||||
return array();
|
||||
}
|
||||
} elseif ($this->parser->getStream()->test(Twig_Token::BLOCK_END_TYPE)) {
|
||||
// if this is not a Constant or a Name, it must be the last element of the tag
|
||||
|
||||
return array();
|
||||
}
|
||||
|
||||
$elements = array();
|
||||
foreach ($this->grammar as $grammar) {
|
||||
$grammar->setParser($this->parser);
|
||||
|
||||
$elements[$grammar->getName()] = $grammar->parse($token);
|
||||
}
|
||||
|
||||
return $elements;
|
||||
}
|
||||
}
|
24
lib/classes/Twig/Grammar/Switch.php
Normal file
24
lib/classes/Twig/Grammar/Switch.php
Normal file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Switch extends Twig_Grammar
|
||||
{
|
||||
public function __toString()
|
||||
{
|
||||
return sprintf('<%s:switch>', $this->name);
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$this->parser->getStream()->expect(Twig_Token::NAME_TYPE, $this->name);
|
||||
|
||||
return new Twig_Node_Expression_Constant(true, $token->getLine());
|
||||
}
|
||||
}
|
56
lib/classes/Twig/Grammar/Tag.php
Executable file
56
lib/classes/Twig/Grammar/Tag.php
Executable file
@@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Grammar_Tag extends Twig_Grammar
|
||||
{
|
||||
protected $grammar;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->grammar = array();
|
||||
foreach (func_get_args() as $grammar) {
|
||||
$this->addGrammar($grammar);
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$repr = array();
|
||||
foreach ($this->grammar as $grammar) {
|
||||
$repr[] = (string) $grammar;
|
||||
}
|
||||
|
||||
return implode(' ', $repr);
|
||||
}
|
||||
|
||||
public function addGrammar(Twig_GrammarInterface $grammar)
|
||||
{
|
||||
$this->grammar[] = $grammar;
|
||||
}
|
||||
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$elements = array();
|
||||
foreach ($this->grammar as $grammar) {
|
||||
$grammar->setParser($this->parser);
|
||||
|
||||
$element = $grammar->parse($token);
|
||||
if (is_array($element)) {
|
||||
$elements = array_merge($elements, $element);
|
||||
} else {
|
||||
$elements[$grammar->getName()] = $element;
|
||||
}
|
||||
}
|
||||
|
||||
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
|
||||
|
||||
return $elements;
|
||||
}
|
||||
}
|
18
lib/classes/Twig/GrammarInterface.php
Executable file
18
lib/classes/Twig/GrammarInterface.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
interface Twig_GrammarInterface
|
||||
{
|
||||
public function setParser(Twig_ParserInterface $parser);
|
||||
|
||||
public function parse(Twig_Token $token);
|
||||
|
||||
public function getName();
|
||||
}
|
31
lib/classes/Twig/LICENSE
Normal file
31
lib/classes/Twig/LICENSE
Normal file
@@ -0,0 +1,31 @@
|
||||
Copyright (c) 2009 by the Twig Team, see AUTHORS for more details.
|
||||
|
||||
Some rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following
|
||||
disclaimer in the documentation and/or other materials provided
|
||||
with the distribution.
|
||||
|
||||
* The names of the contributors may not be used to endorse or
|
||||
promote products derived from this software without specific
|
||||
prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
317
lib/classes/Twig/Lexer.php
Executable file
317
lib/classes/Twig/Lexer.php
Executable file
@@ -0,0 +1,317 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Lexes a template string.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Lexer implements Twig_LexerInterface
|
||||
{
|
||||
protected $cursor;
|
||||
protected $position;
|
||||
protected $end;
|
||||
protected $pushedBack;
|
||||
protected $code;
|
||||
protected $lineno;
|
||||
protected $filename;
|
||||
protected $env;
|
||||
protected $options;
|
||||
|
||||
const POSITION_DATA = 0;
|
||||
const POSITION_BLOCK = 1;
|
||||
const POSITION_VAR = 2;
|
||||
|
||||
const REGEX_NAME = '/[A-Za-z_][A-Za-z0-9_]*/A';
|
||||
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
|
||||
const REGEX_STRING = '/(?:"([^"\\\\]*(?:\\\\.[^"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\')/Asm';
|
||||
const REGEX_OPERATOR = '/<=? | >=? | [!=]= | = | \/\/ | \.\. | [(){}.,%*\/+~|-] | \[ | \] | \? | \:/Ax';
|
||||
|
||||
public function __construct(Twig_Environment $env = null, array $options = array())
|
||||
{
|
||||
if (null !== $env) {
|
||||
$this->setEnvironment($env);
|
||||
}
|
||||
|
||||
$this->options = array_merge(array(
|
||||
'tag_comment' => array('{#', '#}'),
|
||||
'tag_block' => array('{%', '%}'),
|
||||
'tag_variable' => array('{{', '}}'),
|
||||
), $options);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tokenizes a source code.
|
||||
*
|
||||
* @param string $code The source code
|
||||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
*/
|
||||
public function tokenize($code, $filename = 'n/a')
|
||||
{
|
||||
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
|
||||
$mbEncoding = mb_internal_encoding();
|
||||
mb_internal_encoding('ASCII');
|
||||
}
|
||||
|
||||
$this->code = str_replace(array("\r\n", "\r"), "\n", $code);
|
||||
$this->filename = $filename;
|
||||
$this->cursor = 0;
|
||||
$this->lineno = 1;
|
||||
$this->pushedBack = array();
|
||||
$this->end = strlen($this->code);
|
||||
$this->position = self::POSITION_DATA;
|
||||
|
||||
$tokens = array();
|
||||
$end = false;
|
||||
while (!$end) {
|
||||
$token = $this->nextToken();
|
||||
|
||||
$tokens[] = $token;
|
||||
|
||||
$end = $token->getType() === Twig_Token::EOF_TYPE;
|
||||
}
|
||||
|
||||
if (isset($mbEncoding)) {
|
||||
mb_internal_encoding($mbEncoding);
|
||||
}
|
||||
|
||||
return new Twig_TokenStream($tokens, $this->filename);
|
||||
}
|
||||
|
||||
public function setEnvironment(Twig_Environment $env)
|
||||
{
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses the next token and returns it.
|
||||
*/
|
||||
protected function nextToken()
|
||||
{
|
||||
// do we have tokens pushed back? get one
|
||||
if (!empty($this->pushedBack)) {
|
||||
return array_shift($this->pushedBack);
|
||||
}
|
||||
|
||||
// have we reached the end of the code?
|
||||
if ($this->cursor >= $this->end) {
|
||||
return new Twig_Token(Twig_Token::EOF_TYPE, '', $this->lineno);
|
||||
}
|
||||
|
||||
// otherwise dispatch to the lexing functions depending
|
||||
// on our current position in the code.
|
||||
switch ($this->position) {
|
||||
case self::POSITION_DATA:
|
||||
$tokens = $this->lexData();
|
||||
break;
|
||||
|
||||
case self::POSITION_BLOCK:
|
||||
$tokens = $this->lexBlock();
|
||||
break;
|
||||
|
||||
case self::POSITION_VAR:
|
||||
$tokens = $this->lexVar();
|
||||
break;
|
||||
}
|
||||
|
||||
// if the return value is not an array it's a token
|
||||
if (!is_array($tokens)) {
|
||||
return $tokens;
|
||||
}
|
||||
// empty array, call again
|
||||
else if (empty($tokens)) {
|
||||
return $this->nextToken();
|
||||
}
|
||||
// if we have multiple items we push them to the buffer
|
||||
else if (count($tokens) > 1) {
|
||||
$first = array_shift($tokens);
|
||||
$this->pushedBack = $tokens;
|
||||
|
||||
return $first;
|
||||
}
|
||||
// otherwise return the first item of the array.
|
||||
else {
|
||||
return $tokens[0];
|
||||
}
|
||||
}
|
||||
|
||||
protected function lexData()
|
||||
{
|
||||
$match = null;
|
||||
|
||||
$pos1 = strpos($this->code, $this->options['tag_comment'][0], $this->cursor);
|
||||
$pos2 = strpos($this->code, $this->options['tag_variable'][0], $this->cursor);
|
||||
$pos3 = strpos($this->code, $this->options['tag_block'][0], $this->cursor);
|
||||
|
||||
// if no matches are left we return the rest of the template
|
||||
// as simple text token
|
||||
if (false === $pos1 && false === $pos2 && false === $pos3) {
|
||||
$rv = new Twig_Token(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor), $this->lineno);
|
||||
$this->cursor = $this->end;
|
||||
|
||||
return $rv;
|
||||
}
|
||||
|
||||
// min
|
||||
$pos = -log(0);
|
||||
if (false !== $pos1 && $pos1 < $pos) {
|
||||
$pos = $pos1;
|
||||
$token = $this->options['tag_comment'][0];
|
||||
}
|
||||
if (false !== $pos2 && $pos2 < $pos) {
|
||||
$pos = $pos2;
|
||||
$token = $this->options['tag_variable'][0];
|
||||
}
|
||||
if (false !== $pos3 && $pos3 < $pos) {
|
||||
$pos = $pos3;
|
||||
$token = $this->options['tag_block'][0];
|
||||
}
|
||||
|
||||
// update the lineno on the instance
|
||||
$lineno = $this->lineno;
|
||||
|
||||
$text = substr($this->code, $this->cursor, $pos - $this->cursor);
|
||||
$this->moveCursor($text.$token);
|
||||
$this->moveLineNo($text.$token);
|
||||
|
||||
// array of tokens
|
||||
$result = array();
|
||||
|
||||
// push the template text first
|
||||
if (!empty($text)) {
|
||||
$result[] = new Twig_Token(Twig_Token::TEXT_TYPE, $text, $lineno);
|
||||
$lineno += substr_count($text, "\n");
|
||||
}
|
||||
|
||||
switch ($token) {
|
||||
case $this->options['tag_comment'][0]:
|
||||
if (!preg_match('/(.*?)'.preg_quote($this->options['tag_comment'][1], '/').'/As', $this->code, $match, null, $this->cursor)) {
|
||||
throw new Twig_Error_Syntax('unclosed comment', $this->lineno, $this->filename);
|
||||
}
|
||||
$this->moveCursor($match[0]);
|
||||
$this->moveLineNo($match[0]);
|
||||
break;
|
||||
|
||||
case $this->options['tag_block'][0]:
|
||||
// raw data?
|
||||
if (preg_match('/\s*raw\s*'.preg_quote($this->options['tag_block'][1], '/').'(.*?)'.preg_quote($this->options['tag_block'][0], '/').'\s*endraw\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', $this->code, $match, null, $this->cursor)) {
|
||||
$result[] = new Twig_Token(Twig_Token::TEXT_TYPE, $match[1], $lineno);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->moveLineNo($match[0]);
|
||||
$this->position = self::POSITION_DATA;
|
||||
} else {
|
||||
$result[] = new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', $lineno);
|
||||
$this->position = self::POSITION_BLOCK;
|
||||
}
|
||||
break;
|
||||
|
||||
case $this->options['tag_variable'][0]:
|
||||
$result[] = new Twig_Token(Twig_Token::VAR_START_TYPE, '', $lineno);
|
||||
$this->position = self::POSITION_VAR;
|
||||
break;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
protected function lexBlock()
|
||||
{
|
||||
if (preg_match('/\s*'.preg_quote($this->options['tag_block'][1], '/').'/As', $this->code, $match, null, $this->cursor)) {
|
||||
$lineno = $this->lineno;
|
||||
$this->moveCursor($match[0]);
|
||||
$this->moveLineNo($match[0]);
|
||||
$this->position = self::POSITION_DATA;
|
||||
|
||||
return new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', $lineno);
|
||||
}
|
||||
|
||||
return $this->lexExpression();
|
||||
}
|
||||
|
||||
protected function lexVar()
|
||||
{
|
||||
if (preg_match('/\s*'.preg_quote($this->options['tag_variable'][1], '/').'/As', $this->code, $match, null, $this->cursor)) {
|
||||
$lineno = $this->lineno;
|
||||
$this->moveCursor($match[0]);
|
||||
$this->moveLineNo($match[0]);
|
||||
$this->position = self::POSITION_DATA;
|
||||
|
||||
return new Twig_Token(Twig_Token::VAR_END_TYPE, '', $lineno);
|
||||
}
|
||||
|
||||
return $this->lexExpression();
|
||||
}
|
||||
|
||||
protected function lexExpression()
|
||||
{
|
||||
$match = null;
|
||||
|
||||
// whitespace
|
||||
while (preg_match('/\s+/As', $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->moveLineNo($match[0]);
|
||||
}
|
||||
|
||||
// sanity check
|
||||
if ($this->cursor >= $this->end) {
|
||||
throw new Twig_Error_Syntax('Unexpected end of stream', $this->lineno, $this->filename);
|
||||
}
|
||||
|
||||
// first parse operators
|
||||
if (preg_match(self::REGEX_OPERATOR, $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
|
||||
return new Twig_Token(Twig_Token::OPERATOR_TYPE, $match[0], $this->lineno);
|
||||
}
|
||||
// now names
|
||||
else if (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
|
||||
return new Twig_Token(Twig_Token::NAME_TYPE, $match[0], $this->lineno);
|
||||
}
|
||||
// then numbers
|
||||
else if (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$value = (float)$match[0];
|
||||
if ((int)$value === $value) {
|
||||
$value = (int)$value;
|
||||
}
|
||||
|
||||
return new Twig_Token(Twig_Token::NUMBER_TYPE, $value, $this->lineno);
|
||||
}
|
||||
// and finally strings
|
||||
else if (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->moveLineNo($match[0]);
|
||||
$value = stripcslashes(substr($match[0], 1, strlen($match[0]) - 2));
|
||||
|
||||
return new Twig_Token(Twig_Token::STRING_TYPE, $value, $this->lineno);
|
||||
}
|
||||
|
||||
// unlexable
|
||||
throw new Twig_Error_Syntax(sprintf("Unexpected character '%s'", $this->code[$this->cursor]), $this->lineno, $this->filename);
|
||||
}
|
||||
|
||||
protected function moveLineNo($text)
|
||||
{
|
||||
$this->lineno += substr_count($text, "\n");
|
||||
}
|
||||
|
||||
protected function moveCursor($text)
|
||||
{
|
||||
$this->cursor += strlen($text);
|
||||
}
|
||||
|
||||
}
|
29
lib/classes/Twig/LexerInterface.php
Executable file
29
lib/classes/Twig/LexerInterface.php
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface implemented by lexer classes.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_LexerInterface
|
||||
{
|
||||
/**
|
||||
* Tokenizes a source code.
|
||||
*
|
||||
* @param string $code The source code
|
||||
* @param string $filename A unique identifier for the source code
|
||||
*
|
||||
* @return Twig_TokenStream A token stream instance
|
||||
*/
|
||||
public function tokenize($code, $filename = 'n/a');
|
||||
}
|
84
lib/classes/Twig/Loader/Array.php
Executable file
84
lib/classes/Twig/Loader/Array.php
Executable file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads a template from an array.
|
||||
*
|
||||
* When using this loader with a cache mehcanism, 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.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Loader_Array implements Twig_LoaderInterface
|
||||
{
|
||||
protected $templates;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param array $templates An array of templates (keys are the names, and values are the source code)
|
||||
*
|
||||
* @see Twig_Loader
|
||||
*/
|
||||
public function __construct(array $templates)
|
||||
{
|
||||
$this->templates = array();
|
||||
foreach ($templates as $name => $template) {
|
||||
$this->templates[$name] = $template;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The template source code
|
||||
*/
|
||||
public function getSource($name)
|
||||
{
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $this->templates[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The cache key
|
||||
*/
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $this->templates[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param timestamp $time The last modification time of the cached template
|
||||
*/
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
125
lib/classes/Twig/Loader/Filesystem.php
Executable file
125
lib/classes/Twig/Loader/Filesystem.php
Executable file
@@ -0,0 +1,125 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads template from the filesystem.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Loader_Filesystem implements Twig_LoaderInterface
|
||||
{
|
||||
protected $paths;
|
||||
protected $cache;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
*/
|
||||
public function __construct($paths)
|
||||
{
|
||||
$this->setPaths($paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths to the templates.
|
||||
*
|
||||
* @return array The array of paths where to look for templates
|
||||
*/
|
||||
public function getPaths()
|
||||
{
|
||||
return $this->paths;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the paths where templates are stored.
|
||||
*
|
||||
* @param string|array $paths A path or an array of paths where to look for templates
|
||||
*/
|
||||
public function setPaths($paths)
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = array();
|
||||
|
||||
if (!is_array($paths)) {
|
||||
$paths = array($paths);
|
||||
}
|
||||
|
||||
$this->paths = array();
|
||||
foreach ($paths as $path) {
|
||||
if (!is_dir($path)) {
|
||||
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
|
||||
}
|
||||
|
||||
$this->paths[] = realpath($path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The template source code
|
||||
*/
|
||||
public function getSource($name)
|
||||
{
|
||||
return file_get_contents($this->findTemplate($name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The cache key
|
||||
*/
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
return $this->findTemplate($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param timestamp $time The last modification time of the cached template
|
||||
*/
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
return filemtime($this->findTemplate($name)) < $time;
|
||||
}
|
||||
|
||||
protected function findTemplate($name)
|
||||
{
|
||||
if (isset($this->cache[$name])) {
|
||||
return $this->cache[$name];
|
||||
}
|
||||
|
||||
foreach ($this->paths as $path) {
|
||||
if (!file_exists($path.DIRECTORY_SEPARATOR.$name) || is_dir($path.DIRECTORY_SEPARATOR.$name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$file = realpath($path.DIRECTORY_SEPARATOR.$name);
|
||||
|
||||
// simple security check
|
||||
if (0 !== strpos($file, $path)) {
|
||||
throw new Twig_Error_Loader('Looks like you try to load a template outside configured directories.');
|
||||
}
|
||||
|
||||
return $this->cache[$name] = $file;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths)));
|
||||
}
|
||||
}
|
59
lib/classes/Twig/Loader/String.php
Executable file
59
lib/classes/Twig/Loader/String.php
Executable file
@@ -0,0 +1,59 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Loads a template from a string.
|
||||
*
|
||||
* 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.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Loader_String implements Twig_LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The template source code
|
||||
*/
|
||||
public function getSource($name)
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The cache key
|
||||
*/
|
||||
public function getCacheKey($name)
|
||||
{
|
||||
return $name;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param timestamp $time The last modification time of the cached template
|
||||
*/
|
||||
public function isFresh($name, $time)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
45
lib/classes/Twig/LoaderInterface.php
Executable file
45
lib/classes/Twig/LoaderInterface.php
Executable file
@@ -0,0 +1,45 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface all loaders must implement.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Gets the source code of a template, given its name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The template source code
|
||||
*/
|
||||
public function getSource($name);
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
*
|
||||
* @param string $name string The name of the template to load
|
||||
*
|
||||
* @return string The cache key
|
||||
*/
|
||||
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
|
||||
*/
|
||||
public function isFresh($name, $time);
|
||||
}
|
227
lib/classes/Twig/Node.php
Executable file
227
lib/classes/Twig/Node.php
Executable file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a node in the AST.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node implements Twig_NodeInterface, Countable, IteratorAggregate
|
||||
{
|
||||
protected $nodes;
|
||||
protected $attributes;
|
||||
protected $lineno;
|
||||
protected $tag;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* The nodes are automatically made available as properties ($this->node).
|
||||
* The attributes are automatically made available as array items ($this['name']).
|
||||
*
|
||||
* @param array $nodes An array of named nodes
|
||||
* @param array $attributes An array of attributes (should not be nodes)
|
||||
* @param integer $lineno The line number
|
||||
* @param string $tag The tag name associated with the Node
|
||||
*/
|
||||
public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
|
||||
{
|
||||
$this->nodes = $nodes;
|
||||
$this->attributes = $attributes;
|
||||
$this->lineno = $lineno;
|
||||
$this->tag = $tag;
|
||||
}
|
||||
|
||||
public function __toString()
|
||||
{
|
||||
$attributes = array();
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
$attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true)));
|
||||
}
|
||||
|
||||
$repr = array(get_class($this).'('.implode(', ', $attributes));
|
||||
|
||||
if (count($this->nodes)) {
|
||||
foreach ($this->nodes as $name => $node) {
|
||||
$len = strlen($name) + 4;
|
||||
$noderepr = array();
|
||||
foreach (explode("\n", (string) $node) as $line) {
|
||||
$noderepr[] = str_repeat(' ', $len).$line;
|
||||
}
|
||||
|
||||
$repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr)));
|
||||
}
|
||||
|
||||
$repr[] = ')';
|
||||
} else {
|
||||
$repr[0] .= ')';
|
||||
}
|
||||
|
||||
return implode("\n", $repr);
|
||||
}
|
||||
|
||||
public function toXml($asDom = false)
|
||||
{
|
||||
$dom = new DOMDocument('1.0', 'UTF-8');
|
||||
$dom->formatOutput = true;
|
||||
$dom->appendChild($xml = $dom->createElement('twig'));
|
||||
|
||||
$xml->appendChild($node = $dom->createElement('node'));
|
||||
$node->setAttribute('class', get_class($this));
|
||||
|
||||
foreach ($this->attributes as $name => $value) {
|
||||
$node->appendChild($attribute = $dom->createElement('attribute'));
|
||||
$attribute->setAttribute('name', $name);
|
||||
$attribute->appendChild($dom->createTextNode($value));
|
||||
}
|
||||
|
||||
foreach ($this->nodes as $name => $n) {
|
||||
if (null === $n) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$child = $n->toXml(true)->getElementsByTagName('node')->item(0);
|
||||
$child = $dom->importNode($child, true);
|
||||
$child->setAttribute('name', $name);
|
||||
|
||||
$node->appendChild($child);
|
||||
}
|
||||
|
||||
return $asDom ? $dom : $dom->saveXml();
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
foreach ($this->nodes as $node) {
|
||||
$node->compile($compiler);
|
||||
}
|
||||
}
|
||||
|
||||
public function getLine()
|
||||
{
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
public function getNodeTag()
|
||||
{
|
||||
return $this->tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the attribute is defined.
|
||||
*
|
||||
* @param string The attribute name
|
||||
*
|
||||
* @return Boolean true if the attribute is defined, false otherwise
|
||||
*/
|
||||
public function hasAttribute($name)
|
||||
{
|
||||
return array_key_exists($name, $this->attributes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets an attribute.
|
||||
*
|
||||
* @param string The attribute name
|
||||
*
|
||||
* @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)));
|
||||
}
|
||||
|
||||
return $this->attributes[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets an attribute.
|
||||
*
|
||||
* @param string The attribute name
|
||||
* @param mixed The attribute value
|
||||
*/
|
||||
public function setAttribute($name, $value)
|
||||
{
|
||||
$this->attributes[$name] = $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes an attribute.
|
||||
*
|
||||
* @param string The attribute name
|
||||
*/
|
||||
public function removeAttribute($name)
|
||||
{
|
||||
unset($this->attributes[$name]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the node with the given identifier exists.
|
||||
*
|
||||
* @param string The node name
|
||||
*
|
||||
* @return Boolean true if the node with the given name exists, false otherwise
|
||||
*/
|
||||
public function hasNode($name)
|
||||
{
|
||||
return array_key_exists($name, $this->nodes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a node by name.
|
||||
*
|
||||
* @param string The node name
|
||||
*
|
||||
* @return Twig_Node A Twig_Node instance
|
||||
*/
|
||||
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)));
|
||||
}
|
||||
|
||||
return $this->nodes[$name];
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets a node.
|
||||
*
|
||||
* @param string The node name
|
||||
* @param Twig_Node A Twig_Node instance
|
||||
*/
|
||||
public function setNode($name, $node = null)
|
||||
{
|
||||
$this->nodes[$name] = $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes a node by name.
|
||||
*
|
||||
* @param string The node name
|
||||
*/
|
||||
public function removeNode($name)
|
||||
{
|
||||
unset($this->nodes[$name]);
|
||||
}
|
||||
|
||||
public function count()
|
||||
{
|
||||
return count($this->nodes);
|
||||
}
|
||||
|
||||
public function getIterator()
|
||||
{
|
||||
return new ArrayIterator($this->nodes);
|
||||
}
|
||||
}
|
40
lib/classes/Twig/Node/AutoEscape.php
Executable file
40
lib/classes/Twig/Node/AutoEscape.php
Executable file
@@ -0,0 +1,40 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an autoescape node.
|
||||
*
|
||||
* The value is the escaping strategy (can be html, js, ...)
|
||||
*
|
||||
* The true value is equivalent to html.
|
||||
*
|
||||
* If autoescaping is disabled, then the value is false.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_AutoEscape extends Twig_Node
|
||||
{
|
||||
public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
|
||||
{
|
||||
parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
}
|
||||
}
|
45
lib/classes/Twig/Node/Block.php
Executable file
45
lib/classes/Twig/Node/Block.php
Executable file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a block node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Block extends Twig_Node
|
||||
{
|
||||
public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this->getAttribute('name')), "{\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
38
lib/classes/Twig/Node/BlockReference.php
Executable file
38
lib/classes/Twig/Node/BlockReference.php
Executable file
@@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a block call node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_BlockReference extends Twig_Node
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("\$this->getBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
|
||||
;
|
||||
}
|
||||
}
|
21
lib/classes/Twig/Node/Expression.php
Executable file
21
lib/classes/Twig/Node/Expression.php
Executable file
@@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Abstract class for all nodes that represents an expression.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
abstract class Twig_Node_Expression extends Twig_Node
|
||||
{
|
||||
}
|
41
lib/classes/Twig/Node/Expression/Array.php
Executable file
41
lib/classes/Twig/Node/Expression/Array.php
Executable file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Array extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(array $elements, $lineno)
|
||||
{
|
||||
parent::__construct($elements, array(), $lineno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->raw('array(');
|
||||
$first = true;
|
||||
foreach ($this->nodes as $name => $node) {
|
||||
if (!$first) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$compiler
|
||||
->repr($name)
|
||||
->raw(' => ')
|
||||
->subcompile($node)
|
||||
;
|
||||
}
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
24
lib/classes/Twig/Node/Expression/AssignName.php
Executable file
24
lib/classes/Twig/Node/Expression/AssignName.php
Executable file
@@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name
|
||||
{
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->raw(sprintf('$context[\'%s\']', $this->getAttribute('name')));
|
||||
}
|
||||
}
|
40
lib/classes/Twig/Node/Expression/Binary.php
Executable file
40
lib/classes/Twig/Node/Expression/Binary.php
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno)
|
||||
{
|
||||
parent::__construct(array('left' => $left, 'right' => $right), array(), $lineno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(' ')
|
||||
;
|
||||
$this->operator($compiler);
|
||||
$compiler
|
||||
->raw(' ')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
abstract public function operator($compiler);
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/Add.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/Add.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Add extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('+');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/And.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/And.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_And extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('&&');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/Concat.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/Concat.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Concat extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('.');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/Div.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/Div.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Div extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('/');
|
||||
}
|
||||
}
|
29
lib/classes/Twig/Node/Expression/Binary/FloorDiv.php
Executable file
29
lib/classes/Twig/Node/Expression/Binary/FloorDiv.php
Executable file
@@ -0,0 +1,29 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary
|
||||
{
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->raw('floor(');
|
||||
parent::compile($compiler);
|
||||
$compiler->raw(')');
|
||||
}
|
||||
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('/');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/Mod.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/Mod.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Mod extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('%');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/Mul.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/Mul.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Mul extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('*');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/Or.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/Or.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Or extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('||');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Binary/Sub.php
Executable file
18
lib/classes/Twig/Node/Expression/Binary/Sub.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Binary_Sub extends Twig_Node_Expression_Binary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
return $compiler->raw('-');
|
||||
}
|
||||
}
|
61
lib/classes/Twig/Node/Expression/Compare.php
Executable file
61
lib/classes/Twig/Node/Expression/Compare.php
Executable file
@@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Compare extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $expr, Twig_NodeInterface $ops, $lineno)
|
||||
{
|
||||
parent::__construct(array('expr' => $expr, 'ops' => $ops), array(), $lineno);
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
if ('in' === $this->getNode('ops')->getNode('0')->getAttribute('value')) {
|
||||
return $this->compileIn($compiler);
|
||||
}
|
||||
|
||||
$this->getNode('expr')->compile($compiler);
|
||||
|
||||
$nbOps = count($this->getNode('ops'));
|
||||
for ($i = 0; $i < $nbOps; $i += 2) {
|
||||
if ($i > 0) {
|
||||
$compiler->raw(' && ($tmp'.($i / 2));
|
||||
}
|
||||
|
||||
$compiler->raw(' '.$this->getNode('ops')->getNode($i)->getAttribute('value').' ');
|
||||
|
||||
if ($i != $nbOps - 2) {
|
||||
$compiler
|
||||
->raw('($tmp'.(($i / 2) + 1).' = ')
|
||||
->subcompile($this->getNode('ops')->getNode($i + 1))
|
||||
->raw(')')
|
||||
;
|
||||
} else {
|
||||
$compiler->subcompile($this->getNode('ops')->getNode($i + 1));
|
||||
}
|
||||
}
|
||||
|
||||
for ($j = 1; $j < $i / 2; $j++) {
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileIn($compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('twig_in_filter(')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('ops')->getNode(1))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
31
lib/classes/Twig/Node/Expression/Conditional.php
Executable file
31
lib/classes/Twig/Node/Expression/Conditional.php
Executable file
@@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Conditional extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $expr1, Twig_Node_Expression $expr2, Twig_Node_Expression $expr3, $lineno)
|
||||
{
|
||||
parent::__construct(array('expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno);
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('expr1'))
|
||||
->raw(') ? (')
|
||||
->subcompile($this->getNode('expr2'))
|
||||
->raw(') : (')
|
||||
->subcompile($this->getNode('expr3'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
}
|
23
lib/classes/Twig/Node/Expression/Constant.php
Executable file
23
lib/classes/Twig/Node/Expression/Constant.php
Executable file
@@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Constant extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct($value, $lineno)
|
||||
{
|
||||
parent::__construct(array(), array('value' => $value), $lineno);
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->repr($this->getAttribute('value'));
|
||||
}
|
||||
}
|
34
lib/classes/Twig/Node/Expression/ExtensionReference.php
Normal file
34
lib/classes/Twig/Node/Expression/ExtensionReference.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an extension call node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->raw(sprintf("\$this->env->getExtension('%s')", $this->getAttribute('name')));
|
||||
}
|
||||
}
|
43
lib/classes/Twig/Node/Expression/Filter.php
Executable file
43
lib/classes/Twig/Node/Expression/Filter.php
Executable file
@@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Filter extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filter_name, Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('node' => $node, 'filter' => $filter_name, 'arguments' => $arguments), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
$filterMap = $compiler->getEnvironment()->getFilters();
|
||||
|
||||
$name = $this->getNode('filter')->getAttribute('value');
|
||||
$attrs = $this->getNode('arguments');
|
||||
|
||||
if (!isset($filterMap[$name])) {
|
||||
throw new Twig_Error_Syntax(sprintf('The filter "%s" does not exist', $name), $this->getLine());
|
||||
} else {
|
||||
$compiler->raw($filterMap[$name]->compile().($filterMap[$name]->needsEnvironment() ? '($this->env, ' : '('));
|
||||
}
|
||||
|
||||
$this->getNode('node')->compile($compiler);
|
||||
|
||||
foreach ($attrs as $node) {
|
||||
$compiler
|
||||
->raw(', ')
|
||||
->subcompile($node)
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
45
lib/classes/Twig/Node/Expression/GetAttr.php
Executable file
45
lib/classes/Twig/Node/Expression/GetAttr.php
Executable file
@@ -0,0 +1,45 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
|
||||
{
|
||||
const TYPE_ANY = 'any';
|
||||
const TYPE_ARRAY = 'array';
|
||||
const TYPE_METHOD = 'method';
|
||||
|
||||
public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_NodeInterface $arguments, $type, $lineno)
|
||||
{
|
||||
parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type), $lineno);
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->raw('$this->getAttribute(')
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('attribute'))
|
||||
->raw(', array(')
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $node) {
|
||||
$compiler
|
||||
->subcompile($node)
|
||||
->raw(', ')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('), ')
|
||||
->repr($this->getAttribute('type'))
|
||||
->raw(')');
|
||||
}
|
||||
}
|
33
lib/classes/Twig/Node/Expression/Name.php
Executable file
33
lib/classes/Twig/Node/Expression/Name.php
Executable file
@@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Name extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct($name, $lineno)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno);
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
if ('_self' === $this->getAttribute('name')) {
|
||||
$compiler->raw('$this');
|
||||
} elseif ('_context' === $this->getAttribute('name')) {
|
||||
$compiler->raw('$context');
|
||||
} elseif ('_charset' === $this->getAttribute('name')) {
|
||||
$compiler->raw('$this->getEnvironment()->getCharset()');
|
||||
} elseif ($compiler->getEnvironment()->isStrictVariables()) {
|
||||
$compiler->raw(sprintf('$this->getContext($context, \'%s\')', $this->getAttribute('name'), $this->getAttribute('name')));
|
||||
} else {
|
||||
$compiler->raw(sprintf('(isset($context[\'%s\']) ? $context[\'%s\'] : null)', $this->getAttribute('name'), $this->getAttribute('name')));
|
||||
}
|
||||
}
|
||||
}
|
60
lib/classes/Twig/Node/Expression/Test.php
Executable file
60
lib/classes/Twig/Node/Expression/Test.php
Executable file
@@ -0,0 +1,60 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Test extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
|
||||
{
|
||||
parent::__construct(array('node' => $node, 'arguments' => $arguments), array('name' => $name), $lineno);
|
||||
}
|
||||
|
||||
public function compile($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());
|
||||
}
|
||||
|
||||
// defined is a special case
|
||||
if ('defined' === $this->getAttribute('name')) {
|
||||
if (!$this->getNode('node') instanceof Twig_Node_Expression_Name){
|
||||
throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine());
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw($testMap[$this->getAttribute('name')]->compile().'(')
|
||||
->repr($this->getNode('node')->getAttribute('name'))
|
||||
->raw(', $context)')
|
||||
;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw($testMap[$this->getAttribute('name')]->compile().'(')
|
||||
->subcompile($this->getNode('node'))
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('arguments')) {
|
||||
$compiler->raw(', ');
|
||||
|
||||
$max = count($this->getNode('arguments')) - 1;
|
||||
foreach ($this->getNode('arguments') as $i => $node) {
|
||||
$compiler->subcompile($node);
|
||||
|
||||
if ($i != $max) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw(')');
|
||||
}
|
||||
}
|
30
lib/classes/Twig/Node/Expression/Unary.php
Executable file
30
lib/classes/Twig/Node/Expression/Unary.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
abstract class Twig_Node_Expression_Unary extends Twig_Node_Expression
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $node, $lineno)
|
||||
{
|
||||
parent::__construct(array('node' => $node), array(), $lineno);
|
||||
}
|
||||
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->raw('(');
|
||||
$this->operator($compiler);
|
||||
$compiler
|
||||
->subcompile($this->getNode('node'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
abstract public function operator($compiler);
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Unary/Neg.php
Executable file
18
lib/classes/Twig/Node/Expression/Unary/Neg.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Unary_Neg extends Twig_Node_Expression_Unary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
$compiler->raw('-');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Unary/Not.php
Executable file
18
lib/classes/Twig/Node/Expression/Unary/Not.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Unary_Not extends Twig_Node_Expression_Unary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
$compiler->raw('!');
|
||||
}
|
||||
}
|
18
lib/classes/Twig/Node/Expression/Unary/Pos.php
Executable file
18
lib/classes/Twig/Node/Expression/Unary/Pos.php
Executable file
@@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Node_Expression_Unary_Pos extends Twig_Node_Expression_Unary
|
||||
{
|
||||
public function operator($compiler)
|
||||
{
|
||||
$compiler->raw('+');
|
||||
}
|
||||
}
|
124
lib/classes/Twig/Node/For.php
Executable file
124
lib/classes/Twig/Node/For.php
Executable file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a for node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_For extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $withLoop = false, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => $withLoop), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
// the (array) cast bypasses a PHP 5.2.6 bug
|
||||
->write('$context[\'_parent\'] = (array) $context;'."\n")
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('else')) {
|
||||
$compiler->write("\$context['_iterated'] = false;\n");
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("\$context['_seq'] = twig_iterator_to_array(")
|
||||
->subcompile($this->getNode('seq'))
|
||||
->raw(");\n")
|
||||
;
|
||||
|
||||
if ($this->getAttribute('with_loop')) {
|
||||
$compiler
|
||||
->write("\$countable = is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable);\n")
|
||||
->write("\$length = \$countable ? count(\$context['_seq']) : null;\n")
|
||||
|
||||
->write("\$context['loop'] = array(\n")
|
||||
->write(" 'parent' => \$context['_parent'],\n")
|
||||
->write(" 'index0' => 0,\n")
|
||||
->write(" 'index' => 1,\n")
|
||||
->write(" 'first' => true,\n")
|
||||
->write(");\n")
|
||||
->write("if (\$countable) {\n")
|
||||
->indent()
|
||||
->write("\$context['loop']['revindex0'] = \$length - 1;\n")
|
||||
->write("\$context['loop']['revindex'] = \$length;\n")
|
||||
->write("\$context['loop']['length'] = \$length;\n")
|
||||
->write("\$context['loop']['last'] = 1 === \$length;\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("foreach (\$context['_seq'] as ")
|
||||
->subcompile($this->getNode('key_target'))
|
||||
->raw(" => ")
|
||||
->subcompile($this->getNode('value_target'))
|
||||
->raw(") {\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('else')) {
|
||||
$compiler->write("\$context['_iterated'] = true;\n");
|
||||
}
|
||||
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
|
||||
if ($this->getAttribute('with_loop')) {
|
||||
$compiler
|
||||
->write("++\$context['loop']['index0'];\n")
|
||||
->write("++\$context['loop']['index'];\n")
|
||||
->write("\$context['loop']['first'] = false;\n")
|
||||
->write("if (\$countable) {\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
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('else')) {
|
||||
$compiler
|
||||
->write("if (!\$context['_iterated']) {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('else'))
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->write('$_parent = $context[\'_parent\'];'."\n");
|
||||
|
||||
// remove some "private" loop variables (needed for nested loops)
|
||||
$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");
|
||||
}
|
||||
}
|
67
lib/classes/Twig/Node/If.php
Executable file
67
lib/classes/Twig/Node/If.php
Executable file
@@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an if node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_If extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else = null, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('tests' => $tests, 'else' => $else), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
for ($i = 0; $i < count($this->getNode('tests')); $i += 2) {
|
||||
if ($i > 0) {
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("} elseif (")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write('if (')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('tests')->getNode($i))
|
||||
->raw(") {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('tests')->getNode($i + 1))
|
||||
;
|
||||
}
|
||||
|
||||
if ($this->hasNode('else') && null !== $this->getNode('else')) {
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("} else {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('else'))
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n");
|
||||
}
|
||||
}
|
51
lib/classes/Twig/Node/Import.php
Executable file
51
lib/classes/Twig/Node/Import.php
Executable file
@@ -0,0 +1,51 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an import node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Import extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression_AssignName $var, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
->subcompile($this->getNode('var'))
|
||||
->raw(' = ')
|
||||
;
|
||||
|
||||
if ($this->getNode('expr') instanceof Twig_Node_Expression_Name && '_self' === $this->getNode('expr')->getAttribute('name')) {
|
||||
$compiler->raw("\$this");
|
||||
} else {
|
||||
$compiler
|
||||
->raw('$this->env->loadTemplate(')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(", true)")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw(";\n");
|
||||
}
|
||||
}
|
76
lib/classes/Twig/Node/Include.php
Executable file
76
lib/classes/Twig/Node/Include.php
Executable file
@@ -0,0 +1,76 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents an include node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Include extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (Boolean) $only), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
if ($this->getNode('expr') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
->write("\$this->env->loadTemplate(")
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(")->display(")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write("\$template = ")
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(";\n")
|
||||
->write("if (!\$template")
|
||||
->raw(" instanceof Twig_Template) {\n")
|
||||
->indent()
|
||||
->write("\$template = \$this->env->loadTemplate(\$template);\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
->write('$template->display(')
|
||||
;
|
||||
}
|
||||
|
||||
if (false === $this->getAttribute('only')) {
|
||||
if (null === $this->getNode('variables')) {
|
||||
$compiler->raw('$context');
|
||||
} else {
|
||||
$compiler
|
||||
->raw('array_merge($context, ')
|
||||
->subcompile($this->getNode('variables'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
} else {
|
||||
if (null === $this->getNode('variables')) {
|
||||
$compiler->raw('array()');
|
||||
} else {
|
||||
$compiler->subcompile($this->getNode('variables'));
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw(");\n");
|
||||
}
|
||||
}
|
62
lib/classes/Twig/Node/Macro.php
Executable file
62
lib/classes/Twig/Node/Macro.php
Executable file
@@ -0,0 +1,62 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a macro node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Macro extends Twig_Node
|
||||
{
|
||||
public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$arguments = array();
|
||||
foreach ($this->getNode('arguments') as $argument) {
|
||||
$arguments[] = '$'.$argument->getAttribute('name').' = null';
|
||||
}
|
||||
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(sprintf("public function get%s(%s)\n", $this->getAttribute('name'), implode(', ', $arguments)), "{\n")
|
||||
->indent()
|
||||
->write("\$context = array(\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
foreach ($this->getNode('arguments') as $argument) {
|
||||
$compiler
|
||||
->write('')
|
||||
->string($argument->getAttribute('name'))
|
||||
->raw(' => $'.$argument->getAttribute('name'))
|
||||
->raw(",\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write(");\n\n")
|
||||
->subcompile($this->getNode('body'))
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
188
lib/classes/Twig/Node/Module.php
Executable file
188
lib/classes/Twig/Node/Module.php
Executable file
@@ -0,0 +1,188 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a module node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.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, $filename)
|
||||
{
|
||||
parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros), array('filename' => $filename), 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$this->compileTemplate($compiler);
|
||||
}
|
||||
|
||||
protected function compileTemplate($compiler)
|
||||
{
|
||||
$this->compileClassHeader($compiler);
|
||||
|
||||
if (count($this->getNode('blocks'))) {
|
||||
$this->compileConstructor($compiler);
|
||||
}
|
||||
|
||||
$this->compileGetParent($compiler);
|
||||
|
||||
$this->compileDisplayHeader($compiler);
|
||||
|
||||
$this->compileDisplayBody($compiler);
|
||||
|
||||
$this->compileDisplayFooter($compiler);
|
||||
|
||||
$compiler->subcompile($this->getNode('blocks'));
|
||||
|
||||
$this->compileMacros($compiler);
|
||||
|
||||
$this->compileClassFooter($compiler);
|
||||
}
|
||||
|
||||
protected function compileGetParent($compiler)
|
||||
{
|
||||
if (null === $this->getNode('parent')) {
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("public function getParent(array \$context)\n", "{\n")
|
||||
->indent()
|
||||
->write("if (null === \$this->parent) {\n")
|
||||
->indent();
|
||||
;
|
||||
|
||||
if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
|
||||
$compiler
|
||||
->write("\$this->parent = \$this->env->loadTemplate(")
|
||||
->subcompile($this->getNode('parent'))
|
||||
->raw(");\n")
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->write("\$this->parent = ")
|
||||
->subcompile($this->getNode('parent'))
|
||||
->raw(";\n")
|
||||
->write("if (!\$this->parent")
|
||||
->raw(" instanceof Twig_Template) {\n")
|
||||
->indent()
|
||||
->write("\$this->parent = \$this->env->loadTemplate(\$this->parent);\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->write("return \$this->parent;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileDisplayBody($compiler)
|
||||
{
|
||||
if (null !== $this->getNode('parent')) {
|
||||
// remove all but import nodes
|
||||
foreach ($this->getNode('body') as $node) {
|
||||
if ($node instanceof Twig_Node_Import) {
|
||||
$compiler->subcompile($node);
|
||||
}
|
||||
}
|
||||
|
||||
$compiler
|
||||
->write("\$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));\n")
|
||||
;
|
||||
} else {
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileClassHeader($compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("<?php\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')))
|
||||
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
|
||||
->write("{\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('parent')) {
|
||||
$compiler->write("protected \$parent;\n\n");
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileConstructor($compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("public function __construct(Twig_Environment \$env)\n", "{\n")
|
||||
->indent()
|
||||
->write("parent::__construct(\$env);\n\n")
|
||||
->write("\$this->blocks = array(\n")
|
||||
->indent()
|
||||
;
|
||||
|
||||
foreach ($this->getNode('blocks') as $name => $node) {
|
||||
$compiler
|
||||
->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
->outdent()
|
||||
->write("}\n\n");
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileDisplayHeader($compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("public function display(array \$context, array \$blocks = array())\n", "{\n")
|
||||
->indent()
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileDisplayFooter($compiler)
|
||||
{
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileClassFooter($compiler)
|
||||
{
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
|
||||
protected function compileMacros($compiler)
|
||||
{
|
||||
$compiler->subcompile($this->getNode('macros'));
|
||||
}
|
||||
}
|
40
lib/classes/Twig/Node/Parent.php
Executable file
40
lib/classes/Twig/Node/Parent.php
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a parent node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Parent extends Twig_Node
|
||||
{
|
||||
public function __construct($name, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array(), array('name' => $name), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("\$this->getParentBlock(")
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(", \$context, \$blocks);\n")
|
||||
;
|
||||
}
|
||||
}
|
40
lib/classes/Twig/Node/Print.php
Executable file
40
lib/classes/Twig/Node/Print.php
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a node that outputs an expression.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Print 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($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('echo ')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
48
lib/classes/Twig/Node/Sandbox.php
Executable file
48
lib/classes/Twig/Node/Sandbox.php
Executable file
@@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a sandbox node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Sandbox extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $body, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('body' => $body), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("\$sandbox = \$this->env->getExtension('sandbox');\n")
|
||||
->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n")
|
||||
->indent()
|
||||
->write("\$sandbox->enableSandbox();\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("if (!\$alreadySandboxed) {\n")
|
||||
->indent()
|
||||
->write("\$sandbox->disableSandbox();\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
}
|
68
lib/classes/Twig/Node/SandboxedModule.php
Executable file
68
lib/classes/Twig/Node/SandboxedModule.php
Executable file
@@ -0,0 +1,68 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a module node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_SandboxedModule extends Twig_Node_Module
|
||||
{
|
||||
protected $usedFilters;
|
||||
protected $usedTags;
|
||||
|
||||
public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags)
|
||||
{
|
||||
parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
|
||||
|
||||
$this->usedFilters = $usedFilters;
|
||||
$this->usedTags = $usedTags;
|
||||
}
|
||||
|
||||
protected function compileDisplayBody($compiler)
|
||||
{
|
||||
if (null === $this->getNode('parent')) {
|
||||
$compiler->write("\$this->checkSecurity();\n");
|
||||
}
|
||||
|
||||
parent::compileDisplayBody($compiler);
|
||||
}
|
||||
|
||||
protected function compileDisplayFooter($compiler)
|
||||
{
|
||||
parent::compileDisplayFooter($compiler);
|
||||
|
||||
$compiler
|
||||
->write("protected function checkSecurity() {\n")
|
||||
->indent()
|
||||
->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
|
||||
->indent()
|
||||
->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n")
|
||||
->write(!$this->usedFilters ? "array()\n" : "array('".implode('\', \'', $this->usedFilters)."')\n")
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('parent')) {
|
||||
$compiler
|
||||
->raw("\n")
|
||||
->write("\$this->parent->checkSecurity();\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
52
lib/classes/Twig/Node/SandboxedPrint.php
Executable file
52
lib/classes/Twig/Node/SandboxedPrint.php
Executable file
@@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Twig_Node_SandboxedPrint adds a check for the __toString() method
|
||||
* when the variable is an object and the sandbox is activated.
|
||||
*
|
||||
* When there is a simple Print statement, like {{ article }},
|
||||
* 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.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_SandboxedPrint extends Twig_Node_Print
|
||||
{
|
||||
public function __construct(Twig_Node_Print $node)
|
||||
{
|
||||
parent::__construct($node->getNode('expr'), $node->getLine(), $node->getNodeTag());
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('if ($this->env->hasExtension(\'sandbox\') && is_object(')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(')) {'."\n")
|
||||
->indent()
|
||||
->write('$this->env->getExtension(\'sandbox\')->checkMethodAllowed(')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(', \'__toString\');'."\n")
|
||||
->outdent()
|
||||
->write('}'."\n")
|
||||
;
|
||||
|
||||
parent::compile($compiler);
|
||||
}
|
||||
}
|
79
lib/classes/Twig/Node/Set.php
Executable file
79
lib/classes/Twig/Node/Set.php
Executable file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a set node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Set extends Twig_Node
|
||||
{
|
||||
public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterface $values, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('names' => $names, 'values' => $values), array('capture' => $capture), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
if (count($this->getNode('names')) > 1) {
|
||||
$compiler->write('list(');
|
||||
foreach ($this->getNode('names') as $idx => $node) {
|
||||
if ($idx) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
|
||||
$compiler->subcompile($node);
|
||||
}
|
||||
$compiler->raw(')');
|
||||
} else {
|
||||
if ($this->getAttribute('capture')) {
|
||||
$compiler
|
||||
->write("ob_start();\n")
|
||||
->subcompile($this->getNode('values'))
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->subcompile($this->getNode('names'), false);
|
||||
|
||||
if ($this->getAttribute('capture')) {
|
||||
$compiler->raw(" = ob_get_clean()");
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->getAttribute('capture')) {
|
||||
$compiler->raw(' = ');
|
||||
|
||||
if (count($this->getNode('names')) > 1) {
|
||||
$compiler->write('array(');
|
||||
foreach ($this->getNode('values') as $idx => $value) {
|
||||
if ($idx) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
|
||||
$compiler->subcompile($value);
|
||||
}
|
||||
$compiler->raw(')');
|
||||
} else {
|
||||
$compiler->subcompile($this->getNode('values'));
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw(";\n");
|
||||
}
|
||||
}
|
41
lib/classes/Twig/Node/Spaceless.php
Normal file
41
lib/classes/Twig/Node/Spaceless.php
Normal file
@@ -0,0 +1,41 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a spaceless node.
|
||||
*
|
||||
* It removes spaces between HTML tags.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Spaceless extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $body, $lineno, $tag = 'spaceless')
|
||||
{
|
||||
parent::__construct(array('body' => $body), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("ob_start();\n")
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("echo trim(preg_replace('/>\s+</', '><', ob_get_clean()));\n")
|
||||
;
|
||||
}
|
||||
}
|
40
lib/classes/Twig/Node/Text.php
Executable file
40
lib/classes/Twig/Node/Text.php
Executable file
@@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a text node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Text extends Twig_Node
|
||||
{
|
||||
public function __construct($data, $lineno)
|
||||
{
|
||||
parent::__construct(array(), array('data' => $data), $lineno);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('echo ')
|
||||
->string($this->getAttribute('data'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
124
lib/classes/Twig/Node/Trans.php
Executable file
124
lib/classes/Twig/Node/Trans.php
Executable file
@@ -0,0 +1,124 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a trans node.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Node_Trans extends Twig_Node
|
||||
{
|
||||
public function __construct(Twig_NodeInterface $body, Twig_NodeInterface $plural = null, Twig_Node_Expression $count = null, $lineno, $tag = null)
|
||||
{
|
||||
parent::__construct(array('count' => $count, 'body' => $body, 'plural' => $plural), array(), $lineno, $tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler)
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
list($msg, $vars) = $this->compileString($this->getNode('body'));
|
||||
|
||||
if (null !== $this->getNode('plural')) {
|
||||
list($msg1, $vars1) = $this->compileString($this->getNode('plural'));
|
||||
|
||||
$vars = array_merge($vars, $vars1);
|
||||
}
|
||||
|
||||
$function = null === $this->getNode('plural') ? 'gettext' : 'ngettext';
|
||||
|
||||
if ($vars) {
|
||||
$compiler
|
||||
->write('echo strtr('.$function.'(')
|
||||
->subcompile($msg)
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('plural')) {
|
||||
$compiler
|
||||
->raw(', ')
|
||||
->subcompile($msg1)
|
||||
->raw(', abs(')
|
||||
->subcompile($this->getNode('count'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw('), array(');
|
||||
|
||||
foreach ($vars as $var) {
|
||||
if ('count' === $var->getAttribute('name')) {
|
||||
$compiler
|
||||
->string('%count%')
|
||||
->raw(' => abs(')
|
||||
->subcompile($this->getNode('count'))
|
||||
->raw('), ')
|
||||
;
|
||||
} else {
|
||||
$compiler
|
||||
->string('%'.$var->getAttribute('name').'%')
|
||||
->raw(' => ')
|
||||
->subcompile($var)
|
||||
->raw(', ')
|
||||
;
|
||||
}
|
||||
}
|
||||
|
||||
$compiler->raw("));\n");
|
||||
} else {
|
||||
$compiler
|
||||
->write('echo '.$function.'(')
|
||||
->subcompile($msg)
|
||||
;
|
||||
|
||||
if (null !== $this->getNode('plural')) {
|
||||
$compiler
|
||||
->raw(', ')
|
||||
->subcompile($msg1)
|
||||
->raw(', abs(')
|
||||
->subcompile($this->getNode('count'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->raw(');');
|
||||
}
|
||||
}
|
||||
|
||||
protected function compileString(Twig_NodeInterface $body)
|
||||
{
|
||||
if ($body instanceof Twig_Node_Expression_Name || $body instanceof Twig_Node_Expression_Constant) {
|
||||
return array($body, array());
|
||||
}
|
||||
|
||||
$msg = '';
|
||||
$vars = array();
|
||||
foreach ($body as $node) {
|
||||
if ($node instanceof Twig_Node_Print) {
|
||||
$n = $node->getNode('expr');
|
||||
while ($n instanceof Twig_Node_Expression_Filter) {
|
||||
$n = $n->getNode('node');
|
||||
}
|
||||
$msg .= sprintf('%%%s%%', $n->getAttribute('name'));
|
||||
$vars[] = new Twig_Node_Expression_Name($n->getAttribute('name'), $n->getLine());
|
||||
} else {
|
||||
$msg .= $node->getAttribute('data');
|
||||
}
|
||||
}
|
||||
|
||||
return array(new Twig_Node(array(new Twig_Node_Expression_Constant(trim($msg), $node->getLine()))), $vars);
|
||||
}
|
||||
}
|
30
lib/classes/Twig/NodeInterface.php
Executable file
30
lib/classes/Twig/NodeInterface.php
Executable file
@@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a node in the AST.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_NodeInterface
|
||||
{
|
||||
/**
|
||||
* Compiles the node to PHP.
|
||||
*
|
||||
* @param Twig_Compiler A Twig_Compiler instance
|
||||
*/
|
||||
public function compile($compiler);
|
||||
|
||||
public function getLine();
|
||||
|
||||
public function getNodeTag();
|
||||
}
|
79
lib/classes/Twig/NodeTraverser.php
Executable file
79
lib/classes/Twig/NodeTraverser.php
Executable file
@@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Twig_NodeTraverser is a node traverser.
|
||||
*
|
||||
* It visits all nodes and their children and call the given visitor for each.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_NodeTraverser
|
||||
{
|
||||
protected $env;
|
||||
protected $visitors;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* @param Twig_Environment $env A Twig_Environment instance
|
||||
* @param array $visitors An array of Twig_NodeVisitorInterface instances
|
||||
*/
|
||||
public function __construct(Twig_Environment $env, array $visitors = array())
|
||||
{
|
||||
$this->env = $env;
|
||||
$this->visitors = array();
|
||||
foreach ($visitors as $visitor) {
|
||||
$this->addVisitor($visitor);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a visitor.
|
||||
*
|
||||
* @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
|
||||
*/
|
||||
public function addVisitor(Twig_NodeVisitorInterface $visitor)
|
||||
{
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Traverses a node and calls the registered visitors.
|
||||
*
|
||||
* @param Twig_NodeInterface $node A Twig_NodeInterface instance
|
||||
*/
|
||||
public function traverse(Twig_NodeInterface $node = null)
|
||||
{
|
||||
if (null === $node) {
|
||||
return null;
|
||||
}
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$node = $visitor->enterNode($node, $this->env);
|
||||
}
|
||||
|
||||
foreach ($node as $k => $n) {
|
||||
if (false !== $n = $this->traverse($n)) {
|
||||
$node->setNode($k, $n);
|
||||
} else {
|
||||
$node->removeNode($k);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->visitors as $visitor) {
|
||||
$node = $visitor->leaveNode($node, $this->env);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
123
lib/classes/Twig/NodeVisitor/Escaper.php
Executable file
123
lib/classes/Twig/NodeVisitor/Escaper.php
Executable file
@@ -0,0 +1,123 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Twig_NodeVisitor_Escaper implements output escaping.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
|
||||
{
|
||||
protected $statusStack = array();
|
||||
protected $blocks = array();
|
||||
|
||||
protected $safeAnalysis;
|
||||
protected $traverser;
|
||||
|
||||
function __construct()
|
||||
{
|
||||
$this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis();
|
||||
}
|
||||
|
||||
/**
|
||||
* Called before child nodes are visited.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_AutoEscape) {
|
||||
$this->statusStack[] = $node->getAttribute('value');
|
||||
} elseif ($node instanceof Twig_Node_Print) {
|
||||
return $this->escapeNode($node, $env, $this->needEscaping($env));
|
||||
} elseif ($node instanceof Twig_Node_Block) {
|
||||
$this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after child nodes are visited.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) {
|
||||
array_pop($this->statusStack);
|
||||
} elseif ($node instanceof Twig_Node_BlockReference) {
|
||||
$this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function escapeNode(Twig_NodeInterface $node, Twig_Environment $env, $type)
|
||||
{
|
||||
if (false === $type) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
$expression = $node instanceof Twig_Node_Print ? $node->getNode('expr') : $node;
|
||||
|
||||
$safe = $this->safeAnalysis->getSafe($expression);
|
||||
|
||||
if (null === $safe) {
|
||||
if (null === $this->traverser) {
|
||||
$this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis));
|
||||
}
|
||||
$this->traverser->traverse($expression);
|
||||
$safe = $this->safeAnalysis->getSafe($expression);
|
||||
}
|
||||
|
||||
if (false !== in_array($type, $safe) || false !== in_array('all', $safe)) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
if ($node instanceof Twig_Node_Print) {
|
||||
return new Twig_Node_Print(
|
||||
$this->getEscaperFilter($type, $expression),
|
||||
$node->getLine()
|
||||
);
|
||||
}
|
||||
|
||||
return $this->getEscaperFilter($type, $node);
|
||||
}
|
||||
|
||||
protected function needEscaping(Twig_Environment $env)
|
||||
{
|
||||
if (count($this->statusStack)) {
|
||||
return $this->statusStack[count($this->statusStack) - 1];
|
||||
}
|
||||
|
||||
if ($env->hasExtension('escaper') && $env->getExtension('escaper')->isGlobal()) {
|
||||
return 'html';
|
||||
}
|
||||
|
||||
return 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)));
|
||||
return new Twig_Node_Expression_Filter($node, $name, $args, $line);
|
||||
}
|
||||
}
|
84
lib/classes/Twig/NodeVisitor/SafeAnalysis.php
Normal file
84
lib/classes/Twig/NodeVisitor/SafeAnalysis.php
Normal file
@@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
|
||||
{
|
||||
protected $data = array();
|
||||
|
||||
public function getSafe(Twig_NodeInterface $node)
|
||||
{
|
||||
$hash = spl_object_hash($node);
|
||||
if (isset($this->data[$hash])) {
|
||||
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) {
|
||||
if ($bucket['key'] === $node) {
|
||||
$bucket['value'] = $safe;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
$this->data[$hash][] = array(
|
||||
'key' => $node,
|
||||
'value' => $safe,
|
||||
);
|
||||
}
|
||||
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
return $node;
|
||||
}
|
||||
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Expression_Constant) {
|
||||
// constants are marked safe for all
|
||||
$this->setSafe($node, array('all'));
|
||||
} elseif ($node instanceof Twig_Node_Expression_Conditional) {
|
||||
// instersect safeness of both operands
|
||||
$safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3')));
|
||||
$this->setSafe($node, $safe);
|
||||
} elseif ($node instanceof Twig_Node_Expression_Filter) {
|
||||
// filter expression is safe when the filter is safe
|
||||
$filterMap = $env->getFilters();
|
||||
$name = $node->getNode('filter')->getAttribute('value');
|
||||
$args = $node->getNode('arguments');
|
||||
if (isset($filterMap[$name])) {
|
||||
$this->setSafe($node, $filterMap[$name]->getSafe($args));
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
} else {
|
||||
$this->setSafe($node, array());
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
protected function intersectSafe(array $a = null, array $b = null)
|
||||
{
|
||||
if (null === $a || null === $b) {
|
||||
return array();
|
||||
}
|
||||
|
||||
if (in_array('all', $a)) {
|
||||
return $b;
|
||||
}
|
||||
|
||||
if (in_array('all', $b)) {
|
||||
return $a;
|
||||
}
|
||||
|
||||
return array_intersect($a, $b);
|
||||
}
|
||||
}
|
78
lib/classes/Twig/NodeVisitor/Sandbox.php
Executable file
78
lib/classes/Twig/NodeVisitor/Sandbox.php
Executable file
@@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Twig_NodeVisitor_Sandbox implements sandboxing.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
|
||||
{
|
||||
protected $inAModule = false;
|
||||
protected $tags;
|
||||
protected $filters;
|
||||
|
||||
/**
|
||||
* Called before child nodes are visited.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Module) {
|
||||
$this->inAModule = true;
|
||||
$this->tags = array();
|
||||
$this->filters = array();
|
||||
|
||||
return $node;
|
||||
} elseif ($this->inAModule) {
|
||||
// look for tags
|
||||
if ($node->getNodeTag()) {
|
||||
$this->tags[] = $node->getNodeTag();
|
||||
}
|
||||
|
||||
// look for filters
|
||||
if ($node instanceof Twig_Node_Expression_Filter) {
|
||||
$this->filters[] = $node->getNode('filter')->getAttribute('value');
|
||||
}
|
||||
|
||||
// look for simple print statements ({{ article }})
|
||||
if ($node instanceof Twig_Node_Print && $node->getNode('expr') instanceof Twig_Node_Expression_Name) {
|
||||
return new Twig_Node_SandboxedPrint($node);
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called after child nodes are visited.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
|
||||
{
|
||||
if ($node instanceof Twig_Node_Module) {
|
||||
$this->inAModule = false;
|
||||
|
||||
return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags));
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
39
lib/classes/Twig/NodeVisitorInterface.php
Executable file
39
lib/classes/Twig/NodeVisitorInterface.php
Executable file
@@ -0,0 +1,39 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Twig_NodeVisitorInterface is the interface the all node visitor classes must implement.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_NodeVisitorInterface
|
||||
{
|
||||
/**
|
||||
* Called before child nodes are visited.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env);
|
||||
|
||||
/**
|
||||
* Called after child nodes are visited.
|
||||
*
|
||||
* @param Twig_NodeInterface $node The node to visit
|
||||
* @param Twig_Environment $env The Twig environment instance
|
||||
*
|
||||
* @param Twig_NodeInterface The modified node
|
||||
*/
|
||||
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
|
||||
}
|
227
lib/classes/Twig/Parser.php
Executable file
227
lib/classes/Twig/Parser.php
Executable file
@@ -0,0 +1,227 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
class Twig_Parser implements Twig_ParserInterface
|
||||
{
|
||||
protected $stream;
|
||||
protected $parent;
|
||||
protected $handlers;
|
||||
protected $visitors;
|
||||
protected $expressionParser;
|
||||
protected $blocks;
|
||||
protected $blockStack;
|
||||
protected $macros;
|
||||
protected $env;
|
||||
|
||||
public function __construct(Twig_Environment $env = null)
|
||||
{
|
||||
if (null !== $env) {
|
||||
$this->setEnvironment($env);
|
||||
}
|
||||
}
|
||||
|
||||
public function setEnvironment(Twig_Environment $env)
|
||||
{
|
||||
$this->env = $env;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
*/
|
||||
public function parse(Twig_TokenStream $stream)
|
||||
{
|
||||
// tag handlers
|
||||
$this->handlers = $this->env->getTokenParsers();
|
||||
$this->handlers->setParser($this);
|
||||
|
||||
// node visitors
|
||||
$this->visitors = $this->env->getNodeVisitors();
|
||||
|
||||
if (null === $this->expressionParser) {
|
||||
$this->expressionParser = new Twig_ExpressionParser($this);
|
||||
}
|
||||
|
||||
$this->stream = $stream;
|
||||
$this->parent = null;
|
||||
$this->blocks = array();
|
||||
$this->macros = array();
|
||||
$this->blockStack = array();
|
||||
|
||||
try {
|
||||
$body = $this->subparse(null);
|
||||
} catch (Twig_Error_Syntax $e) {
|
||||
if (null === $e->getFilename()) {
|
||||
$e->setFilename($this->stream->getFilename());
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (null !== $this->parent) {
|
||||
$this->checkBodyNodes($body);
|
||||
}
|
||||
|
||||
$node = new Twig_Node_Module($body, $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), $this->stream->getFilename());
|
||||
|
||||
$traverser = new Twig_NodeTraverser($this->env, $this->visitors);
|
||||
|
||||
return $traverser->traverse($node);
|
||||
}
|
||||
|
||||
public function subparse($test, $dropNeedle = false)
|
||||
{
|
||||
$lineno = $this->getCurrentToken()->getLine();
|
||||
$rv = array();
|
||||
while (!$this->stream->isEOF()) {
|
||||
switch ($this->getCurrentToken()->getType()) {
|
||||
case Twig_Token::TEXT_TYPE:
|
||||
$token = $this->stream->next();
|
||||
$rv[] = new Twig_Node_Text($token->getValue(), $token->getLine());
|
||||
break;
|
||||
|
||||
case Twig_Token::VAR_START_TYPE:
|
||||
$token = $this->stream->next();
|
||||
$expr = $this->expressionParser->parseExpression();
|
||||
$this->stream->expect(Twig_Token::VAR_END_TYPE);
|
||||
$rv[] = new Twig_Node_Print($expr, $token->getLine());
|
||||
break;
|
||||
|
||||
case Twig_Token::BLOCK_START_TYPE:
|
||||
$this->stream->next();
|
||||
$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());
|
||||
}
|
||||
|
||||
if (null !== $test && call_user_func($test, $token)) {
|
||||
if ($dropNeedle) {
|
||||
$this->stream->next();
|
||||
}
|
||||
|
||||
return new Twig_Node($rv, array(), $lineno);
|
||||
}
|
||||
|
||||
$subparser = $this->handlers->getTokenParser($token->getValue());
|
||||
if (null === $subparser) {
|
||||
throw new Twig_Error_Syntax(sprintf('Unknown tag name "%s"', $token->getValue()), $token->getLine());
|
||||
}
|
||||
|
||||
$this->stream->next();
|
||||
|
||||
$node = $subparser->parse($token);
|
||||
if (null !== $node) {
|
||||
$rv[] = $node;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.');
|
||||
}
|
||||
}
|
||||
|
||||
return new Twig_Node($rv, array(), $lineno);
|
||||
}
|
||||
|
||||
public function addHandler($name, $class)
|
||||
{
|
||||
$this->handlers[$name] = $class;
|
||||
}
|
||||
|
||||
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
|
||||
{
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
public function getBlockStack()
|
||||
{
|
||||
return $this->blockStack;
|
||||
}
|
||||
|
||||
public function peekBlockStack()
|
||||
{
|
||||
return $this->blockStack[count($this->blockStack) - 1];
|
||||
}
|
||||
|
||||
public function popBlockStack()
|
||||
{
|
||||
array_pop($this->blockStack);
|
||||
}
|
||||
|
||||
public function pushBlockStack($name)
|
||||
{
|
||||
$this->blockStack[] = $name;
|
||||
}
|
||||
|
||||
public function hasBlock($name)
|
||||
{
|
||||
return isset($this->blocks[$name]);
|
||||
}
|
||||
|
||||
public function setBlock($name, $value)
|
||||
{
|
||||
$this->blocks[$name] = $value;
|
||||
}
|
||||
|
||||
public function hasMacro($name)
|
||||
{
|
||||
return isset($this->macros[$name]);
|
||||
}
|
||||
|
||||
public function setMacro($name, $value)
|
||||
{
|
||||
$this->macros[$name] = $value;
|
||||
}
|
||||
|
||||
public function getExpressionParser()
|
||||
{
|
||||
return $this->expressionParser;
|
||||
}
|
||||
|
||||
public function getParent()
|
||||
{
|
||||
return $this->parent;
|
||||
}
|
||||
|
||||
public function setParent($parent)
|
||||
{
|
||||
$this->parent = $parent;
|
||||
}
|
||||
|
||||
public function getStream()
|
||||
{
|
||||
return $this->stream;
|
||||
}
|
||||
|
||||
public function getCurrentToken()
|
||||
{
|
||||
return $this->stream->getCurrent();
|
||||
}
|
||||
|
||||
protected function checkBodyNodes($body)
|
||||
{
|
||||
// check that the body only contains block references and empty text nodes
|
||||
foreach ($body as $node)
|
||||
{
|
||||
if (
|
||||
($node instanceof Twig_Node_Text && !preg_match('/^\s*$/s', $node->getAttribute('data')))
|
||||
||
|
||||
(!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && !$node instanceof Twig_Node_Import)
|
||||
) {
|
||||
throw new Twig_Error_Syntax('A template that extends another one cannot have a body', $node->getLine(), $this->stream->getFilename());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
28
lib/classes/Twig/ParserInterface.php
Executable file
28
lib/classes/Twig/ParserInterface.php
Executable 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interface implemented by parser classes.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_ParserInterface
|
||||
{
|
||||
/**
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @param Twig_TokenStream $stream A token stream instance
|
||||
*
|
||||
* @return Twig_Node_Module A node tree
|
||||
*/
|
||||
public function parse(Twig_TokenStream $code);
|
||||
}
|
8
lib/classes/Twig/README.markdown
Normal file
8
lib/classes/Twig/README.markdown
Normal file
@@ -0,0 +1,8 @@
|
||||
Twig, the flexible, fast, and secure template language for PHP
|
||||
==============================================================
|
||||
|
||||
Twig is a template language for PHP, released under the new BSD license (code
|
||||
and documentation).
|
||||
|
||||
Twig uses a syntax similar to the Django and Jinja template languages which
|
||||
inspired the Twig runtime environment.
|
22
lib/classes/Twig/RuntimeError.php
Executable file
22
lib/classes/Twig/RuntimeError.php
Executable file
@@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs at runtime.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
* @version SVN: $Id$
|
||||
*/
|
||||
class Twig_RuntimeError extends Twig_Error
|
||||
{
|
||||
}
|
20
lib/classes/Twig/Sandbox/SecurityError.php
Executable file
20
lib/classes/Twig/Sandbox/SecurityError.php
Executable file
@@ -0,0 +1,20 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when a security error occurs at runtime.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Sandbox_SecurityError extends Twig_Error
|
||||
{
|
||||
}
|
99
lib/classes/Twig/Sandbox/SecurityPolicy.php
Executable file
99
lib/classes/Twig/Sandbox/SecurityPolicy.php
Executable file
@@ -0,0 +1,99 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Represents a security policy which need to be enforced when sandbox mode is enabled.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterface
|
||||
{
|
||||
protected $allowedTags;
|
||||
protected $allowedFilters;
|
||||
protected $allowedMethods;
|
||||
protected $allowedProperties;
|
||||
|
||||
public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array())
|
||||
{
|
||||
$this->allowedTags = $allowedTags;
|
||||
$this->allowedFilters = $allowedFilters;
|
||||
$this->allowedMethods = $allowedMethods;
|
||||
$this->allowedProperties = $allowedProperties;
|
||||
}
|
||||
|
||||
public function setAllowedTags(array $tags)
|
||||
{
|
||||
$this->allowedTags = $tags;
|
||||
}
|
||||
|
||||
public function setAllowedFilters(array $filters)
|
||||
{
|
||||
$this->allowedFilters = $filters;
|
||||
}
|
||||
|
||||
public function setAllowedMethods(array $methods)
|
||||
{
|
||||
$this->allowedMethods = $methods;
|
||||
}
|
||||
|
||||
public function setAllowedProperties(array $properties)
|
||||
{
|
||||
$this->allowedProperties = $properties;
|
||||
}
|
||||
|
||||
public function checkSecurity($tags, $filters)
|
||||
{
|
||||
foreach ($tags as $tag) {
|
||||
if (!in_array($tag, $this->allowedTags)) {
|
||||
throw new Twig_Sandbox_SecurityError(sprintf('Tag "%s" is not allowed.', $tag));
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($filters as $filter) {
|
||||
if (!in_array($filter, $this->allowedFilters)) {
|
||||
throw new Twig_Sandbox_SecurityError(sprintf('Filter "%s" is not allowed.', $filter));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function checkMethodAllowed($obj, $method)
|
||||
{
|
||||
$allowed = false;
|
||||
foreach ($this->allowedMethods as $class => $methods) {
|
||||
if ($obj instanceof $class) {
|
||||
$allowed = in_array($method, is_array($methods) ? $methods : array($methods));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, get_class($obj)));
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPropertyAllowed($obj, $property)
|
||||
{
|
||||
$allowed = false;
|
||||
foreach ($this->allowedProperties as $class => $properties) {
|
||||
if ($obj instanceof $class) {
|
||||
$allowed = in_array($property, is_array($properties) ? $properties : array($properties));
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$allowed) {
|
||||
throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, get_class($obj)));
|
||||
}
|
||||
}
|
||||
}
|
25
lib/classes/Twig/Sandbox/SecurityPolicyInterface.php
Executable file
25
lib/classes/Twig/Sandbox/SecurityPolicyInterface.php
Executable file
@@ -0,0 +1,25 @@
|
||||
<?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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Interfaces that all security policy classes must implements.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
*/
|
||||
interface Twig_Sandbox_SecurityPolicyInterface
|
||||
{
|
||||
public function checkSecurity($tags, $filters);
|
||||
|
||||
public function checkMethodAllowed($obj, $method);
|
||||
|
||||
public function checkPropertyAllowed($obj, $method);
|
||||
}
|
129
lib/classes/Twig/SimpleTokenParser.php
Executable file
129
lib/classes/Twig/SimpleTokenParser.php
Executable file
@@ -0,0 +1,129 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2010 Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
abstract class Twig_SimpleTokenParser extends Twig_TokenParser
|
||||
{
|
||||
/**
|
||||
* Parses a token and returns a node.
|
||||
*
|
||||
* @param Twig_Token $token A Twig_Token instance
|
||||
*
|
||||
* @return Twig_NodeInterface A Twig_NodeInterface instance
|
||||
*/
|
||||
public function parse(Twig_Token $token)
|
||||
{
|
||||
$grammar = $this->getGrammar();
|
||||
if (!is_object($grammar)) {
|
||||
$grammar = self::parseGrammar($grammar);
|
||||
}
|
||||
|
||||
$grammar->setParser($this->parser);
|
||||
$values = $grammar->parse($token);
|
||||
|
||||
return $this->getNode($values, $token->getLine());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the grammar as an object or as a string.
|
||||
*
|
||||
* @return string|Twig_Grammar A Twig_Grammar instance or a string
|
||||
*/
|
||||
abstract protected function getGrammar();
|
||||
|
||||
/**
|
||||
* Gets the nodes based on the parsed values.
|
||||
*
|
||||
* @param array $values An array of values
|
||||
* @param integer $line The parser line
|
||||
*/
|
||||
abstract protected function getNode(array $values, $line);
|
||||
|
||||
protected function getAttribute($node, $attribute, $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY, $line = -1)
|
||||
{
|
||||
return new Twig_Node_Expression_GetAttr(
|
||||
$node instanceof Twig_NodeInterface ? $node : new Twig_Node_Expression_Name($node, $line),
|
||||
$attribute instanceof Twig_NodeInterface ? $attribute : new Twig_Node_Expression_Constant($attribute, $line),
|
||||
$arguments instanceof Twig_NodeInterface ? $arguments : new Twig_Node($arguments),
|
||||
$type,
|
||||
$line
|
||||
);
|
||||
}
|
||||
|
||||
protected function call($node, $attribute, $arguments = array(), $line = -1)
|
||||
{
|
||||
return $this->getAttribute($node, $attribute, $arguments, Twig_Node_Expression_GetAttr::TYPE_METHOD, $line);
|
||||
}
|
||||
|
||||
protected function markAsSafe(Twig_NodeInterface $node, $line = -1)
|
||||
{
|
||||
return new Twig_Node_Expression_Filter(
|
||||
$node,
|
||||
new Twig_Node_Expression_Constant('raw', $line),
|
||||
new Twig_Node(),
|
||||
$line
|
||||
);
|
||||
}
|
||||
|
||||
protected function output(Twig_NodeInterface $node, $line = -1)
|
||||
{
|
||||
return new Twig_Node_Print($node, $line);
|
||||
}
|
||||
|
||||
protected function getNodeValues(array $values)
|
||||
{
|
||||
$nodes = array();
|
||||
foreach ($values as $value) {
|
||||
if ($value instanceof Twig_NodeInterface) {
|
||||
$nodes[] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
return $nodes;
|
||||
}
|
||||
|
||||
static public function parseGrammar($str, $main = true)
|
||||
{
|
||||
static $cursor;
|
||||
|
||||
if (true === $main) {
|
||||
$cursor = 0;
|
||||
$grammar = new Twig_Grammar_Tag();
|
||||
} else {
|
||||
$grammar = new Twig_Grammar_Optional();
|
||||
}
|
||||
|
||||
while ($cursor < strlen($str)) {
|
||||
if (preg_match('/\s+/A', $str, $match, null, $cursor)) {
|
||||
$cursor += strlen($match[0]);
|
||||
} elseif (preg_match('/<(\w+)(?:\:(\w+))?>/A', $str, $match, null, $cursor)) {
|
||||
$class = sprintf('Twig_Grammar_%s', ucfirst(isset($match[2]) ? $match[2] : 'Expression'));
|
||||
if (!class_exists($class)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Unable to understand "%s" in grammar (%s class does not exist)', $match[0], $class));
|
||||
}
|
||||
$grammar->addGrammar(new $class($match[1]));
|
||||
$cursor += strlen($match[0]);
|
||||
} elseif (preg_match('/(\w+|,)/A', $str, $match, null, $cursor)) {
|
||||
$grammar->addGrammar(new Twig_Grammar_Constant($match[1]));
|
||||
$cursor += strlen($match[0]);
|
||||
} elseif (preg_match('/\[/A', $str, $match, null, $cursor)) {
|
||||
$cursor += strlen($match[0]);
|
||||
$grammar->addGrammar(self::parseGrammar($str, false));
|
||||
} elseif (true !== $main && preg_match('/\]/A', $str, $match, null, $cursor)) {
|
||||
$cursor += strlen($match[0]);
|
||||
|
||||
return $grammar;
|
||||
} else {
|
||||
throw new Twig_Error_Runtime(sprintf('Unable to parse grammar "%s" near "...%s..."', $str, substr($str, $cursor, 10)));
|
||||
}
|
||||
}
|
||||
|
||||
return $grammar;
|
||||
}
|
||||
}
|
53
lib/classes/Twig/SyntaxError.php
Executable file
53
lib/classes/Twig/SyntaxError.php
Executable file
@@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Exception thrown when a syntax error occurs during lexing or parsing of a template.
|
||||
*
|
||||
* @package twig
|
||||
* @author Fabien Potencier <fabien.potencier@symfony-project.com>
|
||||
* @version SVN: $Id$
|
||||
*/
|
||||
class Twig_SyntaxError extends Twig_Error
|
||||
{
|
||||
protected $lineno;
|
||||
protected $filename;
|
||||
protected $rawMessage;
|
||||
|
||||
public function __construct($message, $lineno, $filename = null)
|
||||
{
|
||||
$this->lineno = $lineno;
|
||||
$this->filename = $filename;
|
||||
$this->rawMessage = $message;
|
||||
|
||||
$this->updateRepr();
|
||||
|
||||
parent::__construct($this->message, $lineno);
|
||||
}
|
||||
|
||||
public function getFilename()
|
||||
{
|
||||
return $this->filename;
|
||||
}
|
||||
|
||||
public function setFilename($filename)
|
||||
{
|
||||
$this->filename = $filename;
|
||||
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
protected function updateRepr()
|
||||
{
|
||||
$this->message = $this->rawMessage.' in '.($this->filename ? $this->filename : 'n/a').' at line '.$this->lineno;
|
||||
}
|
||||
}
|
173
lib/classes/Twig/Template.php
Executable file
173
lib/classes/Twig/Template.php
Executable file
@@ -0,0 +1,173 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) 2009 Fabien Potencier
|
||||
* (c) 2009 Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
abstract class Twig_Template implements Twig_TemplateInterface
|
||||
{
|
||||
static protected $cache = array();
|
||||
|
||||
protected $env;
|
||||
protected $blocks;
|
||||
|
||||
public function __construct(Twig_Environment $env)
|
||||
{
|
||||
$this->env = $env;
|
||||
$this->blocks = array();
|
||||
}
|
||||
|
||||
public function getEnvironment()
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
public function getParent(array $context)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getParentBlock($name, array $context, array $blocks = array())
|
||||
{
|
||||
if (false !== $parent = $this->getParent($context)) {
|
||||
return $parent->getBlock($name, $context, $blocks);
|
||||
} else {
|
||||
throw new Twig_Error_Runtime('This template has no parent.');
|
||||
}
|
||||
}
|
||||
|
||||
public function getBlock($name, array $context, array $blocks = array())
|
||||
{
|
||||
if (isset($blocks[$name])) {
|
||||
$b = $blocks;
|
||||
unset($b[$name]);
|
||||
return call_user_func($blocks[$name], $context, $b);
|
||||
} elseif (isset($this->blocks[$name])) {
|
||||
return call_user_func($this->blocks[$name], $context, $blocks);
|
||||
}
|
||||
|
||||
if (false !== $parent = $this->getParent($context)) {
|
||||
return $parent->getBlock($name, $context, array_merge($this->blocks, $blocks));
|
||||
}
|
||||
}
|
||||
|
||||
public function hasBlock($name)
|
||||
{
|
||||
return isset($this->blocks[$name]);
|
||||
}
|
||||
|
||||
public function getBlockNames()
|
||||
{
|
||||
return array_keys($this->blocks);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders the template with the given context and returns it as string.
|
||||
*
|
||||
* @param array $context An array of parameters to pass to the template
|
||||
*
|
||||
* @return string The rendered template
|
||||
*/
|
||||
public function render(array $context)
|
||||
{
|
||||
ob_start();
|
||||
try {
|
||||
$this->display($context);
|
||||
} catch (Exception $e) {
|
||||
ob_end_clean();
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
|
||||
protected function getContext($context, $item)
|
||||
{
|
||||
if (!array_key_exists($item, $context)) {
|
||||
throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist.', $item));
|
||||
}
|
||||
|
||||
return $context[$item];
|
||||
}
|
||||
|
||||
protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_Node_Expression_GetAttr::TYPE_ANY)
|
||||
{
|
||||
// array
|
||||
if (Twig_Node_Expression_GetAttr::TYPE_METHOD !== $type) {
|
||||
if ((is_array($object) || is_object($object) && $object instanceof ArrayAccess) && isset($object[$item])) {
|
||||
return $object[$item];
|
||||
}
|
||||
|
||||
if (Twig_Node_Expression_GetAttr::TYPE_ARRAY === $type) {
|
||||
if (!$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Key "%s" for array "%s" does not exist.', $item, $object));
|
||||
}
|
||||
}
|
||||
|
||||
if (!is_object($object)) {
|
||||
if (!$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Item "%s" for "%s" does not exist.', $item, $object));
|
||||
}
|
||||
|
||||
// get some information about the object
|
||||
$class = get_class($object);
|
||||
if (!isset(self::$cache[$class])) {
|
||||
$r = new ReflectionClass($class);
|
||||
self::$cache[$class] = array('methods' => array(), 'properties' => array());
|
||||
foreach ($r->getMethods(ReflectionMethod::IS_PUBLIC) as $method) {
|
||||
self::$cache[$class]['methods'][strtolower($method->getName())] = true;
|
||||
}
|
||||
|
||||
foreach ($r->getProperties(ReflectionProperty::IS_PUBLIC) as $property) {
|
||||
self::$cache[$class]['properties'][strtolower($property->getName())] = true;
|
||||
}
|
||||
}
|
||||
|
||||
// object property
|
||||
if (Twig_Node_Expression_GetAttr::TYPE_METHOD !== $type) {
|
||||
if (isset(self::$cache[$class]['properties'][strtolower($item)]) || isset($object->$item)) {
|
||||
if ($this->env->hasExtension('sandbox')) {
|
||||
$this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
|
||||
}
|
||||
|
||||
return $object->$item;
|
||||
}
|
||||
}
|
||||
|
||||
// object method
|
||||
$lcItem = strtolower($item);
|
||||
if (isset(self::$cache[$class]['methods'][$lcItem])) {
|
||||
$method = $item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
|
||||
$method = 'get'.$item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
|
||||
$method = 'is'.$item;
|
||||
} elseif (isset(self::$cache[$class]['methods']['__call'])) {
|
||||
$method = $item;
|
||||
} else {
|
||||
if (!$this->env->isStrictVariables()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist.', $item, get_class($object)));
|
||||
}
|
||||
|
||||
if ($this->env->hasExtension('sandbox')) {
|
||||
$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
|
||||
}
|
||||
|
||||
return call_user_func_array(array($object, $method), $arguments);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user