pax_global_header 0000666 0000000 0000000 00000000064 12616650102 0014511 g ustar 00root root 0000000 0000000 52 comment=d9b6333ae8dd2c8e3fd256e127548def0bc614c6
Twig-1.23.1/ 0000775 0000000 0000000 00000000000 12616650102 0012507 5 ustar 00root root 0000000 0000000 Twig-1.23.1/.editorconfig 0000664 0000000 0000000 00000000340 12616650102 0015161 0 ustar 00root root 0000000 0000000 ; top-most EditorConfig file
root = true
; Unix-style newlines
[*]
end_of_line = LF
[*.php]
indent_style = space
indent_size = 4
[*.test]
indent_style = space
indent_size = 4
[*.rst]
indent_style = space
indent_size = 4
Twig-1.23.1/.gitignore 0000664 0000000 0000000 00000000105 12616650102 0014473 0 ustar 00root root 0000000 0000000 /build
/composer.lock
/ext/twig/autom4te.cache/
/phpunit.xml
/vendor
Twig-1.23.1/.travis.yml 0000664 0000000 0000000 00000001642 12616650102 0014623 0 ustar 00root root 0000000 0000000 language: php
sudo: false
cache:
directories:
- vendor
- $HOME/.composer/cache
php:
- 5.2
- 5.3
- 5.4
- 5.5
- 5.6
- 7.0
- hhvm
env:
- TWIG_EXT=no
- TWIG_EXT=yes
install:
# Composer is not available on PHP 5.2
- if [ ${TRAVIS_PHP_VERSION:0:3} != "5.2" ]; then travis_retry composer install; fi
before_script:
- if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && make install"; fi
- if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
- if [ ${TRAVIS_PHP_VERSION:0:3} == "5.2" ]; then sed -i.bak "s|vendor/autoload.php|test/bootstrap.php|" phpunit.xml.dist; fi
matrix:
fast_finish: true
exclude:
- php: hhvm
env: TWIG_EXT=yes
allow_failures:
- php: 7.0
env: TWIG_EXT=yes
Twig-1.23.1/CHANGELOG 0000664 0000000 0000000 00000111346 12616650102 0013727 0 ustar 00root root 0000000 0000000 * 1.23.1 (2015-XX-XX)
* fixed some exception messages which triggered PHP warnings
* fixed BC on Twig_Test_NodeTestCase
* 1.23.0 (2015-10-29)
* deprecated the possibility to override an extension by registering another one with the same name
* deprecated Twig_ExtensionInterface::getGlobals() (added Twig_Extension_GlobalsInterface for BC)
* deprecated Twig_ExtensionInterface::initRuntime() (added Twig_Extension_InitRuntimeInterface for BC)
* deprecated Twig_Environment::computeAlternatives()
* 1.22.3 (2015-10-13)
* fixed regression when using null as a cache strategy
* improved performance when checking template freshness
* fixed warnings when loaded templates do not exist
* fixed template class name generation to prevent possible collisions
* fixed logic for custom escapers to call them even on integers and null values
* changed template cache names to take into account the Twig C extension
* 1.22.2 (2015-09-22)
* fixed a race condition in template loading
* 1.22.1 (2015-09-15)
* fixed regression in template_from_string
* 1.22.0 (2015-09-13)
* made Twig_Test_IntegrationTestCase more flexible
* added an option to force PHP bytecode invalidation when writing a compiled template into the cache
* fixed the profiler duration for the root node
* changed template cache names to take into account enabled extensions
* deprecated Twig_Environment::clearCacheFiles(), Twig_Environment::getCacheFilename(),
Twig_Environment::writeCacheFile(), and Twig_Environment::getTemplateClassPrefix()
* added a way to override the filesystem template cache system
* added a way to get the original template source from Twig_Template
* 1.21.2 (2015-09-09)
* fixed variable names for the deprecation triggering code
* fixed escaping strategy detection based on filename
* added Traversable support for replace, merge, and sort
* deprecated support for character by character replacement for the "replace" filter
* 1.21.1 (2015-08-26)
* fixed regression when using the deprecated Twig_Test_* classes
* 1.21.0 (2015-08-24)
* added deprecation notices for deprecated features
* added a deprecation "framework" for filters/functions/tests and test fixtures
* 1.20.0 (2015-08-12)
* forbid access to the Twig environment from templates and internal parts of Twig_Template
* fixed limited RCEs when in sandbox mode
* deprecated Twig_Template::getEnvironment()
* deprecated the _self variable for usage outside of the from and import tags
* added Twig_BaseNodeVisitor to ease the compatibility of node visitors
between 1.x and 2.x
* 1.19.0 (2015-07-31)
* fixed wrong error message when including an undefined template in a child template
* added support for variadic filters, functions, and tests
* added support for extra positional arguments in macros
* added ignore_missing flag to the source function
* fixed batch filter with zero items
* deprecated Twig_Environment::clearTemplateCache()
* fixed sandbox disabling when using the include function
* 1.18.2 (2015-06-06)
* fixed template/line guessing in exceptions for nested templates
* optimized the number of inodes and the size of realpath cache when using the cache
* 1.18.1 (2015-04-19)
* fixed memory leaks in the C extension
* deprecated Twig_Loader_String
* fixed the slice filter when used with a SimpleXMLElement object
* fixed filesystem loader when trying to load non-files (like directories)
* 1.18.0 (2015-01-25)
* fixed some error messages where the line was wrong (unknown variables or argument names)
* added a new way to customize the main Module node (via empty nodes)
* added Twig_Environment::createTemplate() to create a template from a string
* added a profiler
* fixed filesystem loader cache when different file paths are used for the same template
* 1.17.0 (2015-01-14)
* added a 'filename' autoescaping strategy, which dynamically chooses the
autoescaping strategy for a template based on template file extension.
* 1.16.3 (2014-12-25)
* fixed regression for dynamic parent templates
* fixed cache management with statcache
* fixed a regression in the slice filter
* 1.16.2 (2014-10-17)
* fixed timezone on dates as strings
* fixed 2-words test names when a custom node class is not used
* fixed macros when using an argument named like a PHP super global (like GET or POST)
* fixed date_modify when working with DateTimeImmutable
* optimized for loops
* fixed multi-byte characters handling in the split filter
* fixed a regression in the in operator
* fixed a regression in the slice filter
* 1.16.1 (2014-10-10)
* improved error reporting in a sandboxed template
* fixed missing error file/line information under certain circumstances
* fixed wrong error line number in some error messages
* fixed the in operator to use strict comparisons
* sped up the slice filter
* fixed for mb function overload mb_substr acting different
* fixed the attribute() function when passing a variable for the arguments
* 1.16.0 (2014-07-05)
* changed url_encode to always encode according to RFC 3986
* fixed inheritance in a 'use'-hierarchy
* removed the __toString policy check when the sandbox is disabled
* fixed recursively calling blocks in templates with inheritance
* 1.15.1 (2014-02-13)
* fixed the conversion of the special '0000-00-00 00:00' date
* added an error message when trying to import an undefined block from a trait
* fixed a C extension crash when accessing defined but uninitialized property.
* 1.15.0 (2013-12-06)
* made ignoreStrictCheck in Template::getAttribute() works with __call() methods throwing BadMethodCallException
* added min and max functions
* added the round filter
* fixed a bug that prevented the optimizers to be enabled/disabled selectively
* fixed first and last filters for UTF-8 strings
* added a source function to include the content of a template without rendering it
* fixed the C extension sandbox behavior when get or set is prepend to method name
* 1.14.2 (2013-10-30)
* fixed error filename/line when an error occurs in an included file
* allowed operators that contain whitespaces to have more than one whitespace
* allowed tests to be made of 1 or 2 words (like "same as" or "divisible by")
* 1.14.1 (2013-10-15)
* made it possible to use named operators as variables
* fixed the possibility to have a variable named 'matches'
* added support for PHP 5.5 DateTimeInterface
* 1.14.0 (2013-10-03)
* fixed usage of the html_attr escaping strategy to avoid double-escaping with the html strategy
* added new operators: ends with, starts with, and matches
* fixed some compatibility issues with HHVM
* added a way to add custom escaping strategies
* fixed the C extension compilation on Windows
* fixed the batch filter when using a fill argument with an exact match of elements to batch
* fixed the filesystem loader cache when a template name exists in several namespaces
* fixed template_from_string when the template includes or extends other ones
* fixed a crash of the C extension on an edge case
* 1.13.2 (2013-08-03)
* fixed the error line number for an error occurs in and embedded template
* fixed crashes of the C extension on some edge cases
* 1.13.1 (2013-06-06)
* added the possibility to ignore the filesystem constructor argument in Twig_Loader_Filesystem
* fixed Twig_Loader_Chain::exists() for a loader which implements Twig_ExistsLoaderInterface
* adjusted backtrace call to reduce memory usage when an error occurs
* added support for object instances as the second argument of the constant test
* fixed the include function when used in an assignment
* 1.13.0 (2013-05-10)
* fixed getting a numeric-like item on a variable ('09' for instance)
* fixed getting a boolean or float key on an array, so it is consistent with PHP's array access:
`{{ array[false] }}` behaves the same as `echo $array[false];` (equals `$array[0]`)
* made the escape filter 20% faster for happy path (escaping string for html with UTF-8)
* changed ☃ to § in tests
* enforced usage of named arguments after positional ones
* 1.12.3 (2013-04-08)
* fixed a security issue in the filesystem loader where it was possible to include a template one
level above the configured path
* fixed fatal error that should be an exception when adding a filter/function/test too late
* added a batch filter
* added support for encoding an array as query string in the url_encode filter
* 1.12.2 (2013-02-09)
* fixed the timezone used by the date filter and function when the given date contains a timezone (like 2010-01-28T15:00:00+02:00)
* fixed globals when getGlobals is called early on
* added the first and last filter
* 1.12.1 (2013-01-15)
* added support for object instances as the second argument of the constant function
* relaxed globals management to avoid a BC break
* added support for {{ some_string[:2] }}
* 1.12.0 (2013-01-08)
* added verbatim as an alias for the raw tag to avoid confusion with the raw filter
* fixed registration of tests and functions as anonymous functions
* fixed globals management
* 1.12.0-RC1 (2012-12-29)
* added an include function (does the same as the include tag but in a more flexible way)
* added the ability to use any PHP callable to define filters, functions, and tests
* added a syntax error when using a loop variable that is not defined
* added the ability to set default values for macro arguments
* added support for named arguments for filters, tests, and functions
* moved filters/functions/tests syntax errors to the parser
* added support for extended ternary operator syntaxes
* 1.11.1 (2012-11-11)
* fixed debug info line numbering (was off by 2)
* fixed escaping when calling a macro inside another one (regression introduced in 1.9.1)
* optimized variable access on PHP 5.4
* fixed a crash of the C extension when an exception was thrown from a macro called without being imported (using _self.XXX)
* 1.11.0 (2012-11-07)
* fixed macro compilation when a variable name is a PHP reserved keyword
* changed the date filter behavior to always apply the default timezone, except if false is passed as the timezone
* fixed bitwise operator precedences
* added the template_from_string function
* fixed default timezone usage for the date function
* optimized the way Twig exceptions are managed (to make them faster)
* added Twig_ExistsLoaderInterface (implementing this interface in your loader make the chain loader much faster)
* 1.10.3 (2012-10-19)
* fixed wrong template location in some error messages
* reverted a BC break introduced in 1.10.2
* added a split filter
* 1.10.2 (2012-10-15)
* fixed macro calls on PHP 5.4
* 1.10.1 (2012-10-15)
* made a speed optimization to macro calls when imported via the "import" tag
* fixed C extension compilation on Windows
* fixed a segfault in the C extension when using DateTime objects
* 1.10.0 (2012-09-28)
* extracted functional tests framework to make it reusable for third-party extensions
* added namespaced templates support in Twig_Loader_Filesystem
* added Twig_Loader_Filesystem::prependPath()
* fixed an error when a token parser pass a closure as a test to the subparse() method
* 1.9.2 (2012-08-25)
* fixed the in operator for objects that contain circular references
* fixed the C extension when accessing a public property of an object implementing the \ArrayAccess interface
* 1.9.1 (2012-07-22)
* optimized macro calls when auto-escaping is on
* fixed wrong parent class for Twig_Function_Node
* made Twig_Loader_Chain more explicit about problems
* 1.9.0 (2012-07-13)
* made the parsing independent of the template loaders
* fixed exception trace when an error occurs when rendering a child template
* added escaping strategies for CSS, URL, and HTML attributes
* fixed nested embed tag calls
* added the date_modify filter
* 1.8.3 (2012-06-17)
* fixed paths in the filesystem loader when passing a path that ends with a slash or a backslash
* fixed escaping when a project defines a function named html or js
* fixed chmod mode to apply the umask correctly
* 1.8.2 (2012-05-30)
* added the abs filter
* fixed a regression when using a number in template attributes
* fixed compiler when mbstring.func_overload is set to 2
* fixed DateTimeZone support in date filter
* 1.8.1 (2012-05-17)
* fixed a regression when dealing with SimpleXMLElement instances in templates
* fixed "is_safe" value for the "dump" function when "html_errors" is not defined in php.ini
* switched to use mbstring whenever possible instead of iconv (you might need to update your encoding as mbstring and iconv encoding names sometimes differ)
* 1.8.0 (2012-05-08)
* enforced interface when adding tests, filters, functions, and node visitors from extensions
* fixed a side-effect of the date filter where the timezone might be changed
* simplified usage of the autoescape tag; the only (optional) argument is now the escaping strategy or false (with a BC layer)
* added a way to dynamically change the auto-escaping strategy according to the template "filename"
* changed the autoescape option to also accept a supported escaping strategy (for BC, true is equivalent to html)
* added an embed tag
* 1.7.0 (2012-04-24)
* fixed a PHP warning when using CIFS
* fixed template line number in some exceptions
* added an iterable test
* added an error when defining two blocks with the same name in a template
* added the preserves_safety option for filters
* fixed a PHP notice when trying to access a key on a non-object/array variable
* enhanced error reporting when the template file is an instance of SplFileInfo
* added Twig_Environment::mergeGlobals()
* added compilation checks to avoid misuses of the sandbox tag
* fixed filesystem loader freshness logic for high traffic websites
* fixed random function when charset is null
* 1.6.5 (2012-04-11)
* fixed a regression when a template only extends another one without defining any blocks
* 1.6.4 (2012-04-02)
* fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3
* fixed performance when compiling large files
* optimized parent template creation when the template does not use dynamic inheritance
* 1.6.3 (2012-03-22)
* fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension
* fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot
* made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate
* 1.6.2 (2012-03-18)
* fixed sandbox mode when used with inheritance
* added preserveKeys support for the slice filter
* fixed the date filter when a DateTime instance is passed with a specific timezone
* added a trim filter
* 1.6.1 (2012-02-29)
* fixed Twig C extension
* removed the creation of Twig_Markup instances when not needed
* added a way to set the default global timezone for dates
* fixed the slice filter on strings when the length is not specified
* fixed the creation of the cache directory in case of a race condition
* 1.6.0 (2012-02-04)
* fixed raw blocks when used with the whitespace trim option
* made a speed optimization to macro calls when imported via the "from" tag
* fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added
* fixed the attribute function when passing arguments
* added slice notation support for the [] operator (syntactic sugar for the slice operator)
* added a slice filter
* added string support for the reverse filter
* fixed the empty test and the length filter for Twig_Markup instances
* added a date function to ease date comparison
* fixed unary operators precedence
* added recursive parsing support in the parser
* added string and integer handling for the random function
* 1.5.1 (2012-01-05)
* fixed a regression when parsing strings
* 1.5.0 (2012-01-04)
* added Traversable objects support for the join filter
* 1.5.0-RC2 (2011-12-30)
* added a way to set the default global date interval format
* fixed the date filter for DateInterval instances (setTimezone() does not exist for them)
* refactored Twig_Template::display() to ease its extension
* added a number_format filter
* 1.5.0-RC1 (2011-12-26)
* removed the need to quote hash keys
* allowed hash keys to be any expression
* added a do tag
* added a flush tag
* added support for dynamically named filters and functions
* added a dump function to help debugging templates
* added a nl2br filter
* added a random function
* added a way to change the default format for the date filter
* fixed the lexer when an operator ending with a letter ends a line
* added string interpolation support
* enhanced exceptions for unknown filters, functions, tests, and tags
* 1.4.0 (2011-12-07)
* fixed lexer when using big numbers (> PHP_INT_MAX)
* added missing preserveKeys argument to the reverse filter
* fixed macros containing filter tag calls
* 1.4.0-RC2 (2011-11-27)
* removed usage of Reflection in Twig_Template::getAttribute()
* added a C extension that can optionally replace Twig_Template::getAttribute()
* added negative timestamp support to the date filter
* 1.4.0-RC1 (2011-11-20)
* optimized variable access when using PHP 5.4
* changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby
* added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders
* added Twig_Function_Node to allow more complex functions to have their own Node class
* added Twig_Filter_Node to allow more complex filters to have their own Node class
* added Twig_Test_Node to allow more complex tests to have their own Node class
* added a better error message when a template is empty but contain a BOM
* fixed "in" operator for empty strings
* fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option)
* changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order)
* added Twig_Environment::display()
* made the escape filter smarter when the encoding is not supported by PHP
* added a convert_encoding filter
* moved all node manipulations outside the compile() Node method
* made several speed optimizations
* 1.3.0 (2011-10-08)
no changes
* 1.3.0-RC1 (2011-10-04)
* added an optimization for the parent() function
* added cache reloading when auto_reload is true and an extension has been modified
* added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup)
* allowed empty templates to be used as traits
* added traits support for the "parent" function
* 1.2.0 (2011-09-13)
no changes
* 1.2.0-RC1 (2011-09-10)
* enhanced the exception when a tag remains unclosed
* added support for empty Countable objects for the "empty" test
* fixed algorithm that determines if a template using inheritance is valid (no output between block definitions)
* added better support for encoding problems when escaping a string (available as of PHP 5.4)
* added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %})
* added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %})
* added support for bitwise operators in expressions
* added the "attribute" function to allow getting dynamic attributes on variables
* added Twig_Loader_Chain
* added Twig_Loader_Array::setTemplate()
* added an optimization for the set tag when used to capture a large chunk of static text
* changed name regex to match PHP one "[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" (works for blocks, tags, functions, filters, and macros)
* removed the possibility to use the "extends" tag from a block
* added "if" modifier support to "for" loops
* 1.1.2 (2011-07-30)
* fixed json_encode filter on PHP 5.2
* fixed regression introduced in 1.1.1 ({{ block(foo|lower) }})
* fixed inheritance when using conditional parents
* fixed compilation of templates when the body of a child template is not empty
* fixed output when a macro throws an exception
* fixed a parsing problem when a large chunk of text is enclosed in a comment tag
* added PHPDoc for all Token parsers and Core extension functions
* 1.1.1 (2011-07-17)
* added a performance optimization in the Optimizer (also helps to lower the number of nested level calls)
* made some performance improvement for some edge cases
* 1.1.0 (2011-06-28)
* fixed json_encode filter
* 1.1.0-RC3 (2011-06-24)
* fixed method case-sensitivity when using the sandbox mode
* added timezone support for the date filter
* fixed possible security problems with NUL bytes
* 1.1.0-RC2 (2011-06-16)
* added an exception when the template passed to "use" is not a string
* made 'a.b is defined' not throw an exception if a is not defined (in strict mode)
* added {% line \d+ %} directive
* 1.1.0-RC1 (2011-05-28)
Flush your cache after upgrading.
* fixed date filter when using a timestamp
* fixed the defined test for some cases
* fixed a parsing problem when a large chunk of text is enclosed in a raw tag
* added support for horizontal reuse of template blocks (see docs for more information)
* added whitespace control modifier to all tags (see docs for more information)
* added null as an alias for none (the null test is also an alias for the none test now)
* made TRUE, FALSE, NONE equivalent to their lowercase counterparts
* wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line
* moved display() method to Twig_Template (generated templates should now use doDisplay() instead)
* 1.0.0 (2011-03-27)
* fixed output when using mbstring
* fixed duplicate call of methods when using the sandbox
* made the charset configurable for the escape filter
* 1.0.0-RC2 (2011-02-21)
* changed the way {% set %} works when capturing (the content is now marked as safe)
* added support for macro name in the endmacro tag
* make Twig_Error compatible with PHP 5.3.0 >
* fixed an infinite loop on some Windows configurations
* fixed the "length" filter for numbers
* fixed Template::getAttribute() as properties in PHP are case sensitive
* removed coupling between Twig_Node and Twig_Template
* fixed the ternary operator precedence rule
* 1.0.0-RC1 (2011-01-09)
Backward incompatibilities:
* the "items" filter, which has been deprecated for quite a long time now, has been removed
* the "range" filter has been converted to a function: 0|range(10) -> range(0, 10)
* the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }}
* the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }}
* the "for" tag does not support "joined by" anymore
* the "autoescape" first argument is now "true"/"false" (instead of "on"/"off")
* the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %})
* the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %})
* removed the grammar and simple token parser (moved to the Twig Extensions repository)
Changes:
* added "needs_context" option for filters and functions (the context is then passed as a first argument)
* added global variables support
* made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode)
* added the "from" tag to import macros as functions
* added support for functions (a function is just syntactic sugar for a getAttribute() call)
* made macros callable when sandbox mode is enabled
* added an exception when a macro uses a reserved name
* the "default" filter now uses the "empty" test instead of just checking for null
* added the "empty" test
* 0.9.10 (2010-12-16)
Backward incompatibilities:
* The Escaper extension is enabled by default, which means that all displayed
variables are now automatically escaped. You can revert to the previous
behavior by removing the extension via $env->removeExtension('escaper')
or just set the 'autoescape' option to 'false'.
* removed the "without loop" attribute for the "for" tag (not needed anymore
as the Optimizer take care of that for most cases)
* arrays and hashes have now a different syntax
* arrays keep the same syntax with square brackets: [1, 2]
* hashes now use curly braces (["a": "b"] should now be written as {"a": "b"})
* support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1})
* the i18n extension is now part of the Twig Extensions repository
Changes:
* added the merge filter
* removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead
* fixed usage of operators as method names (like is, in, and not)
* changed the order of execution for node visitors
* fixed default() filter behavior when used with strict_variables set to on
* fixed filesystem loader compatibility with PHAR files
* enhanced error messages when an unexpected token is parsed in an expression
* fixed filename not being added to syntax error messages
* added the autoescape option to enable/disable autoescaping
* removed the newline after a comment (mimics PHP behavior)
* added a syntax error exception when parent block is used on a template that does not extend another one
* made the Escaper extension enabled by default
* fixed sandbox extension when used with auto output escaping
* fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved)
* added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters)
* added priority to node visitors
* 0.9.9 (2010-11-28)
Backward incompatibilities:
* the self special variable has been renamed to _self
* the odd and even filters are now tests:
{{ foo|odd }} must now be written {{ foo is odd }}
* the "safe" filter has been renamed to "raw"
* in Node classes,
sub-nodes are now accessed via getNode() (instead of property access)
attributes via getAttribute() (instead of array access)
* the urlencode filter had been renamed to url_encode
* the include tag now merges the passed variables with the current context by default
(the old behavior is still possible by adding the "only" keyword)
* moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime)
* removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead)
* the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }})
Changes:
* added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template
* changed trans tag to accept any variable for the plural count
* fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements)
* added the ** (power) operator
* changed the algorithm used for parsing expressions
* added the spaceless tag
* removed trim_blocks option
* added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar())
* changed all exceptions to extend Twig_Error
* fixed unary expressions ({{ not(1 or 0) }})
* fixed child templates (with an extend tag) that uses one or more imports
* added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }})
* escaping has been rewritten
* the implementation of template inheritance has been rewritten
(blocks can now be called individually and still work with inheritance)
* fixed error handling for if tag when a syntax error occurs within a subparse process
* added a way to implement custom logic for resolving token parsers given a tag name
* fixed js escaper to be stricter (now uses a whilelist-based js escaper)
* added the following filers: "constant", "trans", "replace", "json_encode"
* added a "constant" test
* fixed objects with __toString() not being autoescaped
* fixed subscript expressions when calling __call() (methods now keep the case)
* added "test" feature (accessible via the "is" operator)
* removed the debug tag (should be done in an extension)
* fixed trans tag when no vars are used in plural form
* fixed race condition when writing template cache
* added the special _charset variable to reference the current charset
* added the special _context variable to reference the current context
* renamed self to _self (to avoid conflict)
* fixed Twig_Template::getAttribute() for protected properties
* 0.9.8 (2010-06-28)
Backward incompatibilities:
* the trans tag plural count is now attached to the plural tag:
old: `{% trans count %}...{% plural %}...{% endtrans %}`
new: `{% trans %}...{% plural count %}...{% endtrans %}`
* added a way to translate strings coming from a variable ({% trans var %})
* fixed trans tag when used with the Escaper extension
* fixed default cache umask
* removed Twig_Template instances from the debug tag output
* fixed objects with __isset() defined
* fixed set tag when used with a capture
* fixed type hinting for Twig_Environment::addFilter() method
* 0.9.7 (2010-06-12)
Backward incompatibilities:
* changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %})
* removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
* refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
* added self as a special variable that refers to the current template (useful for importing macros from the current template)
* added Twig_Template instance support to the include tag
* added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
* added a grammar sub-framework to ease the creation of custom tags
* fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface)
* removed the Twig_Resource::resolveMissingFilter() method
* fixed the filter tag which did not apply filtering to included files
* added a bunch of unit tests
* added a bunch of phpdoc
* added a sandbox tag in the sandbox extension
* changed the date filter to support any date format supported by DateTime
* added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default)
* added the lexer, parser, and compiler as arguments to the Twig_Environment constructor
* changed the cache option to only accepts an explicit path to a cache directory or false
* added a way to add token parsers, filters, and visitors without creating an extension
* added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
* changed the generated code to match the new coding standards
* fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
* added an exception when a child template has a non-empty body (as it is always ignored when rendering)
* 0.9.6 (2010-05-12)
* fixed variables defined outside a loop and for which the value changes in a for loop
* fixed the test suite for PHP 5.2 and older versions of PHPUnit
* added support for __call() in expression resolution
* fixed node visiting for macros (macros are now visited by visitors as any other node)
* fixed nested block definitions with a parent call (rarely useful but nonetheless supported now)
* added the cycle filter
* fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII
* added a long-syntax for the set tag ({% set foo %}...{% endset %})
* unit tests are now powered by PHPUnit
* added support for gettext via the `i18n` extension
* fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values
* added a more useful exception if an if tag is not closed properly
* added support for escaping strategy in the autoescape tag
* fixed lexer when a template has a big chunk of text between/in a block
* 0.9.5 (2010-01-20)
As for any new release, don't forget to remove all cached templates after
upgrading.
If you have defined custom filters, you MUST upgrade them for this release. To
upgrade, replace "array" with "new Twig_Filter_Function", and replace the
environment constant by the "needs_environment" option:
// before
'even' => array('twig_is_even_filter', false),
'escape' => array('twig_escape_filter', true),
// after
'even' => new Twig_Filter_Function('twig_is_even_filter'),
'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
If you have created NodeTransformer classes, you will need to upgrade them to
the new interface (please note that the interface is not yet considered
stable).
* fixed list nodes that did not extend the Twig_NodeListInterface
* added the "without loop" option to the for tag (it disables the generation of the loop variable)
* refactored node transformers to node visitors
* fixed automatic-escaping for blocks
* added a way to specify variables to pass to an included template
* changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
* improved the filter system to allow object methods to be used as filters
* changed the Array and String loaders to actually make use of the cache mechanism
* included the default filter function definitions in the extension class files directly (Core, Escaper)
* added the // operator (like the floor() PHP function)
* added the .. operator (as a syntactic sugar for the range filter when the step is 1)
* added the in operator (as a syntactic sugar for the in filter)
* added the following filters in the Core extension: in, range
* added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
* enhanced some error messages to provide better feedback in case of parsing errors
* 0.9.4 (2009-12-02)
If you have custom loaders, you MUST upgrade them for this release: The
Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
been changed (see the source code for more information or the documentation).
* added support for DateTime instances for the date filter
* fixed loop.last when the array only has one item
* made it possible to insert newlines in tag and variable blocks
* fixed a bug when a literal '\n' were present in a template text
* fixed bug when the filename of a template contains */
* refactored loaders
* 0.9.3 (2009-11-11)
This release is NOT backward compatible with the previous releases.
The loaders do not take the cache and autoReload arguments anymore. Instead,
the Twig_Environment class has two new options: cache and auto_reload.
Upgrading your code means changing this kind of code:
$loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true);
$twig = new Twig_Environment($loader);
to something like this:
$loader = new Twig_Loader_Filesystem('/path/to/templates');
$twig = new Twig_Environment($loader, array(
'cache' => '/path/to/compilation_cache',
'auto_reload' => true,
));
* deprecated the "items" filter as it is not needed anymore
* made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader
* optimized template loading speed
* removed output when an error occurs in a template and render() is used
* made major speed improvements for loops (up to 300% on even the smallest loops)
* added properties as part of the sandbox mode
* added public properties support (obj.item can now be the item property on the obj object)
* extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} )
* fixed bug when \ was used in HTML
* 0.9.2 (2009-10-29)
* made some speed optimizations
* changed the cache extension to .php
* added a js escaping strategy
* added support for short block tag
* changed the filter tag to allow chained filters
* made lexer more flexible as you can now change the default delimiters
* added set tag
* changed default directory permission when cache dir does not exist (more secure)
* added macro support
* changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance
* made Twig_Autoloader::autoload() a static method
* avoid writing template file if an error occurs
* added $ escaping when outputting raw strings
* enhanced some error messages to ease debugging
* fixed empty cache files when the template contains an error
* 0.9.1 (2009-10-14)
* fixed a bug in PHP 5.2.6
* fixed numbers with one than one decimal
* added support for method calls with arguments ({{ foo.bar('a', 43) }})
* made small speed optimizations
* made minor tweaks to allow better extensibility and flexibility
* 0.9.0 (2009-10-12)
* Initial release
Twig-1.23.1/LICENSE 0000664 0000000 0000000 00000002731 12616650102 0013517 0 ustar 00root root 0000000 0000000 Copyright (c) 2009-2014 by the Twig Team.
Some rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following
disclaimer in the documentation and/or other materials provided
with the distribution.
* The names of the contributors may not be used to endorse or
promote products derived from this software without specific
prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Twig-1.23.1/README.rst 0000664 0000000 0000000 00000000746 12616650102 0014205 0 ustar 00root root 0000000 0000000 Twig, the flexible, fast, and secure template language for PHP
==============================================================
Twig is a template language for PHP, released under the new BSD license (code
and documentation).
Twig uses a syntax similar to the Django and Jinja template languages which
inspired the Twig runtime environment.
More Information
----------------
Read the `documentation`_ for more information.
.. _documentation: http://twig.sensiolabs.org/documentation
Twig-1.23.1/composer.json 0000664 0000000 0000000 00000002251 12616650102 0015231 0 ustar 00root root 0000000 0000000 {
"name": "twig/twig",
"type": "library",
"description": "Twig, the flexible, fast, and secure template language for PHP",
"keywords": ["templating"],
"homepage": "http://twig.sensiolabs.org",
"license": "BSD-3-Clause",
"authors": [
{
"name": "Fabien Potencier",
"email": "fabien@symfony.com",
"homepage": "http://fabien.potencier.org",
"role": "Lead Developer"
},
{
"name": "Twig Team",
"homepage": "http://twig.sensiolabs.org/contributors",
"role": "Contributors"
},
{
"name": "Armin Ronacher",
"email": "armin.ronacher@active-4.com",
"role": "Project Founder"
}
],
"support": {
"forum": "https://groups.google.com/forum/#!forum/twig-users"
},
"require": {
"php": ">=5.2.7"
},
"require-dev": {
"symfony/phpunit-bridge": "~2.7",
"symfony/debug": "~2.7"
},
"autoload": {
"psr-0" : {
"Twig_" : "lib/"
}
},
"extra": {
"branch-alias": {
"dev-master": "1.23-dev"
}
}
}
Twig-1.23.1/doc/ 0000775 0000000 0000000 00000000000 12616650102 0013254 5 ustar 00root root 0000000 0000000 Twig-1.23.1/doc/advanced.rst 0000664 0000000 0000000 00000064477 12616650102 0015575 0 ustar 00root root 0000000 0000000 Extending Twig
==============
.. caution::
This section describes how to extend Twig as of **Twig 1.12**. If you are
using an older version, read the :doc:`legacy` chapter
instead.
Twig can be extended in many ways; you can add extra tags, filters, tests,
operators, global variables, and functions. You can even extend the parser
itself with node visitors.
.. note::
The first section of this chapter describes how to extend Twig easily. If
you want to reuse your changes in different projects or if you want to
share them with others, you should then create an extension as described
in the following section.
.. caution::
When extending Twig without creating an extension, Twig won't be able to
recompile your templates when the PHP code is updated. To see your changes
in real-time, either disable template caching or package your code into an
extension (see the next section of this chapter).
Before extending Twig, you must understand the differences between all the
different possible extension points and when to use them.
First, remember that Twig has two main language constructs:
* ``{{ }}``: used to print the result of an expression evaluation;
* ``{% %}``: used to execute statements.
To understand why Twig exposes so many extension points, let's see how to
implement a *Lorem ipsum* generator (it needs to know the number of words to
generate).
You can use a ``lipsum`` *tag*:
.. code-block:: jinja
{% lipsum 40 %}
That works, but using a tag for ``lipsum`` is not a good idea for at least
three main reasons:
* ``lipsum`` is not a language construct;
* The tag outputs something;
* The tag is not flexible as you cannot use it in an expression:
.. code-block:: jinja
{{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
In fact, you rarely need to create tags; and that's good news because tags are
the most complex extension point of Twig.
Now, let's use a ``lipsum`` *filter*:
.. code-block:: jinja
{{ 40|lipsum }}
Again, it works, but it looks weird. A filter transforms the passed value to
something else but here we use the value to indicate the number of words to
generate (so, ``40`` is an argument of the filter, not the value we want to
transform).
Next, let's use a ``lipsum`` *function*:
.. code-block:: jinja
{{ lipsum(40) }}
Here we go. For this specific example, the creation of a function is the
extension point to use. And you can use it anywhere an expression is accepted:
.. code-block:: jinja
{{ 'some text' ~ lipsum(40) ~ 'some more text' }}
{% set lipsum = lipsum(40) %}
Last but not the least, you can also use a *global* object with a method able
to generate lorem ipsum text:
.. code-block:: jinja
{{ text.lipsum(40) }}
As a rule of thumb, use functions for frequently used features and global
objects for everything else.
Keep in mind the following when you want to extend Twig:
========== ========================== ========== =========================
What? Implementation difficulty? How often? When?
========== ========================== ========== =========================
*macro* trivial frequent Content generation
*global* trivial frequent Helper object
*function* trivial frequent Content generation
*filter* trivial frequent Value transformation
*tag* complex rare DSL language construct
*test* trivial rare Boolean decision
*operator* trivial rare Values transformation
========== ========================== ========== =========================
Globals
-------
A global variable is like any other template variable, except that it's
available in all templates and macros::
$twig = new Twig_Environment($loader);
$twig->addGlobal('text', new Text());
You can then use the ``text`` variable anywhere in a template:
.. code-block:: jinja
{{ text.lipsum(40) }}
Filters
-------
Creating a filter is as simple as associating a name with a PHP callable::
// an anonymous function
$filter = new Twig_SimpleFilter('rot13', function ($string) {
return str_rot13($string);
});
// or a simple PHP function
$filter = new Twig_SimpleFilter('rot13', 'str_rot13');
// or a class method
$filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
of the filter you will use in templates and the second one is the PHP callable
to associate with it.
Then, add the filter to your Twig environment::
$twig = new Twig_Environment($loader);
$twig->addFilter($filter);
And here is how to use it in a template:
.. code-block:: jinja
{{ 'Twig'|rot13 }}
{# will output Gjvt #}
When called by Twig, the PHP callable receives the left side of the filter
(before the pipe ``|``) as the first argument and the extra arguments passed
to the filter (within parentheses ``()``) as extra arguments.
For instance, the following code:
.. code-block:: jinja
{{ 'TWIG'|lower }}
{{ now|date('d/m/Y') }}
is compiled to something like the following::
The ``Twig_SimpleFilter`` class takes an array of options as its last
argument::
$filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
Environment-aware Filters
~~~~~~~~~~~~~~~~~~~~~~~~~
If you want to access the current environment instance in your filter, set the
``needs_environment`` option to ``true``; Twig will pass the current
environment as the first argument to the filter call::
$filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $string) {
// get the current charset for instance
$charset = $env->getCharset();
return str_rot13($string);
}, array('needs_environment' => true));
Context-aware Filters
~~~~~~~~~~~~~~~~~~~~~
If you want to access the current context in your filter, set the
``needs_context`` option to ``true``; Twig will pass the current context as
the first argument to the filter call (or the second one if
``needs_environment`` is also set to ``true``)::
$filter = new Twig_SimpleFilter('rot13', function ($context, $string) {
// ...
}, array('needs_context' => true));
$filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $context, $string) {
// ...
}, array('needs_context' => true, 'needs_environment' => true));
Automatic Escaping
~~~~~~~~~~~~~~~~~~
If automatic escaping is enabled, the output of the filter may be escaped
before printing. If your filter acts as an escaper (or explicitly outputs HTML
or JavaScript code), you will want the raw output to be printed. In such a
case, set the ``is_safe`` option::
$filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
Some filters may need to work on input that is already escaped or safe, for
example when adding (safe) HTML tags to originally unsafe output. In such a
case, set the ``pre_escape`` option to escape the input data before it is run
through your filter::
$filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
Variadic Filters
~~~~~~~~~~~~~~~~
.. versionadded:: 1.19
Support for variadic filters was added in Twig 1.19.
When a filter should accept an arbitrary number of arguments, set the
``is_variadic`` option to ``true``; Twig will pass the extra arguments as the
last argument to the filter call as an array::
$filter = new Twig_SimpleFilter('thumbnail', function ($file, array $options = array()) {
// ...
}, array('is_variadic' => true));
Be warned that named arguments passed to a variadic filter cannot be checked
for validity as they will automatically end up in the option array.
Dynamic Filters
~~~~~~~~~~~~~~~
A filter name containing the special ``*`` character is a dynamic filter as
the ``*`` can be any string::
$filter = new Twig_SimpleFilter('*_path', function ($name, $arguments) {
// ...
});
The following filters will be matched by the above defined dynamic filter:
* ``product_path``
* ``category_path``
A dynamic filter can define more than one dynamic parts::
$filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
// ...
});
The filter will receive all dynamic part values before the normal filter
arguments, but after the environment and the context. For instance, a call to
``'foo'|a_path_b()`` will result in the following arguments to be passed to
the filter: ``('a', 'b', 'foo')``.
Deprecated Filters
~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.21
Support for deprecated filters was added in Twig 1.21.
You can mark a filter as being deprecated by setting the ``deprecated`` option
to ``true``. You can also give an alternative filter that replaces the
deprecated one when that makes sense::
$filter = new Twig_SimpleFilter('obsolete', function () {
// ...
}, array('deprecated' => true, 'alternative' => 'new_one'));
When a filter is deprecated, Twig emits a deprecation notice when compiling a
template using it. See :ref:`deprecation-notices` for more information.
Functions
---------
Functions are defined in the exact same way as filters, but you need to create
an instance of ``Twig_SimpleFunction``::
$twig = new Twig_Environment($loader);
$function = new Twig_SimpleFunction('function_name', function () {
// ...
});
$twig->addFunction($function);
Functions support the same features as filters, except for the ``pre_escape``
and ``preserves_safety`` options.
Tests
-----
Tests are defined in the exact same way as filters and functions, but you need
to create an instance of ``Twig_SimpleTest``::
$twig = new Twig_Environment($loader);
$test = new Twig_SimpleTest('test_name', function () {
// ...
});
$twig->addTest($test);
Tests allow you to create custom application specific logic for evaluating
boolean conditions. As a simple example, let's create a Twig test that checks if
objects are 'red'::
$twig = new Twig_Environment($loader);
$test = new Twig_SimpleTest('red', function ($value) {
if (isset($value->color) && $value->color == 'red') {
return true;
}
if (isset($value->paint) && $value->paint == 'red') {
return true;
}
return false;
});
$twig->addTest($test);
Test functions should always return true/false.
When creating tests you can use the ``node_class`` option to provide custom test
compilation. This is useful if your test can be compiled into PHP primitives.
This is used by many of the tests built into Twig::
$twig = new Twig_Environment($loader);
$test = new Twig_SimpleTest(
'odd',
null,
array('node_class' => 'Twig_Node_Expression_Test_Odd'));
$twig->addTest($test);
class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(')
->subcompile($this->getNode('node'))
->raw(' % 2 == 1')
->raw(')')
;
}
}
The above example shows how you can create tests that use a node class. The
node class has access to one sub-node called 'node'. This sub-node contains the
value that is being tested. When the ``odd`` filter is used in code such as:
.. code-block:: jinja
{% if my_value is odd %}
The ``node`` sub-node will contain an expression of ``my_value``. Node-based
tests also have access to the ``arguments`` node. This node will contain the
various other arguments that have been provided to your test.
If you want to pass a variable number of positional or named arguments to the
test, set the ``is_variadic`` option to ``true``. Tests also support dynamic
name feature as filters and functions.
Tags
----
One of the most exciting features of a template engine like Twig is the
possibility to define new language constructs. This is also the most complex
feature as you need to understand how Twig's internals work.
Let's create a simple ``set`` tag that allows the definition of simple
variables from within a template. The tag can be used like follows:
.. code-block:: jinja
{% set name = "value" %}
{{ name }}
{# should output value #}
.. note::
The ``set`` tag is part of the Core extension and as such is always
available. The built-in version is slightly more powerful and supports
multiple assignments by default (cf. the template designers chapter for
more information).
Three steps are needed to define a new tag:
* Defining a Token Parser class (responsible for parsing the template code);
* Defining a Node class (responsible for converting the parsed code to PHP);
* Registering the tag.
Registering a new tag
~~~~~~~~~~~~~~~~~~~~~
Adding a tag is as simple as calling the ``addTokenParser`` method on the
``Twig_Environment`` instance::
$twig = new Twig_Environment($loader);
$twig->addTokenParser(new Project_Set_TokenParser());
Defining a Token Parser
~~~~~~~~~~~~~~~~~~~~~~~
Now, let's see the actual code of this class::
class Project_Set_TokenParser extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$parser = $this->parser;
$stream = $parser->getStream();
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
$stream->expect(Twig_Token::OPERATOR_TYPE, '=');
$value = $parser->getExpressionParser()->parseExpression();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'set';
}
}
The ``getTag()`` method must return the tag we want to parse, here ``set``.
The ``parse()`` method is invoked whenever the parser encounters a ``set``
tag. It should return a ``Twig_Node`` instance that represents the node (the
``Project_Set_Node`` calls creating is explained in the next section).
The parsing process is simplified thanks to a bunch of methods you can call
from the token stream (``$this->parser->getStream()``):
* ``getCurrent()``: Gets the current token in the stream.
* ``next()``: Moves to the next token in the stream, *but returns the old one*.
* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
the current token is of a particular type or value (or both). The value may be an
array of several possible values.
* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
type/value a syntax error is thrown. Otherwise, if the type and value are correct,
the token is returned and the stream moves to the next token.
* ``look()``: Looks a the next token without consuming it.
Parsing expressions is done by calling the ``parseExpression()`` like we did for
the ``set`` tag.
.. tip::
Reading the existing ``TokenParser`` classes is the best way to learn all
the nitty-gritty details of the parsing process.
Defining a Node
~~~~~~~~~~~~~~~
The ``Project_Set_Node`` class itself is rather simple::
class Project_Set_Node extends Twig_Node
{
public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
{
parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('$context[\''.$this->getAttribute('name').'\'] = ')
->subcompile($this->getNode('value'))
->raw(";\n")
;
}
}
The compiler implements a fluid interface and provides methods that helps the
developer generate beautiful and readable PHP code:
* ``subcompile()``: Compiles a node.
* ``raw()``: Writes the given string as is.
* ``write()``: Writes the given string by adding indentation at the beginning
of each line.
* ``string()``: Writes a quoted string.
* ``repr()``: Writes a PHP representation of a given value (see
``Twig_Node_For`` for a usage example).
* ``addDebugInfo()``: Adds the line of the original template file related to
the current node as a comment.
* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
usage example).
* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
usage example).
.. _creating_extensions:
Creating an Extension
---------------------
The main motivation for writing an extension is to move often used code into a
reusable class like adding support for internationalization. An extension can
define tags, filters, tests, operators, global variables, functions, and node
visitors.
Creating an extension also makes for a better separation of code that is
executed at compilation time and code needed at runtime. As such, it makes
your code faster.
Most of the time, it is useful to create a single extension for your project,
to host all the specific tags and filters you want to add to Twig.
.. tip::
When packaging your code into an extension, Twig is smart enough to
recompile your templates whenever you make a change to it (when
``auto_reload`` is enabled).
.. note::
Before writing your own extensions, have a look at the Twig official
extension repository: http://github.com/twigphp/Twig-extensions.
An extension is a class that implements the following interface::
interface Twig_ExtensionInterface
{
/**
* Initializes the runtime environment.
*
* This is where you can load some file that contains filter functions for instance.
*
* @param Twig_Environment $environment The current Twig_Environment instance
*
* @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterace instead
*/
function initRuntime(Twig_Environment $environment);
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
*/
function getTokenParsers();
/**
* Returns the node visitor instances to add to the existing list.
*
* @return array An array of Twig_NodeVisitorInterface instances
*/
function getNodeVisitors();
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
*/
function getFilters();
/**
* Returns a list of tests to add to the existing list.
*
* @return array An array of tests
*/
function getTests();
/**
* Returns a list of functions to add to the existing list.
*
* @return array An array of functions
*/
function getFunctions();
/**
* Returns a list of operators to add to the existing list.
*
* @return array An array of operators
*/
function getOperators();
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*
* @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead
*/
function getGlobals();
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
function getName();
}
To keep your extension class clean and lean, it can inherit from the built-in
``Twig_Extension`` class instead of implementing the whole interface. That
way, you just need to implement the ``getName()`` method as the
``Twig_Extension`` provides empty implementations for all other methods.
The ``getName()`` method must return a unique identifier for your extension.
Now, with this information in mind, let's create the most basic extension
possible::
class Project_Twig_Extension extends Twig_Extension
{
public function getName()
{
return 'project';
}
}
.. note::
Of course, this extension does nothing for now. We will customize it in
the next sections.
Twig does not care where you save your extension on the filesystem, as all
extensions must be registered explicitly to be available in your templates.
You can register an extension by using the ``addExtension()`` method on your
main ``Environment`` object::
$twig = new Twig_Environment($loader);
$twig->addExtension(new Project_Twig_Extension());
.. tip::
The bundled extensions are great examples of how extensions work.
Globals
~~~~~~~
Global variables can be registered in an extension via the ``getGlobals()``
method::
class Project_Twig_Extension extends Twig_Extension
{
public function getGlobals()
{
return array(
'text' => new Text(),
);
}
// ...
}
Functions
~~~~~~~~~
Functions can be registered in an extension via the ``getFunctions()``
method::
class Project_Twig_Extension extends Twig_Extension
{
public function getFunctions()
{
return array(
new Twig_SimpleFunction('lipsum', 'generate_lipsum'),
);
}
// ...
}
Filters
~~~~~~~
To add a filter to an extension, you need to override the ``getFilters()``
method. This method must return an array of filters to add to the Twig
environment::
class Project_Twig_Extension extends Twig_Extension
{
public function getFilters()
{
return array(
new Twig_SimpleFilter('rot13', 'str_rot13'),
);
}
// ...
}
Tags
~~~~
Adding a tag in an extension can be done by overriding the
``getTokenParsers()`` method. This method must return an array of tags to add
to the Twig environment::
class Project_Twig_Extension extends Twig_Extension
{
public function getTokenParsers()
{
return array(new Project_Set_TokenParser());
}
// ...
}
In the above code, we have added a single new tag, defined by the
``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
responsible for parsing the tag and compiling it to PHP.
Operators
~~~~~~~~~
The ``getOperators()`` methods lets you add new operators. Here is how to add
``!``, ``||``, and ``&&`` operators::
class Project_Twig_Extension extends Twig_Extension
{
public function getOperators()
{
return array(
array(
'!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
),
array(
'||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
),
);
}
// ...
}
Tests
~~~~~
The ``getTests()`` method lets you add new test functions::
class Project_Twig_Extension extends Twig_Extension
{
public function getTests()
{
return array(
new Twig_SimpleTest('even', 'twig_test_even'),
);
}
// ...
}
Overloading
-----------
To overload an already defined filter, test, operator, global variable, or
function, re-define it in an extension and register it **as late as
possible** (order matters)::
class MyCoreExtension extends Twig_Extension
{
public function getFilters()
{
return array(
new Twig_SimpleFilter('date', array($this, 'dateFilter')),
);
}
public function dateFilter($timestamp, $format = 'F j, Y H:i')
{
// do something different from the built-in date filter
}
public function getName()
{
return 'project';
}
}
$twig = new Twig_Environment($loader);
$twig->addExtension(new MyCoreExtension());
Here, we have overloaded the built-in ``date`` filter with a custom one.
If you do the same on the Twig_Environment itself, beware that it takes
precedence over any other registered extensions::
$twig = new Twig_Environment($loader);
$twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
// do something different from the built-in date filter
}));
// the date filter will come from the above registration, not
// from the registered extension below
$twig->addExtension(new MyCoreExtension());
.. caution::
Note that overloading the built-in Twig elements is not recommended as it
might be confusing.
Testing an Extension
--------------------
Functional Tests
~~~~~~~~~~~~~~~~
You can create functional tests for extensions simply by creating the
following file structure in your test directory::
Fixtures/
filters/
foo.test
bar.test
functions/
foo.test
bar.test
tags/
foo.test
bar.test
IntegrationTest.php
The ``IntegrationTest.php`` file should look like this::
class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
{
public function getExtensions()
{
return array(
new Project_Twig_Extension1(),
new Project_Twig_Extension2(),
);
}
public function getFixturesDir()
{
return dirname(__FILE__).'/Fixtures/';
}
}
Fixtures examples can be found within the Twig repository
`tests/Twig/Fixtures`_ directory.
Node Tests
~~~~~~~~~~
Testing the node visitors can be complex, so extend your test cases from
``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
`tests/Twig/Node`_ directory.
.. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
.. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures
.. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node
Twig-1.23.1/doc/advanced_legacy.rst 0000664 0000000 0000000 00000065024 12616650102 0017106 0 ustar 00root root 0000000 0000000 Extending Twig
==============
.. caution::
This section describes how to extends Twig for versions **older than
1.12**. If you are using a newer version, read the :doc:`newer`
chapter instead.
Twig can be extended in many ways; you can add extra tags, filters, tests,
operators, global variables, and functions. You can even extend the parser
itself with node visitors.
.. note::
The first section of this chapter describes how to extend Twig easily. If
you want to reuse your changes in different projects or if you want to
share them with others, you should then create an extension as described
in the following section.
.. caution::
When extending Twig by calling methods on the Twig environment instance,
Twig won't be able to recompile your templates when the PHP code is
updated. To see your changes in real-time, either disable template caching
or package your code into an extension (see the next section of this
chapter).
Before extending Twig, you must understand the differences between all the
different possible extension points and when to use them.
First, remember that Twig has two main language constructs:
* ``{{ }}``: used to print the result of an expression evaluation;
* ``{% %}``: used to execute statements.
To understand why Twig exposes so many extension points, let's see how to
implement a *Lorem ipsum* generator (it needs to know the number of words to
generate).
You can use a ``lipsum`` *tag*:
.. code-block:: jinja
{% lipsum 40 %}
That works, but using a tag for ``lipsum`` is not a good idea for at least
three main reasons:
* ``lipsum`` is not a language construct;
* The tag outputs something;
* The tag is not flexible as you cannot use it in an expression:
.. code-block:: jinja
{{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
In fact, you rarely need to create tags; and that's good news because tags are
the most complex extension point of Twig.
Now, let's use a ``lipsum`` *filter*:
.. code-block:: jinja
{{ 40|lipsum }}
Again, it works, but it looks weird. A filter transforms the passed value to
something else but here we use the value to indicate the number of words to
generate (so, ``40`` is an argument of the filter, not the value we want to
transform).
Next, let's use a ``lipsum`` *function*:
.. code-block:: jinja
{{ lipsum(40) }}
Here we go. For this specific example, the creation of a function is the
extension point to use. And you can use it anywhere an expression is accepted:
.. code-block:: jinja
{{ 'some text' ~ ipsum(40) ~ 'some more text' }}
{% set ipsum = ipsum(40) %}
Last but not the least, you can also use a *global* object with a method able
to generate lorem ipsum text:
.. code-block:: jinja
{{ text.lipsum(40) }}
As a rule of thumb, use functions for frequently used features and global
objects for everything else.
Keep in mind the following when you want to extend Twig:
========== ========================== ========== =========================
What? Implementation difficulty? How often? When?
========== ========================== ========== =========================
*macro* trivial frequent Content generation
*global* trivial frequent Helper object
*function* trivial frequent Content generation
*filter* trivial frequent Value transformation
*tag* complex rare DSL language construct
*test* trivial rare Boolean decision
*operator* trivial rare Values transformation
========== ========================== ========== =========================
Globals
-------
A global variable is like any other template variable, except that it's
available in all templates and macros::
$twig = new Twig_Environment($loader);
$twig->addGlobal('text', new Text());
You can then use the ``text`` variable anywhere in a template:
.. code-block:: jinja
{{ text.lipsum(40) }}
Filters
-------
A filter is a regular PHP function or an object method that takes the left
side of the filter (before the pipe ``|``) as first argument and the extra
arguments passed to the filter (within parentheses ``()``) as extra arguments.
Defining a filter is as easy as associating the filter name with a PHP
callable. For instance, let's say you have the following code in a template:
.. code-block:: jinja
{{ 'TWIG'|lower }}
When compiling this template to PHP, Twig looks for the PHP callable
associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig
filter, and it is simply mapped to the PHP ``strtolower()`` function. After
compilation, the generated PHP code is roughly equivalent to:
.. code-block:: html+php
As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP
function.
A filter can also take extra arguments like in the following example:
.. code-block:: jinja
{{ now|date('d/m/Y') }}
In this case, the extra arguments are passed to the function after the main
argument, and the compiled code is equivalent to:
.. code-block:: html+php
Let's see how to create a new filter.
In this section, we will create a ``rot13`` filter, which should return the
`rot13`_ transformation of a string. Here is an example of its usage and the
expected output:
.. code-block:: jinja
{{ "Twig"|rot13 }}
{# should displays Gjvt #}
Adding a filter is as simple as calling the ``addFilter()`` method on the
``Twig_Environment`` instance::
$twig = new Twig_Environment($loader);
$twig->addFilter('rot13', new Twig_Filter_Function('str_rot13'));
The second argument of ``addFilter()`` is an instance of ``Twig_Filter``.
Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The
first argument passed to the ``Twig_Filter_Function`` constructor is the name
of the PHP function to call, here ``str_rot13``, a native PHP function.
Let's say I now want to be able to add a prefix before the converted string:
.. code-block:: jinja
{{ "Twig"|rot13('prefix_') }}
{# should displays prefix_Gjvt #}
As the PHP ``str_rot13()`` function does not support this requirement, let's
create a new PHP function::
function project_compute_rot13($string, $prefix = '')
{
return $prefix.str_rot13($string);
}
As you can see, the ``prefix`` argument of the filter is passed as an extra
argument to the ``project_compute_rot13()`` function.
Adding this filter is as easy as before::
$twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13'));
For better encapsulation, a filter can also be defined as a static method of a
class. The ``Twig_Filter_Function`` class can also be used to register such
static methods as filters::
$twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter'));
.. tip::
In an extension, you can also define a filter as a static method of the
extension class.
Environment aware Filters
~~~~~~~~~~~~~~~~~~~~~~~~~
The ``Twig_Filter`` classes take options as their last argument. For instance,
if you want access to the current environment instance in your filter, set the
``needs_environment`` option to ``true``::
$filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
Twig will then pass the current environment as the first argument to the
filter call::
function twig_compute_rot13(Twig_Environment $env, $string)
{
// get the current charset for instance
$charset = $env->getCharset();
return str_rot13($string);
}
Automatic Escaping
~~~~~~~~~~~~~~~~~~
If automatic escaping is enabled, the output of the filter may be escaped
before printing. If your filter acts as an escaper (or explicitly outputs HTML
or JavaScript code), you will want the raw output to be printed. In such a
case, set the ``is_safe`` option::
$filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
Some filters may need to work on input that is already escaped or safe, for
example when adding (safe) HTML tags to originally unsafe output. In such a
case, set the ``pre_escape`` option to escape the input data before it is run
through your filter::
$filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
Dynamic Filters
~~~~~~~~~~~~~~~
.. versionadded:: 1.5
Dynamic filters support was added in Twig 1.5.
A filter name containing the special ``*`` character is a dynamic filter as
the ``*`` can be any string::
$twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
function twig_path($name, $arguments)
{
// ...
}
The following filters will be matched by the above defined dynamic filter:
* ``product_path``
* ``category_path``
A dynamic filter can define more than one dynamic parts::
$twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
function twig_path($name, $suffix, $arguments)
{
// ...
}
The filter will receive all dynamic part values before the normal filters
arguments. For instance, a call to ``'foo'|a_path_b()`` will result in the
following PHP call: ``twig_path('a', 'b', 'foo')``.
Functions
---------
A function is a regular PHP function or an object method that can be called from
templates.
.. code-block:: jinja
{{ constant("DATE_W3C") }}
When compiling this template to PHP, Twig looks for the PHP callable
associated with the ``constant`` function. The ``constant`` function is a built-in Twig
function, and it is simply mapped to the PHP ``constant()`` function. After
compilation, the generated PHP code is roughly equivalent to:
.. code-block:: html+php
Adding a function is similar to adding a filter. This can be done by calling the
``addFunction()`` method on the ``Twig_Environment`` instance::
$twig = new Twig_Environment($loader);
$twig->addFunction('functionName', new Twig_Function_Function('someFunction'));
You can also expose extension methods as functions in your templates::
// $this is an object that implements Twig_ExtensionInterface.
$twig = new Twig_Environment($loader);
$twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));
Functions also support ``needs_environment`` and ``is_safe`` parameters.
Dynamic Functions
~~~~~~~~~~~~~~~~~
.. versionadded:: 1.5
Dynamic functions support was added in Twig 1.5.
A function name containing the special ``*`` character is a dynamic function
as the ``*`` can be any string::
$twig->addFunction('*_path', new Twig_Function_Function('twig_path'));
function twig_path($name, $arguments)
{
// ...
}
The following functions will be matched by the above defined dynamic function:
* ``product_path``
* ``category_path``
A dynamic function can define more than one dynamic parts::
$twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
function twig_path($name, $suffix, $arguments)
{
// ...
}
The function will receive all dynamic part values before the normal functions
arguments. For instance, a call to ``a_path_b('foo')`` will result in the
following PHP call: ``twig_path('a', 'b', 'foo')``.
Tags
----
One of the most exciting feature of a template engine like Twig is the
possibility to define new language constructs. This is also the most complex
feature as you need to understand how Twig's internals work.
Let's create a simple ``set`` tag that allows the definition of simple
variables from within a template. The tag can be used like follows:
.. code-block:: jinja
{% set name = "value" %}
{{ name }}
{# should output value #}
.. note::
The ``set`` tag is part of the Core extension and as such is always
available. The built-in version is slightly more powerful and supports
multiple assignments by default (cf. the template designers chapter for
more information).
Three steps are needed to define a new tag:
* Defining a Token Parser class (responsible for parsing the template code);
* Defining a Node class (responsible for converting the parsed code to PHP);
* Registering the tag.
Registering a new tag
~~~~~~~~~~~~~~~~~~~~~
Adding a tag is as simple as calling the ``addTokenParser`` method on the
``Twig_Environment`` instance::
$twig = new Twig_Environment($loader);
$twig->addTokenParser(new Project_Set_TokenParser());
Defining a Token Parser
~~~~~~~~~~~~~~~~~~~~~~~
Now, let's see the actual code of this class::
class Project_Set_TokenParser extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
$this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
$value = $this->parser->getExpressionParser()->parseExpression();
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
return new Project_Set_Node($name, $value, $lineno, $this->getTag());
}
public function getTag()
{
return 'set';
}
}
The ``getTag()`` method must return the tag we want to parse, here ``set``.
The ``parse()`` method is invoked whenever the parser encounters a ``set``
tag. It should return a ``Twig_Node`` instance that represents the node (the
``Project_Set_Node`` calls creating is explained in the next section).
The parsing process is simplified thanks to a bunch of methods you can call
from the token stream (``$this->parser->getStream()``):
* ``getCurrent()``: Gets the current token in the stream.
* ``next()``: Moves to the next token in the stream, *but returns the old one*.
* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
the current token is of a particular type or value (or both). The value may be an
array of several possible values.
* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
type/value a syntax error is thrown. Otherwise, if the type and value are correct,
the token is returned and the stream moves to the next token.
* ``look()``: Looks a the next token without consuming it.
Parsing expressions is done by calling the ``parseExpression()`` like we did for
the ``set`` tag.
.. tip::
Reading the existing ``TokenParser`` classes is the best way to learn all
the nitty-gritty details of the parsing process.
Defining a Node
~~~~~~~~~~~~~~~
The ``Project_Set_Node`` class itself is rather simple::
class Project_Set_Node extends Twig_Node
{
public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null)
{
parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('$context[\''.$this->getAttribute('name').'\'] = ')
->subcompile($this->getNode('value'))
->raw(";\n")
;
}
}
The compiler implements a fluid interface and provides methods that helps the
developer generate beautiful and readable PHP code:
* ``subcompile()``: Compiles a node.
* ``raw()``: Writes the given string as is.
* ``write()``: Writes the given string by adding indentation at the beginning
of each line.
* ``string()``: Writes a quoted string.
* ``repr()``: Writes a PHP representation of a given value (see
``Twig_Node_For`` for a usage example).
* ``addDebugInfo()``: Adds the line of the original template file related to
the current node as a comment.
* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
usage example).
* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
usage example).
.. _creating_extensions:
Creating an Extension
---------------------
The main motivation for writing an extension is to move often used code into a
reusable class like adding support for internationalization. An extension can
define tags, filters, tests, operators, global variables, functions, and node
visitors.
Creating an extension also makes for a better separation of code that is
executed at compilation time and code needed at runtime. As such, it makes
your code faster.
Most of the time, it is useful to create a single extension for your project,
to host all the specific tags and filters you want to add to Twig.
.. tip::
When packaging your code into an extension, Twig is smart enough to
recompile your templates whenever you make a change to it (when the
``auto_reload`` is enabled).
.. note::
Before writing your own extensions, have a look at the Twig official
extension repository: http://github.com/twigphp/Twig-extensions.
An extension is a class that implements the following interface::
interface Twig_ExtensionInterface
{
/**
* Initializes the runtime environment.
*
* This is where you can load some file that contains filter functions for instance.
*
* @param Twig_Environment $environment The current Twig_Environment instance
*/
function initRuntime(Twig_Environment $environment);
/**
* Returns the token parser instances to add to the existing list.
*
* @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
*/
function getTokenParsers();
/**
* Returns the node visitor instances to add to the existing list.
*
* @return array An array of Twig_NodeVisitorInterface instances
*/
function getNodeVisitors();
/**
* Returns a list of filters to add to the existing list.
*
* @return array An array of filters
*/
function getFilters();
/**
* Returns a list of tests to add to the existing list.
*
* @return array An array of tests
*/
function getTests();
/**
* Returns a list of functions to add to the existing list.
*
* @return array An array of functions
*/
function getFunctions();
/**
* Returns a list of operators to add to the existing list.
*
* @return array An array of operators
*/
function getOperators();
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*/
function getGlobals();
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
function getName();
}
To keep your extension class clean and lean, it can inherit from the built-in
``Twig_Extension`` class instead of implementing the whole interface. That
way, you just need to implement the ``getName()`` method as the
``Twig_Extension`` provides empty implementations for all other methods.
The ``getName()`` method must return a unique identifier for your extension.
Now, with this information in mind, let's create the most basic extension
possible::
class Project_Twig_Extension extends Twig_Extension
{
public function getName()
{
return 'project';
}
}
.. note::
Of course, this extension does nothing for now. We will customize it in
the next sections.
Twig does not care where you save your extension on the filesystem, as all
extensions must be registered explicitly to be available in your templates.
You can register an extension by using the ``addExtension()`` method on your
main ``Environment`` object::
$twig = new Twig_Environment($loader);
$twig->addExtension(new Project_Twig_Extension());
Of course, you need to first load the extension file by either using
``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
.. tip::
The bundled extensions are great examples of how extensions work.
Globals
~~~~~~~
Global variables can be registered in an extension via the ``getGlobals()``
method::
class Project_Twig_Extension extends Twig_Extension
{
public function getGlobals()
{
return array(
'text' => new Text(),
);
}
// ...
}
Functions
~~~~~~~~~
Functions can be registered in an extension via the ``getFunctions()``
method::
class Project_Twig_Extension extends Twig_Extension
{
public function getFunctions()
{
return array(
'lipsum' => new Twig_Function_Function('generate_lipsum'),
);
}
// ...
}
Filters
~~~~~~~
To add a filter to an extension, you need to override the ``getFilters()``
method. This method must return an array of filters to add to the Twig
environment::
class Project_Twig_Extension extends Twig_Extension
{
public function getFilters()
{
return array(
'rot13' => new Twig_Filter_Function('str_rot13'),
);
}
// ...
}
As you can see in the above code, the ``getFilters()`` method returns an array
where keys are the name of the filters (``rot13``) and the values the
definition of the filter (``new Twig_Filter_Function('str_rot13')``).
As seen in the previous chapter, you can also define filters as static methods
on the extension class::
$twig->addFilter('rot13', new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'));
You can also use ``Twig_Filter_Method`` instead of ``Twig_Filter_Function``
when defining a filter to use a method::
class Project_Twig_Extension extends Twig_Extension
{
public function getFilters()
{
return array(
'rot13' => new Twig_Filter_Method($this, 'rot13Filter'),
);
}
public function rot13Filter($string)
{
return str_rot13($string);
}
// ...
}
The first argument of the ``Twig_Filter_Method`` constructor is always
``$this``, the current extension object. The second one is the name of the
method to call.
Using methods for filters is a great way to package your filter without
polluting the global namespace. This also gives the developer more flexibility
at the cost of a small overhead.
Overriding default Filters
..........................
If some default core filters do not suit your needs, you can easily override
them by creating your own extension. Just use the same names as the one you
want to override::
class MyCoreExtension extends Twig_Extension
{
public function getFilters()
{
return array(
'date' => new Twig_Filter_Method($this, 'dateFilter'),
// ...
);
}
public function dateFilter($timestamp, $format = 'F j, Y H:i')
{
return '...'.twig_date_format_filter($timestamp, $format);
}
public function getName()
{
return 'project';
}
}
Here, we override the ``date`` filter with a custom one. Using this extension
is as simple as registering the ``MyCoreExtension`` extension by calling the
``addExtension()`` method on the environment instance::
$twig = new Twig_Environment($loader);
$twig->addExtension(new MyCoreExtension());
Tags
~~~~
Adding a tag in an extension can be done by overriding the
``getTokenParsers()`` method. This method must return an array of tags to add
to the Twig environment::
class Project_Twig_Extension extends Twig_Extension
{
public function getTokenParsers()
{
return array(new Project_Set_TokenParser());
}
// ...
}
In the above code, we have added a single new tag, defined by the
``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
responsible for parsing the tag and compiling it to PHP.
Operators
~~~~~~~~~
The ``getOperators()`` methods allows to add new operators. Here is how to add
``!``, ``||``, and ``&&`` operators::
class Project_Twig_Extension extends Twig_Extension
{
public function getOperators()
{
return array(
array(
'!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
),
array(
'||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
),
);
}
// ...
}
Tests
~~~~~
The ``getTests()`` methods allows to add new test functions::
class Project_Twig_Extension extends Twig_Extension
{
public function getTests()
{
return array(
'even' => new Twig_Test_Function('twig_test_even'),
);
}
// ...
}
Testing an Extension
--------------------
.. versionadded:: 1.10
Support for functional tests was added in Twig 1.10.
Functional Tests
~~~~~~~~~~~~~~~~
You can create functional tests for extensions simply by creating the
following file structure in your test directory::
Fixtures/
filters/
foo.test
bar.test
functions/
foo.test
bar.test
tags/
foo.test
bar.test
IntegrationTest.php
The ``IntegrationTest.php`` file should look like this::
class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
{
public function getExtensions()
{
return array(
new Project_Twig_Extension1(),
new Project_Twig_Extension2(),
);
}
public function getFixturesDir()
{
return dirname(__FILE__).'/Fixtures/';
}
}
Fixtures examples can be found within the Twig repository
`tests/Twig/Fixtures`_ directory.
Node Tests
~~~~~~~~~~
Testing the node visitors can be complex, so extend your test cases from
``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
`tests/Twig/Node`_ directory.
.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
.. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
.. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Fixtures
.. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/master/test/Twig/Tests/Node
Twig-1.23.1/doc/api.rst 0000664 0000000 0000000 00000044324 12616650102 0014566 0 ustar 00root root 0000000 0000000 Twig for Developers
===================
This chapter describes the API to Twig and not the template language. It will
be most useful as reference to those implementing the template interface to
the application and not those who are creating Twig templates.
Basics
------
Twig uses a central object called the **environment** (of class
``Twig_Environment``). Instances of this class are used to store the
configuration and extensions, and are used to load templates from the file
system or other locations.
Most applications will create one ``Twig_Environment`` object on application
initialization and use that to load templates. In some cases it's however
useful to have multiple environments side by side, if different configurations
are in use.
The simplest way to configure Twig to load templates for your application
looks roughly like this::
require_once '/path/to/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
$loader = new Twig_Loader_Filesystem('/path/to/templates');
$twig = new Twig_Environment($loader, array(
'cache' => '/path/to/compilation_cache',
));
This will create a template environment with the default settings and a loader
that looks up the templates in the ``/path/to/templates/`` folder. Different
loaders are available and you can also write your own if you want to load
templates from a database or other resources.
.. note::
Notice that the second argument of the environment is an array of options.
The ``cache`` option is a compilation cache directory, where Twig caches
the compiled templates to avoid the parsing phase for sub-sequent
requests. It is very different from the cache you might want to add for
the evaluated templates. For such a need, you can use any available PHP
cache library.
To load a template from this environment you just have to call the
``loadTemplate()`` method which then returns a ``Twig_Template`` instance::
$template = $twig->loadTemplate('index.html');
To render the template with some variables, call the ``render()`` method::
echo $template->render(array('the' => 'variables', 'go' => 'here'));
.. note::
The ``display()`` method is a shortcut to output the template directly.
You can also load and render the template in one fell swoop::
echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
.. _environment_options:
Environment Options
-------------------
When creating a new ``Twig_Environment`` instance, you can pass an array of
options as the constructor second argument::
$twig = new Twig_Environment($loader, array('debug' => true));
The following options are available:
* ``debug`` *boolean*
When set to ``true``, the generated templates have a
``__toString()`` method that you can use to display the generated nodes
(default to ``false``).
* ``charset`` *string (default to ``utf-8``)*
The charset used by the templates.
* ``base_template_class`` *string (default to ``Twig_Template``)*
The base template class to use for generated
templates.
* ``cache`` *string|false*
An absolute path where to store the compiled templates, or
``false`` to disable caching (which is the default).
* ``auto_reload`` *boolean*
When developing with Twig, it's useful to recompile the
template whenever the source code changes. If you don't provide a value for
the ``auto_reload`` option, it will be determined automatically based on the
``debug`` value.
* ``strict_variables`` *boolean*
If set to ``false``, Twig will silently ignore invalid
variables (variables and or attributes/methods that do not exist) and
replace them with a ``null`` value. When set to ``true``, Twig throws an
exception instead (default to ``false``).
* ``autoescape`` *string|boolean*
If set to ``true``, HTML auto-escaping will be enabled by
default for all templates (default to ``true``).
As of Twig 1.8, you can set the escaping strategy to use (``html``, ``js``,
``false`` to disable).
As of Twig 1.9, you can set the escaping strategy to use (``css``, ``url``,
``html_attr``, or a PHP callback that takes the template "filename" and must
return the escaping strategy to use -- the callback cannot be a function name
to avoid collision with built-in escaping strategies).
As of Twig 1.17, the ``filename`` escaping strategy determines the escaping
strategy to use for a template based on the template filename extension (this
strategy does not incur any overhead at runtime as auto-escaping is done at
compilation time.)
* ``optimizations`` *integer*
A flag that indicates which optimizations to apply
(default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
disable).
Loaders
-------
Loaders are responsible for loading templates from a resource such as the file
system.
Compilation Cache
~~~~~~~~~~~~~~~~~
All template loaders can cache the compiled templates on the filesystem for
future reuse. It speeds up Twig a lot as templates are only compiled once; and
the performance boost is even larger if you use a PHP accelerator such as APC.
See the ``cache`` and ``auto_reload`` options of ``Twig_Environment`` above
for more information.
Built-in Loaders
~~~~~~~~~~~~~~~~
Here is a list of the built-in loaders Twig provides:
``Twig_Loader_Filesystem``
..........................
.. versionadded:: 1.10
The ``prependPath()`` and support for namespaces were added in Twig 1.10.
``Twig_Loader_Filesystem`` loads templates from the file system. This loader
can find templates in folders on the file system and is the preferred way to
load them::
$loader = new Twig_Loader_Filesystem($templateDir);
It can also look for templates in an array of directories::
$loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2));
With such a configuration, Twig will first look for templates in
``$templateDir1`` and if they do not exist, it will fallback to look for them
in the ``$templateDir2``.
You can add or prepend paths via the ``addPath()`` and ``prependPath()``
methods::
$loader->addPath($templateDir3);
$loader->prependPath($templateDir4);
The filesystem loader also supports namespaced templates. This allows to group
your templates under different namespaces which have their own template paths.
When using the ``setPaths()``, ``addPath()``, and ``prependPath()`` methods,
specify the namespace as the second argument (when not specified, these
methods act on the "main" namespace)::
$loader->addPath($templateDir, 'admin');
Namespaced templates can be accessed via the special
``@namespace_name/template_path`` notation::
$twig->render('@admin/index.html', array());
``Twig_Loader_Array``
.....................
``Twig_Loader_Array`` loads a template from a PHP array. It's passed an array
of strings bound to template names::
$loader = new Twig_Loader_Array(array(
'index.html' => 'Hello {{ name }}!',
));
$twig = new Twig_Environment($loader);
echo $twig->render('index.html', array('name' => 'Fabien'));
This loader is very useful for unit testing. It can also be used for small
projects where storing all templates in a single PHP file might make sense.
.. tip::
When using the ``Array`` or ``String`` loaders 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.
``Twig_Loader_Chain``
.....................
``Twig_Loader_Chain`` delegates the loading of templates to other loaders::
$loader1 = new Twig_Loader_Array(array(
'base.html' => '{% block content %}{% endblock %}',
));
$loader2 = new Twig_Loader_Array(array(
'index.html' => '{% extends "base.html" %}{% block content %}Hello {{ name }}{% endblock %}',
'base.html' => 'Will never be loaded',
));
$loader = new Twig_Loader_Chain(array($loader1, $loader2));
$twig = new Twig_Environment($loader);
When looking for a template, Twig will try each loader in turn and it will
return as soon as the template is found. When rendering the ``index.html``
template from the above example, Twig will load it with ``$loader2`` but the
``base.html`` template will be loaded from ``$loader1``.
``Twig_Loader_Chain`` accepts any loader that implements
``Twig_LoaderInterface``.
.. note::
You can also add loaders via the ``addLoader()`` method.
Create your own Loader
~~~~~~~~~~~~~~~~~~~~~~
All loaders implement the ``Twig_LoaderInterface``::
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
*/
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
*/
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
*/
function isFresh($name, $time);
}
The ``isFresh()`` method must return ``true`` if the current cached template
is still fresh, given the last modification time, or ``false`` otherwise.
.. tip::
As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface``
to make your loader faster when used with the chain loader.
Using Extensions
----------------
Twig extensions are packages that add new features to Twig. Using an
extension is as simple as using the ``addExtension()`` method::
$twig->addExtension(new Twig_Extension_Sandbox());
Twig comes bundled with the following extensions:
* *Twig_Extension_Core*: Defines all the core features of Twig.
* *Twig_Extension_Escaper*: Adds automatic output-escaping and the possibility
to escape/unescape blocks of code.
* *Twig_Extension_Sandbox*: Adds a sandbox mode to the default Twig
environment, making it safe to evaluate untrusted code.
* *Twig_Extension_Profiler*: Enabled the built-in Twig profiler (as of Twig
1.18).
* *Twig_Extension_Optimizer*: Optimizes the node tree before compilation.
The core, escaper, and optimizer extensions do not need to be added to the
Twig environment, as they are registered by default.
Built-in Extensions
-------------------
This section describes the features added by the built-in extensions.
.. tip::
Read the chapter about extending Twig to learn how to create your own
extensions.
Core Extension
~~~~~~~~~~~~~~
The ``core`` extension defines all the core features of Twig:
* :doc:`Tags `;
* :doc:`Filters `;
* :doc:`Functions `;
* :doc:`Tests `.
Escaper Extension
~~~~~~~~~~~~~~~~~
The ``escaper`` extension adds automatic output escaping to Twig. It defines a
tag, ``autoescape``, and a filter, ``raw``.
When creating the escaper extension, you can switch on or off the global
output escaping strategy::
$escaper = new Twig_Extension_Escaper('html');
$twig->addExtension($escaper);
If set to ``html``, all variables in templates are escaped (using the ``html``
escaping strategy), except those using the ``raw`` filter:
.. code-block:: jinja
{{ article.to_html|raw }}
You can also change the escaping mode locally by using the ``autoescape`` tag
(see the :doc:`autoescape` doc for the syntax used before
Twig 1.8):
.. code-block:: jinja
{% autoescape 'html' %}
{{ var }}
{{ var|raw }} {# var won't be escaped #}
{{ var|escape }} {# var won't be double-escaped #}
{% endautoescape %}
.. warning::
The ``autoescape`` tag has no effect on included files.
The escaping rules are implemented as follows:
* Literals (integers, booleans, arrays, ...) used in the template directly as
variables or filter arguments are never automatically escaped:
.. code-block:: jinja
{{ "Twig " }} {# won't be escaped #}
{% set text = "Twig " %}
{{ text }} {# will be escaped #}
* Expressions which the result is always a literal or a variable marked safe
are never automatically escaped:
.. code-block:: jinja
{{ foo ? "Twig " : " Twig" }} {# won't be escaped #}
{% set text = "Twig " %}
{{ foo ? text : " Twig" }} {# will be escaped #}
{% set text = "Twig " %}
{{ foo ? text|raw : " Twig" }} {# won't be escaped #}
{% set text = "Twig " %}
{{ foo ? text|escape : " Twig" }} {# the result of the expression won't be escaped #}
* Escaping is applied before printing, after any other filter is applied:
.. code-block:: jinja
{{ var|upper }} {# is equivalent to {{ var|upper|escape }} #}
* The `raw` filter should only be used at the end of the filter chain:
.. code-block:: jinja
{{ var|raw|upper }} {# will be escaped #}
{{ var|upper|raw }} {# won't be escaped #}
* Automatic escaping is not applied if the last filter in the chain is marked
safe for the current context (e.g. ``html`` or ``js``). ``escape`` and
``escape('html')`` are marked safe for HTML, ``escape('js')`` is marked
safe for JavaScript, ``raw`` is marked safe for everything.
.. code-block:: jinja
{% autoescape 'js' %}
{{ var|escape('html') }} {# will be escaped for HTML and JavaScript #}
{{ var }} {# will be escaped for JavaScript #}
{{ var|escape('js') }} {# won't be double-escaped #}
{% endautoescape %}
.. note::
Note that autoescaping has some limitations as escaping is applied on
expressions after evaluation. For instance, when working with
concatenation, ``{{ foo|raw ~ bar }}`` won't give the expected result as
escaping is applied on the result of the concatenation, not on the
individual variables (so, the ``raw`` filter won't have any effect here).
Sandbox Extension
~~~~~~~~~~~~~~~~~
The ``sandbox`` extension can be used to evaluate untrusted code. Access to
unsafe attributes and methods is prohibited. The sandbox security is managed
by a policy instance. By default, Twig comes with one policy class:
``Twig_Sandbox_SecurityPolicy``. This class allows you to white-list some
tags, filters, properties, and methods::
$tags = array('if');
$filters = array('upper');
$methods = array(
'Article' => array('getTitle', 'getBody'),
);
$properties = array(
'Article' => array('title', 'body'),
);
$functions = array('range');
$policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
With the previous configuration, the security policy will only allow usage of
the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be
able to call the ``getTitle()`` and ``getBody()`` methods on ``Article``
objects, and the ``title`` and ``body`` public properties. Everything else
won't be allowed and will generate a ``Twig_Sandbox_SecurityError`` exception.
The policy object is the first argument of the sandbox constructor::
$sandbox = new Twig_Extension_Sandbox($policy);
$twig->addExtension($sandbox);
By default, the sandbox mode is disabled and should be enabled when including
untrusted template code by using the ``sandbox`` tag:
.. code-block:: jinja
{% sandbox %}
{% include 'user.html' %}
{% endsandbox %}
You can sandbox all templates by passing ``true`` as the second argument of
the extension constructor::
$sandbox = new Twig_Extension_Sandbox($policy, true);
Profiler Extension
~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.18
The Profile extension was added in Twig 1.18.
The ``profiler`` extension enables a profiler for Twig templates; it should
only be used on your development machines as it adds some overhead::
$profile = new Twig_Profiler_Profile();
$twig->addExtension(new Twig_Extension_Profiler($profile));
$dumper = new Twig_Profiler_Dumper_Text();
echo $dumper->dump($profile);
A profile contains information about time and memory consumption for template,
block, and macro executions.
You can also dump the data in a `Blackfire.io `_
compatible format::
$dumper = new Twig_Profiler_Dumper_Blackfire();
file_put_contents('/path/to/profile.prof', $dumper->dump($profile));
Upload the profile to visualize it (create a `free account
`_ first):
.. code-block:: sh
blackfire --slot=7 upload /path/to/profile.prof
Optimizer Extension
~~~~~~~~~~~~~~~~~~~
The ``optimizer`` extension optimizes the node tree before compilation::
$twig->addExtension(new Twig_Extension_Optimizer());
By default, all optimizations are turned on. You can select the ones you want
to enable by passing them to the constructor::
$optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR);
$twig->addExtension($optimizer);
Twig supports the following optimizations:
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_ALL``, enables all optimizations
(this is the default value).
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_NONE``, disables all optimizations.
This reduces the compilation time, but it can increase the execution time
and the consumed memory.
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR``, optimizes the ``for`` tag by
removing the ``loop`` variable creation whenever possible.
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_RAW_FILTER``, removes the ``raw``
filter whenever possible.
* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_VAR_ACCESS``, simplifies the creation
and access of variables in the compiled templates whenever possible.
Exceptions
----------
Twig can throw exceptions:
* ``Twig_Error``: The base exception for all errors.
* ``Twig_Error_Syntax``: Thrown to tell the user that there is a problem with
the template syntax.
* ``Twig_Error_Runtime``: Thrown when an error occurs at runtime (when a filter
does not exist for instance).
* ``Twig_Error_Loader``: Thrown when an error occurs during template loading.
* ``Twig_Sandbox_SecurityError``: Thrown when an unallowed tag, filter, or
method is called in a sandboxed template.
Twig-1.23.1/doc/coding_standards.rst 0000664 0000000 0000000 00000004516 12616650102 0017322 0 ustar 00root root 0000000 0000000 Coding Standards
================
When writing Twig templates, we recommend you to follow these official coding
standards:
* Put one (and only one) space after the start of a delimiter (``{{``, ``{%``,
and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``):
.. code-block:: jinja
{{ foo }}
{# comment #}
{% if foo %}{% endif %}
When using the whitespace control character, do not put any spaces between
it and the delimiter:
.. code-block:: jinja
{{- foo -}}
{#- comment -#}
{%- if foo -%}{%- endif -%}
* Put one (and only one) space before and after the following operators:
comparison operators (``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``), math
operators (``+``, ``-``, ``/``, ``*``, ``%``, ``//``, ``**``), logic
operators (``not``, ``and``, ``or``), ``~``, ``is``, ``in``, and the ternary
operator (``?:``):
.. code-block:: jinja
{{ 1 + 2 }}
{{ foo ~ bar }}
{{ true ? true : false }}
* Put one (and only one) space after the ``:`` sign in hashes and ``,`` in
arrays and hashes:
.. code-block:: jinja
{{ [1, 2, 3] }}
{{ {'foo': 'bar'} }}
* Do not put any spaces after an opening parenthesis and before a closing
parenthesis in expressions:
.. code-block:: jinja
{{ 1 + (2 * 3) }}
* Do not put any spaces before and after string delimiters:
.. code-block:: jinja
{{ 'foo' }}
{{ "foo" }}
* Do not put any spaces before and after the following operators: ``|``,
``.``, ``..``, ``[]``:
.. code-block:: jinja
{{ foo|upper|lower }}
{{ user.name }}
{{ user[name] }}
{% for i in 1..12 %}{% endfor %}
* Do not put any spaces before and after the parenthesis used for filter and
function calls:
.. code-block:: jinja
{{ foo|default('foo') }}
{{ range(1..10) }}
* Do not put any spaces before and after the opening and the closing of arrays
and hashes:
.. code-block:: jinja
{{ [1, 2, 3] }}
{{ {'foo': 'bar'} }}
* Use lower cased and underscored variable names:
.. code-block:: jinja
{% set foo = 'foo' %}
{% set foo_bar = 'foo' %}
* Indent your code inside tags (use the same indentation as the one used for
the target language of the rendered template):
.. code-block:: jinja
{% block foo %}
{% if true %}
true
{% endif %}
{% endblock %}
Twig-1.23.1/doc/deprecated.rst 0000664 0000000 0000000 00000012441 12616650102 0016110 0 ustar 00root root 0000000 0000000 Deprecated Features
===================
This document lists all deprecated features in Twig. Deprecated features are
kept for backward compatibility and removed in the next major release (a
feature that was deprecated in Twig 1.x is removed in Twig 2.0).
Deprecation Notices
-------------------
As of Twig 1.21, Twig generates deprecation notices when a template uses
deprecated features. See :ref:`deprecation-notices` for more information.
Token Parsers
-------------
* As of Twig 1.x, the token parser broker sub-system is deprecated. The
following class and interface will be removed in 2.0:
* ``Twig_TokenParserBrokerInterface``
* ``Twig_TokenParserBroker``
Extensions
----------
* As of Twig 1.x, the ability to remove an extension is deprecated and the
``Twig_Environment::removeExtension()`` method will be removed in 2.0.
* As of Twig 1.23, the ``Twig_ExtensionInterface::initRuntime()`` method is
deprecated. You have two options to avoid the deprecation notice: if you
implement this method to store the environment for your custom filters,
functions, or tests, use the ``needs_environment`` option instead; if you
have more complex needs, explicitly implement
``Twig_Extension_InitRuntimeInterface`` (not recommended).
* As of Twig 1.23, the ``Twig_ExtensionInterface::getGlobals()`` method is
deprecated. Implement ``Twig_Extension_GlobalsInterface`` to avoid
deprecation notices.
PEAR
----
PEAR support has been discontinued in Twig 1.15.1, and no PEAR packages are
provided anymore. Use Composer instead.
Filters
-------
* As of Twig 1.x, use ``Twig_SimpleFilter`` to add a filter. The following
classes and interfaces will be removed in 2.0:
* ``Twig_FilterInterface``
* ``Twig_FilterCallableInterface``
* ``Twig_Filter``
* ``Twig_Filter_Function``
* ``Twig_Filter_Method``
* ``Twig_Filter_Node``
* As of Twig 2.x, the ``Twig_SimpleFilter`` class is deprecated and will be
removed in Twig 3.x (use ``Twig_Filter`` instead). In Twig 2.x,
``Twig_SimpleFilter`` is just an alias for ``Twig_Filter``.
Functions
---------
* As of Twig 1.x, use ``Twig_SimpleFunction`` to add a function. The following
classes and interfaces will be removed in 2.0:
* ``Twig_FunctionInterface``
* ``Twig_FunctionCallableInterface``
* ``Twig_Function``
* ``Twig_Function_Function``
* ``Twig_Function_Method``
* ``Twig_Function_Node``
* As of Twig 2.x, the ``Twig_SimpleFunction`` class is deprecated and will be
removed in Twig 3.x (use ``Twig_Function`` instead). In Twig 2.x,
``Twig_SimpleFunction`` is just an alias for ``Twig_Function``.
Tests
-----
* As of Twig 1.x, use ``Twig_SimpleTest`` to add a test. The following classes
and interfaces will be removed in 2.0:
* ``Twig_TestInterface``
* ``Twig_TestCallableInterface``
* ``Twig_Test``
* ``Twig_Test_Function``
* ``Twig_Test_Method``
* ``Twig_Test_Node``
* As of Twig 2.x, the ``Twig_SimpleTest`` class is deprecated and will be
removed in Twig 3.x (use ``Twig_Test`` instead). In Twig 2.x,
``Twig_SimpleTest`` is just an alias for ``Twig_Test``.
* The ``sameas`` and ``divisibleby`` tests are deprecated in favor of ``same
as`` and ``divisible by`` respectively.
Tags
----
* As of Twig 1.x, the ``raw`` tag is deprecated. You should use ``verbatim``
instead.
Nodes
-----
* As of Twig 1.x, ``Node::toXml()`` is deprecated and will be removed in Twig
2.0.
Interfaces
----------
* As of Twig 2.x, the following interfaces are deprecated and empty (they will
be removed in Twig 3.0):
* ``Twig_CompilerInterface`` (use ``Twig_Compiler`` instead)
* ``Twig_LexerInterface`` (use ``Twig_Lexer`` instead)
* ``Twig_NodeInterface`` (use ``Twig_Node`` instead)
* ``Twig_ParserInterface`` (use ``Twig_Parser`` instead)
* ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
* ``Twig_TemplateInterface`` (use ``Twig_Template`` instead, and use
those constants Twig_Template::ANY_CALL, Twig_Template::ARRAY_CALL,
Twig_Template::METHOD_CALL)
Loaders
-------
* As of Twig 1.x, ``Twig_Loader_String`` is deprecated and will be removed in
2.0. You can render a string via ``Twig_Environment::createTemplate()``.
Node Visitors
-------------
* Because of the removal of ``Twig_NodeInterface`` in 2.0, you need to extend
``Twig_BaseNodeVisitor`` instead of implementing ``Twig_NodeVisitorInterface``
directly to make your node visitors compatible with both Twig 1.x and 2.x.
Globals
-------
* As of Twig 2.x, the ability to register a global variable after the runtime
or the extensions have been initialized is not possible anymore (but
changing the value of an already registered global is possible).
* As of Twig 1.x, the ``_self`` global variable is deprecated except for usage
in the ``from`` and the ``import`` tags. In Twig 2.0, ``_self`` is not
exposed anymore but still usable in the ``from`` and the ``import`` tags.
Miscellaneous
-------------
* As of Twig 1.x, ``Twig_Environment::clearTemplateCache()``, ``Twig_Environment::writeCacheFile()``,
``Twig_Environment::clearCacheFiles()``, ``Twig_Environment::getCacheFilename()``, and
``Twig_Environment::getTemplateClassPrefix()`` are deprecated and will be removed in 2.0.
* As of Twig 1.x, ``Twig_Template::getEnvironment()`` and
``Twig_TemplateInterface::getEnvironment()`` are deprecated and will be
removed in 2.0.
Twig-1.23.1/doc/filters/ 0000775 0000000 0000000 00000000000 12616650102 0014724 5 ustar 00root root 0000000 0000000 Twig-1.23.1/doc/filters/abs.rst 0000664 0000000 0000000 00000000367 12616650102 0016231 0 ustar 00root root 0000000 0000000 ``abs``
=======
The ``abs`` filter returns the absolute value.
.. code-block:: jinja
{# number = -5 #}
{{ number|abs }}
{# outputs 5 #}
.. note::
Internally, Twig uses the PHP `abs`_ function.
.. _`abs`: http://php.net/abs
Twig-1.23.1/doc/filters/batch.rst 0000664 0000000 0000000 00000002102 12616650102 0016532 0 ustar 00root root 0000000 0000000 ``batch``
=========
.. versionadded:: 1.12.3
The ``batch`` filter was added in Twig 1.12.3.
The ``batch`` filter "batches" items by returning a list of lists with the
given number of items. A second parameter can be provided and used to fill in
missing items:
.. code-block:: jinja
{% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}
{% for row in items|batch(3, 'No item') %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %}
The above example will be rendered as:
.. code-block:: jinja
a
b
c
d
e
f
g
No item
No item
Arguments
---------
* ``size``: The size of the batch; fractional numbers will be rounded up
* ``fill``: Used to fill in missing items
Twig-1.23.1/doc/filters/capitalize.rst 0000664 0000000 0000000 00000000351 12616650102 0017602 0 ustar 00root root 0000000 0000000 ``capitalize``
==============
The ``capitalize`` filter capitalizes a value. The first character will be
uppercase, all others lowercase:
.. code-block:: jinja
{{ 'my first car'|capitalize }}
{# outputs 'My first car' #}
Twig-1.23.1/doc/filters/convert_encoding.rst 0000664 0000000 0000000 00000001371 12616650102 0021006 0 ustar 00root root 0000000 0000000 ``convert_encoding``
====================
.. versionadded:: 1.4
The ``convert_encoding`` filter was added in Twig 1.4.
The ``convert_encoding`` filter converts a string from one encoding to
another. The first argument is the expected output charset and the second one
is the input charset:
.. code-block:: jinja
{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
.. note::
This filter relies on the `iconv`_ or `mbstring`_ extension, so one of
them must be installed. In case both are installed, `mbstring`_ is used by
default (Twig before 1.8.1 uses `iconv`_ by default).
Arguments
---------
* ``to``: The output charset
* ``from``: The input charset
.. _`iconv`: http://php.net/iconv
.. _`mbstring`: http://php.net/mbstring
Twig-1.23.1/doc/filters/date.rst 0000664 0000000 0000000 00000005553 12616650102 0016403 0 ustar 00root root 0000000 0000000 ``date``
========
.. versionadded:: 1.1
The timezone support has been added in Twig 1.1.
.. versionadded:: 1.5
The default date format support has been added in Twig 1.5.
.. versionadded:: 1.6.1
The default timezone support has been added in Twig 1.6.1.
.. versionadded:: 1.11.0
The introduction of the false value for the timezone was introduced in Twig 1.11.0
The ``date`` filter formats a date to a given format:
.. code-block:: jinja
{{ post.published_at|date("m/d/Y") }}
The format specifier is the same as supported by `date`_,
except when the filtered data is of type `DateInterval`_, when the format must conform to
`DateInterval::format`_ instead.
The ``date`` filter accepts strings (it must be in a format supported by the
`strtotime`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For
instance, to display the current date, filter the word "now":
.. code-block:: jinja
{{ "now"|date("m/d/Y") }}
To escape words and characters in the date format use ``\\`` in front of each
character:
.. code-block:: jinja
{{ post.published_at|date("F jS \\a\\t g:ia") }}
If the value passed to the ``date`` filter is ``null``, it will return the
current date by default. If an empty string is desired instead of the current
date, use a ternary operator:
.. code-block:: jinja
{{ post.published_at is empty ? "" : post.published_at|date("m/d/Y") }}
If no format is provided, Twig will use the default one: ``F j, Y H:i``. This
default can be easily changed by calling the ``setDateFormat()`` method on the
``core`` extension instance. The first argument is the default format for
dates and the second one is the default format for date intervals:
.. code-block:: php
$twig = new Twig_Environment($loader);
$twig->getExtension('core')->setDateFormat('d/m/Y', '%d days');
Timezone
--------
By default, the date is displayed by applying the default timezone (the one
specified in php.ini or declared in Twig -- see below), but you can override
it by explicitly specifying a timezone:
.. code-block:: jinja
{{ post.published_at|date("m/d/Y", "Europe/Paris") }}
If the date is already a DateTime object, and if you want to keep its current
timezone, pass ``false`` as the timezone value:
.. code-block:: jinja
{{ post.published_at|date("m/d/Y", false) }}
The default timezone can also be set globally by calling ``setTimezone()``:
.. code-block:: php
$twig = new Twig_Environment($loader);
$twig->getExtension('core')->setTimezone('Europe/Paris');
Arguments
---------
* ``format``: The date format
* ``timezone``: The date timezone
.. _`strtotime`: http://www.php.net/strtotime
.. _`DateTime`: http://www.php.net/DateTime
.. _`DateInterval`: http://www.php.net/DateInterval
.. _`date`: http://www.php.net/date
.. _`DateInterval::format`: http://www.php.net/DateInterval.format
Twig-1.23.1/doc/filters/date_modify.rst 0000664 0000000 0000000 00000001165 12616650102 0017745 0 ustar 00root root 0000000 0000000 ``date_modify``
===============
.. versionadded:: 1.9.0
The date_modify filter has been added in Twig 1.9.0.
The ``date_modify`` filter modifies a date with a given modifier string:
.. code-block:: jinja
{{ post.published_at|date_modify("+1 day")|date("m/d/Y") }}
The ``date_modify`` filter accepts strings (it must be in a format supported
by the `strtotime`_ function) or `DateTime`_ instances. You can easily combine
it with the :doc:`date` filter for formatting.
Arguments
---------
* ``modifier``: The modifier
.. _`strtotime`: http://www.php.net/strtotime
.. _`DateTime`: http://www.php.net/DateTime
Twig-1.23.1/doc/filters/default.rst 0000664 0000000 0000000 00000001532 12616650102 0017103 0 ustar 00root root 0000000 0000000 ``default``
===========
The ``default`` filter returns the passed default value if the value is
undefined or empty, otherwise the value of the variable:
.. code-block:: jinja
{{ var|default('var is not defined') }}
{{ var.foo|default('foo item on var is not defined') }}
{{ var['foo']|default('foo item on var is not defined') }}
{{ ''|default('passed var is empty') }}
When using the ``default`` filter on an expression that uses variables in some
method calls, be sure to use the ``default`` filter whenever a variable can be
undefined:
.. code-block:: jinja
{{ var.method(foo|default('foo'))|default('foo') }}
.. note::
Read the documentation for the :doc:`defined<../tests/defined>` and
:doc:`empty<../tests/empty>` tests to learn more about their semantics.
Arguments
---------
* ``default``: The default value
Twig-1.23.1/doc/filters/escape.rst 0000664 0000000 0000000 00000006434 12616650102 0016725 0 ustar 00root root 0000000 0000000 ``escape``
==========
.. versionadded:: 1.9.0
The ``css``, ``url``, and ``html_attr`` strategies were added in Twig
1.9.0.
.. versionadded:: 1.14.0
The ability to define custom escapers was added in Twig 1.14.0.
The ``escape`` filter escapes a string for safe insertion into the final
output. It supports different escaping strategies depending on the template
context.
By default, it uses the HTML escaping strategy:
.. code-block:: jinja
{{ user.username|escape }}
For convenience, the ``e`` filter is defined as an alias:
.. code-block:: jinja
{{ user.username|e }}
The ``escape`` filter can also be used in other contexts than HTML thanks to
an optional argument which defines the escaping strategy to use:
.. code-block:: jinja
{{ user.username|e }}
{# is equivalent to #}
{{ user.username|e('html') }}
And here is how to escape variables included in JavaScript code:
.. code-block:: jinja
{{ user.username|escape('js') }}
{{ user.username|e('js') }}
The ``escape`` filter supports the following escaping strategies:
* ``html``: escapes a string for the **HTML body** context.
* ``js``: escapes a string for the **JavaScript context**.
* ``css``: escapes a string for the **CSS context**. CSS escaping can be
applied to any string being inserted into CSS and escapes everything except
alphanumerics.
* ``url``: escapes a string for the **URI or parameter contexts**. This should
not be used to escape an entire URI; only a subcomponent being inserted.
* ``html_attr``: escapes a string for the **HTML attribute** context.
.. note::
Internally, ``escape`` uses the PHP native `htmlspecialchars`_ function
for the HTML escaping strategy.
.. caution::
When using automatic escaping, Twig tries to not double-escape a variable
when the automatic escaping strategy is the same as the one applied by the
escape filter; but that does not work when using a variable as the
escaping strategy:
.. code-block:: jinja
{% set strategy = 'html' %}
{% autoescape 'html' %}
{{ var|escape('html') }} {# won't be double-escaped #}
{{ var|escape(strategy) }} {# will be double-escaped #}
{% endautoescape %}
When using a variable as the escaping strategy, you should disable
automatic escaping:
.. code-block:: jinja
{% set strategy = 'html' %}
{% autoescape 'html' %}
{{ var|escape(strategy)|raw }} {# won't be double-escaped #}
{% endautoescape %}
Custom Escapers
---------------
You can define custom escapers by calling the ``setEscaper()`` method on the
``core`` extension instance. The first argument is the escaper name (to be
used in the ``escape`` call) and the second one must be a valid PHP callable:
.. code-block:: php
$twig = new Twig_Environment($loader);
$twig->getExtension('core')->setEscaper('csv', 'csv_escaper'));
When called by Twig, the callable receives the Twig environment instance, the
string to escape, and the charset.
.. note::
Built-in escapers cannot be overridden mainly they should be considered as
the final implementation and also for better performance.
Arguments
---------
* ``strategy``: The escaping strategy
* ``charset``: The string charset
.. _`htmlspecialchars`: http://php.net/htmlspecialchars
Twig-1.23.1/doc/filters/first.rst 0000664 0000000 0000000 00000001010 12616650102 0016575 0 ustar 00root root 0000000 0000000 ``first``
=========
.. versionadded:: 1.12.2
The ``first`` filter was added in Twig 1.12.2.
The ``first`` filter returns the first "element" of a sequence, a mapping, or
a string:
.. code-block:: jinja
{{ [1, 2, 3, 4]|first }}
{# outputs 1 #}
{{ { a: 1, b: 2, c: 3, d: 4 }|first }}
{# outputs 1 #}
{{ '1234'|first }}
{# outputs 1 #}
.. note::
It also works with objects implementing the `Traversable`_ interface.
.. _`Traversable`: http://php.net/manual/en/class.traversable.php
Twig-1.23.1/doc/filters/format.rst 0000664 0000000 0000000 00000000610 12616650102 0016743 0 ustar 00root root 0000000 0000000 ``format``
==========
The ``format`` filter formats a given string by replacing the placeholders
(placeholders follows the `sprintf`_ notation):
.. code-block:: jinja
{{ "I like %s and %s."|format(foo, "bar") }}
{# outputs I like foo and bar
if the foo parameter equals to the foo string. #}
.. _`sprintf`: http://www.php.net/sprintf
.. seealso:: :doc:`replace`
Twig-1.23.1/doc/filters/index.rst 0000664 0000000 0000000 00000000622 12616650102 0016565 0 ustar 00root root 0000000 0000000 Filters
=======
.. toctree::
:maxdepth: 1
abs
batch
capitalize
convert_encoding
date
date_modify
default
escape
first
format
join
json_encode
keys
last
length
lower
merge
nl2br
number_format
raw
replace
reverse
round
slice
sort
split
striptags
title
trim
upper
url_encode
Twig-1.23.1/doc/filters/join.rst 0000664 0000000 0000000 00000000655 12616650102 0016423 0 ustar 00root root 0000000 0000000 ``join``
========
The ``join`` filter returns a string which is the concatenation of the items
of a sequence:
.. code-block:: jinja
{{ [1, 2, 3]|join }}
{# returns 123 #}
The separator between elements is an empty string per default, but you can
define it with the optional first parameter:
.. code-block:: jinja
{{ [1, 2, 3]|join('|') }}
{# outputs 1|2|3 #}
Arguments
---------
* ``glue``: The separator
Twig-1.23.1/doc/filters/json_encode.rst 0000664 0000000 0000000 00000000745 12616650102 0017752 0 ustar 00root root 0000000 0000000 ``json_encode``
===============
The ``json_encode`` filter returns the JSON representation of a value:
.. code-block:: jinja
{{ data|json_encode() }}
.. note::
Internally, Twig uses the PHP `json_encode`_ function.
Arguments
---------
* ``options``: A bitmask of `json_encode options`_ (``{{
data|json_encode(constant('JSON_PRETTY_PRINT')) }}``)
.. _`json_encode`: http://php.net/json_encode
.. _`json_encode options`: http://www.php.net/manual/en/json.constants.php
Twig-1.23.1/doc/filters/keys.rst 0000664 0000000 0000000 00000000333 12616650102 0016430 0 ustar 00root root 0000000 0000000 ``keys``
========
The ``keys`` filter returns the keys of an array. It is useful when you want to
iterate over the keys of an array:
.. code-block:: jinja
{% for key in array|keys %}
...
{% endfor %}
Twig-1.23.1/doc/filters/last.rst 0000664 0000000 0000000 00000001000 12616650102 0016410 0 ustar 00root root 0000000 0000000 ``last``
========
.. versionadded:: 1.12.2
The ``last`` filter was added in Twig 1.12.2.
The ``last`` filter returns the last "element" of a sequence, a mapping, or
a string:
.. code-block:: jinja
{{ [1, 2, 3, 4]|last }}
{# outputs 4 #}
{{ { a: 1, b: 2, c: 3, d: 4 }|last }}
{# outputs 4 #}
{{ '1234'|last }}
{# outputs 4 #}
.. note::
It also works with objects implementing the `Traversable`_ interface.
.. _`Traversable`: http://php.net/manual/en/class.traversable.php
Twig-1.23.1/doc/filters/length.rst 0000664 0000000 0000000 00000000321 12616650102 0016733 0 ustar 00root root 0000000 0000000 ``length``
==========
The ``length`` filter returns the number of items of a sequence or mapping, or
the length of a string:
.. code-block:: jinja
{% if users|length > 10 %}
...
{% endif %}
Twig-1.23.1/doc/filters/lower.rst 0000664 0000000 0000000 00000000230 12616650102 0016601 0 ustar 00root root 0000000 0000000 ``lower``
=========
The ``lower`` filter converts a value to lowercase:
.. code-block:: jinja
{{ 'WELCOME'|lower }}
{# outputs 'welcome' #}
Twig-1.23.1/doc/filters/merge.rst 0000664 0000000 0000000 00000002524 12616650102 0016560 0 ustar 00root root 0000000 0000000 ``merge``
=========
The ``merge`` filter merges an array with another array:
.. code-block:: jinja
{% set values = [1, 2] %}
{% set values = values|merge(['apple', 'orange']) %}
{# values now contains [1, 2, 'apple', 'orange'] #}
New values are added at the end of the existing ones.
The ``merge`` filter also works on hashes:
.. code-block:: jinja
{% set items = { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'unknown' } %}
{% set items = items|merge({ 'peugeot': 'car', 'renault': 'car' }) %}
{# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'renault': 'car' } #}
For hashes, the merging process occurs on the keys: if the key does not
already exist, it is added but if the key already exists, its value is
overridden.
.. tip::
If you want to ensure that some values are defined in an array (by given
default values), reverse the two elements in the call:
.. code-block:: jinja
{% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
{% set items = { 'apple': 'unknown' }|merge(items) %}
{# items now contains { 'apple': 'fruit', 'orange': 'fruit' } #}
.. note::
Internally, Twig uses the PHP `array_merge`_ function. It supports
Traversable objects by transforming those to arrays.
.. _`array_merge`: http://php.net/array_merge
Twig-1.23.1/doc/filters/nl2br.rst 0000664 0000000 0000000 00000000655 12616650102 0016503 0 ustar 00root root 0000000 0000000 ``nl2br``
=========
.. versionadded:: 1.5
The ``nl2br`` filter was added in Twig 1.5.
The ``nl2br`` filter inserts HTML line breaks before all newlines in a string:
.. code-block:: jinja
{{ "I like Twig.\nYou will like it too."|nl2br }}
{# outputs
I like Twig.
You will like it too.
#}
.. note::
The ``nl2br`` filter pre-escapes the input before applying the
transformation.
Twig-1.23.1/doc/filters/number_format.rst 0000664 0000000 0000000 00000002301 12616650102 0020312 0 ustar 00root root 0000000 0000000 ``number_format``
=================
.. versionadded:: 1.5
The ``number_format`` filter was added in Twig 1.5
The ``number_format`` filter formats numbers. It is a wrapper around PHP's
`number_format`_ function:
.. code-block:: jinja
{{ 200.35|number_format }}
You can control the number of decimal places, decimal point, and thousands
separator using the additional arguments:
.. code-block:: jinja
{{ 9800.333|number_format(2, '.', ',') }}
If no formatting options are provided then Twig will use the default formatting
options of:
* 0 decimal places.
* ``.`` as the decimal point.
* ``,`` as the thousands separator.
These defaults can be easily changed through the core extension:
.. code-block:: php
$twig = new Twig_Environment($loader);
$twig->getExtension('core')->setNumberFormat(3, '.', ',');
The defaults set for ``number_format`` can be over-ridden upon each call using the
additional parameters.
Arguments
---------
* ``decimal``: The number of decimal points to display
* ``decimal_point``: The character(s) to use for the decimal point
* ``thousand_sep``: The character(s) to use for the thousands separator
.. _`number_format`: http://php.net/number_format
Twig-1.23.1/doc/filters/raw.rst 0000664 0000000 0000000 00000002274 12616650102 0016254 0 ustar 00root root 0000000 0000000 ``raw``
=======
The ``raw`` filter marks the value as being "safe", which means that in an
environment with automatic escaping enabled this variable will not be escaped
if ``raw`` is the last filter applied to it:
.. code-block:: jinja
{% autoescape %}
{{ var|raw }} {# var won't be escaped #}
{% endautoescape %}
.. note::
Be careful when using the ``raw`` filter inside expressions:
.. code-block:: jinja
{% autoescape %}
{% set hello = 'Hello ' %}
{% set hola = 'Hola ' %}
{{ false ? 'Hola ' : hello|raw }}
does not render the same as
{{ false ? hola : hello|raw }}
but renders the same as
{{ (false ? hola : hello)|raw }}
{% endautoescape %}
The first ternary statement is not escaped: ``hello`` is marked as being
safe and Twig does not escape static values (see
:doc:`escape<../tags/autoescape>`). In the second ternary statement, even
if ``hello`` is marked as safe, ``hola`` remains unsafe and so is the whole
expression. The third ternary statement is marked as safe and the result is
not escaped.
Twig-1.23.1/doc/filters/replace.rst 0000664 0000000 0000000 00000000655 12616650102 0017077 0 ustar 00root root 0000000 0000000 ``replace``
===========
The ``replace`` filter formats a given string by replacing the placeholders
(placeholders are free-form):
.. code-block:: jinja
{{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }}
{# outputs I like foo and bar
if the foo parameter equals to the foo string. #}
Arguments
---------
* ``replace_pairs``: The placeholder values
.. seealso:: :doc:`format`
Twig-1.23.1/doc/filters/reverse.rst 0000664 0000000 0000000 00000002050 12616650102 0017126 0 ustar 00root root 0000000 0000000 ``reverse``
===========
.. versionadded:: 1.6
Support for strings has been added in Twig 1.6.
The ``reverse`` filter reverses a sequence, a mapping, or a string:
.. code-block:: jinja
{% for user in users|reverse %}
...
{% endfor %}
{{ '1234'|reverse }}
{# outputs 4321 #}
.. tip::
For sequences and mappings, numeric keys are not preserved. To reverse
them as well, pass ``true`` as an argument to the ``reverse`` filter:
.. code-block:: jinja
{% for key, value in {1: "a", 2: "b", 3: "c"}|reverse %}
{{ key }}: {{ value }}
{%- endfor %}
{# output: 0: c 1: b 2: a #}
{% for key, value in {1: "a", 2: "b", 3: "c"}|reverse(true) %}
{{ key }}: {{ value }}
{%- endfor %}
{# output: 3: c 2: b 1: a #}
.. note::
It also works with objects implementing the `Traversable`_ interface.
Arguments
---------
* ``preserve_keys``: Preserve keys when reversing a mapping or a sequence.
.. _`Traversable`: http://php.net/Traversable
Twig-1.23.1/doc/filters/round.rst 0000664 0000000 0000000 00000001535 12616650102 0016611 0 ustar 00root root 0000000 0000000 ``round``
=========
.. versionadded:: 1.15.0
The ``round`` filter was added in Twig 1.15.0.
The ``round`` filter rounds a number to a given precision:
.. code-block:: jinja
{{ 42.55|round }}
{# outputs 43 #}
{{ 42.55|round(1, 'floor') }}
{# outputs 42.5 #}
The ``round`` filter takes two optional arguments; the first one specifies the
precision (default is ``0``) and the second the rounding method (default is
``common``):
* ``common`` rounds either up or down (rounds the value up to precision decimal
places away from zero, when it is half way there -- making 1.5 into 2 and
-1.5 into -2);
* ``ceil`` always rounds up;
* ``floor`` always rounds down.
.. note::
The ``//`` operator is equivalent to ``|round(0, 'floor')``.
Arguments
---------
* ``precision``: The rounding precision
* ``method``: The rounding method
Twig-1.23.1/doc/filters/slice.rst 0000664 0000000 0000000 00000004221 12616650102 0016554 0 ustar 00root root 0000000 0000000 ``slice``
===========
.. versionadded:: 1.6
The ``slice`` filter was added in Twig 1.6.
The ``slice`` filter extracts a slice of a sequence, a mapping, or a string:
.. code-block:: jinja
{% for i in [1, 2, 3, 4, 5]|slice(1, 2) %}
{# will iterate over 2 and 3 #}
{% endfor %}
{{ '12345'|slice(1, 2) }}
{# outputs 23 #}
You can use any valid expression for both the start and the length:
.. code-block:: jinja
{% for i in [1, 2, 3, 4, 5]|slice(start, length) %}
{# ... #}
{% endfor %}
As syntactic sugar, you can also use the ``[]`` notation:
.. code-block:: jinja
{% for i in [1, 2, 3, 4, 5][start:length] %}
{# ... #}
{% endfor %}
{{ '12345'[1:2] }} {# will display "23" #}
{# you can omit the first argument -- which is the same as 0 #}
{{ '12345'[:2] }} {# will display "12" #}
{# you can omit the last argument -- which will select everything till the end #}
{{ '12345'[2:] }} {# will display "345" #}
The ``slice`` filter works as the `array_slice`_ PHP function for arrays and
`mb_substr`_ for strings with a fallback to `substr`_.
If the start is non-negative, the sequence will start at that start in the
variable. If start is negative, the sequence will start that far from the end
of the variable.
If length is given and is positive, then the sequence will have up to that
many elements in it. If the variable is shorter than the length, then only the
available variable elements will be present. If length is given and is
negative then the sequence will stop that many elements from the end of the
variable. If it is omitted, then the sequence will have everything from offset
up until the end of the variable.
.. note::
It also works with objects implementing the `Traversable`_ interface.
Arguments
---------
* ``start``: The start of the slice
* ``length``: The size of the slice
* ``preserve_keys``: Whether to preserve key or not (when the input is an array)
.. _`Traversable`: http://php.net/manual/en/class.traversable.php
.. _`array_slice`: http://php.net/array_slice
.. _`mb_substr` : http://php.net/mb-substr
.. _`substr`: http://php.net/substr
Twig-1.23.1/doc/filters/sort.rst 0000664 0000000 0000000 00000000530 12616650102 0016443 0 ustar 00root root 0000000 0000000 ``sort``
========
The ``sort`` filter sorts an array:
.. code-block:: jinja
{% for user in users|sort %}
...
{% endfor %}
.. note::
Internally, Twig uses the PHP `asort`_ function to maintain index
association. It supports Traversable objects by transforming
those to arrays.
.. _`asort`: http://php.net/asort
Twig-1.23.1/doc/filters/split.rst 0000664 0000000 0000000 00000002606 12616650102 0016615 0 ustar 00root root 0000000 0000000 ``split``
=========
.. versionadded:: 1.10.3
The ``split`` filter was added in Twig 1.10.3.
The ``split`` filter splits a string by the given delimiter and returns a list
of strings:
.. code-block:: jinja
{% set foo = "one,two,three"|split(',') %}
{# foo contains ['one', 'two', 'three'] #}
You can also pass a ``limit`` argument:
* If ``limit`` is positive, the returned array will contain a maximum of
limit elements with the last element containing the rest of string;
* If ``limit`` is negative, all components except the last -limit are
returned;
* If ``limit`` is zero, then this is treated as 1.
.. code-block:: jinja
{% set foo = "one,two,three,four,five"|split(',', 3) %}
{# foo contains ['one', 'two', 'three,four,five'] #}
If the ``delimiter`` is an empty string, then value will be split by equal
chunks. Length is set by the ``limit`` argument (one character by default).
.. code-block:: jinja
{% set foo = "123"|split('') %}
{# foo contains ['1', '2', '3'] #}
{% set bar = "aabbcc"|split('', 2) %}
{# bar contains ['aa', 'bb', 'cc'] #}
.. note::
Internally, Twig uses the PHP `explode`_ or `str_split`_ (if delimiter is
empty) functions for string splitting.
Arguments
---------
* ``delimiter``: The delimiter
* ``limit``: The limit argument
.. _`explode`: http://php.net/explode
.. _`str_split`: http://php.net/str_split
Twig-1.23.1/doc/filters/striptags.rst 0000664 0000000 0000000 00000000442 12616650102 0017476 0 ustar 00root root 0000000 0000000 ``striptags``
=============
The ``striptags`` filter strips SGML/XML tags and replace adjacent whitespace
by one space:
.. code-block:: jinja
{{ some_html|striptags }}
.. note::
Internally, Twig uses the PHP `strip_tags`_ function.
.. _`strip_tags`: http://php.net/strip_tags
Twig-1.23.1/doc/filters/title.rst 0000664 0000000 0000000 00000000377 12616650102 0016606 0 ustar 00root root 0000000 0000000 ``title``
=========
The ``title`` filter returns a titlecased version of the value. Words will
start with uppercase letters, all remaining characters are lowercase:
.. code-block:: jinja
{{ 'my first car'|title }}
{# outputs 'My First Car' #}
Twig-1.23.1/doc/filters/trim.rst 0000664 0000000 0000000 00000001013 12616650102 0016424 0 ustar 00root root 0000000 0000000 ``trim``
========
.. versionadded:: 1.6.2
The ``trim`` filter was added in Twig 1.6.2.
The ``trim`` filter strips whitespace (or other characters) from the beginning
and end of a string:
.. code-block:: jinja
{{ ' I like Twig. '|trim }}
{# outputs 'I like Twig.' #}
{{ ' I like Twig.'|trim('.') }}
{# outputs ' I like Twig' #}
.. note::
Internally, Twig uses the PHP `trim`_ function.
Arguments
---------
* ``character_mask``: The characters to strip
.. _`trim`: http://php.net/trim
Twig-1.23.1/doc/filters/upper.rst 0000664 0000000 0000000 00000000230 12616650102 0016604 0 ustar 00root root 0000000 0000000 ``upper``
=========
The ``upper`` filter converts a value to uppercase:
.. code-block:: jinja
{{ 'welcome'|upper }}
{# outputs 'WELCOME' #}
Twig-1.23.1/doc/filters/url_encode.rst 0000664 0000000 0000000 00000002075 12616650102 0017601 0 ustar 00root root 0000000 0000000 ``url_encode``
==============
.. versionadded:: 1.12.3
Support for encoding an array as query string was added in Twig 1.12.3.
.. versionadded:: 1.16.0
The ``raw`` argument was removed in Twig 1.16.0. Twig now always encodes
according to RFC 3986.
The ``url_encode`` filter percent encodes a given string as URL segment
or an array as query string:
.. code-block:: jinja
{{ "path-seg*ment"|url_encode }}
{# outputs "path-seg%2Ament" #}
{{ "string with spaces"|url_encode }}
{# outputs "string%20with%20spaces" #}
{{ {'param': 'value', 'foo': 'bar'}|url_encode }}
{# outputs "param=value&foo=bar" #}
.. note::
Internally, Twig uses the PHP `urlencode`_ (or `rawurlencode`_ if you pass
``true`` as the first parameter) or the `http_build_query`_ function. Note
that as of Twig 1.16.0, ``urlencode`` **always** uses ``rawurlencode`` (the
``raw`` argument was removed.)
.. _`urlencode`: http://php.net/urlencode
.. _`rawurlencode`: http://php.net/rawurlencode
.. _`http_build_query`: http://php.net/http_build_query
Twig-1.23.1/doc/functions/ 0000775 0000000 0000000 00000000000 12616650102 0015264 5 ustar 00root root 0000000 0000000 Twig-1.23.1/doc/functions/attribute.rst 0000664 0000000 0000000 00000001251 12616650102 0020020 0 ustar 00root root 0000000 0000000 ``attribute``
=============
.. versionadded:: 1.2
The ``attribute`` function was added in Twig 1.2.
The ``attribute`` function can be used to access a "dynamic" attribute of a
variable:
.. code-block:: jinja
{{ attribute(object, method) }}
{{ attribute(object, method, arguments) }}
{{ attribute(array, item) }}
In addition, the ``defined`` test can check for the existence of a dynamic
attribute:
.. code-block:: jinja
{{ attribute(object, method) is defined ? 'Method exists' : 'Method does not exist' }}
.. note::
The resolution algorithm is the same as the one used for the ``.``
notation, except that the item can be any valid expression.
Twig-1.23.1/doc/functions/block.rst 0000664 0000000 0000000 00000000547 12616650102 0017116 0 ustar 00root root 0000000 0000000 ``block``
=========
When a template uses inheritance and if you want to print a block multiple
times, use the ``block`` function:
.. code-block:: jinja
{% block title %}{% endblock %}
{{ block('title') }}
{% block body %}{% endblock %}
.. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`
Twig-1.23.1/doc/functions/constant.rst 0000664 0000000 0000000 00000000657 12616650102 0017657 0 ustar 00root root 0000000 0000000 ``constant``
============
.. versionadded: 1.12.1
constant now accepts object instances as the second argument.
``constant`` returns the constant value for a given string:
.. code-block:: jinja
{{ some_date|date(constant('DATE_W3C')) }}
{{ constant('Namespace\\Classname::CONSTANT_NAME') }}
As of 1.12.1 you can read constants from object instances as well:
.. code-block:: jinja
{{ constant('RSS', date) }}
Twig-1.23.1/doc/functions/cycle.rst 0000664 0000000 0000000 00000001044 12616650102 0017114 0 ustar 00root root 0000000 0000000 ``cycle``
=========
The ``cycle`` function cycles on an array of values:
.. code-block:: jinja
{% set start_year = date() | date('Y') %}
{% set end_year = start_year + 5 %}
{% for year in start_year..end_year %}
{{ cycle(['odd', 'even'], loop.index0) }}
{% endfor %}
The array can contain any number of values:
.. code-block:: jinja
{% set fruits = ['apple', 'orange', 'citrus'] %}
{% for i in 0..10 %}
{{ cycle(fruits, i) }}
{% endfor %}
Arguments
---------
* ``position``: The cycle position
Twig-1.23.1/doc/functions/date.rst 0000664 0000000 0000000 00000002266 12616650102 0016741 0 ustar 00root root 0000000 0000000 ``date``
========
.. versionadded:: 1.6
The date function has been added in Twig 1.6.
.. versionadded:: 1.6.1
The default timezone support has been added in Twig 1.6.1.
Converts an argument to a date to allow date comparison:
.. code-block:: jinja
{% if date(user.created_at) < date('-2days') %}
{# do something #}
{% endif %}
The argument must be in one of PHP’s supported `date and time formats`_.
You can pass a timezone as the second argument:
.. code-block:: jinja
{% if date(user.created_at) < date('-2days', 'Europe/Paris') %}
{# do something #}
{% endif %}
If no argument is passed, the function returns the current date:
.. code-block:: jinja
{% if date(user.created_at) < date() %}
{# always! #}
{% endif %}
.. note::
You can set the default timezone globally by calling ``setTimezone()`` on
the ``core`` extension instance:
.. code-block:: php
$twig = new Twig_Environment($loader);
$twig->getExtension('core')->setTimezone('Europe/Paris');
Arguments
---------
* ``date``: The date
* ``timezone``: The timezone
.. _`date and time formats`: http://php.net/manual/en/datetime.formats.php
Twig-1.23.1/doc/functions/dump.rst 0000664 0000000 0000000 00000003154 12616650102 0016766 0 ustar 00root root 0000000 0000000 ``dump``
========
.. versionadded:: 1.5
The ``dump`` function was added in Twig 1.5.
The ``dump`` function dumps information about a template variable. This is
mostly useful to debug a template that does not behave as expected by
introspecting its variables:
.. code-block:: jinja
{{ dump(user) }}
.. note::
The ``dump`` function is not available by default. You must add the
``Twig_Extension_Debug`` extension explicitly when creating your Twig
environment::
$twig = new Twig_Environment($loader, array(
'debug' => true,
// ...
));
$twig->addExtension(new Twig_Extension_Debug());
Even when enabled, the ``dump`` function won't display anything if the
``debug`` option on the environment is not enabled (to avoid leaking debug
information on a production server).
In an HTML context, wrap the output with a ``pre`` tag to make it easier to
read:
.. code-block:: jinja
{{ dump(user) }}
.. tip::
Using a ``pre`` tag is not needed when `XDebug`_ is enabled and
``html_errors`` is ``on``; as a bonus, the output is also nicer with
XDebug enabled.
You can debug several variables by passing them as additional arguments:
.. code-block:: jinja
{{ dump(user, categories) }}
If you don't pass any value, all variables from the current context are
dumped:
.. code-block:: jinja
{{ dump() }}
.. note::
Internally, Twig uses the PHP `var_dump`_ function.
Arguments
---------
* ``context``: The context to dump
.. _`XDebug`: http://xdebug.org/docs/display
.. _`var_dump`: http://php.net/var_dump
Twig-1.23.1/doc/functions/include.rst 0000664 0000000 0000000 00000004474 12616650102 0017452 0 ustar 00root root 0000000 0000000 ``include``
===========
.. versionadded:: 1.12
The ``include`` function was added in Twig 1.12.
The ``include`` function returns the rendered content of a template:
.. code-block:: jinja
{{ include('template.html') }}
{{ include(some_var) }}
Included templates have access to the variables of the active context.
If you are using the filesystem loader, the templates are looked for in the
paths defined by it.
The context is passed by default to the template but you can also pass
additional variables:
.. code-block:: jinja
{# template.html will have access to the variables from the current context and the additional ones provided #}
{{ include('template.html', {foo: 'bar'}) }}
You can disable access to the context by setting ``with_context`` to
``false``:
.. code-block:: jinja
{# only the foo variable will be accessible #}
{{ include('template.html', {foo: 'bar'}, with_context = false) }}
.. code-block:: jinja
{# no variables will be accessible #}
{{ include('template.html', with_context = false) }}
And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
directly::
// {{ include(template) }}
$template = $twig->loadTemplate('some_template.twig');
$twig->loadTemplate('template.twig')->display(array('template' => $template));
When you set the ``ignore_missing`` flag, Twig will return an empty string if
the template does not exist:
.. code-block:: jinja
{{ include('sidebar.html', ignore_missing = true) }}
You can also provide a list of templates that are checked for existence before
inclusion. The first template that exists will be rendered:
.. code-block:: jinja
{{ include(['page_detailed.html', 'page.html']) }}
If ``ignore_missing`` is set, it will fall back to rendering nothing if none
of the templates exist, otherwise it will throw an exception.
When including a template created by an end user, you should consider
sandboxing it:
.. code-block:: jinja
{{ include('page.html', sandboxed = true) }}
Arguments
---------
* ``template``: The template to render
* ``variables``: The variables to pass to the template
* ``with_context``: Whether to pass the current context variables or not
* ``ignore_missing``: Whether to ignore missing templates or not
* ``sandboxed``: Whether to sandbox the template or not
Twig-1.23.1/doc/functions/index.rst 0000664 0000000 0000000 00000000325 12616650102 0017125 0 ustar 00root root 0000000 0000000 Functions
=========
.. toctree::
:maxdepth: 1
attribute
block
constant
cycle
date
dump
include
max
min
parent
random
range
source
template_from_string
Twig-1.23.1/doc/functions/max.rst 0000664 0000000 0000000 00000000625 12616650102 0016606 0 ustar 00root root 0000000 0000000 ``max``
=======
.. versionadded:: 1.15
The ``max`` function was added in Twig 1.15.
``max`` returns the biggest value of a sequence or a set of values:
.. code-block:: jinja
{{ max(1, 3, 2) }}
{{ max([1, 3, 2]) }}
When called with a mapping, max ignores keys and only compares values:
.. code-block:: jinja
{{ max({2: "e", 1: "a", 3: "b", 5: "d", 4: "c"}) }}
{# returns "e" #}
Twig-1.23.1/doc/functions/min.rst 0000664 0000000 0000000 00000000624 12616650102 0016603 0 ustar 00root root 0000000 0000000 ``min``
=======
.. versionadded:: 1.15
The ``min`` function was added in Twig 1.15.
``min`` returns the lowest value of a sequence or a set of values:
.. code-block:: jinja
{{ min(1, 3, 2) }}
{{ min([1, 3, 2]) }}
When called with a mapping, min ignores keys and only compares values:
.. code-block:: jinja
{{ min({2: "e", 3: "a", 1: "b", 5: "d", 4: "c"}) }}
{# returns "a" #}
Twig-1.23.1/doc/functions/parent.rst 0000664 0000000 0000000 00000001063 12616650102 0017307 0 ustar 00root root 0000000 0000000 ``parent``
==========
When a template uses inheritance, it's possible to render the contents of the
parent block when overriding a block by using the ``parent`` function:
.. code-block:: jinja
{% extends "base.html" %}
{% block sidebar %}
Table Of Contents
...
{{ parent() }}
{% endblock %}
The ``parent()`` call will return the content of the ``sidebar`` block as
defined in the ``base.html`` template.
.. seealso:: :doc:`extends<../tags/extends>`, :doc:`block<../functions/block>`, :doc:`block<../tags/block>`
Twig-1.23.1/doc/functions/random.rst 0000664 0000000 0000000 00000001527 12616650102 0017303 0 ustar 00root root 0000000 0000000 ``random``
==========
.. versionadded:: 1.5
The ``random`` function was added in Twig 1.5.
.. versionadded:: 1.6
String and integer handling was added in Twig 1.6.
The ``random`` function returns a random value depending on the supplied
parameter type:
* a random item from a sequence;
* a random character from a string;
* a random integer between 0 and the integer parameter (inclusive).
.. code-block:: jinja
{{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #}
{{ random('ABC') }} {# example output: C #}
{{ random() }} {# example output: 15386094 (works as the native PHP mt_rand function) #}
{{ random(5) }} {# example output: 3 #}
Arguments
---------
* ``values``: The values
.. _`mt_rand`: http://php.net/mt_rand
Twig-1.23.1/doc/functions/range.rst 0000664 0000000 0000000 00000001611 12616650102 0017111 0 ustar 00root root 0000000 0000000 ``range``
=========
Returns a list containing an arithmetic progression of integers:
.. code-block:: jinja
{% for i in range(0, 3) %}
{{ i }},
{% endfor %}
{# outputs 0, 1, 2, 3, #}
When step is given (as the third parameter), it specifies the increment (or
decrement):
.. code-block:: jinja
{% for i in range(0, 6, 2) %}
{{ i }},
{% endfor %}
{# outputs 0, 2, 4, 6, #}
The Twig built-in ``..`` operator is just syntactic sugar for the ``range``
function (with a step of 1):
.. code-block:: jinja
{% for i in 0..3 %}
{{ i }},
{% endfor %}
.. tip::
The ``range`` function works as the native PHP `range`_ function.
Arguments
---------
* ``low``: The first value of the sequence.
* ``high``: The highest possible value of the sequence.
* ``step``: The increment between elements of the sequence.
.. _`range`: http://php.net/range
Twig-1.23.1/doc/functions/source.rst 0000664 0000000 0000000 00000001526 12616650102 0017322 0 ustar 00root root 0000000 0000000 ``source``
==========
.. versionadded:: 1.15
The ``source`` function was added in Twig 1.15.
.. versionadded:: 1.18.3
The ``ignore_missing`` flag was added in Twig 1.18.3.
The ``source`` function returns the content of a template without rendering it:
.. code-block:: jinja
{{ source('template.html') }}
{{ source(some_var) }}
When you set the ``ignore_missing`` flag, Twig will return an empty string if
the template does not exist:
.. code-block:: jinja
{{ source('template.html', ignore_missing = true) }}
The function uses the same template loaders as the ones used to include
templates. So, if you are using the filesystem loader, the templates are looked
for in the paths defined by it.
Arguments
---------
* ``name``: The name of the template to read
* ``ignore_missing``: Whether to ignore missing templates or not
Twig-1.23.1/doc/functions/template_from_string.rst 0000664 0000000 0000000 00000001675 12616650102 0022253 0 ustar 00root root 0000000 0000000 ``template_from_string``
========================
.. versionadded:: 1.11
The ``template_from_string`` function was added in Twig 1.11.
The ``template_from_string`` function loads a template from a string:
.. code-block:: jinja
{{ include(template_from_string("Hello {{ name }}")) }}
{{ include(template_from_string(page.template)) }}
.. note::
The ``template_from_string`` function is not available by default. You
must add the ``Twig_Extension_StringLoader`` extension explicitly when
creating your Twig environment::
$twig = new Twig_Environment(...);
$twig->addExtension(new Twig_Extension_StringLoader());
.. note::
Even if you will probably always use the ``template_from_string`` function
with the ``include`` function, you can use it with any tag or function that
takes a template as an argument (like the ``embed`` or ``extends`` tags).
Arguments
---------
* ``template``: The template
Twig-1.23.1/doc/index.rst 0000664 0000000 0000000 00000000353 12616650102 0015116 0 ustar 00root root 0000000 0000000 Twig
====
.. toctree::
:maxdepth: 2
intro
installation
templates
api
advanced
internals
deprecated
recipes
coding_standards
tags/index
filters/index
functions/index
tests/index
Twig-1.23.1/doc/installation.rst 0000664 0000000 0000000 00000006700 12616650102 0016512 0 ustar 00root root 0000000 0000000 Installation
============
You have multiple ways to install Twig.
Installing the Twig PHP package
-------------------------------
Installing via Composer (recommended)
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Install `Composer`_ and run the following command to get the latest version:
.. code-block:: bash
composer require twig/twig:~1.0
Installing from the tarball release
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1. Download the most recent tarball from the `download page`_
2. Verify the integrity of the tarball http://fabien.potencier.org/article/73/signing-project-releases
3. Unpack the tarball
4. Move the files somewhere in your project
Installing the development version
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. code-block:: bash
git clone git://github.com/twigphp/Twig.git
Installing the PEAR package
~~~~~~~~~~~~~~~~~~~~~~~~~~~
.. note::
Using PEAR for installing Twig is deprecated and Twig 1.15.1 was the last
version published on the PEAR channel; use Composer instead.
.. code-block:: bash
pear channel-discover pear.twig-project.org
pear install twig/Twig
Installing the C extension
--------------------------
.. versionadded:: 1.4
The C extension was added in Twig 1.4.
.. note::
The C extension is **optional** but it brings some nice performance
improvements. Note that the extension is not a replacement for the PHP
code; it only implements a small part of the PHP code to improve the
performance at runtime; you must still install the regular PHP code.
Twig comes with a C extension that enhances the performance of the Twig
runtime engine; install it like any other PHP extensions:
.. code-block:: bash
cd ext/twig
phpize
./configure
make
make install
.. note::
You can also install the C extension via PEAR (note that this method is
deprecated and newer versions of Twig are not available on the PEAR
channel):
.. code-block:: bash
pear channel-discover pear.twig-project.org
pear install twig/CTwig
For Windows:
1. Setup the build environment following the `PHP documentation`_
2. Put Twig's C extension source code into ``C:\php-sdk\phpdev\vcXX\x86\php-source-directory\ext\twig``
3. Use the ``configure --disable-all --enable-cli --enable-twig=shared`` command instead of step 14
4. ``nmake``
5. Copy the ``C:\php-sdk\phpdev\vcXX\x86\php-source-directory\Release_TS\php_twig.dll`` file to your PHP setup.
.. tip::
For Windows ZendServer, ZTS is not enabled as mentioned in `Zend Server
FAQ`_.
You have to use ``configure --disable-all --disable-zts --enable-cli
--enable-twig=shared`` to be able to build the twig C extension for
ZendServer.
The built DLL will be available in
``C:\\php-sdk\\phpdev\\vcXX\\x86\\php-source-directory\\Release``
Finally, enable the extension in your ``php.ini`` configuration file:
.. code-block:: ini
extension=twig.so #For Unix systems
extension=php_twig.dll #For Windows systems
And from now on, Twig will automatically compile your templates to take
advantage of the C extension. Note that this extension does not replace the
PHP code but only provides an optimized version of the
``Twig_Template::getAttribute()`` method.
.. _`download page`: https://github.com/twigphp/Twig/tags
.. _`Composer`: https://getcomposer.org/download/
.. _`PHP documentation`: https://wiki.php.net/internals/windows/stepbystepbuild
.. _`Zend Server FAQ`: http://www.zend.com/en/products/server/faq#faqD6
Twig-1.23.1/doc/internals.rst 0000664 0000000 0000000 00000010562 12616650102 0016011 0 ustar 00root root 0000000 0000000 Twig Internals
==============
Twig is very extensible and you can easily hack it. Keep in mind that you
should probably try to create an extension before hacking the core, as most
features and enhancements can be handled with extensions. This chapter is also
useful for people who want to understand how Twig works under the hood.
How does Twig work?
-------------------
The rendering of a Twig template can be summarized into four key steps:
* **Load** the template: If the template is already compiled, load it and go
to the *evaluation* step, otherwise:
* First, the **lexer** tokenizes the template source code into small pieces
for easier processing;
* Then, the **parser** converts the token stream into a meaningful tree
of nodes (the Abstract Syntax Tree);
* Eventually, the *compiler* transforms the AST into PHP code.
* **Evaluate** the template: It basically means calling the ``display()``
method of the compiled template and passing it the context.
The Lexer
---------
The lexer tokenizes a template source code into a token stream (each token is
an instance of ``Twig_Token``, and the stream is an instance of
``Twig_TokenStream``). The default lexer recognizes 13 different token types:
* ``Twig_Token::BLOCK_START_TYPE``, ``Twig_Token::BLOCK_END_TYPE``: Delimiters for blocks (``{% %}``)
* ``Twig_Token::VAR_START_TYPE``, ``Twig_Token::VAR_END_TYPE``: Delimiters for variables (``{{ }}``)
* ``Twig_Token::TEXT_TYPE``: A text outside an expression;
* ``Twig_Token::NAME_TYPE``: A name in an expression;
* ``Twig_Token::NUMBER_TYPE``: A number in an expression;
* ``Twig_Token::STRING_TYPE``: A string in an expression;
* ``Twig_Token::OPERATOR_TYPE``: An operator;
* ``Twig_Token::PUNCTUATION_TYPE``: A punctuation sign;
* ``Twig_Token::INTERPOLATION_START_TYPE``, ``Twig_Token::INTERPOLATION_END_TYPE`` (as of Twig 1.5): Delimiters for string interpolation;
* ``Twig_Token::EOF_TYPE``: Ends of template.
You can manually convert a source code into a token stream by calling the
``tokenize()`` method of an environment::
$stream = $twig->tokenize($source, $identifier);
As the stream has a ``__toString()`` method, you can have a textual
representation of it by echoing the object::
echo $stream."\n";
Here is the output for the ``Hello {{ name }}`` template:
.. code-block:: text
TEXT_TYPE(Hello )
VAR_START_TYPE()
NAME_TYPE(name)
VAR_END_TYPE()
EOF_TYPE()
.. note::
The default lexer (``Twig_Lexer``) can be changed by calling
the ``setLexer()`` method::
$twig->setLexer($lexer);
The Parser
----------
The parser converts the token stream into an AST (Abstract Syntax Tree), or a
node tree (an instance of ``Twig_Node_Module``). The core extension defines
the basic nodes like: ``for``, ``if``, ... and the expression nodes.
You can manually convert a token stream into a node tree by calling the
``parse()`` method of an environment::
$nodes = $twig->parse($stream);
Echoing the node object gives you a nice representation of the tree::
echo $nodes."\n";
Here is the output for the ``Hello {{ name }}`` template:
.. code-block:: text
Twig_Node_Module(
Twig_Node_Text(Hello )
Twig_Node_Print(
Twig_Node_Expression_Name(name)
)
)
.. note::
The default parser (``Twig_TokenParser``) can be changed by calling the
``setParser()`` method::
$twig->setParser($parser);
The Compiler
------------
The last step is done by the compiler. It takes a node tree as an input and
generates PHP code usable for runtime execution of the template.
You can manually compile a node tree to PHP code with the ``compile()`` method
of an environment::
$php = $twig->compile($nodes);
The generated template for a ``Hello {{ name }}`` template reads as follows
(the actual output can differ depending on the version of Twig you are
using)::
/* Hello {{ name }} */
class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template
{
protected function doDisplay(array $context, array $blocks = array())
{
// line 1
echo "Hello ";
echo twig_escape_filter($this->env, isset($context["name"]) ? $context["name"] : null), "html", null, true);
}
// some more code
}
.. note::
The default compiler (``Twig_Compiler``) can be changed by calling the
``setCompiler()`` method::
$twig->setCompiler($compiler);
Twig-1.23.1/doc/intro.rst 0000664 0000000 0000000 00000005164 12616650102 0015147 0 ustar 00root root 0000000 0000000 Introduction
============
This is the documentation for Twig, the flexible, fast, and secure template
engine for PHP.
If you have any exposure to other text-based template languages, such as
Smarty, Django, or Jinja, you should feel right at home with Twig. It's both
designer and developer friendly by sticking to PHP's principles and adding
functionality useful for templating environments.
The key-features are...
* *Fast*: Twig compiles templates down to plain optimized PHP code. The
overhead compared to regular PHP code was reduced to the very minimum.
* *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This
allows Twig to be used as a template language for applications where users
may modify the template design.
* *Flexible*: Twig is powered by a flexible lexer and parser. This allows the
developer to define its own custom tags and filters, and create its own DSL.
Twig is used by many Open-Source projects like Symfony, Drupal8, eZPublish,
phpBB, Piwik, OroCRM, and many frameworks have support for it as well like
Slim, Yii, Laravel, Codeigniter, and Kohana, just to name a few.
Prerequisites
-------------
Twig needs at least **PHP 5.2.7** to run.
Installation
------------
The recommended way to install Twig is via Composer:
.. code-block:: bash
composer require "twig/twig:~1.0"
.. note::
To learn more about the other installation methods, read the
:doc:`installation` chapter; it also explains how to install
the Twig C extension.
Basic API Usage
---------------
This section gives you a brief introduction to the PHP API for Twig.
.. code-block:: php
require_once '/path/to/vendor/autoload.php';
$loader = new Twig_Loader_Array(array(
'index' => 'Hello {{ name }}!',
));
$twig = new Twig_Environment($loader);
echo $twig->render('index', array('name' => 'Fabien'));
Twig uses a loader (``Twig_Loader_Array``) to locate templates, and an
environment (``Twig_Environment``) to store the configuration.
The ``render()`` method loads the template passed as a first argument and
renders it with the variables passed as a second argument.
As templates are generally stored on the filesystem, Twig also comes with a
filesystem loader::
$loader = new Twig_Loader_Filesystem('/path/to/templates');
$twig = new Twig_Environment($loader, array(
'cache' => '/path/to/compilation_cache',
));
echo $twig->render('index.html', array('name' => 'Fabien'));
.. tip::
If you are not using Composer, use the Twig built-in autoloader::
require_once '/path/to/lib/Twig/Autoloader.php';
Twig_Autoloader::register();
Twig-1.23.1/doc/recipes.rst 0000664 0000000 0000000 00000041165 12616650102 0015447 0 ustar 00root root 0000000 0000000 Recipes
=======
.. _deprecation-notices:
Displaying Deprecation Notices
------------------------------
.. versionadded:: 1.21
This works as of Twig 1.21.
Deprecated features generate deprecation notices (via a call to the
``trigger_error()`` PHP function). By default, they are silenced and never
displayed nor logged.
To easily remove all deprecated feature usages from your templates, write and
run a script along the lines of the following::
require_once __DIR__.'/vendor/autoload.php';
$twig = create_your_twig_env();
$deprecations = new Twig_Util_DeprecationCollector($twig);
print_r($deprecations->collectDir(__DIR__.'/templates'));
The ``collectDir()`` method compiles all templates found in a directory,
catches deprecation notices, and return them.
.. tip::
If your templates are not stored on the filesystem, use the ``collect()``
method instead which takes an ``Iterator``; the iterator must return
template names as keys and template contents as values (as done by
``Twig_Util_TemplateDirIterator``).
However, this code won't find all deprecations (like using deprecated some Twig
classes). To catch all notices, register a custom error handler like the one
below::
$deprecations = array();
set_error_handler(function ($type, $msg) use (&$deprecations) {
if (E_USER_DEPRECATED === $type) {
$deprecations[] = $msg;
}
});
// run your application
print_r($deprecations);
Note that most deprecation notices are triggered during **compilation**, so
they won't be generated when templates are already cached.
.. tip::
If you want to manage the deprecation notices from your PHPUnit tests, have
a look at the `symfony/phpunit-bridge
`_ package, which eases the
process a lot.
Making a Layout conditional
---------------------------
Working with Ajax means that the same content is sometimes displayed as is,
and sometimes decorated with a layout. As Twig layout template names can be
any valid expression, you can pass a variable that evaluates to ``true`` when
the request is made via Ajax and choose the layout accordingly:
.. code-block:: jinja
{% extends request.ajax ? "base_ajax.html" : "base.html" %}
{% block content %}
This is the content to be displayed.
{% endblock %}
Making an Include dynamic
-------------------------
When including a template, its name does not need to be a string. For
instance, the name can depend on the value of a variable:
.. code-block:: jinja
{% include var ~ '_foo.html' %}
If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be
rendered.
As a matter of fact, the template name can be any valid expression, such as
the following:
.. code-block:: jinja
{% include var|default('index') ~ '_foo.html' %}
Overriding a Template that also extends itself
----------------------------------------------
A template can be customized in two different ways:
* *Inheritance*: A template *extends* a parent template and overrides some
blocks;
* *Replacement*: If you use the filesystem loader, Twig loads the first
template it finds in a list of configured directories; a template found in a
directory *replaces* another one from a directory further in the list.
But how do you combine both: *replace* a template that also extends itself
(aka a template in a directory further in the list)?
Let's say that your templates are loaded from both ``.../templates/mysite``
and ``.../templates/default`` in this order. The ``page.twig`` template,
stored in ``.../templates/default`` reads as follows:
.. code-block:: jinja
{# page.twig #}
{% extends "layout.twig" %}
{% block content %}
{% endblock %}
You can replace this template by putting a file with the same name in
``.../templates/mysite``. And if you want to extend the original template, you
might be tempted to write the following:
.. code-block:: jinja
{# page.twig in .../templates/mysite #}
{% extends "page.twig" %} {# from .../templates/default #}
Of course, this will not work as Twig will always load the template from
``.../templates/mysite``.
It turns out it is possible to get this to work, by adding a directory right
at the end of your template directories, which is the parent of all of the
other directories: ``.../templates`` in our case. This has the effect of
making every template file within our system uniquely addressable. Most of the
time you will use the "normal" paths, but in the special case of wanting to
extend a template with an overriding version of itself we can reference its
parent's full, unambiguous template path in the extends tag:
.. code-block:: jinja
{# page.twig in .../templates/mysite #}
{% extends "default/page.twig" %} {# from .../templates #}
.. note::
This recipe was inspired by the following Django wiki page:
http://code.djangoproject.com/wiki/ExtendingTemplates
Customizing the Syntax
----------------------
Twig allows some syntax customization for the block delimiters. It's not
recommended to use this feature as templates will be tied with your custom
syntax. But for specific projects, it can make sense to change the defaults.
To change the block delimiters, you need to create your own lexer object::
$twig = new Twig_Environment();
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('{#', '#}'),
'tag_block' => array('{%', '%}'),
'tag_variable' => array('{{', '}}'),
'interpolation' => array('#{', '}'),
));
$twig->setLexer($lexer);
Here are some configuration example that simulates some other template engines
syntax::
// Ruby erb syntax
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('<%#', '%>'),
'tag_block' => array('<%', '%>'),
'tag_variable' => array('<%=', '%>'),
));
// SGML Comment Syntax
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array(''),
'tag_block' => array(''),
'tag_variable' => array('${', '}'),
));
// Smarty like
$lexer = new Twig_Lexer($twig, array(
'tag_comment' => array('{*', '*}'),
'tag_block' => array('{', '}'),
'tag_variable' => array('{$', '}'),
));
Using dynamic Object Properties
-------------------------------
When Twig encounters a variable like ``article.title``, it tries to find a
``title`` public property in the ``article`` object.
It also works if the property does not exist but is rather defined dynamically
thanks to the magic ``__get()`` method; you just need to also implement the
``__isset()`` magic method like shown in the following snippet of code::
class Article
{
public function __get($name)
{
if ('title' == $name) {
return 'The title';
}
// throw some kind of error
}
public function __isset($name)
{
if ('title' == $name) {
return true;
}
return false;
}
}
Accessing the parent Context in Nested Loops
--------------------------------------------
Sometimes, when using nested loops, you need to access the parent context. The
parent context is always accessible via the ``loop.parent`` variable. For
instance, if you have the following template data::
$data = array(
'topics' => array(
'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'),
'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'),
),
);
And the following template to display all messages in all topics:
.. code-block:: jinja
{% for topic, messages in topics %}
* {{ loop.index }}: {{ topic }}
{% for message in messages %}
- {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
{% endfor %}
{% endfor %}
The output will be similar to:
.. code-block:: text
* 1: topic1
- 1.1: The message 1 of topic 1
- 1.2: The message 2 of topic 1
* 2: topic2
- 2.1: The message 1 of topic 2
- 2.2: The message 2 of topic 2
In the inner loop, the ``loop.parent`` variable is used to access the outer
context. So, the index of the current ``topic`` defined in the outer for loop
is accessible via the ``loop.parent.loop.index`` variable.
Defining undefined Functions and Filters on the Fly
---------------------------------------------------
When a function (or a filter) is not defined, Twig defaults to throw a
``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any
valid PHP callable) which should return a function (or a filter).
For filters, register callbacks with ``registerUndefinedFilterCallback()``.
For functions, use ``registerUndefinedFunctionCallback()``::
// auto-register all native PHP functions as Twig functions
// don't try this at home as it's not secure at all!
$twig->registerUndefinedFunctionCallback(function ($name) {
if (function_exists($name)) {
return new Twig_Function_Function($name);
}
return false;
});
If the callable is not able to return a valid function (or filter), it must
return ``false``.
If you register more than one callback, Twig will call them in turn until one
does not return ``false``.
.. tip::
As the resolution of functions and filters is done during compilation,
there is no overhead when registering these callbacks.
Validating the Template Syntax
------------------------------
When template code is provided by a third-party (through a web interface for
instance), it might be interesting to validate the template syntax before
saving it. If the template code is stored in a `$template` variable, here is
how you can do it::
try {
$twig->parse($twig->tokenize($template));
// the $template is valid
} catch (Twig_Error_Syntax $e) {
// $template contains one or more syntax errors
}
If you iterate over a set of files, you can pass the filename to the
``tokenize()`` method to get the filename in the exception message::
foreach ($files as $file) {
try {
$twig->parse($twig->tokenize($template, $file));
// the $template is valid
} catch (Twig_Error_Syntax $e) {
// $template contains one or more syntax errors
}
}
.. note::
This method won't catch any sandbox policy violations because the policy
is enforced during template rendering (as Twig needs the context for some
checks like allowed methods on objects).
Refreshing modified Templates when OPcache or APC is enabled
------------------------------------------------------------
When using OPcache with ``opcache.validate_timestamps`` set to ``0`` or APC
with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing the template
cache won't update the cache.
To get around this, force Twig to invalidate the bytecode cache::
$twig = new Twig_Environment($loader, array(
'cache' => new Twig_Cache_Filesystem('/some/cache/path', Twig_Cache_Filesystem::FORCE_BYTECODE_INVALIDATION),
// ...
));
.. note::
Before Twig 1.22, you should extend ``Twig_Environment`` instead::
class OpCacheAwareTwigEnvironment extends Twig_Environment
{
protected function writeCacheFile($file, $content)
{
parent::writeCacheFile($file, $content);
// Compile cached file into bytecode cache
if (function_exists('opcache_invalidate')) {
opcache_invalidate($file, true);
} elseif (function_exists('apc_compile_file')) {
apc_compile_file($file);
}
}
}
Reusing a stateful Node Visitor
-------------------------------
When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to
visit *all* templates it compiles. If you need to keep some state information
around, you probably want to reset it when visiting a new template.
This can be easily achieved with the following code::
protected $someTemplateState = array();
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
// reset the state as we are entering a new template
$this->someTemplateState = array();
}
// ...
return $node;
}
Using a Database to store Templates
-----------------------------------
If you are developing a CMS, templates are usually stored in a database. This
recipe gives you a simple PDO template loader you can use as a starting point
for your own.
First, let's create a temporary in-memory SQLite3 database to work with::
$dbh = new PDO('sqlite::memory:');
$dbh->exec('CREATE TABLE templates (name STRING, source STRING, last_modified INTEGER)');
$base = '{% block content %}{% endblock %}';
$index = '
{% extends "base.twig" %}
{% block content %}Hello {{ name }}{% endblock %}
';
$now = time();
$dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('base.twig', '$base', $now)");
$dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('index.twig', '$index', $now)");
We have created a simple ``templates`` table that hosts two templates:
``base.twig`` and ``index.twig``.
Now, let's define a loader able to use this database::
class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
protected $dbh;
public function __construct(PDO $dbh)
{
$this->dbh = $dbh;
}
public function getSource($name)
{
if (false === $source = $this->getValue('source', $name)) {
throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
}
return $source;
}
// Twig_ExistsLoaderInterface as of Twig 1.11
public function exists($name)
{
return $name === $this->getValue('name', $name);
}
public function getCacheKey($name)
{
return $name;
}
public function isFresh($name, $time)
{
if (false === $lastModified = $this->getValue('last_modified', $name)) {
return false;
}
return $lastModified <= $time;
}
protected function getValue($column, $name)
{
$sth = $this->dbh->prepare('SELECT '.$column.' FROM templates WHERE name = :name');
$sth->execute(array(':name' => (string) $name));
return $sth->fetchColumn();
}
}
Finally, here is an example on how you can use it::
$loader = new DatabaseTwigLoader($dbh);
$twig = new Twig_Environment($loader);
echo $twig->render('index.twig', array('name' => 'Fabien'));
Using different Template Sources
--------------------------------
This recipe is the continuation of the previous one. Even if you store the
contributed templates in a database, you might want to keep the original/base
templates on the filesystem. When templates can be loaded from different
sources, you need to use the ``Twig_Loader_Chain`` loader.
As you can see in the previous recipe, we reference the template in the exact
same way as we would have done it with a regular filesystem loader. This is
the key to be able to mix and match templates coming from the database, the
filesystem, or any other loader for that matter: the template name should be a
logical name, and not the path from the filesystem::
$loader1 = new DatabaseTwigLoader($dbh);
$loader2 = new Twig_Loader_Array(array(
'base.twig' => '{% block content %}{% endblock %}',
));
$loader = new Twig_Loader_Chain(array($loader1, $loader2));
$twig = new Twig_Environment($loader);
echo $twig->render('index.twig', array('name' => 'Fabien'));
Now that the ``base.twig`` templates is defined in an array loader, you can
remove it from the database, and everything else will still work as before.
Loading a Template from a String
--------------------------------
From a template, you can easily load a template stored in a string via the
``template_from_string`` function (available as of Twig 1.11 via the
``Twig_Extension_StringLoader`` extension)::
.. code-block:: jinja
{{ include(template_from_string("Hello {{ name }}")) }}
From PHP, it's also possible to load a template stored in a string via
``Twig_Environment::createTemplate()`` (available as of Twig 1.18)::
$template = $twig->createTemplate('hello {{ name }}');
echo $template->render(array('name' => 'Fabien'));
.. note::
Never use the ``Twig_Loader_String`` loader, which has severe limitations.
.. _callback: http://www.php.net/manual/en/function.is-callable.php
Twig-1.23.1/doc/tags/ 0000775 0000000 0000000 00000000000 12616650102 0014212 5 ustar 00root root 0000000 0000000 Twig-1.23.1/doc/tags/autoescape.rst 0000664 0000000 0000000 00000004426 12616650102 0017103 0 ustar 00root root 0000000 0000000 ``autoescape``
==============
Whether automatic escaping is enabled or not, you can mark a section of a
template to be escaped or not by using the ``autoescape`` tag:
.. code-block:: jinja
{# The following syntax works as of Twig 1.8 -- see the note below for previous versions #}
{% autoescape %}
Everything will be automatically escaped in this block
using the HTML strategy
{% endautoescape %}
{% autoescape 'html' %}
Everything will be automatically escaped in this block
using the HTML strategy
{% endautoescape %}
{% autoescape 'js' %}
Everything will be automatically escaped in this block
using the js escaping strategy
{% endautoescape %}
{% autoescape false %}
Everything will be outputted as is in this block
{% endautoescape %}
.. note::
Before Twig 1.8, the syntax was different:
.. code-block:: jinja
{% autoescape true %}
Everything will be automatically escaped in this block
using the HTML strategy
{% endautoescape %}
{% autoescape false %}
Everything will be outputted as is in this block
{% endautoescape %}
{% autoescape true js %}
Everything will be automatically escaped in this block
using the js escaping strategy
{% endautoescape %}
When automatic escaping is enabled everything is escaped by default except for
values explicitly marked as safe. Those can be marked in the template by using
the :doc:`raw<../filters/raw>` filter:
.. code-block:: jinja
{% autoescape %}
{{ safe_value|raw }}
{% endautoescape %}
Functions returning template data (like :doc:`macros` and
:doc:`parent<../functions/parent>`) always return safe markup.
.. note::
Twig is smart enough to not escape an already escaped value by the
:doc:`escape<../filters/escape>` filter.
.. note::
Twig does not escape static expressions:
.. code-block:: jinja
{% set hello = "Hello " %}
{{ hello }}
{{ "world " }}
Will be rendered "Hello **world**".
.. note::
The chapter :doc:`Twig for Developers<../api>` gives more information
about when and how automatic escaping is applied.
Twig-1.23.1/doc/tags/block.rst 0000664 0000000 0000000 00000000700 12616650102 0016033 0 ustar 00root root 0000000 0000000 ``block``
=========
Blocks are used for inheritance and act as placeholders and replacements at
the same time. They are documented in detail in the documentation for the
:doc:`extends<../tags/extends>` tag.
Block names should consist of alphanumeric characters, and underscores. Dashes
are not permitted.
.. seealso:: :doc:`block<../functions/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`, :doc:`extends<../tags/extends>`
Twig-1.23.1/doc/tags/do.rst 0000664 0000000 0000000 00000000362 12616650102 0015347 0 ustar 00root root 0000000 0000000 ``do``
======
.. versionadded:: 1.5
The ``do`` tag was added in Twig 1.5.
The ``do`` tag works exactly like the regular variable expression (``{{ ...
}}``) just that it doesn't print anything:
.. code-block:: jinja
{% do 1 + 2 %}
Twig-1.23.1/doc/tags/embed.rst 0000664 0000000 0000000 00000015646 12616650102 0016034 0 ustar 00root root 0000000 0000000 ``embed``
=========
.. versionadded:: 1.8
The ``embed`` tag was added in Twig 1.8.
The ``embed`` tag combines the behaviour of :doc:`include` and
:doc:`extends`.
It allows you to include another template's contents, just like ``include``
does. But it also allows you to override any block defined inside the
included template, like when extending a template.
Think of an embedded template as a "micro layout skeleton".
.. code-block:: jinja
{% embed "teasers_skeleton.twig" %}
{# These blocks are defined in "teasers_skeleton.twig" #}
{# and we override them right here: #}
{% block left_teaser %}
Some content for the left teaser box
{% endblock %}
{% block right_teaser %}
Some content for the right teaser box
{% endblock %}
{% endembed %}
The ``embed`` tag takes the idea of template inheritance to the level of
content fragments. While template inheritance allows for "document skeletons",
which are filled with life by child templates, the ``embed`` tag allows you to
create "skeletons" for smaller units of content and re-use and fill them
anywhere you like.
Since the use case may not be obvious, let's look at a simplified example.
Imagine a base template shared by multiple HTML pages, defining a single block
named "content":
.. code-block:: text
┌─── page layout ─────────────────────┐
│ │
│ ┌── block "content" ──┐ │
│ │ │ │
│ │ │ │
│ │ (child template to │ │
│ │ put content here) │ │
│ │ │ │
│ │ │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────┘
Some pages ("foo" and "bar") share the same content structure -
two vertically stacked boxes:
.. code-block:: text
┌─── page layout ─────────────────────┐
│ │
│ ┌── block "content" ──┐ │
│ │ ┌─ block "top" ───┐ │ │
│ │ │ │ │ │
│ │ └─────────────────┘ │ │
│ │ ┌─ block "bottom" ┐ │ │
│ │ │ │ │ │
│ │ └─────────────────┘ │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────┘
While other pages ("boom" and "baz") share a different content structure -
two boxes side by side:
.. code-block:: text
┌─── page layout ─────────────────────┐
│ │
│ ┌── block "content" ──┐ │
│ │ │ │
│ │ ┌ block ┐ ┌ block ┐ │ │
│ │ │"left" │ │"right"│ │ │
│ │ │ │ │ │ │ │
│ │ │ │ │ │ │ │
│ │ └───────┘ └───────┘ │ │
│ └─────────────────────┘ │
│ │
└─────────────────────────────────────┘
Without the ``embed`` tag, you have two ways to design your templates:
* Create two "intermediate" base templates that extend the master layout
template: one with vertically stacked boxes to be used by the "foo" and
"bar" pages and another one with side-by-side boxes for the "boom" and
"baz" pages.
* Embed the markup for the top/bottom and left/right boxes into each page
template directly.
These two solutions do not scale well because they each have a major drawback:
* The first solution may indeed work for this simplified example. But imagine
we add a sidebar, which may again contain different, recurring structures
of content. Now we would need to create intermediate base templates for
all occurring combinations of content structure and sidebar structure...
and so on.
* The second solution involves duplication of common code with all its negative
consequences: any change involves finding and editing all affected copies
of the structure, correctness has to be verified for each copy, copies may
go out of sync by careless modifications etc.
In such a situation, the ``embed`` tag comes in handy. The common layout
code can live in a single base template, and the two different content structures,
let's call them "micro layouts" go into separate templates which are embedded
as necessary:
Page template ``foo.twig``:
.. code-block:: jinja
{% extends "layout_skeleton.twig" %}
{% block content %}
{% embed "vertical_boxes_skeleton.twig" %}
{% block top %}
Some content for the top box
{% endblock %}
{% block bottom %}
Some content for the bottom box
{% endblock %}
{% endembed %}
{% endblock %}
And here is the code for ``vertical_boxes_skeleton.twig``:
.. code-block:: html+jinja
{% block top %}
Top box default content
{% endblock %}
{% block bottom %}
Bottom box default content
{% endblock %}
The goal of the ``vertical_boxes_skeleton.twig`` template being to factor
out the HTML markup for the boxes.
The ``embed`` tag takes the exact same arguments as the ``include`` tag:
.. code-block:: jinja
{% embed "base" with {'foo': 'bar'} %}
...
{% endembed %}
{% embed "base" with {'foo': 'bar'} only %}
...
{% endembed %}
{% embed "base" ignore missing %}
...
{% endembed %}
.. warning::
As embedded templates do not have "names", auto-escaping strategies based
on the template "filename" won't work as expected if you change the
context (for instance, if you embed a CSS/JavaScript template into an HTML
one). In that case, explicitly set the default auto-escaping strategy with
the ``autoescape`` tag.
.. seealso:: :doc:`include<../tags/include>`
Twig-1.23.1/doc/tags/extends.rst 0000664 0000000 0000000 00000016403 12616650102 0016422 0 ustar 00root root 0000000 0000000 ``extends``
===========
The ``extends`` tag can be used to extend a template from another one.
.. note::
Like PHP, Twig does not support multiple inheritance. So you can only have
one extends tag called per rendering. However, Twig supports horizontal
:doc:`reuse`.
Let's define a base template, ``base.html``, which defines a simple HTML
skeleton document:
.. code-block:: html+jinja
{% block head %}
{% block title %}{% endblock %} - My Webpage
{% endblock %}
{% block content %}{% endblock %}
In this example, the :doc:`block` tags define four blocks that child
templates can fill in.
All the ``block`` tag does is to tell the template engine that a child
template may override those portions of the template.
Child Template
--------------
A child template might look like this:
.. code-block:: jinja
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
{% endblock %}
{% block content %}
Index
Welcome on my awesome homepage.
{% endblock %}
The ``extends`` tag is the key here. It tells the template engine that this
template "extends" another template. When the template system evaluates this
template, first it locates the parent. The extends tag should be the first tag
in the template.
Note that since the child template doesn't define the ``footer`` block, the
value from the parent template is used instead.
You can't define multiple ``block`` tags with the same name in the same
template. This limitation exists because a block tag works in "both"
directions. That is, a block tag doesn't just provide a hole to fill - it also
defines the content that fills the hole in the *parent*. If there were two
similarly-named ``block`` tags in a template, that template's parent wouldn't
know which one of the blocks' content to use.
If you want to print a block multiple times you can however use the
``block`` function:
.. code-block:: jinja
{% block title %}{% endblock %}
{{ block('title') }}
{% block body %}{% endblock %}
Parent Blocks
-------------
It's possible to render the contents of the parent block by using the
:doc:`parent<../functions/parent>` function. This gives back the results of
the parent block:
.. code-block:: jinja
{% block sidebar %}
Table Of Contents
...
{{ parent() }}
{% endblock %}
Named Block End-Tags
--------------------
Twig allows you to put the name of the block after the end tag for better
readability:
.. code-block:: jinja
{% block sidebar %}
{% block inner_sidebar %}
...
{% endblock inner_sidebar %}
{% endblock sidebar %}
Of course, the name after the ``endblock`` word must match the block name.
Block Nesting and Scope
-----------------------
Blocks can be nested for more complex layouts. Per default, blocks have access
to variables from outer scopes:
.. code-block:: jinja
{% for item in seq %}
{% block loop_item %}{{ item }}{% endblock %}
{% endfor %}
Block Shortcuts
---------------
For blocks with few content, it's possible to use a shortcut syntax. The
following constructs do the same:
.. code-block:: jinja
{% block title %}
{{ page_title|title }}
{% endblock %}
.. code-block:: jinja
{% block title page_title|title %}
Dynamic Inheritance
-------------------
Twig supports dynamic inheritance by using a variable as the base template:
.. code-block:: jinja
{% extends some_var %}
If the variable evaluates to a ``Twig_Template`` object, Twig will use it as
the parent template::
// {% extends layout %}
$layout = $twig->loadTemplate('some_layout_template.twig');
$twig->display('template.twig', array('layout' => $layout));
.. versionadded:: 1.2
The possibility to pass an array of templates has been added in Twig 1.2.
You can also provide a list of templates that are checked for existence. The
first template that exists will be used as a parent:
.. code-block:: jinja
{% extends ['layout.html', 'base_layout.html'] %}
Conditional Inheritance
-----------------------
As the template name for the parent can be any valid Twig expression, it's
possible to make the inheritance mechanism conditional:
.. code-block:: jinja
{% extends standalone ? "minimum.html" : "base.html" %}
In this example, the template will extend the "minimum.html" layout template
if the ``standalone`` variable evaluates to ``true``, and "base.html"
otherwise.
How do blocks work?
-------------------
A block provides a way to change how a certain part of a template is rendered
but it does not interfere in any way with the logic around it.
Let's take the following example to illustrate how a block works and more
importantly, how it does not work:
.. code-block:: jinja
{# base.twig #}
{% for post in posts %}
{% block post %}
{{ post.title }}
{{ post.body }}
{% endblock %}
{% endfor %}
If you render this template, the result would be exactly the same with or
without the ``block`` tag. The ``block`` inside the ``for`` loop is just a way
to make it overridable by a child template:
.. code-block:: jinja
{# child.twig #}
{% extends "base.twig" %}
{% block post %}
{% endblock %}
Now, when rendering the child template, the loop is going to use the block
defined in the child template instead of the one defined in the base one; the
executed template is then equivalent to the following one:
.. code-block:: jinja
{% for post in posts %}
{% endfor %}
Let's take another example: a block included within an ``if`` statement:
.. code-block:: jinja
{% if posts is empty %}
{% block head %}
{{ parent() }}
{% endblock head %}
{% endif %}
Contrary to what you might think, this template does not define a block
conditionally; it just makes overridable by a child template the output of
what will be rendered when the condition is ``true``.
If you want the output to be displayed conditionally, use the following
instead:
.. code-block:: jinja
{% block head %}
{{ parent() }}
{% if posts is empty %}
{% endif %}
{% endblock head %}
.. seealso:: :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`
Twig-1.23.1/doc/tags/filter.rst 0000664 0000000 0000000 00000000717 12616650102 0016236 0 ustar 00root root 0000000 0000000 ``filter``
==========
Filter sections allow you to apply regular Twig filters on a block of template
data. Just wrap the code in the special ``filter`` section:
.. code-block:: jinja
{% filter upper %}
This text becomes uppercase
{% endfilter %}
You can also chain filters:
.. code-block:: jinja
{% filter lower|escape %}
SOME TEXT
{% endfilter %}
{# outputs "<strong>some text</strong>" #}
Twig-1.23.1/doc/tags/flush.rst 0000664 0000000 0000000 00000000432 12616650102 0016064 0 ustar 00root root 0000000 0000000 ``flush``
=========
.. versionadded:: 1.5
The flush tag was added in Twig 1.5.
The ``flush`` tag tells Twig to flush the output buffer:
.. code-block:: jinja
{% flush %}
.. note::
Internally, Twig uses the PHP `flush`_ function.
.. _`flush`: http://php.net/flush
Twig-1.23.1/doc/tags/for.rst 0000664 0000000 0000000 00000011051 12616650102 0015530 0 ustar 00root root 0000000 0000000 ``for``
=======
Loop over each item in a sequence. For example, to display a list of users
provided in a variable called ``users``:
.. code-block:: jinja
Members
{% for user in users %}
{{ user.username|e }}
{% endfor %}
.. note::
A sequence can be either an array or an object implementing the
``Traversable`` interface.
If you do need to iterate over a sequence of numbers, you can use the ``..``
operator:
.. code-block:: jinja
{% for i in 0..10 %}
* {{ i }}
{% endfor %}
The above snippet of code would print all numbers from 0 to 10.
It can be also useful with letters:
.. code-block:: jinja
{% for letter in 'a'..'z' %}
* {{ letter }}
{% endfor %}
The ``..`` operator can take any expression at both sides:
.. code-block:: jinja
{% for letter in 'a'|upper..'z'|upper %}
* {{ letter }}
{% endfor %}
.. tip:
If you need a step different from 1, you can use the ``range`` function
instead.
The `loop` variable
-------------------
Inside of a ``for`` loop block you can access some special variables:
===================== =============================================================
Variable Description
===================== =============================================================
``loop.index`` The current iteration of the loop. (1 indexed)
``loop.index0`` The current iteration of the loop. (0 indexed)
``loop.revindex`` The number of iterations from the end of the loop (1 indexed)
``loop.revindex0`` The number of iterations from the end of the loop (0 indexed)
``loop.first`` True if first iteration
``loop.last`` True if last iteration
``loop.length`` The number of items in the sequence
``loop.parent`` The parent context
===================== =============================================================
.. code-block:: jinja
{% for user in users %}
{{ loop.index }} - {{ user.username }}
{% endfor %}
.. note::
The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and
``loop.last`` variables are only available for PHP arrays, or objects that
implement the ``Countable`` interface. They are also not available when
looping with a condition.
.. versionadded:: 1.2
The ``if`` modifier support has been added in Twig 1.2.
Adding a condition
------------------
Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You
can however filter the sequence during iteration which allows you to skip
items. The following example skips all the users which are not active:
.. code-block:: jinja
{% for user in users if user.active %}
{{ user.username|e }}
{% endfor %}
The advantage is that the special loop variable will count correctly thus not
counting the users not iterated over. Keep in mind that properties like
``loop.last`` will not be defined when using loop conditions.
.. note::
Using the ``loop`` variable within the condition is not recommended as it
will probably not be doing what you expect it to. For instance, adding a
condition like ``loop.index > 4`` won't work as the index is only
incremented when the condition is true (so the condition will never
match).
The `else` Clause
-----------------
If no iteration took place because the sequence was empty, you can render a
replacement block by using ``else``:
.. code-block:: jinja
{% for user in users %}
{{ user.username|e }}
{% else %}
no user found
{% endfor %}
Iterating over Keys
-------------------
By default, a loop iterates over the values of the sequence. You can iterate
on keys by using the ``keys`` filter:
.. code-block:: jinja
Members
{% for key in users|keys %}
{{ key }}
{% endfor %}
Iterating over Keys and Values
------------------------------
You can also access both keys and values:
.. code-block:: jinja
Members
{% for key, user in users %}
{{ key }}: {{ user.username|e }}
{% endfor %}
Iterating over a Subset
-----------------------
You might want to iterate over a subset of values. This can be achieved using
the :doc:`slice <../filters/slice>` filter:
.. code-block:: jinja
Top Ten Members
{% for user in users|slice(0, 10) %}
{{ user.username|e }}
{% endfor %}
Twig-1.23.1/doc/tags/from.rst 0000664 0000000 0000000 00000000423 12616650102 0015706 0 ustar 00root root 0000000 0000000 ``from``
========
The ``from`` tag imports :doc:`macro<../tags/macro>` names into the current
namespace. The tag is documented in detail in the documentation for the
:doc:`import<../tags/import>` tag.
.. seealso:: :doc:`macro<../tags/macro>`, :doc:`import<../tags/import>`
Twig-1.23.1/doc/tags/if.rst 0000664 0000000 0000000 00000003653 12616650102 0015351 0 ustar 00root root 0000000 0000000 ``if``
======
The ``if`` statement in Twig is comparable with the if statements of PHP.
In the simplest form you can use it to test if an expression evaluates to
``true``:
.. code-block:: jinja
{% if online == false %}
Our website is in maintenance mode. Please, come back later.
{% endif %}
You can also test if an array is not empty:
.. code-block:: jinja
{% if users %}
{% for user in users %}
{{ user.username|e }}
{% endfor %}
{% endif %}
.. note::
If you want to test if the variable is defined, use ``if users is
defined`` instead.
You can also use ``not`` to check for values that evaluate to ``false``:
.. code-block:: jinja
{% if not user.subscribed %}
You are not subscribed to our mailing list.
{% endif %}
For multiple conditions, ``and`` and ``or`` can be used:
.. code-block:: jinja
{% if temperature > 18 and temperature < 27 %}
It's a nice day for a walk in the park.
{% endif %}
For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can
use more complex ``expressions`` there too:
.. code-block:: jinja
{% if kenny.sick %}
Kenny is sick.
{% elseif kenny.dead %}
You killed Kenny! You bastard!!!
{% else %}
Kenny looks okay --- so far
{% endif %}
.. note::
The rules to determine if an expression is ``true`` or ``false`` are the
same as in PHP; here are the edge cases rules:
====================== ====================
Value Boolean evaluation
====================== ====================
empty string false
numeric zero false
whitespace-only string true
empty array false
null false
non-empty array true
object true
====================== ====================
Twig-1.23.1/doc/tags/import.rst 0000664 0000000 0000000 00000003336 12616650102 0016263 0 ustar 00root root 0000000 0000000 ``import``
==========
Twig supports putting often used code into :doc:`macros<../tags/macro>`. These
macros can go into different templates and get imported from there.
There are two ways to import templates. You can import the complete template
into a variable or request specific macros from it.
Imagine we have a helper module that renders forms (called ``forms.html``):
.. code-block:: jinja
{% macro input(name, value, type, size) %}
{% endmacro %}
{% macro textarea(name, value, rows, cols) %}
{% endmacro %}
The easiest and most flexible is importing the whole module into a variable.
That way you can access the attributes:
.. code-block:: jinja
{% import 'forms.html' as forms %}
Username
{{ forms.input('username') }}
Password
{{ forms.input('password', null, 'password') }}
{{ forms.textarea('comment') }}
Alternatively you can import names from the template into the current
namespace:
.. code-block:: jinja
{% from 'forms.html' import input as input_field, textarea %}
Username
{{ input_field('username') }}
Password
{{ input_field('password', '', 'password') }}
{{ textarea('comment') }}
.. tip::
To import macros from the current file, use the special ``_self`` variable
for the source.
.. seealso:: :doc:`macro<../tags/macro>`, :doc:`from<../tags/from>`
Twig-1.23.1/doc/tags/include.rst 0000664 0000000 0000000 00000005216 12616650102 0016373 0 ustar 00root root 0000000 0000000 ``include``
===========
The ``include`` statement includes a template and returns the rendered content
of that file into the current namespace:
.. code-block:: jinja
{% include 'header.html' %}
Body
{% include 'footer.html' %}
Included templates have access to the variables of the active context.
If you are using the filesystem loader, the templates are looked for in the
paths defined by it.
You can add additional variables by passing them after the ``with`` keyword:
.. code-block:: jinja
{# template.html will have access to the variables from the current context and the additional ones provided #}
{% include 'template.html' with {'foo': 'bar'} %}
{% set vars = {'foo': 'bar'} %}
{% include 'template.html' with vars %}
You can disable access to the context by appending the ``only`` keyword:
.. code-block:: jinja
{# only the foo variable will be accessible #}
{% include 'template.html' with {'foo': 'bar'} only %}
.. code-block:: jinja
{# no variables will be accessible #}
{% include 'template.html' only %}
.. tip::
When including a template created by an end user, you should consider
sandboxing it. More information in the :doc:`Twig for Developers<../api>`
chapter and in the :doc:`sandbox<../tags/sandbox>` tag documentation.
The template name can be any valid Twig expression:
.. code-block:: jinja
{% include some_var %}
{% include ajax ? 'ajax.html' : 'not_ajax.html' %}
And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
directly::
// {% include template %}
$template = $twig->loadTemplate('some_template.twig');
$twig->loadTemplate('template.twig')->display(array('template' => $template));
.. versionadded:: 1.2
The ``ignore missing`` feature has been added in Twig 1.2.
You can mark an include with ``ignore missing`` in which case Twig will ignore
the statement if the template to be included does not exist. It has to be
placed just after the template name. Here some valid examples:
.. code-block:: jinja
{% include 'sidebar.html' ignore missing %}
{% include 'sidebar.html' ignore missing with {'foo': 'bar'} %}
{% include 'sidebar.html' ignore missing only %}
.. versionadded:: 1.2
The possibility to pass an array of templates has been added in Twig 1.2.
You can also provide a list of templates that are checked for existence before
inclusion. The first template that exists will be included:
.. code-block:: jinja
{% include ['page_detailed.html', 'page.html'] %}
If ``ignore missing`` is given, it will fall back to rendering nothing if none
of the templates exist, otherwise it will throw an exception.
Twig-1.23.1/doc/tags/index.rst 0000664 0000000 0000000 00000000345 12616650102 0016055 0 ustar 00root root 0000000 0000000 Tags
====
.. toctree::
:maxdepth: 1
autoescape
block
do
embed
extends
filter
flush
for
from
if
import
include
macro
sandbox
set
spaceless
use
verbatim
Twig-1.23.1/doc/tags/macro.rst 0000664 0000000 0000000 00000005166 12616650102 0016055 0 ustar 00root root 0000000 0000000 ``macro``
=========
Macros are comparable with functions in regular programming languages. They
are useful to put often used HTML idioms into reusable elements to not repeat
yourself.
Here is a small example of a macro that renders a form element:
.. code-block:: jinja
{% macro input(name, value, type, size) %}
{% endmacro %}
Macros differs from native PHP functions in a few ways:
* Default argument values are defined by using the ``default`` filter in the
macro body;
* Arguments of a macro are always optional.
* If extra positional arguments are passed to a macro, they end up in the
special ``varargs`` variable as a list of values.
But as with PHP functions, macros don't have access to the current template
variables.
.. tip::
You can pass the whole context as an argument by using the special
``_context`` variable.
Macros can be defined in any template, and need to be "imported" before being
used (see the documentation for the :doc:`import<../tags/import>` tag for more
information):
.. code-block:: jinja
{% import "forms.html" as forms %}
The above ``import`` call imports the "forms.html" file (which can contain only
macros, or a template and some macros), and import the functions as items of
the ``forms`` variable.
The macro can then be called at will:
.. code-block:: jinja
{{ forms.input('username') }}
{{ forms.input('password', null, 'password') }}
If macros are defined and used in the same template, you can use the
special ``_self`` variable to import them:
.. code-block:: jinja
{% import _self as forms %}
{{ forms.input('username') }}
.. warning::
When you define a macro in the template where you are going to use it, you
might be tempted to call the macro directly via ``_self.input()`` instead
of importing it; even if seems to work, this is just a side-effect of the
current implementation and it won't work anymore in Twig 2.x.
When you want to use a macro in another macro from the same file, you need to
import it locally:
.. code-block:: jinja
{% macro input(name, value, type, size) %}
{% endmacro %}
{% macro wrapped_input(name, value, type, size) %}
{% import _self as forms %}
{{ forms.input(name, value, type, size) }}
{% endmacro %}
.. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>`
Twig-1.23.1/doc/tags/sandbox.rst 0000664 0000000 0000000 00000001374 12616650102 0016407 0 ustar 00root root 0000000 0000000 ``sandbox``
===========
The ``sandbox`` tag can be used to enable the sandboxing mode for an included
template, when sandboxing is not enabled globally for the Twig environment:
.. code-block:: jinja
{% sandbox %}
{% include 'user.html' %}
{% endsandbox %}
.. warning::
The ``sandbox`` tag is only available when the sandbox extension is
enabled (see the :doc:`Twig for Developers<../api>` chapter).
.. note::
The ``sandbox`` tag can only be used to sandbox an include tag and it
cannot be used to sandbox a section of a template. The following example
won't work:
.. code-block:: jinja
{% sandbox %}
{% for i in 1..2 %}
{{ i }}
{% endfor %}
{% endsandbox %}
Twig-1.23.1/doc/tags/set.rst 0000664 0000000 0000000 00000003222 12616650102 0015536 0 ustar 00root root 0000000 0000000 ``set``
=======
Inside code blocks you can also assign values to variables. Assignments use
the ``set`` tag and can have multiple targets.
Here is how you can assign the ``bar`` value to the ``foo`` variable:
.. code-block:: jinja
{% set foo = 'bar' %}
After the ``set`` call, the ``foo`` variable is available in the template like
any other ones:
.. code-block:: jinja
{# displays bar #}
{{ foo }}
The assigned value can be any valid :ref:`Twig expressions
`:
.. code-block:: jinja
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
{% set foo = 'foo' ~ 'bar' %}
Several variables can be assigned in one block:
.. code-block:: jinja
{% set foo, bar = 'foo', 'bar' %}
{# is equivalent to #}
{% set foo = 'foo' %}
{% set bar = 'bar' %}
The ``set`` tag can also be used to 'capture' chunks of text:
.. code-block:: jinja
{% set foo %}
{% endset %}
.. caution::
If you enable automatic output escaping, Twig will only consider the
content to be safe when capturing chunks of text.
.. note::
Note that loops are scoped in Twig; therefore a variable declared inside a
``for`` loop is not accessible outside the loop itself:
.. code-block:: jinja
{% for item in list %}
{% set foo = item %}
{% endfor %}
{# foo is NOT available #}
If you want to access the variable, just declare it before the loop:
.. code-block:: jinja
{% set foo = "" %}
{% for item in list %}
{% set foo = item %}
{% endfor %}
{# foo is available #}
Twig-1.23.1/doc/tags/spaceless.rst 0000664 0000000 0000000 00000002224 12616650102 0016726 0 ustar 00root root 0000000 0000000 ``spaceless``
=============
Use the ``spaceless`` tag to remove whitespace *between HTML tags*, not
whitespace within HTML tags or whitespace in plain text:
.. code-block:: jinja
{% spaceless %}
foo
{% endspaceless %}
{# output will be foo
#}
This tag is not meant to "optimize" the size of the generated HTML content but
merely to avoid extra whitespace between HTML tags to avoid browser rendering
quirks under some circumstances.
.. tip::
If you want to optimize the size of the generated HTML content, gzip
compress the output instead.
.. tip::
If you want to create a tag that actually removes all extra whitespace in
an HTML string, be warned that this is not as easy as it seems to be
(think of ``textarea`` or ``pre`` tags for instance). Using a third-party
library like Tidy is probably a better idea.
.. tip::
For more information on whitespace control, read the
:ref:`dedicated section ` of the documentation and learn how
you can also use the whitespace control modifier on your tags.
Twig-1.23.1/doc/tags/use.rst 0000664 0000000 0000000 00000006532 12616650102 0015546 0 ustar 00root root 0000000 0000000 ``use``
=======
.. versionadded:: 1.1
Horizontal reuse was added in Twig 1.1.
.. note::
Horizontal reuse is an advanced Twig feature that is hardly ever needed in
regular templates. It is mainly used by projects that need to make
template blocks reusable without using inheritance.
Template inheritance is one of the most powerful Twig's feature but it is
limited to single inheritance; a template can only extend one other template.
This limitation makes template inheritance simple to understand and easy to
debug:
.. code-block:: jinja
{% extends "base.html" %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
Horizontal reuse is a way to achieve the same goal as multiple inheritance,
but without the associated complexity:
.. code-block:: jinja
{% extends "base.html" %}
{% use "blocks.html" %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
The ``use`` statement tells Twig to import the blocks defined in
``blocks.html`` into the current template (it's like macros, but for blocks):
.. code-block:: jinja
{# blocks.html #}
{% block sidebar %}{% endblock %}
In this example, the ``use`` statement imports the ``sidebar`` block into the
main template. The code is mostly equivalent to the following one (the
imported blocks are not outputted automatically):
.. code-block:: jinja
{% extends "base.html" %}
{% block sidebar %}{% endblock %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
.. note::
The ``use`` tag only imports a template if it does not extend another
template, if it does not define macros, and if the body is empty. But it
can *use* other templates.
.. note::
Because ``use`` statements are resolved independently of the context
passed to the template, the template reference cannot be an expression.
The main template can also override any imported block. If the template
already defines the ``sidebar`` block, then the one defined in ``blocks.html``
is ignored. To avoid name conflicts, you can rename imported blocks:
.. code-block:: jinja
{% extends "base.html" %}
{% use "blocks.html" with sidebar as base_sidebar, title as base_title %}
{% block sidebar %}{% endblock %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
.. versionadded:: 1.3
The ``parent()`` support was added in Twig 1.3.
The ``parent()`` function automatically determines the correct inheritance
tree, so it can be used when overriding a block defined in an imported
template:
.. code-block:: jinja
{% extends "base.html" %}
{% use "blocks.html" %}
{% block sidebar %}
{{ parent() }}
{% endblock %}
{% block title %}{% endblock %}
{% block content %}{% endblock %}
In this example, ``parent()`` will correctly call the ``sidebar`` block from
the ``blocks.html`` template.
.. tip::
In Twig 1.2, renaming allows you to simulate inheritance by calling the
"parent" block:
.. code-block:: jinja
{% extends "base.html" %}
{% use "blocks.html" with sidebar as parent_sidebar %}
{% block sidebar %}
{{ block('parent_sidebar') }}
{% endblock %}
.. note::
You can use as many ``use`` statements as you want in any given template.
If two imported templates define the same block, the latest one wins.
Twig-1.23.1/doc/tags/verbatim.rst 0000664 0000000 0000000 00000001156 12616650102 0016560 0 ustar 00root root 0000000 0000000 ``verbatim``
============
.. versionadded:: 1.12
The ``verbatim`` tag was added in Twig 1.12 (it was named ``raw`` before).
The ``verbatim`` tag marks sections as being raw text that should not be
parsed. For example to put Twig syntax as example into a template you can use
this snippet:
.. code-block:: jinja
{% verbatim %}
{% for item in seq %}
{{ item }}
{% endfor %}
{% endverbatim %}
.. note::
The ``verbatim`` tag works in the exact same way as the old ``raw`` tag,
but was renamed to avoid confusion with the ``raw`` filter. Twig-1.23.1/doc/templates.rst 0000664 0000000 0000000 00000064407 12616650102 0016017 0 ustar 00root root 0000000 0000000 Twig for Template Designers
===========================
This document describes the syntax and semantics of the template engine and
will be most useful as reference to those creating Twig templates.
Synopsis
--------
A template is simply a text file. It can generate any text-based format (HTML,
XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or
``.xml`` are just fine.
A template contains **variables** or **expressions**, which get replaced with
values when the template is evaluated, and **tags**, which control the logic
of the template.
Below is a minimal template that illustrates a few basics. We will cover further
details later on:
.. code-block:: html+jinja
My Webpage
My Webpage
{{ a_variable }}
There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first
one is used to execute statements such as for-loops, the latter prints the
result of an expression to the template.
IDEs Integration
----------------
Many IDEs support syntax highlighting and auto-completion for Twig:
* *Textmate* via the `Twig bundle`_
* *Vim* via the `Jinja syntax plugin`_ or the `vim-twig plugin`_
* *Netbeans* via the `Twig syntax plugin`_ (until 7.1, native as of 7.2)
* *PhpStorm* (native as of 2.1)
* *Eclipse* via the `Twig plugin`_
* *Sublime Text* via the `Twig bundle`_
* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects)
* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_
* *Coda 2* via the `other Twig syntax mode`_
* *Komodo* and *Komodo Edit* via the Twig highlight/syntax check mode
* *Notepad++* via the `Notepad++ Twig Highlighter`_
* *Emacs* via `web-mode.el`_
* *Atom* via the `PHP-twig for atom`_
Also, `TwigFiddle`_ is an online service that allows you to execute Twig templates
from a browser; it supports all versions of Twig.
Variables
---------
The application passes variables to the templates for manipulation in the
template. Variables may have attributes or elements you can access,
too. The visual representation of a variable depends heavily on the application providing
it.
You can use a dot (``.``) to access attributes of a variable (methods or
properties of a PHP object, or items of a PHP array), or the so-called
"subscript" syntax (``[]``):
.. code-block:: jinja
{{ foo.bar }}
{{ foo['bar'] }}
When the attribute contains special characters (like ``-`` that would be
interpreted as the minus operator), use the ``attribute`` function instead to
access the variable attribute:
.. code-block:: jinja
{# equivalent to the non-working foo.data-foo #}
{{ attribute(foo, 'data-foo') }}
.. note::
It's important to know that the curly braces are *not* part of the
variable but the print statement. When accessing variables inside tags,
don't put the braces around them.
If a variable or attribute does not exist, you will receive a ``null`` value
when the ``strict_variables`` option is set to ``false``; alternatively, if ``strict_variables``
is set, Twig will throw an error (see :ref:`environment options`).
.. sidebar:: Implementation
For convenience's sake ``foo.bar`` does the following things on the PHP
layer:
* check if ``foo`` is an array and ``bar`` a valid element;
* if not, and if ``foo`` is an object, check that ``bar`` is a valid property;
* if not, and if ``foo`` is an object, check that ``bar`` is a valid method
(even if ``bar`` is the constructor - use ``__construct()`` instead);
* if not, and if ``foo`` is an object, check that ``getBar`` is a valid method;
* if not, and if ``foo`` is an object, check that ``isBar`` is a valid method;
* if not, return a ``null`` value.
``foo['bar']`` on the other hand only works with PHP arrays:
* check if ``foo`` is an array and ``bar`` a valid element;
* if not, return a ``null`` value.
.. note::
If you want to access a dynamic attribute of a variable, use the
:doc:`attribute` function instead.
Global Variables
~~~~~~~~~~~~~~~~
The following variables are always available in templates:
* ``_self``: references the current template (deprecated since Twig 1.20);
* ``_context``: references the current context;
* ``_charset``: references the current charset.
Setting Variables
~~~~~~~~~~~~~~~~~
You can assign values to variables inside code blocks. Assignments use the
:doc:`set` tag:
.. code-block:: jinja
{% set foo = 'foo' %}
{% set foo = [1, 2] %}
{% set foo = {'foo': 'bar'} %}
Filters
-------
Variables can be modified by **filters**. Filters are separated from the
variable by a pipe symbol (``|``) and may have optional arguments in
parentheses. Multiple filters can be chained. The output of one filter is
applied to the next.
The following example removes all HTML tags from the ``name`` and title-cases
it:
.. code-block:: jinja
{{ name|striptags|title }}
Filters that accept arguments have parentheses around the arguments. This
example will join a list by commas:
.. code-block:: jinja
{{ list|join(', ') }}
To apply a filter on a section of code, wrap it in the
:doc:`filter` tag:
.. code-block:: jinja
{% filter upper %}
This text becomes uppercase
{% endfilter %}
Go to the :doc:`filters` page to learn more about built-in
filters.
Functions
---------
Functions can be called to generate content. Functions are called by their
name followed by parentheses (``()``) and may have arguments.
For instance, the ``range`` function returns a list containing an arithmetic
progression of integers:
.. code-block:: jinja
{% for i in range(0, 3) %}
{{ i }},
{% endfor %}
Go to the :doc:`functions` page to learn more about the
built-in functions.
Named Arguments
---------------
.. versionadded:: 1.12
Support for named arguments was added in Twig 1.12.
.. code-block:: jinja
{% for i in range(low=1, high=10, step=2) %}
{{ i }},
{% endfor %}
Using named arguments makes your templates more explicit about the meaning of
the values you pass as arguments:
.. code-block:: jinja
{{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
{# versus #}
{{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}
Named arguments also allow you to skip some arguments for which you don't want
to change the default value:
.. code-block:: jinja
{# the first argument is the date format, which defaults to the global date format if null is passed #}
{{ "now"|date(null, "Europe/Paris") }}
{# or skip the format value by using a named argument for the time zone #}
{{ "now"|date(timezone="Europe/Paris") }}
You can also use both positional and named arguments in one call, in which
case positional arguments must always come before named arguments:
.. code-block:: jinja
{{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
.. tip::
Each function and filter documentation page has a section where the names
of all arguments are listed when supported.
Control Structure
-----------------
A control structure refers to all those things that control the flow of a
program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as
well as things like blocks. Control structures appear inside ``{% ... %}``
blocks.
For example, to display a list of users provided in a variable called
``users``, use the :doc:`for` tag:
.. code-block:: jinja
Members
{% for user in users %}
{{ user.username|e }}
{% endfor %}
The :doc:`if` tag can be used to test an expression:
.. code-block:: jinja
{% if users|length > 0 %}
{% for user in users %}
{{ user.username|e }}
{% endfor %}
{% endif %}
Go to the :doc:`tags` page to learn more about the built-in tags.
Comments
--------
To comment-out part of a line in a template, use the comment syntax ``{# ...
#}``. This is useful for debugging or to add information for other template
designers or yourself:
.. code-block:: jinja
{# note: disabled template because we no longer use this
{% for user in users %}
...
{% endfor %}
#}
Including other Templates
-------------------------
The :doc:`include` tag is useful to include a template and
return the rendered content of that template into the current one:
.. code-block:: jinja
{% include 'sidebar.html' %}
Per default included templates are passed the current context.
The context that is passed to the included template includes variables defined
in the template:
.. code-block:: jinja
{% for box in boxes %}
{% include "render_box.html" %}
{% endfor %}
The included template ``render_box.html`` is able to access ``box``.
The filename of the template depends on the template loader. For instance, the
``Twig_Loader_Filesystem`` allows you to access other templates by giving the
filename. You can access templates in subdirectories with a slash:
.. code-block:: jinja
{% include "sections/articles/sidebar.html" %}
This behavior depends on the application embedding Twig.
Template Inheritance
--------------------
The most powerful part of Twig is template inheritance. Template inheritance
allows you to build a base "skeleton" template that contains all the common
elements of your site and defines **blocks** that child templates can
override.
Sounds complicated but it is very basic. It's easier to understand it by
starting with an example.
Let's define a base template, ``base.html``, which defines a simple HTML
skeleton document that you might use for a simple two-column page:
.. code-block:: html+jinja
{% block head %}
{% block title %}{% endblock %} - My Webpage
{% endblock %}
{% block content %}{% endblock %}
In this example, the :doc:`block` tags define four blocks that
child templates can fill in. All the ``block`` tag does is to tell the
template engine that a child template may override those portions of the
template.
A child template might look like this:
.. code-block:: jinja
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
{{ parent() }}
{% endblock %}
{% block content %}
Index
Welcome to my awesome homepage.
{% endblock %}
The :doc:`extends` tag is the key here. It tells the template
engine that this template "extends" another template. When the template system
evaluates this template, first it locates the parent. The extends tag should
be the first tag in the template.
Note that since the child template doesn't define the ``footer`` block, the
value from the parent template is used instead.
It's possible to render the contents of the parent block by using the
:doc:`parent` function. This gives back the results of the
parent block:
.. code-block:: jinja
{% block sidebar %}
Table Of Contents
...
{{ parent() }}
{% endblock %}
.. tip::
The documentation page for the :doc:`extends` tag describes
more advanced features like block nesting, scope, dynamic inheritance, and
conditional inheritance.
.. note::
Twig also supports multiple inheritance with the so called horizontal reuse
with the help of the :doc:`use` tag. This is an advanced feature
hardly ever needed in regular templates.
HTML Escaping
-------------
When generating HTML from templates, there's always a risk that a variable
will include characters that affect the resulting HTML. There are two
approaches: manually escaping each variable or automatically escaping
everything by default.
Twig supports both, automatic escaping is enabled by default.
.. note::
Automatic escaping is only supported if the *escaper* extension has been
enabled (which is the default).
Working with Manual Escaping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If manual escaping is enabled, it is **your** responsibility to escape
variables if needed. What to escape? Any variable you don't trust.
Escaping works by piping the variable through the
:doc:`escape` or ``e`` filter:
.. code-block:: jinja
{{ user.username|e }}
By default, the ``escape`` filter uses the ``html`` strategy, but depending on
the escaping context, you might want to explicitly use any other available
strategies:
.. code-block:: jinja
{{ user.username|e('js') }}
{{ user.username|e('css') }}
{{ user.username|e('url') }}
{{ user.username|e('html_attr') }}
Working with Automatic Escaping
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Whether automatic escaping is enabled or not, you can mark a section of a
template to be escaped or not by using the :doc:`autoescape`
tag:
.. code-block:: jinja
{% autoescape %}
Everything will be automatically escaped in this block (using the HTML strategy)
{% endautoescape %}
By default, auto-escaping uses the ``html`` escaping strategy. If you output
variables in other contexts, you need to explicitly escape them with the
appropriate escaping strategy:
.. code-block:: jinja
{% autoescape 'js' %}
Everything will be automatically escaped in this block (using the JS strategy)
{% endautoescape %}
Escaping
--------
It is sometimes desirable or even necessary to have Twig ignore parts it would
otherwise handle as variables or blocks. For example if the default syntax is
used and you want to use ``{{`` as raw string in the template and not start a
variable you have to use a trick.
The easiest way is to output the variable delimiter (``{{``) by using a variable
expression:
.. code-block:: jinja
{{ '{{' }}
For bigger sections it makes sense to mark a block
:doc:`verbatim`.
Macros
------
.. versionadded:: 1.12
Support for default argument values was added in Twig 1.12.
Macros are comparable with functions in regular programming languages. They
are useful to reuse often used HTML fragments to not repeat yourself.
A macro is defined via the :doc:`macro` tag. Here is a small example
(subsequently called ``forms.html``) of a macro that renders a form element:
.. code-block:: jinja
{% macro input(name, value, type, size) %}
{% endmacro %}
Macros can be defined in any template, and need to be "imported" via the
:doc:`import` tag before being used:
.. code-block:: jinja
{% import "forms.html" as forms %}
{{ forms.input('username') }}
Alternatively, you can import individual macro names from a template into the
current namespace via the :doc:`from` tag and optionally alias them:
.. code-block:: jinja
{% from 'forms.html' import input as input_field %}
Username
{{ input_field('username') }}
Password
{{ input_field('password', '', 'password') }}
A default value can also be defined for macro arguments when not provided in a
macro call:
.. code-block:: jinja
{% macro input(name, value = "", type = "text", size = 20) %}
{% endmacro %}
If extra positional arguments are passed to a macro call, they end up in the
special ``varargs`` variable as a list of values.
.. _twig-expressions:
Expressions
-----------
Twig allows expressions everywhere. These work very similar to regular PHP and
even if you're not working with PHP you should feel comfortable with it.
.. note::
The operator precedence is as follows, with the lowest-precedence
operators listed first: ``b-and``, ``b-xor``, ``b-or``, ``or``, ``and``,
``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``matches``,
``starts with``, ``ends with``, ``..``, ``+``, ``-``, ``~``, ``*``, ``/``,
``//``, ``%``, ``is``, ``**``, ``|``, ``[]``, and ``.``:
.. code-block:: jinja
{% set greeting = 'Hello ' %}
{% set name = 'Fabien' %}
{{ greeting ~ name|lower }} {# Hello fabien #}
{# use parenthesis to change precedence #}
{{ (greeting ~ name)|lower }} {# hello fabien #}
Literals
~~~~~~~~
.. versionadded:: 1.5
Support for hash keys as names and expressions was added in Twig 1.5.
The simplest form of expressions are literals. Literals are representations
for PHP types such as strings, numbers, and arrays. The following literals
exist:
* ``"Hello World"``: Everything between two double or single quotes is a
string. They are useful whenever you need a string in the template (for
example as arguments to function calls, filters or just to extend or include
a template). A string can contain a delimiter if it is preceded by a
backslash (``\``) -- like in ``'It\'s good'``.
* ``42`` / ``42.23``: Integers and floating point numbers are created by just
writing the number down. If a dot is present the number is a float,
otherwise an integer.
* ``["foo", "bar"]``: Arrays are defined by a sequence of expressions
separated by a comma (``,``) and wrapped with squared brackets (``[]``).
* ``{"foo": "bar"}``: Hashes are defined by a list of keys and values
separated by a comma (``,``) and wrapped with curly braces (``{}``):
.. code-block:: jinja
{# keys as string #}
{ 'foo': 'foo', 'bar': 'bar' }
{# keys as names (equivalent to the previous hash) -- as of Twig 1.5 #}
{ foo: 'foo', bar: 'bar' }
{# keys as integer #}
{ 2: 'foo', 4: 'bar' }
{# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #}
{ (1 + 1): 'foo', (a ~ 'b'): 'bar' }
* ``true`` / ``false``: ``true`` represents the true value, ``false``
represents the false value.
* ``null``: ``null`` represents no specific value. This is the value returned
when a variable does not exist. ``none`` is an alias for ``null``.
Arrays and hashes can be nested:
.. code-block:: jinja
{% set foo = [1, {"foo": "bar"}] %}
.. tip::
Using double-quoted or single-quoted strings has no impact on performance
but string interpolation is only supported in double-quoted strings.
Math
~~~~
Twig allows you to calculate with values. This is rarely useful in templates
but exists for completeness' sake. The following operators are supported:
* ``+``: Adds two objects together (the operands are casted to numbers). ``{{
1 + 1 }}`` is ``2``.
* ``-``: Subtracts the second number from the first one. ``{{ 3 - 2 }}`` is
``1``.
* ``/``: Divides two numbers. The returned value will be a floating point
number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``.
* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is
``4``.
* ``//``: Divides two numbers and returns the floored integer result. ``{{ 20
// 7 }}`` is ``2``, ``{{ -20 // 7 }}`` is ``-3`` (this is just syntactic
sugar for the :doc:`round` filter).
* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would
return ``4``.
* ``**``: Raises the left operand to the power of the right operand. ``{{ 2 **
3 }}`` would return ``8``.
Logic
~~~~~
You can combine multiple expressions with the following operators:
* ``and``: Returns true if the left and the right operands are both true.
* ``or``: Returns true if the left or the right operand is true.
* ``not``: Negates a statement.
* ``(expr)``: Groups an expression.
.. note::
Twig also support bitwise operators (``b-and``, ``b-xor``, and ``b-or``).
.. note::
Operators are case sensitive.
Comparisons
~~~~~~~~~~~
The following comparison operators are supported in any expression: ``==``,
``!=``, ``<``, ``>``, ``>=``, and ``<=``.
You can also check if a string ``starts with`` or ``ends with`` another
string:
.. code-block:: jinja
{% if 'Fabien' starts with 'F' %}
{% endif %}
{% if 'Fabien' ends with 'n' %}
{% endif %}
.. note::
For complex string comparisons, the ``matches`` operator allows you to use
`regular expressions`_:
.. code-block:: jinja
{% if phone matches '/^[\\d\\.]+$/' %}
{% endif %}
Containment Operator
~~~~~~~~~~~~~~~~~~~~
The ``in`` operator performs containment test.
It returns ``true`` if the left operand is contained in the right:
.. code-block:: jinja
{# returns true #}
{{ 1 in [1, 2, 3] }}
{{ 'cd' in 'abcde' }}
.. tip::
You can use this filter to perform a containment test on strings, arrays,
or objects implementing the ``Traversable`` interface.
To perform a negative test, use the ``not in`` operator:
.. code-block:: jinja
{% if 1 not in [1, 2, 3] %}
{# is equivalent to #}
{% if not (1 in [1, 2, 3]) %}
Test Operator
~~~~~~~~~~~~~
The ``is`` operator performs tests. Tests can be used to test a variable against
a common expression. The right operand is name of the test:
.. code-block:: jinja
{# find out if a variable is odd #}
{{ name is odd }}
Tests can accept arguments too:
.. code-block:: jinja
{% if post.status is constant('Post::PUBLISHED') %}
Tests can be negated by using the ``is not`` operator:
.. code-block:: jinja
{% if post.status is not constant('Post::PUBLISHED') %}
{# is equivalent to #}
{% if not (post.status is constant('Post::PUBLISHED')) %}
Go to the :doc:`tests` page to learn more about the built-in
tests.
Other Operators
~~~~~~~~~~~~~~~
.. versionadded:: 1.12.0
Support for the extended ternary operator was added in Twig 1.12.0.
The following operators don't fit into any of the other categories:
* ``|``: Applies a filter.
* ``..``: Creates a sequence based on the operand before and after the operator
(this is just syntactic sugar for the :doc:`range` function):
.. code-block:: jinja
{{ 1..5 }}
{# equivalent to #}
{{ range(1, 5) }}
Note that you must use parentheses when combining it with the filter operator
due to the :ref:`operator precedence rules `:
.. code-block:: jinja
(1..5)|join(', ')
* ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello
" ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello
John!``.
* ``.``, ``[]``: Gets an attribute of an object.
* ``?:``: The ternary operator:
.. code-block:: jinja
{{ foo ? 'yes' : 'no' }}
{# as of Twig 1.12.0 #}
{{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }}
{{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }}
String Interpolation
~~~~~~~~~~~~~~~~~~~~
.. versionadded:: 1.5
String interpolation was added in Twig 1.5.
String interpolation (`#{expression}`) allows any valid expression to appear
within a *double-quoted string*. The result of evaluating that expression is
inserted into the string:
.. code-block:: jinja
{{ "foo #{bar} baz" }}
{{ "foo #{1 + 2} baz" }}
.. _templates-whitespace-control:
Whitespace Control
------------------
.. versionadded:: 1.1
Tag level whitespace control was added in Twig 1.1.
The first newline after a template tag is removed automatically (like in PHP.)
Whitespace is not further modified by the template engine, so each whitespace
(spaces, tabs, newlines etc.) is returned unchanged.
Use the ``spaceless`` tag to remove whitespace *between HTML tags*:
.. code-block:: jinja
{% spaceless %}
foo bar
{% endspaceless %}
{# output will be foo bar
#}
In addition to the spaceless tag you can also control whitespace on a per tag
level. By using the whitespace control modifier on your tags, you can trim
leading and or trailing whitespace:
.. code-block:: jinja
{% set value = 'no spaces' %}
{#- No leading/trailing whitespace -#}
{%- if true -%}
{{- value -}}
{%- endif -%}
{# output 'no spaces' #}
The above sample shows the default whitespace control modifier, and how you can
use it to remove whitespace around tags. Trimming space will consume all whitespace
for that side of the tag. It is possible to use whitespace trimming on one side
of a tag:
.. code-block:: jinja
{% set value = 'no spaces' %}
{{- value }}
{# outputs 'no spaces ' #}
Extensions
----------
Twig can be easily extended.
If you are looking for new tags, filters, or functions, have a look at the Twig official
`extension repository`_.
If you want to create your own, read the :ref:`Creating an
Extension` chapter.
.. _`Twig bundle`: https://github.com/Anomareh/PHP-Twig.tmbundle
.. _`Jinja syntax plugin`: http://jinja.pocoo.org/docs/integration/#vim
.. _`vim-twig plugin`: https://github.com/evidens/vim-twig
.. _`Twig syntax plugin`: http://plugins.netbeans.org/plugin/37069/php-twig
.. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin
.. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language
.. _`extension repository`: http://github.com/twigphp/Twig-extensions
.. _`Twig syntax mode`: https://github.com/bobthecow/Twig-HTML.mode
.. _`other Twig syntax mode`: https://github.com/muxx/Twig-HTML.mode
.. _`Notepad++ Twig Highlighter`: https://github.com/Banane9/notepadplusplus-twig
.. _`web-mode.el`: http://web-mode.org/
.. _`regular expressions`: http://php.net/manual/en/pcre.pattern.php
.. _`PHP-twig for atom`: https://github.com/reesef/php-twig
.. _`TwigFiddle`: http://twigfiddle.com/
Twig-1.23.1/doc/tests/ 0000775 0000000 0000000 00000000000 12616650102 0014416 5 ustar 00root root 0000000 0000000 Twig-1.23.1/doc/tests/constant.rst 0000664 0000000 0000000 00000001167 12616650102 0017006 0 ustar 00root root 0000000 0000000 ``constant``
============
.. versionadded: 1.13.1
constant now accepts object instances as the second argument.
``constant`` checks if a variable has the exact same value as a constant. You
can use either global constants or class constants:
.. code-block:: jinja
{% if post.status is constant('Post::PUBLISHED') %}
the status attribute is exactly the same as Post::PUBLISHED
{% endif %}
You can test constants from object instances as well:
.. code-block:: jinja
{% if post.status is constant('PUBLISHED', post) %}
the status attribute is exactly the same as Post::PUBLISHED
{% endif %}
Twig-1.23.1/doc/tests/defined.rst 0000664 0000000 0000000 00000001266 12616650102 0016553 0 ustar 00root root 0000000 0000000 ``defined``
===========
``defined`` checks if a variable is defined in the current context. This is very
useful if you use the ``strict_variables`` option:
.. code-block:: jinja
{# defined works with variable names #}
{% if foo is defined %}
...
{% endif %}
{# and attributes on variables names #}
{% if foo.bar is defined %}
...
{% endif %}
{% if foo['bar'] is defined %}
...
{% endif %}
When using the ``defined`` test on an expression that uses variables in some
method calls, be sure that they are all defined first:
.. code-block:: jinja
{% if var is defined and foo.method(var) is defined %}
...
{% endif %}
Twig-1.23.1/doc/tests/divisibleby.rst 0000664 0000000 0000000 00000000470 12616650102 0017456 0 ustar 00root root 0000000 0000000 ``divisible by``
================
.. versionadded:: 1.14.2
The ``divisible by`` test was added in Twig 1.14.2 as an alias for
``divisibleby``.
``divisible by`` checks if a variable is divisible by a number:
.. code-block:: jinja
{% if loop.index is divisible by(3) %}
...
{% endif %}
Twig-1.23.1/doc/tests/empty.rst 0000664 0000000 0000000 00000000360 12616650102 0016305 0 ustar 00root root 0000000 0000000 ``empty``
=========
``empty`` checks if a variable is empty:
.. code-block:: jinja
{# evaluates to true if the foo variable is null, false, an empty array, or the empty string #}
{% if foo is empty %}
...
{% endif %}
Twig-1.23.1/doc/tests/even.rst 0000664 0000000 0000000 00000000237 12616650102 0016107 0 ustar 00root root 0000000 0000000 ``even``
========
``even`` returns ``true`` if the given number is even:
.. code-block:: jinja
{{ var is even }}
.. seealso:: :doc:`odd<../tests/odd>`
Twig-1.23.1/doc/tests/index.rst 0000664 0000000 0000000 00000000221 12616650102 0016252 0 ustar 00root root 0000000 0000000 Tests
=====
.. toctree::
:maxdepth: 1
constant
defined
divisibleby
empty
even
iterable
null
odd
sameas
Twig-1.23.1/doc/tests/iterable.rst 0000664 0000000 0000000 00000000717 12616650102 0016744 0 ustar 00root root 0000000 0000000 ``iterable``
============
.. versionadded:: 1.7
The iterable test was added in Twig 1.7.
``iterable`` checks if a variable is an array or a traversable object:
.. code-block:: jinja
{# evaluates to true if the foo variable is iterable #}
{% if users is iterable %}
{% for user in users %}
Hello {{ user }}!
{% endfor %}
{% else %}
{# users is probably a string #}
Hello {{ users }}!
{% endif %}
Twig-1.23.1/doc/tests/null.rst 0000664 0000000 0000000 00000000253 12616650102 0016122 0 ustar 00root root 0000000 0000000 ``null``
========
``null`` returns ``true`` if the variable is ``null``:
.. code-block:: jinja
{{ var is null }}
.. note::
``none`` is an alias for ``null``.
Twig-1.23.1/doc/tests/odd.rst 0000664 0000000 0000000 00000000234 12616650102 0015715 0 ustar 00root root 0000000 0000000 ``odd``
=======
``odd`` returns ``true`` if the given number is odd:
.. code-block:: jinja
{{ var is odd }}
.. seealso:: :doc:`even<../tests/even>`
Twig-1.23.1/doc/tests/sameas.rst 0000664 0000000 0000000 00000000574 12616650102 0016427 0 ustar 00root root 0000000 0000000 ``same as``
===========
.. versionadded:: 1.14.2
The ``same as`` test was added in Twig 1.14.2 as an alias for ``sameas``.
``same as`` checks if a variable is the same as another variable.
This is the equivalent to ``===`` in PHP:
.. code-block:: jinja
{% if foo.attribute is same as(false) %}
the foo attribute really is the 'false' PHP value
{% endif %}
Twig-1.23.1/ext/ 0000775 0000000 0000000 00000000000 12616650102 0013307 5 ustar 00root root 0000000 0000000 Twig-1.23.1/ext/twig/ 0000775 0000000 0000000 00000000000 12616650102 0014261 5 ustar 00root root 0000000 0000000 Twig-1.23.1/ext/twig/.gitignore 0000664 0000000 0000000 00000000510 12616650102 0016245 0 ustar 00root root 0000000 0000000 *.sw*
.deps
Makefile
Makefile.fragments
Makefile.global
Makefile.objects
acinclude.m4
aclocal.m4
build/
config.cache
config.guess
config.h
config.h.in
config.log
config.nice
config.status
config.sub
configure
configure.in
install-sh
libtool
ltmain.sh
missing
mkinstalldirs
run-tests.php
twig.loT
.libs/
modules/
twig.la
twig.lo
Twig-1.23.1/ext/twig/config.m4 0000664 0000000 0000000 00000000335 12616650102 0015771 0 ustar 00root root 0000000 0000000 dnl config.m4 for extension twig
PHP_ARG_ENABLE(twig, whether to enable twig support,
[ --enable-twig Enable twig support])
if test "$PHP_TWIG" != "no"; then
PHP_NEW_EXTENSION(twig, twig.c, $ext_shared)
fi
Twig-1.23.1/ext/twig/config.w32 0000664 0000000 0000000 00000000225 12616650102 0016062 0 ustar 00root root 0000000 0000000 // vim:ft=javascript
ARG_ENABLE("twig", "Twig support", "no");
if (PHP_TWIG != "no") {
AC_DEFINE('HAVE_TWIG', 1);
EXTENSION('twig', 'twig.c');
}
Twig-1.23.1/ext/twig/php_twig.h 0000664 0000000 0000000 00000002265 12616650102 0016260 0 ustar 00root root 0000000 0000000 /*
+----------------------------------------------------------------------+
| Twig Extension |
+----------------------------------------------------------------------+
| Copyright (c) 2011 Derick Rethans |
+----------------------------------------------------------------------+
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the conditions mentioned |
| in the accompanying LICENSE file are met (BSD-3-Clause). |
+----------------------------------------------------------------------+
| Author: Derick Rethans |
+----------------------------------------------------------------------+
*/
#ifndef PHP_TWIG_H
#define PHP_TWIG_H
#define PHP_TWIG_VERSION "1.23.1"
#include "php.h"
extern zend_module_entry twig_module_entry;
#define phpext_twig_ptr &twig_module_entry
#ifndef PHP_WIN32
zend_module_entry *get_module(void);
#endif
#ifdef ZTS
#include "TSRM.h"
#endif
PHP_FUNCTION(twig_template_get_attributes);
PHP_RSHUTDOWN_FUNCTION(twig);
#endif
Twig-1.23.1/ext/twig/twig.c 0000664 0000000 0000000 00000076165 12616650102 0015416 0 ustar 00root root 0000000 0000000 /*
+----------------------------------------------------------------------+
| Twig Extension |
+----------------------------------------------------------------------+
| Copyright (c) 2011 Derick Rethans |
+----------------------------------------------------------------------+
| Redistribution and use in source and binary forms, with or without |
| modification, are permitted provided that the conditions mentioned |
| in the accompanying LICENSE file are met (BSD-3-Clause). |
+----------------------------------------------------------------------+
| Author: Derick Rethans |
+----------------------------------------------------------------------+
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "php.h"
#include "php_twig.h"
#include "ext/standard/php_var.h"
#include "ext/standard/php_string.h"
#include "ext/standard/php_smart_str.h"
#include "ext/spl/spl_exceptions.h"
#include "Zend/zend_object_handlers.h"
#include "Zend/zend_interfaces.h"
#include "Zend/zend_exceptions.h"
#ifndef Z_ADDREF_P
#define Z_ADDREF_P(pz) (pz)->refcount++
#endif
#define FREE_DTOR(z) \
zval_dtor(z); \
efree(z);
#if PHP_VERSION_ID >= 50300
#define APPLY_TSRMLS_DC TSRMLS_DC
#define APPLY_TSRMLS_CC TSRMLS_CC
#define APPLY_TSRMLS_FETCH()
#else
#define APPLY_TSRMLS_DC
#define APPLY_TSRMLS_CC
#define APPLY_TSRMLS_FETCH() TSRMLS_FETCH()
#endif
ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 6)
ZEND_ARG_INFO(0, template)
ZEND_ARG_INFO(0, object)
ZEND_ARG_INFO(0, item)
ZEND_ARG_INFO(0, arguments)
ZEND_ARG_INFO(0, type)
ZEND_ARG_INFO(0, isDefinedTest)
ZEND_END_ARG_INFO()
#ifndef PHP_FE_END
#define PHP_FE_END { NULL, NULL, NULL}
#endif
static const zend_function_entry twig_functions[] = {
PHP_FE(twig_template_get_attributes, twig_template_get_attribute_args)
PHP_FE_END
};
PHP_RSHUTDOWN_FUNCTION(twig)
{
#if ZEND_DEBUG
CG(unclean_shutdown) = 0; /* get rid of PHPUnit's exit() and report memleaks */
#endif
return SUCCESS;
}
zend_module_entry twig_module_entry = {
STANDARD_MODULE_HEADER,
"twig",
twig_functions,
NULL,
NULL,
NULL,
PHP_RSHUTDOWN(twig),
NULL,
PHP_TWIG_VERSION,
STANDARD_MODULE_PROPERTIES
};
#ifdef COMPILE_DL_TWIG
ZEND_GET_MODULE(twig)
#endif
static int TWIG_ARRAY_KEY_EXISTS(zval *array, zval *key)
{
if (Z_TYPE_P(array) != IS_ARRAY) {
return 0;
}
switch (Z_TYPE_P(key)) {
case IS_NULL:
return zend_hash_exists(Z_ARRVAL_P(array), "", 1);
case IS_BOOL:
case IS_DOUBLE:
convert_to_long(key);
case IS_LONG:
return zend_hash_index_exists(Z_ARRVAL_P(array), Z_LVAL_P(key));
default:
convert_to_string(key);
return zend_symtable_exists(Z_ARRVAL_P(array), Z_STRVAL_P(key), Z_STRLEN_P(key) + 1);
}
}
static int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
{
if (Z_TYPE_P(object) != IS_OBJECT) {
return 0;
}
return instanceof_function(Z_OBJCE_P(object), interface TSRMLS_CC);
}
static int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
{
zend_class_entry **pce;
if (Z_TYPE_P(object) != IS_OBJECT) {
return 0;
}
if (zend_lookup_class(interface, strlen(interface), &pce TSRMLS_CC) == FAILURE) {
return 0;
}
return instanceof_function(Z_OBJCE_P(object), *pce TSRMLS_CC);
}
static zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
{
zend_class_entry *ce = Z_OBJCE_P(object);
zval *retval;
if (Z_TYPE_P(object) == IS_OBJECT) {
SEPARATE_ARG_IF_REF(offset);
zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
zval_ptr_dtor(&offset);
if (!retval) {
if (!EG(exception)) {
zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
}
return NULL;
}
return retval;
}
return NULL;
}
static int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
{
zend_class_entry *ce = Z_OBJCE_P(object);
zval *retval;
if (Z_TYPE_P(object) == IS_OBJECT) {
SEPARATE_ARG_IF_REF(offset);
zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
zval_ptr_dtor(&offset);
if (!retval) {
if (!EG(exception)) {
zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
}
return 0;
}
return (retval && Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval));
}
return 0;
}
static char *TWIG_STRTOLOWER(const char *str, int str_len)
{
char *item_dup;
item_dup = estrndup(str, str_len);
php_strtolower(item_dup, str_len);
return item_dup;
}
static zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TSRMLS_DC)
{
zend_fcall_info fci;
zval ***args = NULL;
int arg_count = 0;
HashTable *table;
HashPosition pos;
int i = 0;
zval *retval_ptr;
zval *zfunction;
if (arguments) {
table = HASH_OF(arguments);
args = safe_emalloc(sizeof(zval **), table->nNumOfElements, 0);
zend_hash_internal_pointer_reset_ex(table, &pos);
while (zend_hash_get_current_data_ex(table, (void **)&args[i], &pos) == SUCCESS) {
i++;
zend_hash_move_forward_ex(table, &pos);
}
arg_count = table->nNumOfElements;
}
MAKE_STD_ZVAL(zfunction);
ZVAL_STRING(zfunction, function, 1);
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = zfunction;
fci.symbol_table = NULL;
#if PHP_VERSION_ID >= 50300
fci.object_ptr = object;
#else
fci.object_pp = &object;
#endif
fci.retval_ptr_ptr = &retval_ptr;
fci.param_count = arg_count;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
ALLOC_INIT_ZVAL(retval_ptr);
ZVAL_BOOL(retval_ptr, 0);
}
if (args) {
efree(fci.params);
}
FREE_DTOR(zfunction);
return retval_ptr;
}
static int TWIG_CALL_BOOLEAN(zval *object, char *functionName TSRMLS_DC)
{
zval *ret;
int res;
ret = TWIG_CALL_USER_FUNC_ARRAY(object, functionName, NULL TSRMLS_CC);
res = Z_LVAL_P(ret);
zval_ptr_dtor(&ret);
return res;
}
static zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
{
zval **tmp_zval;
zend_class_entry *ce;
if (class == NULL || Z_TYPE_P(class) != IS_OBJECT) {
return NULL;
}
ce = zend_get_class_entry(class TSRMLS_CC);
#if PHP_VERSION_ID >= 50400
tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0, NULL TSRMLS_CC);
#else
tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0 TSRMLS_CC);
#endif
return *tmp_zval;
}
static zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
{
zval **tmp_zval;
if (class == NULL || Z_TYPE_P(class) != IS_ARRAY) {
if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
// array access object
return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
}
return NULL;
}
switch(Z_TYPE_P(prop_name)) {
case IS_NULL:
zend_hash_find(HASH_OF(class), "", 1, (void**) &tmp_zval);
return *tmp_zval;
case IS_BOOL:
case IS_DOUBLE:
convert_to_long(prop_name);
case IS_LONG:
zend_hash_index_find(HASH_OF(class), Z_LVAL_P(prop_name), (void **) &tmp_zval);
return *tmp_zval;
case IS_STRING:
zend_symtable_find(HASH_OF(class), Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name) + 1, (void**) &tmp_zval);
return *tmp_zval;
}
return NULL;
}
static zval *TWIG_GET_ARRAY_ELEMENT(zval *class, char *prop_name, int prop_name_length TSRMLS_DC)
{
zval **tmp_zval;
if (class == NULL/* || Z_TYPE_P(class) != IS_ARRAY*/) {
return NULL;
}
if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
// array access object
zval *tmp_name_zval;
zval *tmp_ret_zval;
ALLOC_INIT_ZVAL(tmp_name_zval);
ZVAL_STRING(tmp_name_zval, prop_name, 1);
tmp_ret_zval = TWIG_GET_ARRAYOBJECT_ELEMENT(class, tmp_name_zval TSRMLS_CC);
FREE_DTOR(tmp_name_zval);
return tmp_ret_zval;
}
if (zend_symtable_find(HASH_OF(class), prop_name, prop_name_length+1, (void**)&tmp_zval) == SUCCESS) {
return *tmp_zval;
}
return NULL;
}
static zval *TWIG_PROPERTY(zval *object, zval *propname TSRMLS_DC)
{
zval *tmp = NULL;
if (Z_OBJ_HT_P(object)->read_property) {
#if PHP_VERSION_ID >= 50400
tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS, NULL TSRMLS_CC);
#else
tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS TSRMLS_CC);
#endif
if (tmp == EG(uninitialized_zval_ptr)) {
ZVAL_NULL(tmp);
}
}
return tmp;
}
static int TWIG_HAS_PROPERTY(zval *object, zval *propname TSRMLS_DC)
{
if (Z_OBJ_HT_P(object)->has_property) {
#if PHP_VERSION_ID >= 50400
return Z_OBJ_HT_P(object)->has_property(object, propname, 0, NULL TSRMLS_CC);
#else
return Z_OBJ_HT_P(object)->has_property(object, propname, 0 TSRMLS_CC);
#endif
}
return 0;
}
static int TWIG_HAS_DYNAMIC_PROPERTY(zval *object, char *prop, int prop_len TSRMLS_DC)
{
if (Z_OBJ_HT_P(object)->get_properties) {
return zend_hash_quick_exists(
Z_OBJ_HT_P(object)->get_properties(object TSRMLS_CC), // the properties hash
prop, // property name
prop_len + 1, // property length
zend_get_hash_value(prop, prop_len + 1) // hash value
);
}
return 0;
}
static zval *TWIG_PROPERTY_CHAR(zval *object, char *propname TSRMLS_DC)
{
zval *tmp_name_zval, *tmp;
ALLOC_INIT_ZVAL(tmp_name_zval);
ZVAL_STRING(tmp_name_zval, propname, 1);
tmp = TWIG_PROPERTY(object, tmp_name_zval TSRMLS_CC);
FREE_DTOR(tmp_name_zval);
return tmp;
}
static zval *TWIG_CALL_S(zval *object, char *method, char *arg0 TSRMLS_DC)
{
zend_fcall_info fci;
zval **args[1];
zval *argument;
zval *zfunction;
zval *retval_ptr;
MAKE_STD_ZVAL(argument);
ZVAL_STRING(argument, arg0, 1);
args[0] = &argument;
MAKE_STD_ZVAL(zfunction);
ZVAL_STRING(zfunction, method, 1);
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = zfunction;
fci.symbol_table = NULL;
#if PHP_VERSION_ID >= 50300
fci.object_ptr = object;
#else
fci.object_pp = &object;
#endif
fci.retval_ptr_ptr = &retval_ptr;
fci.param_count = 1;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
FREE_DTOR(zfunction);
zval_ptr_dtor(&argument);
return 0;
}
FREE_DTOR(zfunction);
zval_ptr_dtor(&argument);
return retval_ptr;
}
static int TWIG_CALL_SB(zval *object, char *method, char *arg0 TSRMLS_DC)
{
zval *retval_ptr;
int success;
retval_ptr = TWIG_CALL_S(object, method, arg0 TSRMLS_CC);
success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
if (retval_ptr) {
zval_ptr_dtor(&retval_ptr);
}
return success;
}
static int TWIG_CALL_ZZ(zval *object, char *method, zval *arg1, zval *arg2 TSRMLS_DC)
{
zend_fcall_info fci;
zval **args[2];
zval *zfunction;
zval *retval_ptr;
int success;
args[0] = &arg1;
args[1] = &arg2;
MAKE_STD_ZVAL(zfunction);
ZVAL_STRING(zfunction, method, 1);
fci.size = sizeof(fci);
fci.function_table = EG(function_table);
fci.function_name = zfunction;
fci.symbol_table = NULL;
#if PHP_VERSION_ID >= 50300
fci.object_ptr = object;
#else
fci.object_pp = &object;
#endif
fci.retval_ptr_ptr = &retval_ptr;
fci.param_count = 2;
fci.params = args;
fci.no_separation = 0;
if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
FREE_DTOR(zfunction);
return 0;
}
FREE_DTOR(zfunction);
success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
if (retval_ptr) {
zval_ptr_dtor(&retval_ptr);
}
return success;
}
#ifndef Z_SET_REFCOUNT_P
# define Z_SET_REFCOUNT_P(pz, rc) pz->refcount = rc
# define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
#endif
static void TWIG_NEW(zval *object, char *class, zval *arg0, zval *arg1 TSRMLS_DC)
{
zend_class_entry **pce;
if (zend_lookup_class(class, strlen(class), &pce TSRMLS_CC) == FAILURE) {
return;
}
Z_TYPE_P(object) = IS_OBJECT;
object_init_ex(object, *pce);
Z_SET_REFCOUNT_P(object, 1);
Z_UNSET_ISREF_P(object);
TWIG_CALL_ZZ(object, "__construct", arg0, arg1 TSRMLS_CC);
}
static int twig_add_array_key_to_string(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
{
smart_str *buf;
char *joiner;
APPLY_TSRMLS_FETCH();
buf = va_arg(args, smart_str*);
joiner = va_arg(args, char*);
if (buf->len != 0) {
smart_str_appends(buf, joiner);
}
if (hash_key->nKeyLength == 0) {
smart_str_append_long(buf, (long) hash_key->h);
} else {
char *key, *tmp_str;
int key_len, tmp_len;
key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
smart_str_appendl(buf, tmp_str, tmp_len);
efree(key);
efree(tmp_str);
}
return 0;
}
static char *TWIG_IMPLODE_ARRAY_KEYS(char *joiner, zval *array TSRMLS_DC)
{
smart_str collector = { 0, 0, 0 };
smart_str_appendl(&collector, "", 0);
zend_hash_apply_with_arguments(HASH_OF(array) APPLY_TSRMLS_CC, twig_add_array_key_to_string, 2, &collector, joiner);
smart_str_0(&collector);
return collector.c;
}
static void TWIG_RUNTIME_ERROR(zval *template TSRMLS_DC, char *message, ...)
{
char *buffer;
va_list args;
zend_class_entry **pce;
zval *ex;
zval *constructor;
zval *zmessage;
zval *lineno;
zval *filename_func;
zval *filename;
zval *constructor_args[3];
zval *constructor_retval;
if (zend_lookup_class("Twig_Error_Runtime", strlen("Twig_Error_Runtime"), &pce TSRMLS_CC) == FAILURE) {
return;
}
va_start(args, message);
vspprintf(&buffer, 0, message, args);
va_end(args);
MAKE_STD_ZVAL(ex);
object_init_ex(ex, *pce);
// Call Twig_Error constructor
MAKE_STD_ZVAL(constructor);
MAKE_STD_ZVAL(zmessage);
MAKE_STD_ZVAL(lineno);
MAKE_STD_ZVAL(filename);
MAKE_STD_ZVAL(filename_func);
MAKE_STD_ZVAL(constructor_retval);
ZVAL_STRINGL(constructor, "__construct", sizeof("__construct")-1, 1);
ZVAL_STRING(zmessage, buffer, 1);
ZVAL_LONG(lineno, -1);
// Get template filename
ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1);
call_user_function(EG(function_table), &template, filename_func, filename, 0, 0 TSRMLS_CC);
constructor_args[0] = zmessage;
constructor_args[1] = lineno;
constructor_args[2] = filename;
call_user_function(EG(function_table), &ex, constructor, constructor_retval, 3, constructor_args TSRMLS_CC);
zval_ptr_dtor(&constructor_retval);
zval_ptr_dtor(&zmessage);
zval_ptr_dtor(&lineno);
zval_ptr_dtor(&filename);
FREE_DTOR(constructor);
FREE_DTOR(filename_func);
efree(buffer);
zend_throw_exception_object(ex TSRMLS_CC);
}
static char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
{
char *class_name;
zend_uint class_name_len;
if (Z_TYPE_P(object) != IS_OBJECT) {
return "";
}
#if PHP_API_VERSION >= 20100412
zend_get_object_classname(object, (const char **) &class_name, &class_name_len TSRMLS_CC);
#else
zend_get_object_classname(object, &class_name, &class_name_len TSRMLS_CC);
#endif
return class_name;
}
static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
{
zend_class_entry *ce;
zval *retval;
char *item;
size_t item_len;
zend_function *mptr = (zend_function *) pDest;
APPLY_TSRMLS_FETCH();
if (!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
return 0;
}
ce = *va_arg(args, zend_class_entry**);
retval = va_arg(args, zval*);
item_len = strlen(mptr->common.function_name);
item = estrndup(mptr->common.function_name, item_len);
php_strtolower(item, item_len);
if (strcmp("getenvironment", item) == 0) {
zend_class_entry **twig_template_ce;
if (zend_lookup_class("Twig_Template", strlen("Twig_Template"), &twig_template_ce TSRMLS_CC) == FAILURE) {
return 0;
}
if (instanceof_function(ce, *twig_template_ce TSRMLS_CC)) {
return 0;
}
}
add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
return 0;
}
static int twig_add_property_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
{
zend_class_entry *ce;
zval *retval;
char *class_name, *prop_name;
zend_property_info *pptr = (zend_property_info *) pDest;
APPLY_TSRMLS_FETCH();
if (!(pptr->flags & ZEND_ACC_PUBLIC) || (pptr->flags & ZEND_ACC_STATIC)) {
return 0;
}
ce = *va_arg(args, zend_class_entry**);
retval = va_arg(args, zval*);
#if PHP_API_VERSION >= 20100412
zend_unmangle_property_name(pptr->name, pptr->name_length, (const char **) &class_name, (const char **) &prop_name);
#else
zend_unmangle_property_name(pptr->name, pptr->name_length, &class_name, &prop_name);
#endif
add_assoc_string(retval, prop_name, prop_name, 1);
return 0;
}
static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name TSRMLS_DC)
{
zval *class_info, *class_methods, *class_properties;
zend_class_entry *class_ce;
class_ce = zend_get_class_entry(object TSRMLS_CC);
ALLOC_INIT_ZVAL(class_info);
ALLOC_INIT_ZVAL(class_methods);
ALLOC_INIT_ZVAL(class_properties);
array_init(class_info);
array_init(class_methods);
array_init(class_properties);
// add all methods to self::cache[$class]['methods']
zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 2, &class_ce, class_methods);
zend_hash_apply_with_arguments(&class_ce->properties_info APPLY_TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
add_assoc_zval(class_info, "methods", class_methods);
add_assoc_zval(class_info, "properties", class_properties);
add_assoc_zval(cache, class_name, class_info);
}
/* {{{ proto mixed twig_template_get_attributes(TwigTemplate template, mixed object, mixed item, array arguments, string type, boolean isDefinedTest, boolean ignoreStrictCheck)
A C implementation of TwigTemplate::getAttribute() */
PHP_FUNCTION(twig_template_get_attributes)
{
zval *template;
zval *object;
char *item;
int item_len;
zval *zitem, ztmpitem;
zval *arguments = NULL;
zval *ret = NULL;
char *type = NULL;
int type_len = 0;
zend_bool isDefinedTest = 0;
zend_bool ignoreStrictCheck = 0;
int free_ret = 0;
zval *tmp_self_cache;
char *class_name = NULL;
zval *tmp_class;
char *type_name;
if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozz|asbb", &template, &object, &zitem, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
return;
}
// convert the item to a string
ztmpitem = *zitem;
zval_copy_ctor(&ztmpitem);
convert_to_string(&ztmpitem);
item_len = Z_STRLEN(ztmpitem);
item = estrndup(Z_STRVAL(ztmpitem), item_len);
zval_dtor(&ztmpitem);
if (!type) {
type = "any";
}
/*
// array
if (Twig_Template::METHOD_CALL !== $type) {
$arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
if ((is_array($object) && array_key_exists($arrayItem, $object))
|| ($object instanceof ArrayAccess && isset($object[$arrayItem]))
) {
if ($isDefinedTest) {
return true;
}
return $object[$arrayItem];
}
*/
if (strcmp("method", type) != 0) {
if ((TWIG_ARRAY_KEY_EXISTS(object, zitem))
|| (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, zitem TSRMLS_CC))
) {
if (isDefinedTest) {
efree(item);
RETURN_TRUE;
}
ret = TWIG_GET_ARRAY_ELEMENT_ZVAL(object, zitem TSRMLS_CC);
if (!ret) {
ret = &EG(uninitialized_zval);
}
RETVAL_ZVAL(ret, 1, 0);
if (free_ret) {
zval_ptr_dtor(&ret);
}
efree(item);
return;
}
/*
if (Twig_Template::ARRAY_CALL === $type) {
if ($isDefinedTest) {
return false;
}
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return null;
}
*/
if (strcmp("array", type) == 0 || Z_TYPE_P(object) != IS_OBJECT) {
if (isDefinedTest) {
efree(item);
RETURN_FALSE;
}
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
efree(item);
return;
}
/*
if ($object instanceof ArrayAccess) {
$message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object));
} elseif (is_object($object)) {
$message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
} elseif (is_array($object)) {
if (empty($object)) {
$message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
} else {
$message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
}
} elseif (Twig_Template::ARRAY_CALL === $type) {
if (null === $object) {
$message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
} elseif (null === $object) {
$message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
}
}
*/
if (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC)) {
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object with ArrayAccess of class \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
} else if (Z_TYPE_P(object) == IS_OBJECT) {
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to access a key \"%s\" on an object of class \"%s\" that does not implement ArrayAccess interface", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
} else if (Z_TYPE_P(object) == IS_ARRAY) {
if (0 == zend_hash_num_elements(Z_ARRVAL_P(object))) {
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" does not exist as the array is empty", item);
} else {
char *array_keys = TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC);
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, array_keys);
efree(array_keys);
}
} else {
char *type_name = zend_zval_type_name(object);
Z_ADDREF_P(object);
if (Z_TYPE_P(object) == IS_NULL) {
convert_to_string(object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC,
(strcmp("array", type) == 0)
? "Impossible to access a key (\"%s\") on a %s variable"
: "Impossible to access an attribute (\"%s\") on a %s variable",
item, type_name);
} else {
convert_to_string(object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC,
(strcmp("array", type) == 0)
? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
: "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
item, type_name, Z_STRVAL_P(object));
}
zval_ptr_dtor(&object);
}
efree(item);
return;
}
}
/*
if (!is_object($object)) {
if ($isDefinedTest) {
return false;
}
*/
if (Z_TYPE_P(object) != IS_OBJECT) {
if (isDefinedTest) {
efree(item);
RETURN_FALSE;
}
/*
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return null;
}
if (null === $object) {
$message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
}
*/
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
efree(item);
return;
}
type_name = zend_zval_type_name(object);
Z_ADDREF_P(object);
if (Z_TYPE_P(object) == IS_NULL) {
convert_to_string_ex(&object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable", item, type_name);
} else {
convert_to_string_ex(&object);
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
}
zval_ptr_dtor(&object);
efree(item);
return;
}
/*
$class = get_class($object);
*/
class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
if (!tmp_class) {
twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
}
efree(class_name);
/*
// object property
if (Twig_Template::METHOD_CALL !== $type && !$object instanceof Twig_Template) {
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
if ($isDefinedTest) {
return true;
}
if ($this->env->hasExtension('sandbox')) {
$this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
}
return $object->$item;
}
}
*/
if (strcmp("method", type) != 0 && !TWIG_INSTANCE_OF_USERLAND(object, "Twig_Template" TSRMLS_CC)) {
zval *tmp_properties, *tmp_item;
tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
if (tmp_item || TWIG_HAS_PROPERTY(object, zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
if (isDefinedTest) {
efree(item);
RETURN_TRUE;
}
if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
}
if (EG(exception)) {
efree(item);
return;
}
ret = TWIG_PROPERTY(object, zitem TSRMLS_CC);
efree(item);
RETURN_ZVAL(ret, 1, 0);
}
}
/*
// object method
if (!isset(self::$cache[$class]['methods'])) {
if ($object instanceof self) {
$ref = new ReflectionClass($class);
$methods = array();
foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
$methodName = strtolower($refMethod->name);
// Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
if ('getenvironment' !== $methodName) {
$methods[$methodName] = true;
}
}
self::$cache[$class]['methods'] = $methods;
} else {
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
}
}
$call = false;
$lcItem = strtolower($item);
if (isset(self::$cache[$class]['methods'][$lcItem])) {
$method = (string) $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 = (string) $item;
$call = true;
*/
{
int call = 0;
char *lcItem = TWIG_STRTOLOWER(item, item_len);
int lcItem_length;
char *method = NULL;
char *tmp_method_name_get;
char *tmp_method_name_is;
zval *zmethod;
zval *tmp_methods;
lcItem_length = strlen(lcItem);
tmp_method_name_get = emalloc(4 + lcItem_length);
tmp_method_name_is = emalloc(3 + lcItem_length);
sprintf(tmp_method_name_get, "get%s", lcItem);
sprintf(tmp_method_name_is, "is%s", lcItem);
tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
method = item;
} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_get, lcItem_length + 3 TSRMLS_CC)) {
method = tmp_method_name_get;
} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_is, lcItem_length + 2 TSRMLS_CC)) {
method = tmp_method_name_is;
} else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
method = item;
call = 1;
/*
} else {
if ($isDefinedTest) {
return false;
}
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return null;
}
throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
}
if ($isDefinedTest) {
return true;
}
*/
} else {
efree(tmp_method_name_get);
efree(tmp_method_name_is);
efree(lcItem);
if (isDefinedTest) {
efree(item);
RETURN_FALSE;
}
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
efree(item);
return;
}
TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Method \"%s\" for object \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
efree(item);
return;
}
if (isDefinedTest) {
efree(tmp_method_name_get);
efree(tmp_method_name_is);
efree(lcItem);efree(item);
RETURN_TRUE;
}
/*
if ($this->env->hasExtension('sandbox')) {
$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
}
*/
MAKE_STD_ZVAL(zmethod);
ZVAL_STRING(zmethod, method, 1);
if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, zmethod TSRMLS_CC);
}
zval_ptr_dtor(&zmethod);
if (EG(exception)) {
efree(tmp_method_name_get);
efree(tmp_method_name_is);
efree(lcItem);efree(item);
return;
}
/*
// Some objects throw exceptions when they have __call, and the method we try
// to call is not supported. If ignoreStrictCheck is true, we should return null.
try {
$ret = call_user_func_array(array($object, $method), $arguments);
} catch (BadMethodCallException $e) {
if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
return null;
}
throw $e;
}
*/
ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
if (EG(exception) && TWIG_INSTANCE_OF(EG(exception), spl_ce_BadMethodCallException TSRMLS_CC)) {
if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
efree(tmp_method_name_get);
efree(tmp_method_name_is);
efree(lcItem);efree(item);
zend_clear_exception(TSRMLS_C);
return;
}
}
free_ret = 1;
efree(tmp_method_name_get);
efree(tmp_method_name_is);
efree(lcItem);
}
/*
// useful when calling a template method from a template
// this is not supported but unfortunately heavily used in the Symfony profiler
if ($object instanceof Twig_TemplateInterface) {
return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
}
return $ret;
*/
efree(item);
// ret can be null, if e.g. the called method throws an exception
if (ret) {
if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
if (Z_STRLEN_P(ret) != 0) {
zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
zval_ptr_dtor(&charset);
if (ret) {
zval_ptr_dtor(&ret);
}
return;
}
}
RETVAL_ZVAL(ret, 1, 0);
if (free_ret) {
zval_ptr_dtor(&ret);
}
}
}
Twig-1.23.1/lib/ 0000775 0000000 0000000 00000000000 12616650102 0013255 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/ 0000775 0000000 0000000 00000000000 12616650102 0014167 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Autoloader.php 0000664 0000000 0000000 00000002640 12616650102 0017001 0 ustar 00root root 0000000 0000000
*
* @deprecated Use Composer instead. Will be removed in Twig 2.0.
*/
class Twig_Autoloader
{
/**
* Registers Twig_Autoloader as an SPL autoloader.
*
* @param bool $prepend Whether to prepend the autoloader or not.
*/
public static function register($prepend = false)
{
@trigger_error('Using Twig_Autoloader is deprecated. Use Composer instead.', E_USER_DEPRECATED);
if (PHP_VERSION_ID < 50300) {
spl_autoload_register(array(__CLASS__, 'autoload'));
} else {
spl_autoload_register(array(__CLASS__, 'autoload'), true, $prepend);
}
}
/**
* Handles autoloading of classes.
*
* @param string $class A class name.
*/
public static function autoload($class)
{
if (0 !== strpos($class, 'Twig')) {
return;
}
if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) {
require $file;
}
}
}
Twig-1.23.1/lib/Twig/BaseNodeVisitor.php 0000664 0000000 0000000 00000003402 12616650102 0017737 0 ustar 00root root 0000000 0000000
*/
abstract class Twig_BaseNodeVisitor implements Twig_NodeVisitorInterface
{
/**
* {@inheritdoc}
*/
final public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
{
if (!$node instanceof Twig_Node) {
throw new LogicException('Twig_BaseNodeVisitor only supports Twig_Node instances.');
}
return $this->doEnterNode($node, $env);
}
/**
* {@inheritdoc}
*/
final public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
{
if (!$node instanceof Twig_Node) {
throw new LogicException('Twig_BaseNodeVisitor only supports Twig_Node instances.');
}
return $this->doLeaveNode($node, $env);
}
/**
* Called before child nodes are visited.
*
* @param Twig_Node $node The node to visit
* @param Twig_Environment $env The Twig environment instance
*
* @return Twig_Node The modified node
*/
abstract protected function doEnterNode(Twig_Node $node, Twig_Environment $env);
/**
* Called after child nodes are visited.
*
* @param Twig_Node $node The node to visit
* @param Twig_Environment $env The Twig environment instance
*
* @return Twig_Node|false The modified node or false if the node must be removed
*/
abstract protected function doLeaveNode(Twig_Node $node, Twig_Environment $env);
}
Twig-1.23.1/lib/Twig/Cache/ 0000775 0000000 0000000 00000000000 12616650102 0015172 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Cache/Filesystem.php 0000664 0000000 0000000 00000004635 12616650102 0020037 0 ustar 00root root 0000000 0000000
*/
class Twig_Cache_Filesystem implements Twig_CacheInterface
{
const FORCE_BYTECODE_INVALIDATION = 1;
private $directory;
private $options;
/**
* @param $directory string The root cache directory
* @param $options int A set of options
*/
public function __construct($directory, $options = 0)
{
$this->directory = $directory;
$this->options = $options;
}
/**
* {@inheritdoc}
*/
public function generateKey($name, $className)
{
$hash = hash('sha256', $className);
return $this->directory.'/'.$hash[0].$hash[1].'/'.$hash.'.php';
}
/**
* {@inheritdoc}
*/
public function load($key)
{
@include_once $key;
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
$dir = dirname($key);
if (!is_dir($dir)) {
if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
throw new RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir));
}
} elseif (!is_writable($dir)) {
throw new RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir));
}
$tmpFile = tempnam($dir, basename($key));
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) {
@chmod($key, 0666 & ~umask());
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
// Compile cached file into bytecode cache
if (function_exists('opcache_invalidate')) {
opcache_invalidate($key, true);
} elseif (function_exists('apc_compile_file')) {
apc_compile_file($key);
}
}
return;
}
throw new RuntimeException(sprintf('Failed to write cache file "%s".', $key));
}
/**
* {@inheritdoc}
*/
public function getTimestamp($key)
{
if (!file_exists($key)) {
return 0;
}
return (int) @filemtime($key);
}
}
Twig-1.23.1/lib/Twig/Cache/Null.php 0000664 0000000 0000000 00000001366 12616650102 0016623 0 ustar 00root root 0000000 0000000
*/
class Twig_Cache_Null implements Twig_CacheInterface
{
/**
* {@inheritdoc}
*/
public function generateKey($name, $className)
{
return '';
}
/**
* {@inheritdoc}
*/
public function write($key, $content)
{
}
/**
* {@inheritdoc}
*/
public function load($key)
{
}
/**
* {@inheritdoc}
*/
public function getTimestamp($key)
{
return 0;
}
}
Twig-1.23.1/lib/Twig/CacheInterface.php 0000664 0000000 0000000 00000002557 12616650102 0017535 0 ustar 00root root 0000000 0000000
*/
interface Twig_CacheInterface
{
/**
* Generates a cache key for the given template class name.
*
* @param string $name The template name
* @param string $className The template class name
*
* @return string
*/
public function generateKey($name, $className);
/**
* Writes the compiled template to cache.
*
* @param string $key The cache key
* @param string $content The template representation as a PHP class
*/
public function write($key, $content);
/**
* Loads a template from the cache.
*
* @param string $key The cache key
*/
public function load($key);
/**
* Returns the modification timestamp of a key.
*
* @param string $key The cache key
*
* @return int
*/
public function getTimestamp($key);
}
Twig-1.23.1/lib/Twig/Compiler.php 0000664 0000000 0000000 00000015635 12616650102 0016464 0 ustar 00root root 0000000 0000000
*/
class Twig_Compiler implements Twig_CompilerInterface
{
protected $lastLine;
protected $source;
protected $indentation;
protected $env;
protected $debugInfo = array();
protected $sourceOffset;
protected $sourceLine;
protected $filename;
/**
* Constructor.
*
* @param Twig_Environment $env The twig environment instance
*/
public function __construct(Twig_Environment $env)
{
$this->env = $env;
}
public function getFilename()
{
return $this->filename;
}
/**
* 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 int $indentation The current indentation
*
* @return Twig_Compiler The current compiler instance
*/
public function compile(Twig_NodeInterface $node, $indentation = 0)
{
$this->lastLine = null;
$this->source = '';
$this->debugInfo = array();
$this->sourceOffset = 0;
// source code starts at 1 (as we then increment it when we encounter new lines)
$this->sourceLine = 1;
$this->indentation = $indentation;
if ($node instanceof Twig_Node_Module) {
$this->filename = $node->getAttribute('filename');
}
$node->compile($this);
return $this;
}
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;
}
/**
* Appends an indentation to the current PHP code after compilation.
*
* @return Twig_Compiler The current compiler instance
*/
public function addIndentation()
{
$this->source .= str_repeat(' ', $this->indentation * 4);
return $this;
}
/**
* Adds a quoted string to the compiled code.
*
* @param string $value The string
*
* @return Twig_Compiler The current compiler instance
*/
public function string($value)
{
$this->source .= sprintf('"%s"', addcslashes($value, "\0\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)) {
if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
setlocale(LC_NUMERIC, 'C');
}
$this->raw($value);
if (false !== $locale) {
setlocale(LC_NUMERIC, $locale);
}
} elseif (null === $value) {
$this->raw('null');
} elseif (is_bool($value)) {
$this->raw($value ? 'true' : 'false');
} elseif (is_array($value)) {
$this->raw('array(');
$first = true;
foreach ($value as $key => $v) {
if (!$first) {
$this->raw(', ');
}
$first = false;
$this->repr($key);
$this->raw(' => ');
$this->repr($v);
}
$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->write(sprintf("// line %d\n", $node->getLine()));
// when mbstring.func_overload is set to 2
// mb_substr_count() replaces substr_count()
// but they have different signatures!
if (((int) ini_get('mbstring.func_overload')) & 2) {
// this is much slower than the "right" version
$this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
} else {
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
}
$this->sourceOffset = strlen($this->source);
$this->debugInfo[$this->sourceLine] = $node->getLine();
$this->lastLine = $node->getLine();
}
return $this;
}
public function getDebugInfo()
{
ksort($this->debugInfo);
return $this->debugInfo;
}
/**
* Indents the generated code.
*
* @param int $step 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 int $step The number of indentation to remove
*
* @return Twig_Compiler The current compiler instance
*
* @throws LogicException When trying to outdent too much so the indentation would become negative
*/
public function outdent($step = 1)
{
// can't outdent by more steps than the current indentation level
if ($this->indentation < $step) {
throw new LogicException('Unable to call outdent() as the indentation would become negative');
}
$this->indentation -= $step;
return $this;
}
public function getVarName()
{
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
}
}
Twig-1.23.1/lib/Twig/CompilerInterface.php 0000664 0000000 0000000 00000001416 12616650102 0020275 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_CompilerInterface
{
/**
* Compiles a node.
*
* @param Twig_NodeInterface $node The node to compile
*
* @return Twig_CompilerInterface 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();
}
Twig-1.23.1/lib/Twig/Environment.php 0000664 0000000 0000000 00000125717 12616650102 0017221 0 ustar 00root root 0000000 0000000
*/
class Twig_Environment
{
const VERSION = '1.23.1';
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 $functions;
protected $globals;
protected $runtimeInitialized = false;
protected $extensionInitialized = false;
protected $loadedTemplates;
protected $strictVariables;
protected $unaryOperators;
protected $binaryOperators;
protected $templateClassPrefix = '__TwigTemplate_';
protected $functionCallbacks = array();
protected $filterCallbacks = array();
protected $staging;
private $originalCache;
private $bcWriteCacheFile = false;
private $bcGetCacheFilename = false;
private $lastModifiedExtension = 0;
/**
* Constructor.
*
* Available options:
*
* * debug: When set to true, it automatically set "auto_reload" to true as
* well (default to false).
*
* * charset: The charset used by the templates (default to UTF-8).
*
* * 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,
* a Twig_Cache_Interface implementation,
* or false to disable compilation cache (default).
*
* * auto_reload: Whether to reload the template if the original source changed.
* If you don't provide the auto_reload option, it will be
* determined automatically based on the debug value.
*
* * strict_variables: Whether to ignore invalid variables in templates
* (default to false).
*
* * autoescape: Whether to enable auto-escaping (default to html):
* * false: disable auto-escaping
* * true: equivalent to html
* * html, js: set the autoescaping to one of the supported strategies
* * filename: set the autoescaping strategy based on the template filename extension
* * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename"
*
* * optimizations: A flag that indicates which optimizations to apply
* (default to -1 which means that all optimizations are enabled;
* set it to 0 to disable).
*
* @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
* @param array $options An array of options
*/
public function __construct(Twig_LoaderInterface $loader = null, $options = array())
{
if (null !== $loader) {
$this->setLoader($loader);
} else {
@trigger_error('Not passing a Twig_LoaderInterface as the first constructor argument of Twig_Environment is deprecated.', E_USER_DEPRECATED);
}
$options = array_merge(array(
'debug' => false,
'charset' => 'UTF-8',
'base_template_class' => 'Twig_Template',
'strict_variables' => false,
'autoescape' => 'html',
'cache' => false,
'auto_reload' => null,
'optimizations' => -1,
), $options);
$this->debug = (bool) $options['debug'];
$this->charset = strtoupper($options['charset']);
$this->baseTemplateClass = $options['base_template_class'];
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
$this->strictVariables = (bool) $options['strict_variables'];
$this->setCache($options['cache']);
$this->addExtension(new Twig_Extension_Core());
$this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
$this->addExtension(new Twig_Extension_Optimizer($options['optimizations']));
$this->staging = new Twig_Extension_Staging();
// For BC
if (is_string($this->originalCache)) {
$r = new ReflectionMethod($this, 'writeCacheFile');
if ($r->getDeclaringClass()->getName() !== __CLASS__) {
@trigger_error('The Twig_Environment::writeCacheFile method is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED);
$this->bcWriteCacheFile = true;
}
$r = new ReflectionMethod($this, 'getCacheFilename');
if ($r->getDeclaringClass()->getName() !== __CLASS__) {
@trigger_error('The Twig_Environment::getCacheFilename method is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED);
$this->bcGetCacheFilename = true;
}
}
}
/**
* Gets the base template class for compiled templates.
*
* @return string The base template class name
*/
public function getBaseTemplateClass()
{
return $this->baseTemplateClass;
}
/**
* Sets the base template class for compiled templates.
*
* @param string $class The base template class name
*/
public function setBaseTemplateClass($class)
{
$this->baseTemplateClass = $class;
}
/**
* Enables debugging mode.
*/
public function enableDebug()
{
$this->debug = true;
}
/**
* Disables debugging mode.
*/
public function disableDebug()
{
$this->debug = false;
}
/**
* Checks if debug mode is enabled.
*
* @return bool true if debug mode is enabled, false otherwise
*/
public function isDebug()
{
return $this->debug;
}
/**
* Enables the auto_reload option.
*/
public function enableAutoReload()
{
$this->autoReload = true;
}
/**
* Disables the auto_reload option.
*/
public function disableAutoReload()
{
$this->autoReload = false;
}
/**
* Checks if the auto_reload option is enabled.
*
* @return bool true if auto_reload is enabled, false otherwise
*/
public function isAutoReload()
{
return $this->autoReload;
}
/**
* Enables the strict_variables option.
*/
public function enableStrictVariables()
{
$this->strictVariables = true;
}
/**
* Disables the strict_variables option.
*/
public function disableStrictVariables()
{
$this->strictVariables = false;
}
/**
* Checks if the strict_variables option is enabled.
*
* @return bool true if strict_variables is enabled, false otherwise
*/
public function isStrictVariables()
{
return $this->strictVariables;
}
/**
* Gets the current cache implementation.
*
* @param bool $original Whether to return the original cache option or the real cache instance
*
* @return Twig_CacheInterface|string|false A Twig_CacheInterface implementation,
* an absolute path to the compiled templates,
* or false to disable cache
*/
public function getCache($original = true)
{
return $original ? $this->originalCache : $this->cache;
}
/**
* Sets the current cache implementation.
*
* @param Twig_CacheInterface|string|false $cache A Twig_CacheInterface implementation,
* an absolute path to the compiled templates,
* or false to disable cache
*/
public function setCache($cache)
{
if (is_string($cache)) {
$this->originalCache = $cache;
$this->cache = new Twig_Cache_Filesystem($cache);
} elseif (false === $cache) {
$this->originalCache = $cache;
$this->cache = new Twig_Cache_Null();
} elseif (null === $cache) {
@trigger_error('Using "null" as the cache strategy is deprecated and will be removed in Twig 2.0.', E_USER_DEPRECATED);
$this->originalCache = false;
$this->cache = new Twig_Cache_Null();
} elseif ($cache instanceof Twig_CacheInterface) {
$this->originalCache = $this->cache = $cache;
} else {
throw new LogicException(sprintf('Cache can only be a string, false, or a Twig_CacheInterface implementation.'));
}
}
/**
* Gets the cache filename for a given template.
*
* @param string $name The template name
*
* @return string|false The cache file name or false when caching is disabled
*
* @deprecated since 1.22 (to be removed in 2.0)
*/
public function getCacheFilename($name)
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
$key = $this->cache->generateKey($name, $this->getTemplateClass($name));
return !$key ? false : $key;
}
/**
* Gets the template class associated with the given string.
*
* The generated template class is based on the following parameters:
*
* * The cache key for the given template;
* * The currently enabled extensions;
* * Whether the Twig C extension is available or not.
*
* @param string $name The name for which to calculate the template class name
* @param int|null $index The index if it is an embedded template
*
* @return string The template class name
*/
public function getTemplateClass($name, $index = null)
{
$key = $this->getLoader()->getCacheKey($name);
$key .= json_encode(array_keys($this->extensions));
$key .= function_exists('twig_template_get_attributes');
return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '_'.$index);
}
/**
* Gets the template class prefix.
*
* @return string The template class prefix
*
* @deprecated since 1.22 (to be removed in 2.0)
*/
public function getTemplateClassPrefix()
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
return $this->templateClassPrefix;
}
/**
* Renders a template.
*
* @param string $name The template name
* @param array $context An array of parameters to pass to the template
*
* @return string The rendered template
*
* @throws Twig_Error_Loader When the template cannot be found
* @throws Twig_Error_Syntax When an error occurred during compilation
* @throws Twig_Error_Runtime When an error occurred during rendering
*/
public function render($name, array $context = array())
{
return $this->loadTemplate($name)->render($context);
}
/**
* Displays a template.
*
* @param string $name The template name
* @param array $context An array of parameters to pass to the template
*
* @throws Twig_Error_Loader When the template cannot be found
* @throws Twig_Error_Syntax When an error occurred during compilation
* @throws Twig_Error_Runtime When an error occurred during rendering
*/
public function display($name, array $context = array())
{
$this->loadTemplate($name)->display($context);
}
/**
* Loads a template by name.
*
* @param string $name The template name
* @param int $index The index if it is an embedded template
*
* @return Twig_TemplateInterface A template instance representing the given template name
*
* @throws Twig_Error_Loader When the template cannot be found
* @throws Twig_Error_Syntax When an error occurred during compilation
*/
public function loadTemplate($name, $index = null)
{
$cls = $this->getTemplateClass($name, $index);
if (isset($this->loadedTemplates[$cls])) {
return $this->loadedTemplates[$cls];
}
if (!class_exists($cls, false)) {
if ($this->bcGetCacheFilename) {
$key = $this->getCacheFilename($name);
} else {
$key = $this->cache->generateKey($name, $cls);
}
if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
$this->cache->load($key);
}
if (!class_exists($cls, false)) {
$content = $this->compileSource($this->getLoader()->getSource($name), $name);
if ($this->bcWriteCacheFile) {
$this->writeCacheFile($key, $content);
} else {
$this->cache->write($key, $content);
}
eval('?>'.$content);
}
}
if (!$this->runtimeInitialized) {
$this->initRuntime();
}
return $this->loadedTemplates[$cls] = new $cls($this);
}
/**
* Creates a template from source.
*
* This method should not be used as a generic way to load templates.
*
* @param string $template The template name
*
* @return Twig_Template A template instance representing the given template name
*
* @throws Twig_Error_Loader When the template cannot be found
* @throws Twig_Error_Syntax When an error occurred during compilation
*/
public function createTemplate($template)
{
$name = sprintf('__string_template__%s', hash('sha256', uniqid(mt_rand(), true), false));
$loader = new Twig_Loader_Chain(array(
new Twig_Loader_Array(array($name => $template)),
$current = $this->getLoader(),
));
$this->setLoader($loader);
try {
$template = $this->loadTemplate($name);
} catch (Exception $e) {
$this->setLoader($current);
throw $e;
}
$this->setLoader($current);
return $template;
}
/**
* Returns true if the template is still fresh.
*
* Besides checking the loader for freshness information,
* this method also checks if the enabled extensions have
* not changed.
*
* @param string $name The template name
* @param int $time The last modification time of the cached template
*
* @return bool true if the template is fresh, false otherwise
*/
public function isTemplateFresh($name, $time)
{
if (0 === $this->lastModifiedExtension) {
foreach ($this->extensions as $extension) {
$r = new ReflectionObject($extension);
if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModifiedExtension) {
$this->lastModifiedExtension = $extensionTime;
}
}
}
return $this->lastModifiedExtension <= $time && $this->getLoader()->isFresh($name, $time);
}
/**
* Tries to load a template consecutively from an array.
*
* Similar to loadTemplate() but it also accepts Twig_TemplateInterface instances and an array
* of templates where each is tried to be loaded.
*
* @param string|Twig_Template|array $names A template or an array of templates to try consecutively
*
* @return Twig_Template
*
* @throws Twig_Error_Loader When none of the templates can be found
* @throws Twig_Error_Syntax When an error occurred during compilation
*/
public function resolveTemplate($names)
{
if (!is_array($names)) {
$names = array($names);
}
foreach ($names as $name) {
if ($name instanceof Twig_Template) {
return $name;
}
try {
return $this->loadTemplate($name);
} catch (Twig_Error_Loader $e) {
}
}
if (1 === count($names)) {
throw $e;
}
throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
}
/**
* Clears the internal template cache.
*
* @deprecated since 1.18.3 (to be removed in 2.0)
*/
public function clearTemplateCache()
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
$this->loadedTemplates = array();
}
/**
* Clears the template cache files on the filesystem.
*
* @deprecated since 1.22 (to be removed in 2.0)
*/
public function clearCacheFiles()
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
if (is_string($this->originalCache)) {
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->originalCache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
if ($file->isFile()) {
@unlink($file->getPathname());
}
}
}
}
/**
* Gets the Lexer instance.
*
* @return Twig_LexerInterface A Twig_LexerInterface instance
*/
public function getLexer()
{
if (null === $this->lexer) {
$this->lexer = new Twig_Lexer($this);
}
return $this->lexer;
}
/**
* Sets the Lexer instance.
*
* @param Twig_LexerInterface $lexer A Twig_LexerInterface instance
*/
public function setLexer(Twig_LexerInterface $lexer)
{
$this->lexer = $lexer;
}
/**
* Tokenizes a source code.
*
* @param string $source The template source code
* @param string $name The template name
*
* @return Twig_TokenStream A Twig_TokenStream instance
*
* @throws Twig_Error_Syntax When the code is syntactically wrong
*/
public function tokenize($source, $name = null)
{
return $this->getLexer()->tokenize($source, $name);
}
/**
* Gets the Parser instance.
*
* @return Twig_ParserInterface A Twig_ParserInterface instance
*/
public function getParser()
{
if (null === $this->parser) {
$this->parser = new Twig_Parser($this);
}
return $this->parser;
}
/**
* Sets the Parser instance.
*
* @param Twig_ParserInterface $parser A Twig_ParserInterface instance
*/
public function setParser(Twig_ParserInterface $parser)
{
$this->parser = $parser;
}
/**
* Converts a token stream to a node tree.
*
* @param Twig_TokenStream $stream A token stream instance
*
* @return Twig_Node_Module A node tree
*
* @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
*/
public function parse(Twig_TokenStream $stream)
{
return $this->getParser()->parse($stream);
}
/**
* Gets the Compiler instance.
*
* @return Twig_CompilerInterface A Twig_CompilerInterface instance
*/
public function getCompiler()
{
if (null === $this->compiler) {
$this->compiler = new Twig_Compiler($this);
}
return $this->compiler;
}
/**
* Sets the Compiler instance.
*
* @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance
*/
public function setCompiler(Twig_CompilerInterface $compiler)
{
$this->compiler = $compiler;
}
/**
* Compiles a node and returns the PHP code.
*
* @param Twig_NodeInterface $node A Twig_NodeInterface instance
*
* @return string The compiled PHP source code
*/
public function compile(Twig_NodeInterface $node)
{
return $this->getCompiler()->compile($node)->getSource();
}
/**
* Compiles a template source code.
*
* @param string $source The template source code
* @param string $name The template name
*
* @return string The compiled PHP source code
*
* @throws Twig_Error_Syntax When there was an error during tokenizing, parsing or compiling
*/
public function compileSource($source, $name = null)
{
try {
$compiled = $this->compile($this->parse($this->tokenize($source, $name)), $source);
if (isset($source[0])) {
$compiled .= '/* '.str_replace(array('*/', "\r\n", "\r", "\n"), array('*//* ', "\n", "\n", "*/\n/* "), $source)."*/\n";
}
return $compiled;
} catch (Twig_Error $e) {
$e->setTemplateFile($name);
throw $e;
} catch (Exception $e) {
throw new Twig_Error_Syntax(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
}
}
/**
* Sets the Loader instance.
*
* @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
*/
public function setLoader(Twig_LoaderInterface $loader)
{
$this->loader = $loader;
}
/**
* Gets the Loader instance.
*
* @return Twig_LoaderInterface A Twig_LoaderInterface instance
*/
public function getLoader()
{
if (null === $this->loader) {
throw new LogicException('You must set a loader first.');
}
return $this->loader;
}
/**
* Sets the default template charset.
*
* @param string $charset The default charset
*/
public function setCharset($charset)
{
$this->charset = strtoupper($charset);
}
/**
* Gets the default template charset.
*
* @return string The default charset
*/
public function getCharset()
{
return $this->charset;
}
/**
* Initializes the runtime environment.
*
* @deprecated since 1.23 (to be removed in 2.0)
*/
public function initRuntime()
{
$this->runtimeInitialized = true;
foreach ($this->getExtensions() as $name => $extension) {
if (!$extension instanceof Twig_Extension_InitRuntimeInterface) {
$m = new ReflectionMethod($extension, 'initRuntime');
if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Defining the initRuntime() method in the "%s" extension is deprecated. Use the `needs_environment` option to get the Twig_Environment instance in filters, functions, or tests; or explicitly implement Twig_Extension_InitRuntimeInterface if needed (not recommended).', $name), E_USER_DEPRECATED);
}
}
$extension->initRuntime($this);
}
}
/**
* Returns true if the given extension is registered.
*
* @param string $name The extension name
*
* @return bool Whether the extension is registered or not
*/
public function hasExtension($name)
{
return isset($this->extensions[$name]);
}
/**
* Gets an extension by name.
*
* @param string $name The extension name
*
* @return Twig_ExtensionInterface A Twig_ExtensionInterface instance
*/
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];
}
/**
* Registers an extension.
*
* @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance
*/
public function addExtension(Twig_ExtensionInterface $extension)
{
$name = $extension->getName();
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $name));
}
if (isset($this->extensions[$name])) {
@trigger_error(sprintf('The possibility to register the same extension twice ("%s") is deprecated and will be removed in Twig 2.0. Use proper PHP inheritance instead.', $name), E_USER_DEPRECATED);
}
$this->lastModifiedExtension = 0;
$this->extensions[$name] = $extension;
}
/**
* Removes an extension by name.
*
* This method is deprecated and you should not use it.
*
* @param string $name The extension name
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
public function removeExtension($name)
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
}
unset($this->extensions[$name]);
}
/**
* Registers an array of extensions.
*
* @param array $extensions An array of extensions
*/
public function setExtensions(array $extensions)
{
foreach ($extensions as $extension) {
$this->addExtension($extension);
}
}
/**
* Returns all registered extensions.
*
* @return array An array of extensions
*/
public function getExtensions()
{
return $this->extensions;
}
/**
* Registers a Token Parser.
*
* @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
*/
public function addTokenParser(Twig_TokenParserInterface $parser)
{
if ($this->extensionInitialized) {
throw new LogicException('Unable to add a token parser as extensions have already been initialized.');
}
$this->staging->addTokenParser($parser);
}
/**
* Gets the registered Token Parsers.
*
* @return Twig_TokenParserBrokerInterface A broker containing token parsers
*/
public function getTokenParsers()
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->parsers;
}
/**
* Gets registered tags.
*
* Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
*
* @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
*/
public function getTags()
{
$tags = array();
foreach ($this->getTokenParsers()->getParsers() as $parser) {
if ($parser instanceof Twig_TokenParserInterface) {
$tags[$parser->getTag()] = $parser;
}
}
return $tags;
}
/**
* Registers a Node Visitor.
*
* @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
*/
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
if ($this->extensionInitialized) {
throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
}
$this->staging->addNodeVisitor($visitor);
}
/**
* Gets the registered Node Visitors.
*
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
*/
public function getNodeVisitors()
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->visitors;
}
/**
* Registers a Filter.
*
* @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance
* @param Twig_FilterInterface|Twig_SimpleFilter $filter A Twig_FilterInterface instance or a Twig_SimpleFilter instance
*/
public function addFilter($name, $filter = null)
{
if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter');
}
if ($name instanceof Twig_SimpleFilter) {
$filter = $name;
$name = $filter->getName();
} else {
@trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFilter" instead when defining filter "%s".', __METHOD__, $name), E_USER_DEPRECATED);
}
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name));
}
$this->staging->addFilter($name, $filter);
}
/**
* Get a filter by name.
*
* Subclasses may override this method and load filters differently;
* so no list of filters is available.
*
* @param string $name The filter name
*
* @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist
*/
public function getFilter($name)
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
if (isset($this->filters[$name])) {
return $this->filters[$name];
}
foreach ($this->filters as $pattern => $filter) {
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
if ($count) {
if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
array_shift($matches);
$filter->setArguments($matches);
return $filter;
}
}
}
foreach ($this->filterCallbacks as $callback) {
if (false !== $filter = call_user_func($callback, $name)) {
return $filter;
}
}
return false;
}
public function registerUndefinedFilterCallback($callable)
{
$this->filterCallbacks[] = $callable;
}
/**
* Gets the registered Filters.
*
* Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback.
*
* @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
*
* @see registerUndefinedFilterCallback
*/
public function getFilters()
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->filters;
}
/**
* Registers a Test.
*
* @param string|Twig_SimpleTest $name The test name or a Twig_SimpleTest instance
* @param Twig_TestInterface|Twig_SimpleTest $test A Twig_TestInterface instance or a Twig_SimpleTest instance
*/
public function addTest($name, $test = null)
{
if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest');
}
if ($name instanceof Twig_SimpleTest) {
$test = $name;
$name = $test->getName();
} else {
@trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleTest" instead when defining test "%s".', __METHOD__, $name), E_USER_DEPRECATED);
}
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
}
$this->staging->addTest($name, $test);
}
/**
* Gets the registered Tests.
*
* @return Twig_TestInterface[] An array of Twig_TestInterface instances
*/
public function getTests()
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->tests;
}
/**
* Gets a test by name.
*
* @param string $name The test name
*
* @return Twig_Test|false A Twig_Test instance or false if the test does not exist
*/
public function getTest($name)
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
if (isset($this->tests[$name])) {
return $this->tests[$name];
}
return false;
}
/**
* Registers a Function.
*
* @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance
* @param Twig_FunctionInterface|Twig_SimpleFunction $function A Twig_FunctionInterface instance or a Twig_SimpleFunction instance
*/
public function addFunction($name, $function = null)
{
if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction');
}
if ($name instanceof Twig_SimpleFunction) {
$function = $name;
$name = $function->getName();
} else {
@trigger_error(sprintf('Passing a name as a first argument to the %s method is deprecated. Pass an instance of "Twig_SimpleFunction" instead when defining function "%s".', __METHOD__, $name), E_USER_DEPRECATED);
}
if ($this->extensionInitialized) {
throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name));
}
$this->staging->addFunction($name, $function);
}
/**
* Get a function by name.
*
* Subclasses may override this method and load functions differently;
* so no list of functions is available.
*
* @param string $name function name
*
* @return Twig_Function|false A Twig_Function instance or false if the function does not exist
*/
public function getFunction($name)
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
if (isset($this->functions[$name])) {
return $this->functions[$name];
}
foreach ($this->functions as $pattern => $function) {
$pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
if ($count) {
if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
array_shift($matches);
$function->setArguments($matches);
return $function;
}
}
}
foreach ($this->functionCallbacks as $callback) {
if (false !== $function = call_user_func($callback, $name)) {
return $function;
}
}
return false;
}
public function registerUndefinedFunctionCallback($callable)
{
$this->functionCallbacks[] = $callable;
}
/**
* Gets registered functions.
*
* Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
*
* @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances
*
* @see registerUndefinedFunctionCallback
*/
public function getFunctions()
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->functions;
}
/**
* Registers a Global.
*
* New globals can be added before compiling or rendering a template;
* but after, you can only update existing globals.
*
* @param string $name The global name
* @param mixed $value The global value
*/
public function addGlobal($name, $value)
{
if ($this->extensionInitialized || $this->runtimeInitialized) {
if (null === $this->globals) {
$this->globals = $this->initGlobals();
}
if (!array_key_exists($name, $this->globals)) {
// The deprecation notice must be turned into the following exception in Twig 2.0
@trigger_error(sprintf('Registering global variable "%s" at runtime or when the extensions have already been initialized is deprecated.', $name), E_USER_DEPRECATED);
//throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
}
}
if ($this->extensionInitialized || $this->runtimeInitialized) {
// update the value
$this->globals[$name] = $value;
} else {
$this->staging->addGlobal($name, $value);
}
}
/**
* Gets the registered Globals.
*
* @return array An array of globals
*/
public function getGlobals()
{
if (!$this->runtimeInitialized && !$this->extensionInitialized) {
return $this->initGlobals();
}
if (null === $this->globals) {
$this->globals = $this->initGlobals();
}
return $this->globals;
}
/**
* Merges a context with the defined globals.
*
* @param array $context An array representing the context
*
* @return array The context merged with the globals
*/
public function mergeGlobals(array $context)
{
// we don't use array_merge as the context being generally
// bigger than globals, this code is faster.
foreach ($this->getGlobals() as $key => $value) {
if (!array_key_exists($key, $context)) {
$context[$key] = $value;
}
}
return $context;
}
/**
* Gets the registered unary Operators.
*
* @return array An array of unary operators
*/
public function getUnaryOperators()
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->unaryOperators;
}
/**
* Gets the registered binary Operators.
*
* @return array An array of binary operators
*/
public function getBinaryOperators()
{
if (!$this->extensionInitialized) {
$this->initExtensions();
}
return $this->binaryOperators;
}
/**
* @deprecated since 1.23 (to be removed in 2.0)
*/
public function computeAlternatives($name, $items)
{
@trigger_error(sprintf('The %s method is deprecated and will be removed in Twig 2.0.', __METHOD__), E_USER_DEPRECATED);
return Twig_Error_Syntax::computeAlternatives($name, $items);
}
protected function initGlobals()
{
$globals = array();
foreach ($this->extensions as $name => $extension) {
if (!$extension instanceof Twig_Extension_GlobalsInterface) {
$m = new ReflectionMethod($extension, 'getGlobals');
if ('Twig_Extension' !== $m->getDeclaringClass()->getName()) {
@trigger_error(sprintf('Defining the getGlobals() method in the "%s" extension is deprecated without explicitly implementing Twig_Extension_GlobalsInterface.', $name), E_USER_DEPRECATED);
}
}
$extGlob = $extension->getGlobals();
if (!is_array($extGlob)) {
throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
}
$globals[] = $extGlob;
}
$globals[] = $this->staging->getGlobals();
return call_user_func_array('array_merge', $globals);
}
protected function initExtensions()
{
if ($this->extensionInitialized) {
return;
}
$this->extensionInitialized = true;
$this->parsers = new Twig_TokenParserBroker(array(), array(), false);
$this->filters = array();
$this->functions = array();
$this->tests = array();
$this->visitors = array();
$this->unaryOperators = array();
$this->binaryOperators = array();
foreach ($this->extensions as $extension) {
$this->initExtension($extension);
}
$this->initExtension($this->staging);
}
protected function initExtension(Twig_ExtensionInterface $extension)
{
// filters
foreach ($extension->getFilters() as $name => $filter) {
if ($filter instanceof Twig_SimpleFilter) {
$name = $filter->getName();
} else {
@trigger_error(sprintf('Using an instance of "%s" for filter "%s" is deprecated. Use Twig_SimpleFilter instead.', get_class($filter), $name), E_USER_DEPRECATED);
}
$this->filters[$name] = $filter;
}
// functions
foreach ($extension->getFunctions() as $name => $function) {
if ($function instanceof Twig_SimpleFunction) {
$name = $function->getName();
} else {
@trigger_error(sprintf('Using an instance of "%s" for function "%s" is deprecated. Use Twig_SimpleFunction instead.', get_class($function), $name), E_USER_DEPRECATED);
}
$this->functions[$name] = $function;
}
// tests
foreach ($extension->getTests() as $name => $test) {
if ($test instanceof Twig_SimpleTest) {
$name = $test->getName();
} else {
@trigger_error(sprintf('Using an instance of "%s" for test "%s" is deprecated. Use Twig_SimpleTest instead.', get_class($test), $name), E_USER_DEPRECATED);
}
$this->tests[$name] = $test;
}
// token parsers
foreach ($extension->getTokenParsers() as $parser) {
if ($parser instanceof Twig_TokenParserInterface) {
$this->parsers->addTokenParser($parser);
} elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
@trigger_error('Registering a Twig_TokenParserBrokerInterface instance is deprecated.', E_USER_DEPRECATED);
$this->parsers->addTokenParserBroker($parser);
} else {
throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
}
}
// node visitors
foreach ($extension->getNodeVisitors() as $visitor) {
$this->visitors[] = $visitor;
}
// operators
if ($operators = $extension->getOperators()) {
if (2 !== count($operators)) {
throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
}
$this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
$this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
}
}
/**
* @deprecated since 1.22 (to be removed in 2.0)
*/
protected function writeCacheFile($file, $content)
{
$this->cache->write($file, $content);
}
}
Twig-1.23.1/lib/Twig/Error.php 0000664 0000000 0000000 00000017477 12616650102 0016011 0 ustar 00root root 0000000 0000000
*/
class Twig_Error extends Exception
{
protected $lineno;
protected $filename;
protected $rawMessage;
protected $previous;
/**
* Constructor.
*
* Set both the line number and the filename to false to
* disable automatic guessing of the original template name
* and line number.
*
* Set the line number to -1 to enable its automatic guessing.
* Set the filename to null to enable its automatic guessing.
*
* By default, automatic guessing is enabled.
*
* @param string $message The error message
* @param int $lineno The template line where the error occurred
* @param string $filename The template file name where the error occurred
* @param Exception $previous The previous exception
*/
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
{
if (PHP_VERSION_ID < 50300) {
$this->previous = $previous;
parent::__construct('');
} else {
parent::__construct('', 0, $previous);
}
$this->lineno = $lineno;
$this->filename = $filename;
if (-1 === $this->lineno || null === $this->filename) {
$this->guessTemplateInfo();
}
$this->rawMessage = $message;
$this->updateRepr();
}
/**
* Gets the raw message.
*
* @return string The raw message
*/
public function getRawMessage()
{
return $this->rawMessage;
}
/**
* Gets the filename where the error occurred.
*
* @return string The filename
*/
public function getTemplateFile()
{
return $this->filename;
}
/**
* Sets the filename where the error occurred.
*
* @param string $filename The filename
*/
public function setTemplateFile($filename)
{
$this->filename = $filename;
$this->updateRepr();
}
/**
* Gets the template line where the error occurred.
*
* @return int The template line
*/
public function getTemplateLine()
{
return $this->lineno;
}
/**
* Sets the template line where the error occurred.
*
* @param int $lineno The template line
*/
public function setTemplateLine($lineno)
{
$this->lineno = $lineno;
$this->updateRepr();
}
public function guess()
{
$this->guessTemplateInfo();
$this->updateRepr();
}
/**
* For PHP < 5.3.0, provides access to the getPrevious() method.
*
* @param string $method The method name
* @param array $arguments The parameters to be passed to the method
*
* @return Exception The previous exception or null
*
* @throws BadMethodCallException
*/
public function __call($method, $arguments)
{
if ('getprevious' == strtolower($method)) {
return $this->previous;
}
throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
}
public function appendMessage($rawMessage)
{
$this->rawMessage .= $rawMessage;
$this->updateRepr();
}
/**
* @internal
*/
protected function updateRepr()
{
$this->message = $this->rawMessage;
$dot = false;
if ('.' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$dot = true;
}
$questionMark = false;
if ('?' === substr($this->message, -1)) {
$this->message = substr($this->message, 0, -1);
$questionMark = true;
}
if ($this->filename) {
if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
$filename = sprintf('"%s"', $this->filename);
} else {
$filename = json_encode($this->filename);
}
$this->message .= sprintf(' in %s', $filename);
}
if ($this->lineno && $this->lineno >= 0) {
$this->message .= sprintf(' at line %d', $this->lineno);
}
if ($dot) {
$this->message .= '.';
}
if ($questionMark) {
$this->message .= '?';
}
}
/**
* @internal
*/
protected function guessTemplateInfo()
{
$template = null;
$templateClass = null;
if (PHP_VERSION_ID >= 50306) {
$backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
} else {
$backtrace = debug_backtrace();
}
foreach ($backtrace as $trace) {
if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
$currentClass = get_class($trace['object']);
$isEmbedContainer = 0 === strpos($templateClass, $currentClass);
if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
$template = $trace['object'];
$templateClass = get_class($trace['object']);
}
}
}
// update template filename
if (null !== $template && null === $this->filename) {
$this->filename = $template->getTemplateName();
}
if (null === $template || $this->lineno > -1) {
return;
}
$r = new ReflectionObject($template);
$file = $r->getFileName();
// hhvm has a bug where eval'ed files comes out as the current directory
if (is_dir($file)) {
$file = '';
}
$exceptions = array($e = $this);
while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) {
$exceptions[] = $e;
}
while ($e = array_pop($exceptions)) {
$traces = $e->getTrace();
array_unshift($traces, array('file' => $e->getFile(), 'line' => $e->getLine()));
while ($trace = array_shift($traces)) {
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
continue;
}
foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
if ($codeLine <= $trace['line']) {
// update template line
$this->lineno = $templateLine;
return;
}
}
}
}
}
}
Twig-1.23.1/lib/Twig/Error/ 0000775 0000000 0000000 00000000000 12616650102 0015260 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Error/Loader.php 0000664 0000000 0000000 00000001662 12616650102 0017204 0 ustar 00root root 0000000 0000000
*/
class Twig_Error_Loader extends Twig_Error
{
public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
{
parent::__construct($message, false, false, $previous);
}
}
Twig-1.23.1/lib/Twig/Error/Runtime.php 0000664 0000000 0000000 00000000613 12616650102 0017414 0 ustar 00root root 0000000 0000000
*/
class Twig_Error_Runtime extends Twig_Error
{
}
Twig-1.23.1/lib/Twig/Error/Syntax.php 0000664 0000000 0000000 00000002612 12616650102 0017260 0 ustar 00root root 0000000 0000000
*/
class Twig_Error_Syntax extends Twig_Error
{
/**
* Tweaks the error message to include suggestions.
*
* @param string $name The original name of the item that does not exist
* @param array $items An array of possible items
*/
public function addSuggestions($name, array $items)
{
if (!$alternatives = self::computeAlternatives($name, $items)) {
return;
}
$this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', $alternatives)));
}
/**
* @internal
*
* To be merged with the addSuggestions() method in 2.0.
*/
public static function computeAlternatives($name, $items)
{
$alternatives = array();
foreach ($items as $item) {
$lev = levenshtein($name, $item);
if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
$alternatives[$item] = $lev;
}
}
asort($alternatives);
return array_keys($alternatives);
}
}
Twig-1.23.1/lib/Twig/ExistsLoaderInterface.php 0000664 0000000 0000000 00000001264 12616650102 0021132 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_ExistsLoaderInterface
{
/**
* Check if we have the source code of a template, given its name.
*
* @param string $name The name of the template to check if we can load
*
* @return bool If the template source code is handled by this loader or not
*/
public function exists($name);
}
Twig-1.23.1/lib/Twig/ExpressionParser.php 0000664 0000000 0000000 00000062105 12616650102 0020220 0 ustar 00root root 0000000 0000000
*/
class Twig_ExpressionParser
{
const OPERATOR_LEFT = 1;
const OPERATOR_RIGHT = 2;
protected $parser;
protected $unaryOperators;
protected $binaryOperators;
public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators)
{
$this->parser = $parser;
$this->unaryOperators = $unaryOperators;
$this->binaryOperators = $binaryOperators;
}
public function parseExpression($precedence = 0)
{
$expr = $this->getPrimary();
$token = $this->parser->getCurrentToken();
while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {
$op = $this->binaryOperators[$token->getValue()];
$this->parser->getStream()->next();
if (isset($op['callable'])) {
$expr = call_user_func($op['callable'], $this->parser, $expr);
} else {
$expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
$class = $op['class'];
$expr = new $class($expr, $expr1, $token->getLine());
}
$token = $this->parser->getCurrentToken();
}
if (0 === $precedence) {
return $this->parseConditionalExpression($expr);
}
return $expr;
}
protected function getPrimary()
{
$token = $this->parser->getCurrentToken();
if ($this->isUnary($token)) {
$operator = $this->unaryOperators[$token->getValue()];
$this->parser->getStream()->next();
$expr = $this->parseExpression($operator['precedence']);
$class = $operator['class'];
return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
$this->parser->getStream()->next();
$expr = $this->parseExpression();
$this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
return $this->parsePostfixExpression($expr);
}
return $this->parsePrimaryExpression();
}
protected function parseConditionalExpression($expr)
{
while ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, '?')) {
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
$expr2 = $this->parseExpression();
if ($this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
$expr3 = $this->parseExpression();
} else {
$expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
}
} else {
$expr2 = $expr;
$expr3 = $this->parseExpression();
}
$expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
}
return $expr;
}
protected function isUnary(Twig_Token $token)
{
return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
}
protected function isBinary(Twig_Token $token)
{
return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
}
public function parsePrimaryExpression()
{
$token = $this->parser->getCurrentToken();
switch ($token->getType()) {
case Twig_Token::NAME_TYPE:
$this->parser->getStream()->next();
switch ($token->getValue()) {
case 'true':
case 'TRUE':
$node = new Twig_Node_Expression_Constant(true, $token->getLine());
break;
case 'false':
case 'FALSE':
$node = new Twig_Node_Expression_Constant(false, $token->getLine());
break;
case 'none':
case 'NONE':
case 'null':
case 'NULL':
$node = new Twig_Node_Expression_Constant(null, $token->getLine());
break;
default:
if ('(' === $this->parser->getCurrentToken()->getValue()) {
$node = $this->getFunctionNode($token->getValue(), $token->getLine());
} else {
$node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
}
}
break;
case Twig_Token::NUMBER_TYPE:
$this->parser->getStream()->next();
$node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
break;
case Twig_Token::STRING_TYPE:
case Twig_Token::INTERPOLATION_START_TYPE:
$node = $this->parseStringExpression();
break;
case Twig_Token::OPERATOR_TYPE:
if (preg_match(Twig_Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
// in this context, string operators are variable names
$this->parser->getStream()->next();
$node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
break;
} elseif (isset($this->unaryOperators[$token->getValue()])) {
$class = $this->unaryOperators[$token->getValue()]['class'];
$ref = new ReflectionClass($class);
$negClass = 'Twig_Node_Expression_Unary_Neg';
$posClass = 'Twig_Node_Expression_Unary_Pos';
if (!(in_array($ref->getName(), array($negClass, $posClass)) || $ref->isSubclassOf($negClass) || $ref->isSubclassOf($posClass))) {
throw new Twig_Error_Syntax(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
$this->parser->getStream()->next();
$expr = $this->parsePrimaryExpression();
$node = new $class($expr, $token->getLine());
break;
}
default:
if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) {
$node = $this->parseArrayExpression();
} elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
$node = $this->parseHashExpression();
} else {
throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
}
return $this->parsePostfixExpression($node);
}
public function parseStringExpression()
{
$stream = $this->parser->getStream();
$nodes = array();
// a string cannot be followed by another string in a single expression
$nextCanBeString = true;
while (true) {
if ($nextCanBeString && $token = $stream->nextIf(Twig_Token::STRING_TYPE)) {
$nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
$nextCanBeString = false;
} elseif ($stream->nextIf(Twig_Token::INTERPOLATION_START_TYPE)) {
$nodes[] = $this->parseExpression();
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
$nextCanBeString = true;
} else {
break;
}
}
$expr = array_shift($nodes);
foreach ($nodes as $node) {
$expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine());
}
return $expr;
}
public function parseArrayExpression()
{
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
$node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
$first = true;
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
if (!$first) {
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
// trailing ,?
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
break;
}
}
$first = false;
$node->addElement($this->parseExpression());
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
return $node;
}
public function parseHashExpression()
{
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
$node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
$first = true;
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
if (!$first) {
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
// trailing ,?
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
break;
}
}
$first = false;
// a hash key can be:
//
// * a number -- 12
// * a string -- 'a'
// * a name, which is equivalent to a string -- a
// * an expression, which must be enclosed in parentheses -- (1 + 2)
if (($token = $stream->nextIf(Twig_Token::STRING_TYPE)) || ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) || $token = $stream->nextIf(Twig_Token::NUMBER_TYPE)) {
$key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
} elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
$key = $this->parseExpression();
} else {
$current = $stream->getCurrent();
throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Twig_Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
$value = $this->parseExpression();
$node->addElement($value, $key);
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
return $node;
}
public function parsePostfixExpression($node)
{
while (true) {
$token = $this->parser->getCurrentToken();
if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) {
if ('.' == $token->getValue() || '[' == $token->getValue()) {
$node = $this->parseSubscriptExpression($node);
} elseif ('|' == $token->getValue()) {
$node = $this->parseFilterExpression($node);
} else {
break;
}
} else {
break;
}
}
return $node;
}
public function getFunctionNode($name, $line)
{
switch ($name) {
case 'parent':
$this->parseArguments();
if (!count($this->parser->getBlockStack())) {
throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden.', $line, $this->parser->getFilename());
}
if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getFilename());
}
return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
case 'block':
return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line);
case 'attribute':
$args = $this->parseArguments();
if (count($args) < 2) {
throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getFilename());
}
return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : null, Twig_Template::ANY_CALL, $line);
default:
if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
$arguments = new Twig_Node_Expression_Array(array(), $line);
foreach ($this->parseArguments() as $n) {
$arguments->addElement($n);
}
$node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line);
$node->setAttribute('safe', true);
return $node;
}
$args = $this->parseArguments(true);
$class = $this->getFunctionNodeClass($name, $line);
return new $class($name, $args, $line);
}
}
public function parseSubscriptExpression($node)
{
$stream = $this->parser->getStream();
$token = $stream->next();
$lineno = $token->getLine();
$arguments = new Twig_Node_Expression_Array(array(), $lineno);
$type = Twig_Template::ANY_CALL;
if ($token->getValue() == '.') {
$token = $stream->next();
if (
$token->getType() == Twig_Token::NAME_TYPE
||
$token->getType() == Twig_Token::NUMBER_TYPE
||
($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue()))
) {
$arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
$type = Twig_TemplateInterface::METHOD_CALL;
foreach ($this->parseArguments() as $n) {
$arguments->addElement($n);
}
}
} else {
throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
}
if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
if (!$arg instanceof Twig_Node_Expression_Constant) {
throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
}
$name = $arg->getAttribute('value');
if ($this->parser->isReservedMacroName($name)) {
throw new Twig_Error_Syntax(sprintf('"%s" cannot be called as macro as it is a reserved keyword.', $name), $token->getLine(), $this->parser->getFilename());
}
$node = new Twig_Node_Expression_MethodCall($node, 'get'.$name, $arguments, $lineno);
$node->setAttribute('safe', true);
return $node;
}
} else {
$type = Twig_Template::ARRAY_CALL;
// slice?
$slice = false;
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
$slice = true;
$arg = new Twig_Node_Expression_Constant(0, $token->getLine());
} else {
$arg = $this->parseExpression();
}
if ($stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ':')) {
$slice = true;
}
if ($slice) {
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
$length = new Twig_Node_Expression_Constant(null, $token->getLine());
} else {
$length = $this->parseExpression();
}
$class = $this->getFilterNodeClass('slice', $token->getLine());
$arguments = new Twig_Node(array($arg, $length));
$filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine());
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
return $filter;
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
}
return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
}
public function parseFilterExpression($node)
{
$this->parser->getStream()->next();
return $this->parseFilterExpressionRaw($node);
}
public function parseFilterExpressionRaw($node, $tag = null)
{
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::PUNCTUATION_TYPE, '(')) {
$arguments = new Twig_Node();
} else {
$arguments = $this->parseArguments(true);
}
$class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
$node = new $class($node, $name, $arguments, $token->getLine(), $tag);
if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) {
break;
}
$this->parser->getStream()->next();
}
return $node;
}
/**
* Parses arguments.
*
* @param bool $namedArguments Whether to allow named arguments or not
* @param bool $definition Whether we are parsing arguments for a function definition
*
* @return Twig_Node
*
* @throws Twig_Error_Syntax
*/
public function parseArguments($namedArguments = false, $definition = false)
{
$args = array();
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) {
if (!empty($args)) {
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
}
if ($definition) {
$token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name');
$value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine());
} else {
$value = $this->parseExpression();
}
$name = null;
if ($namedArguments && $token = $stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
if (!$value instanceof Twig_Node_Expression_Name) {
throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given.', get_class($value)), $token->getLine(), $this->parser->getFilename());
}
$name = $value->getAttribute('name');
if ($definition) {
$value = $this->parsePrimaryExpression();
if (!$this->checkConstantExpression($value)) {
throw new Twig_Error_Syntax(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $this->parser->getFilename());
}
} else {
$value = $this->parseExpression();
}
}
if ($definition) {
if (null === $name) {
$name = $value->getAttribute('name');
$value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
}
$args[$name] = $value;
} else {
if (null === $name) {
$args[] = $value;
} else {
$args[$name] = $value;
}
}
}
$stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
return new Twig_Node($args);
}
public function parseAssignmentExpression()
{
$targets = array();
while (true) {
$token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
if (in_array($token->getValue(), array('true', 'false', 'none'))) {
throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s".', $token->getValue()), $token->getLine(), $this->parser->getFilename());
}
$targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine());
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
break;
}
}
return new Twig_Node($targets);
}
public function parseMultitargetExpression()
{
$targets = array();
while (true) {
$targets[] = $this->parseExpression();
if (!$this->parser->getStream()->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
break;
}
}
return new Twig_Node($targets);
}
protected function getFunctionNodeClass($name, $line)
{
$env = $this->parser->getEnvironment();
if (false === $function = $env->getFunction($name)) {
$e = new Twig_Error_Syntax(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getFilename());
$e->addSuggestions($name, array_keys($env->getFunctions()));
throw $e;
}
if ($function instanceof Twig_SimpleFunction && $function->isDeprecated()) {
$message = sprintf('Twig Function "%s" is deprecated', $function->getName());
if ($function->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $function->getAlternative());
}
$message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line);
@trigger_error($message, E_USER_DEPRECATED);
}
if ($function instanceof Twig_SimpleFunction) {
return $function->getNodeClass();
}
return $function instanceof Twig_Function_Node ? $function->getClass() : 'Twig_Node_Expression_Function';
}
protected function getFilterNodeClass($name, $line)
{
$env = $this->parser->getEnvironment();
if (false === $filter = $env->getFilter($name)) {
$e = new Twig_Error_Syntax(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getFilename());
$e->addSuggestions($name, array_keys($env->getFilters()));
throw $e;
}
if ($filter instanceof Twig_SimpleFilter && $filter->isDeprecated()) {
$message = sprintf('Twig Filter "%s" is deprecated', $filter->getName());
if ($filter->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $filter->getAlternative());
}
$message .= sprintf(' in %s at line %d.', $this->parser->getFilename(), $line);
@trigger_error($message, E_USER_DEPRECATED);
}
if ($filter instanceof Twig_SimpleFilter) {
return $filter->getNodeClass();
}
return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter';
}
// checks that the node only contains "constant" elements
protected function checkConstantExpression(Twig_NodeInterface $node)
{
if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array
|| $node instanceof Twig_Node_Expression_Unary_Neg || $node instanceof Twig_Node_Expression_Unary_Pos
)) {
return false;
}
foreach ($node as $n) {
if (!$this->checkConstantExpression($n)) {
return false;
}
}
return true;
}
}
Twig-1.23.1/lib/Twig/Extension.php 0000664 0000000 0000000 00000002536 12616650102 0016662 0 ustar 00root root 0000000 0000000 escapers[$strategy] = $callable;
}
/**
* Gets all defined escapers.
*
* @return array An array of escapers
*/
public function getEscapers()
{
return $this->escapers;
}
/**
* Sets the default format to be used by the date filter.
*
* @param string $format The default date format string
* @param string $dateIntervalFormat The default date interval format string
*/
public function setDateFormat($format = null, $dateIntervalFormat = null)
{
if (null !== $format) {
$this->dateFormats[0] = $format;
}
if (null !== $dateIntervalFormat) {
$this->dateFormats[1] = $dateIntervalFormat;
}
}
/**
* Gets the default format to be used by the date filter.
*
* @return array The default date format string and the default date interval format string
*/
public function getDateFormat()
{
return $this->dateFormats;
}
/**
* Sets the default timezone to be used by the date filter.
*
* @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object
*/
public function setTimezone($timezone)
{
$this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone);
}
/**
* Gets the default timezone to be used by the date filter.
*
* @return DateTimeZone The default timezone currently in use
*/
public function getTimezone()
{
if (null === $this->timezone) {
$this->timezone = new DateTimeZone(date_default_timezone_get());
}
return $this->timezone;
}
/**
* Sets the default format to be used by the number_format filter.
*
* @param int $decimal The number of decimal places to use.
* @param string $decimalPoint The character(s) to use for the decimal point.
* @param string $thousandSep The character(s) to use for the thousands separator.
*/
public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
{
$this->numberFormat = array($decimal, $decimalPoint, $thousandSep);
}
/**
* Get the default format used by the number_format filter.
*
* @return array The arguments for number_format()
*/
public function getNumberFormat()
{
return $this->numberFormat;
}
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_Use(),
new Twig_TokenParser_Filter(),
new Twig_TokenParser_Macro(),
new Twig_TokenParser_Import(),
new Twig_TokenParser_From(),
new Twig_TokenParser_Set(),
new Twig_TokenParser_Spaceless(),
new Twig_TokenParser_Flush(),
new Twig_TokenParser_Do(),
new Twig_TokenParser_Embed(),
);
}
public function getFilters()
{
$filters = array(
// formatting filters
new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('format', 'sprintf'),
new Twig_SimpleFilter('replace', 'twig_replace_filter'),
new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('abs', 'abs'),
new Twig_SimpleFilter('round', 'twig_round'),
// encoding
new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'),
new Twig_SimpleFilter('json_encode', 'twig_jsonencode_filter'),
new Twig_SimpleFilter('convert_encoding', 'twig_convert_encoding'),
// string filters
new Twig_SimpleFilter('title', 'twig_title_string_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('capitalize', 'twig_capitalize_string_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('upper', 'strtoupper'),
new Twig_SimpleFilter('lower', 'strtolower'),
new Twig_SimpleFilter('striptags', 'strip_tags'),
new Twig_SimpleFilter('trim', 'trim'),
new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
// array helpers
new Twig_SimpleFilter('join', 'twig_join_filter'),
new Twig_SimpleFilter('split', 'twig_split_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('sort', 'twig_sort_filter'),
new Twig_SimpleFilter('merge', 'twig_array_merge'),
new Twig_SimpleFilter('batch', 'twig_array_batch'),
// string/array filters
new Twig_SimpleFilter('reverse', 'twig_reverse_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('length', 'twig_length_filter', array('needs_environment' => true)),
new Twig_SimpleFilter('slice', 'twig_slice', array('needs_environment' => true)),
new Twig_SimpleFilter('first', 'twig_first', array('needs_environment' => true)),
new Twig_SimpleFilter('last', 'twig_last', array('needs_environment' => true)),
// iteration and runtime
new Twig_SimpleFilter('default', '_twig_default_filter', array('node_class' => 'Twig_Node_Expression_Filter_Default')),
new Twig_SimpleFilter('keys', 'twig_get_array_keys_filter'),
// escaping
new Twig_SimpleFilter('escape', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
new Twig_SimpleFilter('e', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
);
if (function_exists('mb_get_info')) {
$filters[] = new Twig_SimpleFilter('upper', 'twig_upper_filter', array('needs_environment' => true));
$filters[] = new Twig_SimpleFilter('lower', 'twig_lower_filter', array('needs_environment' => true));
}
return $filters;
}
public function getFunctions()
{
return array(
new Twig_SimpleFunction('max', 'max'),
new Twig_SimpleFunction('min', 'min'),
new Twig_SimpleFunction('range', 'range'),
new Twig_SimpleFunction('constant', 'twig_constant'),
new Twig_SimpleFunction('cycle', 'twig_cycle'),
new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))),
new Twig_SimpleFunction('source', 'twig_source', array('needs_environment' => true, 'is_safe' => array('all'))),
);
}
public function getTests()
{
return array(
new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')),
new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas', 'deprecated' => true, 'alternative' => 'same as')),
new Twig_SimpleTest('same as', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby', 'deprecated' => true, 'alternative' => 'divisible by')),
new Twig_SimpleTest('divisible by', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
new Twig_SimpleTest('empty', 'twig_test_empty'),
new Twig_SimpleTest('iterable', 'twig_test_iterable'),
);
}
public function getOperators()
{
return array(
array(
'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
'-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'),
'+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
),
array(
'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'matches' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Matches', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'starts with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_StartsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'ends with' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_EndsWith', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
'**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
),
);
}
public function parseNotTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
{
return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
}
public function parseTestExpression(Twig_Parser $parser, Twig_NodeInterface $node)
{
$stream = $parser->getStream();
list($name, $test) = $this->getTest($parser, $node->getLine());
if ($test instanceof Twig_SimpleTest && $test->isDeprecated()) {
$message = sprintf('Twig Test "%s" is deprecated', $name);
if ($test->getAlternative()) {
$message .= sprintf('. Use "%s" instead', $test->getAlternative());
}
$message .= sprintf(' in %s at line %d.', $stream->getFilename(), $stream->getCurrent()->getLine());
@trigger_error($message, E_USER_DEPRECATED);
}
$class = $this->getTestNodeClass($parser, $test);
$arguments = null;
if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
$arguments = $parser->getExpressionParser()->parseArguments(true);
}
return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
}
protected function getTest(Twig_Parser $parser, $line)
{
$stream = $parser->getStream();
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
$env = $parser->getEnvironment();
if ($test = $env->getTest($name)) {
return array($name, $test);
}
if ($stream->test(Twig_Token::NAME_TYPE)) {
// try 2-words tests
$name = $name.' '.$parser->getCurrentToken()->getValue();
if ($test = $env->getTest($name)) {
$parser->getStream()->next();
return array($name, $test);
}
}
$e = new Twig_Error_Syntax(sprintf('Unknown "%s" test.', $name), $line, $parser->getFilename());
$e->addSuggestions($name, array_keys($env->getTests()));
throw $e;
}
protected function getTestNodeClass(Twig_Parser $parser, $test)
{
if ($test instanceof Twig_SimpleTest) {
return $test->getNodeClass();
}
return $test instanceof Twig_Test_Node ? $test->getClass() : 'Twig_Node_Expression_Test';
}
public function getName()
{
return 'core';
}
}
/**
* Cycles over a value.
*
* @param ArrayAccess|array $values An array or an ArrayAccess instance
* @param int $position The cycle position
*
* @return string The next value in the cycle
*/
function twig_cycle($values, $position)
{
if (!is_array($values) && !$values instanceof ArrayAccess) {
return $values;
}
return $values[$position % count($values)];
}
/**
* Returns a random value depending on the supplied parameter type:
* - a random item from a Traversable or array
* - a random character from a string
* - a random integer between 0 and the integer parameter.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param Traversable|array|int|string $values The values to pick a random item from
*
* @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is).
*
* @return mixed A random value from the given sequence
*/
function twig_random(Twig_Environment $env, $values = null)
{
if (null === $values) {
return mt_rand();
}
if (is_int($values) || is_float($values)) {
return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values);
}
if ($values instanceof Traversable) {
$values = iterator_to_array($values);
} elseif (is_string($values)) {
if ('' === $values) {
return '';
}
if (null !== $charset = $env->getCharset()) {
if ('UTF-8' != $charset) {
$values = twig_convert_encoding($values, 'UTF-8', $charset);
}
// unicode version of str_split()
// split at all positions, but not after the start and not before the end
$values = preg_split('/(? $value) {
$values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
}
}
} else {
return $values[mt_rand(0, strlen($values) - 1)];
}
}
if (!is_array($values)) {
return $values;
}
if (0 === count($values)) {
throw new Twig_Error_Runtime('The random function cannot pick from an empty array.');
}
return $values[array_rand($values, 1)];
}
/**
* Converts a date to the given format.
*
*
* {{ post.published_at|date("m/d/Y") }}
*
*
* @param Twig_Environment $env A Twig_Environment instance
* @param DateTime|DateTimeInterface|DateInterval|string $date A date
* @param string|null $format The target format, null to use the default
* @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
*
* @return string The formatted date
*/
function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
{
if (null === $format) {
$formats = $env->getExtension('core')->getDateFormat();
$format = $date instanceof DateInterval ? $formats[1] : $formats[0];
}
if ($date instanceof DateInterval) {
return $date->format($format);
}
return twig_date_converter($env, $date, $timezone)->format($format);
}
/**
* Returns a new date object modified.
*
*
* {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
*
*
* @param Twig_Environment $env A Twig_Environment instance
* @param DateTime|string $date A date
* @param string $modifier A modifier string
*
* @return DateTime A new date object
*/
function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
{
$date = twig_date_converter($env, $date, false);
$resultDate = $date->modify($modifier);
// This is a hack to ensure PHP 5.2 support and support for DateTimeImmutable
// DateTime::modify does not return the modified DateTime object < 5.3.0
// and DateTimeImmutable does not modify $date.
return null === $resultDate ? $date : $resultDate;
}
/**
* Converts an input to a DateTime instance.
*
*
* {% if date(user.created_at) < date('+2days') %}
* {# do something #}
* {% endif %}
*
*
* @param Twig_Environment $env A Twig_Environment instance
* @param DateTime|DateTimeInterface|string|null $date A date
* @param DateTimeZone|string|null|false $timezone The target timezone, null to use the default, false to leave unchanged
*
* @return DateTime A DateTime instance
*/
function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null)
{
// determine the timezone
if (false !== $timezone) {
if (null === $timezone) {
$timezone = $env->getExtension('core')->getTimezone();
} elseif (!$timezone instanceof DateTimeZone) {
$timezone = new DateTimeZone($timezone);
}
}
// immutable dates
if ($date instanceof DateTimeImmutable) {
return false !== $timezone ? $date->setTimezone($timezone) : $date;
}
if ($date instanceof DateTime || $date instanceof DateTimeInterface) {
$date = clone $date;
if (false !== $timezone) {
$date->setTimezone($timezone);
}
return $date;
}
if (null === $date || 'now' === $date) {
return new DateTime($date, false !== $timezone ? $timezone : $env->getExtension('core')->getTimezone());
}
$asString = (string) $date;
if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
$date = new DateTime('@'.$date);
} else {
$date = new DateTime($date, $env->getExtension('core')->getTimezone());
}
if (false !== $timezone) {
$date->setTimezone($timezone);
}
return $date;
}
/**
* Replaces strings within a string.
*
* @param string $str String to replace in
* @param array|Traversable $from Replace values
* @param string|null $to Replace to, deprecated (@see http://php.net/manual/en/function.strtr.php)
*
* @return string
*/
function twig_replace_filter($str, $from, $to = null)
{
if ($from instanceof Traversable) {
$from = iterator_to_array($from);
} elseif (is_string($from) && is_string($to)) {
@trigger_error('Using "replace" with character by character replacement is deprecated and will be removed in Twig 2.0', E_USER_DEPRECATED);
return strtr($str, $from, $to);
} elseif (!is_array($from)) {
throw new Twig_Error_Runtime(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".',is_object($from) ? get_class($from) : gettype($from)));
}
return strtr($str, $from);
}
/**
* Rounds a number.
*
* @param int|float $value The value to round
* @param int|float $precision The rounding precision
* @param string $method The method to use for rounding
*
* @return int|float The rounded number
*/
function twig_round($value, $precision = 0, $method = 'common')
{
if ('common' == $method) {
return round($value, $precision);
}
if ('ceil' != $method && 'floor' != $method) {
throw new Twig_Error_Runtime('The round filter only supports the "common", "ceil", and "floor" methods.');
}
return $method($value * pow(10, $precision)) / pow(10, $precision);
}
/**
* Number format filter.
*
* All of the formatting options can be left null, in that case the defaults will
* be used. Supplying any of the parameters will override the defaults set in the
* environment object.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param mixed $number A float/int/string of the number to format
* @param int $decimal The number of decimal points to display.
* @param string $decimalPoint The character(s) to use for the decimal point.
* @param string $thousandSep The character(s) to use for the thousands separator.
*
* @return string The formatted number
*/
function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
{
$defaults = $env->getExtension('core')->getNumberFormat();
if (null === $decimal) {
$decimal = $defaults[0];
}
if (null === $decimalPoint) {
$decimalPoint = $defaults[1];
}
if (null === $thousandSep) {
$thousandSep = $defaults[2];
}
return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
}
/**
* URL encodes (RFC 3986) a string as a path segment or an array as a query string.
*
* @param string|array $url A URL or an array of query parameters
*
* @return string The URL encoded value
*/
function twig_urlencode_filter($url)
{
if (is_array($url)) {
if (defined('PHP_QUERY_RFC3986')) {
return http_build_query($url, '', '&', PHP_QUERY_RFC3986);
}
return http_build_query($url, '', '&');
}
return rawurlencode($url);
}
if (PHP_VERSION_ID < 50300) {
/**
* JSON encodes a variable.
*
* @param mixed $value The value to encode.
* @param int $options Not used on PHP 5.2.x
*
* @return mixed The JSON encoded value
*/
function twig_jsonencode_filter($value, $options = 0)
{
if ($value instanceof Twig_Markup) {
$value = (string) $value;
} elseif (is_array($value)) {
array_walk_recursive($value, '_twig_markup2string');
}
return json_encode($value);
}
} else {
/**
* JSON encodes a variable.
*
* @param mixed $value The value to encode.
* @param int $options Bitmask consisting of JSON_HEX_QUOT, JSON_HEX_TAG, JSON_HEX_AMP, JSON_HEX_APOS, JSON_NUMERIC_CHECK, JSON_PRETTY_PRINT, JSON_UNESCAPED_SLASHES, JSON_FORCE_OBJECT
*
* @return mixed The JSON encoded value
*/
function twig_jsonencode_filter($value, $options = 0)
{
if ($value instanceof Twig_Markup) {
$value = (string) $value;
} elseif (is_array($value)) {
array_walk_recursive($value, '_twig_markup2string');
}
return json_encode($value, $options);
}
}
function _twig_markup2string(&$value)
{
if ($value instanceof Twig_Markup) {
$value = (string) $value;
}
}
/**
* Merges an array with another one.
*
*
* {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
*
* {% set items = items|merge({ 'peugeot': 'car' }) %}
*
* {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
*
*
* @param array|Traversable $arr1 An array
* @param array|Traversable $arr2 An array
*
* @return array The merged array
*/
function twig_array_merge($arr1, $arr2)
{
if ($arr1 instanceof Traversable) {
$arr1 = iterator_to_array($arr1);
} elseif (!is_array($arr1)) {
throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', gettype($arr1)));
}
if ($arr2 instanceof Traversable) {
$arr2 = iterator_to_array($arr2);
} elseif (!is_array($arr2)) {
throw new Twig_Error_Runtime(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', gettype($arr2)));
}
return array_merge($arr1, $arr2);
}
/**
* Slices a variable.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param mixed $item A variable
* @param int $start Start of the slice
* @param int $length Size of the slice
* @param bool $preserveKeys Whether to preserve key or not (when the input is an array)
*
* @return mixed The sliced variable
*/
function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false)
{
if ($item instanceof Traversable) {
if ($item instanceof IteratorAggregate) {
$item = $item->getIterator();
}
if ($start >= 0 && $length >= 0 && $item instanceof Iterator) {
try {
return iterator_to_array(new LimitIterator($item, $start, $length === null ? -1 : $length), $preserveKeys);
} catch (OutOfBoundsException $exception) {
return array();
}
}
$item = iterator_to_array($item, $preserveKeys);
}
if (is_array($item)) {
return array_slice($item, $start, $length, $preserveKeys);
}
$item = (string) $item;
if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
return (string) mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
}
return (string) (null === $length ? substr($item, $start) : substr($item, $start, $length));
}
/**
* Returns the first element of the item.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param mixed $item A variable
*
* @return mixed The first element of the item
*/
function twig_first(Twig_Environment $env, $item)
{
$elements = twig_slice($env, $item, 0, 1, false);
return is_string($elements) ? $elements : current($elements);
}
/**
* Returns the last element of the item.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param mixed $item A variable
*
* @return mixed The last element of the item
*/
function twig_last(Twig_Environment $env, $item)
{
$elements = twig_slice($env, $item, -1, 1, false);
return is_string($elements) ? $elements : current($elements);
}
/**
* Joins the values to a string.
*
* The separator between elements is an empty string per default, you can define it with the optional parameter.
*
*
* {{ [1, 2, 3]|join('|') }}
* {# returns 1|2|3 #}
*
* {{ [1, 2, 3]|join }}
* {# returns 123 #}
*
*
* @param array $value An array
* @param string $glue The separator
*
* @return string The concatenated string
*/
function twig_join_filter($value, $glue = '')
{
if ($value instanceof Traversable) {
$value = iterator_to_array($value, false);
}
return implode($glue, (array) $value);
}
/**
* Splits the string into an array.
*
*
* {{ "one,two,three"|split(',') }}
* {# returns [one, two, three] #}
*
* {{ "one,two,three,four,five"|split(',', 3) }}
* {# returns [one, two, "three,four,five"] #}
*
* {{ "123"|split('') }}
* {# returns [1, 2, 3] #}
*
* {{ "aabbcc"|split('', 2) }}
* {# returns [aa, bb, cc] #}
*
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $value A string
* @param string $delimiter The delimiter
* @param int $limit The limit
*
* @return array The split string as an array
*/
function twig_split_filter(Twig_Environment $env, $value, $delimiter, $limit = null)
{
if (!empty($delimiter)) {
return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
}
if (!function_exists('mb_get_info') || null === $charset = $env->getCharset()) {
return str_split($value, null === $limit ? 1 : $limit);
}
if ($limit <= 1) {
return preg_split('/(?
* {% for key in array|keys %}
* {# ... #}
* {% endfor %}
*
*
* @param array $array An array
*
* @return array The keys
*/
function twig_get_array_keys_filter($array)
{
if ($array instanceof Traversable) {
return array_keys(iterator_to_array($array));
}
if (!is_array($array)) {
return array();
}
return array_keys($array);
}
/**
* Reverses a variable.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param array|Traversable|string $item An array, a Traversable instance, or a string
* @param bool $preserveKeys Whether to preserve key or not
*
* @return mixed The reversed input
*/
function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false)
{
if ($item instanceof Traversable) {
return array_reverse(iterator_to_array($item), $preserveKeys);
}
if (is_array($item)) {
return array_reverse($item, $preserveKeys);
}
if (null !== $charset = $env->getCharset()) {
$string = (string) $item;
if ('UTF-8' != $charset) {
$item = twig_convert_encoding($string, 'UTF-8', $charset);
}
preg_match_all('/./us', $item, $matches);
$string = implode('', array_reverse($matches[0]));
if ('UTF-8' != $charset) {
$string = twig_convert_encoding($string, $charset, 'UTF-8');
}
return $string;
}
return strrev((string) $item);
}
/**
* Sorts an array.
*
* @param array|Traversable $array
*
* @return array
*/
function twig_sort_filter($array)
{
if ($array instanceof Traversable) {
$array = iterator_to_array($array);
} elseif (!is_array($array)) {
throw new Twig_Error_Runtime(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', gettype($array)));
}
asort($array);
return $array;
}
/**
* @internal
*/
function twig_in_filter($value, $compare)
{
if (is_array($compare)) {
return in_array($value, $compare, is_object($value) || is_resource($value));
} elseif (is_string($compare) && (is_string($value) || is_int($value) || is_float($value))) {
return '' === $value || false !== strpos($compare, (string) $value);
} elseif ($compare instanceof Traversable) {
return in_array($value, iterator_to_array($compare, false), is_object($value) || is_resource($value));
}
return false;
}
/**
* Escapes a string.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $string The value to be escaped
* @param string $strategy The escaping strategy
* @param string $charset The charset
* @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
*
* @return string
*/
function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
{
if ($autoescape && $string instanceof Twig_Markup) {
return $string;
}
if (!is_string($string)) {
if (is_object($string) && method_exists($string, '__toString')) {
$string = (string) $string;
} elseif (in_array($strategy, array('html', 'js', 'css', 'html_attr', 'url'))) {
return $string;
}
}
if (null === $charset) {
$charset = $env->getCharset();
}
switch ($strategy) {
case 'html':
// see http://php.net/htmlspecialchars
// Using a static variable to avoid initializing the array
// each time the function is called. Moving the declaration on the
// top of the function slow downs other escaping strategies.
static $htmlspecialcharsCharsets;
if (null === $htmlspecialcharsCharsets) {
if (defined('HHVM_VERSION')) {
$htmlspecialcharsCharsets = array('utf-8' => true, 'UTF-8' => true);
} else {
$htmlspecialcharsCharsets = array(
'ISO-8859-1' => true, 'ISO8859-1' => true,
'ISO-8859-15' => true, 'ISO8859-15' => true,
'utf-8' => true, 'UTF-8' => true,
'CP866' => true, 'IBM866' => true, '866' => true,
'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
'1251' => true,
'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
'BIG5' => true, '950' => true,
'GB2312' => true, '936' => true,
'BIG5-HKSCS' => true,
'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
'EUC-JP' => true, 'EUCJP' => true,
'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
);
}
}
if (isset($htmlspecialcharsCharsets[$charset])) {
return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
}
if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
// cache the lowercase variant for future iterations
$htmlspecialcharsCharsets[$charset] = true;
return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
}
$string = twig_convert_encoding($string, 'UTF-8', $charset);
$string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
return twig_convert_encoding($string, $charset, 'UTF-8');
case 'js':
// escape all non-alphanumeric characters
// into their \xHH or \uHHHH representations
if ('UTF-8' != $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset);
}
if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
}
$string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
if ('UTF-8' != $charset) {
$string = twig_convert_encoding($string, $charset, 'UTF-8');
}
return $string;
case 'css':
if ('UTF-8' != $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset);
}
if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
}
$string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
if ('UTF-8' != $charset) {
$string = twig_convert_encoding($string, $charset, 'UTF-8');
}
return $string;
case 'html_attr':
if ('UTF-8' != $charset) {
$string = twig_convert_encoding($string, 'UTF-8', $charset);
}
if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
}
$string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
if ('UTF-8' != $charset) {
$string = twig_convert_encoding($string, $charset, 'UTF-8');
}
return $string;
case 'url':
if (PHP_VERSION_ID < 50300) {
return str_replace('%7E', '~', rawurlencode($string));
}
return rawurlencode($string);
default:
static $escapers;
if (null === $escapers) {
$escapers = $env->getExtension('core')->getEscapers();
}
if (isset($escapers[$strategy])) {
return call_user_func($escapers[$strategy], $env, $string, $charset);
}
$validStrategies = implode(', ', array_merge(array('html', 'js', 'url', 'css', 'html_attr'), array_keys($escapers)));
throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies));
}
}
/**
* @internal
*/
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'));
}
return array();
}
return array('html');
}
if (function_exists('mb_convert_encoding')) {
function twig_convert_encoding($string, $to, $from)
{
return mb_convert_encoding($string, $to, $from);
}
} elseif (function_exists('iconv')) {
function twig_convert_encoding($string, $to, $from)
{
return iconv($from, $to, $string);
}
} 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'.strtoupper(substr('00'.bin2hex($char), -2));
}
// \uHHHH
$char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4));
}
function _twig_escape_css_callback($matches)
{
$char = $matches[0];
// \xHH
if (!isset($char[1])) {
$hex = ltrim(strtoupper(bin2hex($char)), '0');
if (0 === strlen($hex)) {
$hex = '0';
}
return '\\'.$hex.' ';
}
// \uHHHH
$char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' ';
}
/**
* This function is adapted from code coming from Zend Framework.
*
* @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
* @license http://framework.zend.com/license/new-bsd New BSD License
*/
function _twig_escape_html_attr_callback($matches)
{
/*
* While HTML supports far more named entities, the lowest common denominator
* has become HTML5's XML Serialisation which is restricted to the those named
* entities that XML supports. Using HTML entities would result in this error:
* XML Parsing Error: undefined entity
*/
static $entityMap = array(
34 => 'quot', /* quotation mark */
38 => 'amp', /* ampersand */
60 => 'lt', /* less-than sign */
62 => 'gt', /* greater-than sign */
);
$chr = $matches[0];
$ord = ord($chr);
/*
* The following replaces characters undefined in HTML with the
* hex entity for the Unicode replacement character.
*/
if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) {
return '�';
}
/*
* Check if the current character to escape has a name entity we should
* replace it with while grabbing the hex value of the character.
*/
if (strlen($chr) == 1) {
$hex = strtoupper(substr('00'.bin2hex($chr), -2));
} else {
$chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8');
$hex = strtoupper(substr('0000'.bin2hex($chr), -4));
}
$int = hexdec($hex);
if (array_key_exists($int, $entityMap)) {
return sprintf('&%s;', $entityMap[$int]);
}
/*
* Per OWASP recommendations, we'll use hex entities for any other
* characters where a named entity does not exist.
*/
return sprintf('%s;', $hex);
}
// add multibyte extensions if possible
if (function_exists('mb_get_info')) {
/**
* Returns the length of a variable.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param mixed $thing A variable
*
* @return int The length of the value
*/
function twig_length_filter(Twig_Environment $env, $thing)
{
return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
}
/**
* Converts a string to uppercase.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $string A string
*
* @return string The uppercased string
*/
function twig_upper_filter(Twig_Environment $env, $string)
{
if (null !== ($charset = $env->getCharset())) {
return mb_strtoupper($string, $charset);
}
return strtoupper($string);
}
/**
* Converts a string to lowercase.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $string A string
*
* @return string The lowercased string
*/
function twig_lower_filter(Twig_Environment $env, $string)
{
if (null !== ($charset = $env->getCharset())) {
return mb_strtolower($string, $charset);
}
return strtolower($string);
}
/**
* Returns a titlecased string.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $string A string
*
* @return string The titlecased 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));
}
/**
* Returns a capitalized string.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $string A string
*
* @return string The capitalized string
*/
function twig_capitalize_string_filter(Twig_Environment $env, $string)
{
if (null !== $charset = $env->getCharset()) {
return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
}
return ucfirst(strtolower($string));
}
}
// and byte fallback
else {
/**
* Returns the length of a variable.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param mixed $thing A variable
*
* @return int The length of the value
*/
function twig_length_filter(Twig_Environment $env, $thing)
{
return is_scalar($thing) ? strlen($thing) : count($thing);
}
/**
* Returns a titlecased string.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $string A string
*
* @return string The titlecased string
*/
function twig_title_string_filter(Twig_Environment $env, $string)
{
return ucwords(strtolower($string));
}
/**
* Returns a capitalized string.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $string A string
*
* @return string The capitalized string
*/
function twig_capitalize_string_filter(Twig_Environment $env, $string)
{
return ucfirst(strtolower($string));
}
}
/**
* @internal
*/
function twig_ensure_traversable($seq)
{
if ($seq instanceof Traversable || is_array($seq)) {
return $seq;
}
return array();
}
/**
* Checks if a variable is empty.
*
*
* {# evaluates to true if the foo variable is null, false, or the empty string #}
* {% if foo is empty %}
* {# ... #}
* {% endif %}
*
*
* @param mixed $value A variable
*
* @return bool true if the value is empty, false otherwise
*/
function twig_test_empty($value)
{
if ($value instanceof Countable) {
return 0 == count($value);
}
return '' === $value || false === $value || null === $value || array() === $value;
}
/**
* Checks if a variable is traversable.
*
*
* {# evaluates to true if the foo variable is an array or a traversable object #}
* {% if foo is traversable %}
* {# ... #}
* {% endif %}
*
*
* @param mixed $value A variable
*
* @return bool true if the value is traversable
*/
function twig_test_iterable($value)
{
return $value instanceof Traversable || is_array($value);
}
/**
* Renders a template.
*
* @param Twig_Environment $env
* @param array $context
* @param string|array $template The template to render or an array of templates to try consecutively
* @param array $variables The variables to pass to the template
* @param bool $withContext
* @param bool $ignoreMissing Whether to ignore missing templates or not
* @param bool $sandboxed Whether to sandbox the template or not
*
* @return string The rendered template
*/
function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
{
$alreadySandboxed = false;
$sandbox = null;
if ($withContext) {
$variables = array_merge($context, $variables);
}
if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) {
$sandbox = $env->getExtension('sandbox');
if (!$alreadySandboxed = $sandbox->isSandboxed()) {
$sandbox->enableSandbox();
}
}
$result = null;
try {
$result = $env->resolveTemplate($template)->render($variables);
} catch (Twig_Error_Loader $e) {
if (!$ignoreMissing) {
if ($isSandboxed && !$alreadySandboxed) {
$sandbox->disableSandbox();
}
throw $e;
}
}
if ($isSandboxed && !$alreadySandboxed) {
$sandbox->disableSandbox();
}
return $result;
}
/**
* Returns a template content without rendering it.
*
* @param Twig_Environment $env
* @param string $name The template name
* @param bool $ignoreMissing Whether to ignore missing templates or not
*
* @return string The template source
*/
function twig_source(Twig_Environment $env, $name, $ignoreMissing = false)
{
try {
return $env->getLoader()->getSource($name);
} catch (Twig_Error_Loader $e) {
if (!$ignoreMissing) {
throw $e;
}
}
}
/**
* Provides the ability to get constants from instances as well as class/global constants.
*
* @param string $constant The name of the constant
* @param null|object $object The object to get the constant from
*
* @return string
*/
function twig_constant($constant, $object = null)
{
if (null !== $object) {
$constant = get_class($object).'::'.$constant;
}
return constant($constant);
}
/**
* Batches item.
*
* @param array $items An array of items
* @param int $size The size of the batch
* @param mixed $fill A value used to fill missing items
*
* @return array
*/
function twig_array_batch($items, $size, $fill = null)
{
if ($items instanceof Traversable) {
$items = iterator_to_array($items, false);
}
$size = ceil($size);
$result = array_chunk($items, $size, true);
if (null !== $fill && !empty($result)) {
$last = count($result) - 1;
if ($fillCount = $size - count($result[$last])) {
$result[$last] = array_merge(
$result[$last],
array_fill(0, $fillCount, $fill)
);
}
}
return $result;
}
Twig-1.23.1/lib/Twig/Extension/Debug.php 0000664 0000000 0000000 00000003337 12616650102 0017710 0 ustar 00root root 0000000 0000000 $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)),
);
}
public function getName()
{
return 'debug';
}
}
function twig_var_dump(Twig_Environment $env, $context)
{
if (!$env->isDebug()) {
return;
}
ob_start();
$count = func_num_args();
if (2 === $count) {
$vars = array();
foreach ($context as $key => $value) {
if (!$value instanceof Twig_Template) {
$vars[$key] = $value;
}
}
var_dump($vars);
} else {
for ($i = 2; $i < $count; ++$i) {
var_dump(func_get_arg($i));
}
}
return ob_get_clean();
}
Twig-1.23.1/lib/Twig/Extension/Escaper.php 0000664 0000000 0000000 00000005114 12616650102 0020237 0 ustar 00root root 0000000 0000000 setDefaultStrategy($defaultStrategy);
}
public function getTokenParsers()
{
return array(new Twig_TokenParser_AutoEscape());
}
public function getNodeVisitors()
{
return array(new Twig_NodeVisitor_Escaper());
}
public function getFilters()
{
return array(
new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))),
);
}
/**
* Sets the default strategy to use when not defined by the user.
*
* The strategy can be a valid PHP callback that takes the template
* "filename" as an argument and returns the strategy to use.
*
* @param string|false|callable $defaultStrategy An escaping strategy
*/
public function setDefaultStrategy($defaultStrategy)
{
// for BC
if (true === $defaultStrategy) {
@trigger_error('Using "true" as the default strategy is deprecated. Use "html" instead.', E_USER_DEPRECATED);
$defaultStrategy = 'html';
}
if ('filename' === $defaultStrategy) {
$defaultStrategy = array('Twig_FileExtensionEscapingStrategy', 'guess');
}
$this->defaultStrategy = $defaultStrategy;
}
/**
* Gets the default strategy to use when not defined by the user.
*
* @param string $filename The template "filename"
*
* @return string|false The default strategy to use for the template
*/
public function getDefaultStrategy($filename)
{
// disable string callables to avoid calling a function named html or js,
// or any other upcoming escaping strategy
if (!is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
return call_user_func($this->defaultStrategy, $filename);
}
return $this->defaultStrategy;
}
public function getName()
{
return 'escaper';
}
}
/**
* Marks a variable as being safe.
*
* @param string $string A PHP variable
*
* @return string
*/
function twig_raw_filter($string)
{
return $string;
}
Twig-1.23.1/lib/Twig/Extension/GlobalsInterface.php 0000664 0000000 0000000 00000001000 12616650102 0022047 0 ustar 00root root 0000000 0000000
*/
interface Twig_Extension_GlobalsInterface
{
}
Twig-1.23.1/lib/Twig/Extension/InitRuntimeInterface.php 0000664 0000000 0000000 00000001006 12616650102 0022741 0 ustar 00root root 0000000 0000000
*/
interface Twig_Extension_InitRuntimeInterface
{
}
Twig-1.23.1/lib/Twig/Extension/Optimizer.php 0000664 0000000 0000000 00000001116 12616650102 0020635 0 ustar 00root root 0000000 0000000 optimizers = $optimizers;
}
public function getNodeVisitors()
{
return array(new Twig_NodeVisitor_Optimizer($this->optimizers));
}
public function getName()
{
return 'optimizer';
}
}
Twig-1.23.1/lib/Twig/Extension/Profiler.php 0000664 0000000 0000000 00000001761 12616650102 0020443 0 ustar 00root root 0000000 0000000 actives[] = $profile;
}
public function enter(Twig_Profiler_Profile $profile)
{
$this->actives[0]->addProfile($profile);
array_unshift($this->actives, $profile);
}
public function leave(Twig_Profiler_Profile $profile)
{
$profile->leave();
array_shift($this->actives);
if (1 === count($this->actives)) {
$this->actives[0]->leave();
}
}
public function getNodeVisitors()
{
return array(new Twig_Profiler_NodeVisitor_Profiler($this->getName()));
}
public function getName()
{
return 'profiler';
}
}
Twig-1.23.1/lib/Twig/Extension/Sandbox.php 0000664 0000000 0000000 00000004160 12616650102 0020253 0 ustar 00root root 0000000 0000000 policy = $policy;
$this->sandboxedGlobally = $sandboxed;
}
public function getTokenParsers()
{
return array(new Twig_TokenParser_Sandbox());
}
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, $functions)
{
if ($this->isSandboxed()) {
$this->policy->checkSecurity($tags, $filters, $functions);
}
}
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);
}
}
public function ensureToStringAllowed($obj)
{
if ($this->isSandboxed() && is_object($obj)) {
$this->policy->checkMethodAllowed($obj, '__toString');
}
return $obj;
}
public function getName()
{
return 'sandbox';
}
}
Twig-1.23.1/lib/Twig/Extension/Staging.php 0000664 0000000 0000000 00000003475 12616650102 0020261 0 ustar 00root root 0000000 0000000
*
* @internal
*/
class Twig_Extension_Staging extends Twig_Extension
{
protected $functions = array();
protected $filters = array();
protected $visitors = array();
protected $tokenParsers = array();
protected $globals = array();
protected $tests = array();
public function addFunction($name, $function)
{
$this->functions[$name] = $function;
}
public function getFunctions()
{
return $this->functions;
}
public function addFilter($name, $filter)
{
$this->filters[$name] = $filter;
}
public function getFilters()
{
return $this->filters;
}
public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
{
$this->visitors[] = $visitor;
}
public function getNodeVisitors()
{
return $this->visitors;
}
public function addTokenParser(Twig_TokenParserInterface $parser)
{
$this->tokenParsers[] = $parser;
}
public function getTokenParsers()
{
return $this->tokenParsers;
}
public function addGlobal($name, $value)
{
$this->globals[$name] = $value;
}
public function getGlobals()
{
return $this->globals;
}
public function addTest($name, $test)
{
$this->tests[$name] = $test;
}
public function getTests()
{
return $this->tests;
}
public function getName()
{
return 'staging';
}
}
Twig-1.23.1/lib/Twig/Extension/StringLoader.php 0000664 0000000 0000000 00000001770 12616650102 0021256 0 ustar 00root root 0000000 0000000 true)),
);
}
public function getName()
{
return 'string_loader';
}
}
/**
* Loads a template from a string.
*
*
* {{ include(template_from_string("Hello {{ name }}")) }}
*
*
* @param Twig_Environment $env A Twig_Environment instance
* @param string $template A template as a string or object implementing __toString()
*
* @return Twig_Template A Twig_Template instance
*/
function twig_template_from_string(Twig_Environment $env, $template)
{
return $env->createTemplate((string) $template);
}
Twig-1.23.1/lib/Twig/ExtensionInterface.php 0000664 0000000 0000000 00000004275 12616650102 0020505 0 ustar 00root root 0000000 0000000
*/
interface Twig_ExtensionInterface
{
/**
* Initializes the runtime environment.
*
* This is where you can load some file that contains filter functions for instance.
*
* @param Twig_Environment $environment The current Twig_Environment instance
*
* @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_InitRuntimeInterace instead
*/
public function initRuntime(Twig_Environment $environment);
/**
* Returns the token parser instances to add to the existing list.
*
* @return Twig_TokenParserInterface[]
*/
public function getTokenParsers();
/**
* Returns the node visitor instances to add to the existing list.
*
* @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
*/
public function getNodeVisitors();
/**
* Returns a list of filters to add to the existing list.
*
* @return Twig_SimpleFilter[]
*/
public function getFilters();
/**
* Returns a list of tests to add to the existing list.
*
* @return Twig_SimpleTest[]
*/
public function getTests();
/**
* Returns a list of functions to add to the existing list.
*
* @return Twig_SimpleFunction[]
*/
public function getFunctions();
/**
* Returns a list of operators to add to the existing list.
*
* @return array An array of operators
*/
public function getOperators();
/**
* Returns a list of global variables to add to the existing list.
*
* @return array An array of global variables
*
* @deprecated since 1.23 (to be removed in 2.0), implement Twig_Extension_GlobalsInterface instead
*/
public function getGlobals();
/**
* Returns the name of the extension.
*
* @return string The extension name
*/
public function getName();
}
Twig-1.23.1/lib/Twig/FileExtensionEscapingStrategy.php 0000664 0000000 0000000 00000002660 12616650102 0022655 0 ustar 00root root 0000000 0000000
*/
class Twig_FileExtensionEscapingStrategy
{
/**
* Guesses the best autoescaping strategy based on the file name.
*
* @param string $filename The template file name
*
* @return string|false The escaping strategy name to use or false to disable
*/
public static function guess($filename)
{
if (in_array(substr($filename, -1), array('/', '\\'))) {
return 'html'; // return html for directories
}
if ('.twig' === substr($filename, -5)) {
$filename = substr($filename, 0, -5);
}
$extension = pathinfo($filename, PATHINFO_EXTENSION);
switch ($extension) {
case 'js':
return 'js';
case 'css':
return 'css';
case 'txt':
return false;
default:
return 'html';
}
}
}
Twig-1.23.1/lib/Twig/Filter.php 0000664 0000000 0000000 00000003724 12616650102 0016133 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface
{
protected $options;
protected $arguments = array();
public function __construct(array $options = array())
{
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'pre_escape' => null,
'preserves_safety' => null,
'callable' => null,
), $options);
}
public function setArguments($arguments)
{
$this->arguments = $arguments;
}
public function getArguments()
{
return $this->arguments;
}
public function needsEnvironment()
{
return $this->options['needs_environment'];
}
public function needsContext()
{
return $this->options['needs_context'];
}
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);
}
}
public function getPreservesSafety()
{
return $this->options['preserves_safety'];
}
public function getPreEscape()
{
return $this->options['pre_escape'];
}
public function getCallable()
{
return $this->options['callable'];
}
}
Twig-1.23.1/lib/Twig/Filter/ 0000775 0000000 0000000 00000000000 12616650102 0015414 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Filter/Function.php 0000664 0000000 0000000 00000001621 12616650102 0017712 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Function extends Twig_Filter
{
protected $function;
public function __construct($function, array $options = array())
{
$options['callable'] = $function;
parent::__construct($options);
$this->function = $function;
}
public function compile()
{
return $this->function;
}
}
Twig-1.23.1/lib/Twig/Filter/Method.php 0000664 0000000 0000000 00000002105 12616650102 0017343 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Method extends Twig_Filter
{
protected $extension;
protected $method;
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
{
$options['callable'] = array($extension, $method);
parent::__construct($options);
$this->extension = $extension;
$this->method = $method;
}
public function compile()
{
return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
}
}
Twig-1.23.1/lib/Twig/Filter/Node.php 0000664 0000000 0000000 00000001574 12616650102 0017021 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Filter_Node extends Twig_Filter
{
protected $class;
public function __construct($class, array $options = array())
{
parent::__construct($options);
$this->class = $class;
}
public function getClass()
{
return $this->class;
}
public function compile()
{
}
}
Twig-1.23.1/lib/Twig/FilterCallableInterface.php 0000664 0000000 0000000 00000000734 12616650102 0021372 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FilterCallableInterface
{
public function getCallable();
}
Twig-1.23.1/lib/Twig/FilterInterface.php 0000664 0000000 0000000 00000001521 12616650102 0017745 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FilterInterface
{
/**
* Compiles a filter.
*
* @return string The PHP code for the filter
*/
public function compile();
public function needsEnvironment();
public function needsContext();
public function getSafe(Twig_Node $filterArgs);
public function getPreservesSafety();
public function getPreEscape();
public function setArguments($arguments);
public function getArguments();
}
Twig-1.23.1/lib/Twig/Function.php 0000664 0000000 0000000 00000003357 12616650102 0016475 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface
{
protected $options;
protected $arguments = array();
public function __construct(array $options = array())
{
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'callable' => null,
), $options);
}
public function setArguments($arguments)
{
$this->arguments = $arguments;
}
public function getArguments()
{
return $this->arguments;
}
public function needsEnvironment()
{
return $this->options['needs_environment'];
}
public function needsContext()
{
return $this->options['needs_context'];
}
public function getSafe(Twig_Node $functionArgs)
{
if (isset($this->options['is_safe'])) {
return $this->options['is_safe'];
}
if (isset($this->options['is_safe_callback'])) {
return call_user_func($this->options['is_safe_callback'], $functionArgs);
}
return array();
}
public function getCallable()
{
return $this->options['callable'];
}
}
Twig-1.23.1/lib/Twig/Function/ 0000775 0000000 0000000 00000000000 12616650102 0015754 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Function/Function.php 0000664 0000000 0000000 00000001671 12616650102 0020257 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Function extends Twig_Function
{
protected $function;
public function __construct($function, array $options = array())
{
$options['callable'] = $function;
parent::__construct($options);
$this->function = $function;
}
public function compile()
{
return $this->function;
}
}
Twig-1.23.1/lib/Twig/Function/Method.php 0000664 0000000 0000000 00000002155 12616650102 0017710 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Method extends Twig_Function
{
protected $extension;
protected $method;
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
{
$options['callable'] = array($extension, $method);
parent::__construct($options);
$this->extension = $extension;
$this->method = $method;
}
public function compile()
{
return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
}
}
Twig-1.23.1/lib/Twig/Function/Node.php 0000664 0000000 0000000 00000001610 12616650102 0017350 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Function_Node extends Twig_Function
{
protected $class;
public function __construct($class, array $options = array())
{
parent::__construct($options);
$this->class = $class;
}
public function getClass()
{
return $this->class;
}
public function compile()
{
}
}
Twig-1.23.1/lib/Twig/FunctionCallableInterface.php 0000664 0000000 0000000 00000000742 12616650102 0021731 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FunctionCallableInterface
{
public function getCallable();
}
Twig-1.23.1/lib/Twig/FunctionInterface.php 0000664 0000000 0000000 00000001447 12616650102 0020314 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_FunctionInterface
{
/**
* Compiles a function.
*
* @return string The PHP code for the function
*/
public function compile();
public function needsEnvironment();
public function needsContext();
public function getSafe(Twig_Node $filterArgs);
public function setArguments($arguments);
public function getArguments();
}
Twig-1.23.1/lib/Twig/Lexer.php 0000664 0000000 0000000 00000037603 12616650102 0015770 0 ustar 00root root 0000000 0000000
*/
class Twig_Lexer implements Twig_LexerInterface
{
protected $tokens;
protected $code;
protected $cursor;
protected $lineno;
protected $end;
protected $state;
protected $states;
protected $brackets;
protected $env;
protected $filename;
protected $options;
protected $regexes;
protected $position;
protected $positions;
protected $currentVarBlockLine;
const STATE_DATA = 0;
const STATE_BLOCK = 1;
const STATE_VAR = 2;
const STATE_STRING = 3;
const STATE_INTERPOLATION = 4;
const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
const REGEX_DQ_STRING_DELIM = '/"/A';
const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
const PUNCTUATION = '()[]{}?:.,|';
public function __construct(Twig_Environment $env, array $options = array())
{
$this->env = $env;
$this->options = array_merge(array(
'tag_comment' => array('{#', '#}'),
'tag_block' => array('{%', '%}'),
'tag_variable' => array('{{', '}}'),
'whitespace_trim' => '-',
'interpolation' => array('#{', '}'),
), $options);
$this->regexes = array(
'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
'lex_block' => '/\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')\n?/A',
'lex_raw_data' => '/('.preg_quote($this->options['tag_block'][0].$this->options['whitespace_trim'], '/').'|'.preg_quote($this->options['tag_block'][0], '/').')\s*(?:end%s)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/s',
'operator' => $this->getOperatorRegex(),
'lex_comment' => '/(?:'.preg_quote($this->options['whitespace_trim'], '/').preg_quote($this->options['tag_comment'][1], '/').'\s*|'.preg_quote($this->options['tag_comment'][1], '/').')\n?/s',
'lex_block_raw' => '/\s*(raw|verbatim)\s*(?:'.preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_block'][1], '/').')/As',
'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
'lex_tokens_start' => '/('.preg_quote($this->options['tag_variable'][0], '/').'|'.preg_quote($this->options['tag_block'][0], '/').'|'.preg_quote($this->options['tag_comment'][0], '/').')('.preg_quote($this->options['whitespace_trim'], '/').')?/s',
'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
);
}
/**
* {@inheritdoc}
*/
public function tokenize($code, $filename = null)
{
if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
$mbEncoding = mb_internal_encoding();
mb_internal_encoding('ASCII');
} else {
$mbEncoding = null;
}
$this->code = str_replace(array("\r\n", "\r"), "\n", $code);
$this->filename = $filename;
$this->cursor = 0;
$this->lineno = 1;
$this->end = strlen($this->code);
$this->tokens = array();
$this->state = self::STATE_DATA;
$this->states = array();
$this->brackets = array();
$this->position = -1;
// find all token starts in one go
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
$this->positions = $matches;
while ($this->cursor < $this->end) {
// dispatch to the lexing functions depending
// on the current state
switch ($this->state) {
case self::STATE_DATA:
$this->lexData();
break;
case self::STATE_BLOCK:
$this->lexBlock();
break;
case self::STATE_VAR:
$this->lexVar();
break;
case self::STATE_STRING:
$this->lexString();
break;
case self::STATE_INTERPOLATION:
$this->lexInterpolation();
break;
}
}
$this->pushToken(Twig_Token::EOF_TYPE);
if (!empty($this->brackets)) {
list($expect, $lineno) = array_pop($this->brackets);
throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->filename);
}
if ($mbEncoding) {
mb_internal_encoding($mbEncoding);
}
return new Twig_TokenStream($this->tokens, $this->filename);
}
protected function lexData()
{
// if no matches are left we return the rest of the template as simple text token
if ($this->position == count($this->positions[0]) - 1) {
$this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor));
$this->cursor = $this->end;
return;
}
// Find the first token after the current cursor
$position = $this->positions[0][++$this->position];
while ($position[1] < $this->cursor) {
if ($this->position == count($this->positions[0]) - 1) {
return;
}
$position = $this->positions[0][++$this->position];
}
// push the template text first
$text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
if (isset($this->positions[2][$this->position][0])) {
$text = rtrim($text);
}
$this->pushToken(Twig_Token::TEXT_TYPE, $text);
$this->moveCursor($textContent.$position[0]);
switch ($this->positions[1][$this->position][0]) {
case $this->options['tag_comment'][0]:
$this->lexComment();
break;
case $this->options['tag_block'][0]:
// raw data?
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
$this->moveCursor($match[0]);
$this->lexRawData($match[1]);
// {% line \d+ %}
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
$this->moveCursor($match[0]);
$this->lineno = (int) $match[1];
} else {
$this->pushToken(Twig_Token::BLOCK_START_TYPE);
$this->pushState(self::STATE_BLOCK);
$this->currentVarBlockLine = $this->lineno;
}
break;
case $this->options['tag_variable'][0]:
$this->pushToken(Twig_Token::VAR_START_TYPE);
$this->pushState(self::STATE_VAR);
$this->currentVarBlockLine = $this->lineno;
break;
}
}
protected function lexBlock()
{
if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) {
$this->pushToken(Twig_Token::BLOCK_END_TYPE);
$this->moveCursor($match[0]);
$this->popState();
} else {
$this->lexExpression();
}
}
protected function lexVar()
{
if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) {
$this->pushToken(Twig_Token::VAR_END_TYPE);
$this->moveCursor($match[0]);
$this->popState();
} else {
$this->lexExpression();
}
}
protected function lexExpression()
{
// whitespace
if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) {
$this->moveCursor($match[0]);
if ($this->cursor >= $this->end) {
throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->filename);
}
}
// operators
if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
$this->pushToken(Twig_Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0]));
$this->moveCursor($match[0]);
}
// names
elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) {
$this->pushToken(Twig_Token::NAME_TYPE, $match[0]);
$this->moveCursor($match[0]);
}
// numbers
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
$number = (float) $match[0]; // floats
if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
$number = (int) $match[0]; // integers lower than the maximum
}
$this->pushToken(Twig_Token::NUMBER_TYPE, $number);
$this->moveCursor($match[0]);
}
// punctuation
elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
// opening bracket
if (false !== strpos('([{', $this->code[$this->cursor])) {
$this->brackets[] = array($this->code[$this->cursor], $this->lineno);
}
// closing bracket
elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
if (empty($this->brackets)) {
throw new Twig_Error_Syntax(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->filename);
}
list($expect, $lineno) = array_pop($this->brackets);
if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->filename);
}
}
$this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
++$this->cursor;
}
// strings
elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) {
$this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
$this->moveCursor($match[0]);
}
// opening double quoted string
elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
$this->brackets[] = array('"', $this->lineno);
$this->pushState(self::STATE_STRING);
$this->moveCursor($match[0]);
}
// unlexable
else {
throw new Twig_Error_Syntax(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->filename);
}
}
protected function lexRawData($tag)
{
if ('raw' === $tag) {
@trigger_error(sprintf('Twig Tag "raw" is deprecated. Use "verbatim" instead in %s at line %d.', $this->filename, $this->lineno), E_USER_DEPRECATED);
}
if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block.', $tag), $this->lineno, $this->filename);
}
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
$this->moveCursor($text.$match[0][0]);
if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
$text = rtrim($text);
}
$this->pushToken(Twig_Token::TEXT_TYPE, $text);
}
protected function lexComment()
{
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
throw new Twig_Error_Syntax('Unclosed comment.', $this->lineno, $this->filename);
}
$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
}
protected function lexString()
{
if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) {
$this->brackets[] = array($this->options['interpolation'][0], $this->lineno);
$this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
$this->moveCursor($match[0]);
$this->pushState(self::STATE_INTERPOLATION);
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
$this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
$this->moveCursor($match[0]);
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
list($expect, $lineno) = array_pop($this->brackets);
if ($this->code[$this->cursor] != '"') {
throw new Twig_Error_Syntax(sprintf('Unclosed "%s".', $expect), $lineno, $this->filename);
}
$this->popState();
++$this->cursor;
}
}
protected function lexInterpolation()
{
$bracket = end($this->brackets);
if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) {
array_pop($this->brackets);
$this->pushToken(Twig_Token::INTERPOLATION_END_TYPE);
$this->moveCursor($match[0]);
$this->popState();
} else {
$this->lexExpression();
}
}
protected function pushToken($type, $value = '')
{
// do not push empty text tokens
if (Twig_Token::TEXT_TYPE === $type && '' === $value) {
return;
}
$this->tokens[] = new Twig_Token($type, $value, $this->lineno);
}
protected function moveCursor($text)
{
$this->cursor += strlen($text);
$this->lineno += substr_count($text, "\n");
}
protected function getOperatorRegex()
{
$operators = array_merge(
array('='),
array_keys($this->env->getUnaryOperators()),
array_keys($this->env->getBinaryOperators())
);
$operators = array_combine($operators, array_map('strlen', $operators));
arsort($operators);
$regex = array();
foreach ($operators as $operator => $length) {
// an operator that ends with a character must be followed by
// a whitespace or a parenthesis
if (ctype_alpha($operator[$length - 1])) {
$r = preg_quote($operator, '/').'(?=[\s()])';
} else {
$r = preg_quote($operator, '/');
}
// an operator with a space can be any amount of whitespaces
$r = preg_replace('/\s+/', '\s+', $r);
$regex[] = $r;
}
return '/'.implode('|', $regex).'/A';
}
protected function pushState($state)
{
$this->states[] = $this->state;
$this->state = $state;
}
protected function popState()
{
if (0 === count($this->states)) {
throw new Exception('Cannot pop state without a previous state');
}
$this->state = array_pop($this->states);
}
}
Twig-1.23.1/lib/Twig/LexerInterface.php 0000664 0000000 0000000 00000001374 12616650102 0017605 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_LexerInterface
{
/**
* Tokenizes a source code.
*
* @param string $code The source code
* @param string $filename A unique identifier for the source code
*
* @return Twig_TokenStream A token stream instance
*
* @throws Twig_Error_Syntax When the code is syntactically wrong
*/
public function tokenize($code, $filename = null);
}
Twig-1.23.1/lib/Twig/Loader/ 0000775 0000000 0000000 00000000000 12616650102 0015375 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Loader/Array.php 0000664 0000000 0000000 00000004525 12616650102 0017172 0 ustar 00root root 0000000 0000000
*/
class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
protected $templates = array();
/**
* Constructor.
*
* @param array $templates An array of templates (keys are the names, and values are the source code)
*/
public function __construct(array $templates)
{
$this->templates = $templates;
}
/**
* Adds or overrides a template.
*
* @param string $name The template name
* @param string $template The template source
*/
public function setTemplate($name, $template)
{
$this->templates[(string) $name] = $template;
}
/**
* {@inheritdoc}
*/
public function getSource($name)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
return $this->templates[$name];
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
return isset($this->templates[(string) $name]);
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
return $this->templates[$name];
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
$name = (string) $name;
if (!isset($this->templates[$name])) {
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
}
return true;
}
}
Twig-1.23.1/lib/Twig/Loader/Chain.php 0000664 0000000 0000000 00000007201 12616650102 0017130 0 ustar 00root root 0000000 0000000
*/
class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
private $hasSourceCache = array();
protected $loaders = array();
/**
* Constructor.
*
* @param Twig_LoaderInterface[] $loaders An array of loader instances
*/
public function __construct(array $loaders = array())
{
foreach ($loaders as $loader) {
$this->addLoader($loader);
}
}
/**
* Adds a loader instance.
*
* @param Twig_LoaderInterface $loader A Loader instance
*/
public function addLoader(Twig_LoaderInterface $loader)
{
$this->loaders[] = $loader;
$this->hasSourceCache = array();
}
/**
* {@inheritdoc}
*/
public function getSource($name)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getSource($name);
} catch (Twig_Error_Loader $e) {
$exceptions[] = $e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
$name = (string) $name;
if (isset($this->hasSourceCache[$name])) {
return $this->hasSourceCache[$name];
}
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface) {
if ($loader->exists($name)) {
return $this->hasSourceCache[$name] = true;
}
continue;
}
try {
$loader->getSource($name);
return $this->hasSourceCache[$name] = true;
} catch (Twig_Error_Loader $e) {
}
}
return $this->hasSourceCache[$name] = false;
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->getCacheKey($name);
} catch (Twig_Error_Loader $e) {
$exceptions[] = get_class($loader).': '.$e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
$exceptions = array();
foreach ($this->loaders as $loader) {
if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
continue;
}
try {
return $loader->isFresh($name, $time);
} catch (Twig_Error_Loader $e) {
$exceptions[] = get_class($loader).': '.$e->getMessage();
}
}
throw new Twig_Error_Loader(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
}
}
Twig-1.23.1/lib/Twig/Loader/Filesystem.php 0000664 0000000 0000000 00000015466 12616650102 0020246 0 ustar 00root root 0000000 0000000
*/
class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
/** Identifier of the main namespace. */
const MAIN_NAMESPACE = '__main__';
protected $paths = array();
protected $cache = array();
protected $errorCache = array();
/**
* Constructor.
*
* @param string|array $paths A path or an array of paths where to look for templates
*/
public function __construct($paths = array())
{
if ($paths) {
$this->setPaths($paths);
}
}
/**
* Returns the paths to the templates.
*
* @param string $namespace A path namespace
*
* @return array The array of paths where to look for templates
*/
public function getPaths($namespace = self::MAIN_NAMESPACE)
{
return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
}
/**
* Returns the path namespaces.
*
* The main namespace is always defined.
*
* @return array The array of defined namespaces
*/
public function getNamespaces()
{
return array_keys($this->paths);
}
/**
* Sets the paths where templates are stored.
*
* @param string|array $paths A path or an array of paths where to look for templates
* @param string $namespace A path namespace
*/
public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
{
if (!is_array($paths)) {
$paths = array($paths);
}
$this->paths[$namespace] = array();
foreach ($paths as $path) {
$this->addPath($path, $namespace);
}
}
/**
* Adds a path where templates are stored.
*
* @param string $path A path where to look for templates
* @param string $namespace A path name
*
* @throws Twig_Error_Loader
*/
public function addPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = $this->errorCache = array();
if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
}
$this->paths[$namespace][] = rtrim($path, '/\\');
}
/**
* Prepends a path where templates are stored.
*
* @param string $path A path where to look for templates
* @param string $namespace A path name
*
* @throws Twig_Error_Loader
*/
public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
{
// invalidate the cache
$this->cache = $this->errorCache = array();
if (!is_dir($path)) {
throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
}
$path = rtrim($path, '/\\');
if (!isset($this->paths[$namespace])) {
$this->paths[$namespace][] = $path;
} else {
array_unshift($this->paths[$namespace], $path);
}
}
/**
* {@inheritdoc}
*/
public function getSource($name)
{
return file_get_contents($this->findTemplate($name));
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
return $this->findTemplate($name);
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
$name = $this->normalizeName($name);
if (isset($this->cache[$name])) {
return true;
}
try {
return false !== $this->findTemplate($name, false);
} catch (Twig_Error_Loader $exception) {
return false;
}
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
return filemtime($this->findTemplate($name)) <= $time;
}
protected function findTemplate($name)
{
$throw = func_num_args() > 1 ? func_get_arg(1) : true;
$name = $this->normalizeName($name);
if (isset($this->cache[$name])) {
return $this->cache[$name];
}
if (isset($this->errorCache[$name])) {
if (!$throw) {
return false;
}
throw new Twig_Error_Loader($this->errorCache[$name]);
}
$this->validateName($name);
list($namespace, $shortname) = $this->parseName($name);
if (!isset($this->paths[$namespace])) {
$this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace);
if (!$throw) {
return false;
}
throw new Twig_Error_Loader($this->errorCache[$name]);
}
foreach ($this->paths[$namespace] as $path) {
if (is_file($path.'/'.$shortname)) {
if (false !== $realpath = realpath($path.'/'.$shortname)) {
return $this->cache[$name] = $realpath;
}
return $this->cache[$name] = $path.'/'.$shortname;
}
}
$this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));
if (!$throw) {
return false;
}
throw new Twig_Error_Loader($this->errorCache[$name]);
}
protected function parseName($name, $default = self::MAIN_NAMESPACE)
{
if (isset($name[0]) && '@' == $name[0]) {
if (false === $pos = strpos($name, '/')) {
throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
}
$namespace = substr($name, 1, $pos - 1);
$shortname = substr($name, $pos + 1);
return array($namespace, $shortname);
}
return array($default, $name);
}
protected function normalizeName($name)
{
return preg_replace('#/{2,}#', '/', str_replace('\\', '/', (string) $name));
}
protected function validateName($name)
{
if (false !== strpos($name, "\0")) {
throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
}
$name = ltrim($name, '/');
$parts = explode('/', $name);
$level = 0;
foreach ($parts as $part) {
if ('..' === $part) {
--$level;
} elseif ('.' !== $part) {
++$level;
}
if ($level < 0) {
throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
}
}
}
}
Twig-1.23.1/lib/Twig/Loader/String.php 0000664 0000000 0000000 00000002747 12616650102 0017366 0 ustar 00root root 0000000 0000000
*/
class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
{
/**
* {@inheritdoc}
*/
public function getSource($name)
{
return $name;
}
/**
* {@inheritdoc}
*/
public function exists($name)
{
return true;
}
/**
* {@inheritdoc}
*/
public function getCacheKey($name)
{
return $name;
}
/**
* {@inheritdoc}
*/
public function isFresh($name, $time)
{
return true;
}
}
Twig-1.23.1/lib/Twig/LoaderInterface.php 0000664 0000000 0000000 00000002525 12616650102 0017733 0 ustar 00root root 0000000 0000000
*/
interface Twig_LoaderInterface
{
/**
* Gets the source code of a template, given its name.
*
* @param string $name The name of the template to load
*
* @return string The template source code
*
* @throws Twig_Error_Loader When $name is not found
*/
public function getSource($name);
/**
* Gets the cache key to use for the cache for a given template name.
*
* @param string $name The name of the template to load
*
* @return string The cache key
*
* @throws Twig_Error_Loader When $name is not found
*/
public function getCacheKey($name);
/**
* Returns true if the template is still fresh.
*
* @param string $name The template name
* @param int $time Timestamp of the last modification time of the
* cached template
*
* @return bool true if the template is fresh, false otherwise
*
* @throws Twig_Error_Loader When $name is not found
*/
public function isFresh($name, $time);
}
Twig-1.23.1/lib/Twig/Markup.php 0000664 0000000 0000000 00000001374 12616650102 0016144 0 ustar 00root root 0000000 0000000
*/
class Twig_Markup implements Countable
{
protected $content;
protected $charset;
public function __construct($content, $charset)
{
$this->content = (string) $content;
$this->charset = $charset;
}
public function __toString()
{
return $this->content;
}
public function count()
{
return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content);
}
}
Twig-1.23.1/lib/Twig/Node.php 0000664 0000000 0000000 00000013226 12616650102 0015571 0 ustar 00root root 0000000 0000000
*/
class Twig_Node implements Twig_NodeInterface
{
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 int $lineno The line number
* @param string $tag The tag name associated with the Node
*/
public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
{
$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);
}
/**
* @deprecated since 1.16.1 (to be removed in 2.0)
*/
public function toXml($asDom = false)
{
@trigger_error(sprintf('%s is deprecated.', __METHOD__), E_USER_DEPRECATED);
$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(Twig_Compiler $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 $name The attribute name
*
* @return bool true if the attribute is defined, false otherwise
*/
public function hasAttribute($name)
{
return array_key_exists($name, $this->attributes);
}
/**
* Gets an attribute value by name.
*
* @param string $name
*
* @return mixed
*/
public function getAttribute($name)
{
if (!array_key_exists($name, $this->attributes)) {
throw new LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this)));
}
return $this->attributes[$name];
}
/**
* Sets an attribute by name to a value.
*
* @param string $name
* @param mixed $value
*/
public function setAttribute($name, $value)
{
$this->attributes[$name] = $value;
}
/**
* Removes an attribute by name.
*
* @param string $name
*/
public function removeAttribute($name)
{
unset($this->attributes[$name]);
}
/**
* Returns true if the node with the given name exists.
*
* @param string $name
*
* @return bool
*/
public function hasNode($name)
{
return array_key_exists($name, $this->nodes);
}
/**
* Gets a node by name.
*
* @param string $name
*
* @return Twig_Node
*/
public function getNode($name)
{
if (!array_key_exists($name, $this->nodes)) {
throw new LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this)));
}
return $this->nodes[$name];
}
/**
* Sets a node.
*
* @param string $name
* @param Twig_Node $node
*/
public function setNode($name, $node = null)
{
$this->nodes[$name] = $node;
}
/**
* Removes a node by name.
*
* @param string $name
*/
public function removeNode($name)
{
unset($this->nodes[$name]);
}
public function count()
{
return count($this->nodes);
}
public function getIterator()
{
return new ArrayIterator($this->nodes);
}
}
Twig-1.23.1/lib/Twig/Node/ 0000775 0000000 0000000 00000000000 12616650102 0015054 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Node/AutoEscape.php 0000664 0000000 0000000 00000001511 12616650102 0017614 0 ustar 00root root 0000000 0000000
*/
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);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->subcompile($this->getNode('body'));
}
}
Twig-1.23.1/lib/Twig/Node/Block.php 0000664 0000000 0000000 00000001713 12616650102 0016621 0 ustar 00root root 0000000 0000000
*/
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);
}
public function compile(Twig_Compiler $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")
;
}
}
Twig-1.23.1/lib/Twig/Node/BlockReference.php 0000664 0000000 0000000 00000001446 12616650102 0020443 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInterface
{
public function __construct($name, $lineno, $tag = null)
{
parent::__construct(array(), array('name' => $name), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
;
}
}
Twig-1.23.1/lib/Twig/Node/Body.php 0000664 0000000 0000000 00000000521 12616650102 0016460 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Body extends Twig_Node
{
}
Twig-1.23.1/lib/Twig/Node/CheckSecurity.php 0000664 0000000 0000000 00000005672 12616650102 0020344 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_CheckSecurity extends Twig_Node
{
protected $usedFilters;
protected $usedTags;
protected $usedFunctions;
public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
{
$this->usedFilters = $usedFilters;
$this->usedTags = $usedTags;
$this->usedFunctions = $usedFunctions;
parent::__construct();
}
public function compile(Twig_Compiler $compiler)
{
$tags = $filters = $functions = array();
foreach (array('tags', 'filters', 'functions') as $type) {
foreach ($this->{'used'.ucfirst($type)} as $name => $node) {
if ($node instanceof Twig_Node) {
${$type}[$name] = $node->getLine();
} else {
${$type}[$node] = null;
}
}
}
$compiler
->write('$tags = ')->repr(array_filter($tags))->raw(";\n")
->write('$filters = ')->repr(array_filter($filters))->raw(";\n")
->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n")
->write("try {\n")
->indent()
->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
->indent()
->write(!$tags ? "array(),\n" : "array('".implode("', '", array_keys($tags))."'),\n")
->write(!$filters ? "array(),\n" : "array('".implode("', '", array_keys($filters))."'),\n")
->write(!$functions ? "array()\n" : "array('".implode("', '", array_keys($functions))."')\n")
->outdent()
->write(");\n")
->outdent()
->write("} catch (Twig_Sandbox_SecurityError \$e) {\n")
->indent()
->write("\$e->setTemplateFile(\$this->getTemplateName());\n\n")
->write("if (\$e instanceof Twig_Sandbox_SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
->indent()
->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
->outdent()
->write("} elseif (\$e instanceof Twig_Sandbox_SecurityNotAllowedFilterError && isset(\$filters[\$e->getFilterName()])) {\n")
->indent()
->write("\$e->setTemplateLine(\$filters[\$e->getFilterName()]);\n")
->outdent()
->write("} elseif (\$e instanceof Twig_Sandbox_SecurityNotAllowedFunctionError && isset(\$functions[\$e->getFunctionName()])) {\n")
->indent()
->write("\$e->setTemplateLine(\$functions[\$e->getFunctionName()]);\n")
->outdent()
->write("}\n\n")
->write("throw \$e;\n")
->outdent()
->write("}\n\n")
;
}
}
Twig-1.23.1/lib/Twig/Node/Do.php 0000664 0000000 0000000 00000001332 12616650102 0016126 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Do extends Twig_Node
{
public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
{
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('')
->subcompile($this->getNode('expr'))
->raw(";\n")
;
}
}
Twig-1.23.1/lib/Twig/Node/Embed.php 0000664 0000000 0000000 00000002431 12616650102 0016601 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Embed extends Twig_Node_Include
{
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
{
parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
$this->setAttribute('filename', $filename);
$this->setAttribute('index', $index);
}
protected function addGetTemplate(Twig_Compiler $compiler)
{
$compiler
->write('$this->loadTemplate(')
->string($this->getAttribute('filename'))
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getLine())
->raw(', ')
->string($this->getAttribute('index'))
->raw(')')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression.php 0000664 0000000 0000000 00000000637 12616650102 0017732 0 ustar 00root root 0000000 0000000
*/
abstract class Twig_Node_Expression extends Twig_Node
{
}
Twig-1.23.1/lib/Twig/Node/Expression/ 0000775 0000000 0000000 00000000000 12616650102 0017213 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Node/Expression/Array.php 0000664 0000000 0000000 00000004301 12616650102 0021000 0 ustar 00root root 0000000 0000000 index = -1;
foreach ($this->getKeyValuePairs() as $pair) {
if ($pair['key'] instanceof Twig_Node_Expression_Constant && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) {
$this->index = $pair['key']->getAttribute('value');
}
}
}
public function getKeyValuePairs()
{
$pairs = array();
foreach (array_chunk($this->nodes, 2) as $pair) {
$pairs[] = array(
'key' => $pair[0],
'value' => $pair[1],
);
}
return $pairs;
}
public function hasElement(Twig_Node_Expression $key)
{
foreach ($this->getKeyValuePairs() as $pair) {
// we compare the string representation of the keys
// to avoid comparing the line numbers which are not relevant here.
if ((string) $key == (string) $pair['key']) {
return true;
}
}
return false;
}
public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null)
{
if (null === $key) {
$key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine());
}
array_push($this->nodes, $key, $value);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->raw('array(');
$first = true;
foreach ($this->getKeyValuePairs() as $pair) {
if (!$first) {
$compiler->raw(', ');
}
$first = false;
$compiler
->subcompile($pair['key'])
->raw(' => ')
->subcompile($pair['value'])
;
}
$compiler->raw(')');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/AssignName.php 0000664 0000000 0000000 00000000773 12616650102 0021760 0 ustar 00root root 0000000 0000000 raw('$context[')
->string($this->getAttribute('name'))
->raw(']')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary.php 0000664 0000000 0000000 00000001627 12616650102 0021156 0 ustar 00root root 0000000 0000000 $left, 'right' => $right), array(), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(')
->subcompile($this->getNode('left'))
->raw(' ')
;
$this->operator($compiler);
$compiler
->raw(' ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
abstract public function operator(Twig_Compiler $compiler);
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/ 0000775 0000000 0000000 00000000000 12616650102 0020437 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Node/Expression/Binary/Add.php 0000664 0000000 0000000 00000000635 12616650102 0021644 0 ustar 00root root 0000000 0000000 raw('+');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/And.php 0000664 0000000 0000000 00000000636 12616650102 0021657 0 ustar 00root root 0000000 0000000 raw('&&');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/BitwiseAnd.php 0000664 0000000 0000000 00000000644 12616650102 0023205 0 ustar 00root root 0000000 0000000 raw('&');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/BitwiseOr.php 0000664 0000000 0000000 00000000643 12616650102 0023062 0 ustar 00root root 0000000 0000000 raw('|');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/BitwiseXor.php 0000664 0000000 0000000 00000000644 12616650102 0023253 0 ustar 00root root 0000000 0000000 raw('^');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Concat.php 0000664 0000000 0000000 00000000640 12616650102 0022357 0 ustar 00root root 0000000 0000000 raw('.');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Div.php 0000664 0000000 0000000 00000000635 12616650102 0021676 0 ustar 00root root 0000000 0000000 raw('/');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/EndsWith.php 0000664 0000000 0000000 00000001574 12616650102 0022704 0 ustar 00root root 0000000 0000000 getVarName();
$right = $compiler->getVarName();
$compiler
->raw(sprintf('(is_string($%s = ', $left))
->subcompile($this->getNode('left'))
->raw(sprintf(') && is_string($%s = ', $right))
->subcompile($this->getNode('right'))
->raw(sprintf(') && (\'\' === $%2$s || $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right))
;
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Equal.php 0000664 0000000 0000000 00000000605 12616650102 0022220 0 ustar 00root root 0000000 0000000 raw('==');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/FloorDiv.php 0000664 0000000 0000000 00000001064 12616650102 0022675 0 ustar 00root root 0000000 0000000 raw('intval(floor(');
parent::compile($compiler);
$compiler->raw('))');
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('/');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Greater.php 0000664 0000000 0000000 00000000606 12616650102 0022543 0 ustar 00root root 0000000 0000000 raw('>');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/GreaterEqual.php 0000664 0000000 0000000 00000000614 12616650102 0023532 0 ustar 00root root 0000000 0000000 raw('>=');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/In.php 0000664 0000000 0000000 00000001227 12616650102 0021520 0 ustar 00root root 0000000 0000000 raw('twig_in_filter(')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('in');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Less.php 0000664 0000000 0000000 00000000603 12616650102 0022055 0 ustar 00root root 0000000 0000000 raw('<');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/LessEqual.php 0000664 0000000 0000000 00000000611 12616650102 0023044 0 ustar 00root root 0000000 0000000 raw('<=');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Matches.php 0000664 0000000 0000000 00000001226 12616650102 0022535 0 ustar 00root root 0000000 0000000 raw('preg_match(')
->subcompile($this->getNode('right'))
->raw(', ')
->subcompile($this->getNode('left'))
->raw(')')
;
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Mod.php 0000664 0000000 0000000 00000000635 12616650102 0021673 0 ustar 00root root 0000000 0000000 raw('%');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Mul.php 0000664 0000000 0000000 00000000635 12616650102 0021711 0 ustar 00root root 0000000 0000000 raw('*');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/NotEqual.php 0000664 0000000 0000000 00000000610 12616650102 0022675 0 ustar 00root root 0000000 0000000 raw('!=');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/NotIn.php 0000664 0000000 0000000 00000001237 12616650102 0022202 0 ustar 00root root 0000000 0000000 raw('!twig_in_filter(')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('not in');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Or.php 0000664 0000000 0000000 00000000635 12616650102 0021534 0 ustar 00root root 0000000 0000000 raw('||');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Power.php 0000664 0000000 0000000 00000001217 12616650102 0022245 0 ustar 00root root 0000000 0000000 raw('pow(')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('**');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Range.php 0000664 0000000 0000000 00000001221 12616650102 0022200 0 ustar 00root root 0000000 0000000 raw('range(')
->subcompile($this->getNode('left'))
->raw(', ')
->subcompile($this->getNode('right'))
->raw(')')
;
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('..');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/StartsWith.php 0000664 0000000 0000000 00000001561 12616650102 0023267 0 ustar 00root root 0000000 0000000 getVarName();
$right = $compiler->getVarName();
$compiler
->raw(sprintf('(is_string($%s = ', $left))
->subcompile($this->getNode('left'))
->raw(sprintf(') && is_string($%s = ', $right))
->subcompile($this->getNode('right'))
->raw(sprintf(') && (\'\' === $%2$s || 0 === strpos($%1$s, $%2$s)))', $left, $right))
;
}
public function operator(Twig_Compiler $compiler)
{
return $compiler->raw('');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Binary/Sub.php 0000664 0000000 0000000 00000000635 12616650102 0021705 0 ustar 00root root 0000000 0000000 raw('-');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/BlockReference.php 0000664 0000000 0000000 00000002375 12616650102 0022604 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Expression_BlockReference extends Twig_Node_Expression
{
public function __construct(Twig_NodeInterface $name, $asString = false, $lineno, $tag = null)
{
parent::__construct(array('name' => $name), array('as_string' => $asString, 'output' => false), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
if ($this->getAttribute('as_string')) {
$compiler->raw('(string) ');
}
if ($this->getAttribute('output')) {
$compiler
->addDebugInfo($this)
->write('$this->displayBlock(')
->subcompile($this->getNode('name'))
->raw(", \$context, \$blocks);\n")
;
} else {
$compiler
->raw('$this->renderBlock(')
->subcompile($this->getNode('name'))
->raw(', $context, $blocks)')
;
}
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Call.php 0000664 0000000 0000000 00000022506 12616650102 0020604 0 ustar 00root root 0000000 0000000 hasAttribute('callable') && $callable = $this->getAttribute('callable')) {
if (is_string($callable)) {
$compiler->raw($callable);
} elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) {
$compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1]));
} else {
$type = ucfirst($this->getAttribute('type'));
$compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
$closingParenthesis = true;
}
} else {
$compiler->raw($this->getAttribute('thing')->compile());
}
$this->compileArguments($compiler);
if ($closingParenthesis) {
$compiler->raw(')');
}
}
protected function compileArguments(Twig_Compiler $compiler)
{
$compiler->raw('(');
$first = true;
if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
$compiler->raw('$this->env');
$first = false;
}
if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
if (!$first) {
$compiler->raw(', ');
}
$compiler->raw('$context');
$first = false;
}
if ($this->hasAttribute('arguments')) {
foreach ($this->getAttribute('arguments') as $argument) {
if (!$first) {
$compiler->raw(', ');
}
$compiler->string($argument);
$first = false;
}
}
if ($this->hasNode('node')) {
if (!$first) {
$compiler->raw(', ');
}
$compiler->subcompile($this->getNode('node'));
$first = false;
}
if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) {
$callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
$arguments = $this->getArguments($callable, $this->getNode('arguments'));
foreach ($arguments as $node) {
if (!$first) {
$compiler->raw(', ');
}
$compiler->subcompile($node);
$first = false;
}
}
$compiler->raw(')');
}
protected function getArguments($callable, $arguments)
{
$callType = $this->getAttribute('type');
$callName = $this->getAttribute('name');
$parameters = array();
$named = false;
foreach ($arguments as $name => $node) {
if (!is_int($name)) {
$named = true;
$name = $this->normalizeName($name);
} elseif ($named) {
throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName));
}
$parameters[$name] = $node;
}
$isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic');
if (!$named && !$isVariadic) {
return $parameters;
}
if (!$callable) {
if ($named) {
$message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName);
} else {
$message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName);
}
throw new LogicException($message);
}
// manage named arguments
if (is_array($callable)) {
$r = new ReflectionMethod($callable[0], $callable[1]);
} elseif (is_object($callable) && !$callable instanceof Closure) {
$r = new ReflectionObject($callable);
$r = $r->getMethod('__invoke');
} elseif (is_string($callable) && false !== strpos($callable, '::')) {
$r = new ReflectionMethod($callable);
} else {
$r = new ReflectionFunction($callable);
}
$definition = $r->getParameters();
if ($this->hasNode('node')) {
array_shift($definition);
}
if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
array_shift($definition);
}
if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
array_shift($definition);
}
if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
foreach ($this->getAttribute('arguments') as $argument) {
array_shift($definition);
}
}
if ($isVariadic) {
$argument = end($definition);
if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && array() === $argument->getDefaultValue()) {
array_pop($definition);
} else {
$callableName = $r->name;
if ($r->getDeclaringClass()) {
$callableName = $r->getDeclaringClass()->name.'::'.$callableName;
}
throw new LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = array()".', $callableName, $callType, $callName));
}
}
$arguments = array();
$names = array();
$missingArguments = array();
$optionalArguments = array();
$pos = 0;
foreach ($definition as $param) {
$names[] = $name = $this->normalizeName($param->name);
if (array_key_exists($name, $parameters)) {
if (array_key_exists($pos, $parameters)) {
throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName));
}
if (!empty($missingArguments)) {
throw new Twig_Error_Syntax(sprintf(
'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".',
$name, $callType, $callName, implode(', ', $names), count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments))
);
}
$arguments = array_merge($arguments, $optionalArguments);
$arguments[] = $parameters[$name];
unset($parameters[$name]);
$optionalArguments = array();
} elseif (array_key_exists($pos, $parameters)) {
$arguments = array_merge($arguments, $optionalArguments);
$arguments[] = $parameters[$pos];
unset($parameters[$pos]);
$optionalArguments = array();
++$pos;
} elseif ($param->isDefaultValueAvailable()) {
$optionalArguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
} elseif ($param->isOptional()) {
if (empty($parameters)) {
break;
} else {
$missingArguments[] = $name;
}
} else {
throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName));
}
}
if ($isVariadic) {
$arbitraryArguments = new Twig_Node_Expression_Array(array(), -1);
foreach ($parameters as $key => $value) {
if (is_int($key)) {
$arbitraryArguments->addElement($value);
} else {
$arbitraryArguments->addElement($value, new Twig_Node_Expression_Constant($key, -1));
}
unset($parameters[$key]);
}
if ($arbitraryArguments->count()) {
$arguments = array_merge($arguments, $optionalArguments);
$arguments[] = $arbitraryArguments;
}
}
if (!empty($parameters)) {
$unknownParameter = null;
foreach ($parameters as $parameter) {
if ($parameter instanceof Twig_Node) {
$unknownParameter = $parameter;
break;
}
}
throw new Twig_Error_Syntax(sprintf(
'Unknown argument%s "%s" for %s "%s(%s)".',
count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names)
), $unknownParameter ? $unknownParameter->getLine() : -1);
}
return $arguments;
}
protected function normalizeName($name)
{
return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Conditional.php 0000664 0000000 0000000 00000001606 12616650102 0022172 0 ustar 00root root 0000000 0000000 $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('((')
->subcompile($this->getNode('expr1'))
->raw(') ? (')
->subcompile($this->getNode('expr2'))
->raw(') : (')
->subcompile($this->getNode('expr3'))
->raw('))')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Constant.php 0000664 0000000 0000000 00000001055 12616650102 0021516 0 ustar 00root root 0000000 0000000 $value), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->repr($this->getAttribute('value'));
}
}
Twig-1.23.1/lib/Twig/Node/Expression/ExtensionReference.php 0000664 0000000 0000000 00000001271 12616650102 0023520 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
{
public function __construct($name, $lineno, $tag = null)
{
parent::__construct(array(), array('name' => $name), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->raw(sprintf("\$this->env->getExtension('%s')", $this->getAttribute('name')));
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Filter.php 0000664 0000000 0000000 00000002747 12616650102 0021163 0 ustar 00root root 0000000 0000000 $node, 'filter' => $filterName, 'arguments' => $arguments), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getNode('filter')->getAttribute('value');
$filter = $compiler->getEnvironment()->getFilter($name);
$this->setAttribute('name', $name);
$this->setAttribute('type', 'filter');
$this->setAttribute('thing', $filter);
$this->setAttribute('needs_environment', $filter->needsEnvironment());
$this->setAttribute('needs_context', $filter->needsContext());
$this->setAttribute('arguments', $filter->getArguments());
if ($filter instanceof Twig_FilterCallableInterface || $filter instanceof Twig_SimpleFilter) {
$this->setAttribute('callable', $filter->getCallable());
}
if ($filter instanceof Twig_SimpleFilter) {
$this->setAttribute('is_variadic', $filter->isVariadic());
}
$this->compileCallable($compiler);
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Filter/ 0000775 0000000 0000000 00000000000 12616650102 0020440 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Node/Expression/Filter/Default.php 0000664 0000000 0000000 00000003057 12616650102 0022542 0 ustar 00root root 0000000 0000000
* {{ var.foo|default('foo item on var is not defined') }}
*
*
* @author Fabien Potencier
*/
class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter
{
public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
{
$default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getLine()), $arguments, $node->getLine());
if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
$test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine());
$false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine());
$node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine());
} else {
$node = $default;
}
parent::__construct($node, $filterName, $arguments, $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->subcompile($this->getNode('node'));
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Function.php 0000664 0000000 0000000 00000002554 12616650102 0021517 0 ustar 00root root 0000000 0000000 $arguments), array('name' => $name), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getAttribute('name');
$function = $compiler->getEnvironment()->getFunction($name);
$this->setAttribute('name', $name);
$this->setAttribute('type', 'function');
$this->setAttribute('thing', $function);
$this->setAttribute('needs_environment', $function->needsEnvironment());
$this->setAttribute('needs_context', $function->needsContext());
$this->setAttribute('arguments', $function->getArguments());
if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
$this->setAttribute('callable', $function->getCallable());
}
if ($function instanceof Twig_SimpleFunction) {
$this->setAttribute('is_variadic', $function->isVariadic());
}
$this->compileCallable($compiler);
}
}
Twig-1.23.1/lib/Twig/Node/Expression/GetAttr.php 0000664 0000000 0000000 00000004412 12616650102 0021277 0 ustar 00root root 0000000 0000000 $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
if (function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) {
$compiler->raw('twig_template_get_attributes($this, ');
} else {
$compiler->raw('$this->getAttribute(');
}
if ($this->getAttribute('ignore_strict_check')) {
$this->getNode('node')->setAttribute('ignore_strict_check', true);
}
$compiler->subcompile($this->getNode('node'));
$compiler->raw(', ')->subcompile($this->getNode('attribute'));
// only generate optional arguments when needed (to make generated code more readable)
$needFourth = $this->getAttribute('ignore_strict_check');
$needThird = $needFourth || $this->getAttribute('is_defined_test');
$needSecond = $needThird || Twig_Template::ANY_CALL !== $this->getAttribute('type');
$needFirst = $needSecond || null !== $this->getNode('arguments');
if ($needFirst) {
if (null !== $this->getNode('arguments')) {
$compiler->raw(', ')->subcompile($this->getNode('arguments'));
} else {
$compiler->raw(', array()');
}
}
if ($needSecond) {
$compiler->raw(', ')->repr($this->getAttribute('type'));
}
if ($needThird) {
$compiler->raw(', ')->repr($this->getAttribute('is_defined_test'));
}
if ($needFourth) {
$compiler->raw(', ')->repr($this->getAttribute('ignore_strict_check'));
}
$compiler->raw(')');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/MethodCall.php 0000664 0000000 0000000 00000002263 12616650102 0021743 0 ustar 00root root 0000000 0000000 $node, 'arguments' => $arguments), array('method' => $method, 'safe' => false), $lineno);
if ($node instanceof Twig_Node_Expression_Name) {
$node->setAttribute('always_defined', true);
}
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->subcompile($this->getNode('node'))
->raw('->')
->raw($this->getAttribute('method'))
->raw('(')
;
$first = true;
foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
if (!$first) {
$compiler->raw(', ');
}
$first = false;
$compiler->subcompile($pair['value']);
}
$compiler->raw(')');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Name.php 0000664 0000000 0000000 00000006324 12616650102 0020611 0 ustar 00root root 0000000 0000000 '$this',
'_context' => '$context',
'_charset' => '$this->env->getCharset()',
);
public function __construct($name, $lineno)
{
parent::__construct(array(), array('name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getAttribute('name');
$compiler->addDebugInfo($this);
if ($this->getAttribute('is_defined_test')) {
if ($this->isSpecial()) {
if ('_self' === $name) {
@trigger_error(sprintf('Global variable "_self" is deprecated in %s at line %d', '?', $this->getLine()), E_USER_DEPRECATED);
}
$compiler->repr(true);
} else {
$compiler->raw('array_key_exists(')->repr($name)->raw(', $context)');
}
} elseif ($this->isSpecial()) {
if ('_self' === $name) {
@trigger_error(sprintf('Global variable "_self" is deprecated in %s at line %d', '?', $this->getLine()), E_USER_DEPRECATED);
}
$compiler->raw($this->specialVars[$name]);
} elseif ($this->getAttribute('always_defined')) {
$compiler
->raw('$context[')
->string($name)
->raw(']')
;
} else {
// remove the non-PHP 5.4 version when PHP 5.3 support is dropped
// as the non-optimized version is just a workaround for slow ternary operator
// when the context has a lot of variables
if (PHP_VERSION_ID >= 50400) {
// PHP 5.4 ternary operator performance was optimized
$compiler
->raw('(isset($context[')
->string($name)
->raw(']) ? $context[')
->string($name)
->raw('] : ')
;
if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
$compiler->raw('null)');
} else {
$compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
}
} else {
$compiler
->raw('$this->getContext($context, ')
->string($name)
;
if ($this->getAttribute('ignore_strict_check')) {
$compiler->raw(', true');
}
$compiler
->raw(')')
;
}
}
}
public function isSpecial()
{
return isset($this->specialVars[$this->getAttribute('name')]);
}
public function isSimple()
{
return !$this->isSpecial() && !$this->getAttribute('is_defined_test');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Parent.php 0000664 0000000 0000000 00000002135 12616650102 0021156 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Expression_Parent extends Twig_Node_Expression
{
public function __construct($name, $lineno, $tag = null)
{
parent::__construct(array(), array('output' => false, 'name' => $name), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
if ($this->getAttribute('output')) {
$compiler
->addDebugInfo($this)
->write('$this->displayParentBlock(')
->string($this->getAttribute('name'))
->raw(", \$context, \$blocks);\n")
;
} else {
$compiler
->raw('$this->renderParentBlock(')
->string($this->getAttribute('name'))
->raw(', $context, $blocks)')
;
}
}
}
Twig-1.23.1/lib/Twig/Node/Expression/TempName.php 0000664 0000000 0000000 00000001122 12616650102 0021426 0 ustar 00root root 0000000 0000000 $name), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('$_')
->raw($this->getAttribute('name'))
->raw('_')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test.php 0000664 0000000 0000000 00000002213 12616650102 0020641 0 ustar 00root root 0000000 0000000 $node, 'arguments' => $arguments), array('name' => $name), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getAttribute('name');
$test = $compiler->getEnvironment()->getTest($name);
$this->setAttribute('name', $name);
$this->setAttribute('type', 'test');
$this->setAttribute('thing', $test);
if ($test instanceof Twig_TestCallableInterface || $test instanceof Twig_SimpleTest) {
$this->setAttribute('callable', $test->getCallable());
}
if ($test instanceof Twig_SimpleTest) {
$this->setAttribute('is_variadic', $test->isVariadic());
}
$this->compileCallable($compiler);
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test/ 0000775 0000000 0000000 00000000000 12616650102 0020132 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Node/Expression/Test/Constant.php 0000664 0000000 0000000 00000002152 12616650102 0022434 0 ustar 00root root 0000000 0000000
* {% if post.status is constant('Post::PUBLISHED') %}
* the status attribute is exactly the same as Post::PUBLISHED
* {% endif %}
*
*
* @author Fabien Potencier
*/
class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(')
->subcompile($this->getNode('node'))
->raw(' === constant(')
;
if ($this->getNode('arguments')->hasNode(1)) {
$compiler
->raw('get_class(')
->subcompile($this->getNode('arguments')->getNode(1))
->raw(')."::".')
;
}
$compiler
->subcompile($this->getNode('arguments')->getNode(0))
->raw('))')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test/Defined.php 0000664 0000000 0000000 00000003110 12616650102 0022174 0 ustar 00root root 0000000 0000000
* {# defined works with variable names and variable attributes #}
* {% if foo is defined %}
* {# ... #}
* {% endif %}
*
*
* @author Fabien Potencier
*/
class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
{
public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
{
parent::__construct($node, $name, $arguments, $lineno);
if ($node instanceof Twig_Node_Expression_Name) {
$node->setAttribute('is_defined_test', true);
} elseif ($node instanceof Twig_Node_Expression_GetAttr) {
$node->setAttribute('is_defined_test', true);
$this->changeIgnoreStrictCheck($node);
} else {
throw new Twig_Error_Syntax('The "defined" test only works with simple variables.', $this->getLine());
}
}
protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node)
{
$node->setAttribute('ignore_strict_check', true);
if ($node->getNode('node') instanceof Twig_Node_Expression_GetAttr) {
$this->changeIgnoreStrictCheck($node->getNode('node'));
}
}
public function compile(Twig_Compiler $compiler)
{
$compiler->subcompile($this->getNode('node'));
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test/Divisibleby.php 0000664 0000000 0000000 00000001354 12616650102 0023113 0 ustar 00root root 0000000 0000000
* {% if loop.index is divisible by(3) %}
*
*
* @author Fabien Potencier
*/
class Twig_Node_Expression_Test_Divisibleby extends Twig_Node_Expression_Test
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(0 == ')
->subcompile($this->getNode('node'))
->raw(' % ')
->subcompile($this->getNode('arguments')->getNode(0))
->raw(')')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test/Even.php 0000664 0000000 0000000 00000001174 12616650102 0021543 0 ustar 00root root 0000000 0000000
* {{ var is even }}
*
*
* @author Fabien Potencier
*/
class Twig_Node_Expression_Test_Even extends Twig_Node_Expression_Test
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(')
->subcompile($this->getNode('node'))
->raw(' % 2 == 0')
->raw(')')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test/Null.php 0000664 0000000 0000000 00000001152 12616650102 0021554 0 ustar 00root root 0000000 0000000
* {{ var is none }}
*
*
* @author Fabien Potencier
*/
class Twig_Node_Expression_Test_Null extends Twig_Node_Expression_Test
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(null === ')
->subcompile($this->getNode('node'))
->raw(')')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test/Odd.php 0000664 0000000 0000000 00000001171 12616650102 0021351 0 ustar 00root root 0000000 0000000
* {{ var is odd }}
*
*
* @author Fabien Potencier
*/
class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(')
->subcompile($this->getNode('node'))
->raw(' % 2 == 1')
->raw(')')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Test/Sameas.php 0000664 0000000 0000000 00000001262 12616650102 0022055 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Expression_Test_Sameas extends Twig_Node_Expression_Test
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->raw('(')
->subcompile($this->getNode('node'))
->raw(' === ')
->subcompile($this->getNode('arguments')->getNode(0))
->raw(')')
;
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Unary.php 0000664 0000000 0000000 00000001305 12616650102 0021021 0 ustar 00root root 0000000 0000000 $node), array(), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->raw(' ');
$this->operator($compiler);
$compiler->subcompile($this->getNode('node'));
}
abstract public function operator(Twig_Compiler $compiler);
}
Twig-1.23.1/lib/Twig/Node/Expression/Unary/ 0000775 0000000 0000000 00000000000 12616650102 0020311 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Node/Expression/Unary/Neg.php 0000664 0000000 0000000 00000000624 12616650102 0021535 0 ustar 00root root 0000000 0000000 raw('-');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Unary/Not.php 0000664 0000000 0000000 00000000624 12616650102 0021564 0 ustar 00root root 0000000 0000000 raw('!');
}
}
Twig-1.23.1/lib/Twig/Node/Expression/Unary/Pos.php 0000664 0000000 0000000 00000000624 12616650102 0021565 0 ustar 00root root 0000000 0000000 raw('+');
}
}
Twig-1.23.1/lib/Twig/Node/Flush.php 0000664 0000000 0000000 00000001156 12616650102 0016651 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Flush extends Twig_Node
{
public function __construct($lineno, $tag)
{
parent::__construct(array(), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write("flush();\n")
;
}
}
Twig-1.23.1/lib/Twig/Node/For.php 0000664 0000000 0000000 00000010141 12616650102 0016310 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_For extends Twig_Node
{
protected $loop;
public function __construct(Twig_Node_Expression_AssignName $keyTarget, Twig_Node_Expression_AssignName $valueTarget, Twig_Node_Expression $seq, Twig_Node_Expression $ifexpr = null, Twig_NodeInterface $body, Twig_NodeInterface $else = null, $lineno, $tag = null)
{
$body = new Twig_Node(array($body, $this->loop = new Twig_Node_ForLoop($lineno, $tag)));
if (null !== $ifexpr) {
$body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag);
}
parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write("\$context['_parent'] = \$context;\n")
->write("\$context['_seq'] = twig_ensure_traversable(")
->subcompile($this->getNode('seq'))
->raw(");\n")
;
if (null !== $this->getNode('else')) {
$compiler->write("\$context['_iterated'] = false;\n");
}
if ($this->getAttribute('with_loop')) {
$compiler
->write("\$context['loop'] = array(\n")
->write(" 'parent' => \$context['_parent'],\n")
->write(" 'index0' => 0,\n")
->write(" 'index' => 1,\n")
->write(" 'first' => true,\n")
->write(");\n")
;
if (!$this->getAttribute('ifexpr')) {
$compiler
->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n")
->indent()
->write("\$length = count(\$context['_seq']);\n")
->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")
;
}
}
$this->loop->setAttribute('else', null !== $this->getNode('else'));
$this->loop->setAttribute('with_loop', $this->getAttribute('with_loop'));
$this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr'));
$compiler
->write("foreach (\$context['_seq'] as ")
->subcompile($this->getNode('key_target'))
->raw(' => ')
->subcompile($this->getNode('value_target'))
->raw(") {\n")
->indent()
->subcompile($this->getNode('body'))
->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_intersect_key(\$context, \$_parent) + \$_parent;\n");
}
}
Twig-1.23.1/lib/Twig/Node/ForLoop.php 0000664 0000000 0000000 00000002752 12616650102 0017153 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_ForLoop extends Twig_Node
{
public function __construct($lineno, $tag = null)
{
parent::__construct(array(), array('with_loop' => false, 'ifexpr' => false, 'else' => false), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
if ($this->getAttribute('else')) {
$compiler->write("\$context['_iterated'] = true;\n");
}
if ($this->getAttribute('with_loop')) {
$compiler
->write("++\$context['loop']['index0'];\n")
->write("++\$context['loop']['index'];\n")
->write("\$context['loop']['first'] = false;\n")
;
if (!$this->getAttribute('ifexpr')) {
$compiler
->write("if (isset(\$context['loop']['length'])) {\n")
->indent()
->write("--\$context['loop']['revindex0'];\n")
->write("--\$context['loop']['revindex'];\n")
->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n")
->outdent()
->write("}\n")
;
}
}
}
}
Twig-1.23.1/lib/Twig/Node/If.php 0000664 0000000 0000000 00000003116 12616650102 0016124 0 ustar 00root root 0000000 0000000
*/
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);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
for ($i = 0, $count = count($this->getNode('tests')); $i < $count; $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");
}
}
Twig-1.23.1/lib/Twig/Node/Import.php 0000664 0000000 0000000 00000002450 12616650102 0017040 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Import extends Twig_Node
{
public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $var, $lineno, $tag = null)
{
parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $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->loadTemplate(')
->subcompile($this->getNode('expr'))
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getLine())
->raw(')')
;
}
$compiler->raw(";\n");
}
}
Twig-1.23.1/lib/Twig/Node/Include.php 0000664 0000000 0000000 00000004563 12616650102 0017160 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
{
public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
{
parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (bool) $only, 'ignore_missing' => (bool) $ignoreMissing), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler->addDebugInfo($this);
if ($this->getAttribute('ignore_missing')) {
$compiler
->write("try {\n")
->indent()
;
}
$this->addGetTemplate($compiler);
$compiler->raw('->display(');
$this->addTemplateArguments($compiler);
$compiler->raw(");\n");
if ($this->getAttribute('ignore_missing')) {
$compiler
->outdent()
->write("} catch (Twig_Error_Loader \$e) {\n")
->indent()
->write("// ignore missing template\n")
->outdent()
->write("}\n\n")
;
}
}
protected function addGetTemplate(Twig_Compiler $compiler)
{
$compiler
->write('$this->loadTemplate(')
->subcompile($this->getNode('expr'))
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getLine())
->raw(')')
;
}
protected function addTemplateArguments(Twig_Compiler $compiler)
{
if (null === $this->getNode('variables')) {
$compiler->raw(false === $this->getAttribute('only') ? '$context' : 'array()');
} elseif (false === $this->getAttribute('only')) {
$compiler
->raw('array_merge($context, ')
->subcompile($this->getNode('variables'))
->raw(')')
;
} else {
$compiler->subcompile($this->getNode('variables'));
}
}
}
Twig-1.23.1/lib/Twig/Node/Macro.php 0000664 0000000 0000000 00000006477 12616650102 0016644 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Macro extends Twig_Node
{
const VARARGS_NAME = 'varargs';
public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null)
{
foreach ($arguments as $argumentName => $argument) {
if (self::VARARGS_NAME === $argumentName) {
throw new Twig_Error_Syntax(sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getLine());
}
}
parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write(sprintf('public function get%s(', $this->getAttribute('name')))
;
$count = count($this->getNode('arguments'));
$pos = 0;
foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->raw('$__'.$name.'__ = ')
->subcompile($default)
;
if (++$pos < $count) {
$compiler->raw(', ');
}
}
if (PHP_VERSION_ID >= 50600) {
if ($count) {
$compiler->raw(', ');
}
$compiler->raw('...$__varargs__');
}
$compiler
->raw(")\n")
->write("{\n")
->indent()
;
$compiler
->write("\$context = \$this->env->mergeGlobals(array(\n")
->indent()
;
foreach ($this->getNode('arguments') as $name => $default) {
$compiler
->addIndentation()
->string($name)
->raw(' => $__'.$name.'__')
->raw(",\n")
;
}
$compiler
->addIndentation()
->string(self::VARARGS_NAME)
->raw(' => ')
;
if (PHP_VERSION_ID >= 50600) {
$compiler->raw("\$__varargs__,\n");
} else {
$compiler
->raw('func_num_args() > ')
->repr($count)
->raw(' ? array_slice(func_get_args(), ')
->repr($count)
->raw(") : array(),\n")
;
}
$compiler
->outdent()
->write("));\n\n")
->write("\$blocks = array();\n\n")
->write("ob_start();\n")
->write("try {\n")
->indent()
->subcompile($this->getNode('body'))
->outdent()
->write("} catch (Exception \$e) {\n")
->indent()
->write("ob_end_clean();\n\n")
->write("throw \$e;\n")
->outdent()
->write("}\n\n")
->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());\n")
->outdent()
->write("}\n\n")
;
}
}
Twig-1.23.1/lib/Twig/Node/Module.php 0000664 0000000 0000000 00000031313 12616650102 0017013 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Module extends Twig_Node
{
public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename)
{
// embedded templates are set as attributes so that they are only visited once by the visitors
parent::__construct(array(
'parent' => $parent,
'body' => $body,
'blocks' => $blocks,
'macros' => $macros,
'traits' => $traits,
'display_start' => new Twig_Node(),
'display_end' => new Twig_Node(),
'constructor_start' => new Twig_Node(),
'constructor_end' => new Twig_Node(),
'class_end' => new Twig_Node(),
), array(
'filename' => $filename,
'index' => null,
'embedded_templates' => $embeddedTemplates,
), 1);
}
public function setIndex($index)
{
$this->setAttribute('index', $index);
}
public function compile(Twig_Compiler $compiler)
{
$this->compileTemplate($compiler);
foreach ($this->getAttribute('embedded_templates') as $template) {
$compiler->subcompile($template);
}
}
protected function compileTemplate(Twig_Compiler $compiler)
{
if (!$this->getAttribute('index')) {
$compiler->write('compileClassHeader($compiler);
if (
count($this->getNode('blocks'))
|| count($this->getNode('traits'))
|| null === $this->getNode('parent')
|| $this->getNode('parent') instanceof Twig_Node_Expression_Constant
|| count($this->getNode('constructor_start'))
|| count($this->getNode('constructor_end'))
) {
$this->compileConstructor($compiler);
}
$this->compileGetParent($compiler);
$this->compileDisplay($compiler);
$compiler->subcompile($this->getNode('blocks'));
$this->compileMacros($compiler);
$this->compileGetTemplateName($compiler);
$this->compileIsTraitable($compiler);
$this->compileDebugInfo($compiler);
$this->compileClassFooter($compiler);
}
protected function compileGetParent(Twig_Compiler $compiler)
{
if (null === $parent = $this->getNode('parent')) {
return;
}
$compiler
->write("protected function doGetParent(array \$context)\n", "{\n")
->indent()
->addDebugInfo($parent)
->write('return ')
;
if ($parent instanceof Twig_Node_Expression_Constant) {
$compiler->subcompile($parent);
} else {
$compiler
->raw('$this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getNode('parent')->getLine())
->raw(')')
;
}
$compiler
->raw(";\n")
->outdent()
->write("}\n\n")
;
}
protected function compileClassHeader(Twig_Compiler $compiler)
{
$compiler
->write("\n\n")
// if the filename contains */, add a blank to avoid a PHP parse error
->write('/* '.str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index')))
->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
->write("{\n")
->indent()
;
}
protected function compileConstructor(Twig_Compiler $compiler)
{
$compiler
->write("public function __construct(Twig_Environment \$env)\n", "{\n")
->indent()
->subcompile($this->getNode('constructor_start'))
->write("parent::__construct(\$env);\n\n")
;
// parent
if (null === $parent = $this->getNode('parent')) {
$compiler->write("\$this->parent = false;\n\n");
} elseif ($parent instanceof Twig_Node_Expression_Constant) {
$compiler
->addDebugInfo($parent)
->write('$this->parent = $this->loadTemplate(')
->subcompile($parent)
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($this->getNode('parent')->getLine())
->raw(");\n")
;
}
$countTraits = count($this->getNode('traits'));
if ($countTraits) {
// traits
foreach ($this->getNode('traits') as $i => $trait) {
$this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
$compiler
->addDebugInfo($trait->getNode('template'))
->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
->indent()
->write("throw new Twig_Error_Runtime('Template \"'.")
->subcompile($trait->getNode('template'))
->raw(".'\" cannot be used as a trait.');\n")
->outdent()
->write("}\n")
->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
;
foreach ($trait->getNode('targets') as $key => $value) {
$compiler
->write(sprintf('if (!isset($_trait_%s_blocks[', $i))
->string($key)
->raw("])) {\n")
->indent()
->write("throw new Twig_Error_Runtime(sprintf('Block ")
->string($key)
->raw(' is not defined in trait ')
->subcompile($trait->getNode('template'))
->raw(".'));\n")
->outdent()
->write("}\n\n")
->write(sprintf('$_trait_%s_blocks[', $i))
->subcompile($value)
->raw(sprintf('] = $_trait_%s_blocks[', $i))
->string($key)
->raw(sprintf(']; unset($_trait_%s_blocks[', $i))
->string($key)
->raw("]);\n\n")
;
}
}
if ($countTraits > 1) {
$compiler
->write("\$this->traits = array_merge(\n")
->indent()
;
for ($i = 0; $i < $countTraits; ++$i) {
$compiler
->write(sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ? '' : ',')."\n", $i))
;
}
$compiler
->outdent()
->write(");\n\n")
;
} else {
$compiler
->write("\$this->traits = \$_trait_0_blocks;\n\n")
;
}
$compiler
->write("\$this->blocks = array_merge(\n")
->indent()
->write("\$this->traits,\n")
->write("array(\n")
;
} else {
$compiler
->write("\$this->blocks = array(\n")
;
}
// blocks
$compiler
->indent()
;
foreach ($this->getNode('blocks') as $name => $node) {
$compiler
->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
;
}
if ($countTraits) {
$compiler
->outdent()
->write(")\n")
;
}
$compiler
->outdent()
->write(");\n")
->outdent()
->subcompile($this->getNode('constructor_end'))
->write("}\n\n")
;
}
protected function compileDisplay(Twig_Compiler $compiler)
{
$compiler
->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
->indent()
->subcompile($this->getNode('display_start'))
->subcompile($this->getNode('body'))
;
if (null !== $parent = $this->getNode('parent')) {
$compiler->addDebugInfo($parent);
if ($parent instanceof Twig_Node_Expression_Constant) {
$compiler->write('$this->parent');
} else {
$compiler->write('$this->getParent($context)');
}
$compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
}
$compiler
->subcompile($this->getNode('display_end'))
->outdent()
->write("}\n\n")
;
}
protected function compileClassFooter(Twig_Compiler $compiler)
{
$compiler
->subcompile($this->getNode('class_end'))
->outdent()
->write("}\n")
;
}
protected function compileMacros(Twig_Compiler $compiler)
{
$compiler->subcompile($this->getNode('macros'));
}
protected function compileGetTemplateName(Twig_Compiler $compiler)
{
$compiler
->write("public function getTemplateName()\n", "{\n")
->indent()
->write('return ')
->repr($this->getAttribute('filename'))
->raw(";\n")
->outdent()
->write("}\n\n")
;
}
protected function compileIsTraitable(Twig_Compiler $compiler)
{
// A template can be used as a trait if:
// * it has no parent
// * it has no macros
// * it has no body
//
// Put another way, a template can be used as a trait if it
// only contains blocks and use statements.
$traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
if ($traitable) {
if ($this->getNode('body') instanceof Twig_Node_Body) {
$nodes = $this->getNode('body')->getNode(0);
} else {
$nodes = $this->getNode('body');
}
if (!count($nodes)) {
$nodes = new Twig_Node(array($nodes));
}
foreach ($nodes as $node) {
if (!count($node)) {
continue;
}
if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
continue;
}
if ($node instanceof Twig_Node_BlockReference) {
continue;
}
$traitable = false;
break;
}
}
if ($traitable) {
return;
}
$compiler
->write("public function isTraitable()\n", "{\n")
->indent()
->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
->outdent()
->write("}\n\n")
;
}
protected function compileDebugInfo(Twig_Compiler $compiler)
{
$compiler
->write("public function getDebugInfo()\n", "{\n")
->indent()
->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
->outdent()
->write("}\n")
;
}
protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
{
if ($node instanceof Twig_Node_Expression_Constant) {
$compiler
->write(sprintf('%s = $this->loadTemplate(', $var))
->subcompile($node)
->raw(', ')
->repr($compiler->getFilename())
->raw(', ')
->repr($node->getLine())
->raw(");\n")
;
} else {
throw new LogicException('Trait templates can only be constant nodes');
}
}
}
Twig-1.23.1/lib/Twig/Node/Print.php 0000664 0000000 0000000 00000001471 12616650102 0016664 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface
{
public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
{
parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('echo ')
->subcompile($this->getNode('expr'))
->raw(";\n")
;
}
}
Twig-1.23.1/lib/Twig/Node/Sandbox.php 0000664 0000000 0000000 00000002176 12616650102 0017171 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Sandbox extends Twig_Node
{
public function __construct(Twig_NodeInterface $body, $lineno, $tag = null)
{
parent::__construct(array('body' => $body), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $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")
;
}
}
Twig-1.23.1/lib/Twig/Node/SandboxedPrint.php 0000664 0000000 0000000 00000002536 12616650102 0020517 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_SandboxedPrint extends Twig_Node_Print
{
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('echo $this->env->getExtension(\'sandbox\')->ensureToStringAllowed(')
->subcompile($this->getNode('expr'))
->raw(");\n")
;
}
/**
* Removes node filters.
*
* This is mostly needed when another visitor adds filters (like the escaper one).
*
* @param Twig_Node $node A Node
*
* @return Twig_Node
*/
protected function removeNodeFilter($node)
{
if ($node instanceof Twig_Node_Expression_Filter) {
return $this->removeNodeFilter($node->getNode('node'));
}
return $node;
}
}
Twig-1.23.1/lib/Twig/Node/Set.php 0000664 0000000 0000000 00000006053 12616650102 0016324 0 ustar 00root root 0000000 0000000
*/
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, 'safe' => false), $lineno, $tag);
/*
* Optimizes the node when capture is used for a large block of text.
*
* {% set foo %}foo{% endset %} is compiled to $context['foo'] = new Twig_Markup("foo");
*/
if ($this->getAttribute('capture')) {
$this->setAttribute('safe', true);
$values = $this->getNode('values');
if ($values instanceof Twig_Node_Text) {
$this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getLine()));
$this->setAttribute('capture', false);
}
}
}
public function compile(Twig_Compiler $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(" = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())");
}
}
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 {
if ($this->getAttribute('safe')) {
$compiler
->raw("('' === \$tmp = ")
->subcompile($this->getNode('values'))
->raw(") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())")
;
} else {
$compiler->subcompile($this->getNode('values'));
}
}
}
$compiler->raw(";\n");
}
}
Twig-1.23.1/lib/Twig/Node/SetTemp.php 0000664 0000000 0000000 00000001510 12616650102 0017143 0 ustar 00root root 0000000 0000000 $name), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$name = $this->getAttribute('name');
$compiler
->addDebugInfo($this)
->write('if (isset($context[')
->string($name)
->raw('])) { $_')
->raw($name)
->raw('_ = $context[')
->repr($name)
->raw(']; } else { $_')
->raw($name)
->raw("_ = null; }\n")
;
}
}
Twig-1.23.1/lib/Twig/Node/Spaceless.php 0000664 0000000 0000000 00000001537 12616650102 0017515 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Spaceless extends Twig_Node
{
public function __construct(Twig_NodeInterface $body, $lineno, $tag = 'spaceless')
{
parent::__construct(array('body' => $body), array(), $lineno, $tag);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write("ob_start();\n")
->subcompile($this->getNode('body'))
->write("echo trim(preg_replace('/>\s+', '><', ob_get_clean()));\n")
;
}
}
Twig-1.23.1/lib/Twig/Node/Text.php 0000664 0000000 0000000 00000001373 12616650102 0016515 0 ustar 00root root 0000000 0000000
*/
class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface
{
public function __construct($data, $lineno)
{
parent::__construct(array(), array('data' => $data), $lineno);
}
public function compile(Twig_Compiler $compiler)
{
$compiler
->addDebugInfo($this)
->write('echo ')
->string($this->getAttribute('data'))
->raw(";\n")
;
}
}
Twig-1.23.1/lib/Twig/NodeInterface.php 0000664 0000000 0000000 00000001226 12616650102 0017407 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_NodeInterface extends Countable, IteratorAggregate
{
/**
* Compiles the node to PHP.
*
* @param Twig_Compiler $compiler A Twig_Compiler instance
*/
public function compile(Twig_Compiler $compiler);
public function getLine();
public function getNodeTag();
}
Twig-1.23.1/lib/Twig/NodeOutputInterface.php 0000664 0000000 0000000 00000000537 12616650102 0020634 0 ustar 00root root 0000000 0000000
*/
interface Twig_NodeOutputInterface
{
}
Twig-1.23.1/lib/Twig/NodeTraverser.php 0000664 0000000 0000000 00000004475 12616650102 0017475 0 ustar 00root root 0000000 0000000
*/
class Twig_NodeTraverser
{
protected $env;
protected $visitors = array();
/**
* Constructor.
*
* @param Twig_Environment $env A Twig_Environment instance
* @param Twig_NodeVisitorInterface[] $visitors An array of Twig_NodeVisitorInterface instances
*/
public function __construct(Twig_Environment $env, array $visitors = array())
{
$this->env = $env;
foreach ($visitors as $visitor) {
$this->addVisitor($visitor);
}
}
/**
* Adds a visitor.
*
* @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
*/
public function addVisitor(Twig_NodeVisitorInterface $visitor)
{
if (!isset($this->visitors[$visitor->getPriority()])) {
$this->visitors[$visitor->getPriority()] = array();
}
$this->visitors[$visitor->getPriority()][] = $visitor;
}
/**
* Traverses a node and calls the registered visitors.
*
* @param Twig_NodeInterface $node A Twig_NodeInterface instance
*
* @return Twig_NodeInterface
*/
public function traverse(Twig_NodeInterface $node)
{
ksort($this->visitors);
foreach ($this->visitors as $visitors) {
foreach ($visitors as $visitor) {
$node = $this->traverseForVisitor($visitor, $node);
}
}
return $node;
}
protected function traverseForVisitor(Twig_NodeVisitorInterface $visitor, Twig_NodeInterface $node = null)
{
if (null === $node) {
return;
}
$node = $visitor->enterNode($node, $this->env);
foreach ($node as $k => $n) {
if (false !== $n = $this->traverseForVisitor($visitor, $n)) {
$node->setNode($k, $n);
} else {
$node->removeNode($k);
}
}
return $visitor->leaveNode($node, $this->env);
}
}
Twig-1.23.1/lib/Twig/NodeVisitor/ 0000775 0000000 0000000 00000000000 12616650102 0016434 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/NodeVisitor/Escaper.php 0000664 0000000 0000000 00000011465 12616650102 0020536 0 ustar 00root root 0000000 0000000
*/
class Twig_NodeVisitor_Escaper extends Twig_BaseNodeVisitor
{
protected $statusStack = array();
protected $blocks = array();
protected $safeAnalysis;
protected $traverser;
protected $defaultStrategy = false;
protected $safeVars = array();
public function __construct()
{
$this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis();
}
/**
* {@inheritdoc}
*/
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) {
$this->defaultStrategy = $defaultStrategy;
}
$this->safeVars = array();
} elseif ($node instanceof Twig_Node_AutoEscape) {
$this->statusStack[] = $node->getAttribute('value');
} elseif ($node instanceof Twig_Node_Block) {
$this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
} elseif ($node instanceof Twig_Node_Import) {
$this->safeVars[] = $node->getNode('var')->getAttribute('name');
}
return $node;
}
/**
* {@inheritdoc}
*/
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
$this->defaultStrategy = false;
$this->safeVars = array();
} elseif ($node instanceof Twig_Node_Expression_Filter) {
return $this->preEscapeFilterNode($node, $env);
} elseif ($node instanceof Twig_Node_Print) {
return $this->escapePrintNode($node, $env, $this->needEscaping($env));
}
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 escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type)
{
if (false === $type) {
return $node;
}
$expression = $node->getNode('expr');
if ($this->isSafeFor($type, $expression, $env)) {
return $node;
}
$class = get_class($node);
return new $class(
$this->getEscaperFilter($type, $expression),
$node->getLine()
);
}
protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env)
{
$name = $filter->getNode('filter')->getAttribute('value');
$type = $env->getFilter($name)->getPreEscape();
if (null === $type) {
return $filter;
}
$node = $filter->getNode('node');
if ($this->isSafeFor($type, $node, $env)) {
return $filter;
}
$filter->setNode('node', $this->getEscaperFilter($type, $node));
return $filter;
}
protected function isSafeFor($type, Twig_NodeInterface $expression, $env)
{
$safe = $this->safeAnalysis->getSafe($expression);
if (null === $safe) {
if (null === $this->traverser) {
$this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis));
}
$this->safeAnalysis->setSafeVars($this->safeVars);
$this->traverser->traverse($expression);
$safe = $this->safeAnalysis->getSafe($expression);
}
return in_array($type, $safe) || in_array('all', $safe);
}
protected function needEscaping(Twig_Environment $env)
{
if (count($this->statusStack)) {
return $this->statusStack[count($this->statusStack) - 1];
}
return $this->defaultStrategy ? $this->defaultStrategy : false;
}
protected function getEscaperFilter($type, Twig_NodeInterface $node)
{
$line = $node->getLine();
$name = new Twig_Node_Expression_Constant('escape', $line);
$args = new Twig_Node(array(new Twig_Node_Expression_Constant((string) $type, $line), new Twig_Node_Expression_Constant(null, $line), new Twig_Node_Expression_Constant(true, $line)));
return new Twig_Node_Expression_Filter($node, $name, $args, $line);
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return 0;
}
}
Twig-1.23.1/lib/Twig/NodeVisitor/Optimizer.php 0000664 0000000 0000000 00000021542 12616650102 0021133 0 ustar 00root root 0000000 0000000
*/
class Twig_NodeVisitor_Optimizer extends Twig_BaseNodeVisitor
{
const OPTIMIZE_ALL = -1;
const OPTIMIZE_NONE = 0;
const OPTIMIZE_FOR = 2;
const OPTIMIZE_RAW_FILTER = 4;
const OPTIMIZE_VAR_ACCESS = 8;
protected $loops = array();
protected $loopsTargets = array();
protected $optimizers;
protected $prependedNodes = array();
protected $inABody = false;
/**
* Constructor.
*
* @param int $optimizers The optimizer mode
*/
public function __construct($optimizers = -1)
{
if (!is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) {
throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
}
$this->optimizers = $optimizers;
}
/**
* {@inheritdoc}
*/
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
{
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
$this->enterOptimizeFor($node, $env);
}
if (PHP_VERSION_ID < 50400 && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
if ($this->inABody) {
if (!$node instanceof Twig_Node_Expression) {
if (get_class($node) !== 'Twig_Node') {
array_unshift($this->prependedNodes, array());
}
} else {
$node = $this->optimizeVariables($node, $env);
}
} elseif ($node instanceof Twig_Node_Body) {
$this->inABody = true;
}
}
return $node;
}
/**
* {@inheritdoc}
*/
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
{
$expression = $node instanceof Twig_Node_Expression;
if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
$this->leaveOptimizeFor($node, $env);
}
if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) {
$node = $this->optimizeRawFilter($node, $env);
}
$node = $this->optimizePrintNode($node, $env);
if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
if ($node instanceof Twig_Node_Body) {
$this->inABody = false;
} elseif ($this->inABody) {
if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
$nodes = array();
foreach (array_unique($prependedNodes) as $name) {
$nodes[] = new Twig_Node_SetTemp($name, $node->getLine());
}
$nodes[] = $node;
$node = new Twig_Node($nodes);
}
}
}
return $node;
}
protected function optimizeVariables(Twig_NodeInterface $node, Twig_Environment $env)
{
if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
$this->prependedNodes[0][] = $node->getAttribute('name');
return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine());
}
return $node;
}
/**
* Optimizes print nodes.
*
* It replaces:
*
* * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
*
* @param Twig_NodeInterface $node A Node
* @param Twig_Environment $env The current Twig environment
*
* @return Twig_NodeInterface
*/
protected function optimizePrintNode(Twig_NodeInterface $node, Twig_Environment $env)
{
if (!$node instanceof Twig_Node_Print) {
return $node;
}
if (
$node->getNode('expr') instanceof Twig_Node_Expression_BlockReference ||
$node->getNode('expr') instanceof Twig_Node_Expression_Parent
) {
$node->getNode('expr')->setAttribute('output', true);
return $node->getNode('expr');
}
return $node;
}
/**
* Removes "raw" filters.
*
* @param Twig_NodeInterface $node A Node
* @param Twig_Environment $env The current Twig environment
*
* @return Twig_NodeInterface
*/
protected function optimizeRawFilter(Twig_NodeInterface $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
return $node->getNode('node');
}
return $node;
}
/**
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
*
* @param Twig_NodeInterface $node A Node
* @param Twig_Environment $env The current Twig environment
*/
protected function enterOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_For) {
// disable the loop variable by default
$node->setAttribute('with_loop', false);
array_unshift($this->loops, $node);
array_unshift($this->loopsTargets, $node->getNode('value_target')->getAttribute('name'));
array_unshift($this->loopsTargets, $node->getNode('key_target')->getAttribute('name'));
} elseif (!$this->loops) {
// we are outside a loop
return;
}
// when do we need to add the loop variable back?
// the loop variable is referenced for the current loop
elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
$node->setAttribute('always_defined', true);
$this->addLoopToCurrent();
}
// optimize access to loop targets
elseif ($node instanceof Twig_Node_Expression_Name && in_array($node->getAttribute('name'), $this->loopsTargets)) {
$node->setAttribute('always_defined', true);
}
// block reference
elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
$this->addLoopToCurrent();
}
// include without the only attribute
elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
$this->addLoopToAll();
}
// include function without the with_context=false parameter
elseif ($node instanceof Twig_Node_Expression_Function
&& 'include' === $node->getAttribute('name')
&& (!$node->getNode('arguments')->hasNode('with_context')
|| false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value')
)
) {
$this->addLoopToAll();
}
// the loop variable is referenced via an attribute
elseif ($node instanceof Twig_Node_Expression_GetAttr
&& (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
|| 'parent' === $node->getNode('attribute')->getAttribute('value')
)
&& (true === $this->loops[0]->getAttribute('with_loop')
|| ($node->getNode('node') instanceof Twig_Node_Expression_Name
&& 'loop' === $node->getNode('node')->getAttribute('name')
)
)
) {
$this->addLoopToAll();
}
}
/**
* Optimizes "for" tag by removing the "loop" variable creation whenever possible.
*
* @param Twig_NodeInterface $node A Node
* @param Twig_Environment $env The current Twig environment
*/
protected function leaveOptimizeFor(Twig_NodeInterface $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_For) {
array_shift($this->loops);
array_shift($this->loopsTargets);
array_shift($this->loopsTargets);
}
}
protected function addLoopToCurrent()
{
$this->loops[0]->setAttribute('with_loop', true);
}
protected function addLoopToAll()
{
foreach ($this->loops as $loop) {
$loop->setAttribute('with_loop', true);
}
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return 255;
}
}
Twig-1.23.1/lib/Twig/NodeVisitor/SafeAnalysis.php 0000664 0000000 0000000 00000011357 12616650102 0021536 0 ustar 00root root 0000000 0000000 safeVars = $safeVars;
}
public function getSafe(Twig_NodeInterface $node)
{
$hash = spl_object_hash($node);
if (!isset($this->data[$hash])) {
return;
}
foreach ($this->data[$hash] as $bucket) {
if ($bucket['key'] !== $node) {
continue;
}
if (in_array('html_attr', $bucket['value'])) {
$bucket['value'][] = 'html';
}
return $bucket['value'];
}
}
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,
);
}
/**
* {@inheritdoc}
*/
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
{
return $node;
}
/**
* {@inheritdoc}
*/
protected function doLeaveNode(Twig_Node $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_BlockReference) {
// blocks are safe by definition
$this->setSafe($node, array('all'));
} elseif ($node instanceof Twig_Node_Expression_Parent) {
// parent block is safe by definition
$this->setSafe($node, array('all'));
} elseif ($node instanceof Twig_Node_Expression_Conditional) {
// intersect 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
$name = $node->getNode('filter')->getAttribute('value');
$args = $node->getNode('arguments');
if (false !== $filter = $env->getFilter($name)) {
$safe = $filter->getSafe($args);
if (null === $safe) {
$safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety());
}
$this->setSafe($node, $safe);
} else {
$this->setSafe($node, array());
}
} elseif ($node instanceof Twig_Node_Expression_Function) {
// function expression is safe when the function is safe
$name = $node->getAttribute('name');
$args = $node->getNode('arguments');
$function = $env->getFunction($name);
if (false !== $function) {
$this->setSafe($node, $function->getSafe($args));
} else {
$this->setSafe($node, array());
}
} elseif ($node instanceof Twig_Node_Expression_MethodCall) {
if ($node->getAttribute('safe')) {
$this->setSafe($node, array('all'));
} else {
$this->setSafe($node, array());
}
} elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) {
$name = $node->getNode('node')->getAttribute('name');
// attributes on template instances are safe
if ('_self' == $name || in_array($name, $this->safeVars)) {
$this->setSafe($node, array('all'));
} else {
$this->setSafe($node, array());
}
} else {
$this->setSafe($node, array());
}
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);
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return 0;
}
}
Twig-1.23.1/lib/Twig/NodeVisitor/Sandbox.php 0000664 0000000 0000000 00000004504 12616650102 0020546 0 ustar 00root root 0000000 0000000
*/
class Twig_NodeVisitor_Sandbox extends Twig_BaseNodeVisitor
{
protected $inAModule = false;
protected $tags;
protected $filters;
protected $functions;
/**
* {@inheritdoc}
*/
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
$this->inAModule = true;
$this->tags = array();
$this->filters = array();
$this->functions = array();
return $node;
} elseif ($this->inAModule) {
// look for tags
if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) {
$this->tags[$node->getNodeTag()] = $node;
}
// look for filters
if ($node instanceof Twig_Node_Expression_Filter && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) {
$this->filters[$node->getNode('filter')->getAttribute('value')] = $node;
}
// look for functions
if ($node instanceof Twig_Node_Expression_Function && !isset($this->functions[$node->getAttribute('name')])) {
$this->functions[$node->getAttribute('name')] = $node;
}
// wrap print to check __toString() calls
if ($node instanceof Twig_Node_Print) {
return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag());
}
}
return $node;
}
/**
* {@inheritdoc}
*/
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
$this->inAModule = false;
$node->setNode('display_start', new Twig_Node(array(new Twig_Node_CheckSecurity($this->filters, $this->tags, $this->functions), $node->getNode('display_start'))));
}
return $node;
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return 0;
}
}
Twig-1.23.1/lib/Twig/NodeVisitorInterface.php 0000664 0000000 0000000 00000002454 12616650102 0020773 0 ustar 00root root 0000000 0000000
*/
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
*
* @return 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
*
* @return Twig_NodeInterface|false The modified node or false if the node must be removed
*/
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
/**
* Returns the priority for this visitor.
*
* Priority should be between -10 and 10 (0 is the default).
*
* @return int The priority level
*/
public function getPriority();
}
Twig-1.23.1/lib/Twig/Parser.php 0000664 0000000 0000000 00000027116 12616650102 0016143 0 ustar 00root root 0000000 0000000
*/
class Twig_Parser implements Twig_ParserInterface
{
protected $stack = array();
protected $stream;
protected $parent;
protected $handlers;
protected $visitors;
protected $expressionParser;
protected $blocks;
protected $blockStack;
protected $macros;
protected $env;
protected $reservedMacroNames;
protected $importedSymbols;
protected $traits;
protected $embeddedTemplates = array();
/**
* Constructor.
*
* @param Twig_Environment $env A Twig_Environment instance
*/
public function __construct(Twig_Environment $env)
{
$this->env = $env;
}
public function getEnvironment()
{
return $this->env;
}
public function getVarName()
{
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
}
public function getFilename()
{
return $this->stream->getFilename();
}
/**
* {@inheritdoc}
*/
public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
{
// push all variables into the stack to keep the current state of the parser
$vars = get_object_vars($this);
unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']);
$this->stack[] = $vars;
// tag handlers
if (null === $this->handlers) {
$this->handlers = $this->env->getTokenParsers();
$this->handlers->setParser($this);
}
// node visitors
if (null === $this->visitors) {
$this->visitors = $this->env->getNodeVisitors();
}
if (null === $this->expressionParser) {
$this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators());
}
$this->stream = $stream;
$this->parent = null;
$this->blocks = array();
$this->macros = array();
$this->traits = array();
$this->blockStack = array();
$this->importedSymbols = array(array());
$this->embeddedTemplates = array();
try {
$body = $this->subparse($test, $dropNeedle);
if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) {
$body = new Twig_Node();
}
} catch (Twig_Error_Syntax $e) {
if (!$e->getTemplateFile()) {
$e->setTemplateFile($this->getFilename());
}
if (!$e->getTemplateLine()) {
$e->setTemplateLine($this->stream->getCurrent()->getLine());
}
throw $e;
}
$node = new Twig_Node_Module(new Twig_Node_Body(array($body)), $this->parent, new Twig_Node($this->blocks), new Twig_Node($this->macros), new Twig_Node($this->traits), $this->embeddedTemplates, $this->getFilename());
$traverser = new Twig_NodeTraverser($this->env, $this->visitors);
$node = $traverser->traverse($node);
// restore previous stack so previous parse() call can resume working
foreach (array_pop($this->stack) as $key => $val) {
$this->$key = $val;
}
return $node;
}
public function subparse($test, $dropNeedle = false)
{
$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(), $this->getFilename());
}
if (null !== $test && call_user_func($test, $token)) {
if ($dropNeedle) {
$this->stream->next();
}
if (1 === count($rv)) {
return $rv[0];
}
return new Twig_Node($rv, array(), $lineno);
}
$subparser = $this->handlers->getTokenParser($token->getValue());
if (null === $subparser) {
if (null !== $test) {
$e = new Twig_Error_Syntax(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->getFilename());
if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) {
$e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno));
}
} else {
$e = new Twig_Error_Syntax(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->getFilename());
$e->addSuggestions($token->getValue(), array_keys($this->env->getTags()));
}
throw $e;
}
$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.', 0, $this->getFilename());
}
}
if (1 === count($rv)) {
return $rv[0];
}
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 getBlock($name)
{
return $this->blocks[$name];
}
public function setBlock($name, Twig_Node_Block $value)
{
$this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine());
}
public function hasMacro($name)
{
return isset($this->macros[$name]);
}
public function setMacro($name, Twig_Node_Macro $node)
{
if ($this->isReservedMacroName($name)) {
throw new Twig_Error_Syntax(sprintf('"%s" cannot be used as a macro name as it is a reserved keyword.', $name), $node->getLine(), $this->getFilename());
}
$this->macros[$name] = $node;
}
public function isReservedMacroName($name)
{
if (null === $this->reservedMacroNames) {
$this->reservedMacroNames = array();
$r = new ReflectionClass($this->env->getBaseTemplateClass());
foreach ($r->getMethods() as $method) {
$methodName = strtolower($method->getName());
if ('get' === substr($methodName, 0, 3) && isset($methodName[3])) {
$this->reservedMacroNames[] = substr($methodName, 3);
}
}
}
return in_array(strtolower($name), $this->reservedMacroNames);
}
public function addTrait($trait)
{
$this->traits[] = $trait;
}
public function hasTraits()
{
return count($this->traits) > 0;
}
public function embedTemplate(Twig_Node_Module $template)
{
$template->setIndex(mt_rand());
$this->embeddedTemplates[] = $template;
}
public function addImportedSymbol($type, $alias, $name = null, Twig_Node_Expression $node = null)
{
$this->importedSymbols[0][$type][$alias] = array('name' => $name, 'node' => $node);
}
public function getImportedSymbol($type, $alias)
{
foreach ($this->importedSymbols as $functions) {
if (isset($functions[$type][$alias])) {
return $functions[$type][$alias];
}
}
}
public function isMainScope()
{
return 1 === count($this->importedSymbols);
}
public function pushLocalScope()
{
array_unshift($this->importedSymbols, array());
}
public function popLocalScope()
{
array_shift($this->importedSymbols);
}
/**
* Gets the expression parser.
*
* @return Twig_ExpressionParser The expression parser
*/
public function getExpressionParser()
{
return $this->expressionParser;
}
public function getParent()
{
return $this->parent;
}
public function setParent($parent)
{
$this->parent = $parent;
}
/**
* Gets the token stream.
*
* @return Twig_TokenStream The token stream
*/
public function getStream()
{
return $this->stream;
}
/**
* Gets the current token.
*
* @return Twig_Token The current token
*/
public function getCurrentToken()
{
return $this->stream->getCurrent();
}
protected function filterBodyNodes(Twig_NodeInterface $node)
{
// check that the body does not contain non-empty output nodes
if (
($node instanceof Twig_Node_Text && !ctype_space($node->getAttribute('data')))
||
(!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface)
) {
if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) {
throw new Twig_Error_Syntax('A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed.', $node->getLine(), $this->getFilename());
}
throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->getFilename());
}
// bypass "set" nodes as they "capture" the output
if ($node instanceof Twig_Node_Set) {
return $node;
}
if ($node instanceof Twig_NodeOutputInterface) {
return;
}
foreach ($node as $k => $n) {
if (null !== $n && null === $this->filterBodyNodes($n)) {
$node->removeNode($k);
}
}
return $node;
}
}
Twig-1.23.1/lib/Twig/ParserInterface.php 0000664 0000000 0000000 00000001340 12616650102 0017753 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
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
*
* @throws Twig_Error_Syntax When the token stream is syntactically or semantically wrong
*/
public function parse(Twig_TokenStream $stream);
}
Twig-1.23.1/lib/Twig/Profiler/ 0000775 0000000 0000000 00000000000 12616650102 0015751 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Profiler/Dumper/ 0000775 0000000 0000000 00000000000 12616650102 0017205 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Profiler/Dumper/Blackfire.php 0000664 0000000 0000000 00000003650 12616650102 0021604 0 ustar 00root root 0000000 0000000
*/
class Twig_Profiler_Dumper_Blackfire
{
public function dump(Twig_Profiler_Profile $profile)
{
$data = array();
$this->dumpProfile('main()', $profile, $data);
$this->dumpChildren('main()', $profile, $data);
$start = microtime(true);
$str = << $values) {
$str .= "{$name}//{$values['ct']} {$values['wt']} {$values['mu']} {$values['pmu']}\n";
}
return $str;
}
private function dumpChildren($parent, Twig_Profiler_Profile $profile, &$data)
{
foreach ($profile as $p) {
if ($p->isTemplate()) {
$name = $p->getTemplate();
} else {
$name = sprintf('%s::%s(%s)', $p->getTemplate(), $p->getType(), $p->getName());
}
$this->dumpProfile(sprintf('%s==>%s', $parent, $name), $p, $data);
$this->dumpChildren($name, $p, $data);
}
}
private function dumpProfile($edge, Twig_Profiler_Profile $profile, &$data)
{
if (isset($data[$edge])) {
$data[$edge]['ct'] += 1;
$data[$edge]['wt'] += floor($profile->getDuration() * 1000000);
$data[$edge]['mu'] += $profile->getMemoryUsage();
$data[$edge]['pmu'] += $profile->getPeakMemoryUsage();
} else {
$data[$edge] = array(
'ct' => 1,
'wt' => floor($profile->getDuration() * 1000000),
'mu' => $profile->getMemoryUsage(),
'pmu' => $profile->getPeakMemoryUsage(),
);
}
}
}
Twig-1.23.1/lib/Twig/Profiler/Dumper/Html.php 0000664 0000000 0000000 00000002637 12616650102 0020632 0 ustar 00root root 0000000 0000000
*/
class Twig_Profiler_Dumper_Html extends Twig_Profiler_Dumper_Text
{
private static $colors = array(
'block' => '#dfd',
'macro' => '#ddf',
'template' => '#ffd',
'big' => '#d44',
);
public function dump(Twig_Profiler_Profile $profile)
{
return ''.parent::dump($profile).' ';
}
protected function formatTemplate(Twig_Profiler_Profile $profile, $prefix)
{
return sprintf('%s└ %s ', $prefix, self::$colors['template'], $profile->getTemplate());
}
protected function formatNonTemplate(Twig_Profiler_Profile $profile, $prefix)
{
return sprintf('%s└ %s::%s(%s )', $prefix, $profile->getTemplate(), $profile->getType(), isset(self::$colors[$profile->getType()]) ? self::$colors[$profile->getType()] : 'auto', $profile->getName());
}
protected function formatTime(Twig_Profiler_Profile $profile, $percent)
{
return sprintf('%.2fms/%.0f%% ', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent);
}
}
Twig-1.23.1/lib/Twig/Profiler/Dumper/Text.php 0000664 0000000 0000000 00000003702 12616650102 0020644 0 ustar 00root root 0000000 0000000
*/
class Twig_Profiler_Dumper_Text
{
private $root;
public function dump(Twig_Profiler_Profile $profile)
{
return $this->dumpProfile($profile);
}
protected function formatTemplate(Twig_Profiler_Profile $profile, $prefix)
{
return sprintf('%s└ %s', $prefix, $profile->getTemplate());
}
protected function formatNonTemplate(Twig_Profiler_Profile $profile, $prefix)
{
return sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), $profile->getName());
}
protected function formatTime(Twig_Profiler_Profile $profile, $percent)
{
return sprintf('%.2fms/%.0f%%', $profile->getDuration() * 1000, $percent);
}
private function dumpProfile(Twig_Profiler_Profile $profile, $prefix = '', $sibling = false)
{
if ($profile->isRoot()) {
$this->root = $profile->getDuration();
$start = $profile->getName();
} else {
if ($profile->isTemplate()) {
$start = $this->formatTemplate($profile, $prefix);
} else {
$start = $this->formatNonTemplate($profile, $prefix);
}
$prefix .= $sibling ? '│ ' : ' ';
}
$percent = $this->root ? $profile->getDuration() / $this->root * 100 : 0;
if ($profile->getDuration() * 1000 < 1) {
$str = $start."\n";
} else {
$str = sprintf("%s %s\n", $start, $this->formatTime($profile, $percent));
}
$nCount = count($profile->getProfiles());
foreach ($profile as $i => $p) {
$str .= $this->dumpProfile($p, $prefix, $i + 1 !== $nCount);
}
return $str;
}
}
Twig-1.23.1/lib/Twig/Profiler/Node/ 0000775 0000000 0000000 00000000000 12616650102 0016636 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Profiler/Node/EnterProfile.php 0000664 0000000 0000000 00000002307 12616650102 0021747 0 ustar 00root root 0000000 0000000
*/
class Twig_Profiler_Node_EnterProfile extends Twig_Node
{
public function __construct($extensionName, $type, $name, $varName)
{
parent::__construct(array(), array('extension_name' => $extensionName, 'name' => $name, 'type' => $type, 'var_name' => $varName));
}
/**
* {@inheritdoc}
*/
public function compile(Twig_Compiler $compiler)
{
$compiler
->write(sprintf('$%s = $this->env->getExtension(', $this->getAttribute('var_name')))
->repr($this->getAttribute('extension_name'))
->raw(");\n")
->write(sprintf('$%s->enter($%s = new Twig_Profiler_Profile($this->getTemplateName(), ', $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof'))
->repr($this->getAttribute('type'))
->raw(', ')
->repr($this->getAttribute('name'))
->raw("));\n\n")
;
}
}
Twig-1.23.1/lib/Twig/Profiler/Node/LeaveProfile.php 0000664 0000000 0000000 00000001404 12616650102 0021723 0 ustar 00root root 0000000 0000000
*/
class Twig_Profiler_Node_LeaveProfile extends Twig_Node
{
public function __construct($varName)
{
parent::__construct(array(), array('var_name' => $varName));
}
/**
* {@inheritdoc}
*/
public function compile(Twig_Compiler $compiler)
{
$compiler
->write("\n")
->write(sprintf("\$%s->leave(\$%s);\n\n", $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof'))
;
}
}
Twig-1.23.1/lib/Twig/Profiler/NodeVisitor/ 0000775 0000000 0000000 00000000000 12616650102 0020216 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Profiler/NodeVisitor/Profiler.php 0000664 0000000 0000000 00000004436 12616650102 0022520 0 ustar 00root root 0000000 0000000
*/
class Twig_Profiler_NodeVisitor_Profiler extends Twig_BaseNodeVisitor
{
private $extensionName;
public function __construct($extensionName)
{
$this->extensionName = $extensionName;
}
/**
* {@inheritdoc}
*/
protected function doEnterNode(Twig_Node $node, Twig_Environment $env)
{
return $node;
}
/**
* {@inheritdoc}
*/
protected function doLeaveNode(Twig_Node $node, Twig_Environment $env)
{
if ($node instanceof Twig_Node_Module) {
$varName = $this->getVarName();
$node->setNode('display_start', new Twig_Node(array(new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::TEMPLATE, $node->getAttribute('filename'), $varName), $node->getNode('display_start'))));
$node->setNode('display_end', new Twig_Node(array(new Twig_Profiler_Node_LeaveProfile($varName), $node->getNode('display_end'))));
} elseif ($node instanceof Twig_Node_Block) {
$varName = $this->getVarName();
$node->setNode('body', new Twig_Node_Body(array(
new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::BLOCK, $node->getAttribute('name'), $varName),
$node->getNode('body'),
new Twig_Profiler_Node_LeaveProfile($varName),
)));
} elseif ($node instanceof Twig_Node_Macro) {
$varName = $this->getVarName();
$node->setNode('body', new Twig_Node_Body(array(
new Twig_Profiler_Node_EnterProfile($this->extensionName, Twig_Profiler_Profile::MACRO, $node->getAttribute('name'), $varName),
$node->getNode('body'),
new Twig_Profiler_Node_LeaveProfile($varName),
)));
}
return $node;
}
private function getVarName()
{
return sprintf('__internal_%s', hash('sha256', uniqid(mt_rand(), true), false));
}
/**
* {@inheritdoc}
*/
public function getPriority()
{
return 0;
}
}
Twig-1.23.1/lib/Twig/Profiler/Profile.php 0000664 0000000 0000000 00000007134 12616650102 0020067 0 ustar 00root root 0000000 0000000
*/
class Twig_Profiler_Profile implements IteratorAggregate, Serializable
{
const ROOT = 'ROOT';
const BLOCK = 'block';
const TEMPLATE = 'template';
const MACRO = 'macro';
private $template;
private $name;
private $type;
private $starts = array();
private $ends = array();
private $profiles = array();
public function __construct($template = 'main', $type = self::ROOT, $name = 'main')
{
$this->template = $template;
$this->type = $type;
$this->name = 0 === strpos($name, '__internal_') ? 'INTERNAL' : $name;
$this->enter();
}
public function getTemplate()
{
return $this->template;
}
public function getType()
{
return $this->type;
}
public function getName()
{
return $this->name;
}
public function isRoot()
{
return self::ROOT === $this->type;
}
public function isTemplate()
{
return self::TEMPLATE === $this->type;
}
public function isBlock()
{
return self::BLOCK === $this->type;
}
public function isMacro()
{
return self::MACRO === $this->type;
}
public function getProfiles()
{
return $this->profiles;
}
public function addProfile(Twig_Profiler_Profile $profile)
{
$this->profiles[] = $profile;
}
/**
* Returns the duration in microseconds.
*
* @return int
*/
public function getDuration()
{
if ($this->isRoot() && $this->profiles) {
// for the root node with children, duration is the sum of all child durations
$duration = 0;
foreach ($this->profiles as $profile) {
$duration += $profile->getDuration();
}
return $duration;
}
return isset($this->ends['wt']) && isset($this->starts['wt']) ? $this->ends['wt'] - $this->starts['wt'] : 0;
}
/**
* Returns the memory usage in bytes.
*
* @return int
*/
public function getMemoryUsage()
{
return isset($this->ends['mu']) && isset($this->starts['mu']) ? $this->ends['mu'] - $this->starts['mu'] : 0;
}
/**
* Returns the peak memory usage in bytes.
*
* @return int
*/
public function getPeakMemoryUsage()
{
return isset($this->ends['pmu']) && isset($this->starts['pmu']) ? $this->ends['pmu'] - $this->starts['pmu'] : 0;
}
/**
* Starts the profiling.
*/
public function enter()
{
$this->starts = array(
'wt' => microtime(true),
'mu' => memory_get_usage(),
'pmu' => memory_get_peak_usage(),
);
}
/**
* Stops the profiling.
*/
public function leave()
{
$this->ends = array(
'wt' => microtime(true),
'mu' => memory_get_usage(),
'pmu' => memory_get_peak_usage(),
);
}
public function getIterator()
{
return new ArrayIterator($this->profiles);
}
public function serialize()
{
return serialize(array($this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles));
}
public function unserialize($data)
{
list($this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles) = unserialize($data);
}
}
Twig-1.23.1/lib/Twig/Sandbox/ 0000775 0000000 0000000 00000000000 12616650102 0015565 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Sandbox/SecurityError.php 0000664 0000000 0000000 00000000600 12616650102 0021113 0 ustar 00root root 0000000 0000000
*/
class Twig_Sandbox_SecurityError extends Twig_Error
{
}
Twig-1.23.1/lib/Twig/Sandbox/SecurityNotAllowedFilterError.php 0000664 0000000 0000000 00000001410 12616650102 0024252 0 ustar 00root root 0000000 0000000
*/
class Twig_Sandbox_SecurityNotAllowedFilterError extends Twig_Sandbox_SecurityError
{
private $filterName;
public function __construct($message, $functionName, $lineno = -1, $filename = null, Exception $previous = null)
{
parent::__construct($message, $lineno, $filename, $previous);
$this->filterName = $functionName;
}
public function getFilterName()
{
return $this->filterName;
}
}
Twig-1.23.1/lib/Twig/Sandbox/SecurityNotAllowedFunctionError.php 0000664 0000000 0000000 00000001424 12616650102 0024617 0 ustar 00root root 0000000 0000000
*/
class Twig_Sandbox_SecurityNotAllowedFunctionError extends Twig_Sandbox_SecurityError
{
private $functionName;
public function __construct($message, $functionName, $lineno = -1, $filename = null, Exception $previous = null)
{
parent::__construct($message, $lineno, $filename, $previous);
$this->functionName = $functionName;
}
public function getFunctionName()
{
return $this->functionName;
}
}
Twig-1.23.1/lib/Twig/Sandbox/SecurityNotAllowedTagError.php 0000664 0000000 0000000 00000001354 12616650102 0023547 0 ustar 00root root 0000000 0000000
*/
class Twig_Sandbox_SecurityNotAllowedTagError extends Twig_Sandbox_SecurityError
{
private $tagName;
public function __construct($message, $tagName, $lineno = -1, $filename = null, Exception $previous = null)
{
parent::__construct($message, $lineno, $filename, $previous);
$this->tagName = $tagName;
}
public function getTagName()
{
return $this->tagName;
}
}
Twig-1.23.1/lib/Twig/Sandbox/SecurityPolicy.php 0000664 0000000 0000000 00000007305 12616650102 0021272 0 ustar 00root root 0000000 0000000
*/
class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterface
{
protected $allowedTags;
protected $allowedFilters;
protected $allowedMethods;
protected $allowedProperties;
protected $allowedFunctions;
public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array(), array $allowedFunctions = array())
{
$this->allowedTags = $allowedTags;
$this->allowedFilters = $allowedFilters;
$this->setAllowedMethods($allowedMethods);
$this->allowedProperties = $allowedProperties;
$this->allowedFunctions = $allowedFunctions;
}
public function setAllowedTags(array $tags)
{
$this->allowedTags = $tags;
}
public function setAllowedFilters(array $filters)
{
$this->allowedFilters = $filters;
}
public function setAllowedMethods(array $methods)
{
$this->allowedMethods = array();
foreach ($methods as $class => $m) {
$this->allowedMethods[$class] = array_map('strtolower', is_array($m) ? $m : array($m));
}
}
public function setAllowedProperties(array $properties)
{
$this->allowedProperties = $properties;
}
public function setAllowedFunctions(array $functions)
{
$this->allowedFunctions = $functions;
}
public function checkSecurity($tags, $filters, $functions)
{
foreach ($tags as $tag) {
if (!in_array($tag, $this->allowedTags)) {
throw new Twig_Sandbox_SecurityNotAllowedTagError(sprintf('Tag "%s" is not allowed.', $tag), $tag);
}
}
foreach ($filters as $filter) {
if (!in_array($filter, $this->allowedFilters)) {
throw new Twig_Sandbox_SecurityNotAllowedFilterError(sprintf('Filter "%s" is not allowed.', $filter), $filter);
}
}
foreach ($functions as $function) {
if (!in_array($function, $this->allowedFunctions)) {
throw new Twig_Sandbox_SecurityNotAllowedFunctionError(sprintf('Function "%s" is not allowed.', $function), $function);
}
}
}
public function checkMethodAllowed($obj, $method)
{
if ($obj instanceof Twig_TemplateInterface || $obj instanceof Twig_Markup) {
return true;
}
$allowed = false;
$method = strtolower($method);
foreach ($this->allowedMethods as $class => $methods) {
if ($obj instanceof $class) {
$allowed = in_array($method, $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)));
}
}
}
Twig-1.23.1/lib/Twig/Sandbox/SecurityPolicyInterface.php 0000664 0000000 0000000 00000001060 12616650102 0023103 0 ustar 00root root 0000000 0000000
*/
interface Twig_Sandbox_SecurityPolicyInterface
{
public function checkSecurity($tags, $filters, $functions);
public function checkMethodAllowed($obj, $method);
public function checkPropertyAllowed($obj, $method);
}
Twig-1.23.1/lib/Twig/SimpleFilter.php 0000664 0000000 0000000 00000004652 12616650102 0017306 0 ustar 00root root 0000000 0000000
*/
class Twig_SimpleFilter
{
protected $name;
protected $callable;
protected $options;
protected $arguments = array();
public function __construct($name, $callable, array $options = array())
{
$this->name = $name;
$this->callable = $callable;
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'is_variadic' => false,
'is_safe' => null,
'is_safe_callback' => null,
'pre_escape' => null,
'preserves_safety' => null,
'node_class' => 'Twig_Node_Expression_Filter',
'deprecated' => false,
'alternative' => null,
), $options);
}
public function getName()
{
return $this->name;
}
public function getCallable()
{
return $this->callable;
}
public function getNodeClass()
{
return $this->options['node_class'];
}
public function setArguments($arguments)
{
$this->arguments = $arguments;
}
public function getArguments()
{
return $this->arguments;
}
public function needsEnvironment()
{
return $this->options['needs_environment'];
}
public function needsContext()
{
return $this->options['needs_context'];
}
public function getSafe(Twig_Node $filterArgs)
{
if (null !== $this->options['is_safe']) {
return $this->options['is_safe'];
}
if (null !== $this->options['is_safe_callback']) {
return call_user_func($this->options['is_safe_callback'], $filterArgs);
}
}
public function getPreservesSafety()
{
return $this->options['preserves_safety'];
}
public function getPreEscape()
{
return $this->options['pre_escape'];
}
public function isVariadic()
{
return $this->options['is_variadic'];
}
public function isDeprecated()
{
return $this->options['deprecated'];
}
public function getAlternative()
{
return $this->options['alternative'];
}
}
Twig-1.23.1/lib/Twig/SimpleFunction.php 0000664 0000000 0000000 00000004275 12616650102 0017647 0 ustar 00root root 0000000 0000000
*/
class Twig_SimpleFunction
{
protected $name;
protected $callable;
protected $options;
protected $arguments = array();
public function __construct($name, $callable, array $options = array())
{
$this->name = $name;
$this->callable = $callable;
$this->options = array_merge(array(
'needs_environment' => false,
'needs_context' => false,
'is_variadic' => false,
'is_safe' => null,
'is_safe_callback' => null,
'node_class' => 'Twig_Node_Expression_Function',
'deprecated' => false,
'alternative' => null,
), $options);
}
public function getName()
{
return $this->name;
}
public function getCallable()
{
return $this->callable;
}
public function getNodeClass()
{
return $this->options['node_class'];
}
public function setArguments($arguments)
{
$this->arguments = $arguments;
}
public function getArguments()
{
return $this->arguments;
}
public function needsEnvironment()
{
return $this->options['needs_environment'];
}
public function needsContext()
{
return $this->options['needs_context'];
}
public function getSafe(Twig_Node $functionArgs)
{
if (null !== $this->options['is_safe']) {
return $this->options['is_safe'];
}
if (null !== $this->options['is_safe_callback']) {
return call_user_func($this->options['is_safe_callback'], $functionArgs);
}
return array();
}
public function isVariadic()
{
return $this->options['is_variadic'];
}
public function isDeprecated()
{
return $this->options['deprecated'];
}
public function getAlternative()
{
return $this->options['alternative'];
}
}
Twig-1.23.1/lib/Twig/SimpleTest.php 0000664 0000000 0000000 00000002433 12616650102 0016773 0 ustar 00root root 0000000 0000000
*/
class Twig_SimpleTest
{
protected $name;
protected $callable;
protected $options;
public function __construct($name, $callable, array $options = array())
{
$this->name = $name;
$this->callable = $callable;
$this->options = array_merge(array(
'is_variadic' => false,
'node_class' => 'Twig_Node_Expression_Test',
'deprecated' => false,
'alternative' => null,
), $options);
}
public function getName()
{
return $this->name;
}
public function getCallable()
{
return $this->callable;
}
public function getNodeClass()
{
return $this->options['node_class'];
}
public function isVariadic()
{
return $this->options['is_variadic'];
}
public function isDeprecated()
{
return $this->options['deprecated'];
}
public function getAlternative()
{
return $this->options['alternative'];
}
}
Twig-1.23.1/lib/Twig/Template.php 0000664 0000000 0000000 00000047613 12616650102 0016466 0 ustar 00root root 0000000 0000000
*/
abstract class Twig_Template implements Twig_TemplateInterface
{
protected static $cache = array();
protected $parent;
protected $parents = array();
protected $env;
protected $blocks = array();
protected $traits = array();
/**
* Constructor.
*
* @param Twig_Environment $env A Twig_Environment instance
*/
public function __construct(Twig_Environment $env)
{
$this->env = $env;
}
/**
* Returns the template name.
*
* @return string The template name
*/
abstract public function getTemplateName();
/**
* @deprecated since 1.20 (to be removed in 2.0)
*/
public function getEnvironment()
{
@trigger_error('The '.__METHOD__.' method is deprecated since version 1.20 and will be removed in 2.0.', E_USER_DEPRECATED);
return $this->env;
}
/**
* Returns the parent template.
*
* This method is for internal use only and should never be called
* directly.
*
* @param array $context
*
* @return Twig_TemplateInterface|false The parent template or false if there is no parent
*
* @internal
*/
public function getParent(array $context)
{
if (null !== $this->parent) {
return $this->parent;
}
try {
$parent = $this->doGetParent($context);
if (false === $parent) {
return false;
}
if ($parent instanceof self) {
return $this->parents[$parent->getTemplateName()] = $parent;
}
if (!isset($this->parents[$parent])) {
$this->parents[$parent] = $this->loadTemplate($parent);
}
} catch (Twig_Error_Loader $e) {
$e->setTemplateFile(null);
$e->guess();
throw $e;
}
return $this->parents[$parent];
}
protected function doGetParent(array $context)
{
return false;
}
public function isTraitable()
{
return true;
}
/**
* Displays a parent block.
*
* This method is for internal use only and should never be called
* directly.
*
* @param string $name The block name to display from the parent
* @param array $context The context
* @param array $blocks The current set of blocks
*
* @internal
*/
public function displayParentBlock($name, array $context, array $blocks = array())
{
$name = (string) $name;
if (isset($this->traits[$name])) {
$this->traits[$name][0]->displayBlock($name, $context, $blocks, false);
} elseif (false !== $parent = $this->getParent($context)) {
$parent->displayBlock($name, $context, $blocks, false);
} else {
throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
}
}
/**
* Displays a block.
*
* This method is for internal use only and should never be called
* directly.
*
* @param string $name The block name to display
* @param array $context The context
* @param array $blocks The current set of blocks
* @param bool $useBlocks Whether to use the current set of blocks
*
* @internal
*/
public function displayBlock($name, array $context, array $blocks = array(), $useBlocks = true)
{
$name = (string) $name;
if ($useBlocks && isset($blocks[$name])) {
$template = $blocks[$name][0];
$block = $blocks[$name][1];
} elseif (isset($this->blocks[$name])) {
$template = $this->blocks[$name][0];
$block = $this->blocks[$name][1];
} else {
$template = null;
$block = null;
}
if (null !== $template) {
// avoid RCEs when sandbox is enabled
if (!$template instanceof self) {
throw new LogicException('A block must be a method on a Twig_Template instance.');
}
try {
$template->$block($context, $blocks);
} catch (Twig_Error $e) {
if (!$e->getTemplateFile()) {
$e->setTemplateFile($template->getTemplateName());
}
// this is mostly useful for Twig_Error_Loader exceptions
// see Twig_Error_Loader
if (false === $e->getTemplateLine()) {
$e->setTemplateLine(-1);
$e->guess();
}
throw $e;
} catch (Exception $e) {
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getTemplateName(), $e);
}
} elseif (false !== $parent = $this->getParent($context)) {
$parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false);
}
}
/**
* Renders a parent block.
*
* This method is for internal use only and should never be called
* directly.
*
* @param string $name The block name to render from the parent
* @param array $context The context
* @param array $blocks The current set of blocks
*
* @return string The rendered block
*
* @internal
*/
public function renderParentBlock($name, array $context, array $blocks = array())
{
ob_start();
$this->displayParentBlock($name, $context, $blocks);
return ob_get_clean();
}
/**
* Renders a block.
*
* This method is for internal use only and should never be called
* directly.
*
* @param string $name The block name to render
* @param array $context The context
* @param array $blocks The current set of blocks
* @param bool $useBlocks Whether to use the current set of blocks
*
* @return string The rendered block
*
* @internal
*/
public function renderBlock($name, array $context, array $blocks = array(), $useBlocks = true)
{
ob_start();
$this->displayBlock($name, $context, $blocks, $useBlocks);
return ob_get_clean();
}
/**
* Returns whether a block exists or not.
*
* This method is for internal use only and should never be called
* directly.
*
* This method does only return blocks defined in the current template
* or defined in "used" traits.
*
* It does not return blocks from parent templates as the parent
* template name can be dynamic, which is only known based on the
* current context.
*
* @param string $name The block name
*
* @return bool true if the block exists, false otherwise
*
* @internal
*/
public function hasBlock($name)
{
return isset($this->blocks[(string) $name]);
}
/**
* Returns all block names.
*
* This method is for internal use only and should never be called
* directly.
*
* @return array An array of block names
*
* @see hasBlock
*
* @internal
*/
public function getBlockNames()
{
return array_keys($this->blocks);
}
protected function loadTemplate($template, $templateName = null, $line = null, $index = null)
{
try {
if (is_array($template)) {
return $this->env->resolveTemplate($template);
}
if ($template instanceof self) {
return $template;
}
return $this->env->loadTemplate($template, $index);
} catch (Twig_Error $e) {
if (!$e->getTemplateFile()) {
$e->setTemplateFile($templateName ? $templateName : $this->getTemplateName());
}
if ($e->getTemplateLine()) {
throw $e;
}
if (!$line) {
$e->guess();
} else {
$e->setTemplateLine($line);
}
throw $e;
}
}
/**
* Returns all blocks.
*
* This method is for internal use only and should never be called
* directly.
*
* @return array An array of blocks
*
* @see hasBlock
*
* @internal
*/
public function getBlocks()
{
return $this->blocks;
}
/**
* Returns the template source code.
*
* @return string|null The template source code or null if it is not available
*/
public function getSource()
{
$reflector = new ReflectionClass($this);
$file = $reflector->getFileName();
if (!file_exists($file)) {
return;
}
$source = file($file, FILE_IGNORE_NEW_LINES);
array_splice($source, 0, $reflector->getEndLine());
$i = 0;
while (isset($source[$i]) && '/* */' === substr_replace($source[$i], '', 3, -2)) {
$source[$i] = str_replace('*//* ', '*/', substr($source[$i], 3, -2));
++$i;
}
array_splice($source, $i);
return implode("\n", $source);
}
/**
* {@inheritdoc}
*/
public function display(array $context, array $blocks = array())
{
$this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks));
}
/**
* {@inheritdoc}
*/
public function render(array $context)
{
$level = ob_get_level();
ob_start();
try {
$this->display($context);
} catch (Exception $e) {
while (ob_get_level() > $level) {
ob_end_clean();
}
throw $e;
}
return ob_get_clean();
}
protected function displayWithErrorHandling(array $context, array $blocks = array())
{
try {
$this->doDisplay($context, $blocks);
} catch (Twig_Error $e) {
if (!$e->getTemplateFile()) {
$e->setTemplateFile($this->getTemplateName());
}
// this is mostly useful for Twig_Error_Loader exceptions
// see Twig_Error_Loader
if (false === $e->getTemplateLine()) {
$e->setTemplateLine(-1);
$e->guess();
}
throw $e;
} catch (Exception $e) {
throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getTemplateName(), $e);
}
}
/**
* Auto-generated method to display the template with the given context.
*
* @param array $context An array of parameters to pass to the template
* @param array $blocks An array of blocks to pass to the template
*/
abstract protected function doDisplay(array $context, array $blocks = array());
/**
* Returns a variable from the context.
*
* This method is for internal use only and should never be called
* directly.
*
* This method should not be overridden in a sub-class as this is an
* implementation detail that has been introduced to optimize variable
* access for versions of PHP before 5.4. This is not a way to override
* the way to get a variable value.
*
* @param array $context The context
* @param string $item The variable to return from the context
* @param bool $ignoreStrictCheck Whether to ignore the strict variable check or not
*
* @return mixed The content of the context variable
*
* @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
*
* @internal
*/
final protected function getContext($context, $item, $ignoreStrictCheck = false)
{
if (!array_key_exists($item, $context)) {
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return;
}
throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
}
return $context[$item];
}
/**
* Returns the attribute value for a given array/object.
*
* @param mixed $object The object or array from where to get the item
* @param mixed $item The item to get from the array or object
* @param array $arguments An array of arguments to pass if the item is an object method
* @param string $type The type of attribute (@see Twig_Template constants)
* @param bool $isDefinedTest Whether this is only a defined check
* @param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not
*
* @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
*
* @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
*/
protected function getAttribute($object, $item, array $arguments = array(), $type = self::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
{
// array
if (self::METHOD_CALL !== $type) {
$arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
if ((is_array($object) && array_key_exists($arrayItem, $object))
|| ($object instanceof ArrayAccess && isset($object[$arrayItem]))
) {
if ($isDefinedTest) {
return true;
}
return $object[$arrayItem];
}
if (self::ARRAY_CALL === $type || !is_object($object)) {
if ($isDefinedTest) {
return false;
}
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return;
}
if ($object instanceof ArrayAccess) {
$message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist', $arrayItem, get_class($object));
} elseif (is_object($object)) {
$message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface', $item, get_class($object));
} elseif (is_array($object)) {
if (empty($object)) {
$message = sprintf('Key "%s" does not exist as the array is empty', $arrayItem);
} else {
$message = sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object)));
}
} elseif (self::ARRAY_CALL === $type) {
if (null === $object) {
$message = sprintf('Impossible to access a key ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
} elseif (null === $object) {
$message = sprintf('Impossible to access an attribute ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
}
}
if (!is_object($object)) {
if ($isDefinedTest) {
return false;
}
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return;
}
if (null === $object) {
$message = sprintf('Impossible to invoke a method ("%s") on a null variable', $item);
} else {
$message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object);
}
throw new Twig_Error_Runtime($message, -1, $this->getTemplateName());
}
// object property
if (self::METHOD_CALL !== $type && !$object instanceof self) { // Twig_Template does not have public properties, and we don't want to allow access to internal ones
if (isset($object->$item) || array_key_exists((string) $item, $object)) {
if ($isDefinedTest) {
return true;
}
if ($this->env->hasExtension('sandbox')) {
$this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
}
return $object->$item;
}
}
$class = get_class($object);
// object method
if (!isset(self::$cache[$class]['methods'])) {
// get_class_methods returns all methods accessible in the scope, but we only want public ones to be accessible in templates
if ($object instanceof self) {
$ref = new ReflectionClass($class);
$methods = array();
foreach ($ref->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod) {
$methodName = strtolower($refMethod->name);
// Accessing the environment from templates is forbidden to prevent untrusted changes to the environment
if ('getenvironment' !== $methodName) {
$methods[$methodName] = true;
}
}
self::$cache[$class]['methods'] = $methods;
} else {
self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
}
}
$call = false;
$lcItem = strtolower($item);
if (isset(self::$cache[$class]['methods'][$lcItem])) {
$method = (string) $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 = (string) $item;
$call = true;
} else {
if ($isDefinedTest) {
return false;
}
if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
return;
}
throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
}
if ($isDefinedTest) {
return true;
}
if ($this->env->hasExtension('sandbox')) {
$this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
}
// Some objects throw exceptions when they have __call, and the method we try
// to call is not supported. If ignoreStrictCheck is true, we should return null.
try {
$ret = call_user_func_array(array($object, $method), $arguments);
} catch (BadMethodCallException $e) {
if ($call && ($ignoreStrictCheck || !$this->env->isStrictVariables())) {
return;
}
throw $e;
}
// useful when calling a template method from a template
// this is not supported but unfortunately heavily used in the Symfony profiler
if ($object instanceof Twig_TemplateInterface) {
return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
}
return $ret;
}
}
Twig-1.23.1/lib/Twig/TemplateInterface.php 0000664 0000000 0000000 00000002333 12616650102 0020275 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 3.0)
*/
interface Twig_TemplateInterface
{
const ANY_CALL = 'any';
const ARRAY_CALL = 'array';
const METHOD_CALL = 'method';
/**
* 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);
/**
* Displays the template with the given context.
*
* @param array $context An array of parameters to pass to the template
* @param array $blocks An array of blocks to pass to the template
*/
public function display(array $context, array $blocks = array());
/**
* Returns the bound environment for this template.
*
* @return Twig_Environment The current environment
*/
public function getEnvironment();
}
Twig-1.23.1/lib/Twig/Test.php 0000664 0000000 0000000 00000001611 12616650102 0015616 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
abstract class Twig_Test implements Twig_TestInterface, Twig_TestCallableInterface
{
protected $options;
protected $arguments = array();
public function __construct(array $options = array())
{
$this->options = array_merge(array(
'callable' => null,
), $options);
}
public function getCallable()
{
return $this->options['callable'];
}
}
Twig-1.23.1/lib/Twig/Test/ 0000775 0000000 0000000 00000000000 12616650102 0015106 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Test/Function.php 0000664 0000000 0000000 00000001542 12616650102 0017406 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Test_Function extends Twig_Test
{
protected $function;
public function __construct($function, array $options = array())
{
$options['callable'] = $function;
parent::__construct($options);
$this->function = $function;
}
public function compile()
{
return $this->function;
}
}
Twig-1.23.1/lib/Twig/Test/IntegrationTestCase.php 0000664 0000000 0000000 00000017206 12616650102 0021544 0 ustar 00root root 0000000 0000000
* @author Karma Dordrak
*/
abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
{
/**
* @return string
*/
abstract protected function getFixturesDir();
/**
* @return Twig_ExtensionInterface[]
*/
protected function getExtensions()
{
return array();
}
/**
* @return Twig_SimpleFilter[]
*/
protected function getTwigFilters()
{
return array();
}
/**
* @return Twig_SimpleFunction[]
*/
protected function getTwigFunctions()
{
return array();
}
/**
* @return Twig_SimpleTest[]
*/
protected function getTwigTests()
{
return array();
}
/**
* @dataProvider getTests
*/
public function testIntegration($file, $message, $condition, $templates, $exception, $outputs)
{
$this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs);
}
/**
* @dataProvider getLegacyTests
* @group legacy
*/
public function testLegacyIntegration($file, $message, $condition, $templates, $exception, $outputs)
{
$this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs);
}
public function getTests($name, $legacyTests = false)
{
$fixturesDir = realpath($this->getFixturesDir());
$tests = array();
foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
if (!preg_match('/\.test$/', $file)) {
continue;
}
if ($legacyTests xor false !== strpos($file->getRealpath(), '.legacy.test')) {
continue;
}
$test = file_get_contents($file->getRealpath());
if (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)\s*(?:--DATA--\s*(.*))?\s*--EXCEPTION--\s*(.*)/sx', $test, $match)) {
$message = $match[1];
$condition = $match[2];
$templates = self::parseTemplates($match[3]);
$exception = $match[5];
$outputs = array(array(null, $match[4], null, ''));
} elseif (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s', $test, $match)) {
$message = $match[1];
$condition = $match[2];
$templates = self::parseTemplates($match[3]);
$exception = false;
preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, PREG_SET_ORDER);
} else {
throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file)));
}
$tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $templates, $exception, $outputs);
}
if ($legacyTests && empty($tests)) {
// add a dummy test to avoid a PHPUnit message
return array(array('not', '-', '', array(), '', array()));
}
return $tests;
}
public function getLegacyTests()
{
return $this->getTests('testLegacyIntegration', true);
}
protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs)
{
if ($condition) {
eval('$ret = '.$condition.';');
if (!$ret) {
$this->markTestSkipped($condition);
}
}
$loader = new Twig_Loader_Array($templates);
foreach ($outputs as $i => $match) {
$config = array_merge(array(
'cache' => false,
'strict_variables' => true,
), $match[2] ? eval($match[2].';') : array());
$twig = new Twig_Environment($loader, $config);
$twig->addGlobal('global', 'global');
foreach ($this->getExtensions() as $extension) {
$twig->addExtension($extension);
}
foreach ($this->getTwigFilters() as $filter) {
$twig->addFilter($filter);
}
foreach ($this->getTwigTests() as $test) {
$twig->addTest($test);
}
foreach ($this->getTwigFunctions() as $function) {
$twig->addFunction($function);
}
// avoid using the same PHP class name for different cases
// only for PHP 5.2+
if (PHP_VERSION_ID >= 50300) {
$p = new ReflectionProperty($twig, 'templateClassPrefix');
$p->setAccessible(true);
$p->setValue($twig, '__TwigTemplate_'.hash('sha256', uniqid(mt_rand(), true), false).'_');
}
try {
$template = $twig->loadTemplate('index.twig');
} catch (Exception $e) {
if (false !== $exception) {
$message = $e->getMessage();
$this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $message)));
$this->assertSame('.', substr($message, strlen($message) - 1), $message, 'Exception message must end with a dot.');
return;
}
if ($e instanceof Twig_Error_Syntax) {
$e->setTemplateFile($file);
throw $e;
}
throw new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
}
try {
$output = trim($template->render(eval($match[1].';')), "\n ");
} catch (Exception $e) {
if (false !== $exception) {
$this->assertSame(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage())));
return;
}
if ($e instanceof Twig_Error_Syntax) {
$e->setTemplateFile($file);
} else {
$e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
}
$output = trim(sprintf('%s: %s', get_class($e), $e->getMessage()));
}
if (false !== $exception) {
list($class) = explode(':', $exception);
$this->assertThat(null, new PHPUnit_Framework_Constraint_Exception($class));
}
$expected = trim($match[3], "\n ");
if ($expected !== $output) {
printf("Compiled templates that failed on case %d:\n", $i + 1);
foreach (array_keys($templates) as $name) {
echo "Template: $name\n";
$source = $loader->getSource($name);
echo $twig->compile($twig->parse($twig->tokenize($source, $name)));
}
}
$this->assertEquals($expected, $output, $message.' (in '.$file.')');
}
}
protected static function parseTemplates($test)
{
$templates = array();
preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, PREG_SET_ORDER);
foreach ($matches as $match) {
$templates[($match[1] ? $match[1] : 'index.twig')] = $match[2];
}
return $templates;
}
}
Twig-1.23.1/lib/Twig/Test/Method.php 0000664 0000000 0000000 00000002026 12616650102 0017037 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Test_Method extends Twig_Test
{
protected $extension;
protected $method;
public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
{
$options['callable'] = array($extension, $method);
parent::__construct($options);
$this->extension = $extension;
$this->method = $method;
}
public function compile()
{
return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
}
}
Twig-1.23.1/lib/Twig/Test/Node.php 0000664 0000000 0000000 00000001460 12616650102 0016505 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_Test_Node extends Twig_Test
{
protected $class;
public function __construct($class, array $options = array())
{
parent::__construct($options);
$this->class = $class;
}
public function getClass()
{
return $this->class;
}
public function compile()
{
}
}
Twig-1.23.1/lib/Twig/Test/NodeTestCase.php 0000664 0000000 0000000 00000003531 12616650102 0020142 0 ustar 00root root 0000000 0000000 assertNodeCompilation($source, $node, $environment, $isPattern);
}
public function assertNodeCompilation($source, Twig_Node $node, Twig_Environment $environment = null, $isPattern = false)
{
$compiler = $this->getCompiler($environment);
$compiler->compile($node);
if ($isPattern) {
$this->assertStringMatchesFormat($source, trim($compiler->getSource()));
} else {
$this->assertEquals($source, trim($compiler->getSource()));
}
}
protected function getCompiler(Twig_Environment $environment = null)
{
return new Twig_Compiler(null === $environment ? $this->getEnvironment() : $environment);
}
protected function getEnvironment()
{
return new Twig_Environment(new Twig_Loader_Array(array()));
}
protected function getVariableGetter($name, $line = false)
{
$line = $line > 0 ? "// line {$line}\n" : '';
if (PHP_VERSION_ID >= 50400) {
return sprintf('%s(isset($context["%s"]) ? $context["%s"] : null)', $line, $name, $name);
}
return sprintf('%s$this->getContext($context, "%s")', $line, $name);
}
protected function getAttributeGetter()
{
if (function_exists('twig_template_get_attributes')) {
return 'twig_template_get_attributes($this, ';
}
return '$this->getAttribute(';
}
}
Twig-1.23.1/lib/Twig/TestCallableInterface.php 0000664 0000000 0000000 00000000663 12616650102 0021065 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_TestCallableInterface
{
public function getCallable();
}
Twig-1.23.1/lib/Twig/TestInterface.php 0000664 0000000 0000000 00000000775 12616650102 0017451 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_TestInterface
{
/**
* Compiles a test.
*
* @return string The PHP code for the test
*/
public function compile();
}
Twig-1.23.1/lib/Twig/Token.php 0000664 0000000 0000000 00000013626 12616650102 0015770 0 ustar 00root root 0000000 0000000
*/
class Twig_Token
{
protected $value;
protected $type;
protected $lineno;
const EOF_TYPE = -1;
const TEXT_TYPE = 0;
const BLOCK_START_TYPE = 1;
const VAR_START_TYPE = 2;
const BLOCK_END_TYPE = 3;
const VAR_END_TYPE = 4;
const NAME_TYPE = 5;
const NUMBER_TYPE = 6;
const STRING_TYPE = 7;
const OPERATOR_TYPE = 8;
const PUNCTUATION_TYPE = 9;
const INTERPOLATION_START_TYPE = 10;
const INTERPOLATION_END_TYPE = 11;
/**
* Constructor.
*
* @param int $type The type of the token
* @param string $value The token value
* @param int $lineno The line position in the source
*/
public function __construct($type, $value, $lineno)
{
$this->type = $type;
$this->value = $value;
$this->lineno = $lineno;
}
/**
* Returns a string representation of the token.
*
* @return string A string representation of the token
*/
public function __toString()
{
return sprintf('%s(%s)', self::typeToString($this->type, true), $this->value);
}
/**
* Tests the current token for a type and/or a value.
*
* Parameters may be:
* * just type
* * type and value (or array of possible values)
* * just value (or array of possible values) (NAME_TYPE is used as type)
*
* @param array|int $type The type to test
* @param array|string|null $values The token value
*
* @return bool
*/
public function test($type, $values = null)
{
if (null === $values && !is_int($type)) {
$values = $type;
$type = self::NAME_TYPE;
}
return ($this->type === $type) && (
null === $values ||
(is_array($values) && in_array($this->value, $values)) ||
$this->value == $values
);
}
/**
* Gets the line.
*
* @return int The source line
*/
public function getLine()
{
return $this->lineno;
}
/**
* Gets the token type.
*
* @return int The token type
*/
public function getType()
{
return $this->type;
}
/**
* Gets the token value.
*
* @return string The token value
*/
public function getValue()
{
return $this->value;
}
/**
* Returns the constant representation (internal) of a given type.
*
* @param int $type The type as an integer
* @param bool $short Whether to return a short representation or not
*
* @return string The string representation
*/
public static function typeToString($type, $short = false)
{
switch ($type) {
case self::EOF_TYPE:
$name = 'EOF_TYPE';
break;
case self::TEXT_TYPE:
$name = 'TEXT_TYPE';
break;
case self::BLOCK_START_TYPE:
$name = 'BLOCK_START_TYPE';
break;
case self::VAR_START_TYPE:
$name = 'VAR_START_TYPE';
break;
case self::BLOCK_END_TYPE:
$name = 'BLOCK_END_TYPE';
break;
case self::VAR_END_TYPE:
$name = 'VAR_END_TYPE';
break;
case self::NAME_TYPE:
$name = 'NAME_TYPE';
break;
case self::NUMBER_TYPE:
$name = 'NUMBER_TYPE';
break;
case self::STRING_TYPE:
$name = 'STRING_TYPE';
break;
case self::OPERATOR_TYPE:
$name = 'OPERATOR_TYPE';
break;
case self::PUNCTUATION_TYPE:
$name = 'PUNCTUATION_TYPE';
break;
case self::INTERPOLATION_START_TYPE:
$name = 'INTERPOLATION_START_TYPE';
break;
case self::INTERPOLATION_END_TYPE:
$name = 'INTERPOLATION_END_TYPE';
break;
default:
throw new LogicException(sprintf('Token of type "%s" does not exist.', $type));
}
return $short ? $name : 'Twig_Token::'.$name;
}
/**
* Returns the english representation of a given type.
*
* @param int $type The type as an integer
*
* @return string The string representation
*/
public static function typeToEnglish($type)
{
switch ($type) {
case self::EOF_TYPE:
return 'end of template';
case self::TEXT_TYPE:
return 'text';
case self::BLOCK_START_TYPE:
return 'begin of statement block';
case self::VAR_START_TYPE:
return 'begin of print statement';
case self::BLOCK_END_TYPE:
return 'end of statement block';
case self::VAR_END_TYPE:
return 'end of print statement';
case self::NAME_TYPE:
return 'name';
case self::NUMBER_TYPE:
return 'number';
case self::STRING_TYPE:
return 'string';
case self::OPERATOR_TYPE:
return 'operator';
case self::PUNCTUATION_TYPE:
return 'punctuation';
case self::INTERPOLATION_START_TYPE:
return 'begin of string interpolation';
case self::INTERPOLATION_END_TYPE:
return 'end of string interpolation';
default:
throw new LogicException(sprintf('Token of type "%s" does not exist.', $type));
}
}
}
Twig-1.23.1/lib/Twig/TokenParser.php 0000664 0000000 0000000 00000001243 12616650102 0017135 0 ustar 00root root 0000000 0000000
*/
abstract class Twig_TokenParser implements Twig_TokenParserInterface
{
/**
* @var Twig_Parser
*/
protected $parser;
/**
* Sets the parser associated with this token parser.
*
* @param Twig_Parser $parser A Twig_Parser instance
*/
public function setParser(Twig_Parser $parser)
{
$this->parser = $parser;
}
}
Twig-1.23.1/lib/Twig/TokenParser/ 0000775 0000000 0000000 00000000000 12616650102 0016424 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/TokenParser/AutoEscape.php 0000664 0000000 0000000 00000004613 12616650102 0021172 0 ustar 00root root 0000000 0000000
* {% autoescape true %}
* Everything will be automatically escaped in this block
* {% endautoescape %}
*
* {% autoescape false %}
* Everything will be outputed as is in this block
* {% endautoescape %}
*
* {% autoescape true js %}
* Everything will be automatically escaped in this block
* using the js escaping strategy
* {% endautoescape %}
*
*/
class Twig_TokenParser_AutoEscape extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
if ($stream->test(Twig_Token::BLOCK_END_TYPE)) {
$value = 'html';
} else {
$expr = $this->parser->getExpressionParser()->parseExpression();
if (!$expr instanceof Twig_Node_Expression_Constant) {
throw new Twig_Error_Syntax('An escaping strategy must be a string or a bool.', $stream->getCurrent()->getLine(), $stream->getFilename());
}
$value = $expr->getAttribute('value');
$compat = true === $value || false === $value;
if (true === $value) {
$value = 'html';
}
if ($compat && $stream->test(Twig_Token::NAME_TYPE)) {
@trigger_error('Using the autoescape tag with "true" or "false" before the strategy name is deprecated.', E_USER_DEPRECATED);
if (false === $value) {
throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $stream->getCurrent()->getLine(), $stream->getFilename());
}
$value = $stream->next()->getValue();
}
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_AutoEscape($value, $body, $lineno, $this->getTag());
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endautoescape');
}
public function getTag()
{
return 'autoescape';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Block.php 0000664 0000000 0000000 00000004426 12616650102 0020175 0 ustar 00root root 0000000 0000000
* {% block head %}
*
* {% block title %}{% endblock %} - My Webpage
* {% endblock %}
*
*/
class Twig_TokenParser_Block extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
if ($this->parser->hasBlock($name)) {
throw new Twig_Error_Syntax(sprintf("The block '%s' has already been defined line %d.", $name, $this->parser->getBlock($name)->getLine()), $stream->getCurrent()->getLine(), $stream->getFilename());
}
$this->parser->setBlock($name, $block = new Twig_Node_Block($name, new Twig_Node(array()), $lineno));
$this->parser->pushLocalScope();
$this->parser->pushBlockStack($name);
if ($stream->nextIf(Twig_Token::BLOCK_END_TYPE)) {
$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
if ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) {
$value = $token->getValue();
if ($value != $name) {
throw new Twig_Error_Syntax(sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getFilename());
}
}
} else {
$body = new Twig_Node(array(
new Twig_Node_Print($this->parser->getExpressionParser()->parseExpression(), $lineno),
));
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$block->setNode('body', $body);
$this->parser->popBlockStack();
$this->parser->popLocalScope();
return new Twig_Node_BlockReference($name, $lineno, $this->getTag());
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endblock');
}
public function getTag()
{
return 'block';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Do.php 0000664 0000000 0000000 00000001240 12616650102 0017474 0 ustar 00root root 0000000 0000000 parser->getExpressionParser()->parseExpression();
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_Do($expr, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'do';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Embed.php 0000664 0000000 0000000 00000003147 12616650102 0020156 0 ustar 00root root 0000000 0000000 parser->getStream();
$parent = $this->parser->getExpressionParser()->parseExpression();
list($variables, $only, $ignoreMissing) = $this->parseArguments();
// inject a fake parent to make the parent() function work
$stream->injectTokens(array(
new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', $token->getLine()),
new Twig_Token(Twig_Token::NAME_TYPE, 'extends', $token->getLine()),
new Twig_Token(Twig_Token::STRING_TYPE, '__parent__', $token->getLine()),
new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', $token->getLine()),
));
$module = $this->parser->parse($stream, array($this, 'decideBlockEnd'), true);
// override the parent with the correct one
$module->setNode('parent', $parent);
$this->parser->embedTemplate($module);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_Embed($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endembed');
}
public function getTag()
{
return 'embed';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Extends.php 0000664 0000000 0000000 00000002031 12616650102 0020543 0 ustar 00root root 0000000 0000000
* {% extends "base.html" %}
*
*/
class Twig_TokenParser_Extends extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
if (!$this->parser->isMainScope()) {
throw new Twig_Error_Syntax('Cannot extend from a block.', $token->getLine(), $this->parser->getFilename());
}
if (null !== $this->parser->getParent()) {
throw new Twig_Error_Syntax('Multiple extends tags are forbidden.', $token->getLine(), $this->parser->getFilename());
}
$this->parser->setParent($this->parser->getExpressionParser()->parseExpression());
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
}
public function getTag()
{
return 'extends';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Filter.php 0000664 0000000 0000000 00000002562 12616650102 0020367 0 ustar 00root root 0000000 0000000
* {% filter upper %}
* This text becomes uppercase
* {% endfilter %}
*
*/
class Twig_TokenParser_Filter extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$name = $this->parser->getVarName();
$ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), true, $token->getLine(), $this->getTag());
$filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag());
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$block = new Twig_Node_Block($name, $body, $token->getLine());
$this->parser->setBlock($name, $block);
return new Twig_Node_Print($filter, $token->getLine(), $this->getTag());
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endfilter');
}
public function getTag()
{
return 'filter';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Flush.php 0000664 0000000 0000000 00000001125 12616650102 0020215 0 ustar 00root root 0000000 0000000 parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_Flush($token->getLine(), $this->getTag());
}
public function getTag()
{
return 'flush';
}
}
Twig-1.23.1/lib/Twig/TokenParser/For.php 0000664 0000000 0000000 00000010615 12616650102 0017666 0 ustar 00root root 0000000 0000000
*
* {% for user in users %}
* {{ user.username|e }}
* {% endfor %}
*
*
*/
class Twig_TokenParser_For extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
$stream->expect(Twig_Token::OPERATOR_TYPE, 'in');
$seq = $this->parser->getExpressionParser()->parseExpression();
$ifexpr = null;
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'if')) {
$ifexpr = $this->parser->getExpressionParser()->parseExpression();
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideForFork'));
if ($stream->next()->getValue() == 'else') {
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$else = $this->parser->subparse(array($this, 'decideForEnd'), true);
} else {
$else = null;
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
if (count($targets) > 1) {
$keyTarget = $targets->getNode(0);
$keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine());
$valueTarget = $targets->getNode(1);
$valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
} else {
$keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
$valueTarget = $targets->getNode(0);
$valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
}
if ($ifexpr) {
$this->checkLoopUsageCondition($stream, $ifexpr);
$this->checkLoopUsageBody($stream, $body);
}
return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
}
public function decideForFork(Twig_Token $token)
{
return $token->test(array('else', 'endfor'));
}
public function decideForEnd(Twig_Token $token)
{
return $token->test('endfor');
}
// the loop variable cannot be used in the condition
protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
{
if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition.', $node->getLine(), $stream->getFilename());
}
foreach ($node as $n) {
if (!$n) {
continue;
}
$this->checkLoopUsageCondition($stream, $n);
}
}
// check usage of non-defined loop-items
// it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node)
{
if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
$attribute = $node->getNode('attribute');
if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
throw new Twig_Error_Syntax(sprintf('The "loop.%s" variable is not defined when looping with a condition.', $attribute->getAttribute('value')), $node->getLine(), $stream->getFilename());
}
}
// should check for parent.loop.XXX usage
if ($node instanceof Twig_Node_For) {
return;
}
foreach ($node as $n) {
if (!$n) {
continue;
}
$this->checkLoopUsageBody($stream, $n);
}
}
public function getTag()
{
return 'for';
}
}
Twig-1.23.1/lib/Twig/TokenParser/From.php 0000664 0000000 0000000 00000003302 12616650102 0020036 0 ustar 00root root 0000000 0000000
* {% from 'forms.html' import forms %}
*
*/
class Twig_TokenParser_From extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$macro = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
$stream->expect('import');
$targets = array();
do {
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
$alias = $name;
if ($stream->nextIf('as')) {
$alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
}
$targets[$name] = $alias;
if (!$stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
break;
}
} while (true);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$node = new Twig_Node_Import($macro, new Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $token->getLine(), $this->getTag());
foreach ($targets as $name => $alias) {
if ($this->parser->isReservedMacroName($name)) {
throw new Twig_Error_Syntax(sprintf('"%s" cannot be an imported macro as it is a reserved keyword.', $name), $token->getLine(), $stream->getFilename());
}
$this->parser->addImportedSymbol('function', $alias, 'get'.$name, $node->getNode('var'));
}
return $node;
}
public function getTag()
{
return 'from';
}
}
Twig-1.23.1/lib/Twig/TokenParser/If.php 0000664 0000000 0000000 00000004542 12616650102 0017500 0 ustar 00root root 0000000 0000000
* {% if users %}
*
* {% for user in users %}
* {{ user.username|e }}
* {% endfor %}
*
* {% endif %}
*
*/
class Twig_TokenParser_If extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$expr = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideIfFork'));
$tests = array($expr, $body);
$else = null;
$end = false;
while (!$end) {
switch ($stream->next()->getValue()) {
case 'else':
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$else = $this->parser->subparse(array($this, 'decideIfEnd'));
break;
case 'elseif':
$expr = $this->parser->getExpressionParser()->parseExpression();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideIfFork'));
$tests[] = $expr;
$tests[] = $body;
break;
case 'endif':
$end = true;
break;
default:
throw new Twig_Error_Syntax(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getFilename());
}
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_If(new Twig_Node($tests), $else, $lineno, $this->getTag());
}
public function decideIfFork(Twig_Token $token)
{
return $token->test(array('elseif', 'else', 'endif'));
}
public function decideIfEnd(Twig_Token $token)
{
return $token->test(array('endif'));
}
public function getTag()
{
return 'if';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Import.php 0000664 0000000 0000000 00000001734 12616650102 0020414 0 ustar 00root root 0000000 0000000
* {% import 'forms.html' as forms %}
*
*/
class Twig_TokenParser_Import extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$macro = $this->parser->getExpressionParser()->parseExpression();
$this->parser->getStream()->expect('as');
$var = new Twig_Node_Expression_AssignName($this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(), $token->getLine());
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$this->parser->addImportedSymbol('template', $var->getAttribute('name'));
return new Twig_Node_Import($macro, $var, $token->getLine(), $this->getTag());
}
public function getTag()
{
return 'import';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Include.php 0000664 0000000 0000000 00000003002 12616650102 0020513 0 ustar 00root root 0000000 0000000
* {% include 'header.html' %}
* Body
* {% include 'footer.html' %}
*
*/
class Twig_TokenParser_Include extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$expr = $this->parser->getExpressionParser()->parseExpression();
list($variables, $only, $ignoreMissing) = $this->parseArguments();
return new Twig_Node_Include($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
}
protected function parseArguments()
{
$stream = $this->parser->getStream();
$ignoreMissing = false;
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'ignore')) {
$stream->expect(Twig_Token::NAME_TYPE, 'missing');
$ignoreMissing = true;
}
$variables = null;
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'with')) {
$variables = $this->parser->getExpressionParser()->parseExpression();
}
$only = false;
if ($stream->nextIf(Twig_Token::NAME_TYPE, 'only')) {
$only = true;
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
return array($variables, $only, $ignoreMissing);
}
public function getTag()
{
return 'include';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Macro.php 0000664 0000000 0000000 00000003272 12616650102 0020202 0 ustar 00root root 0000000 0000000
* {% macro input(name, value, type, size) %}
*
* {% endmacro %}
*
*/
class Twig_TokenParser_Macro extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
$arguments = $this->parser->getExpressionParser()->parseArguments(true, true);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$this->parser->pushLocalScope();
$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
if ($token = $stream->nextIf(Twig_Token::NAME_TYPE)) {
$value = $token->getValue();
if ($value != $name) {
throw new Twig_Error_Syntax(sprintf('Expected endmacro for macro "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getFilename());
}
}
$this->parser->popLocalScope();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$this->parser->setMacro($name, new Twig_Node_Macro($name, new Twig_Node_Body(array($body)), $arguments, $lineno, $this->getTag()));
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endmacro');
}
public function getTag()
{
return 'macro';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Sandbox.php 0000664 0000000 0000000 00000003153 12616650102 0020535 0 ustar 00root root 0000000 0000000
* {% sandbox %}
* {% include 'user.html' %}
* {% endsandbox %}
*
*
* @see http://www.twig-project.org/doc/api.html#sandbox-extension for details
*/
class Twig_TokenParser_Sandbox extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
// in a sandbox tag, only include tags are allowed
if (!$body instanceof Twig_Node_Include) {
foreach ($body as $node) {
if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
continue;
}
if (!$node instanceof Twig_Node_Include) {
throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section.', $node->getLine(), $this->parser->getFilename());
}
}
}
return new Twig_Node_Sandbox($body, $token->getLine(), $this->getTag());
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endsandbox');
}
public function getTag()
{
return 'sandbox';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Set.php 0000664 0000000 0000000 00000003672 12616650102 0017700 0 ustar 00root root 0000000 0000000
* {% set foo = 'foo' %}
*
* {% set foo = [1, 2] %}
*
* {% set foo = {'foo': 'bar'} %}
*
* {% set foo = 'foo' ~ 'bar' %}
*
* {% set foo, bar = 'foo', 'bar' %}
*
* {% set foo %}Some content{% endset %}
*
*/
class Twig_TokenParser_Set extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$stream = $this->parser->getStream();
$names = $this->parser->getExpressionParser()->parseAssignmentExpression();
$capture = false;
if ($stream->nextIf(Twig_Token::OPERATOR_TYPE, '=')) {
$values = $this->parser->getExpressionParser()->parseMultitargetExpression();
$stream->expect(Twig_Token::BLOCK_END_TYPE);
if (count($names) !== count($values)) {
throw new Twig_Error_Syntax('When using set, you must have the same number of variables and assignments.', $stream->getCurrent()->getLine(), $stream->getFilename());
}
} else {
$capture = true;
if (count($names) > 1) {
throw new Twig_Error_Syntax('When using set with a block, you cannot have a multi-target.', $stream->getCurrent()->getLine(), $stream->getFilename());
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$values = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
}
return new Twig_Node_Set($capture, $names, $values, $lineno, $this->getTag());
}
public function decideBlockEnd(Twig_Token $token)
{
return $token->test('endset');
}
public function getTag()
{
return 'set';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Spaceless.php 0000664 0000000 0000000 00000002074 12616650102 0021062 0 ustar 00root root 0000000 0000000
* {% spaceless %}
*
* foo
*
* {% endspaceless %}
*
* {# output will be foo
#}
*
*/
class Twig_TokenParser_Spaceless extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$lineno = $token->getLine();
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
$body = $this->parser->subparse(array($this, 'decideSpacelessEnd'), true);
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_Spaceless($body, $lineno, $this->getTag());
}
public function decideSpacelessEnd(Twig_Token $token)
{
return $token->test('endspaceless');
}
public function getTag()
{
return 'spaceless';
}
}
Twig-1.23.1/lib/Twig/TokenParser/Use.php 0000664 0000000 0000000 00000003443 12616650102 0017675 0 ustar 00root root 0000000 0000000
* {% extends "base.html" %}
*
* {% use "blocks.html" %}
*
* {% block title %}{% endblock %}
* {% block content %}{% endblock %}
*
*
* @see http://www.twig-project.org/doc/templates.html#horizontal-reuse for details.
*/
class Twig_TokenParser_Use extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$template = $this->parser->getExpressionParser()->parseExpression();
$stream = $this->parser->getStream();
if (!$template instanceof Twig_Node_Expression_Constant) {
throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $stream->getCurrent()->getLine(), $stream->getFilename());
}
$targets = array();
if ($stream->nextIf('with')) {
do {
$name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
$alias = $name;
if ($stream->nextIf('as')) {
$alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
}
$targets[$name] = new Twig_Node_Expression_Constant($alias, -1);
if (!$stream->nextIf(Twig_Token::PUNCTUATION_TYPE, ',')) {
break;
}
} while (true);
}
$stream->expect(Twig_Token::BLOCK_END_TYPE);
$this->parser->addTrait(new Twig_Node(array('template' => $template, 'targets' => new Twig_Node($targets))));
}
public function getTag()
{
return 'use';
}
}
Twig-1.23.1/lib/Twig/TokenParserBroker.php 0000664 0000000 0000000 00000010140 12616650102 0020276 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface
{
protected $parser;
protected $parsers = array();
protected $brokers = array();
/**
* Constructor.
*
* @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances
* @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances
* @param bool $triggerDeprecationError
*/
public function __construct($parsers = array(), $brokers = array(), $triggerDeprecationError = true)
{
if ($triggerDeprecationError) {
@trigger_error('The '.__CLASS__.' class is deprecated since version 1.12 and will be removed in 2.0.', E_USER_DEPRECATED);
}
foreach ($parsers as $parser) {
if (!$parser instanceof Twig_TokenParserInterface) {
throw new LogicException('$parsers must a an array of Twig_TokenParserInterface.');
}
$this->parsers[$parser->getTag()] = $parser;
}
foreach ($brokers as $broker) {
if (!$broker instanceof Twig_TokenParserBrokerInterface) {
throw new LogicException('$brokers must a an array of Twig_TokenParserBrokerInterface.');
}
$this->brokers[] = $broker;
}
}
/**
* Adds a TokenParser.
*
* @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
*/
public function addTokenParser(Twig_TokenParserInterface $parser)
{
$this->parsers[$parser->getTag()] = $parser;
}
/**
* Removes a TokenParser.
*
* @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
*/
public function removeTokenParser(Twig_TokenParserInterface $parser)
{
$name = $parser->getTag();
if (isset($this->parsers[$name]) && $parser === $this->parsers[$name]) {
unset($this->parsers[$name]);
}
}
/**
* Adds a TokenParserBroker.
*
* @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance
*/
public function addTokenParserBroker(Twig_TokenParserBroker $broker)
{
$this->brokers[] = $broker;
}
/**
* Removes a TokenParserBroker.
*
* @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance
*/
public function removeTokenParserBroker(Twig_TokenParserBroker $broker)
{
if (false !== $pos = array_search($broker, $this->brokers)) {
unset($this->brokers[$pos]);
}
}
/**
* Gets a suitable TokenParser for a tag.
*
* First looks in parsers, then in brokers.
*
* @param string $tag A tag name
*
* @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found
*/
public function getTokenParser($tag)
{
if (isset($this->parsers[$tag])) {
return $this->parsers[$tag];
}
$broker = end($this->brokers);
while (false !== $broker) {
$parser = $broker->getTokenParser($tag);
if (null !== $parser) {
return $parser;
}
$broker = prev($this->brokers);
}
}
public function getParsers()
{
return $this->parsers;
}
public function getParser()
{
return $this->parser;
}
public function setParser(Twig_ParserInterface $parser)
{
$this->parser = $parser;
foreach ($this->parsers as $tokenParser) {
$tokenParser->setParser($parser);
}
foreach ($this->brokers as $broker) {
$broker->setParser($parser);
}
}
}
Twig-1.23.1/lib/Twig/TokenParserBrokerInterface.php 0000664 0000000 0000000 00000002371 12616650102 0022126 0 ustar 00root root 0000000 0000000
*
* @deprecated since 1.12 (to be removed in 2.0)
*/
interface Twig_TokenParserBrokerInterface
{
/**
* Gets a TokenParser suitable for a tag.
*
* @param string $tag A tag name
*
* @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found
*/
public function getTokenParser($tag);
/**
* Calls Twig_TokenParserInterface::setParser on all parsers the implementation knows of.
*
* @param Twig_ParserInterface $parser A Twig_ParserInterface interface
*/
public function setParser(Twig_ParserInterface $parser);
/**
* Gets the Twig_ParserInterface.
*
* @return null|Twig_ParserInterface A Twig_ParserInterface instance or null
*/
public function getParser();
}
Twig-1.23.1/lib/Twig/TokenParserInterface.php 0000664 0000000 0000000 00000001706 12616650102 0020762 0 ustar 00root root 0000000 0000000
*/
interface Twig_TokenParserInterface
{
/**
* Sets the parser associated with this token parser.
*
* @param Twig_Parser $parser A Twig_Parser instance
*/
public function setParser(Twig_Parser $parser);
/**
* Parses a token and returns a node.
*
* @param Twig_Token $token A Twig_Token instance
*
* @return Twig_NodeInterface A Twig_NodeInterface instance
*
* @throws Twig_Error_Syntax
*/
public function parse(Twig_Token $token);
/**
* Gets the tag name associated with this token parser.
*
* @return string The tag name
*/
public function getTag();
}
Twig-1.23.1/lib/Twig/TokenStream.php 0000664 0000000 0000000 00000007561 12616650102 0017145 0 ustar 00root root 0000000 0000000
*/
class Twig_TokenStream
{
protected $tokens;
protected $current = 0;
protected $filename;
/**
* Constructor.
*
* @param array $tokens An array of tokens
* @param string $filename The name of the filename which tokens are associated with
*/
public function __construct(array $tokens, $filename = null)
{
$this->tokens = $tokens;
$this->filename = $filename;
}
/**
* Returns a string representation of the token stream.
*
* @return string
*/
public function __toString()
{
return implode("\n", $this->tokens);
}
public function injectTokens(array $tokens)
{
$this->tokens = array_merge(array_slice($this->tokens, 0, $this->current), $tokens, array_slice($this->tokens, $this->current));
}
/**
* Sets the pointer to the next token and returns the old one.
*
* @return Twig_Token
*/
public function next()
{
if (!isset($this->tokens[++$this->current])) {
throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current - 1]->getLine(), $this->filename);
}
return $this->tokens[$this->current - 1];
}
/**
* Tests a token, sets the pointer to the next one and returns it or throws a syntax error.
*
* @return Twig_Token|null The next token if the condition is true, null otherwise
*/
public function nextIf($primary, $secondary = null)
{
if ($this->tokens[$this->current]->test($primary, $secondary)) {
return $this->next();
}
}
/**
* Tests a token and returns it or throws a syntax error.
*
* @return Twig_Token
*/
public function expect($type, $value = null, $message = null)
{
$token = $this->tokens[$this->current];
if (!$token->test($type, $value)) {
$line = $token->getLine();
throw new Twig_Error_Syntax(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s).',
$message ? $message.'. ' : '',
Twig_Token::typeToEnglish($token->getType()), $token->getValue(),
Twig_Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''),
$line,
$this->filename
);
}
$this->next();
return $token;
}
/**
* Looks at the next token.
*
* @param int $number
*
* @return Twig_Token
*/
public function look($number = 1)
{
if (!isset($this->tokens[$this->current + $number])) {
throw new Twig_Error_Syntax('Unexpected end of template.', $this->tokens[$this->current + $number - 1]->getLine(), $this->filename);
}
return $this->tokens[$this->current + $number];
}
/**
* Tests the current token.
*
* @return bool
*/
public function test($primary, $secondary = null)
{
return $this->tokens[$this->current]->test($primary, $secondary);
}
/**
* Checks if end of stream was reached.
*
* @return bool
*/
public function isEOF()
{
return $this->tokens[$this->current]->getType() === Twig_Token::EOF_TYPE;
}
/**
* Gets the current token.
*
* @return Twig_Token
*/
public function getCurrent()
{
return $this->tokens[$this->current];
}
/**
* Gets the filename associated with this stream.
*
* @return string
*/
public function getFilename()
{
return $this->filename;
}
}
Twig-1.23.1/lib/Twig/Util/ 0000775 0000000 0000000 00000000000 12616650102 0015104 5 ustar 00root root 0000000 0000000 Twig-1.23.1/lib/Twig/Util/DeprecationCollector.php 0000664 0000000 0000000 00000004113 12616650102 0021720 0 ustar 00root root 0000000 0000000
*/
class Twig_Util_DeprecationCollector
{
private $twig;
private $deprecations;
public function __construct(Twig_Environment $twig)
{
$this->twig = $twig;
}
/**
* Returns deprecations for templates contained in a directory.
*
* @param string $dir A directory where templates are stored
* @param string $ext Limit the loaded templates by extension
*
* @return array() An array of deprecations
*/
public function collectDir($dir, $ext = '.twig')
{
$iterator = new RegexIterator(
new RecursiveIteratorIterator(
new RecursiveDirectoryIterator($dir), RecursiveIteratorIterator::LEAVES_ONLY
), '{'.preg_quote($ext).'$}'
);
return $this->collect(new Twig_Util_TemplateDirIterator($iterator));
}
/**
* Returns deprecations for passed templates.
*
* @param Iterator $iterator An iterator of templates (where keys are template names and values the contents of the template)
*
* @return array() An array of deprecations
*/
public function collect(Iterator $iterator)
{
$this->deprecations = array();
set_error_handler(array($this, 'errorHandler'));
foreach ($iterator as $name => $contents) {
try {
$this->twig->parse($this->twig->tokenize($contents, $name));
} catch (Twig_Error_Syntax $e) {
// ignore templates containing syntax errors
}
}
restore_error_handler();
$deprecations = $this->deprecations;
$this->deprecations = array();
return $deprecations;
}
/**
* @internal
*/
public function errorHandler($type, $msg)
{
if (E_USER_DEPRECATED === $type) {
$this->deprecations[] = $msg;
}
}
}
Twig-1.23.1/lib/Twig/Util/TemplateDirIterator.php 0000664 0000000 0000000 00000000761 12616650102 0021545 0 ustar 00root root 0000000 0000000
*/
class Twig_Util_TemplateDirIterator extends IteratorIterator
{
public function current()
{
return file_get_contents(parent::current());
}
public function key()
{
return (string) parent::key();
}
}
Twig-1.23.1/phpunit.xml.dist 0000664 0000000 0000000 00000001214 12616650102 0015660 0 ustar 00root root 0000000 0000000
./test/Twig/
./lib/Twig/
Twig-1.23.1/test/ 0000775 0000000 0000000 00000000000 12616650102 0013466 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/ 0000775 0000000 0000000 00000000000 12616650102 0014400 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/ 0000775 0000000 0000000 00000000000 12616650102 0015502 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/AutoloaderTest.php 0000664 0000000 0000000 00000001216 12616650102 0021152 0 ustar 00root root 0000000 0000000 assertFalse(class_exists('FooBarFoo'), '->autoload() does not try to load classes that does not begin with Twig');
$autoloader = new Twig_Autoloader();
$this->assertNull($autoloader->autoload('Foo'), '->autoload() returns false if it is not able to load a class');
}
}
Twig-1.23.1/test/Twig/Tests/Cache/ 0000775 0000000 0000000 00000000000 12616650102 0016505 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Cache/FilesystemTest.php 0000664 0000000 0000000 00000011502 12616650102 0022201 0 ustar 00root root 0000000 0000000 nonce = hash('sha256', uniqid(mt_rand(), true));
$this->classname = '__Twig_Tests_Cache_FilesystemTest_Template_'.$this->nonce;
$this->directory = sys_get_temp_dir().'/twig-test-'.$this->nonce;
$this->cache = new Twig_Cache_Filesystem($this->directory);
}
protected function tearDown()
{
if (file_exists($this->directory)) {
$iterator = new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->directory), RecursiveIteratorIterator::CHILD_FIRST);
foreach ($iterator as $filename => $fileInfo) {
if (!$iterator->isDot()) {
if ($fileInfo->isDir()) {
rmdir($filename);
} else {
unlink($filename);
}
}
}
rmdir($this->directory);
}
}
public function testLoad()
{
$key = $this->directory.'/cache/cachefile.php';
$dir = dirname($key);
@mkdir($dir, 0777, true);
$this->assertTrue(is_dir($dir));
$this->assertFalse(class_exists($this->classname, false));
$content = $this->generateSource();
file_put_contents($key, $content);
$this->cache->load($key);
$this->assertTrue(class_exists($this->classname, false));
}
public function testLoadMissing()
{
$key = $this->directory.'/cache/cachefile.php';
$this->assertFalse(class_exists($this->classname, false));
$this->cache->load($key);
$this->assertFalse(class_exists($this->classname, false));
}
public function testWrite()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
$this->assertFalse(file_exists($this->directory));
$this->cache->write($key, $content);
$this->assertTrue(file_exists($this->directory));
$this->assertTrue(file_exists($key));
$this->assertSame(file_get_contents($key), $content);
}
/**
* @expectedException RuntimeException
* @expectedExceptionMessageRegExp #^Unable to create the cache directory #
*/
public function testWriteFailMkdir()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
// Create read-only root directory.
@mkdir($this->directory, 0555, true);
$this->assertTrue(is_dir($this->directory));
$this->cache->write($key, $content);
}
/**
* @expectedException RuntimeException
* @expectedExceptionMessageRegExp #^Unable to write in the cache directory #
*/
public function testWriteFailDirWritable()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
// Create root directory.
@mkdir($this->directory, 0777, true);
// Create read-only subdirectory.
@mkdir($this->directory.'/cache' , 0555);
$this->assertTrue(is_dir($this->directory.'/cache'));
$this->cache->write($key, $content);
}
/**
* @expectedException RuntimeException
* @expectedExceptionMessageRegExp #^Failed to write cache file #
*/
public function testWriteFailWriteFile()
{
$key = $this->directory.'/cache/cachefile.php';
$content = $this->generateSource();
$this->assertFalse(file_exists($key));
// Create a directory in the place of the cache file.
@mkdir($key, 0777, true);
$this->assertTrue(is_dir($key));
$this->cache->write($key, $content);
}
public function testGetTimestamp()
{
$key = $this->directory.'/cache/cachefile.php';
$dir = dirname($key);
@mkdir($dir, 0777, true);
$this->assertTrue(is_dir($dir));
// Create the file with a specific modification time.
touch($key, 1234567890);
$this->assertSame(1234567890, $this->cache->getTimestamp($key));
}
public function testGetTimestampMissingFile()
{
$key = $this->directory.'/cache/cachefile.php';
$this->assertSame(0, $this->cache->getTimestamp($key));
}
private function generateSource()
{
return strtr(' $this->classname,
));
}
}
Twig-1.23.1/test/Twig/Tests/CompilerTest.php 0000664 0000000 0000000 00000002110 12616650102 0020617 0 ustar 00root root 0000000 0000000 getMock('Twig_LoaderInterface')));
$locale = setlocale(LC_NUMERIC, 0);
if (false === $locale) {
$this->markTestSkipped('Your platform does not support locales.');
}
$required_locales = array('fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252');
if (false === setlocale(LC_NUMERIC, $required_locales)) {
$this->markTestSkipped('Could not set any of required locales: '.implode(', ', $required_locales));
}
$this->assertEquals('1.2', $compiler->repr(1.2)->getSource());
$this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0)));
setlocale(LC_NUMERIC, $locale);
}
}
Twig-1.23.1/test/Twig/Tests/EnvironmentTest.php 0000664 0000000 0000000 00000041713 12616650102 0021365 0 ustar 00root root 0000000 0000000 render('test');
}
public function testAutoescapeOption()
{
$loader = new Twig_Loader_Array(array(
'html' => '{{ foo }} {{ foo }}',
'js' => '{{ bar }} {{ bar }}',
));
$twig = new Twig_Environment($loader, array(
'debug' => true,
'cache' => false,
'autoescape' => array($this, 'escapingStrategyCallback'),
));
$this->assertEquals('foo<br/ > foo<br/ >', $twig->render('html', array('foo' => 'foo ')));
$this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo ')));
}
public function escapingStrategyCallback($filename)
{
return $filename;
}
public function testGlobals()
{
// globals can be added after calling getGlobals
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->addGlobal('foo', 'bar');
$globals = $twig->getGlobals();
$this->assertEquals('bar', $globals['foo']);
// globals can be modified after a template has been loaded
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->loadTemplate('index');
$twig->addGlobal('foo', 'bar');
$globals = $twig->getGlobals();
$this->assertEquals('bar', $globals['foo']);
// globals can be modified after extensions init
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
$twig->addGlobal('foo', 'bar');
$globals = $twig->getGlobals();
$this->assertEquals('bar', $globals['foo']);
// globals can be modified after extensions and a template has been loaded
$twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{foo}}')));
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
$twig->loadTemplate('index');
$twig->addGlobal('foo', 'bar');
$globals = $twig->getGlobals();
$this->assertEquals('bar', $globals['foo']);
$twig = new Twig_Environment($loader);
$twig->getGlobals();
$twig->addGlobal('foo', 'bar');
$template = $twig->loadTemplate('index');
$this->assertEquals('bar', $template->render(array()));
/* to be uncomment in Twig 2.0
// globals cannot be added after a template has been loaded
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->loadTemplate('index');
try {
$twig->addGlobal('bar', 'bar');
$this->fail();
} catch (LogicException $e) {
$this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
}
// globals cannot be added after extensions init
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
try {
$twig->addGlobal('bar', 'bar');
$this->fail();
} catch (LogicException $e) {
$this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
}
// globals cannot be added after extensions and a template has been loaded
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addGlobal('foo', 'foo');
$twig->getGlobals();
$twig->getFunctions();
$twig->loadTemplate('index');
try {
$twig->addGlobal('bar', 'bar');
$this->fail();
} catch (LogicException $e) {
$this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
}
// test adding globals after a template has been loaded without call to getGlobals
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->loadTemplate('index');
try {
$twig->addGlobal('bar', 'bar');
$this->fail();
} catch (LogicException $e) {
$this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
}
*/
}
public function testCompileSourceInlinesSource()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$source = " */*foo*/ ?>\r\nbar\n";
$expected = "/* *//* *foo*//* ?>*/\n/* bar*/\n/* */\n";
$compiled = $twig->compileSource($source, 'index');
$this->assertContains($expected, $compiled);
$this->assertNotContains('/**', $compiled);
}
public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate()
{
$uid = function_exists('posix_getuid') ? posix_getuid() : '';
$cache = new Twig_Cache_Filesystem($dir = sys_get_temp_dir().'/twig'.$uid);
$options = array('cache' => $cache, 'auto_reload' => false, 'debug' => false);
// force compilation
$twig = new Twig_Environment($loader = new Twig_Loader_Array(array('index' => '{{ foo }}')), $options);
$key = $cache->generateKey('index', $twig->getTemplateClass('index'));
$cache->write($key, $twig->compileSource('{{ foo }}', 'index'));
// check that extensions won't be initialized when rendering a template that is already in the cache
$twig = $this
->getMockBuilder('Twig_Environment')
->setConstructorArgs(array($loader, $options))
->setMethods(array('initExtensions'))
->getMock()
;
$twig->expects($this->never())->method('initExtensions');
// render template
$output = $twig->render('index', array('foo' => 'bar'));
$this->assertEquals('bar', $output);
unlink($key);
}
public function testAutoReloadCacheMiss()
{
$templateName = __FUNCTION__;
$templateContent = __FUNCTION__;
$cache = $this->getMock('Twig_CacheInterface');
$loader = $this->getMockLoader($templateName, $templateContent);
$twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
// Cache miss: getTimestamp returns 0 and as a result the load() is
// skipped.
$cache->expects($this->once())
->method('generateKey')
->will($this->returnValue('key'));
$cache->expects($this->once())
->method('getTimestamp')
->will($this->returnValue(0));
$loader->expects($this->never())
->method('isFresh');
$cache->expects($this->never())
->method('load');
$twig->loadTemplate($templateName);
}
public function testAutoReloadCacheHit()
{
$templateName = __FUNCTION__;
$templateContent = __FUNCTION__;
$cache = $this->getMock('Twig_CacheInterface');
$loader = $this->getMockLoader($templateName, $templateContent);
$twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
$now = time();
// Cache hit: getTimestamp returns something > extension timestamps and
// the loader returns true for isFresh().
$cache->expects($this->once())
->method('generateKey')
->will($this->returnValue('key'));
$cache->expects($this->once())
->method('getTimestamp')
->will($this->returnValue($now));
$loader->expects($this->once())
->method('isFresh')
->will($this->returnValue(true));
$cache->expects($this->once())
->method('load');
$twig->loadTemplate($templateName);
}
public function testAutoReloadOutdatedCacheHit()
{
$templateName = __FUNCTION__;
$templateContent = __FUNCTION__;
$cache = $this->getMock('Twig_CacheInterface');
$loader = $this->getMockLoader($templateName, $templateContent);
$twig = new Twig_Environment($loader, array('cache' => $cache, 'auto_reload' => true, 'debug' => false));
$now = time();
$cache->expects($this->once())
->method('generateKey')
->will($this->returnValue('key'));
$cache->expects($this->once())
->method('getTimestamp')
->will($this->returnValue($now));
$loader->expects($this->once())
->method('isFresh')
->will($this->returnValue(false));
$cache->expects($this->never())
->method('load');
$twig->loadTemplate($templateName);
}
public function testAddExtension()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
$this->assertArrayHasKey('test', $twig->getTags());
$this->assertArrayHasKey('foo_filter', $twig->getFilters());
$this->assertArrayHasKey('foo_function', $twig->getFunctions());
$this->assertArrayHasKey('foo_test', $twig->getTests());
$this->assertArrayHasKey('foo_unary', $twig->getUnaryOperators());
$this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators());
$this->assertArrayHasKey('foo_global', $twig->getGlobals());
$visitors = $twig->getNodeVisitors();
$this->assertEquals('Twig_Tests_EnvironmentTest_NodeVisitor', get_class($visitors[2]));
}
/**
* @requires PHP 5.3
*/
public function testAddExtensionWithDeprecatedGetGlobals()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addExtension(new Twig_Tests_EnvironmentTest_Extension_WithGlobals());
$this->deprecations = array();
set_error_handler(array($this, 'handleError'));
$this->assertArrayHasKey('foo_global', $twig->getGlobals());
$this->assertCount(1, $this->deprecations);
$this->assertContains('Defining the getGlobals() method in the "environment_test" extension is deprecated', $this->deprecations[0]);
restore_error_handler();
}
/**
* @group legacy
*/
public function testRemoveExtension()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
$twig->removeExtension('environment_test');
$this->assertFalse(array_key_exists('test', $twig->getTags()));
$this->assertFalse(array_key_exists('foo_filter', $twig->getFilters()));
$this->assertFalse(array_key_exists('foo_function', $twig->getFunctions()));
$this->assertFalse(array_key_exists('foo_test', $twig->getTests()));
$this->assertFalse(array_key_exists('foo_unary', $twig->getUnaryOperators()));
$this->assertFalse(array_key_exists('foo_binary', $twig->getBinaryOperators()));
$this->assertFalse(array_key_exists('foo_global', $twig->getGlobals()));
$this->assertCount(2, $twig->getNodeVisitors());
}
public function testAddMockExtension()
{
$extension = $this->getMock('Twig_ExtensionInterface');
$extension->expects($this->once())
->method('getName')
->will($this->returnValue('mock'));
$loader = new Twig_Loader_Array(array('page' => 'hey'));
$twig = new Twig_Environment($loader);
$twig->addExtension($extension);
$this->assertInstanceOf('Twig_ExtensionInterface', $twig->getExtension('mock'));
$this->assertTrue($twig->isTemplateFresh('page', time()));
}
public function testInitRuntimeWithAnExtensionUsingInitRuntimeNoDeprecation()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime());
$twig->initRuntime();
}
/**
* @requires PHP 5.3
*/
public function testInitRuntimeWithAnExtensionUsingInitRuntimeDeprecation()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime());
$this->deprecations = array();
set_error_handler(array($this, 'handleError'));
$twig->initRuntime();
$this->assertCount(1, $this->deprecations);
$this->assertContains('Defining the initRuntime() method in the "with_deprecation" extension is deprecated.', $this->deprecations[0]);
restore_error_handler();
}
public function handleError($type, $msg)
{
if (E_USER_DEPRECATED === $type) {
$this->deprecations[] = $msg;
}
}
/**
* @requires PHP 5.3
*/
public function testOverrideExtension()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->addExtension(new Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime());
$this->deprecations = array();
set_error_handler(array($this, 'handleError'));
$twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
$twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
$this->assertCount(1, $this->deprecations);
$this->assertContains('The possibility to register the same extension twice', $this->deprecations[0]);
restore_error_handler();
}
protected function getMockLoader($templateName, $templateContent)
{
$loader = $this->getMock('Twig_LoaderInterface');
$loader->expects($this->any())
->method('getSource')
->with($templateName)
->will($this->returnValue($templateContent));
$loader->expects($this->any())
->method('getCacheKey')
->with($templateName)
->will($this->returnValue($templateName));
return $loader;
}
}
class Twig_Tests_EnvironmentTest_Extension_WithGlobals extends Twig_Extension
{
public function getGlobals()
{
return array(
'foo_global' => 'foo_global',
);
}
public function getName()
{
return 'environment_test';
}
}
class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension implements Twig_Extension_GlobalsInterface
{
public function getTokenParsers()
{
return array(
new Twig_Tests_EnvironmentTest_TokenParser(),
);
}
public function getNodeVisitors()
{
return array(
new Twig_Tests_EnvironmentTest_NodeVisitor(),
);
}
public function getFilters()
{
return array(
new Twig_SimpleFilter('foo_filter', 'foo_filter'),
);
}
public function getTests()
{
return array(
new Twig_SimpleTest('foo_test', 'foo_test'),
);
}
public function getFunctions()
{
return array(
new Twig_SimpleFunction('foo_function', 'foo_function'),
);
}
public function getOperators()
{
return array(
array('foo_unary' => array()),
array('foo_binary' => array()),
);
}
public function getGlobals()
{
return array(
'foo_global' => 'foo_global',
);
}
public function getName()
{
return 'environment_test';
}
}
class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
}
public function getTag()
{
return 'test';
}
}
class Twig_Tests_EnvironmentTest_NodeVisitor implements Twig_NodeVisitorInterface
{
public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
{
return $node;
}
public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
{
return $node;
}
public function getPriority()
{
return 0;
}
}
class Twig_Tests_EnvironmentTest_ExtensionWithDeprecationInitRuntime extends Twig_Extension
{
public function initRuntime(Twig_Environment $env)
{
}
public function getName()
{
return 'with_deprecation';
}
}
class Twig_Tests_EnvironmentTest_ExtensionWithoutDeprecationInitRuntime extends Twig_Extension implements Twig_Extension_InitRuntimeInterface
{
public function initRuntime(Twig_Environment $env)
{
}
public function getName()
{
return 'without_deprecation';
}
}
Twig-1.23.1/test/Twig/Tests/ErrorTest.php 0000664 0000000 0000000 00000011413 12616650102 0020144 0 ustar 00root root 0000000 0000000 setTemplateFile(new SplFileInfo(__FILE__));
$this->assertContains('test'.DIRECTORY_SEPARATOR.'Twig'.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage());
}
public function testErrorWithArrayFilename()
{
$error = new Twig_Error('foo');
$error->setTemplateFile(array('foo' => 'bar'));
$this->assertEquals('foo in {"foo":"bar"}', $error->getMessage());
}
public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritanceOnDisk()
{
$loader = new Twig_Loader_Filesystem(dirname(__FILE__).'/Fixtures/errors');
$twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
$template = $twig->loadTemplate('index.html');
try {
$template->render(array());
$this->fail();
} catch (Twig_Error_Runtime $e) {
$this->assertEquals('Variable "foo" does not exist in "index.html" at line 3', $e->getMessage());
$this->assertEquals(3, $e->getTemplateLine());
$this->assertEquals('index.html', $e->getTemplateFile());
}
try {
$template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
$this->fail();
} catch (Twig_Error_Runtime $e) {
$this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage());
$this->assertEquals(3, $e->getTemplateLine());
$this->assertEquals('index.html', $e->getTemplateFile());
}
}
/**
* @dataProvider getErroredTemplates
*/
public function testTwigExceptionAddsFileAndLine($templates, $name, $line)
{
$loader = new Twig_Loader_Array($templates);
$twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
$template = $twig->loadTemplate('index');
try {
$template->render(array());
$this->fail();
} catch (Twig_Error_Runtime $e) {
$this->assertEquals(sprintf('Variable "foo" does not exist in "%s" at line %d', $name, $line), $e->getMessage());
$this->assertEquals($line, $e->getTemplateLine());
$this->assertEquals($name, $e->getTemplateFile());
}
try {
$template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
$this->fail();
} catch (Twig_Error_Runtime $e) {
$this->assertEquals(sprintf('An exception has been thrown during the rendering of a template ("Runtime error...") in "%s" at line %d.', $name, $line), $e->getMessage());
$this->assertEquals($line, $e->getTemplateLine());
$this->assertEquals($name, $e->getTemplateFile());
}
}
public function getErroredTemplates()
{
return array(
// error occurs in a template
array(
array(
'index' => "\n\n{{ foo.bar }}\n\n\n{{ 'foo' }}",
),
'index', 3,
),
// error occurs in an included template
array(
array(
'index' => "{% include 'partial' %}",
'partial' => '{{ foo.bar }}',
),
'partial', 1,
),
// error occurs in a parent block when called via parent()
array(
array(
'index' => "{% extends 'base' %}
{% block content %}
{{ parent() }}
{% endblock %}",
'base' => '{% block content %}{{ foo.bar }}{% endblock %}',
),
'base', 1,
),
// error occurs in a block from the child
array(
array(
'index' => "{% extends 'base' %}
{% block content %}
{{ foo.bar }}
{% endblock %}
{% block foo %}
{{ foo.bar }}
{% endblock %}",
'base' => '{% block content %}{% endblock %}',
),
'index', 3,
),
);
}
}
class Twig_Tests_ErrorTest_Foo
{
public function bar()
{
throw new Exception('Runtime error...');
}
}
Twig-1.23.1/test/Twig/Tests/ExpressionParserTest.php 0000664 0000000 0000000 00000033125 12616650102 0022373 0 ustar 00root root 0000000 0000000 getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize($template, 'index'));
}
public function getFailingTestsForAssignment()
{
return array(
array('{% set false = "foo" %}'),
array('{% set true = "foo" %}'),
array('{% set none = "foo" %}'),
array('{% set 3 = "foo" %}'),
array('{% set 1 + 2 = "foo" %}'),
array('{% set "bar" = "foo" %}'),
array('{% set %}{% endset %}'),
);
}
/**
* @dataProvider getTestsForArray
*/
public function testArrayExpression($template, $expected)
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$stream = $env->tokenize($template, 'index');
$parser = new Twig_Parser($env);
$this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
}
/**
* @expectedException Twig_Error_Syntax
* @dataProvider getFailingTestsForArray
*/
public function testArraySyntaxError($template)
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize($template, 'index'));
}
public function getFailingTestsForArray()
{
return array(
array('{{ [1, "a": "b"] }}'),
array('{{ {"a": "b", 2} }}'),
);
}
public function getTestsForArray()
{
return array(
// simple array
array('{{ [1, 2] }}', new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant(0, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Constant(2, 1),
), 1),
),
// array with trailing ,
array('{{ [1, 2, ] }}', new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant(0, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Constant(2, 1),
), 1),
),
// simple hash
array('{{ {"a": "b", "b": "c"} }}', new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant('a', 1),
new Twig_Node_Expression_Constant('b', 1),
new Twig_Node_Expression_Constant('b', 1),
new Twig_Node_Expression_Constant('c', 1),
), 1),
),
// hash with trailing ,
array('{{ {"a": "b", "b": "c", } }}', new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant('a', 1),
new Twig_Node_Expression_Constant('b', 1),
new Twig_Node_Expression_Constant('b', 1),
new Twig_Node_Expression_Constant('c', 1),
), 1),
),
// hash in an array
array('{{ [1, {"a": "b", "b": "c"}] }}', new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant(0, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant('a', 1),
new Twig_Node_Expression_Constant('b', 1),
new Twig_Node_Expression_Constant('b', 1),
new Twig_Node_Expression_Constant('c', 1),
), 1),
), 1),
),
// array in a hash
array('{{ {"a": [1, 2], "b": "c"} }}', new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant('a', 1),
new Twig_Node_Expression_Array(array(
new Twig_Node_Expression_Constant(0, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Constant(1, 1),
new Twig_Node_Expression_Constant(2, 1),
), 1),
new Twig_Node_Expression_Constant('b', 1),
new Twig_Node_Expression_Constant('c', 1),
), 1),
),
);
}
/**
* @expectedException Twig_Error_Syntax
*/
public function testStringExpressionDoesNotConcatenateTwoConsecutiveStrings()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
$stream = $env->tokenize('{{ "a" "b" }}', 'index');
$parser = new Twig_Parser($env);
$parser->parse($stream);
}
/**
* @dataProvider getTestsForString
*/
public function testStringExpression($template, $expected)
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
$stream = $env->tokenize($template, 'index');
$parser = new Twig_Parser($env);
$this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
}
public function getTestsForString()
{
return array(
array(
'{{ "foo" }}', new Twig_Node_Expression_Constant('foo', 1),
),
array(
'{{ "foo #{bar}" }}', new Twig_Node_Expression_Binary_Concat(
new Twig_Node_Expression_Constant('foo ', 1),
new Twig_Node_Expression_Name('bar', 1),
1
),
),
array(
'{{ "foo #{bar} baz" }}', new Twig_Node_Expression_Binary_Concat(
new Twig_Node_Expression_Binary_Concat(
new Twig_Node_Expression_Constant('foo ', 1),
new Twig_Node_Expression_Name('bar', 1),
1
),
new Twig_Node_Expression_Constant(' baz', 1),
1
),
),
array(
'{{ "foo #{"foo #{bar} baz"} baz" }}', new Twig_Node_Expression_Binary_Concat(
new Twig_Node_Expression_Binary_Concat(
new Twig_Node_Expression_Constant('foo ', 1),
new Twig_Node_Expression_Binary_Concat(
new Twig_Node_Expression_Binary_Concat(
new Twig_Node_Expression_Constant('foo ', 1),
new Twig_Node_Expression_Name('bar', 1),
1
),
new Twig_Node_Expression_Constant(' baz', 1),
1
),
1
),
new Twig_Node_Expression_Constant(' baz', 1),
1
),
),
);
}
/**
* @expectedException Twig_Error_Syntax
*/
public function testAttributeCallDoesNotSupportNamedArguments()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{{ foo.bar(name="Foo") }}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
*/
public function testMacroCallDoesNotSupportNamedArguments()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage An argument must be a name. Unexpected token "string" of value "a" ("name" expected) in "index" at line 1.
*/
public function testMacroDefinitionDoesNotSupportNonNameVariableName()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{% macro foo("a") %}{% endmacro %}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage A default value for an argument must be a constant (a boolean, a string, a number, or an array) in "index" at line 1
* @dataProvider getMacroDefinitionDoesNotSupportNonConstantDefaultValues
*/
public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template)
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize($template, 'index'));
}
public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues()
{
return array(
array('{% macro foo(name = "a #{foo} a") %}{% endmacro %}'),
array('{% macro foo(name = [["b", "a #{foo} a"]]) %}{% endmacro %}'),
);
}
/**
* @dataProvider getMacroDefinitionSupportsConstantDefaultValues
*/
public function testMacroDefinitionSupportsConstantDefaultValues($template)
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize($template, 'index'));
}
public function getMacroDefinitionSupportsConstantDefaultValues()
{
return array(
array('{% macro foo(name = "aa") %}{% endmacro %}'),
array('{% macro foo(name = 12) %}{% endmacro %}'),
array('{% macro foo(name = true) %}{% endmacro %}'),
array('{% macro foo(name = ["a"]) %}{% endmacro %}'),
array('{% macro foo(name = [["a"]]) %}{% endmacro %}'),
array('{% macro foo(name = {a: "a"}) %}{% endmacro %}'),
array('{% macro foo(name = {a: {b: "a"}}) %}{% endmacro %}'),
);
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unknown "cycl" function. Did you mean "cycle" in "index" at line 1?
*/
public function testUnknownFunction()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{{ cycl() }}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unknown "foobar" function in "index" at line 1.
*/
public function testUnknownFunctionWithoutSuggestions()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{{ foobar() }}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unknown "lowe" filter. Did you mean "lower" in "index" at line 1?
*/
public function testUnknownFilter()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{{ 1|lowe }}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unknown "foobar" filter in "index" at line 1.
*/
public function testUnknownFilterWithoutSuggestions()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{{ 1|foobar }}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unknown "nul" test. Did you mean "null" in "index" at line 1
*/
public function testUnknownTest()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{{ 1 is nul }}', 'index'));
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unknown "foobar" test in "index" at line 1.
*/
public function testUnknownTestWithoutSuggestions()
{
$env = new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('cache' => false, 'autoescape' => false));
$parser = new Twig_Parser($env);
$parser->parse($env->tokenize('{{ 1 is foobar }}', 'index'));
}
}
Twig-1.23.1/test/Twig/Tests/Extension/ 0000775 0000000 0000000 00000000000 12616650102 0017456 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Extension/CoreTest.php 0000664 0000000 0000000 00000012337 12616650102 0021725 0 ustar 00root root 0000000 0000000 getMock('Twig_LoaderInterface'));
for ($i = 0; $i < 100; ++$i) {
$this->assertTrue(in_array(twig_random($env, $value), $expectedInArray, true)); // assertContains() would not consider the type
}
}
public function getRandomFunctionTestData()
{
return array(
array(// array
array('apple', 'orange', 'citrus'),
array('apple', 'orange', 'citrus'),
),
array(// Traversable
new ArrayObject(array('apple', 'orange', 'citrus')),
array('apple', 'orange', 'citrus'),
),
array(// unicode string
'Ä€é',
array('Ä', '€', 'é'),
),
array(// numeric but string
'123',
array('1', '2', '3'),
),
array(// integer
5,
range(0, 5, 1),
),
array(// float
5.9,
range(0, 5, 1),
),
array(// negative
-2,
array(0, -1, -2),
),
);
}
public function testRandomFunctionWithoutParameter()
{
$max = mt_getrandmax();
for ($i = 0; $i < 100; ++$i) {
$val = twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$this->assertTrue(is_int($val) && $val >= 0 && $val <= $max);
}
}
public function testRandomFunctionReturnsAsIs()
{
$this->assertSame('', twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface')), ''));
$this->assertSame('', twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface'), array('charset' => null)), ''));
$instance = new stdClass();
$this->assertSame($instance, twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface')), $instance));
}
/**
* @expectedException Twig_Error_Runtime
*/
public function testRandomFunctionOfEmptyArrayThrowsException()
{
twig_random(new Twig_Environment($this->getMock('Twig_LoaderInterface')), array());
}
public function testRandomFunctionOnNonUTF8String()
{
if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
$this->markTestSkipped('needs iconv or mbstring');
}
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->setCharset('ISO-8859-1');
$text = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8');
for ($i = 0; $i < 30; ++$i) {
$rand = twig_random($twig, $text);
$this->assertTrue(in_array(twig_convert_encoding($rand, 'UTF-8', 'ISO-8859-1'), array('Ä', 'é'), true));
}
}
public function testReverseFilterOnNonUTF8String()
{
if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
$this->markTestSkipped('needs iconv or mbstring');
}
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->setCharset('ISO-8859-1');
$input = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8');
$output = twig_convert_encoding(twig_reverse_filter($twig, $input), 'UTF-8', 'ISO-8859-1');
$this->assertEquals($output, 'éÄ');
}
public function testCustomEscaper()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$twig->getExtension('core')->setEscaper('foo', 'foo_escaper_for_test');
$this->assertEquals('fooUTF-8', twig_escape_filter($twig, 'foo', 'foo'));
$this->assertEquals('UTF-8', twig_escape_filter($twig, null, 'foo'));
$this->assertEquals('42UTF-8', twig_escape_filter($twig, 42, 'foo'));
}
/**
* @expectedException Twig_Error_Runtime
*/
public function testUnknownCustomEscaper()
{
twig_escape_filter(new Twig_Environment($this->getMock('Twig_LoaderInterface')), 'foo', 'bar');
}
public function testTwigFirst()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$this->assertEquals('a', twig_first($twig, 'abc'));
$this->assertEquals(1, twig_first($twig, array(1, 2, 3)));
$this->assertSame('', twig_first($twig, null));
$this->assertSame('', twig_first($twig, ''));
}
public function testTwigLast()
{
$twig = new Twig_Environment($this->getMock('Twig_LoaderInterface'));
$this->assertEquals('c', twig_last($twig, 'abc'));
$this->assertEquals(3, twig_last($twig, array(1, 2, 3)));
$this->assertSame('', twig_last($twig, null));
$this->assertSame('', twig_last($twig, ''));
}
}
function foo_escaper_for_test(Twig_Environment $env, $string, $charset)
{
return $string.$charset;
}
Twig-1.23.1/test/Twig/Tests/Extension/SandboxTest.php 0000664 0000000 0000000 00000022614 12616650102 0022432 0 ustar 00root root 0000000 0000000 'Fabien',
'obj' => new FooObject(),
'arr' => array('obj' => new FooObject()),
);
self::$templates = array(
'1_basic1' => '{{ obj.foo }}',
'1_basic2' => '{{ name|upper }}',
'1_basic3' => '{% if name %}foo{% endif %}',
'1_basic4' => '{{ obj.bar }}',
'1_basic5' => '{{ obj }}',
'1_basic6' => '{{ arr.obj }}',
'1_basic7' => '{{ cycle(["foo","bar"], 1) }}',
'1_basic8' => '{{ obj.getfoobar }}{{ obj.getFooBar }}',
'1_basic9' => '{{ obj.foobar }}{{ obj.fooBar }}',
'1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
'1_layout' => '{% block content %}{% endblock %}',
'1_child' => "{% extends \"1_layout\" %}\n{% block content %}\n{{ \"a\"|json_encode }}\n{% endblock %}",
);
}
/**
* @expectedException Twig_Sandbox_SecurityError
* @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child" at line 3.
*/
public function testSandboxWithInheritance()
{
$twig = $this->getEnvironment(true, array(), self::$templates, array('block'));
$twig->loadTemplate('1_child')->render(array());
}
public function testSandboxGloballySet()
{
$twig = $this->getEnvironment(false, array(), self::$templates);
$this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('1_basic1')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
} catch (Twig_Sandbox_SecurityError $e) {
}
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('1_basic2')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
} catch (Twig_Sandbox_SecurityError $e) {
}
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('1_basic3')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
} catch (Twig_Sandbox_SecurityError $e) {
}
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('1_basic4')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
} catch (Twig_Sandbox_SecurityError $e) {
}
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('1_basic5')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
} catch (Twig_Sandbox_SecurityError $e) {
}
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('1_basic6')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
} catch (Twig_Sandbox_SecurityError $e) {
}
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('1_basic7')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
} catch (Twig_Sandbox_SecurityError $e) {
}
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
FooObject::reset();
$this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
$this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
FooObject::reset();
$this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
$this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
$twig = $this->getEnvironment(false, array(), self::$templates);
FooObject::reset();
$this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled');
$this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
$this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
$twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
$this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
$this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
$this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
$twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
FooObject::reset();
$this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way');
$this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once');
$this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic9')->render(self::$params), 'Sandbox allow methods via shortcut names (ie. without get/set)');
}
}
public function testSandboxLocallySetForAnInclude()
{
self::$templates = array(
'2_basic' => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}',
'2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
);
$twig = $this->getEnvironment(false, array(), self::$templates);
$this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
self::$templates = array(
'3_basic' => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}',
'3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
);
$twig = $this->getEnvironment(true, array(), self::$templates);
try {
$twig->loadTemplate('3_basic')->render(self::$params);
$this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
} catch (Twig_Sandbox_SecurityError $e) {
}
}
public function testMacrosInASandbox()
{
$twig = $this->getEnvironment(true, array('autoescape' => 'html'), array('index' => <<{{ text }}
{% endmacro %}
{{- macros.test('username') }}
EOF
), array('macro', 'import'), array('escape'));
$this->assertEquals('username
', $twig->loadTemplate('index')->render(array()));
}
protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array())
{
$loader = new Twig_Loader_Array($templates);
$twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
$policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
$twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
return $twig;
}
}
class FooObject
{
public static $called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
public $bar = 'bar';
public static function reset()
{
self::$called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
}
public function __toString()
{
++self::$called['__toString'];
return 'foo';
}
public function foo()
{
++self::$called['foo'];
return 'foo';
}
public function getFooBar()
{
++self::$called['getFooBar'];
return 'foobar';
}
}
Twig-1.23.1/test/Twig/Tests/FileCachingTest.php 0000664 0000000 0000000 00000004334 12616650102 0021213 0 ustar 00root root 0000000 0000000 tmpDir = sys_get_temp_dir().'/TwigTests';
if (!file_exists($this->tmpDir)) {
@mkdir($this->tmpDir, 0777, true);
}
if (!is_writable($this->tmpDir)) {
$this->markTestSkipped(sprintf('Unable to run the tests as "%s" is not writable.', $this->tmpDir));
}
$this->env = new Twig_Environment(new Twig_Loader_Array(array('index' => 'index', 'index2' => 'index2')), array('cache' => $this->tmpDir));
}
public function tearDown()
{
if ($this->fileName) {
unlink($this->fileName);
}
$this->removeDir($this->tmpDir);
}
/**
* @group legacy
*/
public function testWritingCacheFiles()
{
$name = 'index';
$this->env->loadTemplate($name);
$cacheFileName = $this->env->getCacheFilename($name);
$this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
$this->fileName = $cacheFileName;
}
/**
* @group legacy
*/
public function testClearingCacheFiles()
{
$name = 'index2';
$this->env->loadTemplate($name);
$cacheFileName = $this->env->getCacheFilename($name);
$this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
$this->env->clearCacheFiles();
$this->assertFalse(file_exists($cacheFileName), 'Cache file was not cleared.');
}
private function removeDir($target)
{
$fp = opendir($target);
while (false !== $file = readdir($fp)) {
if (in_array($file, array('.', '..'))) {
continue;
}
if (is_dir($target.'/'.$file)) {
self::removeDir($target.'/'.$file);
} else {
unlink($target.'/'.$file);
}
}
closedir($fp);
rmdir($target);
}
}
Twig-1.23.1/test/Twig/Tests/FileExtensionEscapingStrategyTest.php 0000664 0000000 0000000 00000002520 12616650102 0025023 0 ustar 00root root 0000000 0000000 assertSame($strategy, Twig_FileExtensionEscapingStrategy::guess($filename));
}
public function getGuessData()
{
return array(
// default
array('html', 'foo.html'),
array('html', 'foo.html.twig'),
array('html', 'foo'),
array('html', 'foo.bar.twig'),
array('html', 'foo.txt/foo'),
array('html', 'foo.txt/foo.js/'),
// css
array('css', 'foo.css'),
array('css', 'foo.css.twig'),
array('css', 'foo.twig.css'),
array('css', 'foo.js.css'),
array('css', 'foo.js.css.twig'),
// js
array('js', 'foo.js'),
array('js', 'foo.js.twig'),
array('js', 'foo.txt/foo.js'),
array('js', 'foo.txt.twig/foo.js'),
// txt
array(false, 'foo.txt'),
array(false, 'foo.txt.twig'),
);
}
}
Twig-1.23.1/test/Twig/Tests/Fixtures/ 0000775 0000000 0000000 00000000000 12616650102 0017313 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/autoescape/ 0000775 0000000 0000000 00000000000 12616650102 0021444 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/autoescape/filename.test 0000664 0000000 0000000 00000000531 12616650102 0024124 0 ustar 00root root 0000000 0000000 --TEST--
"filename" autoescape strategy
--TEMPLATE--
{{ br -}}
{{ include('index.html.twig') -}}
{{ include('index.txt.twig') -}}
--TEMPLATE(index.html.twig)--
{{ br -}}
--TEMPLATE(index.txt.twig)--
{{ br -}}
--DATA--
return array('br' => ' ')
--CONFIG--
return array('autoescape' => 'filename')
--EXPECT--
<br />
<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/errors/ 0000775 0000000 0000000 00000000000 12616650102 0020627 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/errors/base.html 0000664 0000000 0000000 00000000042 12616650102 0022423 0 ustar 00root root 0000000 0000000 {% block content %}{% endblock %}
Twig-1.23.1/test/Twig/Tests/Fixtures/errors/index.html 0000664 0000000 0000000 00000000200 12616650102 0022614 0 ustar 00root root 0000000 0000000 {% extends 'base.html' %}
{% block content %}
{{ foo.bar }}
{% endblock %}
{% block foo %}
{{ foo.bar }}
{% endblock %}
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/ 0000775 0000000 0000000 00000000000 12616650102 0021474 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test 0000664 0000000 0000000 00000000455 12616650102 0032522 0 ustar 00root root 0000000 0000000 --TEST--
Exception for multiline array with undefined variable
--TEMPLATE--
{% set foo = {
foo: 'foo',
bar: 'bar',
foobar: foobar,
foo2: foo2,
} %}
--DATA--
return array('foobar' => 'foobar')
--EXCEPTION--
Twig_Error_Runtime: Variable "foo2" does not exist in "index.twig" at line 11
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test 0000664 0000000 0000000 00000000432 12616650102 0033654 0 ustar 00root root 0000000 0000000 --TEST--
Exception for multiline array with undefined variable
--TEMPLATE--
{% set foo = {
foo: 'foo',
bar: 'bar',
foobar: foobar,
foo2: foo2,
} %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Runtime: Variable "foobar" does not exist in "index.twig" at line 7
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test 0000664 0000000 0000000 00000000422 12616650102 0033223 0 ustar 00root root 0000000 0000000 --TEST--
Exception for multile function with undefined variable
--TEMPLATE--
{{ include('foo',
with_context=with_context
) }}
--TEMPLATE(foo)--
Foo
--DATA--
return array()
--EXCEPTION--
Twig_Error_Runtime: Variable "with_context" does not exist in "index.twig" at line 3
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/multiline_function_with_unknown_argument.test 0000664 0000000 0000000 00000000467 12616650102 0033027 0 ustar 00root root 0000000 0000000 --TEST--
Exception for multiline function with unknown argument
--TEMPLATE--
{{ include('foo',
with_context=True,
invalid=False
) }}
--EXCEPTION--
Twig_Error_Syntax: Unknown argument "invalid" for function "include(template, variables, with_context, ignore_missing, sandboxed)" in "index.twig" at line 4.
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test 0000664 0000000 0000000 00000000364 12616650102 0032156 0 ustar 00root root 0000000 0000000 --TEST--
Exception for multiline tag with undefined variable
--TEMPLATE--
{% include 'foo'
with vars
%}
--TEMPLATE(foo)--
Foo
--DATA--
return array()
--EXCEPTION--
Twig_Error_Runtime: Variable "vars" does not exist in "index.twig" at line 3
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/syntax_error_in_reused_template.test 0000664 0000000 0000000 00000000461 12616650102 0031065 0 ustar 00root root 0000000 0000000 --TEST--
Exception for syntax error in reused template
--TEMPLATE--
{% use 'foo.twig' %}
--TEMPLATE(foo.twig)--
{% block bar %}
{% do node.data = 5 %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Unexpected token "operator" of value "=" ("end of statement block" expected) in "foo.twig" at line 3.
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test 0000664 0000000 0000000 00000000464 12616650102 0025050 0 ustar 00root root 0000000 0000000 --TEST--
Exception for an unclosed tag
--TEMPLATE--
{% block foo %}
{% if foo %}
{% for i in fo %}
{% endfor %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Unexpected "endblock" tag (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16.
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/undefined_parent.test 0000664 0000000 0000000 00000000307 12616650102 0025707 0 ustar 00root root 0000000 0000000 --TEST--
Exception for an undefined parent
--TEMPLATE--
{% extends 'foo.html' %}
{% set foo = "foo" %}
--EXCEPTION--
Twig_Error_Loader: Template "foo.html" is not defined in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/undefined_template_in_child_template.test 0000664 0000000 0000000 00000000544 12616650102 0031760 0 ustar 00root root 0000000 0000000 --TEST--
Exception for an undefined template in a child template
--TEMPLATE--
{% extends 'base.twig' %}
{% block sidebar %}
{{ include('include.twig') }}
{% endblock %}
--TEMPLATE(base.twig)--
{% block sidebar %}
{% endblock %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Loader: Template "include.twig" is not defined in "index.twig" at line 5.
Twig-1.23.1/test/Twig/Tests/Fixtures/exceptions/undefined_trait.test 0000664 0000000 0000000 00000000365 12616650102 0025545 0 ustar 00root root 0000000 0000000 --TEST--
Exception for an undefined trait
--TEMPLATE--
{% use 'foo' with foobar as bar %}
--TEMPLATE(foo)--
{% block bar %}
{% endblock %}
--EXCEPTION--
Twig_Error_Runtime: Block "foobar" is not defined in trait "foo" in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/ 0000775 0000000 0000000 00000000000 12616650102 0021675 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/array.test 0000664 0000000 0000000 00000001776 12616650102 0023727 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports array notation
--TEMPLATE--
{# empty array #}
{{ []|join(',') }}
{{ [1, 2]|join(',') }}
{{ ['foo', "bar"]|join(',') }}
{{ {0: 1, 'foo': 'bar'}|join(',') }}
{{ {0: 1, 'foo': 'bar'}|keys|join(',') }}
{{ {0: 1, foo: 'bar'}|join(',') }}
{{ {0: 1, foo: 'bar'}|keys|join(',') }}
{# nested arrays #}
{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %}
{{ a[2]|join(',') }}
{{ a[3]["foo"]|join(',') }}
{# works even if [] is used inside the array #}
{{ [foo[bar]]|join(',') }}
{# elements can be any expression #}
{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }}
{# arrays can have a trailing , like in PHP #}
{{
[
1,
2,
]|join(',')
}}
{# keys can be any expression #}
{% set a = 1 %}
{% set b = "foo" %}
{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %}
{{ ary|keys|join(',') }}
{{ ary|join(',') }}
--DATA--
return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
--EXPECT--
1,2
foo,bar
1,bar
0,foo
1,bar
0,foo
1,2
bar
bar
FOO,BAR,
1,2
1,foo,c,1foo
a,b,c,d
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/array_call.test 0000664 0000000 0000000 00000000360 12616650102 0024706 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports method calls
--TEMPLATE--
{{ items.foo }}
{{ items['foo'] }}
{{ items[foo] }}
{{ items[items[foo]] }}
--DATA--
return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo'))
--EXPECT--
bar
bar
foo
bar
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/binary.test 0000664 0000000 0000000 00000000756 12616650102 0024072 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports binary operations (+, -, *, /, ~, %, and, or)
--TEMPLATE--
{{ 1 + 1 }}
{{ 2 - 1 }}
{{ 2 * 2 }}
{{ 2 / 2 }}
{{ 3 % 2 }}
{{ 1 and 1 }}
{{ 1 and 0 }}
{{ 0 and 1 }}
{{ 0 and 0 }}
{{ 1 or 1 }}
{{ 1 or 0 }}
{{ 0 or 1 }}
{{ 0 or 0 }}
{{ 0 or 1 and 0 }}
{{ 1 or 0 and 1 }}
{{ "foo" ~ "bar" }}
{{ foo ~ "bar" }}
{{ "foo" ~ bar }}
{{ foo ~ bar }}
{{ 20 // 7 }}
--DATA--
return array('foo' => 'bar', 'bar' => 'foo')
--EXPECT--
2
1
4
1
1
1
1
1
1
1
foobar
barbar
foofoo
barfoo
2
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/bitwise.test 0000664 0000000 0000000 00000000324 12616650102 0024243 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports bitwise operations
--TEMPLATE--
{{ 1 b-and 5 }}
{{ 1 b-or 5 }}
{{ 1 b-xor 5 }}
{{ (1 and 0 b-or 0) is same as(1 and (0 b-or 0)) ? 'ok' : 'ko' }}
--DATA--
return array()
--EXPECT--
1
5
4
ok
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/comparison.test 0000664 0000000 0000000 00000000435 12616650102 0024752 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports comparison operators (==, !=, <, >, >=, <=)
--TEMPLATE--
{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }}
{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }}
{{ 1 == 1 }}/{{ 1 == 2 }}
{{ 1 != 1 }}/{{ 1 != 2 }}
--DATA--
return array()
--EXPECT--
///1
1//1/1
1/
/1
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/divisibleby.test 0000664 0000000 0000000 00000000421 12616650102 0025100 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the "divisible by" operator
--TEMPLATE--
{{ 8 is divisible by(2) ? 'OK' }}
{{ 8 is not divisible by(3) ? 'OK' }}
{{ 8 is divisible by (2) ? 'OK' }}
{{ 8 is not
divisible
by
(3) ? 'OK' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/dotdot.test 0000664 0000000 0000000 00000001040 12616650102 0024066 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the .. operator
--TEMPLATE--
{% for i in 0..10 %}{{ i }} {% endfor %}
{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}
{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}
{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}
{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %}
--DATA--
return array('foo' => array(1, 10))
--EXPECT--
0 1 2 3 4 5 6 7 8 9 10
a b c d e f g h i j k l m n o p q r s t u v w x y z
A B C D E F G H I J K L M N O P Q R S T U V W X Y Z
1 2 3 4 5 6 7 8 9 10
1 2 3 4 5 6 7 8 9
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/ends_with.test 0000664 0000000 0000000 00000001037 12616650102 0024563 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the "ends with" operator
--TEMPLATE--
{{ 'foo' ends with 'o' ? 'OK' : 'KO' }}
{{ not ('foo' ends with 'f') ? 'OK' : 'KO' }}
{{ not ('foo' ends with 'foowaytoolong') ? 'OK' : 'KO' }}
{{ 'foo' ends with '' ? 'OK' : 'KO' }}
{{ '1' ends with true ? 'OK' : 'KO' }}
{{ 1 ends with true ? 'OK' : 'KO' }}
{{ 0 ends with false ? 'OK' : 'KO' }}
{{ '' ends with false ? 'OK' : 'KO' }}
{{ false ends with false ? 'OK' : 'KO' }}
{{ false ends with '' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
KO
KO
KO
KO
KO
KO
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/grouping.test 0000664 0000000 0000000 00000000163 12616650102 0024430 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports grouping of expressions
--TEMPLATE--
{{ (2 + 2) / 2 }}
--DATA--
return array()
--EXPECT--
2
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/literals.test 0000664 0000000 0000000 00000000324 12616650102 0024414 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports literals
--TEMPLATE--
1 {{ true }}
2 {{ TRUE }}
3 {{ false }}
4 {{ FALSE }}
5 {{ none }}
6 {{ NONE }}
7 {{ null }}
8 {{ NULL }}
--DATA--
return array()
--EXPECT--
1 1
2 1
3
4
5
6
7
8
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/magic_call.test 0000664 0000000 0000000 00000000701 12616650102 0024647 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports __call() for attributes
--TEMPLATE--
{{ foo.foo }}
{{ foo.bar }}
--DATA--
class TestClassForMagicCallAttributes
{
public function getBar()
{
return 'bar_from_getbar';
}
public function __call($method, $arguments)
{
if ('foo' === $method)
{
return 'foo_from_call';
}
return false;
}
}
return array('foo' => new TestClassForMagicCallAttributes())
--EXPECT--
foo_from_call
bar_from_getbar
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/matches.test 0000664 0000000 0000000 00000000342 12616650102 0024221 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the "matches" operator
--TEMPLATE--
{{ 'foo' matches '/o/' ? 'OK' : 'KO' }}
{{ 'foo' matches '/^fo/' ? 'OK' : 'KO' }}
{{ 'foo' matches '/O/i' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/method_call.test 0000664 0000000 0000000 00000000744 12616650102 0025056 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports method calls
--TEMPLATE--
{{ items.foo.foo }}
{{ items.foo.getFoo() }}
{{ items.foo.bar }}
{{ items.foo['bar'] }}
{{ items.foo.bar('a', 43) }}
{{ items.foo.bar(foo) }}
{{ items.foo.self.foo() }}
{{ items.foo.is }}
{{ items.foo.in }}
{{ items.foo.not }}
--DATA--
return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo'))
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
foo
foo
bar
bar_a-43
bar_bar
foo
is
in
not
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/negative_numbers.test 0000664 0000000 0000000 00000000263 12616650102 0026134 0 ustar 00root root 0000000 0000000 --TEST--
Twig manages negative numbers correctly
--TEMPLATE--
{{ -1 }}
{{ - 1 }}
{{ 5 - 1 }}
{{ 5-1 }}
{{ 5 + -1 }}
{{ 5 + - 1 }}
--DATA--
return array()
--EXPECT--
-1
-1
4
4
4
4
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/operators_as_variables.test 0000664 0000000 0000000 00000000374 12616650102 0027333 0 ustar 00root root 0000000 0000000 --TEST--
Twig allows to use named operators as variable names
--TEMPLATE--
{% for match in matches %}
{{- match }}
{% endfor %}
{{ in }}
{{ is }}
--DATA--
return array('matches' => array(1, 2, 3), 'in' => 'in', 'is' => 'is')
--EXPECT--
1
2
3
in
is
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/postfix.test 0000664 0000000 0000000 00000000420 12616650102 0024266 0 ustar 00root root 0000000 0000000 --TEST--
Twig parses postfix expressions
--TEMPLATE--
{% import _self as macros %}
{% macro foo() %}foo{% endmacro %}
{{ 'a' }}
{{ 'a'|upper }}
{{ ('a')|upper }}
{{ -1|upper }}
{{ macros.foo() }}
{{ (macros).foo() }}
--DATA--
return array();
--EXPECT--
a
A
A
-1
foo
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/sameas.test 0000664 0000000 0000000 00000000510 12616650102 0024043 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the "same as" operator
--TEMPLATE--
{{ 1 is same as(1) ? 'OK' }}
{{ 1 is not same as(true) ? 'OK' }}
{{ 1 is same as(1) ? 'OK' }}
{{ 1 is not same as(true) ? 'OK' }}
{{ 1 is same as (1) ? 'OK' }}
{{ 1 is not
same
as
(true) ? 'OK' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
OK
OK
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/starts_with.test 0000664 0000000 0000000 00000001076 12616650102 0025155 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the "starts with" operator
--TEMPLATE--
{{ 'foo' starts with 'f' ? 'OK' : 'KO' }}
{{ not ('foo' starts with 'oo') ? 'OK' : 'KO' }}
{{ not ('foo' starts with 'foowaytoolong') ? 'OK' : 'KO' }}
{{ 'foo' starts with 'f' ? 'OK' : 'KO' }}
{{ 'foo' starts
with 'f' ? 'OK' : 'KO' }}
{{ 'foo' starts with '' ? 'OK' : 'KO' }}
{{ '1' starts with true ? 'OK' : 'KO' }}
{{ '' starts with false ? 'OK' : 'KO' }}
{{ 'a' starts with false ? 'OK' : 'KO' }}
{{ false starts with '' ? 'OK' : 'KO' }}
--DATA--
return array()
--EXPECT--
OK
OK
OK
OK
OK
OK
KO
KO
KO
KO
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/strings.test 0000664 0000000 0000000 00000000317 12616650102 0024270 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports string interpolation
--TEMPLATE--
{{ "foo #{"foo #{bar} baz"} baz" }}
{{ "foo #{bar}#{bar} baz" }}
--DATA--
return array('bar' => 'BAR');
--EXPECT--
foo foo BAR baz baz
foo BARBAR baz
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/ternary_operator.test 0000664 0000000 0000000 00000000533 12616650102 0026176 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 1 ? 'YES' : 'NO' }}
{{ 0 ? 'YES' : 'NO' }}
{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }}
{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }}
{{ 1 == 1 ? 'foo ':'' }}
{{ foo ~ (bar ? ('-' ~ bar) : '') }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--EXPECT--
YES
NO
YES1
NO1
foo
foo-bar
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test 0000664 0000000 0000000 00000000201 12616650102 0027533 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 1 ? 'YES' }}
{{ 0 ? 'YES' }}
--DATA--
return array()
--EXPECT--
YES
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test 0000664 0000000 0000000 00000000207 12616650102 0027547 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the ternary operator
--TEMPLATE--
{{ 'YES' ?: 'NO' }}
{{ 0 ?: 'NO' }}
--DATA--
return array()
--EXPECT--
YES
NO
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/two_word_operators_as_variables.test 0000664 0000000 0000000 00000000365 12616650102 0031257 0 ustar 00root root 0000000 0000000 --TEST--
Twig does not allow to use two-word named operators as variable names
--TEMPLATE--
{{ starts with }}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected token "operator" of value "starts with" in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/unary.test 0000664 0000000 0000000 00000000270 12616650102 0023733 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports unary operators (not, -, +)
--TEMPLATE--
{{ not 1 }}/{{ not 0 }}
{{ +1 + 1 }}/{{ -1 - 1 }}
{{ not (false or true) }}
--DATA--
return array()
--EXPECT--
/1
2/-2
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/unary_macro_arguments.test 0000664 0000000 0000000 00000001143 12616650102 0027201 0 ustar 00root root 0000000 0000000 --TEST--
Twig manages negative numbers as default parameters
--TEMPLATE--
{% import _self as macros %}
{{ macros.negative_number1() }}
{{ macros.negative_number2() }}
{{ macros.negative_number3() }}
{{ macros.positive_number1() }}
{{ macros.positive_number2() }}
{% macro negative_number1(nb=-1) %}{{ nb }}{% endmacro %}
{% macro negative_number2(nb = --1) %}{{ nb }}{% endmacro %}
{% macro negative_number3(nb = - 1) %}{{ nb }}{% endmacro %}
{% macro positive_number1(nb = +1) %}{{ nb }}{% endmacro %}
{% macro positive_number2(nb = ++1) %}{{ nb }}{% endmacro %}
--DATA--
return array()
--EXPECT--
-1
1
-1
1
1
Twig-1.23.1/test/Twig/Tests/Fixtures/expressions/unary_precedence.test 0000664 0000000 0000000 00000000236 12616650102 0026112 0 ustar 00root root 0000000 0000000 --TEST--
Twig unary operators precedence
--TEMPLATE--
{{ -1 - 1 }}
{{ -1 - -1 }}
{{ -1 * -1 }}
{{ 4 / -1 * 5 }}
--DATA--
return array()
--EXPECT--
-2
0
1
-20
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/ 0000775 0000000 0000000 00000000000 12616650102 0020763 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/filters/abs.test 0000664 0000000 0000000 00000000614 12616650102 0022432 0 ustar 00root root 0000000 0000000 --TEST--
"abs" filter
--TEMPLATE--
{{ (-5.5)|abs }}
{{ (-5)|abs }}
{{ (-0)|abs }}
{{ 0|abs }}
{{ 5|abs }}
{{ 5.5|abs }}
{{ number1|abs }}
{{ number2|abs }}
{{ number3|abs }}
{{ number4|abs }}
{{ number5|abs }}
{{ number6|abs }}
--DATA--
return array('number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5)
--EXPECT--
5.5
5
0
0
5
5.5
5.5
5
0
0
5
5.5
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/batch.test 0000664 0000000 0000000 00000001300 12616650102 0022737 0 ustar 00root root 0000000 0000000 --TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3) %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/batch_float.test 0000664 0000000 0000000 00000001245 12616650102 0024134 0 ustar 00root root 0000000 0000000 --TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3.1) %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test 0000664 0000000 0000000 00000001075 12616650102 0026227 0 ustar 00root root 0000000 0000000 --TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3, '') %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/batch_with_exact_elements.test 0000664 0000000 0000000 00000001416 12616650102 0027062 0 ustar 00root root 0000000 0000000 --TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3, 'fill') %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l'))
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/batch_with_fill.test 0000664 0000000 0000000 00000001111 12616650102 0025000 0 ustar 00root root 0000000 0000000 --TEST--
"batch" filter
--TEMPLATE--
{% for row in items|batch(3, 'fill') %}
{% for column in row %}
{{ column }}
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
--EXPECT--
a
b
c
d
e
f
g
h
i
j
fill
fill
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/batch_with_keys.test 0000664 0000000 0000000 00000000401 12616650102 0025026 0 ustar 00root root 0000000 0000000 --TEST--
"batch" filter preserves array keys
--TEMPLATE--
{{ {'foo': 'bar', 'key': 'value'}|batch(4)|first|keys|join(',') }}
{{ {'foo': 'bar', 'key': 'value'}|batch(4, 'fill')|first|keys|join(',') }}
--DATA--
return array()
--EXPECT--
foo,key
foo,key,0,1
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/batch_with_zero_elements.test 0000664 0000000 0000000 00000000231 12616650102 0026727 0 ustar 00root root 0000000 0000000 --TEST--
"batch" filter with zero elements
--TEMPLATE--
{{ []|batch(3)|length }}
{{ []|batch(3, 'fill')|length }}
--DATA--
return array()
--EXPECT--
0
0
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/convert_encoding.test 0000664 0000000 0000000 00000000460 12616650102 0025212 0 ustar 00root root 0000000 0000000 --TEST--
"convert_encoding" filter
--CONDITION--
function_exists('iconv') || function_exists('mb_convert_encoding')
--TEMPLATE--
{{ "愛していますか?"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}
--DATA--
return array()
--EXPECT--
愛していますか?
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/date.test 0000664 0000000 0000000 00000004547 12616650102 0022613 0 ustar 00root root 0000000 0000000 --TEST--
"date" filter
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }}
{{ date1|date('e') }}
{{ date1|date('d/m/Y H:i:s') }}
{{ date2|date }}
{{ date2|date('d/m/Y') }}
{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date2|date('d/m/Y H:i:s', timezone1) }}
{{ date2|date('d/m/Y H:i:s') }}
{{ date3|date }}
{{ date3|date('d/m/Y') }}
{{ date4|date }}
{{ date4|date('d/m/Y') }}
{{ date5|date }}
{{ date5|date('d/m/Y') }}
{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }}
{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date6|date('d/m/Y H:i:s P', false) }}
{{ date6|date('e', 'Europe/Paris') }}
{{ date6|date('e', false) }}
{{ date7|date }}
{{ date7|date(timezone='Europe/Paris') }}
{{ date7|date(timezone='Asia/Hong_Kong') }}
{{ date7|date(timezone=false) }}
{{ date7|date(timezone='Indian/Mauritius') }}
{{ '2010-01-28 15:00:00'|date(timezone="Europe/Paris") }}
{{ '2010-01-28 15:00:00'|date(timezone="Asia/Hong_Kong") }}
--DATA--
date_default_timezone_set('Europe/Paris');
return array(
'date1' => mktime(13, 45, 0, 10, 4, 2010),
'date2' => new DateTime('2010-10-04 13:45'),
'date3' => '2010-10-04 13:45',
'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
'date6' => new DateTime('2010-10-04 13:45', new DateTimeZone('America/New_York')),
'date7' => '2010-01-28T15:00:00+04:00',
'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 19:45:00 +08:00
04/10/2010 06:45:00 -05:00
Europe/Paris
04/10/2010 13:45:00
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 07:45:00
04/10/2010 13:45:00
October 4, 2010 13:45
04/10/2010
October 4, 2010 15:45
04/10/2010
January 2, 1964 04:04
02/01/1964
04/10/2010 19:45:00 +02:00
05/10/2010 01:45:00 +08:00
04/10/2010 13:45:00 -04:00
Europe/Paris
America/New_York
January 28, 2010 12:00
January 28, 2010 12:00
January 28, 2010 19:00
January 28, 2010 15:00
January 28, 2010 15:00
January 28, 2010 15:00
January 28, 2010 22:00
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/date_default_format.test 0000664 0000000 0000000 00000000443 12616650102 0025656 0 ustar 00root root 0000000 0000000 --TEST--
"date" filter
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
--DATA--
date_default_timezone_set('UTC');
$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
return array(
'date1' => mktime(13, 45, 0, 10, 4, 2010),
)
--EXPECT--
2010-10-04
04/10/2010
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test 0000664 0000000 0000000 00000000572 12616650102 0027565 0 ustar 00root root 0000000 0000000 --TEST--
"date" filter (interval support as of PHP 5.3)
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
{{ date2|date }}
{{ date2|date('%d days') }}
--DATA--
date_default_timezone_set('UTC');
$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
return array(
'date2' => new DateInterval('P2D'),
)
--EXPECT--
2 days 0 hours
2 days
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/date_immutable.test 0000664 0000000 0000000 00000002024 12616650102 0024636 0 ustar 00root root 0000000 0000000 --TEST--
"date" filter
--CONDITION--
version_compare(phpversion(), '5.5.0', '>=')
--TEMPLATE--
{{ date1|date }}
{{ date1|date('d/m/Y') }}
{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
{{ date1|date('d/m/Y H:i:s', timezone1) }}
{{ date1|date('d/m/Y H:i:s') }}
{{ date1|date_modify('+1 hour')|date('d/m/Y H:i:s') }}
{{ date2|date('d/m/Y H:i:s P', 'Europe/Paris') }}
{{ date2|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
{{ date2|date('d/m/Y H:i:s P', false) }}
{{ date2|date('e', 'Europe/Paris') }}
{{ date2|date('e', false) }}
--DATA--
date_default_timezone_set('Europe/Paris');
return array(
'date1' => new DateTimeImmutable('2010-10-04 13:45'),
'date2' => new DateTimeImmutable('2010-10-04 13:45', new DateTimeZone('America/New_York')),
'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
October 4, 2010 13:45
04/10/2010
04/10/2010 19:45:00
04/10/2010 07:45:00
04/10/2010 13:45:00
04/10/2010 14:45:00
04/10/2010 19:45:00 +02:00
05/10/2010 01:45:00 +08:00
04/10/2010 13:45:00 -04:00
Europe/Paris
America/New_York
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/date_interval.test 0000664 0000000 0000000 00000000757 12616650102 0024516 0 ustar 00root root 0000000 0000000 --TEST--
"date" filter (interval support as of PHP 5.3)
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
{{ date1|date }}
{{ date1|date('%d days %h hours') }}
{{ date1|date('%d days %h hours', timezone1) }}
--DATA--
date_default_timezone_set('UTC');
return array(
'date1' => new DateInterval('P2D'),
// This should have no effect on DateInterval formatting
'timezone1' => new DateTimeZone('America/New_York'),
)
--EXPECT--
2 days
2 days 0 hours
2 days 0 hours
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/date_modify.test 0000664 0000000 0000000 00000000527 12616650102 0024154 0 ustar 00root root 0000000 0000000 --TEST--
"date_modify" filter
--TEMPLATE--
{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }}
{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }}
--DATA--
date_default_timezone_set('UTC');
return array(
'date1' => '2010-10-04 13:45',
'date2' => new DateTime('2010-10-04 13:45'),
)
--EXPECT--
2010-10-03 13:45:00
2010-10-03 13:45:00
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/date_namedargs.test 0000664 0000000 0000000 00000000647 12616650102 0024631 0 ustar 00root root 0000000 0000000 --TEST--
"date" filter
--TEMPLATE--
{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }}
{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }}
{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }}
--DATA--
date_default_timezone_set('UTC');
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
--EXPECT--
04/10/2010 08:45:00 -05:00
04/10/2010 08:45:00 -05:00
04/10/2010 08:45:00 -05:00
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/default.test 0000664 0000000 0000000 00000007663 12616650102 0023324 0 ustar 00root root 0000000 0000000 --TEST--
"default" filter
--TEMPLATE--
Variable:
{{ definedVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nullVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }}
Array access:
{{ nested.definedVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested['definedVar'] |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested.zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested.emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.nullVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested['undefinedVar'] |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ undefinedVar.foo |default('default') is same as('default') ? 'ok' : 'ko' }}
Plain values:
{{ 'defined' |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ 0 |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ '' |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ null |default('default') is same as('default') ? 'ok' : 'ko' }}
Precedence:
{{ 'o' ~ nullVar |default('k') }}
{{ 'o' ~ nested.nullVar |default('k') }}
Object methods:
{{ object.foo |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.getFoo() |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.getFoo('a') |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.undefinedMethod() |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.undefinedMethod('a') |default('default') is same as('default') ? 'ok' : 'ko' }}
Deep nested:
{{ nested.undefinedVar.foo.bar |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ nested.definedArray.0 |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ nested['definedArray'][0] |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.self.foo |default('default') is same as('default') ? 'ko' : 'ok' }}
{{ object.self.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }}
{{ object.undefinedMethod.self |default('default') is same as('default') ? 'ok' : 'ko' }}
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
)
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
Variable:
ok
ok
ok
ok
ok
Array access:
ok
ok
ok
ok
ok
ok
ok
ok
Plain values:
ok
ok
ok
ok
Precedence:
ok
ok
Object methods:
ok
ok
ok
ok
ok
ok
Deep nested:
ok
ok
ok
ok
ok
ok
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'emptyVar' => '',
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
)
--CONFIG--
return array('strict_variables' => true)
--EXPECT--
Variable:
ok
ok
ok
ok
ok
Array access:
ok
ok
ok
ok
ok
ok
ok
ok
Plain values:
ok
ok
ok
ok
Precedence:
ok
ok
Object methods:
ok
ok
ok
ok
ok
ok
Deep nested:
ok
ok
ok
ok
ok
ok
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/dynamic_filter.test 0000664 0000000 0000000 00000000205 12616650102 0024652 0 ustar 00root root 0000000 0000000 --TEST--
dynamic filter
--TEMPLATE--
{{ 'bar'|foo_path }}
{{ 'bar'|a_foo_b_bar }}
--DATA--
return array()
--EXPECT--
foo/bar
a/b/bar
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/escape.test 0000664 0000000 0000000 00000000157 12616650102 0023127 0 ustar 00root root 0000000 0000000 --TEST--
"escape" filter
--TEMPLATE--
{{ "foo "|e }}
--DATA--
return array()
--EXPECT--
foo <br />
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/escape_html_attr.test 0000664 0000000 0000000 00000000314 12616650102 0025200 0 ustar 00root root 0000000 0000000 --TEST--
"escape" filter does not escape with the html strategy when using the html_attr strategy
--TEMPLATE--
{{ ' '|escape('html_attr') }}
--DATA--
return array()
--EXPECT--
<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test 0000664 0000000 0000000 00000000231 12616650102 0027610 0 ustar 00root root 0000000 0000000 --TEST--
"escape" filter
--TEMPLATE--
{{ "愛していますか? "|e }}
--DATA--
return array()
--EXPECT--
愛していますか? <br />
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/first.test 0000664 0000000 0000000 00000000404 12616650102 0023011 0 ustar 00root root 0000000 0000000 --TEST--
"first" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|first }}
{{ {a: 1, b: 2, c: 3, d: 4}|first }}
{{ '1234'|first }}
{{ arr|first }}
{{ 'Ä€é'|first }}
{{ ''|first }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
1
1
1
1
Ä
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/force_escape.test 0000664 0000000 0000000 00000000442 12616650102 0024302 0 ustar 00root root 0000000 0000000 --TEST--
"escape" filter
--TEMPLATE--
{% set foo %}
foo
{% endset %}
{{ foo|e('html') -}}
{{ foo|e('js') }}
{% autoescape true %}
{{ foo }}
{% endautoescape %}
--DATA--
return array()
--EXPECT--
foo<br />
\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/format.test 0000664 0000000 0000000 00000000216 12616650102 0023153 0 ustar 00root root 0000000 0000000 --TEST--
"format" filter
--TEMPLATE--
{{ string|format(foo, 3) }}
--DATA--
return array('string' => '%s/%d', 'foo' => 'bar')
--EXPECT--
bar/3
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/join.test 0000664 0000000 0000000 00000000345 12616650102 0022625 0 ustar 00root root 0000000 0000000 --TEST--
"join" filter
--TEMPLATE--
{{ ["foo", "bar"]|join(', ') }}
{{ foo|join(', ') }}
{{ bar|join(', ') }}
--DATA--
return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4)))
--EXPECT--
foo, bar
1, 2
3, 4
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/json_encode.test 0000664 0000000 0000000 00000000351 12616650102 0024151 0 ustar 00root root 0000000 0000000 --TEST--
"json_encode" filter
--TEMPLATE--
{{ "foo"|json_encode|raw }}
{{ foo|json_encode|raw }}
{{ [foo, "foo"]|json_encode|raw }}
--DATA--
return array('foo' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
"foo"
"foo"
["foo","foo"]
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/last.test 0000664 0000000 0000000 00000000375 12616650102 0022634 0 ustar 00root root 0000000 0000000 --TEST--
"last" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|last }}
{{ {a: 1, b: 2, c: 3, d: 4}|last }}
{{ '1234'|last }}
{{ arr|last }}
{{ 'Ä€é'|last }}
{{ ''|last }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
4
4
4
4
é
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/length.test 0000664 0000000 0000000 00000000410 12616650102 0023140 0 ustar 00root root 0000000 0000000 --TEST--
"length" filter
--TEMPLATE--
{{ array|length }}
{{ string|length }}
{{ number|length }}
{{ markup|length }}
--DATA--
return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
2
3
4
3
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/length_utf8.test 0000664 0000000 0000000 00000000342 12616650102 0024112 0 ustar 00root root 0000000 0000000 --TEST--
"length" filter
--CONDITION--
function_exists('mb_get_info')
--TEMPLATE--
{{ string|length }}
{{ markup|length }}
--DATA--
return array('string' => 'été', 'markup' => new Twig_Markup('foo', 'UTF-8'))
--EXPECT--
3
3
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/merge.test 0000664 0000000 0000000 00000001041 12616650102 0022757 0 ustar 00root root 0000000 0000000 --TEST--
"merge" filter
--TEMPLATE--
{{ items|merge({'bar': 'foo'})|join }}
{{ items|merge({'bar': 'foo'})|keys|join }}
{{ {'bar': 'foo'}|merge(items)|join }}
{{ {'bar': 'foo'}|merge(items)|keys|join }}
{{ numerics|merge([4, 5, 6])|join }}
{{ traversable.a|merge(traversable.b)|join }}
--DATA--
return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3), 'traversable' => array('a' => new ArrayObject(array(0 => 1, 1 => 2, 2 => 3)), 'b' => new ArrayObject(array('a' => 'b'))))
--EXPECT--
barfoo
foobar
foobar
barfoo
123456
123b
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/nl2br.test 0000664 0000000 0000000 00000000576 12616650102 0022713 0 ustar 00root root 0000000 0000000 --TEST--
"nl2br" filter
--TEMPLATE--
{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }}
{{ text|nl2br }}
--DATA--
return array('text' => "If you have some HTML \nit will be escaped.")
--EXPECT--
I like Twig.
You will like it too.
Everybody like it!
If you have some <strong>HTML</strong>
it will be escaped.
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/number_format.test 0000664 0000000 0000000 00000000462 12616650102 0024526 0 ustar 00root root 0000000 0000000 --TEST--
"number_format" filter
--TEMPLATE--
{{ 20|number_format }}
{{ 20.25|number_format }}
{{ 20.25|number_format(2) }}
{{ 20.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',', '.') }}
--DATA--
return array();
--EXPECT--
20
20
20.25
20,25
1,020,25
1.020,25
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/number_format_default.test 0000664 0000000 0000000 00000000646 12616650102 0026236 0 ustar 00root root 0000000 0000000 --TEST--
"number_format" filter with defaults.
--TEMPLATE--
{{ 20|number_format }}
{{ 20.25|number_format }}
{{ 20.25|number_format(1) }}
{{ 20.25|number_format(2, ',') }}
{{ 1020.25|number_format }}
{{ 1020.25|number_format(2, ',') }}
{{ 1020.25|number_format(2, ',', '.') }}
--DATA--
$twig->getExtension('core')->setNumberFormat(2, '!', '=');
return array();
--EXPECT--
20!00
20!25
20!3
20,25
1=020!25
1=020,25
1.020,25
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/replace.test 0000664 0000000 0000000 00000000707 12616650102 0023303 0 ustar 00root root 0000000 0000000 --TEST--
"replace" filter
--TEMPLATE--
{{ "I liké %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }}
{{ 'I like single replace operation only %that%'|replace({'%that%' : '%that%1'}) }}
{{ 'I like %this% and %that%.'|replace(traversable) }}
--DATA--
return array('traversable' => new ArrayObject(array('%this%' => 'foo', '%that%' => 'bar')))
--EXPECT--
I liké foo and bar.
I like single replace operation only %that%1
I like foo and bar.
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/replace_invalid_arg.test 0000664 0000000 0000000 00000000471 12616650102 0025640 0 ustar 00root root 0000000 0000000 --TEST--
Exception for invalid argument type in replace call
--TEMPLATE--
{{ 'test %foo%'|replace(stdClass) }}
--DATA--
return array('stdClass' => new stdClass())
--EXCEPTION--
Twig_Error_Runtime: The "replace" filter expects an array or "Traversable" as replace values, got "stdClass" in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/reverse.test 0000664 0000000 0000000 00000000664 12616650102 0023345 0 ustar 00root root 0000000 0000000 --TEST--
"reverse" filter
--TEMPLATE--
{{ [1, 2, 3, 4]|reverse|join('') }}
{{ '1234évènement'|reverse }}
{{ arr|reverse|join('') }}
{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }}
{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }}
{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }}
--DATA--
return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
--EXPECT--
4321
tnemenèvé4321
4321
a,c
a,c
a,c
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/round.test 0000664 0000000 0000000 00000000406 12616650102 0023013 0 ustar 00root root 0000000 0000000 --TEST--
"round" filter
--TEMPLATE--
{{ 2.7|round }}
{{ 2.1|round }}
{{ 2.1234|round(3, 'floor') }}
{{ 2.1|round(0, 'ceil') }}
{{ 21.3|round(-1)}}
{{ 21.3|round(-1, 'ceil')}}
{{ 21.3|round(-1, 'floor')}}
--DATA--
return array()
--EXPECT--
3
2
2.123
3
20
30
20
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/slice.test 0000664 0000000 0000000 00000001754 12616650102 0022772 0 ustar 00root root 0000000 0000000 --TEST--
"slice" filter
--TEMPLATE--
{{ [1, 2, 3, 4][1:2]|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }}
{{ [1, 2, 3, 4][start:length]|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2)|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }}
{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }}
{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }}
{{ '1234'|slice(1, 2) }}
{{ '1234'[1:2] }}
{{ arr|slice(1, 2)|join('') }}
{{ arr[1:2]|join('') }}
{{ arr[4:1]|join('') }}
{{ arr[3:2]|join('') }}
{{ [1, 2, 3, 4]|slice(1)|join('') }}
{{ [1, 2, 3, 4][1:]|join('') }}
{{ '1234'|slice(1) }}
{{ '1234'[1:] }}
{{ '1234'[:1] }}
{{ arr|slice(3)|join('') }}
{{ arr[2:]|join('') }}
{{ xml|slice(1)|join('')}}
--DATA--
return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4)), 'xml' => new SimpleXMLElement('- 1
- 2
'))
--EXPECT--
23
23
23
23
01
12
23
bc
23
23
23
23
4
234
234
234
234
1
4
34
2
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/sort.test 0000664 0000000 0000000 00000000430 12616650102 0022650 0 ustar 00root root 0000000 0000000 --TEST--
"sort" filter
--TEMPLATE--
{{ array1|sort|join }}
{{ array2|sort|join }}
{{ traversable|sort|join }}
--DATA--
return array('array1' => array(4, 1), 'array2' => array('foo', 'bar'), 'traversable' => new ArrayObject(array(0 => 3, 1 => 2, 2 => 1)))
--EXPECT--
14
barfoo
123
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/special_chars.test 0000664 0000000 0000000 00000000143 12616650102 0024462 0 ustar 00root root 0000000 0000000 --TEST--
"§" custom filter
--TEMPLATE--
{{ 'foo'|§ }}
--DATA--
return array()
--EXPECT--
§foo§
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/split.test 0000664 0000000 0000000 00000000747 12616650102 0023027 0 ustar 00root root 0000000 0000000 --TEST--
"split" filter
--TEMPLATE--
{{ "one,two,three,four,five"|split(',')|join('-') }}
{{ foo|split(',')|join('-') }}
{{ foo|split(',', 3)|join('-') }}
{{ baz|split('')|join('-') }}
{{ baz|split('', 1)|join('-') }}
{{ baz|split('', 2)|join('-') }}
{{ foo|split(',', -2)|join('-') }}
--DATA--
return array('foo' => "one,two,three,four,five", 'baz' => '12345',)
--EXPECT--
one-two-three-four-five
one-two-three-four-five
one-two-three,four,five
1-2-3-4-5
1-2-3-4-5
12-34-5
one-two-three Twig-1.23.1/test/Twig/Tests/Fixtures/filters/split_utf8.test 0000664 0000000 0000000 00000001010 12616650102 0023755 0 ustar 00root root 0000000 0000000 --TEST--
"split" filter
--CONDITION--
function_exists('mb_get_info')
--TEMPLATE--
{{ "é"|split('', 10)|join('-') }}
{{ foo|split(',')|join('-') }}
{{ foo|split(',', 1)|join('-') }}
{{ foo|split(',', 2)|join('-') }}
{{ foo|split(',', 3)|join('-') }}
{{ baz|split('')|join('-') }}
{{ baz|split('', 1)|join('-') }}
{{ baz|split('', 2)|join('-') }}
--DATA--
return array('foo' => 'Ä,é,Äほ', 'baz' => 'éÄßごa',)
--EXPECT--
é
Ä-é-Äほ
Ä,é,Äほ
Ä-é,Äほ
Ä-é-Äほ
é-Ä-ß-ご-a
é-Ä-ß-ご-a
éÄ-ßご-a Twig-1.23.1/test/Twig/Tests/Fixtures/filters/trim.test 0000664 0000000 0000000 00000000462 12616650102 0022641 0 ustar 00root root 0000000 0000000 --TEST--
"trim" filter
--TEMPLATE--
{{ " I like Twig. "|trim }}
{{ text|trim }}
{{ " foo/"|trim("/") }}
--DATA--
return array('text' => " If you have some HTML it will be escaped. ")
--EXPECT--
I like Twig.
If you have some <strong>HTML</strong> it will be escaped.
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/urlencode.test 0000664 0000000 0000000 00000001034 12616650102 0023642 0 ustar 00root root 0000000 0000000 --TEST--
"url_encode" filter
--CONDITION--
defined('PHP_QUERY_RFC3986')
--TEMPLATE--
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
{{ {}|url_encode|default("default") }}
{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
--DATA--
return array()
--EXPECT--
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
default
sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
Twig-1.23.1/test/Twig/Tests/Fixtures/filters/urlencode_deprecated.test 0000664 0000000 0000000 00000001063 12616650102 0026024 0 ustar 00root root 0000000 0000000 --TEST--
"url_encode" filter for PHP < 5.4 and HHVM
--CONDITION--
defined('PHP_QUERY_RFC3986')
--TEMPLATE--
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
{{ {}|url_encode|default("default") }}
{{ 'spéßi%le%c0d@dspa ce'|url_encode }}
--DATA--
return array()
--EXPECT--
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce=
default
sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/ 0000775 0000000 0000000 00000000000 12616650102 0021323 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/functions/attribute.test 0000664 0000000 0000000 00000000772 12616650102 0024235 0 ustar 00root root 0000000 0000000 --TEST--
"attribute" function
--TEMPLATE--
{{ attribute(obj, method) }}
{{ attribute(array, item) }}
{{ attribute(obj, "bar", ["a", "b"]) }}
{{ attribute(obj, "bar", arguments) }}
{{ attribute(obj, method) is defined ? 'ok' : 'ko' }}
{{ attribute(obj, nonmethod) is defined ? 'ok' : 'ko' }}
--DATA--
return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo', 'nonmethod' => 'xxx', 'arguments' => array('a', 'b'))
--EXPECT--
foo
bar
bar_a-b
bar_a-b
ok
ko
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/block.test 0000664 0000000 0000000 00000000372 12616650102 0023320 0 ustar 00root root 0000000 0000000 --TEST--
"block" function
--TEMPLATE--
{% extends 'base.twig' %}
{% block bar %}BAR{% endblock %}
--TEMPLATE(base.twig)--
{% block foo %}{{ block('bar') }}{% endblock %}
{% block bar %}BAR_BASE{% endblock %}
--DATA--
return array()
--EXPECT--
BARBAR
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/constant.test 0000664 0000000 0000000 00000000364 12616650102 0024060 0 ustar 00root root 0000000 0000000 --TEST--
"constant" function
--TEMPLATE--
{{ constant('DATE_W3C') == expect ? 'true' : 'false' }}
{{ constant('ARRAY_AS_PROPS', object) }}
--DATA--
return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi')));
--EXPECT--
true
2
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/cycle.test 0000664 0000000 0000000 00000000466 12616650102 0023331 0 ustar 00root root 0000000 0000000 --TEST--
"cycle" function
--TEMPLATE--
{% for i in 0..6 %}
{{ cycle(array1, i) }}-{{ cycle(array2, i) }}
{% endfor %}
--DATA--
return array('array1' => array('odd', 'even'), 'array2' => array('apple', 'orange', 'citrus'))
--EXPECT--
odd-apple
even-orange
odd-citrus
even-apple
odd-orange
even-citrus
odd-apple
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/date.test 0000664 0000000 0000000 00000001577 12616650102 0023153 0 ustar 00root root 0000000 0000000 --TEST--
"date" function
--TEMPLATE--
{{ date() == date('now') ? 'OK' : 'KO' }}
{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }}
--DATA--
date_default_timezone_set('UTC');
return array(
'date1' => mktime(13, 45, 0, 10, 4, 2010),
'date2' => new DateTime('2010-10-04 13:45'),
'date3' => '2010-10-04 13:45',
'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
)
--EXPECT--
OK
OK
OK
OK
OK
OK
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/date_namedargs.test 0000664 0000000 0000000 00000000533 12616650102 0025163 0 ustar 00root root 0000000 0000000 --TEST--
"date" function
--TEMPLATE--
{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }}
{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }}
--DATA--
date_default_timezone_set('UTC');
return array('date' => mktime(13, 45, 0, 10, 4, 2010))
--EXPECT--
04/10/2010 09:45:00 -04:00
04/10/2010 09:45:00 -04:00
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/dump.test 0000664 0000000 0000000 00000000456 12616650102 0023176 0 ustar 00root root 0000000 0000000 --TEST--
"dump" function
--CONDITION--
!extension_loaded('xdebug')
--TEMPLATE--
{{ dump('foo') }}
{{ dump('foo', 'bar') }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--CONFIG--
return array('debug' => true, 'autoescape' => false);
--EXPECT--
string(3) "foo"
string(3) "foo"
string(3) "bar"
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/dump_array.test 0000664 0000000 0000000 00000000742 12616650102 0024372 0 ustar 00root root 0000000 0000000 --TEST--
"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded
--CONDITION--
!extension_loaded('xdebug') || (($r = new ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<'))
--TEMPLATE--
{{ dump() }}
--DATA--
return array('foo' => 'foo', 'bar' => 'bar')
--CONFIG--
return array('debug' => true, 'autoescape' => false);
--EXPECT--
array(3) {
["foo"]=>
string(3) "foo"
["bar"]=>
string(3) "bar"
["global"]=>
string(6) "global"
}
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/dynamic_function.test 0000664 0000000 0000000 00000000211 12616650102 0025547 0 ustar 00root root 0000000 0000000 --TEST--
dynamic function
--TEMPLATE--
{{ foo_path('bar') }}
{{ a_foo_b_bar('bar') }}
--DATA--
return array()
--EXPECT--
foo/bar
a/b/bar
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/ 0000775 0000000 0000000 00000000000 12616650102 0022746 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/assignment.test 0000664 0000000 0000000 00000000255 12616650102 0026021 0 ustar 00root root 0000000 0000000 --TEST--
"include" function
--TEMPLATE--
{% set tmp = include("foo.twig") %}
FOO{{ tmp }}BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO
FOOBARBAR
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/autoescaping.test 0000664 0000000 0000000 00000000257 12616650102 0026335 0 ustar 00root root 0000000 0000000 --TEST--
"include" function is safe for auto-escaping
--TEMPLATE--
{{ include("foo.twig") }}
--TEMPLATE(foo.twig)--
Test
--DATA--
return array()
--EXPECT--
Test
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/basic.test 0000664 0000000 0000000 00000000236 12616650102 0024731 0 ustar 00root root 0000000 0000000 --TEST--
"include" function
--TEMPLATE--
FOO
{{ include("foo.twig") }}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO
FOOBAR
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/expression.test 0000664 0000000 0000000 00000000331 12616650102 0026043 0 ustar 00root root 0000000 0000000 --TEST--
"include" function allows expressions for the template to include
--TEMPLATE--
FOO
{{ include(foo) }}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO
FOOBAR
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test 0000664 0000000 0000000 00000000523 12616650102 0026663 0 ustar 00root root 0000000 0000000 --TEST--
"include" function
--TEMPLATE--
{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }}
{{ include("foo.twig", ignore_missing = true) }}
{{ include("foo.twig", ignore_missing = true, variables = {}) }}
{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/missing.test 0000664 0000000 0000000 00000000273 12616650102 0025322 0 ustar 00root root 0000000 0000000 --TEST--
"include" function
--TEMPLATE--
{{ include("foo.twig") }}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/missing_nested.test 0000664 0000000 0000000 00000000512 12616650102 0026660 0 ustar 00root root 0000000 0000000 --TEST--
"include" function
--TEMPLATE--
{% extends "base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}
{{ include("foo.twig") }}
{% endblock %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/sandbox.test 0000664 0000000 0000000 00000000420 12616650102 0025301 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag sandboxed
--TEMPLATE--
{{ include("foo.twig", sandboxed = true) }}
--TEMPLATE(foo.twig)--
{{ foo|e }}
{{ foo|e }}
--DATA--
return array()
--EXCEPTION--
Twig_Sandbox_SecurityNotAllowedFilterError: Filter "e" is not allowed in "foo.twig" at line 4.
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/sandbox_disabling.test 0000664 0000000 0000000 00000000377 12616650102 0027330 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag sandboxed
--TEMPLATE--
{{ include("foo.twig", sandboxed = true) }}
{{ include("bar.twig") }}
--TEMPLATE(foo.twig)--
foo
--TEMPLATE(bar.twig)--
{{ foo|e }}
--DATA--
return array('foo' => 'bar ')
--EXPECT--
foo
bar<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/sandbox_disabling_ignore_missing.test 0000664 0000000 0000000 00000000373 12616650102 0032420 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag sandboxed
--TEMPLATE--
{{ include("unknown.twig", sandboxed = true, ignore_missing = true) }}
{{ include("bar.twig") }}
--TEMPLATE(bar.twig)--
{{ foo|e }}
--DATA--
return array('foo' => 'bar ')
--EXPECT--
bar<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/template_instance.test 0000664 0000000 0000000 00000000315 12616650102 0027345 0 ustar 00root root 0000000 0000000 --TEST--
"include" function accepts Twig_Template instance
--TEMPLATE--
{{ include(foo) }} FOO
--TEMPLATE(foo.twig)--
BAR
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BAR FOO
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test 0000664 0000000 0000000 00000000300 12616650102 0027517 0 ustar 00root root 0000000 0000000 --TEST--
"include" function
--TEMPLATE--
{{ include(["foo.twig", "bar.twig"]) }}
{{- include(["bar.twig", "foo.twig"]) }}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/with_context.test 0000664 0000000 0000000 00000000715 12616650102 0026371 0 ustar 00root root 0000000 0000000 --TEST--
"include" function accept variables and with_context
--TEMPLATE--
{{ include("foo.twig") }}
{{- include("foo.twig", with_context = false) }}
{{- include("foo.twig", {'foo1': 'bar'}) }}
{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }}
--TEMPLATE(foo.twig)--
{% for k, v in _context %}{{ k }},{% endfor %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
foo,global,_parent,
global,_parent,
foo,global,foo1,_parent,
foo1,global,_parent,
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/include/with_variables.test 0000664 0000000 0000000 00000000360 12616650102 0026651 0 ustar 00root root 0000000 0000000 --TEST--
"include" function accept variables
--TEMPLATE--
{{ include("foo.twig", {'foo': 'bar'}) }}
{{- include("foo.twig", vars) }}
--TEMPLATE(foo.twig)--
{{ foo }}
--DATA--
return array('vars' => array('foo' => 'bar'))
--EXPECT--
bar
bar
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/max.test 0000664 0000000 0000000 00000000300 12616650102 0023002 0 ustar 00root root 0000000 0000000 --TEST--
"max" function
--TEMPLATE--
{{ max([2, 1, 3, 5, 4]) }}
{{ max(2, 1, 3, 5, 4) }}
{{ max({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
--DATA--
return array()
--EXPECT--
5
5
two
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/min.test 0000664 0000000 0000000 00000000301 12616650102 0023001 0 ustar 00root root 0000000 0000000 --TEST--
"min" function
--TEMPLATE--
{{ min(2, 1, 3, 5, 4) }}
{{ min([2, 1, 3, 5, 4]) }}
{{ min({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }}
--DATA--
return array()
--EXPECT--
1
1
five
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/range.test 0000664 0000000 0000000 00000000206 12616650102 0023316 0 ustar 00root root 0000000 0000000 --TEST--
"range" function
--TEMPLATE--
{{ range(low=0+1, high=10+0, step=2)|join(',') }}
--DATA--
return array()
--EXPECT--
1,3,5,7,9
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/recursive_block_with_inheritance.test 0000664 0000000 0000000 00000002223 12616650102 0031010 0 ustar 00root root 0000000 0000000 --TEST--
"block" function recursively called in a parent template
--TEMPLATE--
{% extends "ordered_menu.twig" %}
{% block label %}"{{ parent() }}"{% endblock %}
{% block list %}{% set class = 'b' %}{{ parent() }}{% endblock %}
--TEMPLATE(ordered_menu.twig)--
{% extends "menu.twig" %}
{% block list %}{% set class = class|default('a') %}{{ block('children') }} {% endblock %}
--TEMPLATE(menu.twig)--
{% extends "base.twig" %}
{% block list %}{% endblock %}
{% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %}
{% block item %}{% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %} {% endblock %}
{% block label %}{{ item }}{{ block('unknown') }}{% endblock %}
--TEMPLATE(base.twig)--
{{ block('list') }}
--DATA--
return array('item' => array('1', '2', array('3.1', array('3.2.1', '3.2.2'), '3.4')))
--EXPECT--
"1" "2" "3.1" "3.2.1" "3.2.2" "3.4"
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/source.test 0000664 0000000 0000000 00000000256 12616650102 0023527 0 ustar 00root root 0000000 0000000 --TEST--
"source" function
--TEMPLATE--
FOO
{{ source("foo.twig") }}
BAR
--TEMPLATE(foo.twig)--
{{ foo }}
--DATA--
return array()
--EXPECT--
FOO
{{ foo }}
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/special_chars.test 0000664 0000000 0000000 00000000146 12616650102 0025025 0 ustar 00root root 0000000 0000000 --TEST--
"§" custom function
--TEMPLATE--
{{ §('foo') }}
--DATA--
return array()
--EXPECT--
§foo§
Twig-1.23.1/test/Twig/Tests/Fixtures/functions/template_from_string.test 0000664 0000000 0000000 00000000710 12616650102 0026446 0 ustar 00root root 0000000 0000000 --TEST--
"template_from_string" function
--TEMPLATE--
{% include template_from_string(template) %}
{% include template_from_string("Hello {{ name }}") %}
{% include template_from_string('{% extends "parent.twig" %}{% block content %}Hello {{ name }}{% endblock %}') %}
--TEMPLATE(parent.twig)--
{% block content %}{% endblock %}
--DATA--
return array('name' => 'Fabien', 'template' => "Hello {{ name }}")
--EXPECT--
Hello Fabien
Hello Fabien
Hello Fabien
Twig-1.23.1/test/Twig/Tests/Fixtures/macros/ 0000775 0000000 0000000 00000000000 12616650102 0020577 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/macros/default_values.test 0000664 0000000 0000000 00000000327 12616650102 0024505 0 ustar 00root root 0000000 0000000 --TEST--
macro
--TEMPLATE--
{% from _self import test %}
{% macro test(a, b = 'bar') -%}
{{ a }}{{ b }}
{%- endmacro %}
{{ test('foo') }}
{{ test('bar', 'foo') }}
--DATA--
return array();
--EXPECT--
foobar
barfoo
Twig-1.23.1/test/Twig/Tests/Fixtures/macros/nested_calls.test 0000664 0000000 0000000 00000000346 12616650102 0024143 0 ustar 00root root 0000000 0000000 --TEST--
macro
--TEMPLATE--
{% import _self as macros %}
{% macro foo(data) %}
{{ data }}
{% endmacro %}
{% macro bar() %}
{% endmacro %}
{{ macros.foo(macros.bar()) }}
--DATA--
return array();
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/macros/reserved_variables.test 0000664 0000000 0000000 00000000272 12616650102 0025350 0 ustar 00root root 0000000 0000000 --TEST--
macro
--TEMPLATE--
{% from _self import test %}
{% macro test(this) -%}
{{ this }}
{%- endmacro %}
{{ test(this) }}
--DATA--
return array('this' => 'foo');
--EXPECT--
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/macros/simple.test 0000664 0000000 0000000 00000000547 12616650102 0022777 0 ustar 00root root 0000000 0000000 --TEST--
macro
--TEMPLATE--
{% import _self as test %}
{% from _self import test %}
{% macro test(a, b) -%}
{{ a|default('a') }}
{{- b|default('b') }}
{%- endmacro %}
{{ test.test() }}
{{ test() }}
{{ test.test(1, "c") }}
{{ test(1, "c") }}
--DATA--
return array();
--EXPECT--
a b
a b
1 c
1 c
Twig-1.23.1/test/Twig/Tests/Fixtures/macros/varargs.test 0000664 0000000 0000000 00000000574 12616650102 0023153 0 ustar 00root root 0000000 0000000 --TEST--
macro with arbitrary arguments
--TEMPLATE--
{% from _self import test1, test2 %}
{% macro test1(var) %}
{{- var }}: {{ varargs|join(", ") }}
{% endmacro %}
{% macro test2() %}
{{- varargs|join(", ") }}
{% endmacro %}
{{ test1("foo", "bar", "foobar") }}
{{ test2("foo", "bar", "foobar") }}
--DATA--
return array();
--EXPECT--
foo: bar, foobar
foo, bar, foobar
Twig-1.23.1/test/Twig/Tests/Fixtures/macros/varargs_argument.test 0000664 0000000 0000000 00000000424 12616650102 0025047 0 ustar 00root root 0000000 0000000 --TEST--
macro with varargs argument
--TEMPLATE--
{% macro test(varargs) %}
{% endmacro %}
--EXCEPTION--
Twig_Error_Syntax: The argument "varargs" in macro "test" cannot be defined because the variable "varargs" is reserved for arbitrary arguments in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/macros/with_filters.test 0000664 0000000 0000000 00000000337 12616650102 0024206 0 ustar 00root root 0000000 0000000 --TEST--
macro with a filter
--TEMPLATE--
{% import _self as test %}
{% macro test() %}
{% filter escape %}foo {% endfilter %}
{% endmacro %}
{{ test.test() }}
--DATA--
return array();
--EXPECT--
foo<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/regression/ 0000775 0000000 0000000 00000000000 12616650102 0021473 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/regression/combined_debug_info.test 0000664 0000000 0000000 00000000514 12616650102 0026335 0 ustar 00root root 0000000 0000000 --TEST--
Exception with bad line number
--TEMPLATE--
{% block content %}
{{ foo }}
{{ include("foo") }}
{% endblock %}
index
--TEMPLATE(foo)--
foo
{{ foo.bar }}
--DATA--
return array('foo' => 'foo');
--EXCEPTION--
Twig_Error_Runtime: Impossible to access an attribute ("bar") on a string variable ("foo") in "foo" at line 3
Twig-1.23.1/test/Twig/Tests/Fixtures/regression/empty_token.test 0000664 0000000 0000000 00000000202 12616650102 0024724 0 ustar 00root root 0000000 0000000 --TEST--
Twig outputs 0 nodes correctly
--TEMPLATE--
{{ foo }}0{{ foo }}
--DATA--
return array('foo' => 'foo')
--EXPECT--
foo0foo
Twig-1.23.1/test/Twig/Tests/Fixtures/regression/issue_1143.test 0000664 0000000 0000000 00000000762 12616650102 0024201 0 ustar 00root root 0000000 0000000 --TEST--
error in twig extension
--TEMPLATE--
{{ object.region is not null ? object.regionChoices[object.region] }}
--DATA--
class House
{
const REGION_S = 1;
const REGION_P = 2;
public static $regionChoices = array(self::REGION_S => 'house.region.s', self::REGION_P => 'house.region.p');
public function getRegionChoices()
{
return self::$regionChoices;
}
}
$object = new House();
$object->region = 1;
return array('object' => $object)
--EXPECT--
house.region.s
Twig-1.23.1/test/Twig/Tests/Fixtures/regression/multi_word_tests.test 0000664 0000000 0000000 00000000317 12616650102 0026004 0 ustar 00root root 0000000 0000000 --TEST--
Twig allows multi-word tests without a custom node class
--TEMPLATE--
{{ 'foo' is multi word ? 'yes' : 'no' }}
{{ 'foo bar' is multi word ? 'yes' : 'no' }}
--DATA--
return array()
--EXPECT--
no
yes
Twig-1.23.1/test/Twig/Tests/Fixtures/regression/simple_xml_element.test 0000664 0000000 0000000 00000001037 12616650102 0026257 0 ustar 00root root 0000000 0000000 --TEST--
Twig is able to deal with SimpleXMLElement instances as variables
--CONDITION--
version_compare(phpversion(), '5.3.0', '>=')
--TEMPLATE--
Hello '{{ images.image.0.group }}'!
{{ images.image.0.group.attributes.myattr }}
{{ images.children().image.count() }}
{% for image in images %}
- {{ image.group }}
{% endfor %}
--DATA--
return array('images' => new SimpleXMLElement('foo bar '))
--EXPECT--
Hello 'foo'!
example
2
- foo
- bar
Twig-1.23.1/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test 0000664 0000000 0000000 00000000260 12616650102 0026622 0 ustar 00root root 0000000 0000000 --TEST--
Twig does not confuse strings with integers in getAttribute()
--TEMPLATE--
{{ hash['2e2'] }}
--DATA--
return array('hash' => array('2e2' => 'works'))
--EXPECT--
works
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/ 0000775 0000000 0000000 00000000000 12616650102 0020251 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/ 0000775 0000000 0000000 00000000000 12616650102 0022402 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/basic.test 0000664 0000000 0000000 00000000760 12616650102 0024367 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag applies escaping on its children
--TEMPLATE--
{% autoescape %}
{{ var }}
{% endautoescape %}
{% autoescape 'html' %}
{{ var }}
{% endautoescape %}
{% autoescape false %}
{{ var }}
{% endautoescape %}
{% autoescape true %}
{{ var }}
{% endautoescape %}
{% autoescape false %}
{{ var }}
{% endautoescape %}
--DATA--
return array('var' => ' ')
--EXPECT--
<br />
<br />
<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test 0000664 0000000 0000000 00000000351 12616650102 0024557 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag applies escaping on embedded blocks
--TEMPLATE--
{% autoescape 'html' %}
{% block foo %}
{{ var }}
{% endblock %}
{% endautoescape %}
--DATA--
return array('var' => ' ')
--EXPECT--
<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test 0000664 0000000 0000000 00000000274 12616650102 0026431 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag does not double-escape
--TEMPLATE--
{% autoescape 'html' %}
{{ var|escape }}
{% endautoescape %}
--DATA--
return array('var' => ' ')
--EXPECT--
<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/functions.test 0000664 0000000 0000000 00000001542 12616650102 0025315 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag applies escaping after calling functions
--TEMPLATE--
autoescape false
{% autoescape false %}
safe_br
{{ safe_br() }}
unsafe_br
{{ unsafe_br() }}
{% endautoescape %}
autoescape 'html'
{% autoescape 'html' %}
safe_br
{{ safe_br() }}
unsafe_br
{{ unsafe_br() }}
unsafe_br()|raw
{{ (unsafe_br())|raw }}
safe_br()|escape
{{ (safe_br())|escape }}
safe_br()|raw
{{ (safe_br())|raw }}
unsafe_br()|escape
{{ (unsafe_br())|escape }}
{% endautoescape %}
autoescape js
{% autoescape 'js' %}
safe_br
{{ safe_br() }}
{% endautoescape %}
--DATA--
return array()
--EXPECT--
autoescape false
safe_br
unsafe_br
autoescape 'html'
safe_br
unsafe_br
<br />
unsafe_br()|raw
safe_br()|escape
<br />
safe_br()|raw
unsafe_br()|escape
<br />
autoescape js
safe_br
\x3Cbr\x20\x2F\x3E
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/literal.test 0000664 0000000 0000000 00000001630 12616650102 0024737 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag does not apply escaping on literals
--TEMPLATE--
{% autoescape 'html' %}
1. Simple literal
{{ " " }}
2. Conditional expression with only literals
{{ true ? " " : " " }}
3. Conditional expression with a variable
{{ true ? " " : someVar }}
4. Nested conditionals with only literals
{{ true ? (true ? " " : " ") : "\n" }}
5. Nested conditionals with a variable
{{ true ? (true ? " " : someVar) : "\n" }}
6. Nested conditionals with a variable marked safe
{{ true ? (true ? " " : someVar|raw) : "\n" }}
{% endautoescape %}
--DATA--
return array()
--EXPECT--
1. Simple literal
2. Conditional expression with only literals
3. Conditional expression with a variable
<br />
4. Nested conditionals with only literals
5. Nested conditionals with a variable
<br />
6. Nested conditionals with a variable marked safe
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/nested.test 0000664 0000000 0000000 00000000706 12616650102 0024570 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tags can be nested at will
--TEMPLATE--
{{ var }}
{% autoescape 'html' %}
{{ var }}
{% autoescape false %}
{{ var }}
{% autoescape 'html' %}
{{ var }}
{% endautoescape %}
{{ var }}
{% endautoescape %}
{{ var }}
{% endautoescape %}
{{ var }}
--DATA--
return array('var' => ' ')
--EXPECT--
<br />
<br />
<br />
<br />
<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/objects.test 0000664 0000000 0000000 00000000720 12616650102 0024733 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag applies escaping to object method calls
--TEMPLATE--
{% autoescape 'html' %}
{{ user.name }}
{{ user.name|lower }}
{{ user }}
{% endautoescape %}
--DATA--
class UserForAutoEscapeTest
{
public function getName()
{
return 'Fabien ';
}
public function __toString()
{
return 'Fabien ';
}
}
return array('user' => new UserForAutoEscapeTest())
--EXPECT--
Fabien<br />
fabien<br />
Fabien<br />
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/raw.test 0000664 0000000 0000000 00000000311 12616650102 0024067 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag does not escape when raw is used as a filter
--TEMPLATE--
{% autoescape 'html' %}
{{ var|raw }}
{% endautoescape %}
--DATA--
return array('var' => ' ')
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/strategy.legacy.test 0000664 0000000 0000000 00000000420 12616650102 0026404 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag accepts an escaping strategy
--TEMPLATE--
{% autoescape true js %}{{ var }}{% endautoescape %}
{% autoescape true html %}{{ var }}{% endautoescape %}
--DATA--
return array('var' => ' "')
--EXPECT--
\x3Cbr\x20\x2F\x3E\x22
<br />"
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test 0000664 0000000 0000000 00000000412 12616650102 0025142 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag accepts an escaping strategy
--TEMPLATE--
{% autoescape 'js' %}{{ var }}{% endautoescape %}
{% autoescape 'html' %}{{ var }}{% endautoescape %}
--DATA--
return array('var' => ' "')
--EXPECT--
\x3Cbr\x20\x2F\x3E\x22
<br />"
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/type.test 0000664 0000000 0000000 00000002710 12616650102 0024264 0 ustar 00root root 0000000 0000000 --TEST--
escape types
--TEMPLATE--
1. autoescape 'html' |escape('js')
{% autoescape 'html' %}
{% endautoescape %}
2. autoescape 'html' |escape('js')
{% autoescape 'html' %}
{% endautoescape %}
3. autoescape 'js' |escape('js')
{% autoescape 'js' %}
{% endautoescape %}
4. no escape
{% autoescape false %}
{% endautoescape %}
5. |escape('js')|escape('html')
{% autoescape false %}
{% endautoescape %}
6. autoescape 'html' |escape('js')|escape('html')
{% autoescape 'html' %}
{% endautoescape %}
--DATA--
return array('msg' => "<>\n'\"")
--EXPECT--
1. autoescape 'html' |escape('js')
2. autoescape 'html' |escape('js')
3. autoescape 'js' |escape('js')
4. no escape
5. |escape('js')|escape('html')
6. autoescape 'html' |escape('js')|escape('html')
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test 0000664 0000000 0000000 00000007525 12616650102 0026017 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag applies escaping after calling filters
--TEMPLATE--
{% autoescape 'html' %}
(escape_and_nl2br is an escaper filter)
1. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped )
{{ var|escape_and_nl2br }}
2. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped, |raw is redundant )
{{ var|escape_and_nl2br|raw }}
3. Explicit escape
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is explicitly escaped by |escape )
{{ var|escape_and_nl2br|escape }}
4. Escape non-escaper filter output
( var is upper-cased by |upper,
the output is auto-escaped )
{{ var|upper }}
5. Escape if last filter is not an escaper
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is upper-cased by |upper,
the output is auto-escaped as |upper is not an escaper )
{{ var|escape_and_nl2br|upper }}
6. Don't escape escaper filter output
( var is upper cased by upper,
the output is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped as |escape_and_nl2br is an escaper )
{{ var|upper|escape_and_nl2br }}
7. Escape if last filter is not an escaper
( the output of |format is "" ~ var ~ " ",
the output is auto-escaped )
{{ "%s "|format(var) }}
8. Escape if last filter is not an escaper
( the output of |format is "" ~ var ~ " ",
|raw is redundant,
the output is auto-escaped )
{{ "%s "|raw|format(var) }}
9. Don't escape escaper filter output
( the output of |format is "" ~ var ~ " ",
the output is not escaped due to |raw filter at the end )
{{ "%s "|format(var)|raw }}
10. Don't escape escaper filter output
( the output of |format is "" ~ var ~ " ",
the output is not escaped due to |raw filter at the end,
the |raw filter on var is redundant )
{{ "%s "|format(var|raw)|raw }}
{% endautoescape %}
--DATA--
return array('var' => "\nTwig")
--EXPECT--
(escape_and_nl2br is an escaper filter)
1. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped )
<Fabien>
Twig
2. Don't escape escaper filter output
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped, |raw is redundant )
<Fabien>
Twig
3. Explicit escape
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is explicitly escaped by |escape )
<Fabien><br />
Twig
4. Escape non-escaper filter output
( var is upper-cased by |upper,
the output is auto-escaped )
<FABIEN>
TWIG
5. Escape if last filter is not an escaper
( var is escaped by |escape_and_nl2br, line-breaks are added,
the output is upper-cased by |upper,
the output is auto-escaped as |upper is not an escaper )
<FABIEN><BR />
TWIG
6. Don't escape escaper filter output
( var is upper cased by upper,
the output is escaped by |escape_and_nl2br, line-breaks are added,
the output is not escaped as |escape_and_nl2br is an escaper )
<FABIEN>
TWIG
7. Escape if last filter is not an escaper
( the output of |format is "" ~ var ~ " ",
the output is auto-escaped )
<b><Fabien>
Twig</b>
8. Escape if last filter is not an escaper
( the output of |format is "" ~ var ~ " ",
|raw is redundant,
the output is auto-escaped )
<b><Fabien>
Twig</b>
9. Don't escape escaper filter output
( the output of |format is "" ~ var ~ " ",
the output is not escaped due to |raw filter at the end )
Twig
10. Don't escape escaper filter output
( the output of |format is "" ~ var ~ " ",
the output is not escaped due to |raw filter at the end,
the |raw filter on var is redundant )
Twig
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test 0000664 0000000 0000000 00000000741 12616650102 0030075 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag do not applies escaping on filter arguments
--TEMPLATE--
{% autoescape 'html' %}
{{ var|nl2br(" ") }}
{{ var|nl2br(" "|escape) }}
{{ var|nl2br(sep) }}
{{ var|nl2br(sep|raw) }}
{{ var|nl2br(sep|escape) }}
{% endautoescape %}
--DATA--
return array('var' => "\nTwig", 'sep' => ' ')
--EXPECT--
<Fabien>
Twig
<Fabien><br />
Twig
<Fabien>
Twig
<Fabien>
Twig
<Fabien><br />
Twig
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test 0000664 0000000 0000000 00000002651 12616650102 0030200 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters
--TEMPLATE--
{% autoescape 'html' %}
(nl2br is pre_escaped for "html" and declared safe for "html")
1. Pre-escape and don't post-escape
( var|escape|nl2br )
{{ var|nl2br }}
2. Don't double-pre-escape
( var|escape|nl2br )
{{ var|escape|nl2br }}
3. Don't escape safe values
( var|raw|nl2br )
{{ var|raw|nl2br }}
4. Don't escape safe values
( var|escape|nl2br|nl2br )
{{ var|nl2br|nl2br }}
5. Re-escape values that are escaped for an other contexts
( var|escape_something|escape|nl2br )
{{ var|escape_something|nl2br }}
6. Still escape when using filters not declared safe
( var|escape|nl2br|upper|escape )
{{ var|nl2br|upper }}
{% endautoescape %}
--DATA--
return array('var' => "\nTwig")
--EXPECT--
(nl2br is pre_escaped for "html" and declared safe for "html")
1. Pre-escape and don't post-escape
( var|escape|nl2br )
<Fabien>
Twig
2. Don't double-pre-escape
( var|escape|nl2br )
<Fabien>
Twig
3. Don't escape safe values
( var|raw|nl2br )
Twig
4. Don't escape safe values
( var|escape|nl2br|nl2br )
<Fabien>
Twig
5. Re-escape values that are escaped for an other contexts
( var|escape_something|escape|nl2br )
<FABIEN>
TWIG
6. Still escape when using filters not declared safe
( var|escape|nl2br|upper|escape )
<FABIEN><BR />
TWIG
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test 0000664 0000000 0000000 00000002364 12616650102 0031464 0 ustar 00root root 0000000 0000000 --TEST--
"autoescape" tag handles filters preserving the safety
--TEMPLATE--
{% autoescape 'html' %}
(preserves_safety is preserving safety for "html")
1. Unsafe values are still unsafe
( var|preserves_safety|escape )
{{ var|preserves_safety }}
2. Safe values are still safe
( var|escape|preserves_safety )
{{ var|escape|preserves_safety }}
3. Re-escape values that are escaped for an other contexts
( var|escape_something|preserves_safety|escape )
{{ var|escape_something|preserves_safety }}
4. Still escape when using filters not declared safe
( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }}
{% endautoescape %}
--DATA--
return array('var' => "\nTwig")
--EXPECT--
(preserves_safety is preserving safety for "html")
1. Unsafe values are still unsafe
( var|preserves_safety|escape )
<FABIEN>
TWIG
2. Safe values are still safe
( var|escape|preserves_safety )
<FABIEN>
TWIG
3. Re-escape values that are escaped for an other contexts
( var|escape_something|preserves_safety|escape )
<FABIEN>
TWIG
4. Still escape when using filters not declared safe
( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
<FABPOT>
TWIG
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/block/ 0000775 0000000 0000000 00000000000 12616650102 0021343 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/block/basic.test 0000664 0000000 0000000 00000000324 12616650102 0023324 0 ustar 00root root 0000000 0000000 --TEST--
"block" tag
--TEMPLATE--
{% block title1 %}FOO{% endblock %}
{% block title2 foo|lower %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
FOObar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test 0000664 0000000 0000000 00000000370 12616650102 0025724 0 ustar 00root root 0000000 0000000 --TEST--
"block" tag
--TEMPLATE--
{% block content %}
{% block content %}
{% endblock %}
{% endblock %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/block/special_chars.test 0000664 0000000 0000000 00000000203 12616650102 0025037 0 ustar 00root root 0000000 0000000 --TEST--
"§" special chars in a block name
--TEMPLATE--
{% block § %}
§
{% endblock § %}
--DATA--
return array()
--EXPECT--
§
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/embed/ 0000775 0000000 0000000 00000000000 12616650102 0021325 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/embed/basic.test 0000664 0000000 0000000 00000000600 12616650102 0023303 0 ustar 00root root 0000000 0000000 --TEST--
"embed" tag
--TEMPLATE--
FOO
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
BAR
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
FOO
A
block1
block1extended
B
block2
C
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/embed/error_line.test 0000664 0000000 0000000 00000000502 12616650102 0024363 0 ustar 00root root 0000000 0000000 --TEST--
"embed" tag
--TEMPLATE(index.twig)--
FOO
{% embed "foo.twig" %}
{% block c1 %}
{{ nothing }}
{% endblock %}
{% endembed %}
BAR
--TEMPLATE(foo.twig)--
{% block c1 %}{% endblock %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/embed/multiple.test 0000664 0000000 0000000 00000001074 12616650102 0024063 0 ustar 00root root 0000000 0000000 --TEST--
"embed" tag
--TEMPLATE--
FOO
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
BAR
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
FOO
A
block1
block1extended
B
block2
C
A
block1
block1extended
B
block2
C
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/embed/nested.test 0000664 0000000 0000000 00000001114 12616650102 0023505 0 ustar 00root root 0000000 0000000 --TEST--
"embed" tag
--TEMPLATE--
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
{% endblock %}
{% endembed %}
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
A
block1
A
block1
block1extended
B
block2
C
B
block2
C
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/embed/with_extends.test 0000664 0000000 0000000 00000001353 12616650102 0024735 0 ustar 00root root 0000000 0000000 --TEST--
"embed" tag
--TEMPLATE--
{% extends "base.twig" %}
{% block c1 %}
{{ parent() }}
blockc1baseextended
{% endblock %}
{% block c2 %}
{{ parent() }}
{% embed "foo.twig" %}
{% block c1 %}
{{ parent() }}
block1extended
{% endblock %}
{% endembed %}
{% endblock %}
--TEMPLATE(base.twig)--
A
{% block c1 %}
blockc1base
{% endblock %}
{% block c2 %}
blockc2base
{% endblock %}
B
--TEMPLATE(foo.twig)--
A
{% block c1 %}
block1
{% endblock %}
B
{% block c2 %}
block2
{% endblock %}
C
--DATA--
return array()
--EXPECT--
A
blockc1base
blockc1baseextended
blockc2base
A
block1
block1extended
B
block2
CB Twig-1.23.1/test/Twig/Tests/Fixtures/tags/filter/ 0000775 0000000 0000000 00000000000 12616650102 0021536 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/filter/basic.test 0000664 0000000 0000000 00000000310 12616650102 0023512 0 ustar 00root root 0000000 0000000 --TEST--
"filter" tag applies a filter on its children
--TEMPLATE--
{% filter upper %}
Some text with a {{ var }}
{% endfilter %}
--DATA--
return array('var' => 'var')
--EXPECT--
SOME TEXT WITH A VAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/filter/json_encode.test 0000664 0000000 0000000 00000000236 12616650102 0024726 0 ustar 00root root 0000000 0000000 --TEST--
"filter" tag applies a filter on its children
--TEMPLATE--
{% filter json_encode|raw %}test{% endfilter %}
--DATA--
return array()
--EXPECT--
"test"
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/filter/multiple.test 0000664 0000000 0000000 00000000262 12616650102 0024272 0 ustar 00root root 0000000 0000000 --TEST--
"filter" tags accept multiple chained filters
--TEMPLATE--
{% filter lower|title %}
{{ var }}
{% endfilter %}
--DATA--
return array('var' => 'VAR')
--EXPECT--
Var
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/filter/nested.test 0000664 0000000 0000000 00000000371 12616650102 0023722 0 ustar 00root root 0000000 0000000 --TEST--
"filter" tags can be nested at will
--TEMPLATE--
{% filter lower|title %}
{{ var }}
{% filter upper %}
{{ var }}
{% endfilter %}
{{ var }}
{% endfilter %}
--DATA--
return array('var' => 'var')
--EXPECT--
Var
Var
Var
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test 0000664 0000000 0000000 00000000330 12616650102 0025107 0 ustar 00root root 0000000 0000000 --TEST--
"filter" tag applies the filter on "for" tags
--TEMPLATE--
{% filter upper %}
{% for item in items %}
{{ item }}
{% endfor %}
{% endfilter %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
A
B
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test 0000664 0000000 0000000 00000000567 12616650102 0024733 0 ustar 00root root 0000000 0000000 --TEST--
"filter" tag applies the filter on "if" tags
--TEMPLATE--
{% filter upper %}
{% if items %}
{{ items|join(', ') }}
{% endif %}
{% if items.3 is defined %}
FOO
{% else %}
{{ items.1 }}
{% endif %}
{% if items.3 is defined %}
FOO
{% elseif items.1 %}
{{ items.0 }}
{% endif %}
{% endfilter %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
A, B
B
A
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/ 0000775 0000000 0000000 00000000000 12616650102 0021037 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/condition.test 0000664 0000000 0000000 00000000415 12616650102 0023726 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag takes a condition
--TEMPLATE--
{% for i in 1..5 if i is odd -%}
{{ loop.index }}.{{ i }}{{ foo.bar }}
{% endfor %}
--DATA--
return array('foo' => array('bar' => 'X'))
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
1.1X
2.3X
3.5X
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/context.test 0000664 0000000 0000000 00000000432 12616650102 0023423 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag keeps the context safe
--TEMPLATE--
{% for item in items %}
{% for item in items %}
* {{ item }}
{% endfor %}
* {{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* a
* b
* a
* a
* b
* b
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/else.test 0000664 0000000 0000000 00000000554 12616650102 0022674 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag can use an "else" clause
--TEMPLATE--
{% for item in items %}
* {{ item }}
{% else %}
no item
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* a
* b
--DATA--
return array('items' => array())
--EXPECT--
no item
--DATA--
return array()
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
no item
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/inner_variables.test 0000664 0000000 0000000 00000000446 12616650102 0025107 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag does not reset inner variables
--TEMPLATE--
{% for i in 1..2 %}
{% for j in 0..2 %}
{{k}}{% set k = k+1 %} {{ loop.parent.loop.index }}
{% endfor %}
{% endfor %}
--DATA--
return array('k' => 0)
--EXPECT--
0 1
1 1
2 1
3 2
4 2
5 2
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/keys.test 0000664 0000000 0000000 00000000266 12616650102 0022717 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag can iterate over keys
--TEMPLATE--
{% for key in items|keys %}
* {{ key }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* 0
* 1
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test 0000664 0000000 0000000 00000000321 12616650102 0025110 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag can iterate over keys and values
--TEMPLATE--
{% for key, item in items %}
* {{ key }}/{{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* 0/a
* 1/b
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/loop_context.test 0000664 0000000 0000000 00000000545 12616650102 0024461 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag adds a loop variable to the context
--TEMPLATE--
{% for item in items %}
* {{ loop.index }}/{{ loop.index0 }}
* {{ loop.revindex }}/{{ loop.revindex0 }}
* {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* 1/0
* 2/1
* 1//2
* 2/1
* 1/0
* /1/2
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test 0000664 0000000 0000000 00000000331 12616650102 0025624 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag adds a loop variable to the context locally
--TEMPLATE--
{% for item in items %}
{% endfor %}
{% if loop is not defined %}WORKS{% endif %}
--DATA--
return array('items' => array())
--EXPECT--
WORKS
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test 0000664 0000000 0000000 00000000431 12616650102 0025245 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag
--TEMPLATE--
{% for i, item in items if i > 0 %}
{{ loop.last }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXCEPTION--
Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test 0000664 0000000 0000000 00000000401 12616650102 0026245 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag
--TEMPLATE--
{% for i, item in items if loop.last > 0 %}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXCEPTION--
Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/nested_else.test 0000664 0000000 0000000 00000000462 12616650102 0024234 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag can use an "else" clause
--TEMPLATE--
{% for item in items %}
{% for item in items1 %}
* {{ item }}
{% else %}
no {{ item }}
{% endfor %}
{% else %}
no item1
{% endfor %}
--DATA--
return array('items' => array('a', 'b'), 'items1' => array())
--EXPECT--
no a
no b
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/objects.test 0000664 0000000 0000000 00000001541 12616650102 0023372 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag iterates over iterable objects
--TEMPLATE--
{% for item in items %}
* {{ item }}
* {{ loop.index }}/{{ loop.index0 }}
* {{ loop.first }}
{% endfor %}
{% for key, value in items %}
* {{ key }}/{{ value }}
{% endfor %}
{% for key in items|keys %}
* {{ key }}
{% endfor %}
--DATA--
class ItemsIterator implements Iterator
{
protected $values = array('foo' => 'bar', 'bar' => 'foo');
public function current() { return current($this->values); }
public function key() { return key($this->values); }
public function next() { return next($this->values); }
public function rewind() { return reset($this->values); }
public function valid() { return false !== current($this->values); }
}
return array('items' => new ItemsIterator())
--EXPECT--
* bar
* 1/0
* 1
* foo
* 2/1
*
* foo/bar
* bar/foo
* foo
* bar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/objects_countable.test 0000664 0000000 0000000 00000002055 12616650102 0025427 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag iterates over iterable and countable objects
--TEMPLATE--
{% for item in items %}
* {{ item }}
* {{ loop.index }}/{{ loop.index0 }}
* {{ loop.revindex }}/{{ loop.revindex0 }}
* {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
{% endfor %}
{% for key, value in items %}
* {{ key }}/{{ value }}
{% endfor %}
{% for key in items|keys %}
* {{ key }}
{% endfor %}
--DATA--
class ItemsIteratorCountable implements Iterator, Countable
{
protected $values = array('foo' => 'bar', 'bar' => 'foo');
public function current() { return current($this->values); }
public function key() { return key($this->values); }
public function next() { return next($this->values); }
public function rewind() { return reset($this->values); }
public function valid() { return false !== current($this->values); }
public function count() { return count($this->values); }
}
return array('items' => new ItemsIteratorCountable())
--EXPECT--
* bar
* 1/0
* 2/1
* 1//2
* foo
* 2/1
* 1/0
* /1/2
* foo/bar
* bar/foo
* foo
* bar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/recursive.test 0000664 0000000 0000000 00000000550 12616650102 0023747 0 ustar 00root root 0000000 0000000 --TEST--
"for" tags can be nested
--TEMPLATE--
{% for key, item in items %}
* {{ key }} ({{ loop.length }}):
{% for value in item %}
* {{ value }} ({{ loop.length }})
{% endfor %}
{% endfor %}
--DATA--
return array('items' => array('a' => array('a1', 'a2', 'a3'), 'b' => array('b1')))
--EXPECT--
* a (2):
* a1 (3)
* a2 (3)
* a3 (3)
* b (2):
* b1 (1)
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/for/values.test 0000664 0000000 0000000 00000000267 12616650102 0023244 0 ustar 00root root 0000000 0000000 --TEST--
"for" tag iterates over item values
--TEMPLATE--
{% for item in items %}
* {{ item }}
{% endfor %}
--DATA--
return array('items' => array('a', 'b'))
--EXPECT--
* a
* b
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/from.test 0000664 0000000 0000000 00000000375 12616650102 0022122 0 ustar 00root root 0000000 0000000 --TEST--
global variables
--TEMPLATE--
{% include "included.twig" %}
{% from "included.twig" import foobar %}
{{ foobar() }}
--TEMPLATE(included.twig)--
{% macro foobar() %}
called foobar
{% endmacro %}
--DATA--
return array();
--EXPECT--
called foobar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/if/ 0000775 0000000 0000000 00000000000 12616650102 0020647 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/if/basic.test 0000664 0000000 0000000 00000000443 12616650102 0022632 0 ustar 00root root 0000000 0000000 --TEST--
"if" creates a condition
--TEMPLATE--
{% if a is defined %}
{{ a }}
{% elseif b is defined %}
{{ b }}
{% else %}
NOTHING
{% endif %}
--DATA--
return array('a' => 'a')
--EXPECT--
a
--DATA--
return array('b' => 'b')
--EXPECT--
b
--DATA--
return array()
--EXPECT--
NOTHING
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/if/expression.test 0000664 0000000 0000000 00000000423 12616650102 0023746 0 ustar 00root root 0000000 0000000 --TEST--
"if" takes an expression as a test
--TEMPLATE--
{% if a < 2 %}
A1
{% elseif a > 10 %}
A2
{% else %}
A3
{% endif %}
--DATA--
return array('a' => 1)
--EXPECT--
A1
--DATA--
return array('a' => 12)
--EXPECT--
A2
--DATA--
return array('a' => 7)
--EXPECT--
A3
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/ 0000775 0000000 0000000 00000000000 12616650102 0021674 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/basic.test 0000664 0000000 0000000 00000000227 12616650102 0023657 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag
--TEMPLATE--
FOO
{% include "foo.twig" %}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array()
--EXPECT--
FOO
FOOBAR
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/expression.test 0000664 0000000 0000000 00000000322 12616650102 0024771 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag allows expressions for the template to include
--TEMPLATE--
FOO
{% include foo %}
BAR
--TEMPLATE(foo.twig)--
FOOBAR
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO
FOOBAR
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test 0000664 0000000 0000000 00000000412 12616650102 0025606 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag
--TEMPLATE--
{% include ["foo.twig", "bar.twig"] ignore missing %}
{% include "foo.twig" ignore missing %}
{% include "foo.twig" ignore missing with {} %}
{% include "foo.twig" ignore missing with {} only %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/missing.test 0000664 0000000 0000000 00000000265 12616650102 0024251 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag
--TEMPLATE--
{% include "foo.twig" %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/missing_nested.test 0000664 0000000 0000000 00000000504 12616650102 0025607 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag
--TEMPLATE--
{% extends "base.twig" %}
{% block content %}
{{ parent() }}
{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}
{% include "foo.twig" %}
{% endblock %}
--DATA--
return array();
--EXCEPTION--
Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/only.test 0000664 0000000 0000000 00000000637 12616650102 0023564 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag accept variables and only
--TEMPLATE--
{% include "foo.twig" %}
{% include "foo.twig" only %}
{% include "foo.twig" with {'foo1': 'bar'} %}
{% include "foo.twig" with {'foo1': 'bar'} only %}
--TEMPLATE(foo.twig)--
{% for k, v in _context %}{{ k }},{% endfor %}
--DATA--
return array('foo' => 'bar')
--EXPECT--
foo,global,_parent,
global,_parent,
foo,global,foo1,_parent,
foo1,global,_parent,
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/template_instance.test 0000664 0000000 0000000 00000000307 12616650102 0026274 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag accepts Twig_Template instance
--TEMPLATE--
{% include foo %} FOO
--TEMPLATE(foo.twig)--
BAR
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BAR FOO
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test 0000664 0000000 0000000 00000000270 12616650102 0026453 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag
--TEMPLATE--
{% include ["foo.twig", "bar.twig"] %}
{% include ["bar.twig", "foo.twig"] %}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/include/with_variables.test 0000664 0000000 0000000 00000000360 12616650102 0025577 0 ustar 00root root 0000000 0000000 --TEST--
"include" tag accept variables
--TEMPLATE--
{% include "foo.twig" with {'foo': 'bar'} %}
{% include "foo.twig" with vars %}
--TEMPLATE(foo.twig)--
{{ foo }}
--DATA--
return array('vars' => array('foo' => 'bar'))
--EXPECT--
bar
bar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/ 0000775 0000000 0000000 00000000000 12616650102 0022542 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/basic.test 0000664 0000000 0000000 00000000305 12616650102 0024522 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}
FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array()
--EXPECT--
FOO
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/block_expr.test 0000664 0000000 0000000 00000001153 12616650102 0025573 0 ustar 00root root 0000000 0000000 --TEST--
block_expr
--TEMPLATE--
{% extends "base.twig" %}
{% block element -%}
Element:
{{- parent() -}}
{% endblock %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
{%- if item.children is defined %}
{%- for item in item.children %}
{{- block('element') -}}
{% endfor %}
{%- endif -%}
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
'item' => array(
'children' => array(
null,
null,
)
)
)
--EXPECT--
Element:
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/block_expr2.test 0000664 0000000 0000000 00000001240 12616650102 0025652 0 ustar 00root root 0000000 0000000 --TEST--
block_expr2
--TEMPLATE--
{% extends "base2.twig" %}
{% block element -%}
Element:
{{- parent() -}}
{% endblock %}
--TEMPLATE(base2.twig)--
{% extends "base.twig" %}
--TEMPLATE(base.twig)--
{% spaceless %}
{% block element -%}
{%- if item.children is defined %}
{%- for item in item.children %}
{{- block('element') -}}
{% endfor %}
{%- endif -%}
{%- endblock %}
{% endspaceless %}
--DATA--
return array(
'item' => array(
'children' => array(
null,
null,
)
)
)
--EXPECT--
Element:
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test 0000664 0000000 0000000 00000000517 12616650102 0025751 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends standalone ? foo : 'bar.twig' %}
{% block content %}{{ parent() }}FOO{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}FOO{% endblock %}
--TEMPLATE(bar.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array('foo' => 'foo.twig', 'standalone' => true)
--EXPECT--
FOOFOO
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test 0000664 0000000 0000000 00000000321 12616650102 0025063 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends foo %}
{% block content %}
FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}{% endblock %}
--DATA--
return array('foo' => 'foo.twig')
--EXPECT--
FOO
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/empty.test 0000664 0000000 0000000 00000000240 12616650102 0024575 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
--TEMPLATE(foo.twig)--
{% block content %}FOO{% endblock %}
--DATA--
return array()
--EXPECT--
FOO
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test 0000664 0000000 0000000 00000000260 12616650102 0026774 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends ["foo.twig", "bar.twig"] %}
--TEMPLATE(bar.twig)--
{% block content %}
foo
{% endblock %}
--DATA--
return array()
--EXPECT--
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array_with_empty_name.test 0000664 0000000 0000000 00000000250 12616650102 0032244 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends ["", "bar.twig"] %}
--TEMPLATE(bar.twig)--
{% block content %}
foo
{% endblock %}
--DATA--
return array()
--EXPECT--
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array_with_null_name.test 0000664 0000000 0000000 00000000252 12616650102 0032062 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends [null, "bar.twig"] %}
--TEMPLATE(bar.twig)--
{% block content %}
foo
{% endblock %}
--DATA--
return array()
--EXPECT--
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test 0000664 0000000 0000000 00000000523 12616650102 0025276 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %}
--TEMPLATE(layout.twig)--
{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %}
--TEMPLATE(base.twig)--
{% block content %}base {% endblock %}
--DATA--
return array()
--EXPECT--
base layout index
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/multiple_dynamic.test 0000664 0000000 0000000 00000000716 12616650102 0027006 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% set foo = 1 %}
{{ include('parent.twig') }}
{{ include('parent.twig') }}
{% set foo = 2 %}
{{ include('parent.twig') }}
--TEMPLATE(parent.twig)--
{% extends foo~'_parent.twig' %}{% block content %}{{ parent() }} parent{% endblock %}
--TEMPLATE(1_parent.twig)--
{% block content %}1{% endblock %}
--TEMPLATE(2_parent.twig)--
{% block content %}2{% endblock %}
--DATA--
return array()
--EXPECT--
1 parent
1 parent
2 parent
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test 0000664 0000000 0000000 00000000614 12616650102 0026263 0 ustar 00root root 0000000 0000000 --TEST--
"block" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}
{% block subcontent %}
{% block subsubcontent %}
SUBSUBCONTENT
{% endblock %}
{% endblock %}
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}
{% block subcontent %}
SUBCONTENT
{% endblock %}
{% endblock %}
--DATA--
return array()
--EXPECT--
SUBSUBCONTENT
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test 0000664 0000000 0000000 00000000373 12616650102 0030677 0 ustar 00root root 0000000 0000000 --TEST--
"block" tag
--TEMPLATE--
{% block content %}
CONTENT
{%- block subcontent -%}
SUBCONTENT
{%- endblock -%}
ENDCONTENT
{% endblock %}
--TEMPLATE(foo.twig)--
--DATA--
return array()
--EXPECT--
CONTENTSUBCONTENTENDCONTENT
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test 0000664 0000000 0000000 00000000467 12616650102 0027305 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends "layout.twig" %}
{% block inside %}INSIDE{% endblock inside %}
--TEMPLATE(layout.twig)--
{% extends "base.twig" %}
{% block body %}
{% block inside '' %}
{% endblock body %}
--TEMPLATE(base.twig)--
{% block body '' %}
--DATA--
return array()
--EXPECT--
INSIDE
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/parent.test 0000664 0000000 0000000 00000000350 12616650102 0024732 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array()
--EXPECT--
BARFOOBAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test 0000664 0000000 0000000 00000000357 12616650102 0026246 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends foo ? 'foo.twig' : 'bar.twig' %}
--TEMPLATE(foo.twig)--
FOO
--TEMPLATE(bar.twig)--
BAR
--DATA--
return array('foo' => true)
--EXPECT--
FOO
--DATA--
return array('foo' => false)
--EXPECT--
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test 0000664 0000000 0000000 00000000273 12616650102 0027076 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% block content %}
{% extends "foo.twig" %}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test 0000664 0000000 0000000 00000000720 12616650102 0027014 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends "base.twig" %}
{% block content %}{% include "included.twig" %}{% endblock %}
{% block footer %}Footer{% endblock %}
--TEMPLATE(included.twig)--
{% extends "base.twig" %}
{% block content %}Included Content{% endblock %}
--TEMPLATE(base.twig)--
{% block content %}Default Content{% endblock %}
{% block footer %}Default Footer{% endblock %}
--DATA--
return array()
--EXPECT--
Included Content
Default Footer
Footer
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test 0000664 0000000 0000000 00000000523 12616650102 0026276 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag
--TEMPLATE--
{% extends "foo.twig" %}
{% block content %}
{% block inside %}
INSIDE OVERRIDDEN
{% endblock %}
BEFORE
{{ parent() }}
AFTER
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}
BAR
{% endblock %}
--DATA--
return array()
--EXPECT--
INSIDE OVERRIDDEN
BEFORE
BAR
AFTER
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test 0000664 0000000 0000000 00000000361 12616650102 0030251 0 ustar 00root root 0000000 0000000 --TEST--
"parent" tag
--TEMPLATE--
{% block content %}
{{ parent() }}
{% endblock %}
--EXCEPTION--
Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test 0000664 0000000 0000000 00000000322 12616650102 0032506 0 ustar 00root root 0000000 0000000 --TEST--
"parent" tag
--TEMPLATE--
{% use 'foo.twig' %}
{% block content %}
{{ parent() }}
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array()
--EXPECT--
BAR
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test 0000664 0000000 0000000 00000000431 12616650102 0027140 0 ustar 00root root 0000000 0000000 --TEST--
"extends" tag accepts Twig_Template instance
--TEMPLATE--
{% extends foo %}
{% block content %}
{{ parent() }}FOO
{% endblock %}
--TEMPLATE(foo.twig)--
{% block content %}BAR{% endblock %}
--DATA--
return array('foo' => $twig->loadTemplate('foo.twig'))
--EXPECT--
BARFOO
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/inheritance/use.test 0000664 0000000 0000000 00000001726 12616650102 0024245 0 ustar 00root root 0000000 0000000 --TEST--
"parent" function
--TEMPLATE--
{% extends "parent.twig" %}
{% use "use1.twig" %}
{% use "use2.twig" %}
{% block content_parent %}
{{ parent() }}
{% endblock %}
{% block content_use1 %}
{{ parent() }}
{% endblock %}
{% block content_use2 %}
{{ parent() }}
{% endblock %}
{% block content %}
{{ block('content_use1_only') }}
{{ block('content_use2_only') }}
{% endblock %}
--TEMPLATE(parent.twig)--
{% block content_parent 'content_parent' %}
{% block content_use1 'content_parent' %}
{% block content_use2 'content_parent' %}
{% block content '' %}
--TEMPLATE(use1.twig)--
{% block content_use1 'content_use1' %}
{% block content_use2 'content_use1' %}
{% block content_use1_only 'content_use1_only' %}
--TEMPLATE(use2.twig)--
{% block content_use2 'content_use2' %}
{% block content_use2_only 'content_use2_only' %}
--DATA--
return array()
--EXPECT--
content_parent
content_use1
content_use2
content_use1_only
content_use2_only
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/ 0000775 0000000 0000000 00000000000 12616650102 0021352 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/basic.test 0000664 0000000 0000000 00000000745 12616650102 0023342 0 ustar 00root root 0000000 0000000 --TEST--
"macro" tag
--TEMPLATE--
{% import _self as macros %}
{{ macros.input('username') }}
{{ macros.input('password', null, 'password', 1) }}
{% macro input(name, value, type, size) %}
{% endmacro %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test 0000664 0000000 0000000 00000000370 12616650102 0025043 0 ustar 00root root 0000000 0000000 --TEST--
"macro" tag supports name for endmacro
--TEMPLATE--
{% import _self as macros %}
{{ macros.foo() }}
{{ macros.bar() }}
{% macro foo() %}foo{% endmacro %}
{% macro bar() %}bar{% endmacro bar %}
--DATA--
return array()
--EXPECT--
foo
bar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/external.test 0000664 0000000 0000000 00000001001 12616650102 0024065 0 ustar 00root root 0000000 0000000 --TEST--
"macro" tag
--TEMPLATE--
{% import 'forms.twig' as forms %}
{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}
--TEMPLATE(forms.twig)--
{% macro input(name, value, type, size) %}
{% endmacro %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/from.test 0000664 0000000 0000000 00000000541 12616650102 0023216 0 ustar 00root root 0000000 0000000 --TEST--
"macro" tag
--TEMPLATE--
{% from 'forms.twig' import foo %}
{% from 'forms.twig' import foo as foobar, bar %}
{{ foo('foo') }}
{{ foobar('foo') }}
{{ bar('foo') }}
--TEMPLATE(forms.twig)--
{% macro foo(name) %}foo{{ name }}{% endmacro %}
{% macro bar(name) %}bar{{ name }}{% endmacro %}
--DATA--
return array()
--EXPECT--
foofoo
foofoo
barfoo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/from_with_reserved_name.test 0000664 0000000 0000000 00000000424 12616650102 0027150 0 ustar 00root root 0000000 0000000 --TEST--
"from" tag with reserved name
--TEMPLATE--
{% from 'forms.twig' import templateName %}
--TEMPLATE(forms.twig)--
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: "templateName" cannot be an imported macro as it is a reserved keyword in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/global.test 0000664 0000000 0000000 00000000374 12616650102 0023517 0 ustar 00root root 0000000 0000000 --TEST--
"macro" tag
--TEMPLATE--
{% from 'forms.twig' import foo %}
{{ foo('foo') }}
{{ foo() }}
--TEMPLATE(forms.twig)--
{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %}
--DATA--
return array()
--EXPECT--
fooglobal
fooglobal
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/import_with_reserved_nam.test 0000664 0000000 0000000 00000000433 12616650102 0027352 0 ustar 00root root 0000000 0000000 --TEST--
"from" tag with reserved name
--TEMPLATE--
{% import 'forms.twig' as macros %}
{{ macros.parent() }}
--TEMPLATE(forms.twig)--
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: "parent" cannot be called as macro as it is a reserved keyword in "index.twig" at line 4.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/reserved_name.test 0000664 0000000 0000000 00000000406 12616650102 0025072 0 ustar 00root root 0000000 0000000 --TEST--
"macro" tag with reserved name
--TEMPLATE--
{% macro parent(arg1, arg2) %}
parent
{% endmacro %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: "parent" cannot be used as a macro name as it is a reserved keyword in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/self_import.test 0000664 0000000 0000000 00000000742 12616650102 0024601 0 ustar 00root root 0000000 0000000 --TEST--
"macro" tag
--TEMPLATE--
{% import _self as forms %}
{{ forms.input('username') }}
{{ forms.input('password', null, 'password', 1) }}
{% macro input(name, value, type, size) %}
{% endmacro %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/special_chars.test 0000664 0000000 0000000 00000000277 12616650102 0025061 0 ustar 00root root 0000000 0000000 --TEST--
"§" as a macro name
--TEMPLATE--
{% import _self as macros %}
{{ macros.§('foo') }}
{% macro §(foo) %}
§{{ foo }}§
{% endmacro %}
--DATA--
return array()
--EXPECT--
§foo§
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/macro/super_globals.test 0000664 0000000 0000000 00000000307 12616650102 0025114 0 ustar 00root root 0000000 0000000 --TEST--
Super globals as macro arguments
--TEMPLATE--
{% import _self as macros %}
{{ macros.foo('foo') }}
{% macro foo(GET) %}
{{ GET }}
{% endmacro %}
--DATA--
return array()
--EXPECT--
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/raw/ 0000775 0000000 0000000 00000000000 12616650102 0021042 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/raw/basic.legacy.test 0000664 0000000 0000000 00000000156 12616650102 0024271 0 ustar 00root root 0000000 0000000 --TEST--
"raw" tag
--TEMPLATE--
{% raw %}
{{ foo }}
{% endraw %}
--DATA--
return array()
--EXPECT--
{{ foo }}
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.legacy.test 0000664 0000000 0000000 00000000307 12616650102 0027404 0 ustar 00root root 0000000 0000000 --TEST--
"raw" tag
--TEMPLATE--
{% raw %}
{{ foo }}
{% endverbatim %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.legacy.test 0000664 0000000 0000000 00000000701 12616650102 0027100 0 ustar 00root root 0000000 0000000 --TEST--
"raw" tag
--TEMPLATE--
1***
{%- raw %}
{{ 'bla' }}
{% endraw %}
1***
2***
{%- raw -%}
{{ 'bla' }}
{% endraw %}
2***
3***
{%- raw -%}
{{ 'bla' }}
{% endraw -%}
3***
4***
{%- raw -%}
{{ 'bla' }}
{%- endraw %}
4***
5***
{%- raw -%}
{{ 'bla' }}
{%- endraw -%}
5***
--DATA--
return array()
--EXPECT--
1***
{{ 'bla' }}
1***
2***{{ 'bla' }}
2***
3***{{ 'bla' }}
3***
4***{{ 'bla' }}
4***
5***{{ 'bla' }}5***
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/sandbox/ 0000775 0000000 0000000 00000000000 12616650102 0021707 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test 0000664 0000000 0000000 00000000371 12616650102 0024651 0 ustar 00root root 0000000 0000000 --TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
{%- include "foo.twig" %}
a
{%- endsandbox %}
--TEMPLATE(foo.twig)--
foo
--EXCEPTION--
Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test 0000664 0000000 0000000 00000000465 12616650102 0024656 0 ustar 00root root 0000000 0000000 --TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
{%- include "foo.twig" %}
{% if 1 %}
{%- include "foo.twig" %}
{% endif %}
{%- endsandbox %}
--TEMPLATE(foo.twig)--
foo
--EXCEPTION--
Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/sandbox/simple.test 0000664 0000000 0000000 00000000505 12616650102 0024101 0 ustar 00root root 0000000 0000000 --TEST--
sandbox tag
--TEMPLATE--
{%- sandbox %}
{%- include "foo.twig" %}
{%- endsandbox %}
{%- sandbox %}
{%- include "foo.twig" %}
{%- include "foo.twig" %}
{%- endsandbox %}
{%- sandbox %}{% include "foo.twig" %}{% endsandbox %}
--TEMPLATE(foo.twig)--
foo
--DATA--
return array()
--EXPECT--
foo
foo
foo
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/set/ 0000775 0000000 0000000 00000000000 12616650102 0021044 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/set/basic.test 0000664 0000000 0000000 00000000336 12616650102 0023030 0 ustar 00root root 0000000 0000000 --TEST--
"set" tag
--TEMPLATE--
{% set foo = 'foo' %}
{% set bar = 'foo ' %}
{{ foo }}
{{ bar }}
{% set foo, bar = 'foo', 'bar' %}
{{ foo }}{{ bar }}
--DATA--
return array()
--EXPECT--
foo
foo<br />
foobar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/set/capture-empty.test 0000664 0000000 0000000 00000000216 12616650102 0024543 0 ustar 00root root 0000000 0000000 --TEST--
"set" tag block empty capture
--TEMPLATE--
{% set foo %}{% endset %}
{% if foo %}FAIL{% endif %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/set/capture.test 0000664 0000000 0000000 00000000225 12616650102 0023407 0 ustar 00root root 0000000 0000000 --TEST--
"set" tag block capture
--TEMPLATE--
{% set foo %}f o o{% endset %}
{{ foo }}
--DATA--
return array()
--EXPECT--
f o o
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/set/expression.test 0000664 0000000 0000000 00000000230 12616650102 0024137 0 ustar 00root root 0000000 0000000 --TEST--
"set" tag
--TEMPLATE--
{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %}
{{ foo }}
{{ bar }}
--DATA--
return array()
--EXPECT--
foobar
barfoo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/spaceless/ 0000775 0000000 0000000 00000000000 12616650102 0022233 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/spaceless/simple.test 0000664 0000000 0000000 00000000331 12616650102 0024422 0 ustar 00root root 0000000 0000000 --TEST--
"spaceless" tag removes whites between HTML tags
--TEMPLATE--
{% spaceless %}
{% endspaceless %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/special_chars.test 0000664 0000000 0000000 00000000125 12616650102 0023750 0 ustar 00root root 0000000 0000000 --TEST--
"§" custom tag
--TEMPLATE--
{% § %}
--DATA--
return array()
--EXPECT--
§
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/trim_block.test 0000664 0000000 0000000 00000001673 12616650102 0023306 0 ustar 00root root 0000000 0000000 --TEST--
Whitespace trimming on tags.
--TEMPLATE--
{{ 5 * '{#-'|length }}
{{ '{{-'|length * 5 + '{%-'|length }}
Trim on control tag:
{% for i in range(1, 9) -%}
{{ i }}
{%- endfor %}
Trim on output tag:
{% for i in range(1, 9) %}
{{- i -}}
{% endfor %}
Trim comments:
{#- Invisible -#}
After the comment.
Trim leading space:
{% if leading %}
{{- leading }}
{% endif %}
{%- if leading %}
{{- leading }}
{%- endif %}
Trim trailing space:
{% if trailing -%}
{{ trailing -}}
{% endif -%}
Combined:
{%- if both -%}
{%- endif -%}
end
--DATA--
return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both')
--EXPECT--
15
18
Trim on control tag:
123456789
Trim on output tag:
123456789
Trim comments:After the comment.
Trim leading space:
leading space
leading space
Trim trailing space:
trailing spaceCombined:end
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/ 0000775 0000000 0000000 00000000000 12616650102 0021045 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/aliases.test 0000664 0000000 0000000 00000000273 12616650102 0023371 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "blocks.twig" with content as foo %}
{{ block('foo') }}
--TEMPLATE(blocks.twig)--
{% block content 'foo' %}
--DATA--
return array()
--EXPECT--
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/basic.test 0000664 0000000 0000000 00000000253 12616650102 0023027 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "blocks.twig" %}
{{ block('content') }}
--TEMPLATE(blocks.twig)--
{% block content 'foo' %}
--DATA--
return array()
--EXPECT--
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/deep.test 0000664 0000000 0000000 00000000506 12616650102 0022664 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
--TEMPLATE(foo.twig)--
{% use "bar.twig" %}
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
foo
foo
bar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/deep_empty.test 0000664 0000000 0000000 00000000233 12616650102 0024077 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
--TEMPLATE(foo.twig)--
{% use "bar.twig" %}
--TEMPLATE(bar.twig)--
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/inheritance.test 0000664 0000000 0000000 00000001137 12616650102 0024241 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "parent.twig" %}
{{ block('container') }}
--TEMPLATE(parent.twig)--
{% use "ancestor.twig" %}
{% block sub_container %}
overridden sub_container
{% endblock %}
--TEMPLATE(ancestor.twig)--
{% block container %}
{{ block('sub_container') }}
{% endblock %}
{% block sub_container %}
sub_container
{% endblock %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/inheritance2.test 0000664 0000000 0000000 00000001136 12616650102 0024322 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "ancestor.twig" %}
{% use "parent.twig" %}
{{ block('container') }}
--TEMPLATE(parent.twig)--
{% block sub_container %}
overridden sub_container
{% endblock %}
--TEMPLATE(ancestor.twig)--
{% block container %}
{{ block('sub_container') }}
{% endblock %}
{% block sub_container %}
sub_container
{% endblock %}
--DATA--
return array()
--EXPECT--
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/multiple.test 0000664 0000000 0000000 00000000505 12616650102 0023601 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" %}
{% use "bar.twig" %}
{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
--TEMPLATE(foo.twig)--
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
bar
foo
bar
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test 0000664 0000000 0000000 00000000600 12616650102 0025276 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use "foo.twig" with content as foo_content %}
{% use "bar.twig" %}
{{ block('content') }}
{{ block('foo') }}
{{ block('bar') }}
{{ block('foo_content') }}
--TEMPLATE(foo.twig)--
{% block content 'foo' %}
{% block foo 'foo' %}
--TEMPLATE(bar.twig)--
{% block content 'bar' %}
{% block bar 'bar' %}
--DATA--
return array()
--EXPECT--
bar
foo
bar
foo
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/parent_block.test 0000664 0000000 0000000 00000001155 12616650102 0024413 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig' with foobar as base_base_foobar %}
{% block foobar %}
{{- block('base_base_foobar') -}}
Content of block (second override)
{% endblock foobar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' with foobar as base_foobar %}
{% block foobar %}
{{- block('base_foobar') -}}
Content of block (first override)
{% endblock foobar %}
--TEMPLATE(file1.html.twig)--
{% block foobar -%}
Content of block
{% endblock foobar %}
--DATA--
return array()
--EXPECT--
Content of block
Content of block (first override)
Content of block (second override)
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/parent_block2.test 0000664 0000000 0000000 00000001024 12616650102 0024470 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig'%}
{% block foobar %}
{{- parent() -}}
Content of block (second override)
{% endblock foobar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' %}
{% block foobar %}
{{- parent() -}}
Content of block (first override)
{% endblock foobar %}
--TEMPLATE(file1.html.twig)--
{% block foobar -%}
Content of block
{% endblock foobar %}
--DATA--
return array()
--EXPECT--
Content of block
Content of block (first override)
Content of block (second override)
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/use/parent_block3.test 0000664 0000000 0000000 00000001474 12616650102 0024502 0 ustar 00root root 0000000 0000000 --TEST--
"use" tag
--TEMPLATE--
{% use 'file2.html.twig' %}
{% use 'file1.html.twig' with foo %}
{% block foo %}
{{- parent() -}}
Content of foo (second override)
{% endblock foo %}
{% block bar %}
{{- parent() -}}
Content of bar (second override)
{% endblock bar %}
--TEMPLATE(file2.html.twig)--
{% use 'file1.html.twig' %}
{% block foo %}
{{- parent() -}}
Content of foo (first override)
{% endblock foo %}
{% block bar %}
{{- parent() -}}
Content of bar (first override)
{% endblock bar %}
--TEMPLATE(file1.html.twig)--
{% block foo -%}
Content of foo
{% endblock foo %}
{% block bar -%}
Content of bar
{% endblock bar %}
--DATA--
return array()
--EXPECT--
Content of foo
Content of foo (first override)
Content of foo (second override)
Content of bar
Content of bar (second override)
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/verbatim/ 0000775 0000000 0000000 00000000000 12616650102 0022062 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tags/verbatim/basic.test 0000664 0000000 0000000 00000000175 12616650102 0024047 0 ustar 00root root 0000000 0000000 --TEST--
"verbatim" tag
--TEMPLATE--
{% verbatim %}
{{ foo }}
{% endverbatim %}
--DATA--
return array()
--EXPECT--
{{ foo }}
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test 0000664 0000000 0000000 00000000321 12616650102 0027155 0 ustar 00root root 0000000 0000000 --TEST--
"verbatim" tag
--TEMPLATE--
{% verbatim %}
{{ foo }}
{% endraw %}
--DATA--
return array()
--EXCEPTION--
Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2.
Twig-1.23.1/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test 0000664 0000000 0000000 00000000770 12616650102 0026663 0 ustar 00root root 0000000 0000000 --TEST--
"verbatim" tag
--TEMPLATE--
1***
{%- verbatim %}
{{ 'bla' }}
{% endverbatim %}
1***
2***
{%- verbatim -%}
{{ 'bla' }}
{% endverbatim %}
2***
3***
{%- verbatim -%}
{{ 'bla' }}
{% endverbatim -%}
3***
4***
{%- verbatim -%}
{{ 'bla' }}
{%- endverbatim %}
4***
5***
{%- verbatim -%}
{{ 'bla' }}
{%- endverbatim -%}
5***
--DATA--
return array()
--EXPECT--
1***
{{ 'bla' }}
1***
2***{{ 'bla' }}
2***
3***{{ 'bla' }}
3***
4***{{ 'bla' }}
4***
5***{{ 'bla' }}5***
Twig-1.23.1/test/Twig/Tests/Fixtures/tests/ 0000775 0000000 0000000 00000000000 12616650102 0020455 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Fixtures/tests/array.test 0000664 0000000 0000000 00000000576 12616650102 0022504 0 ustar 00root root 0000000 0000000 --TEST--
array index test
--TEMPLATE--
{% for key, value in days %}
{{ key }}
{% endfor %}
--DATA--
return array('days' => array(
1 => array('money' => 9),
2 => array('money' => 21),
3 => array('money' => 38),
4 => array('money' => 6),
18 => array('money' => 6),
19 => array('money' => 3),
31 => array('money' => 11),
));
--EXPECT--
1
2
3
4
18
19
31
Twig-1.23.1/test/Twig/Tests/Fixtures/tests/constant.test 0000664 0000000 0000000 00000000564 12616650102 0023214 0 ustar 00root root 0000000 0000000 --TEST--
"const" test
--TEMPLATE--
{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }}
{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }}
--DATA--
return array('value' => 'bar', 'object' => new ArrayObject(array('hi')));
--EXPECT--
ok
ok
ok
ok Twig-1.23.1/test/Twig/Tests/Fixtures/tests/defined.test 0000664 0000000 0000000 00000004744 12616650102 0022765 0 ustar 00root root 0000000 0000000 --TEST--
"defined" test
--TEMPLATE--
{{ definedVar is defined ? 'ok' : 'ko' }}
{{ definedVar is not defined ? 'ko' : 'ok' }}
{{ undefinedVar is defined ? 'ko' : 'ok' }}
{{ undefinedVar is not defined ? 'ok' : 'ko' }}
{{ zeroVar is defined ? 'ok' : 'ko' }}
{{ nullVar is defined ? 'ok' : 'ko' }}
{{ nested.definedVar is defined ? 'ok' : 'ko' }}
{{ nested['definedVar'] is defined ? 'ok' : 'ko' }}
{{ nested.definedVar is not defined ? 'ko' : 'ok' }}
{{ nested.undefinedVar is defined ? 'ko' : 'ok' }}
{{ nested['undefinedVar'] is defined ? 'ko' : 'ok' }}
{{ nested.undefinedVar is not defined ? 'ok' : 'ko' }}
{{ nested.zeroVar is defined ? 'ok' : 'ko' }}
{{ nested.nullVar is defined ? 'ok' : 'ko' }}
{{ nested.definedArray.0 is defined ? 'ok' : 'ko' }}
{{ nested['definedArray'][0] is defined ? 'ok' : 'ko' }}
{{ object.foo is defined ? 'ok' : 'ko' }}
{{ object.undefinedMethod is defined ? 'ko' : 'ok' }}
{{ object.getFoo() is defined ? 'ok' : 'ko' }}
{{ object.getFoo('a') is defined ? 'ok' : 'ko' }}
{{ object.undefinedMethod() is defined ? 'ko' : 'ok' }}
{{ object.undefinedMethod('a') is defined ? 'ko' : 'ok' }}
{{ object.self.foo is defined ? 'ok' : 'ko' }}
{{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }}
{{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }}
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
);
--EXPECT--
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
--DATA--
return array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'nested' => array(
'definedVar' => 'defined',
'zeroVar' => 0,
'nullVar' => null,
'definedArray' => array(0),
),
'object' => new TwigTestFoo(),
);
--CONFIG--
return array('strict_variables' => false)
--EXPECT--
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
ok
Twig-1.23.1/test/Twig/Tests/Fixtures/tests/empty.test 0000664 0000000 0000000 00000002023 12616650102 0022511 0 ustar 00root root 0000000 0000000 --TEST--
"empty" test
--TEMPLATE--
{{ foo is empty ? 'ok' : 'ko' }}
{{ bar is empty ? 'ok' : 'ko' }}
{{ foobar is empty ? 'ok' : 'ko' }}
{{ array is empty ? 'ok' : 'ko' }}
{{ zero is empty ? 'ok' : 'ko' }}
{{ string is empty ? 'ok' : 'ko' }}
{{ countable_empty is empty ? 'ok' : 'ko' }}
{{ countable_not_empty is empty ? 'ok' : 'ko' }}
{{ markup_empty is empty ? 'ok' : 'ko' }}
{{ markup_not_empty is empty ? 'ok' : 'ko' }}
--DATA--
class CountableStub implements Countable
{
private $items;
public function __construct(array $items)
{
$this->items = $items;
}
public function count()
{
return count($this->items);
}
}
return array(
'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0',
'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)),
'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'),
);
--EXPECT--
ok
ok
ok
ok
ko
ko
ok
ko
ok
ko
Twig-1.23.1/test/Twig/Tests/Fixtures/tests/even.test 0000664 0000000 0000000 00000000321 12616650102 0022307 0 ustar 00root root 0000000 0000000 --TEST--
"even" test
--TEMPLATE--
{{ 1 is even ? 'ko' : 'ok' }}
{{ 2 is even ? 'ok' : 'ko' }}
{{ 1 is not even ? 'ok' : 'ko' }}
{{ 2 is not even ? 'ko' : 'ok' }}
--DATA--
return array()
--EXPECT--
ok
ok
ok
ok
Twig-1.23.1/test/Twig/Tests/Fixtures/tests/in.test 0000664 0000000 0000000 00000004332 12616650102 0021766 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the in operator
--TEMPLATE--
{% if bar in foo %}
TRUE
{% endif %}
{% if not (bar in foo) %}
{% else %}
TRUE
{% endif %}
{% if bar not in foo %}
{% else %}
TRUE
{% endif %}
{% if 'a' in bar %}
TRUE
{% endif %}
{% if 'c' not in bar %}
TRUE
{% endif %}
{% if '' in bar %}
TRUE
{% endif %}
{% if '' in '' %}
TRUE
{% endif %}
{% if '0' not in '' %}
TRUE
{% endif %}
{% if 'a' not in '0' %}
TRUE
{% endif %}
{% if '0' in '0' %}
TRUE
{% endif %}
{{ false in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ true in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ '0' in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ '' in [0, 1] ? 'TRUE' : 'FALSE' }}
{{ 0 in ['', 1] ? 'TRUE' : 'FALSE' }}
{{ '' in 'foo' ? 'TRUE' : 'FALSE' }}
{{ 0 in 'foo' ? 'TRUE' : 'FALSE' }}
{{ false in 'foo' ? 'TRUE' : 'FALSE' }}
{{ false in '100' ? 'TRUE' : 'FALSE' }}
{{ true in '100' ? 'TRUE' : 'FALSE' }}
{{ [] in [true, false] ? 'TRUE' : 'FALSE' }}
{{ [] in [true, ''] ? 'TRUE' : 'FALSE' }}
{{ [] in [true, []] ? 'TRUE' : 'FALSE' }}
{{ resource ? 'TRUE' : 'FALSE' }}
{{ resource in 'foo'~resource ? 'TRUE' : 'FALSE' }}
{{ object in 'stdClass' ? 'TRUE' : 'FALSE' }}
{{ [] in 'Array' ? 'TRUE' : 'FALSE' }}
{{ dir_object in 'foo'~dir_object ? 'TRUE' : 'FALSE' }}
{{ ''~resource in resource ? 'TRUE' : 'FALSE' }}
{{ 'stdClass' in object ? 'TRUE' : 'FALSE' }}
{{ 'Array' in [] ? 'TRUE' : 'FALSE' }}
{{ ''~dir_object in dir_object ? 'TRUE' : 'FALSE' }}
{{ resource in [''~resource] ? 'TRUE' : 'FALSE' }}
{{ resource in [resource + 1 - 1] ? 'TRUE' : 'FALSE' }}
{{ dir_object in [''~dir_object] ? 'TRUE' : 'FALSE' }}
{{ 5 in 125 ? 'TRUE' : 'FALSE' }}
{{ 5 in '125' ? 'TRUE' : 'FALSE' }}
{{ '5' in 125 ? 'TRUE' : 'FALSE' }}
{{ '5' in '125' ? 'TRUE' : 'FALSE' }}
{{ 5.5 in 125.5 ? 'TRUE' : 'FALSE' }}
{{ 5.5 in '125.5' ? 'TRUE' : 'FALSE' }}
{{ '5.5' in 125.5 ? 'TRUE' : 'FALSE' }}
--DATA--
return array('bar' => 'bar', 'foo' => array('bar' => 'bar'), 'dir_object' => new SplFileInfo(dirname(__FILE__)), 'object' => new stdClass(), 'resource' => opendir(dirname(__FILE__)))
--EXPECT--
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
TRUE
FALSE
FALSE
FALSE
FALSE
TRUE
FALSE
TRUE
TRUE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
FALSE
TRUE
FALSE
TRUE
FALSE
TRUE
FALSE
Twig-1.23.1/test/Twig/Tests/Fixtures/tests/in_with_objects.test 0000664 0000000 0000000 00000000514 12616650102 0024530 0 ustar 00root root 0000000 0000000 --TEST--
Twig supports the in operator when using objects
--TEMPLATE--
{% if object in object_list %}
TRUE
{% endif %}
--DATA--
$foo = new TwigTestFoo();
$foo1 = new TwigTestFoo();
$foo->position = $foo1;
$foo1->position = $foo;
return array(
'object' => $foo,
'object_list' => array($foo1, $foo),
);
--EXPECT--
TRUE
Twig-1.23.1/test/Twig/Tests/Fixtures/tests/iterable.test 0000664 0000000 0000000 00000000547 12616650102 0023153 0 ustar 00root root 0000000 0000000 --TEST--
"iterable" test
--TEMPLATE--
{{ foo is iterable ? 'ok' : 'ko' }}
{{ traversable is iterable ? 'ok' : 'ko' }}
{{ obj is iterable ? 'ok' : 'ko' }}
{{ val is iterable ? 'ok' : 'ko' }}
--DATA--
return array(
'foo' => array(),
'traversable' => new ArrayIterator(array()),
'obj' => new stdClass(),
'val' => 'test',
);
--EXPECT--
ok
ok
ko
ko Twig-1.23.1/test/Twig/Tests/Fixtures/tests/odd.test 0000664 0000000 0000000 00000000203 12616650102 0022117 0 ustar 00root root 0000000 0000000 --TEST--
"odd" test
--TEMPLATE--
{{ 1 is odd ? 'ok' : 'ko' }}
{{ 2 is odd ? 'ko' : 'ok' }}
--DATA--
return array()
--EXPECT--
ok
ok Twig-1.23.1/test/Twig/Tests/IntegrationTest.php 0000664 0000000 0000000 00000012306 12616650102 0021340 0 ustar 00root root 0000000 0000000 position = 0;
}
public function current()
{
return $this->array[$this->position];
}
public function key()
{
return 'a';
}
public function next()
{
++$this->position;
}
public function valid()
{
return isset($this->array[$this->position]);
}
}
class TwigTestTokenParser_§ extends Twig_TokenParser
{
public function parse(Twig_Token $token)
{
$this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
return new Twig_Node_Print(new Twig_Node_Expression_Constant('§', -1), -1);
}
public function getTag()
{
return '§';
}
}
class TwigTestExtension extends Twig_Extension
{
public function getTokenParsers()
{
return array(
new TwigTestTokenParser_§(),
);
}
public function getFilters()
{
return array(
new Twig_SimpleFilter('§', array($this, '§Filter')),
new Twig_SimpleFilter('escape_and_nl2br', array($this, 'escape_and_nl2br'), array('needs_environment' => true, 'is_safe' => array('html'))),
new Twig_SimpleFilter('nl2br', array($this, 'nl2br'), array('pre_escape' => 'html', 'is_safe' => array('html'))),
new Twig_SimpleFilter('escape_something', array($this, 'escape_something'), array('is_safe' => array('something'))),
new Twig_SimpleFilter('preserves_safety', array($this, 'preserves_safety'), array('preserves_safety' => array('html'))),
new Twig_SimpleFilter('*_path', array($this, 'dynamic_path')),
new Twig_SimpleFilter('*_foo_*_bar', array($this, 'dynamic_foo')),
);
}
public function getFunctions()
{
return array(
new Twig_SimpleFunction('§', array($this, '§Function')),
new Twig_SimpleFunction('safe_br', array($this, 'br'), array('is_safe' => array('html'))),
new Twig_SimpleFunction('unsafe_br', array($this, 'br')),
new Twig_SimpleFunction('*_path', array($this, 'dynamic_path')),
new Twig_SimpleFunction('*_foo_*_bar', array($this, 'dynamic_foo')),
);
}
public function getTests()
{
return array(
new Twig_SimpleTest('multi word', array($this, 'is_multi_word')),
);
}
public function §Filter($value)
{
return "§{$value}§";
}
public function §Function($value)
{
return "§{$value}§";
}
/**
* nl2br which also escapes, for testing escaper filters.
*/
public function escape_and_nl2br($env, $value, $sep = ' ')
{
return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep);
}
/**
* nl2br only, for testing filters with pre_escape.
*/
public function nl2br($value, $sep = ' ')
{
// not secure if $value contains html tags (not only entities)
// don't use
return str_replace("\n", "$sep\n", $value);
}
public function dynamic_path($element, $item)
{
return $element.'/'.$item;
}
public function dynamic_foo($foo, $bar, $item)
{
return $foo.'/'.$bar.'/'.$item;
}
public function escape_something($value)
{
return strtoupper($value);
}
public function preserves_safety($value)
{
return strtoupper($value);
}
public function br()
{
return ' ';
}
public function is_multi_word($value)
{
return false !== strpos($value, ' ');
}
public function getName()
{
return 'integration_test';
}
}
Twig-1.23.1/test/Twig/Tests/LegacyFixtures/ 0000775 0000000 0000000 00000000000 12616650102 0020440 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/LegacyFixtures/test.legacy.test 0000664 0000000 0000000 00000000174 12616650102 0023565 0 ustar 00root root 0000000 0000000 --TEST--
Old test classes usage
--TEMPLATE--
{{ 'foo' is multi word ? 'yes' : 'no' }}
--DATA--
return array()
--EXPECT--
no
Twig-1.23.1/test/Twig/Tests/LegacyIntegrationTest.php 0000664 0000000 0000000 00000002154 12616650102 0022465 0 ustar 00root root 0000000 0000000 new Twig_Test_Method($this, 'is_multi_word'),
);
}
public function is_multi_word($value)
{
return false !== strpos($value, ' ');
}
public function getName()
{
return 'legacy_integration_test';
}
}
Twig-1.23.1/test/Twig/Tests/LexerTest.php 0000664 0000000 0000000 00000023376 12616650102 0020145 0 ustar 00root root 0000000 0000000 getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::BLOCK_START_TYPE);
$this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
}
public function testNameLabelForFunction()
{
$template = '{{ §() }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::VAR_START_TYPE);
$this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
}
public function testBracketsNesting()
{
$template = '{{ {"a":{"b":"c"}} }}';
$this->assertEquals(2, $this->countToken($template, Twig_Token::PUNCTUATION_TYPE, '{'));
$this->assertEquals(2, $this->countToken($template, Twig_Token::PUNCTUATION_TYPE, '}'));
}
protected function countToken($template, $type, $value = null)
{
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$count = 0;
while (!$stream->isEOF()) {
$token = $stream->next();
if ($type === $token->getType()) {
if (null === $value || $value === $token->getValue()) {
++$count;
}
}
}
return $count;
}
public function testLineDirective()
{
$template = "foo\n"
."bar\n"
."{% line 10 %}\n"
."{{\n"
."baz\n"
."}}\n";
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
// foo\nbar\n
$this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
// \n (after {% line %})
$this->assertSame(10, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
// {{
$this->assertSame(11, $stream->expect(Twig_Token::VAR_START_TYPE)->getLine());
// baz
$this->assertSame(12, $stream->expect(Twig_Token::NAME_TYPE)->getLine());
}
public function testLineDirectiveInline()
{
$template = "foo\n"
."bar{% line 10 %}{{\n"
."baz\n"
."}}\n";
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
// foo\nbar
$this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
// {{
$this->assertSame(10, $stream->expect(Twig_Token::VAR_START_TYPE)->getLine());
// baz
$this->assertSame(11, $stream->expect(Twig_Token::NAME_TYPE)->getLine());
}
public function testLongComments()
{
$template = '{# '.str_repeat('*', 100000).' #}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$lexer->tokenize($template);
// should not throw an exception
}
public function testLongVerbatim()
{
$template = '{% verbatim %}'.str_repeat('*', 100000).'{% endverbatim %}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$lexer->tokenize($template);
// should not throw an exception
}
public function testLongVar()
{
$template = '{{ '.str_repeat('x', 100000).' }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$lexer->tokenize($template);
// should not throw an exception
}
public function testLongBlock()
{
$template = '{% '.str_repeat('x', 100000).' %}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$lexer->tokenize($template);
// should not throw an exception
}
public function testBigNumbers()
{
$template = '{{ 922337203685477580700 }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->next();
$node = $stream->next();
$this->assertEquals('922337203685477580700', $node->getValue());
}
public function testStringWithEscapedDelimiter()
{
$tests = array(
"{{ 'foo \' bar' }}" => 'foo \' bar',
'{{ "foo \" bar" }}' => 'foo " bar',
);
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
foreach ($tests as $template => $expected) {
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::VAR_START_TYPE);
$stream->expect(Twig_Token::STRING_TYPE, $expected);
}
}
public function testStringWithInterpolation()
{
$template = 'foo {{ "bar #{ baz + 1 }" }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::TEXT_TYPE, 'foo ');
$stream->expect(Twig_Token::VAR_START_TYPE);
$stream->expect(Twig_Token::STRING_TYPE, 'bar ');
$stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
$stream->expect(Twig_Token::NAME_TYPE, 'baz');
$stream->expect(Twig_Token::OPERATOR_TYPE, '+');
$stream->expect(Twig_Token::NUMBER_TYPE, '1');
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
$stream->expect(Twig_Token::VAR_END_TYPE);
}
public function testStringWithEscapedInterpolation()
{
$template = '{{ "bar \#{baz+1}" }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::VAR_START_TYPE);
$stream->expect(Twig_Token::STRING_TYPE, 'bar #{baz+1}');
$stream->expect(Twig_Token::VAR_END_TYPE);
}
public function testStringWithHash()
{
$template = '{{ "bar # baz" }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::VAR_START_TYPE);
$stream->expect(Twig_Token::STRING_TYPE, 'bar # baz');
$stream->expect(Twig_Token::VAR_END_TYPE);
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unclosed """
*/
public function testStringWithUnterminatedInterpolation()
{
$template = '{{ "bar #{x" }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$lexer->tokenize($template);
}
public function testStringWithNestedInterpolations()
{
$template = '{{ "bar #{ "foo#{bar}" }" }}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::VAR_START_TYPE);
$stream->expect(Twig_Token::STRING_TYPE, 'bar ');
$stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
$stream->expect(Twig_Token::STRING_TYPE, 'foo');
$stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
$stream->expect(Twig_Token::NAME_TYPE, 'bar');
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
$stream->expect(Twig_Token::VAR_END_TYPE);
}
public function testStringWithNestedInterpolationsInBlock()
{
$template = '{% foo "bar #{ "foo#{bar}" }" %}';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::BLOCK_START_TYPE);
$stream->expect(Twig_Token::NAME_TYPE, 'foo');
$stream->expect(Twig_Token::STRING_TYPE, 'bar ');
$stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
$stream->expect(Twig_Token::STRING_TYPE, 'foo');
$stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
$stream->expect(Twig_Token::NAME_TYPE, 'bar');
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
$stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
$stream->expect(Twig_Token::BLOCK_END_TYPE);
}
public function testOperatorEndingWithALetterAtTheEndOfALine()
{
$template = "{{ 1 and\n0}}";
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$stream = $lexer->tokenize($template);
$stream->expect(Twig_Token::VAR_START_TYPE);
$stream->expect(Twig_Token::NUMBER_TYPE, 1);
$stream->expect(Twig_Token::OPERATOR_TYPE, 'and');
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unclosed "variable" at line 3
*/
public function testUnterminatedVariable()
{
$template = '
{{
bar
';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$lexer->tokenize($template);
}
/**
* @expectedException Twig_Error_Syntax
* @expectedExceptionMessage Unclosed "block" at line 3
*/
public function testUnterminatedBlock()
{
$template = '
{%
bar
';
$lexer = new Twig_Lexer(new Twig_Environment($this->getMock('Twig_LoaderInterface')));
$lexer->tokenize($template);
}
}
Twig-1.23.1/test/Twig/Tests/Loader/ 0000775 0000000 0000000 00000000000 12616650102 0016710 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/ArrayTest.php 0000664 0000000 0000000 00000004302 12616650102 0021336 0 ustar 00root root 0000000 0000000 'bar'));
$this->assertEquals('bar', $loader->getSource('foo'));
}
/**
* @expectedException Twig_Error_Loader
*/
public function testGetSourceWhenTemplateDoesNotExist()
{
$loader = new Twig_Loader_Array(array());
$loader->getSource('foo');
}
public function testGetCacheKey()
{
$loader = new Twig_Loader_Array(array('foo' => 'bar'));
$this->assertEquals('bar', $loader->getCacheKey('foo'));
}
/**
* @expectedException Twig_Error_Loader
*/
public function testGetCacheKeyWhenTemplateDoesNotExist()
{
$loader = new Twig_Loader_Array(array());
$loader->getCacheKey('foo');
}
public function testSetTemplate()
{
$loader = new Twig_Loader_Array(array());
$loader->setTemplate('foo', 'bar');
$this->assertEquals('bar', $loader->getSource('foo'));
}
public function testIsFresh()
{
$loader = new Twig_Loader_Array(array('foo' => 'bar'));
$this->assertTrue($loader->isFresh('foo', time()));
}
/**
* @expectedException Twig_Error_Loader
*/
public function testIsFreshWhenTemplateDoesNotExist()
{
$loader = new Twig_Loader_Array(array());
$loader->isFresh('foo', time());
}
public function testTemplateReference()
{
$name = new Twig_Test_Loader_TemplateReference('foo');
$loader = new Twig_Loader_Array(array('foo' => 'bar'));
$loader->getCacheKey($name);
$loader->getSource($name);
$loader->isFresh($name, time());
$loader->setTemplate($name, 'foobar');
}
}
class Twig_Test_Loader_TemplateReference
{
private $name;
public function __construct($name)
{
$this->name = $name;
}
public function __toString()
{
return $this->name;
}
}
Twig-1.23.1/test/Twig/Tests/Loader/ChainTest.php 0000664 0000000 0000000 00000004435 12616650102 0021311 0 ustar 00root root 0000000 0000000 'bar')),
new Twig_Loader_Array(array('foo' => 'foobar', 'bar' => 'foo')),
));
$this->assertEquals('bar', $loader->getSource('foo'));
$this->assertEquals('foo', $loader->getSource('bar'));
}
/**
* @expectedException Twig_Error_Loader
*/
public function testGetSourceWhenTemplateDoesNotExist()
{
$loader = new Twig_Loader_Chain(array());
$loader->getSource('foo');
}
public function testGetCacheKey()
{
$loader = new Twig_Loader_Chain(array(
new Twig_Loader_Array(array('foo' => 'bar')),
new Twig_Loader_Array(array('foo' => 'foobar', 'bar' => 'foo')),
));
$this->assertEquals('bar', $loader->getCacheKey('foo'));
$this->assertEquals('foo', $loader->getCacheKey('bar'));
}
/**
* @expectedException Twig_Error_Loader
*/
public function testGetCacheKeyWhenTemplateDoesNotExist()
{
$loader = new Twig_Loader_Chain(array());
$loader->getCacheKey('foo');
}
public function testAddLoader()
{
$loader = new Twig_Loader_Chain();
$loader->addLoader(new Twig_Loader_Array(array('foo' => 'bar')));
$this->assertEquals('bar', $loader->getSource('foo'));
}
public function testExists()
{
$loader1 = $this->getMock('Twig_Loader_Array', array('exists', 'getSource'), array(), '', false);
$loader1->expects($this->once())->method('exists')->will($this->returnValue(false));
$loader1->expects($this->never())->method('getSource');
$loader2 = $this->getMock('Twig_LoaderInterface');
$loader2->expects($this->once())->method('getSource')->will($this->returnValue('content'));
$loader = new Twig_Loader_Chain();
$loader->addLoader($loader1);
$loader->addLoader($loader2);
$this->assertTrue($loader->exists('foo'));
}
}
Twig-1.23.1/test/Twig/Tests/Loader/FilesystemTest.php 0000664 0000000 0000000 00000015054 12616650102 0022412 0 ustar 00root root 0000000 0000000 getCacheKey($template);
$this->fail();
} catch (Twig_Error_Loader $e) {
$this->assertNotContains('Unable to find template', $e->getMessage());
}
}
public function getSecurityTests()
{
return array(
array("AutoloaderTest\0.php"),
array('..\\AutoloaderTest.php'),
array('..\\\\\\AutoloaderTest.php'),
array('../AutoloaderTest.php'),
array('..////AutoloaderTest.php'),
array('./../AutoloaderTest.php'),
array('.\\..\\AutoloaderTest.php'),
array('././././././../AutoloaderTest.php'),
array('.\\./.\\./.\\./../AutoloaderTest.php'),
array('foo/../../AutoloaderTest.php'),
array('foo\\..\\..\\AutoloaderTest.php'),
array('foo/../bar/../../AutoloaderTest.php'),
array('foo/bar/../../../AutoloaderTest.php'),
array('filters/../../AutoloaderTest.php'),
array('filters//..//..//AutoloaderTest.php'),
array('filters\\..\\..\\AutoloaderTest.php'),
array('filters\\\\..\\\\..\\\\AutoloaderTest.php'),
array('filters\\//../\\/\\..\\AutoloaderTest.php'),
array('/../AutoloaderTest.php'),
);
}
public function testPaths()
{
$basePath = dirname(__FILE__).'/Fixtures';
$loader = new Twig_Loader_Filesystem(array($basePath.'/normal', $basePath.'/normal_bis'));
$loader->setPaths(array($basePath.'/named', $basePath.'/named_bis'), 'named');
$loader->addPath($basePath.'/named_ter', 'named');
$loader->addPath($basePath.'/normal_ter');
$loader->prependPath($basePath.'/normal_final');
$loader->prependPath($basePath.'/named/../named_quater', 'named');
$loader->prependPath($basePath.'/named_final', 'named');
$this->assertEquals(array(
$basePath.'/normal_final',
$basePath.'/normal',
$basePath.'/normal_bis',
$basePath.'/normal_ter',
), $loader->getPaths());
$this->assertEquals(array(
$basePath.'/named_final',
$basePath.'/named/../named_quater',
$basePath.'/named',
$basePath.'/named_bis',
$basePath.'/named_ter',
), $loader->getPaths('named'));
$this->assertEquals(
realpath($basePath.'/named_quater/named_absolute.html'),
realpath($loader->getCacheKey('@named/named_absolute.html'))
);
$this->assertEquals("path (final)\n", $loader->getSource('index.html'));
$this->assertEquals("path (final)\n", $loader->getSource('@__main__/index.html'));
$this->assertEquals("named path (final)\n", $loader->getSource('@named/index.html'));
}
public function testEmptyConstructor()
{
$loader = new Twig_Loader_Filesystem();
$this->assertEquals(array(), $loader->getPaths());
}
public function testGetNamespaces()
{
$loader = new Twig_Loader_Filesystem(sys_get_temp_dir());
$this->assertEquals(array(Twig_Loader_Filesystem::MAIN_NAMESPACE), $loader->getNamespaces());
$loader->addPath(sys_get_temp_dir(), 'named');
$this->assertEquals(array(Twig_Loader_Filesystem::MAIN_NAMESPACE, 'named'), $loader->getNamespaces());
}
public function testFindTemplateExceptionNamespace()
{
$basePath = dirname(__FILE__).'/Fixtures';
$loader = new Twig_Loader_Filesystem(array($basePath.'/normal'));
$loader->addPath($basePath.'/named', 'named');
try {
$loader->getSource('@named/nowhere.html');
} catch (Exception $e) {
$this->assertInstanceof('Twig_Error_Loader', $e);
$this->assertContains('Unable to find template "@named/nowhere.html"', $e->getMessage());
}
}
public function testFindTemplateWithCache()
{
$basePath = dirname(__FILE__).'/Fixtures';
$loader = new Twig_Loader_Filesystem(array($basePath.'/normal'));
$loader->addPath($basePath.'/named', 'named');
// prime the cache for index.html in the named namespace
$namedSource = $loader->getSource('@named/index.html');
$this->assertEquals("named path\n", $namedSource);
// get index.html from the main namespace
$this->assertEquals("path\n", $loader->getSource('index.html'));
}
public function testLoadTemplateAndRenderBlockWithCache()
{
$loader = new Twig_Loader_Filesystem(array());
$loader->addPath(dirname(__FILE__).'/Fixtures/themes/theme2');
$loader->addPath(dirname(__FILE__).'/Fixtures/themes/theme1');
$loader->addPath(dirname(__FILE__).'/Fixtures/themes/theme1', 'default_theme');
$twig = new Twig_Environment($loader);
$template = $twig->loadTemplate('blocks.html.twig');
$this->assertSame('block from theme 1', $template->renderBlock('b1', array()));
$template = $twig->loadTemplate('blocks.html.twig');
$this->assertSame('block from theme 2', $template->renderBlock('b2', array()));
}
public function getArrayInheritanceTests()
{
return array(
'valid array inheritance' => array('array_inheritance_valid_parent.html.twig'),
'array inheritance with null first template' => array('array_inheritance_null_parent.html.twig'),
'array inheritance with empty first template' => array('array_inheritance_empty_parent.html.twig'),
'array inheritance with non-existent first template' => array('array_inheritance_nonexistent_parent.html.twig'),
);
}
/**
* @dataProvider getArrayInheritanceTests
*
* @param $templateName string Template name with array inheritance
*/
public function testArrayInheritance($templateName)
{
$loader = new Twig_Loader_Filesystem(array());
$loader->addPath(dirname(__FILE__).'/Fixtures/inheritance');
$twig = new Twig_Environment($loader);
$template = $twig->loadTemplate($templateName);
$this->assertSame('VALID Child', $template->renderBlock('body', array()));
}
}
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/ 0000775 0000000 0000000 00000000000 12616650102 0020521 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/inheritance/ 0000775 0000000 0000000 00000000000 12616650102 0023012 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/inheritance/array_inheritance_empty_parent.html.twig 0000664 0000000 0000000 00000000132 12616650102 0033123 0 ustar 00root root 0000000 0000000 {% extends ['','parent.html.twig'] %}
{% block body %}{{ parent() }} Child{% endblock %}
array_inheritance_nonexistent_parent.html.twig 0000664 0000000 0000000 00000000157 12616650102 0034273 0 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/inheritance {% extends ['nonexistent.html.twig','parent.html.twig'] %}
{% block body %}{{ parent() }} Child{% endblock %}
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/inheritance/array_inheritance_null_parent.html.twig 0000664 0000000 0000000 00000000134 12616650102 0032741 0 ustar 00root root 0000000 0000000 {% extends [null,'parent.html.twig'] %}
{% block body %}{{ parent() }} Child{% endblock %}
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/inheritance/array_inheritance_valid_parent.html.twig 0000664 0000000 0000000 00000000160 12616650102 0033065 0 ustar 00root root 0000000 0000000 {% extends ['parent.html.twig','spare_parent.html.twig'] %}
{% block body %}{{ parent() }} Child{% endblock %}
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/inheritance/parent.html.twig 0000664 0000000 0000000 00000000044 12616650102 0026140 0 ustar 00root root 0000000 0000000 {% block body %}VALID{% endblock %}
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/inheritance/spare_parent.html.twig 0000664 0000000 0000000 00000000053 12616650102 0027332 0 ustar 00root root 0000000 0000000 {% block body %}SPARE PARENT{% endblock %}
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named/ 0000775 0000000 0000000 00000000000 12616650102 0021605 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named/index.html 0000664 0000000 0000000 00000000013 12616650102 0023574 0 ustar 00root root 0000000 0000000 named path
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_bis/ 0000775 0000000 0000000 00000000000 12616650102 0022442 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_bis/index.html 0000664 0000000 0000000 00000000021 12616650102 0024430 0 ustar 00root root 0000000 0000000 named path (bis)
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_final/ 0000775 0000000 0000000 00000000000 12616650102 0022756 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_final/index.html 0000664 0000000 0000000 00000000023 12616650102 0024746 0 ustar 00root root 0000000 0000000 named path (final)
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_quater/ 0000775 0000000 0000000 00000000000 12616650102 0023166 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_quater/named_absolute.html 0000664 0000000 0000000 00000000024 12616650102 0027032 0 ustar 00root root 0000000 0000000 named path (quater)
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_ter/ 0000775 0000000 0000000 00000000000 12616650102 0022457 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/named_ter/index.html 0000664 0000000 0000000 00000000021 12616650102 0024445 0 ustar 00root root 0000000 0000000 named path (ter)
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal/ 0000775 0000000 0000000 00000000000 12616650102 0022011 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal/index.html 0000664 0000000 0000000 00000000005 12616650102 0024001 0 ustar 00root root 0000000 0000000 path
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal_bis/ 0000775 0000000 0000000 00000000000 12616650102 0022646 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html 0000664 0000000 0000000 00000000013 12616650102 0024635 0 ustar 00root root 0000000 0000000 path (bis)
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal_final/ 0000775 0000000 0000000 00000000000 12616650102 0023162 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal_final/index.html 0000664 0000000 0000000 00000000015 12616650102 0025153 0 ustar 00root root 0000000 0000000 path (final)
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal_ter/ 0000775 0000000 0000000 00000000000 12616650102 0022663 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html 0000664 0000000 0000000 00000000013 12616650102 0024652 0 ustar 00root root 0000000 0000000 path (ter)
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/themes/ 0000775 0000000 0000000 00000000000 12616650102 0022006 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/themes/theme1/ 0000775 0000000 0000000 00000000000 12616650102 0023171 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/themes/theme1/blocks.html.twig 0000664 0000000 0000000 00000000137 12616650102 0026306 0 ustar 00root root 0000000 0000000 {% block b1 %}block from theme 1{% endblock %}
{% block b2 %}block from theme 1{% endblock %}
Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/themes/theme2/ 0000775 0000000 0000000 00000000000 12616650102 0023172 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Loader/Fixtures/themes/theme2/blocks.html.twig 0000664 0000000 0000000 00000000134 12616650102 0026304 0 ustar 00root root 0000000 0000000 {% use '@default_theme/blocks.html.twig' %}
{% block b2 %}block from theme 2{% endblock %}
Twig-1.23.1/test/Twig/Tests/NativeExtensionTest.php 0000664 0000000 0000000 00000001674 12616650102 0022206 0 ustar 00root root 0000000 0000000 markTestSkipped('Skip under HHVM as the behavior is not the same as plain PHP (which is an edge case anyway)');
}
$twig = new Twig_Environment(new Twig_Loader_Array(array('index' => '{{ d1.date }}{{ d2.date }}')), array(
'debug' => true,
'cache' => false,
'autoescape' => false,
));
$d1 = new DateTime();
$d2 = new DateTime();
$output = $twig->render('index', compact('d1', 'd2'));
// If it fails, PHP will crash.
$this->assertEquals($output, $d1->date.$d2->date);
}
}
Twig-1.23.1/test/Twig/Tests/Node/ 0000775 0000000 0000000 00000000000 12616650102 0016367 5 ustar 00root root 0000000 0000000 Twig-1.23.1/test/Twig/Tests/Node/AutoEscapeTest.php 0000664 0000000 0000000 00000001472 12616650102 0021775 0 ustar 00root root 0000000 0000000 assertEquals($body, $node->getNode('body'));
$this->assertTrue($node->getAttribute('value'));
}
public function getTests()
{
$body = new Twig_Node(array(new Twig_Node_Text('foo', 1)));
$node = new Twig_Node_AutoEscape(true, $body, 1);
return array(
array($node, "// line 1\necho \"foo\";"),
);
}
}
Twig-1.23.1/test/Twig/Tests/Node/BlockReferenceTest.php 0000664 0000000 0000000 00000001237 12616650102 0022614 0 ustar 00root root 0000000 0000000 assertEquals('foo', $node->getAttribute('name'));
}
public function getTests()
{
return array(
array(new Twig_Node_BlockReference('foo', 1), <<displayBlock('foo', \$context, \$blocks);
EOF
),
);
}
}
Twig-1.23.1/test/Twig/Tests/Node/BlockTest.php 0000664 0000000 0000000 00000001543 12616650102 0020775 0 ustar 00root root 0000000 0000000 assertEquals($body, $node->getNode('body'));
$this->assertEquals('foo', $node->getAttribute('name'));
}
public function getTests()
{
$body = new Twig_Node_Text('foo', 1);
$node = new Twig_Node_Block('foo', $body, 1);
return array(
array($node, <<