aboutsummaryrefslogtreecommitdiffhomepage
path: root/vendor/twig
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/twig')
m---------vendor/twig/extensions0
-rw-r--r--vendor/twig/twig/.editorconfig18
-rw-r--r--vendor/twig/twig/.gitignore2
-rw-r--r--vendor/twig/twig/.travis.yml15
-rw-r--r--vendor/twig/twig/AUTHORS9
-rw-r--r--vendor/twig/twig/CHANGELOG637
-rw-r--r--vendor/twig/twig/LICENSE31
-rw-r--r--vendor/twig/twig/README.markdown17
-rw-r--r--vendor/twig/twig/composer.json31
-rw-r--r--vendor/twig/twig/doc/advanced.rst829
-rw-r--r--vendor/twig/twig/doc/advanced_legacy.rst887
-rw-r--r--vendor/twig/twig/doc/api.rst529
-rw-r--r--vendor/twig/twig/doc/coding_standards.rst101
-rw-r--r--vendor/twig/twig/doc/deprecated.rst98
-rw-r--r--vendor/twig/twig/doc/filters/abs.rst18
-rw-r--r--vendor/twig/twig/doc/filters/batch.rst45
-rw-r--r--vendor/twig/twig/doc/filters/capitalize.rst11
-rw-r--r--vendor/twig/twig/doc/filters/convert_encoding.rst28
-rw-r--r--vendor/twig/twig/doc/filters/date.rst88
-rw-r--r--vendor/twig/twig/doc/filters/date_modify.rst23
-rw-r--r--vendor/twig/twig/doc/filters/default.rst33
-rw-r--r--vendor/twig/twig/doc/filters/escape.rst93
-rw-r--r--vendor/twig/twig/doc/filters/first.rst25
-rw-r--r--vendor/twig/twig/doc/filters/format.rst16
-rw-r--r--vendor/twig/twig/doc/filters/index.rst36
-rw-r--r--vendor/twig/twig/doc/filters/join.rst23
-rw-r--r--vendor/twig/twig/doc/filters/json_encode.rst21
-rw-r--r--vendor/twig/twig/doc/filters/keys.rst11
-rw-r--r--vendor/twig/twig/doc/filters/last.rst25
-rw-r--r--vendor/twig/twig/doc/filters/length.rst12
-rw-r--r--vendor/twig/twig/doc/filters/lower.rst10
-rw-r--r--vendor/twig/twig/doc/filters/merge.rst41
-rw-r--r--vendor/twig/twig/doc/filters/nl2br.rst22
-rw-r--r--vendor/twig/twig/doc/filters/number_format.rst45
-rw-r--r--vendor/twig/twig/doc/filters/raw.rst12
-rw-r--r--vendor/twig/twig/doc/filters/replace.rst19
-rw-r--r--vendor/twig/twig/doc/filters/reverse.rst47
-rw-r--r--vendor/twig/twig/doc/filters/slice.rst70
-rw-r--r--vendor/twig/twig/doc/filters/sort.rst17
-rw-r--r--vendor/twig/twig/doc/filters/split.rst53
-rw-r--r--vendor/twig/twig/doc/filters/striptags.rst15
-rw-r--r--vendor/twig/twig/doc/filters/title.rst11
-rw-r--r--vendor/twig/twig/doc/filters/trim.rst29
-rw-r--r--vendor/twig/twig/doc/filters/upper.rst10
-rw-r--r--vendor/twig/twig/doc/filters/url_encode.rst28
-rw-r--r--vendor/twig/twig/doc/functions/attribute.rst18
-rw-r--r--vendor/twig/twig/doc/functions/block.rst15
-rw-r--r--vendor/twig/twig/doc/functions/constant.rst18
-rw-r--r--vendor/twig/twig/doc/functions/cycle.rst25
-rw-r--r--vendor/twig/twig/doc/functions/date.rst52
-rw-r--r--vendor/twig/twig/doc/functions/dump.rst69
-rw-r--r--vendor/twig/twig/doc/functions/include.rst80
-rw-r--r--vendor/twig/twig/doc/functions/index.rst17
-rw-r--r--vendor/twig/twig/doc/functions/parent.rst20
-rw-r--r--vendor/twig/twig/doc/functions/random.rst29
-rw-r--r--vendor/twig/twig/doc/functions/range.rst45
-rw-r--r--vendor/twig/twig/doc/functions/template_from_string.rst32
-rw-r--r--vendor/twig/twig/doc/index.rst18
-rw-r--r--vendor/twig/twig/doc/internals.rst140
-rw-r--r--vendor/twig/twig/doc/intro.rst164
-rw-r--r--vendor/twig/twig/doc/recipes.rst475
-rw-r--r--vendor/twig/twig/doc/tags/autoescape.rst71
-rw-r--r--vendor/twig/twig/doc/tags/block.rst11
-rw-r--r--vendor/twig/twig/doc/tags/do.rst12
-rw-r--r--vendor/twig/twig/doc/tags/embed.rst178
-rw-r--r--vendor/twig/twig/doc/tags/extends.rst268
-rw-r--r--vendor/twig/twig/doc/tags/filter.rst21
-rw-r--r--vendor/twig/twig/doc/tags/flush.rst17
-rw-r--r--vendor/twig/twig/doc/tags/for.rst172
-rw-r--r--vendor/twig/twig/doc/tags/from.rst8
-rw-r--r--vendor/twig/twig/doc/tags/if.rst43
-rw-r--r--vendor/twig/twig/doc/tags/import.rst57
-rw-r--r--vendor/twig/twig/doc/tags/include.rst86
-rw-r--r--vendor/twig/twig/doc/tags/index.rst24
-rw-r--r--vendor/twig/twig/doc/tags/macro.rst83
-rw-r--r--vendor/twig/twig/doc/tags/sandbox.rst30
-rw-r--r--vendor/twig/twig/doc/tags/set.rst78
-rw-r--r--vendor/twig/twig/doc/tags/spaceless.rst37
-rw-r--r--vendor/twig/twig/doc/tags/use.rst123
-rw-r--r--vendor/twig/twig/doc/tags/verbatim.rst24
-rw-r--r--vendor/twig/twig/doc/templates.rst851
-rw-r--r--vendor/twig/twig/doc/tests/constant.rst22
-rw-r--r--vendor/twig/twig/doc/tests/defined.rst30
-rw-r--r--vendor/twig/twig/doc/tests/divisibleby.rst10
-rw-r--r--vendor/twig/twig/doc/tests/empty.rst11
-rw-r--r--vendor/twig/twig/doc/tests/even.rst10
-rw-r--r--vendor/twig/twig/doc/tests/index.rst15
-rw-r--r--vendor/twig/twig/doc/tests/iterable.rst19
-rw-r--r--vendor/twig/twig/doc/tests/null.rst12
-rw-r--r--vendor/twig/twig/doc/tests/odd.rst10
-rw-r--r--vendor/twig/twig/doc/tests/sameas.rst11
-rw-r--r--vendor/twig/twig/ext/twig/.gitignore30
-rw-r--r--vendor/twig/twig/ext/twig/LICENSE22
-rw-r--r--vendor/twig/twig/ext/twig/config.m48
-rw-r--r--vendor/twig/twig/ext/twig/config.w328
-rw-r--r--vendor/twig/twig/ext/twig/php_twig.h31
-rw-r--r--vendor/twig/twig/ext/twig/twig.c1076
-rw-r--r--vendor/twig/twig/lib/Twig/Autoloader.php48
-rw-r--r--vendor/twig/twig/lib/Twig/Compiler.php267
-rw-r--r--vendor/twig/twig/lib/Twig/CompilerInterface.php35
-rw-r--r--vendor/twig/twig/lib/Twig/Environment.php1224
-rw-r--r--vendor/twig/twig/lib/Twig/Error.php243
-rw-r--r--vendor/twig/twig/lib/Twig/Error/Loader.php31
-rw-r--r--vendor/twig/twig/lib/Twig/Error/Runtime.php20
-rw-r--r--vendor/twig/twig/lib/Twig/Error/Syntax.php20
-rw-r--r--vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php28
-rw-r--r--vendor/twig/twig/lib/Twig/ExpressionParser.php600
-rw-r--r--vendor/twig/twig/lib/Twig/Extension.php93
-rw-r--r--vendor/twig/twig/lib/Twig/Extension/Core.php1355
-rw-r--r--vendor/twig/twig/lib/Twig/Extension/Debug.php71
-rw-r--r--vendor/twig/twig/lib/Twig/Extension/Escaper.php107
-rw-r--r--vendor/twig/twig/lib/Twig/Extension/Optimizer.php35
-rw-r--r--vendor/twig/twig/lib/Twig/Extension/Sandbox.php112
-rw-r--r--vendor/twig/twig/lib/Twig/Extension/Staging.php113
-rw-r--r--vendor/twig/twig/lib/Twig/Extension/StringLoader.php64
-rw-r--r--vendor/twig/twig/lib/Twig/ExtensionInterface.php83
-rw-r--r--vendor/twig/twig/lib/Twig/Filter.php81
-rw-r--r--vendor/twig/twig/lib/Twig/Filter/Function.php37
-rw-r--r--vendor/twig/twig/lib/Twig/Filter/Method.php39
-rw-r--r--vendor/twig/twig/lib/Twig/Filter/Node.php39
-rw-r--r--vendor/twig/twig/lib/Twig/FilterCallableInterface.php23
-rw-r--r--vendor/twig/twig/lib/Twig/FilterInterface.php42
-rw-r--r--vendor/twig/twig/lib/Twig/Function.php71
-rw-r--r--vendor/twig/twig/lib/Twig/Function/Function.php38
-rw-r--r--vendor/twig/twig/lib/Twig/Function/Method.php40
-rw-r--r--vendor/twig/twig/lib/Twig/Function/Node.php39
-rw-r--r--vendor/twig/twig/lib/Twig/FunctionCallableInterface.php23
-rw-r--r--vendor/twig/twig/lib/Twig/FunctionInterface.php39
-rw-r--r--vendor/twig/twig/lib/Twig/Lexer.php408
-rw-r--r--vendor/twig/twig/lib/Twig/LexerInterface.php29
-rw-r--r--vendor/twig/twig/lib/Twig/Loader/Array.php98
-rw-r--r--vendor/twig/twig/lib/Twig/Loader/Chain.php139
-rw-r--r--vendor/twig/twig/lib/Twig/Loader/Filesystem.php226
-rw-r--r--vendor/twig/twig/lib/Twig/Loader/String.php59
-rw-r--r--vendor/twig/twig/lib/Twig/LoaderInterface.php52
-rw-r--r--vendor/twig/twig/lib/Twig/Markup.php37
-rw-r--r--vendor/twig/twig/lib/Twig/Node.php226
-rw-r--r--vendor/twig/twig/lib/Twig/Node/AutoEscape.php39
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Block.php44
-rw-r--r--vendor/twig/twig/lib/Twig/Node/BlockReference.php37
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Body.php19
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Do.php38
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Embed.php38
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression.php20
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Array.php86
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php28
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary.php40
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php17
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php29
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php17
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php17
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php33
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php17
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php17
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php17
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php33
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php33
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php33
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php51
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Call.php178
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php31
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Constant.php23
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php33
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Filter.php36
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php43
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Function.php35
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php53
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php41
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Name.php88
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Parent.php47
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/TempName.php26
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test.php32
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php46
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php54
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php33
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php32
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php31
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php32
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php29
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Unary.php30
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php18
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Flush.php36
-rw-r--r--vendor/twig/twig/lib/Twig/Node/For.php112
-rw-r--r--vendor/twig/twig/lib/Twig/Node/ForLoop.php55
-rw-r--r--vendor/twig/twig/lib/Twig/Node/If.php66
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Import.php50
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Include.php99
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Macro.php96
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Module.php371
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Print.php39
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Sandbox.php47
-rw-r--r--vendor/twig/twig/lib/Twig/Node/SandboxedModule.php60
-rw-r--r--vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php59
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Set.php101
-rw-r--r--vendor/twig/twig/lib/Twig/Node/SetTemp.php35
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Spaceless.php40
-rw-r--r--vendor/twig/twig/lib/Twig/Node/Text.php39
-rw-r--r--vendor/twig/twig/lib/Twig/NodeInterface.php30
-rw-r--r--vendor/twig/twig/lib/Twig/NodeOutputInterface.php19
-rw-r--r--vendor/twig/twig/lib/Twig/NodeTraverser.php88
-rw-r--r--vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php167
-rw-r--r--vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php246
-rw-r--r--vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php131
-rw-r--r--vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php92
-rw-r--r--vendor/twig/twig/lib/Twig/NodeVisitorInterface.php47
-rw-r--r--vendor/twig/twig/lib/Twig/Parser.php394
-rw-r--r--vendor/twig/twig/lib/Twig/ParserInterface.php28
-rw-r--r--vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php19
-rw-r--r--vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php119
-rw-r--r--vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php24
-rw-r--r--vendor/twig/twig/lib/Twig/SimpleFilter.php94
-rw-r--r--vendor/twig/twig/lib/Twig/SimpleFunction.php84
-rw-r--r--vendor/twig/twig/lib/Twig/SimpleTest.php46
-rw-r--r--vendor/twig/twig/lib/Twig/Template.php455
-rw-r--r--vendor/twig/twig/lib/Twig/TemplateInterface.php47
-rw-r--r--vendor/twig/twig/lib/Twig/Test.php34
-rw-r--r--vendor/twig/twig/lib/Twig/Test/Function.php35
-rw-r--r--vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php154
-rw-r--r--vendor/twig/twig/lib/Twig/Test/Method.php37
-rw-r--r--vendor/twig/twig/lib/Twig/Test/Node.php37
-rw-r--r--vendor/twig/twig/lib/Twig/Test/NodeTestCase.php58
-rw-r--r--vendor/twig/twig/lib/Twig/TestCallableInterface.php21
-rw-r--r--vendor/twig/twig/lib/Twig/TestInterface.php26
-rw-r--r--vendor/twig/twig/lib/Twig/Token.php218
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser.php33
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php89
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Block.php83
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Do.php42
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Embed.php66
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Extends.php52
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Filter.php61
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Flush.php42
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/For.php136
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/From.php74
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/If.php94
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Import.php49
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Include.php80
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Macro.php68
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php68
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Set.php84
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php59
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParser/Use.php82
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParserBroker.php136
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php45
-rw-r--r--vendor/twig/twig/lib/Twig/TokenParserInterface.php41
-rw-r--r--vendor/twig/twig/lib/Twig/TokenStream.php144
-rw-r--r--vendor/twig/twig/phpunit.xml.dist25
-rw-r--r--vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php21
-rw-r--r--vendor/twig/twig/test/Twig/Tests/CompilerTest.php33
-rw-r--r--vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php288
-rw-r--r--vendor/twig/twig/test/Twig/Tests/ErrorTest.php159
-rw-r--r--vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php332
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php117
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php212
-rw-r--r--vendor/twig/twig/test/Twig/Tests/FileCachingTest.php70
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html7
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test20
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test61
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test46
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test20
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test27
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test28
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test30
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test31
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php31
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test37
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test37
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test76
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test19
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test13
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test150
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test21
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test42
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test27
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test19
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test13
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test26
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test83
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test45
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test26
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test26
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test69
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test131
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test23
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test68
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test50
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test35
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test50
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test42
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test57
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test13
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test29
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test23
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test19
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test9
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test43
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test15
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test20
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test28
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test44
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test16
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test18
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test17
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test56
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test11
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test20
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test9
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test8
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test74
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test12
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test22
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test21
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test23
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test56
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test24
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test108
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test45
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test14
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test48
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test19
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test19
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test10
-rw-r--r--vendor/twig/twig/test/Twig/Tests/IntegrationTest.php217
-rw-r--r--vendor/twig/twig/test/Twig/Tests/LexerTest.php301
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php97
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php79
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php97
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html1
-rw-r--r--vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php29
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php44
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php43
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php51
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/DoTest.php44
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php49
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php41
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php47
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php67
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php50
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php42
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php133
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php99
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php62
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php49
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php6
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php6
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php6
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php40
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php68
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php44
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php44
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php44
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/ForTest.php203
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/IfTest.php100
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php52
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php96
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php73
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php196
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php41
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php56
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php173
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php45
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/SetTest.php81
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php49
-rw-r--r--vendor/twig/twig/test/Twig/Tests/Node/TextTest.php40
-rw-r--r--vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php114
-rw-r--r--vendor/twig/twig/test/Twig/Tests/ParserTest.php180
-rw-r--r--vendor/twig/twig/test/Twig/Tests/TemplateTest.php626
-rw-r--r--vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php70
-rw-r--r--vendor/twig/twig/test/Twig/Tests/escapingTest.php320
-rw-r--r--vendor/twig/twig/test/bootstrap.php13
530 files changed, 32998 insertions, 0 deletions
diff --git a/vendor/twig/extensions b/vendor/twig/extensions
new file mode 160000
Subproject f5b0c84f3699e494c84ee627d7d583e115d2c4a
diff --git a/vendor/twig/twig/.editorconfig b/vendor/twig/twig/.editorconfig
new file mode 100644
index 00000000..270f1d1b
--- /dev/null
+++ b/vendor/twig/twig/.editorconfig
@@ -0,0 +1,18 @@
1; top-most EditorConfig file
2root = true
3
4; Unix-style newlines
5[*]
6end_of_line = LF
7
8[*.php]
9indent_style = space
10indent_size = 4
11
12[*.test]
13indent_style = space
14indent_size = 4
15
16[*.rst]
17indent_style = space
18indent_size = 4
diff --git a/vendor/twig/twig/.gitignore b/vendor/twig/twig/.gitignore
new file mode 100644
index 00000000..840b78e7
--- /dev/null
+++ b/vendor/twig/twig/.gitignore
@@ -0,0 +1,2 @@
1/ext/twig/autom4te.cache/
2
diff --git a/vendor/twig/twig/.travis.yml b/vendor/twig/twig/.travis.yml
new file mode 100644
index 00000000..8569a395
--- /dev/null
+++ b/vendor/twig/twig/.travis.yml
@@ -0,0 +1,15 @@
1language: php
2
3php:
4 - 5.2
5 - 5.3
6 - 5.4
7 - 5.5
8
9env:
10 - TWIG_EXT=no
11 - TWIG_EXT=yes
12
13before_script:
14 - if [ "$TWIG_EXT" == "yes" ]; then sh -c "cd ext/twig && phpize && ./configure --enable-twig && make && sudo make install"; fi
15 - if [ "$TWIG_EXT" == "yes" ]; then echo "extension=twig.so" >> `php --ini | grep "Loaded Configuration" | sed -e "s|.*:\s*||"`; fi
diff --git a/vendor/twig/twig/AUTHORS b/vendor/twig/twig/AUTHORS
new file mode 100644
index 00000000..eb5db051
--- /dev/null
+++ b/vendor/twig/twig/AUTHORS
@@ -0,0 +1,9 @@
1Twig is written and maintained by the Twig Team:
2
3Lead Developer:
4
5- Fabien Potencier <fabien.potencier@symfony-project.org>
6
7Project Founder:
8
9- Armin Ronacher <armin.ronacher@active-4.com>
diff --git a/vendor/twig/twig/CHANGELOG b/vendor/twig/twig/CHANGELOG
new file mode 100644
index 00000000..80ff9d2d
--- /dev/null
+++ b/vendor/twig/twig/CHANGELOG
@@ -0,0 +1,637 @@
1* 1.13.2 (2013-08-03)
2
3 * fixed the error line number for an error occurs in and embedded template
4 * fixed crashes of the C extension on some edge cases
5
6* 1.13.1 (2013-06-06)
7
8 * added the possibility to ignore the filesystem constructor argument in Twig_Loader_Filesystem
9 * fixed Twig_Loader_Chain::exists() for a loader which implements Twig_ExistsLoaderInterface
10 * adjusted backtrace call to reduce memory usage when an error occurs
11 * added support for object instances as the second argument of the constant test
12 * fixed the include function when used in an assignment
13
14* 1.13.0 (2013-05-10)
15
16 * fixed getting a numeric-like item on a variable ('09' for instance)
17 * fixed getting a boolean or float key on an array, so it is consistent with PHP's array access:
18 `{{ array[false] }}` behaves the same as `echo $array[false];` (equals `$array[0]`)
19 * made the escape filter 20% faster for happy path (escaping string for html with UTF-8)
20 * changed ☃ to § in tests
21 * enforced usage of named arguments after positional ones
22
23* 1.12.3 (2013-04-08)
24
25 * fixed a security issue in the filesystem loader where it was possible to include a template one
26 level above the configured path
27 * fixed fatal error that should be an exception when adding a filter/function/test too late
28 * added a batch filter
29 * added support for encoding an array as query string in the url_encode filter
30
31* 1.12.2 (2013-02-09)
32
33 * 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)
34 * fixed globals when getGlobals is called early on
35 * added the first and last filter
36
37* 1.12.1 (2013-01-15)
38
39 * added support for object instances as the second argument of the constant function
40 * relaxed globals management to avoid a BC break
41 * added support for {{ some_string[:2] }}
42
43* 1.12.0 (2013-01-08)
44
45 * added verbatim as an alias for the raw tag to avoid confusion with the raw filter
46 * fixed registration of tests and functions as anonymous functions
47 * fixed globals management
48
49* 1.12.0-RC1 (2012-12-29)
50
51 * added an include function (does the same as the include tag but in a more flexible way)
52 * added the ability to use any PHP callable to define filters, functions, and tests
53 * added a syntax error when using a loop variable that is not defined
54 * added the ability to set default values for macro arguments
55 * added support for named arguments for filters, tests, and functions
56 * moved filters/functions/tests syntax errors to the parser
57 * added support for extended ternary operator syntaxes
58
59* 1.11.1 (2012-11-11)
60
61 * fixed debug info line numbering (was off by 2)
62 * fixed escaping when calling a macro inside another one (regression introduced in 1.9.1)
63 * optimized variable access on PHP 5.4
64 * fixed a crash of the C extension when an exception was thrown from a macro called without being imported (using _self.XXX)
65
66* 1.11.0 (2012-11-07)
67
68 * fixed macro compilation when a variable name is a PHP reserved keyword
69 * changed the date filter behavior to always apply the default timezone, except if false is passed as the timezone
70 * fixed bitwise operator precedences
71 * added the template_from_string function
72 * fixed default timezone usage for the date function
73 * optimized the way Twig exceptions are managed (to make them faster)
74 * added Twig_ExistsLoaderInterface (implementing this interface in your loader make the chain loader much faster)
75
76* 1.10.3 (2012-10-19)
77
78 * fixed wrong template location in some error messages
79 * reverted a BC break introduced in 1.10.2
80 * added a split filter
81
82* 1.10.2 (2012-10-15)
83
84 * fixed macro calls on PHP 5.4
85
86* 1.10.1 (2012-10-15)
87
88 * made a speed optimization to macro calls when imported via the "import" tag
89 * fixed C extension compilation on Windows
90 * fixed a segfault in the C extension when using DateTime objects
91
92* 1.10.0 (2012-09-28)
93
94 * extracted functional tests framework to make it reusable for third-party extensions
95 * added namespaced templates support in Twig_Loader_Filesystem
96 * added Twig_Loader_Filesystem::prependPath()
97 * fixed an error when a token parser pass a closure as a test to the subparse() method
98
99* 1.9.2 (2012-08-25)
100
101 * fixed the in operator for objects that contain circular references
102 * fixed the C extension when accessing a public property of an object implementing the \ArrayAccess interface
103
104* 1.9.1 (2012-07-22)
105
106 * optimized macro calls when auto-escaping is on
107 * fixed wrong parent class for Twig_Function_Node
108 * made Twig_Loader_Chain more explicit about problems
109
110* 1.9.0 (2012-07-13)
111
112 * made the parsing independent of the template loaders
113 * fixed exception trace when an error occurs when rendering a child template
114 * added escaping strategies for CSS, URL, and HTML attributes
115 * fixed nested embed tag calls
116 * added the date_modify filter
117
118* 1.8.3 (2012-06-17)
119
120 * fixed paths in the filesystem loader when passing a path that ends with a slash or a backslash
121 * fixed escaping when a project defines a function named html or js
122 * fixed chmod mode to apply the umask correctly
123
124* 1.8.2 (2012-05-30)
125
126 * added the abs filter
127 * fixed a regression when using a number in template attributes
128 * fixed compiler when mbstring.func_overload is set to 2
129 * fixed DateTimeZone support in date filter
130
131* 1.8.1 (2012-05-17)
132
133 * fixed a regression when dealing with SimpleXMLElement instances in templates
134 * fixed "is_safe" value for the "dump" function when "html_errors" is not defined in php.ini
135 * switched to use mbstring whenever possible instead of iconv (you might need to update your encoding as mbstring and iconv encoding names sometimes differ)
136
137* 1.8.0 (2012-05-08)
138
139 * enforced interface when adding tests, filters, functions, and node visitors from extensions
140 * fixed a side-effect of the date filter where the timezone might be changed
141 * simplified usage of the autoescape tag; the only (optional) argument is now the escaping strategy or false (with a BC layer)
142 * added a way to dynamically change the auto-escaping strategy according to the template "filename"
143 * changed the autoescape option to also accept a supported escaping strategy (for BC, true is equivalent to html)
144 * added an embed tag
145
146* 1.7.0 (2012-04-24)
147
148 * fixed a PHP warning when using CIFS
149 * fixed template line number in some exceptions
150 * added an iterable test
151 * added an error when defining two blocks with the same name in a template
152 * added the preserves_safety option for filters
153 * fixed a PHP notice when trying to access a key on a non-object/array variable
154 * enhanced error reporting when the template file is an instance of SplFileInfo
155 * added Twig_Environment::mergeGlobals()
156 * added compilation checks to avoid misuses of the sandbox tag
157 * fixed filesystem loader freshness logic for high traffic websites
158 * fixed random function when charset is null
159
160* 1.6.5 (2012-04-11)
161
162 * fixed a regression when a template only extends another one without defining any blocks
163
164* 1.6.4 (2012-04-02)
165
166 * fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3
167 * fixed performance when compiling large files
168 * optimized parent template creation when the template does not use dynamic inheritance
169
170* 1.6.3 (2012-03-22)
171
172 * fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension
173 * fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot
174 * made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate
175
176* 1.6.2 (2012-03-18)
177
178 * fixed sandbox mode when used with inheritance
179 * added preserveKeys support for the slice filter
180 * fixed the date filter when a DateTime instance is passed with a specific timezone
181 * added a trim filter
182
183* 1.6.1 (2012-02-29)
184
185 * fixed Twig C extension
186 * removed the creation of Twig_Markup instances when not needed
187 * added a way to set the default global timezone for dates
188 * fixed the slice filter on strings when the length is not specified
189 * fixed the creation of the cache directory in case of a race condition
190
191* 1.6.0 (2012-02-04)
192
193 * fixed raw blocks when used with the whitespace trim option
194 * made a speed optimization to macro calls when imported via the "from" tag
195 * fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added
196 * fixed the attribute function when passing arguments
197 * added slice notation support for the [] operator (syntactic sugar for the slice operator)
198 * added a slice filter
199 * added string support for the reverse filter
200 * fixed the empty test and the length filter for Twig_Markup instances
201 * added a date function to ease date comparison
202 * fixed unary operators precedence
203 * added recursive parsing support in the parser
204 * added string and integer handling for the random function
205
206* 1.5.1 (2012-01-05)
207
208 * fixed a regression when parsing strings
209
210* 1.5.0 (2012-01-04)
211
212 * added Traversable objects support for the join filter
213
214* 1.5.0-RC2 (2011-12-30)
215
216 * added a way to set the default global date interval format
217 * fixed the date filter for DateInterval instances (setTimezone() does not exist for them)
218 * refactored Twig_Template::display() to ease its extension
219 * added a number_format filter
220
221* 1.5.0-RC1 (2011-12-26)
222
223 * removed the need to quote hash keys
224 * allowed hash keys to be any expression
225 * added a do tag
226 * added a flush tag
227 * added support for dynamically named filters and functions
228 * added a dump function to help debugging templates
229 * added a nl2br filter
230 * added a random function
231 * added a way to change the default format for the date filter
232 * fixed the lexer when an operator ending with a letter ends a line
233 * added string interpolation support
234 * enhanced exceptions for unknown filters, functions, tests, and tags
235
236* 1.4.0 (2011-12-07)
237
238 * fixed lexer when using big numbers (> PHP_INT_MAX)
239 * added missing preserveKeys argument to the reverse filter
240 * fixed macros containing filter tag calls
241
242* 1.4.0-RC2 (2011-11-27)
243
244 * removed usage of Reflection in Twig_Template::getAttribute()
245 * added a C extension that can optionally replace Twig_Template::getAttribute()
246 * added negative timestamp support to the date filter
247
248* 1.4.0-RC1 (2011-11-20)
249
250 * optimized variable access when using PHP 5.4
251 * changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby
252 * added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders
253 * added Twig_Function_Node to allow more complex functions to have their own Node class
254 * added Twig_Filter_Node to allow more complex filters to have their own Node class
255 * added Twig_Test_Node to allow more complex tests to have their own Node class
256 * added a better error message when a template is empty but contain a BOM
257 * fixed "in" operator for empty strings
258 * 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)
259 * changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order)
260 * added Twig_Environment::display()
261 * made the escape filter smarter when the encoding is not supported by PHP
262 * added a convert_encoding filter
263 * moved all node manipulations outside the compile() Node method
264 * made several speed optimizations
265
266* 1.3.0 (2011-10-08)
267
268no changes
269
270* 1.3.0-RC1 (2011-10-04)
271
272 * added an optimization for the parent() function
273 * added cache reloading when auto_reload is true and an extension has been modified
274 * added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup)
275 * allowed empty templates to be used as traits
276 * added traits support for the "parent" function
277
278* 1.2.0 (2011-09-13)
279
280no changes
281
282* 1.2.0-RC1 (2011-09-10)
283
284 * enhanced the exception when a tag remains unclosed
285 * added support for empty Countable objects for the "empty" test
286 * fixed algorithm that determines if a template using inheritance is valid (no output between block definitions)
287 * added better support for encoding problems when escaping a string (available as of PHP 5.4)
288 * added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %})
289 * added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %})
290 * added support for bitwise operators in expressions
291 * added the "attribute" function to allow getting dynamic attributes on variables
292 * added Twig_Loader_Chain
293 * added Twig_Loader_Array::setTemplate()
294 * added an optimization for the set tag when used to capture a large chunk of static text
295 * 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)
296 * removed the possibility to use the "extends" tag from a block
297 * added "if" modifier support to "for" loops
298
299* 1.1.2 (2011-07-30)
300
301 * fixed json_encode filter on PHP 5.2
302 * fixed regression introduced in 1.1.1 ({{ block(foo|lower) }})
303 * fixed inheritance when using conditional parents
304 * fixed compilation of templates when the body of a child template is not empty
305 * fixed output when a macro throws an exception
306 * fixed a parsing problem when a large chunk of text is enclosed in a comment tag
307 * added PHPDoc for all Token parsers and Core extension functions
308
309* 1.1.1 (2011-07-17)
310
311 * added a performance optimization in the Optimizer (also helps to lower the number of nested level calls)
312 * made some performance improvement for some edge cases
313
314* 1.1.0 (2011-06-28)
315
316 * fixed json_encode filter
317
318* 1.1.0-RC3 (2011-06-24)
319
320 * fixed method case-sensitivity when using the sandbox mode
321 * added timezone support for the date filter
322 * fixed possible security problems with NUL bytes
323
324* 1.1.0-RC2 (2011-06-16)
325
326 * added an exception when the template passed to "use" is not a string
327 * made 'a.b is defined' not throw an exception if a is not defined (in strict mode)
328 * added {% line \d+ %} directive
329
330* 1.1.0-RC1 (2011-05-28)
331
332Flush your cache after upgrading.
333
334 * fixed date filter when using a timestamp
335 * fixed the defined test for some cases
336 * fixed a parsing problem when a large chunk of text is enclosed in a raw tag
337 * added support for horizontal reuse of template blocks (see docs for more information)
338 * added whitespace control modifier to all tags (see docs for more information)
339 * added null as an alias for none (the null test is also an alias for the none test now)
340 * made TRUE, FALSE, NONE equivalent to their lowercase counterparts
341 * wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line
342 * moved display() method to Twig_Template (generated templates should now use doDisplay() instead)
343
344* 1.0.0 (2011-03-27)
345
346 * fixed output when using mbstring
347 * fixed duplicate call of methods when using the sandbox
348 * made the charset configurable for the escape filter
349
350* 1.0.0-RC2 (2011-02-21)
351
352 * changed the way {% set %} works when capturing (the content is now marked as safe)
353 * added support for macro name in the endmacro tag
354 * make Twig_Error compatible with PHP 5.3.0 >
355 * fixed an infinite loop on some Windows configurations
356 * fixed the "length" filter for numbers
357 * fixed Template::getAttribute() as properties in PHP are case sensitive
358 * removed coupling between Twig_Node and Twig_Template
359 * fixed the ternary operator precedence rule
360
361* 1.0.0-RC1 (2011-01-09)
362
363Backward incompatibilities:
364
365 * the "items" filter, which has been deprecated for quite a long time now, has been removed
366 * the "range" filter has been converted to a function: 0|range(10) -> range(0, 10)
367 * the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }}
368 * the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }}
369 * the "for" tag does not support "joined by" anymore
370 * the "autoescape" first argument is now "true"/"false" (instead of "on"/"off")
371 * the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %})
372 * the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %})
373 * removed the grammar and simple token parser (moved to the Twig Extensions repository)
374
375Changes:
376
377 * added "needs_context" option for filters and functions (the context is then passed as a first argument)
378 * added global variables support
379 * made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode)
380 * added the "from" tag to import macros as functions
381 * added support for functions (a function is just syntactic sugar for a getAttribute() call)
382 * made macros callable when sandbox mode is enabled
383 * added an exception when a macro uses a reserved name
384 * the "default" filter now uses the "empty" test instead of just checking for null
385 * added the "empty" test
386
387* 0.9.10 (2010-12-16)
388
389Backward incompatibilities:
390
391 * The Escaper extension is enabled by default, which means that all displayed
392 variables are now automatically escaped. You can revert to the previous
393 behavior by removing the extension via $env->removeExtension('escaper')
394 or just set the 'autoescape' option to 'false'.
395 * removed the "without loop" attribute for the "for" tag (not needed anymore
396 as the Optimizer take care of that for most cases)
397 * arrays and hashes have now a different syntax
398 * arrays keep the same syntax with square brackets: [1, 2]
399 * hashes now use curly braces (["a": "b"] should now be written as {"a": "b"})
400 * support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1})
401 * the i18n extension is now part of the Twig Extensions repository
402
403Changes:
404
405 * added the merge filter
406 * removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead
407 * fixed usage of operators as method names (like is, in, and not)
408 * changed the order of execution for node visitors
409 * fixed default() filter behavior when used with strict_variables set to on
410 * fixed filesystem loader compatibility with PHAR files
411 * enhanced error messages when an unexpected token is parsed in an expression
412 * fixed filename not being added to syntax error messages
413 * added the autoescape option to enable/disable autoescaping
414 * removed the newline after a comment (mimics PHP behavior)
415 * added a syntax error exception when parent block is used on a template that does not extend another one
416 * made the Escaper extension enabled by default
417 * fixed sandbox extension when used with auto output escaping
418 * fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved)
419 * added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters)
420 * added priority to node visitors
421
422* 0.9.9 (2010-11-28)
423
424Backward incompatibilities:
425 * the self special variable has been renamed to _self
426 * the odd and even filters are now tests:
427 {{ foo|odd }} must now be written {{ foo is odd }}
428 * the "safe" filter has been renamed to "raw"
429 * in Node classes,
430 sub-nodes are now accessed via getNode() (instead of property access)
431 attributes via getAttribute() (instead of array access)
432 * the urlencode filter had been renamed to url_encode
433 * the include tag now merges the passed variables with the current context by default
434 (the old behavior is still possible by adding the "only" keyword)
435 * moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime)
436 * removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead)
437 * the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }})
438
439Changes:
440 * added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template
441 * changed trans tag to accept any variable for the plural count
442 * fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements)
443 * added the ** (power) operator
444 * changed the algorithm used for parsing expressions
445 * added the spaceless tag
446 * removed trim_blocks option
447 * added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar())
448 * changed all exceptions to extend Twig_Error
449 * fixed unary expressions ({{ not(1 or 0) }})
450 * fixed child templates (with an extend tag) that uses one or more imports
451 * added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }})
452 * escaping has been rewritten
453 * the implementation of template inheritance has been rewritten
454 (blocks can now be called individually and still work with inheritance)
455 * fixed error handling for if tag when a syntax error occurs within a subparse process
456 * added a way to implement custom logic for resolving token parsers given a tag name
457 * fixed js escaper to be stricter (now uses a whilelist-based js escaper)
458 * added the following filers: "constant", "trans", "replace", "json_encode"
459 * added a "constant" test
460 * fixed objects with __toString() not being autoescaped
461 * fixed subscript expressions when calling __call() (methods now keep the case)
462 * added "test" feature (accessible via the "is" operator)
463 * removed the debug tag (should be done in an extension)
464 * fixed trans tag when no vars are used in plural form
465 * fixed race condition when writing template cache
466 * added the special _charset variable to reference the current charset
467 * added the special _context variable to reference the current context
468 * renamed self to _self (to avoid conflict)
469 * fixed Twig_Template::getAttribute() for protected properties
470
471* 0.9.8 (2010-06-28)
472
473Backward incompatibilities:
474 * the trans tag plural count is now attached to the plural tag:
475 old: `{% trans count %}...{% plural %}...{% endtrans %}`
476 new: `{% trans %}...{% plural count %}...{% endtrans %}`
477
478 * added a way to translate strings coming from a variable ({% trans var %})
479 * fixed trans tag when used with the Escaper extension
480 * fixed default cache umask
481 * removed Twig_Template instances from the debug tag output
482 * fixed objects with __isset() defined
483 * fixed set tag when used with a capture
484 * fixed type hinting for Twig_Environment::addFilter() method
485
486* 0.9.7 (2010-06-12)
487
488Backward incompatibilities:
489 * changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %})
490 * removed the sandboxed attribute of the include tag (use the new sandbox tag instead)
491 * refactored the Node system (if you have custom nodes, you will have to update them to use the new API)
492
493 * added self as a special variable that refers to the current template (useful for importing macros from the current template)
494 * added Twig_Template instance support to the include tag
495 * added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %})
496 * added a grammar sub-framework to ease the creation of custom tags
497 * fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface)
498 * removed the Twig_Resource::resolveMissingFilter() method
499 * fixed the filter tag which did not apply filtering to included files
500 * added a bunch of unit tests
501 * added a bunch of phpdoc
502 * added a sandbox tag in the sandbox extension
503 * changed the date filter to support any date format supported by DateTime
504 * added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default)
505 * added the lexer, parser, and compiler as arguments to the Twig_Environment constructor
506 * changed the cache option to only accepts an explicit path to a cache directory or false
507 * added a way to add token parsers, filters, and visitors without creating an extension
508 * added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface
509 * changed the generated code to match the new coding standards
510 * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }})
511 * added an exception when a child template has a non-empty body (as it is always ignored when rendering)
512
513* 0.9.6 (2010-05-12)
514
515 * fixed variables defined outside a loop and for which the value changes in a for loop
516 * fixed the test suite for PHP 5.2 and older versions of PHPUnit
517 * added support for __call() in expression resolution
518 * fixed node visiting for macros (macros are now visited by visitors as any other node)
519 * fixed nested block definitions with a parent call (rarely useful but nonetheless supported now)
520 * added the cycle filter
521 * fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII
522 * added a long-syntax for the set tag ({% set foo %}...{% endset %})
523 * unit tests are now powered by PHPUnit
524 * added support for gettext via the `i18n` extension
525 * fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values
526 * added a more useful exception if an if tag is not closed properly
527 * added support for escaping strategy in the autoescape tag
528 * fixed lexer when a template has a big chunk of text between/in a block
529
530* 0.9.5 (2010-01-20)
531
532As for any new release, don't forget to remove all cached templates after
533upgrading.
534
535If you have defined custom filters, you MUST upgrade them for this release. To
536upgrade, replace "array" with "new Twig_Filter_Function", and replace the
537environment constant by the "needs_environment" option:
538
539 // before
540 'even' => array('twig_is_even_filter', false),
541 'escape' => array('twig_escape_filter', true),
542
543 // after
544 'even' => new Twig_Filter_Function('twig_is_even_filter'),
545 'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)),
546
547If you have created NodeTransformer classes, you will need to upgrade them to
548the new interface (please note that the interface is not yet considered
549stable).
550
551 * fixed list nodes that did not extend the Twig_NodeListInterface
552 * added the "without loop" option to the for tag (it disables the generation of the loop variable)
553 * refactored node transformers to node visitors
554 * fixed automatic-escaping for blocks
555 * added a way to specify variables to pass to an included template
556 * changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules)
557 * improved the filter system to allow object methods to be used as filters
558 * changed the Array and String loaders to actually make use of the cache mechanism
559 * included the default filter function definitions in the extension class files directly (Core, Escaper)
560 * added the // operator (like the floor() PHP function)
561 * added the .. operator (as a syntactic sugar for the range filter when the step is 1)
562 * added the in operator (as a syntactic sugar for the in filter)
563 * added the following filters in the Core extension: in, range
564 * added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes)
565 * enhanced some error messages to provide better feedback in case of parsing errors
566
567* 0.9.4 (2009-12-02)
568
569If you have custom loaders, you MUST upgrade them for this release: The
570Twig_Loader base class has been removed, and the Twig_LoaderInterface has also
571been changed (see the source code for more information or the documentation).
572
573 * added support for DateTime instances for the date filter
574 * fixed loop.last when the array only has one item
575 * made it possible to insert newlines in tag and variable blocks
576 * fixed a bug when a literal '\n' were present in a template text
577 * fixed bug when the filename of a template contains */
578 * refactored loaders
579
580* 0.9.3 (2009-11-11)
581
582This release is NOT backward compatible with the previous releases.
583
584 The loaders do not take the cache and autoReload arguments anymore. Instead,
585 the Twig_Environment class has two new options: cache and auto_reload.
586 Upgrading your code means changing this kind of code:
587
588 $loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true);
589 $twig = new Twig_Environment($loader);
590
591 to something like this:
592
593 $loader = new Twig_Loader_Filesystem('/path/to/templates');
594 $twig = new Twig_Environment($loader, array(
595 'cache' => '/path/to/compilation_cache',
596 'auto_reload' => true,
597 ));
598
599 * deprecated the "items" filter as it is not needed anymore
600 * made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader
601 * optimized template loading speed
602 * removed output when an error occurs in a template and render() is used
603 * made major speed improvements for loops (up to 300% on even the smallest loops)
604 * added properties as part of the sandbox mode
605 * added public properties support (obj.item can now be the item property on the obj object)
606 * extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} )
607 * fixed bug when \ was used in HTML
608
609* 0.9.2 (2009-10-29)
610
611 * made some speed optimizations
612 * changed the cache extension to .php
613 * added a js escaping strategy
614 * added support for short block tag
615 * changed the filter tag to allow chained filters
616 * made lexer more flexible as you can now change the default delimiters
617 * added set tag
618 * changed default directory permission when cache dir does not exist (more secure)
619 * added macro support
620 * changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance
621 * made Twig_Autoloader::autoload() a static method
622 * avoid writing template file if an error occurs
623 * added $ escaping when outputting raw strings
624 * enhanced some error messages to ease debugging
625 * fixed empty cache files when the template contains an error
626
627* 0.9.1 (2009-10-14)
628
629 * fixed a bug in PHP 5.2.6
630 * fixed numbers with one than one decimal
631 * added support for method calls with arguments ({{ foo.bar('a', 43) }})
632 * made small speed optimizations
633 * made minor tweaks to allow better extensibility and flexibility
634
635* 0.9.0 (2009-10-12)
636
637 * Initial release
diff --git a/vendor/twig/twig/LICENSE b/vendor/twig/twig/LICENSE
new file mode 100644
index 00000000..3384cc55
--- /dev/null
+++ b/vendor/twig/twig/LICENSE
@@ -0,0 +1,31 @@
1Copyright (c) 2009-2013 by the Twig Team, see AUTHORS for more details.
2
3Some rights reserved.
4
5Redistribution and use in source and binary forms, with or without
6modification, are permitted provided that the following conditions are
7met:
8
9 * Redistributions of source code must retain the above copyright
10 notice, this list of conditions and the following disclaimer.
11
12 * Redistributions in binary form must reproduce the above
13 copyright notice, this list of conditions and the following
14 disclaimer in the documentation and/or other materials provided
15 with the distribution.
16
17 * The names of the contributors may not be used to endorse or
18 promote products derived from this software without specific
19 prior written permission.
20
21THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
25OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
28DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
29THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
30(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
31OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/twig/twig/README.markdown b/vendor/twig/twig/README.markdown
new file mode 100644
index 00000000..88d6fabc
--- /dev/null
+++ b/vendor/twig/twig/README.markdown
@@ -0,0 +1,17 @@
1Twig, the flexible, fast, and secure template language for PHP
2==============================================================
3
4[![Build Status](https://secure.travis-ci.org/fabpot/Twig.png?branch=master)](http://travis-ci.org/fabpot/Twig)
5
6Twig is a template language for PHP, released under the new BSD license (code
7and documentation).
8
9Twig uses a syntax similar to the Django and Jinja template languages which
10inspired the Twig runtime environment.
11
12More Information
13----------------
14
15Read the [documentation][1] for more information.
16
17[1]: http://twig.sensiolabs.org/documentation
diff --git a/vendor/twig/twig/composer.json b/vendor/twig/twig/composer.json
new file mode 100644
index 00000000..67a08aad
--- /dev/null
+++ b/vendor/twig/twig/composer.json
@@ -0,0 +1,31 @@
1{
2 "name": "twig/twig",
3 "type": "library",
4 "description": "Twig, the flexible, fast, and secure template language for PHP",
5 "keywords": ["templating"],
6 "homepage": "http://twig.sensiolabs.org",
7 "license": "BSD-3-Clause",
8 "authors": [
9 {
10 "name": "Fabien Potencier",
11 "email": "fabien@symfony.com"
12 },
13 {
14 "name": "Armin Ronacher",
15 "email": "armin.ronacher@active-4.com"
16 }
17 ],
18 "require": {
19 "php": ">=5.2.4"
20 },
21 "autoload": {
22 "psr-0" : {
23 "Twig_" : "lib/"
24 }
25 },
26 "extra": {
27 "branch-alias": {
28 "dev-master": "1.13-dev"
29 }
30 }
31}
diff --git a/vendor/twig/twig/doc/advanced.rst b/vendor/twig/twig/doc/advanced.rst
new file mode 100644
index 00000000..e1945ebb
--- /dev/null
+++ b/vendor/twig/twig/doc/advanced.rst
@@ -0,0 +1,829 @@
1Extending Twig
2==============
3
4.. caution::
5
6 This section describes how to extend Twig as of **Twig 1.12**. If you are
7 using an older version, read the :doc:`legacy<advanced_legacy>` chapter
8 instead.
9
10Twig can be extended in many ways; you can add extra tags, filters, tests,
11operators, global variables, and functions. You can even extend the parser
12itself with node visitors.
13
14.. note::
15
16 The first section of this chapter describes how to extend Twig easily. If
17 you want to reuse your changes in different projects or if you want to
18 share them with others, you should then create an extension as described
19 in the following section.
20
21.. caution::
22
23 When extending Twig without creating an extension, Twig won't be able to
24 recompile your templates when the PHP code is updated. To see your changes
25 in real-time, either disable template caching or package your code into an
26 extension (see the next section of this chapter).
27
28Before extending Twig, you must understand the differences between all the
29different possible extension points and when to use them.
30
31First, remember that Twig has two main language constructs:
32
33* ``{{ }}``: used to print the result of an expression evaluation;
34
35* ``{% %}``: used to execute statements.
36
37To understand why Twig exposes so many extension points, let's see how to
38implement a *Lorem ipsum* generator (it needs to know the number of words to
39generate).
40
41You can use a ``lipsum`` *tag*:
42
43.. code-block:: jinja
44
45 {% lipsum 40 %}
46
47That works, but using a tag for ``lipsum`` is not a good idea for at least
48three main reasons:
49
50* ``lipsum`` is not a language construct;
51* The tag outputs something;
52* The tag is not flexible as you cannot use it in an expression:
53
54 .. code-block:: jinja
55
56 {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
57
58In fact, you rarely need to create tags; and that's good news because tags are
59the most complex extension point of Twig.
60
61Now, let's use a ``lipsum`` *filter*:
62
63.. code-block:: jinja
64
65 {{ 40|lipsum }}
66
67Again, it works, but it looks weird. A filter transforms the passed value to
68something else but here we use the value to indicate the number of words to
69generate (so, ``40`` is an argument of the filter, not the value we want to
70transform).
71
72Next, let's use a ``lipsum`` *function*:
73
74.. code-block:: jinja
75
76 {{ lipsum(40) }}
77
78Here we go. For this specific example, the creation of a function is the
79extension point to use. And you can use it anywhere an expression is accepted:
80
81.. code-block:: jinja
82
83 {{ 'some text' ~ lipsum(40) ~ 'some more text' }}
84
85 {% set lipsum = lipsum(40) %}
86
87Last but not the least, you can also use a *global* object with a method able
88to generate lorem ipsum text:
89
90.. code-block:: jinja
91
92 {{ text.lipsum(40) }}
93
94As a rule of thumb, use functions for frequently used features and global
95objects for everything else.
96
97Keep in mind the following when you want to extend Twig:
98
99========== ========================== ========== =========================
100What? Implementation difficulty? How often? When?
101========== ========================== ========== =========================
102*macro* trivial frequent Content generation
103*global* trivial frequent Helper object
104*function* trivial frequent Content generation
105*filter* trivial frequent Value transformation
106*tag* complex rare DSL language construct
107*test* trivial rare Boolean decision
108*operator* trivial rare Values transformation
109========== ========================== ========== =========================
110
111Globals
112-------
113
114A global variable is like any other template variable, except that it's
115available in all templates and macros::
116
117 $twig = new Twig_Environment($loader);
118 $twig->addGlobal('text', new Text());
119
120You can then use the ``text`` variable anywhere in a template:
121
122.. code-block:: jinja
123
124 {{ text.lipsum(40) }}
125
126Filters
127-------
128
129Creating a filter is as simple as associating a name with a PHP callable::
130
131 // an anonymous function
132 $filter = new Twig_SimpleFilter('rot13', function ($string) {
133 return str_rot13($string);
134 });
135
136 // or a simple PHP function
137 $filter = new Twig_SimpleFilter('rot13', 'str_rot13');
138
139 // or a class method
140 $filter = new Twig_SimpleFilter('rot13', array('SomeClass', 'rot13Filter'));
141
142The first argument passed to the ``Twig_SimpleFilter`` constructor is the name
143of the filter you will use in templates and the second one is the PHP callable
144to associate with it.
145
146Then, add the filter to your Twig environment::
147
148 $twig = new Twig_Environment($loader);
149 $twig->addFilter($filter);
150
151And here is how to use it in a template:
152
153.. code-block:: jinja
154
155 {{ 'Twig'|rot13 }}
156
157 {# will output Gjvt #}
158
159When called by Twig, the PHP callable receives the left side of the filter
160(before the pipe ``|``) as the first argument and the extra arguments passed
161to the filter (within parentheses ``()``) as extra arguments.
162
163For instance, the following code:
164
165.. code-block:: jinja
166
167 {{ 'TWIG'|lower }}
168 {{ now|date('d/m/Y') }}
169
170is compiled to something like the following::
171
172 <?php echo strtolower('TWIG') ?>
173 <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
174
175The ``Twig_SimpleFilter`` class takes an array of options as its last
176argument::
177
178 $filter = new Twig_SimpleFilter('rot13', 'str_rot13', $options);
179
180Environment aware Filters
181~~~~~~~~~~~~~~~~~~~~~~~~~
182
183If you want to access the current environment instance in your filter, set the
184``needs_environment`` option to ``true``; Twig will pass the current
185environment as the first argument to the filter call::
186
187 $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $string) {
188 // get the current charset for instance
189 $charset = $env->getCharset();
190
191 return str_rot13($string);
192 }, array('needs_environment' => true));
193
194Context aware Filters
195~~~~~~~~~~~~~~~~~~~~~
196
197If you want to access the current context in your filter, set the
198``needs_context`` option to ``true``; Twig will pass the current context as
199the first argument to the filter call (or the second one if
200``needs_environment`` is also set to ``true``)::
201
202 $filter = new Twig_SimpleFilter('rot13', function ($context, $string) {
203 // ...
204 }, array('needs_context' => true));
205
206 $filter = new Twig_SimpleFilter('rot13', function (Twig_Environment $env, $context, $string) {
207 // ...
208 }, array('needs_context' => true, 'needs_environment' => true));
209
210Automatic Escaping
211~~~~~~~~~~~~~~~~~~
212
213If automatic escaping is enabled, the output of the filter may be escaped
214before printing. If your filter acts as an escaper (or explicitly outputs html
215or JavaScript code), you will want the raw output to be printed. In such a
216case, set the ``is_safe`` option::
217
218 $filter = new Twig_SimpleFilter('nl2br', 'nl2br', array('is_safe' => array('html')));
219
220Some filters may need to work on input that is already escaped or safe, for
221example when adding (safe) html tags to originally unsafe output. In such a
222case, set the ``pre_escape`` option to escape the input data before it is run
223through your filter::
224
225 $filter = new Twig_SimpleFilter('somefilter', 'somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
226
227Dynamic Filters
228~~~~~~~~~~~~~~~
229
230A filter name containing the special ``*`` character is a dynamic filter as
231the ``*`` can be any string::
232
233 $filter = new Twig_SimpleFilter('*_path', function ($name, $arguments) {
234 // ...
235 });
236
237The following filters will be matched by the above defined dynamic filter:
238
239* ``product_path``
240* ``category_path``
241
242A dynamic filter can define more than one dynamic parts::
243
244 $filter = new Twig_SimpleFilter('*_path_*', function ($name, $suffix, $arguments) {
245 // ...
246 });
247
248The filter will receive all dynamic part values before the normal filter
249arguments, but after the environment and the context. For instance, a call to
250``'foo'|a_path_b()`` will result in the following arguments to be passed to
251the filter: ``('a', 'b', 'foo')``.
252
253Functions
254---------
255
256Functions are defined in the exact same way as filters, but you need to create
257an instance of ``Twig_SimpleFunction``::
258
259 $twig = new Twig_Environment($loader);
260 $function = new Twig_SimpleFunction('function_name', function () {
261 // ...
262 });
263 $twig->addFunction($function);
264
265Functions support the same features as filters, except for the ``pre_escape``
266and ``preserves_safety`` options.
267
268Tests
269-----
270
271Tests are defined in the exact same way as filters and functions, but you need
272to create an instance of ``Twig_SimpleTest``::
273
274 $twig = new Twig_Environment($loader);
275 $test = new Twig_SimpleTest('test_name', function () {
276 // ...
277 });
278 $twig->addTest($test);
279
280Tests allow you to create custom application specific logic for evaluating
281boolean conditions. As a simple, example let's create a Twig test that checks if
282objects are 'red'::
283
284 $twig = new Twig_Environment($loader)
285 $test = new Twig_SimpleTest('red', function ($value) {
286 if (isset($value->color) && $value->color == 'red') {
287 return true;
288 }
289 if (isset($value->paint) && $value->paint == 'red') {
290 return true;
291 }
292 return false;
293 });
294 $twig->addTest($test);
295
296Test functions should always return true/false.
297
298When creating tests you can use the ``node_class`` option to provide custom test
299compilation. This is useful if your test can be compiled into PHP primitives.
300This is used by many of the tests built into Twig::
301
302 $twig = new Twig_Environment($loader)
303 $test = new Twig_SimpleTest(
304 'odd',
305 null,
306 array('node_class' => 'Twig_Node_Expression_Test_Odd'));
307 $twig->addTest($test);
308
309 class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
310 {
311 public function compile(Twig_Compiler $compiler)
312 {
313 $compiler
314 ->raw('(')
315 ->subcompile($this->getNode('node'))
316 ->raw(' % 2 == 1')
317 ->raw(')')
318 ;
319 }
320 }
321
322The above example, shows how you can create tests that use a node class. The
323node class has access to one sub-node called 'node'. This sub-node contains the
324value that is being tested. When the ``odd`` filter is used in code like:
325
326.. code-block:: jinja
327
328 {% if my_value is odd %}
329
330The ``node`` sub-node will contain an expression of ``my_value``. Node based
331tests also have access to the ``arguments`` node. This node will contain the
332various other arguments that have been provided to your test.
333
334Tags
335----
336
337One of the most exciting feature of a template engine like Twig is the
338possibility to define new language constructs. This is also the most complex
339feature as you need to understand how Twig's internals work.
340
341Let's create a simple ``set`` tag that allows the definition of simple
342variables from within a template. The tag can be used like follows:
343
344.. code-block:: jinja
345
346 {% set name = "value" %}
347
348 {{ name }}
349
350 {# should output value #}
351
352.. note::
353
354 The ``set`` tag is part of the Core extension and as such is always
355 available. The built-in version is slightly more powerful and supports
356 multiple assignments by default (cf. the template designers chapter for
357 more information).
358
359Three steps are needed to define a new tag:
360
361* Defining a Token Parser class (responsible for parsing the template code);
362
363* Defining a Node class (responsible for converting the parsed code to PHP);
364
365* Registering the tag.
366
367Registering a new tag
368~~~~~~~~~~~~~~~~~~~~~
369
370Adding a tag is as simple as calling the ``addTokenParser`` method on the
371``Twig_Environment`` instance::
372
373 $twig = new Twig_Environment($loader);
374 $twig->addTokenParser(new Project_Set_TokenParser());
375
376Defining a Token Parser
377~~~~~~~~~~~~~~~~~~~~~~~
378
379Now, let's see the actual code of this class::
380
381 class Project_Set_TokenParser extends Twig_TokenParser
382 {
383 public function parse(Twig_Token $token)
384 {
385 $parser = $this->parser;
386 $stream = $parser->getStream();
387
388 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
389 $stream->expect(Twig_Token::OPERATOR_TYPE, '=');
390 $value = $parser->getExpressionParser()->parseExpression();
391 $stream->expect(Twig_Token::BLOCK_END_TYPE);
392
393 return new Project_Set_Node($name, $value, $token->getLine(), $this->getTag());
394 }
395
396 public function getTag()
397 {
398 return 'set';
399 }
400 }
401
402The ``getTag()`` method must return the tag we want to parse, here ``set``.
403
404The ``parse()`` method is invoked whenever the parser encounters a ``set``
405tag. It should return a ``Twig_Node`` instance that represents the node (the
406``Project_Set_Node`` calls creating is explained in the next section).
407
408The parsing process is simplified thanks to a bunch of methods you can call
409from the token stream (``$this->parser->getStream()``):
410
411* ``getCurrent()``: Gets the current token in the stream.
412
413* ``next()``: Moves to the next token in the stream, *but returns the old one*.
414
415* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
416 the current token is of a particular type or value (or both). The value may be an
417 array of several possible values.
418
419* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
420 type/value a syntax error is thrown. Otherwise, if the type and value are correct,
421 the token is returned and the stream moves to the next token.
422
423* ``look()``: Looks a the next token without consuming it.
424
425Parsing expressions is done by calling the ``parseExpression()`` like we did for
426the ``set`` tag.
427
428.. tip::
429
430 Reading the existing ``TokenParser`` classes is the best way to learn all
431 the nitty-gritty details of the parsing process.
432
433Defining a Node
434~~~~~~~~~~~~~~~
435
436The ``Project_Set_Node`` class itself is rather simple::
437
438 class Project_Set_Node extends Twig_Node
439 {
440 public function __construct($name, Twig_Node_Expression $value, $line, $tag = null)
441 {
442 parent::__construct(array('value' => $value), array('name' => $name), $line, $tag);
443 }
444
445 public function compile(Twig_Compiler $compiler)
446 {
447 $compiler
448 ->addDebugInfo($this)
449 ->write('$context[\''.$this->getAttribute('name').'\'] = ')
450 ->subcompile($this->getNode('value'))
451 ->raw(";\n")
452 ;
453 }
454 }
455
456The compiler implements a fluid interface and provides methods that helps the
457developer generate beautiful and readable PHP code:
458
459* ``subcompile()``: Compiles a node.
460
461* ``raw()``: Writes the given string as is.
462
463* ``write()``: Writes the given string by adding indentation at the beginning
464 of each line.
465
466* ``string()``: Writes a quoted string.
467
468* ``repr()``: Writes a PHP representation of a given value (see
469 ``Twig_Node_For`` for a usage example).
470
471* ``addDebugInfo()``: Adds the line of the original template file related to
472 the current node as a comment.
473
474* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
475 usage example).
476
477* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
478 usage example).
479
480.. _creating_extensions:
481
482Creating an Extension
483---------------------
484
485The main motivation for writing an extension is to move often used code into a
486reusable class like adding support for internationalization. An extension can
487define tags, filters, tests, operators, global variables, functions, and node
488visitors.
489
490Creating an extension also makes for a better separation of code that is
491executed at compilation time and code needed at runtime. As such, it makes
492your code faster.
493
494Most of the time, it is useful to create a single extension for your project,
495to host all the specific tags and filters you want to add to Twig.
496
497.. tip::
498
499 When packaging your code into an extension, Twig is smart enough to
500 recompile your templates whenever you make a change to it (when
501 ``auto_reload`` is enabled).
502
503.. note::
504
505 Before writing your own extensions, have a look at the Twig official
506 extension repository: http://github.com/fabpot/Twig-extensions.
507
508An extension is a class that implements the following interface::
509
510 interface Twig_ExtensionInterface
511 {
512 /**
513 * Initializes the runtime environment.
514 *
515 * This is where you can load some file that contains filter functions for instance.
516 *
517 * @param Twig_Environment $environment The current Twig_Environment instance
518 */
519 function initRuntime(Twig_Environment $environment);
520
521 /**
522 * Returns the token parser instances to add to the existing list.
523 *
524 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
525 */
526 function getTokenParsers();
527
528 /**
529 * Returns the node visitor instances to add to the existing list.
530 *
531 * @return array An array of Twig_NodeVisitorInterface instances
532 */
533 function getNodeVisitors();
534
535 /**
536 * Returns a list of filters to add to the existing list.
537 *
538 * @return array An array of filters
539 */
540 function getFilters();
541
542 /**
543 * Returns a list of tests to add to the existing list.
544 *
545 * @return array An array of tests
546 */
547 function getTests();
548
549 /**
550 * Returns a list of functions to add to the existing list.
551 *
552 * @return array An array of functions
553 */
554 function getFunctions();
555
556 /**
557 * Returns a list of operators to add to the existing list.
558 *
559 * @return array An array of operators
560 */
561 function getOperators();
562
563 /**
564 * Returns a list of global variables to add to the existing list.
565 *
566 * @return array An array of global variables
567 */
568 function getGlobals();
569
570 /**
571 * Returns the name of the extension.
572 *
573 * @return string The extension name
574 */
575 function getName();
576 }
577
578To keep your extension class clean and lean, it can inherit from the built-in
579``Twig_Extension`` class instead of implementing the whole interface. That
580way, you just need to implement the ``getName()`` method as the
581``Twig_Extension`` provides empty implementations for all other methods.
582
583The ``getName()`` method must return a unique identifier for your extension.
584
585Now, with this information in mind, let's create the most basic extension
586possible::
587
588 class Project_Twig_Extension extends Twig_Extension
589 {
590 public function getName()
591 {
592 return 'project';
593 }
594 }
595
596.. note::
597
598 Of course, this extension does nothing for now. We will customize it in
599 the next sections.
600
601Twig does not care where you save your extension on the filesystem, as all
602extensions must be registered explicitly to be available in your templates.
603
604You can register an extension by using the ``addExtension()`` method on your
605main ``Environment`` object::
606
607 $twig = new Twig_Environment($loader);
608 $twig->addExtension(new Project_Twig_Extension());
609
610Of course, you need to first load the extension file by either using
611``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
612
613.. tip::
614
615 The bundled extensions are great examples of how extensions work.
616
617Globals
618~~~~~~~
619
620Global variables can be registered in an extension via the ``getGlobals()``
621method::
622
623 class Project_Twig_Extension extends Twig_Extension
624 {
625 public function getGlobals()
626 {
627 return array(
628 'text' => new Text(),
629 );
630 }
631
632 // ...
633 }
634
635Functions
636~~~~~~~~~
637
638Functions can be registered in an extension via the ``getFunctions()``
639method::
640
641 class Project_Twig_Extension extends Twig_Extension
642 {
643 public function getFunctions()
644 {
645 return array(
646 new Twig_SimpleFunction('lipsum', 'generate_lipsum'),
647 );
648 }
649
650 // ...
651 }
652
653Filters
654~~~~~~~
655
656To add a filter to an extension, you need to override the ``getFilters()``
657method. This method must return an array of filters to add to the Twig
658environment::
659
660 class Project_Twig_Extension extends Twig_Extension
661 {
662 public function getFilters()
663 {
664 return array(
665 new Twig_SimpleFilter('rot13', 'str_rot13'),
666 );
667 }
668
669 // ...
670 }
671
672Tags
673~~~~
674
675Adding a tag in an extension can be done by overriding the
676``getTokenParsers()`` method. This method must return an array of tags to add
677to the Twig environment::
678
679 class Project_Twig_Extension extends Twig_Extension
680 {
681 public function getTokenParsers()
682 {
683 return array(new Project_Set_TokenParser());
684 }
685
686 // ...
687 }
688
689In the above code, we have added a single new tag, defined by the
690``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
691responsible for parsing the tag and compiling it to PHP.
692
693Operators
694~~~~~~~~~
695
696The ``getOperators()`` methods allows to add new operators. Here is how to add
697``!``, ``||``, and ``&&`` operators::
698
699 class Project_Twig_Extension extends Twig_Extension
700 {
701 public function getOperators()
702 {
703 return array(
704 array(
705 '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
706 ),
707 array(
708 '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
709 '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
710 ),
711 );
712 }
713
714 // ...
715 }
716
717Tests
718~~~~~
719
720The ``getTests()`` methods allows to add new test functions::
721
722 class Project_Twig_Extension extends Twig_Extension
723 {
724 public function getTests()
725 {
726 return array(
727 new Twig_SimpleTest('even', 'twig_test_even'),
728 );
729 }
730
731 // ...
732 }
733
734Overloading
735-----------
736
737To overload an already defined filter, test, operator, global variable, or
738function, define it again **as late as possible**::
739
740 $twig = new Twig_Environment($loader);
741 $twig->addFilter(new Twig_SimpleFilter('date', function ($timestamp, $format = 'F j, Y H:i') {
742 // do something different from the built-in date filter
743 }));
744
745Here, we have overloaded the built-in ``date`` filter with a custom one.
746
747That also works with an extension::
748
749 class MyCoreExtension extends Twig_Extension
750 {
751 public function getFilters()
752 {
753 return array(
754 new Twig_SimpleFilter('date', array($this, 'dateFilter')),
755 );
756 }
757
758 public function dateFilter($timestamp, $format = 'F j, Y H:i')
759 {
760 // do something different from the built-in date filter
761 }
762
763 public function getName()
764 {
765 return 'project';
766 }
767 }
768
769 $twig = new Twig_Environment($loader);
770 $twig->addExtension(new MyCoreExtension());
771
772.. caution::
773
774 Note that overloading the built-in Twig elements is not recommended as it
775 might be confusing.
776
777Testing an Extension
778--------------------
779
780Functional Tests
781~~~~~~~~~~~~~~~~
782
783You can create functional tests for extensions simply by creating the
784following file structure in your test directory::
785
786 Fixtures/
787 filters/
788 foo.test
789 bar.test
790 functions/
791 foo.test
792 bar.test
793 tags/
794 foo.test
795 bar.test
796 IntegrationTest.php
797
798The ``IntegrationTest.php`` file should look like this::
799
800 class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
801 {
802 public function getExtensions()
803 {
804 return array(
805 new Project_Twig_Extension1(),
806 new Project_Twig_Extension2(),
807 );
808 }
809
810 public function getFixturesDir()
811 {
812 return dirname(__FILE__).'/Fixtures/';
813 }
814 }
815
816Fixtures examples can be found within the Twig repository
817`tests/Twig/Fixtures`_ directory.
818
819Node Tests
820~~~~~~~~~~
821
822Testing the node visitors can be complex, so extend your test cases from
823``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
824`tests/Twig/Node`_ directory.
825
826.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
827.. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
828.. _`tests/Twig/Fixtures`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Fixtures
829.. _`tests/Twig/Node`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Node
diff --git a/vendor/twig/twig/doc/advanced_legacy.rst b/vendor/twig/twig/doc/advanced_legacy.rst
new file mode 100644
index 00000000..3d34f932
--- /dev/null
+++ b/vendor/twig/twig/doc/advanced_legacy.rst
@@ -0,0 +1,887 @@
1Extending Twig
2==============
3
4.. caution::
5
6 This section describes how to extends Twig for versions **older than
7 1.12**. If you are using a newer version, read the :doc:`newer<advanced>`
8 chapter instead.
9
10Twig can be extended in many ways; you can add extra tags, filters, tests,
11operators, global variables, and functions. You can even extend the parser
12itself with node visitors.
13
14.. note::
15
16 The first section of this chapter describes how to extend Twig easily. If
17 you want to reuse your changes in different projects or if you want to
18 share them with others, you should then create an extension as described
19 in the following section.
20
21.. caution::
22
23 When extending Twig by calling methods on the Twig environment instance,
24 Twig won't be able to recompile your templates when the PHP code is
25 updated. To see your changes in real-time, either disable template caching
26 or package your code into an extension (see the next section of this
27 chapter).
28
29Before extending Twig, you must understand the differences between all the
30different possible extension points and when to use them.
31
32First, remember that Twig has two main language constructs:
33
34* ``{{ }}``: used to print the result of an expression evaluation;
35
36* ``{% %}``: used to execute statements.
37
38To understand why Twig exposes so many extension points, let's see how to
39implement a *Lorem ipsum* generator (it needs to know the number of words to
40generate).
41
42You can use a ``lipsum`` *tag*:
43
44.. code-block:: jinja
45
46 {% lipsum 40 %}
47
48That works, but using a tag for ``lipsum`` is not a good idea for at least
49three main reasons:
50
51* ``lipsum`` is not a language construct;
52* The tag outputs something;
53* The tag is not flexible as you cannot use it in an expression:
54
55 .. code-block:: jinja
56
57 {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }}
58
59In fact, you rarely need to create tags; and that's good news because tags are
60the most complex extension point of Twig.
61
62Now, let's use a ``lipsum`` *filter*:
63
64.. code-block:: jinja
65
66 {{ 40|lipsum }}
67
68Again, it works, but it looks weird. A filter transforms the passed value to
69something else but here we use the value to indicate the number of words to
70generate (so, ``40`` is an argument of the filter, not the value we want to
71transform).
72
73Next, let's use a ``lipsum`` *function*:
74
75.. code-block:: jinja
76
77 {{ lipsum(40) }}
78
79Here we go. For this specific example, the creation of a function is the
80extension point to use. And you can use it anywhere an expression is accepted:
81
82.. code-block:: jinja
83
84 {{ 'some text' ~ ipsum(40) ~ 'some more text' }}
85
86 {% set ipsum = ipsum(40) %}
87
88Last but not the least, you can also use a *global* object with a method able
89to generate lorem ipsum text:
90
91.. code-block:: jinja
92
93 {{ text.lipsum(40) }}
94
95As a rule of thumb, use functions for frequently used features and global
96objects for everything else.
97
98Keep in mind the following when you want to extend Twig:
99
100========== ========================== ========== =========================
101What? Implementation difficulty? How often? When?
102========== ========================== ========== =========================
103*macro* trivial frequent Content generation
104*global* trivial frequent Helper object
105*function* trivial frequent Content generation
106*filter* trivial frequent Value transformation
107*tag* complex rare DSL language construct
108*test* trivial rare Boolean decision
109*operator* trivial rare Values transformation
110========== ========================== ========== =========================
111
112Globals
113-------
114
115A global variable is like any other template variable, except that it's
116available in all templates and macros::
117
118 $twig = new Twig_Environment($loader);
119 $twig->addGlobal('text', new Text());
120
121You can then use the ``text`` variable anywhere in a template:
122
123.. code-block:: jinja
124
125 {{ text.lipsum(40) }}
126
127Filters
128-------
129
130A filter is a regular PHP function or an object method that takes the left
131side of the filter (before the pipe ``|``) as first argument and the extra
132arguments passed to the filter (within parentheses ``()``) as extra arguments.
133
134Defining a filter is as easy as associating the filter name with a PHP
135callable. For instance, let's say you have the following code in a template:
136
137.. code-block:: jinja
138
139 {{ 'TWIG'|lower }}
140
141When compiling this template to PHP, Twig looks for the PHP callable
142associated with the ``lower`` filter. The ``lower`` filter is a built-in Twig
143filter, and it is simply mapped to the PHP ``strtolower()`` function. After
144compilation, the generated PHP code is roughly equivalent to:
145
146.. code-block:: html+php
147
148 <?php echo strtolower('TWIG') ?>
149
150As you can see, the ``'TWIG'`` string is passed as a first argument to the PHP
151function.
152
153A filter can also take extra arguments like in the following example:
154
155.. code-block:: jinja
156
157 {{ now|date('d/m/Y') }}
158
159In this case, the extra arguments are passed to the function after the main
160argument, and the compiled code is equivalent to:
161
162.. code-block:: html+php
163
164 <?php echo twig_date_format_filter($now, 'd/m/Y') ?>
165
166Let's see how to create a new filter.
167
168In this section, we will create a ``rot13`` filter, which should return the
169`rot13`_ transformation of a string. Here is an example of its usage and the
170expected output:
171
172.. code-block:: jinja
173
174 {{ "Twig"|rot13 }}
175
176 {# should displays Gjvt #}
177
178Adding a filter is as simple as calling the ``addFilter()`` method on the
179``Twig_Environment`` instance::
180
181 $twig = new Twig_Environment($loader);
182 $twig->addFilter('rot13', new Twig_Filter_Function('str_rot13'));
183
184The second argument of ``addFilter()`` is an instance of ``Twig_Filter``.
185Here, we use ``Twig_Filter_Function`` as the filter is a PHP function. The
186first argument passed to the ``Twig_Filter_Function`` constructor is the name
187of the PHP function to call, here ``str_rot13``, a native PHP function.
188
189Let's say I now want to be able to add a prefix before the converted string:
190
191.. code-block:: jinja
192
193 {{ "Twig"|rot13('prefix_') }}
194
195 {# should displays prefix_Gjvt #}
196
197As the PHP ``str_rot13()`` function does not support this requirement, let's
198create a new PHP function::
199
200 function project_compute_rot13($string, $prefix = '')
201 {
202 return $prefix.str_rot13($string);
203 }
204
205As you can see, the ``prefix`` argument of the filter is passed as an extra
206argument to the ``project_compute_rot13()`` function.
207
208Adding this filter is as easy as before::
209
210 $twig->addFilter('rot13', new Twig_Filter_Function('project_compute_rot13'));
211
212For better encapsulation, a filter can also be defined as a static method of a
213class. The ``Twig_Filter_Function`` class can also be used to register such
214static methods as filters::
215
216 $twig->addFilter('rot13', new Twig_Filter_Function('SomeClass::rot13Filter'));
217
218.. tip::
219
220 In an extension, you can also define a filter as a static method of the
221 extension class.
222
223Environment aware Filters
224~~~~~~~~~~~~~~~~~~~~~~~~~
225
226The ``Twig_Filter`` classes take options as their last argument. For instance,
227if you want access to the current environment instance in your filter, set the
228``needs_environment`` option to ``true``::
229
230 $filter = new Twig_Filter_Function('str_rot13', array('needs_environment' => true));
231
232Twig will then pass the current environment as the first argument to the
233filter call::
234
235 function twig_compute_rot13(Twig_Environment $env, $string)
236 {
237 // get the current charset for instance
238 $charset = $env->getCharset();
239
240 return str_rot13($string);
241 }
242
243Automatic Escaping
244~~~~~~~~~~~~~~~~~~
245
246If automatic escaping is enabled, the output of the filter may be escaped
247before printing. If your filter acts as an escaper (or explicitly outputs html
248or javascript code), you will want the raw output to be printed. In such a
249case, set the ``is_safe`` option::
250
251 $filter = new Twig_Filter_Function('nl2br', array('is_safe' => array('html')));
252
253Some filters may need to work on input that is already escaped or safe, for
254example when adding (safe) html tags to originally unsafe output. In such a
255case, set the ``pre_escape`` option to escape the input data before it is run
256through your filter::
257
258 $filter = new Twig_Filter_Function('somefilter', array('pre_escape' => 'html', 'is_safe' => array('html')));
259
260Dynamic Filters
261~~~~~~~~~~~~~~~
262
263.. versionadded:: 1.5
264 Dynamic filters support was added in Twig 1.5.
265
266A filter name containing the special ``*`` character is a dynamic filter as
267the ``*`` can be any string::
268
269 $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
270
271 function twig_path($name, $arguments)
272 {
273 // ...
274 }
275
276The following filters will be matched by the above defined dynamic filter:
277
278* ``product_path``
279* ``category_path``
280
281A dynamic filter can define more than one dynamic parts::
282
283 $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
284
285 function twig_path($name, $suffix, $arguments)
286 {
287 // ...
288 }
289
290The filter will receive all dynamic part values before the normal filters
291arguments. For instance, a call to ``'foo'|a_path_b()`` will result in the
292following PHP call: ``twig_path('a', 'b', 'foo')``.
293
294Functions
295---------
296
297A function is a regular PHP function or an object method that can be called from
298templates.
299
300.. code-block:: jinja
301
302 {{ constant("DATE_W3C") }}
303
304When compiling this template to PHP, Twig looks for the PHP callable
305associated with the ``constant`` function. The ``constant`` function is a built-in Twig
306function, and it is simply mapped to the PHP ``constant()`` function. After
307compilation, the generated PHP code is roughly equivalent to:
308
309.. code-block:: html+php
310
311 <?php echo constant('DATE_W3C') ?>
312
313Adding a function is similar to adding a filter. This can be done by calling the
314``addFunction()`` method on the ``Twig_Environment`` instance::
315
316 $twig = new Twig_Environment($loader);
317 $twig->addFunction('functionName', new Twig_Function_Function('someFunction'));
318
319You can also expose extension methods as functions in your templates::
320
321 // $this is an object that implements Twig_ExtensionInterface.
322 $twig = new Twig_Environment($loader);
323 $twig->addFunction('otherFunction', new Twig_Function_Method($this, 'someMethod'));
324
325Functions also support ``needs_environment`` and ``is_safe`` parameters.
326
327Dynamic Functions
328~~~~~~~~~~~~~~~~~
329
330.. versionadded:: 1.5
331 Dynamic functions support was added in Twig 1.5.
332
333A function name containing the special ``*`` character is a dynamic function
334as the ``*`` can be any string::
335
336 $twig->addFunction('*_path', new Twig_Function_Function('twig_path'));
337
338 function twig_path($name, $arguments)
339 {
340 // ...
341 }
342
343The following functions will be matched by the above defined dynamic function:
344
345* ``product_path``
346* ``category_path``
347
348A dynamic function can define more than one dynamic parts::
349
350 $twig->addFilter('*_path_*', new Twig_Filter_Function('twig_path'));
351
352 function twig_path($name, $suffix, $arguments)
353 {
354 // ...
355 }
356
357The function will receive all dynamic part values before the normal functions
358arguments. For instance, a call to ``a_path_b('foo')`` will result in the
359following PHP call: ``twig_path('a', 'b', 'foo')``.
360
361Tags
362----
363
364One of the most exciting feature of a template engine like Twig is the
365possibility to define new language constructs. This is also the most complex
366feature as you need to understand how Twig's internals work.
367
368Let's create a simple ``set`` tag that allows the definition of simple
369variables from within a template. The tag can be used like follows:
370
371.. code-block:: jinja
372
373 {% set name = "value" %}
374
375 {{ name }}
376
377 {# should output value #}
378
379.. note::
380
381 The ``set`` tag is part of the Core extension and as such is always
382 available. The built-in version is slightly more powerful and supports
383 multiple assignments by default (cf. the template designers chapter for
384 more information).
385
386Three steps are needed to define a new tag:
387
388* Defining a Token Parser class (responsible for parsing the template code);
389
390* Defining a Node class (responsible for converting the parsed code to PHP);
391
392* Registering the tag.
393
394Registering a new tag
395~~~~~~~~~~~~~~~~~~~~~
396
397Adding a tag is as simple as calling the ``addTokenParser`` method on the
398``Twig_Environment`` instance::
399
400 $twig = new Twig_Environment($loader);
401 $twig->addTokenParser(new Project_Set_TokenParser());
402
403Defining a Token Parser
404~~~~~~~~~~~~~~~~~~~~~~~
405
406Now, let's see the actual code of this class::
407
408 class Project_Set_TokenParser extends Twig_TokenParser
409 {
410 public function parse(Twig_Token $token)
411 {
412 $lineno = $token->getLine();
413 $name = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue();
414 $this->parser->getStream()->expect(Twig_Token::OPERATOR_TYPE, '=');
415 $value = $this->parser->getExpressionParser()->parseExpression();
416
417 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
418
419 return new Project_Set_Node($name, $value, $lineno, $this->getTag());
420 }
421
422 public function getTag()
423 {
424 return 'set';
425 }
426 }
427
428The ``getTag()`` method must return the tag we want to parse, here ``set``.
429
430The ``parse()`` method is invoked whenever the parser encounters a ``set``
431tag. It should return a ``Twig_Node`` instance that represents the node (the
432``Project_Set_Node`` calls creating is explained in the next section).
433
434The parsing process is simplified thanks to a bunch of methods you can call
435from the token stream (``$this->parser->getStream()``):
436
437* ``getCurrent()``: Gets the current token in the stream.
438
439* ``next()``: Moves to the next token in the stream, *but returns the old one*.
440
441* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether
442 the current token is of a particular type or value (or both). The value may be an
443 array of several possible values.
444
445* ``expect($type[, $value[, $message]])``: If the current token isn't of the given
446 type/value a syntax error is thrown. Otherwise, if the type and value are correct,
447 the token is returned and the stream moves to the next token.
448
449* ``look()``: Looks a the next token without consuming it.
450
451Parsing expressions is done by calling the ``parseExpression()`` like we did for
452the ``set`` tag.
453
454.. tip::
455
456 Reading the existing ``TokenParser`` classes is the best way to learn all
457 the nitty-gritty details of the parsing process.
458
459Defining a Node
460~~~~~~~~~~~~~~~
461
462The ``Project_Set_Node`` class itself is rather simple::
463
464 class Project_Set_Node extends Twig_Node
465 {
466 public function __construct($name, Twig_Node_Expression $value, $lineno, $tag = null)
467 {
468 parent::__construct(array('value' => $value), array('name' => $name), $lineno, $tag);
469 }
470
471 public function compile(Twig_Compiler $compiler)
472 {
473 $compiler
474 ->addDebugInfo($this)
475 ->write('$context[\''.$this->getAttribute('name').'\'] = ')
476 ->subcompile($this->getNode('value'))
477 ->raw(";\n")
478 ;
479 }
480 }
481
482The compiler implements a fluid interface and provides methods that helps the
483developer generate beautiful and readable PHP code:
484
485* ``subcompile()``: Compiles a node.
486
487* ``raw()``: Writes the given string as is.
488
489* ``write()``: Writes the given string by adding indentation at the beginning
490 of each line.
491
492* ``string()``: Writes a quoted string.
493
494* ``repr()``: Writes a PHP representation of a given value (see
495 ``Twig_Node_For`` for a usage example).
496
497* ``addDebugInfo()``: Adds the line of the original template file related to
498 the current node as a comment.
499
500* ``indent()``: Indents the generated code (see ``Twig_Node_Block`` for a
501 usage example).
502
503* ``outdent()``: Outdents the generated code (see ``Twig_Node_Block`` for a
504 usage example).
505
506.. _creating_extensions:
507
508Creating an Extension
509---------------------
510
511The main motivation for writing an extension is to move often used code into a
512reusable class like adding support for internationalization. An extension can
513define tags, filters, tests, operators, global variables, functions, and node
514visitors.
515
516Creating an extension also makes for a better separation of code that is
517executed at compilation time and code needed at runtime. As such, it makes
518your code faster.
519
520Most of the time, it is useful to create a single extension for your project,
521to host all the specific tags and filters you want to add to Twig.
522
523.. tip::
524
525 When packaging your code into an extension, Twig is smart enough to
526 recompile your templates whenever you make a change to it (when the
527 ``auto_reload`` is enabled).
528
529.. note::
530
531 Before writing your own extensions, have a look at the Twig official
532 extension repository: http://github.com/fabpot/Twig-extensions.
533
534An extension is a class that implements the following interface::
535
536 interface Twig_ExtensionInterface
537 {
538 /**
539 * Initializes the runtime environment.
540 *
541 * This is where you can load some file that contains filter functions for instance.
542 *
543 * @param Twig_Environment $environment The current Twig_Environment instance
544 */
545 function initRuntime(Twig_Environment $environment);
546
547 /**
548 * Returns the token parser instances to add to the existing list.
549 *
550 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
551 */
552 function getTokenParsers();
553
554 /**
555 * Returns the node visitor instances to add to the existing list.
556 *
557 * @return array An array of Twig_NodeVisitorInterface instances
558 */
559 function getNodeVisitors();
560
561 /**
562 * Returns a list of filters to add to the existing list.
563 *
564 * @return array An array of filters
565 */
566 function getFilters();
567
568 /**
569 * Returns a list of tests to add to the existing list.
570 *
571 * @return array An array of tests
572 */
573 function getTests();
574
575 /**
576 * Returns a list of functions to add to the existing list.
577 *
578 * @return array An array of functions
579 */
580 function getFunctions();
581
582 /**
583 * Returns a list of operators to add to the existing list.
584 *
585 * @return array An array of operators
586 */
587 function getOperators();
588
589 /**
590 * Returns a list of global variables to add to the existing list.
591 *
592 * @return array An array of global variables
593 */
594 function getGlobals();
595
596 /**
597 * Returns the name of the extension.
598 *
599 * @return string The extension name
600 */
601 function getName();
602 }
603
604To keep your extension class clean and lean, it can inherit from the built-in
605``Twig_Extension`` class instead of implementing the whole interface. That
606way, you just need to implement the ``getName()`` method as the
607``Twig_Extension`` provides empty implementations for all other methods.
608
609The ``getName()`` method must return a unique identifier for your extension.
610
611Now, with this information in mind, let's create the most basic extension
612possible::
613
614 class Project_Twig_Extension extends Twig_Extension
615 {
616 public function getName()
617 {
618 return 'project';
619 }
620 }
621
622.. note::
623
624 Of course, this extension does nothing for now. We will customize it in
625 the next sections.
626
627Twig does not care where you save your extension on the filesystem, as all
628extensions must be registered explicitly to be available in your templates.
629
630You can register an extension by using the ``addExtension()`` method on your
631main ``Environment`` object::
632
633 $twig = new Twig_Environment($loader);
634 $twig->addExtension(new Project_Twig_Extension());
635
636Of course, you need to first load the extension file by either using
637``require_once()`` or by using an autoloader (see `spl_autoload_register()`_).
638
639.. tip::
640
641 The bundled extensions are great examples of how extensions work.
642
643Globals
644~~~~~~~
645
646Global variables can be registered in an extension via the ``getGlobals()``
647method::
648
649 class Project_Twig_Extension extends Twig_Extension
650 {
651 public function getGlobals()
652 {
653 return array(
654 'text' => new Text(),
655 );
656 }
657
658 // ...
659 }
660
661Functions
662~~~~~~~~~
663
664Functions can be registered in an extension via the ``getFunctions()``
665method::
666
667 class Project_Twig_Extension extends Twig_Extension
668 {
669 public function getFunctions()
670 {
671 return array(
672 'lipsum' => new Twig_Function_Function('generate_lipsum'),
673 );
674 }
675
676 // ...
677 }
678
679Filters
680~~~~~~~
681
682To add a filter to an extension, you need to override the ``getFilters()``
683method. This method must return an array of filters to add to the Twig
684environment::
685
686 class Project_Twig_Extension extends Twig_Extension
687 {
688 public function getFilters()
689 {
690 return array(
691 'rot13' => new Twig_Filter_Function('str_rot13'),
692 );
693 }
694
695 // ...
696 }
697
698As you can see in the above code, the ``getFilters()`` method returns an array
699where keys are the name of the filters (``rot13``) and the values the
700definition of the filter (``new Twig_Filter_Function('str_rot13')``).
701
702As seen in the previous chapter, you can also define filters as static methods
703on the extension class::
704
705$twig->addFilter('rot13', new Twig_Filter_Function('Project_Twig_Extension::rot13Filter'));
706
707You can also use ``Twig_Filter_Method`` instead of ``Twig_Filter_Function``
708when defining a filter to use a method::
709
710 class Project_Twig_Extension extends Twig_Extension
711 {
712 public function getFilters()
713 {
714 return array(
715 'rot13' => new Twig_Filter_Method($this, 'rot13Filter'),
716 );
717 }
718
719 public function rot13Filter($string)
720 {
721 return str_rot13($string);
722 }
723
724 // ...
725 }
726
727The first argument of the ``Twig_Filter_Method`` constructor is always
728``$this``, the current extension object. The second one is the name of the
729method to call.
730
731Using methods for filters is a great way to package your filter without
732polluting the global namespace. This also gives the developer more flexibility
733at the cost of a small overhead.
734
735Overriding default Filters
736..........................
737
738If some default core filters do not suit your needs, you can easily override
739them by creating your own extension. Just use the same names as the one you
740want to override::
741
742 class MyCoreExtension extends Twig_Extension
743 {
744 public function getFilters()
745 {
746 return array(
747 'date' => new Twig_Filter_Method($this, 'dateFilter'),
748 // ...
749 );
750 }
751
752 public function dateFilter($timestamp, $format = 'F j, Y H:i')
753 {
754 return '...'.twig_date_format_filter($timestamp, $format);
755 }
756
757 public function getName()
758 {
759 return 'project';
760 }
761 }
762
763Here, we override the ``date`` filter with a custom one. Using this extension
764is as simple as registering the ``MyCoreExtension`` extension by calling the
765``addExtension()`` method on the environment instance::
766
767 $twig = new Twig_Environment($loader);
768 $twig->addExtension(new MyCoreExtension());
769
770Tags
771~~~~
772
773Adding a tag in an extension can be done by overriding the
774``getTokenParsers()`` method. This method must return an array of tags to add
775to the Twig environment::
776
777 class Project_Twig_Extension extends Twig_Extension
778 {
779 public function getTokenParsers()
780 {
781 return array(new Project_Set_TokenParser());
782 }
783
784 // ...
785 }
786
787In the above code, we have added a single new tag, defined by the
788``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is
789responsible for parsing the tag and compiling it to PHP.
790
791Operators
792~~~~~~~~~
793
794The ``getOperators()`` methods allows to add new operators. Here is how to add
795``!``, ``||``, and ``&&`` operators::
796
797 class Project_Twig_Extension extends Twig_Extension
798 {
799 public function getOperators()
800 {
801 return array(
802 array(
803 '!' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
804 ),
805 array(
806 '||' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
807 '&&' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
808 ),
809 );
810 }
811
812 // ...
813 }
814
815Tests
816~~~~~
817
818The ``getTests()`` methods allows to add new test functions::
819
820 class Project_Twig_Extension extends Twig_Extension
821 {
822 public function getTests()
823 {
824 return array(
825 'even' => new Twig_Test_Function('twig_test_even'),
826 );
827 }
828
829 // ...
830 }
831
832Testing an Extension
833--------------------
834
835.. versionadded:: 1.10
836 Support for functional tests was added in Twig 1.10.
837
838Functional Tests
839~~~~~~~~~~~~~~~~
840
841You can create functional tests for extensions simply by creating the
842following file structure in your test directory::
843
844 Fixtures/
845 filters/
846 foo.test
847 bar.test
848 functions/
849 foo.test
850 bar.test
851 tags/
852 foo.test
853 bar.test
854 IntegrationTest.php
855
856The ``IntegrationTest.php`` file should look like this::
857
858 class Project_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
859 {
860 public function getExtensions()
861 {
862 return array(
863 new Project_Twig_Extension1(),
864 new Project_Twig_Extension2(),
865 );
866 }
867
868 public function getFixturesDir()
869 {
870 return dirname(__FILE__).'/Fixtures/';
871 }
872 }
873
874Fixtures examples can be found within the Twig repository
875`tests/Twig/Fixtures`_ directory.
876
877Node Tests
878~~~~~~~~~~
879
880Testing the node visitors can be complex, so extend your test cases from
881``Twig_Test_NodeTestCase``. Examples can be found in the Twig repository
882`tests/Twig/Node`_ directory.
883
884.. _`spl_autoload_register()`: http://www.php.net/spl_autoload_register
885.. _`rot13`: http://www.php.net/manual/en/function.str-rot13.php
886.. _`tests/Twig/Fixtures`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Fixtures
887.. _`tests/Twig/Node`: https://github.com/fabpot/Twig/tree/master/test/Twig/Tests/Node
diff --git a/vendor/twig/twig/doc/api.rst b/vendor/twig/twig/doc/api.rst
new file mode 100644
index 00000000..cbccb0f7
--- /dev/null
+++ b/vendor/twig/twig/doc/api.rst
@@ -0,0 +1,529 @@
1Twig for Developers
2===================
3
4This chapter describes the API to Twig and not the template language. It will
5be most useful as reference to those implementing the template interface to
6the application and not those who are creating Twig templates.
7
8Basics
9------
10
11Twig uses a central object called the **environment** (of class
12``Twig_Environment``). Instances of this class are used to store the
13configuration and extensions, and are used to load templates from the file
14system or other locations.
15
16Most applications will create one ``Twig_Environment`` object on application
17initialization and use that to load templates. In some cases it's however
18useful to have multiple environments side by side, if different configurations
19are in use.
20
21The simplest way to configure Twig to load templates for your application
22looks roughly like this::
23
24 require_once '/path/to/lib/Twig/Autoloader.php';
25 Twig_Autoloader::register();
26
27 $loader = new Twig_Loader_Filesystem('/path/to/templates');
28 $twig = new Twig_Environment($loader, array(
29 'cache' => '/path/to/compilation_cache',
30 ));
31
32This will create a template environment with the default settings and a loader
33that looks up the templates in the ``/path/to/templates/`` folder. Different
34loaders are available and you can also write your own if you want to load
35templates from a database or other resources.
36
37.. note::
38
39 Notice that the second argument of the environment is an array of options.
40 The ``cache`` option is a compilation cache directory, where Twig caches
41 the compiled templates to avoid the parsing phase for sub-sequent
42 requests. It is very different from the cache you might want to add for
43 the evaluated templates. For such a need, you can use any available PHP
44 cache library.
45
46To load a template from this environment you just have to call the
47``loadTemplate()`` method which then returns a ``Twig_Template`` instance::
48
49 $template = $twig->loadTemplate('index.html');
50
51To render the template with some variables, call the ``render()`` method::
52
53 echo $template->render(array('the' => 'variables', 'go' => 'here'));
54
55.. note::
56
57 The ``display()`` method is a shortcut to output the template directly.
58
59You can also load and render the template in one fell swoop::
60
61 echo $twig->render('index.html', array('the' => 'variables', 'go' => 'here'));
62
63.. _environment_options:
64
65Environment Options
66-------------------
67
68When creating a new ``Twig_Environment`` instance, you can pass an array of
69options as the constructor second argument::
70
71 $twig = new Twig_Environment($loader, array('debug' => true));
72
73The following options are available:
74
75* ``debug``: When set to ``true``, the generated templates have a
76 ``__toString()`` method that you can use to display the generated nodes
77 (default to ``false``).
78
79* ``charset``: The charset used by the templates (default to ``utf-8``).
80
81* ``base_template_class``: The base template class to use for generated
82 templates (default to ``Twig_Template``).
83
84* ``cache``: An absolute path where to store the compiled templates, or
85 ``false`` to disable caching (which is the default).
86
87* ``auto_reload``: When developing with Twig, it's useful to recompile the
88 template whenever the source code changes. If you don't provide a value for
89 the ``auto_reload`` option, it will be determined automatically based on the
90 ``debug`` value.
91
92* ``strict_variables``: If set to ``false``, Twig will silently ignore invalid
93 variables (variables and or attributes/methods that do not exist) and
94 replace them with a ``null`` value. When set to ``true``, Twig throws an
95 exception instead (default to ``false``).
96
97* ``autoescape``: If set to ``true``, auto-escaping will be enabled by default
98 for all templates (default to ``true``). As of Twig 1.8, you can set the
99 escaping strategy to use (``html``, ``js``, ``false`` to disable).
100 As of Twig 1.9, you can set the escaping strategy to use (``css``, ``url``,
101 ``html_attr``, or a PHP callback that takes the template "filename" and must
102 return the escaping strategy to use -- the callback cannot be a function name
103 to avoid collision with built-in escaping strategies).
104
105* ``optimizations``: A flag that indicates which optimizations to apply
106 (default to ``-1`` -- all optimizations are enabled; set it to ``0`` to
107 disable).
108
109Loaders
110-------
111
112Loaders are responsible for loading templates from a resource such as the file
113system.
114
115Compilation Cache
116~~~~~~~~~~~~~~~~~
117
118All template loaders can cache the compiled templates on the filesystem for
119future reuse. It speeds up Twig a lot as templates are only compiled once; and
120the performance boost is even larger if you use a PHP accelerator such as APC.
121See the ``cache`` and ``auto_reload`` options of ``Twig_Environment`` above
122for more information.
123
124Built-in Loaders
125~~~~~~~~~~~~~~~~
126
127Here is a list of the built-in loaders Twig provides:
128
129``Twig_Loader_Filesystem``
130..........................
131
132.. versionadded:: 1.10
133 The ``prependPath()`` and support for namespaces were added in Twig 1.10.
134
135``Twig_Loader_Filesystem`` loads templates from the file system. This loader
136can find templates in folders on the file system and is the preferred way to
137load them::
138
139 $loader = new Twig_Loader_Filesystem($templateDir);
140
141It can also look for templates in an array of directories::
142
143 $loader = new Twig_Loader_Filesystem(array($templateDir1, $templateDir2));
144
145With such a configuration, Twig will first look for templates in
146``$templateDir1`` and if they do not exist, it will fallback to look for them
147in the ``$templateDir2``.
148
149You can add or prepend paths via the ``addPath()`` and ``prependPath()``
150methods::
151
152 $loader->addPath($templateDir3);
153 $loader->prependPath($templateDir4);
154
155The filesystem loader also supports namespaced templates. This allows to group
156your templates under different namespaces which have their own template paths.
157
158When using the ``setPaths()``, ``addPath()``, and ``prependPath()`` methods,
159specify the namespace as the second argument (when not specified, these
160methods act on the "main" namespace)::
161
162 $loader->addPath($templateDir, 'admin');
163
164Namespaced templates can be accessed via the special
165``@namespace_name/template_path`` notation::
166
167 $twig->render('@admin/index.html', array());
168
169``Twig_Loader_String``
170......................
171
172``Twig_Loader_String`` loads templates from strings. It's a dummy loader as
173the template reference is the template source code::
174
175 $loader = new Twig_Loader_String();
176 $twig = new Twig_Environment($loader);
177
178 echo $twig->render('Hello {{ name }}!', array('name' => 'Fabien'));
179
180This loader should only be used for unit testing as it has severe limitations:
181several tags, like ``extends`` or ``include`` do not make sense to use as the
182reference to the template is the template source code itself.
183
184``Twig_Loader_Array``
185.....................
186
187``Twig_Loader_Array`` loads a template from a PHP array. It's passed an array
188of strings bound to template names::
189
190 $loader = new Twig_Loader_Array(array(
191 'index.html' => 'Hello {{ name }}!',
192 ));
193 $twig = new Twig_Environment($loader);
194
195 echo $twig->render('index.html', array('name' => 'Fabien'));
196
197This loader is very useful for unit testing. It can also be used for small
198projects where storing all templates in a single PHP file might make sense.
199
200.. tip::
201
202 When using the ``Array`` or ``String`` loaders with a cache mechanism, you
203 should know that a new cache key is generated each time a template content
204 "changes" (the cache key being the source code of the template). If you
205 don't want to see your cache grows out of control, you need to take care
206 of clearing the old cache file by yourself.
207
208``Twig_Loader_Chain``
209.....................
210
211``Twig_Loader_Chain`` delegates the loading of templates to other loaders::
212
213 $loader1 = new Twig_Loader_Array(array(
214 'base.html' => '{% block content %}{% endblock %}',
215 ));
216 $loader2 = new Twig_Loader_Array(array(
217 'index.html' => '{% extends "base.twig" %}{% block content %}Hello {{ name }}{% endblock %}',
218 'base.html' => 'Will never be loaded',
219 ));
220
221 $loader = new Twig_Loader_Chain(array($loader1, $loader2));
222
223 $twig = new Twig_Environment($loader);
224
225When looking for a template, Twig will try each loader in turn and it will
226return as soon as the template is found. When rendering the ``index.html``
227template from the above example, Twig will load it with ``$loader2`` but the
228``base.html`` template will be loaded from ``$loader1``.
229
230``Twig_Loader_Chain`` accepts any loader that implements
231``Twig_LoaderInterface``.
232
233.. note::
234
235 You can also add loaders via the ``addLoader()`` method.
236
237Create your own Loader
238~~~~~~~~~~~~~~~~~~~~~~
239
240All loaders implement the ``Twig_LoaderInterface``::
241
242 interface Twig_LoaderInterface
243 {
244 /**
245 * Gets the source code of a template, given its name.
246 *
247 * @param string $name string The name of the template to load
248 *
249 * @return string The template source code
250 */
251 function getSource($name);
252
253 /**
254 * Gets the cache key to use for the cache for a given template name.
255 *
256 * @param string $name string The name of the template to load
257 *
258 * @return string The cache key
259 */
260 function getCacheKey($name);
261
262 /**
263 * Returns true if the template is still fresh.
264 *
265 * @param string $name The template name
266 * @param timestamp $time The last modification time of the cached template
267 */
268 function isFresh($name, $time);
269 }
270
271As an example, here is how the built-in ``Twig_Loader_String`` reads::
272
273 class Twig_Loader_String implements Twig_LoaderInterface
274 {
275 public function getSource($name)
276 {
277 return $name;
278 }
279
280 public function getCacheKey($name)
281 {
282 return $name;
283 }
284
285 public function isFresh($name, $time)
286 {
287 return false;
288 }
289 }
290
291The ``isFresh()`` method must return ``true`` if the current cached template
292is still fresh, given the last modification time, or ``false`` otherwise.
293
294.. tip::
295
296 As of Twig 1.11.0, you can also implement ``Twig_ExistsLoaderInterface``
297 to make your loader faster when used with the chain loader.
298
299Using Extensions
300----------------
301
302Twig extensions are packages that add new features to Twig. Using an
303extension is as simple as using the ``addExtension()`` method::
304
305 $twig->addExtension(new Twig_Extension_Sandbox());
306
307Twig comes bundled with the following extensions:
308
309* *Twig_Extension_Core*: Defines all the core features of Twig.
310
311* *Twig_Extension_Escaper*: Adds automatic output-escaping and the possibility
312 to escape/unescape blocks of code.
313
314* *Twig_Extension_Sandbox*: Adds a sandbox mode to the default Twig
315 environment, making it safe to evaluate untrusted code.
316
317* *Twig_Extension_Optimizer*: Optimizes the node tree before compilation.
318
319The core, escaper, and optimizer extensions do not need to be added to the
320Twig environment, as they are registered by default.
321
322Built-in Extensions
323-------------------
324
325This section describes the features added by the built-in extensions.
326
327.. tip::
328
329 Read the chapter about extending Twig to learn how to create your own
330 extensions.
331
332Core Extension
333~~~~~~~~~~~~~~
334
335The ``core`` extension defines all the core features of Twig:
336
337* :doc:`Tags <tags/index>`;
338* :doc:`Filters <filters/index>`;
339* :doc:`Functions <functions/index>`;
340* :doc:`Tests <tests/index>`.
341
342Escaper Extension
343~~~~~~~~~~~~~~~~~
344
345The ``escaper`` extension adds automatic output escaping to Twig. It defines a
346tag, ``autoescape``, and a filter, ``raw``.
347
348When creating the escaper extension, you can switch on or off the global
349output escaping strategy::
350
351 $escaper = new Twig_Extension_Escaper('html');
352 $twig->addExtension($escaper);
353
354If set to ``html``, all variables in templates are escaped (using the ``html``
355escaping strategy), except those using the ``raw`` filter:
356
357.. code-block:: jinja
358
359 {{ article.to_html|raw }}
360
361You can also change the escaping mode locally by using the ``autoescape`` tag
362(see the :doc:`autoescape<tags/autoescape>` doc for the syntax used before
363Twig 1.8):
364
365.. code-block:: jinja
366
367 {% autoescape 'html' %}
368 {{ var }}
369 {{ var|raw }} {# var won't be escaped #}
370 {{ var|escape }} {# var won't be double-escaped #}
371 {% endautoescape %}
372
373.. warning::
374
375 The ``autoescape`` tag has no effect on included files.
376
377The escaping rules are implemented as follows:
378
379* Literals (integers, booleans, arrays, ...) used in the template directly as
380 variables or filter arguments are never automatically escaped:
381
382 .. code-block:: jinja
383
384 {{ "Twig<br />" }} {# won't be escaped #}
385
386 {% set text = "Twig<br />" %}
387 {{ text }} {# will be escaped #}
388
389* Expressions which the result is always a literal or a variable marked safe
390 are never automatically escaped:
391
392 .. code-block:: jinja
393
394 {{ foo ? "Twig<br />" : "<br />Twig" }} {# won't be escaped #}
395
396 {% set text = "Twig<br />" %}
397 {{ foo ? text : "<br />Twig" }} {# will be escaped #}
398
399 {% set text = "Twig<br />" %}
400 {{ foo ? text|raw : "<br />Twig" }} {# won't be escaped #}
401
402 {% set text = "Twig<br />" %}
403 {{ foo ? text|escape : "<br />Twig" }} {# the result of the expression won't be escaped #}
404
405* Escaping is applied before printing, after any other filter is applied:
406
407 .. code-block:: jinja
408
409 {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #}
410
411* The `raw` filter should only be used at the end of the filter chain:
412
413 .. code-block:: jinja
414
415 {{ var|raw|upper }} {# will be escaped #}
416
417 {{ var|upper|raw }} {# won't be escaped #}
418
419* Automatic escaping is not applied if the last filter in the chain is marked
420 safe for the current context (e.g. ``html`` or ``js``). ``escaper`` and
421 ``escaper('html')`` are marked safe for html, ``escaper('js')`` is marked
422 safe for javascript, ``raw`` is marked safe for everything.
423
424 .. code-block:: jinja
425
426 {% autoescape 'js' %}
427 {{ var|escape('html') }} {# will be escaped for html and javascript #}
428 {{ var }} {# will be escaped for javascript #}
429 {{ var|escape('js') }} {# won't be double-escaped #}
430 {% endautoescape %}
431
432.. note::
433
434 Note that autoescaping has some limitations as escaping is applied on
435 expressions after evaluation. For instance, when working with
436 concatenation, ``{{ foo|raw ~ bar }}`` won't give the expected result as
437 escaping is applied on the result of the concatenation, not on the
438 individual variables (so, the ``raw`` filter won't have any effect here).
439
440Sandbox Extension
441~~~~~~~~~~~~~~~~~
442
443The ``sandbox`` extension can be used to evaluate untrusted code. Access to
444unsafe attributes and methods is prohibited. The sandbox security is managed
445by a policy instance. By default, Twig comes with one policy class:
446``Twig_Sandbox_SecurityPolicy``. This class allows you to white-list some
447tags, filters, properties, and methods::
448
449 $tags = array('if');
450 $filters = array('upper');
451 $methods = array(
452 'Article' => array('getTitle', 'getBody'),
453 );
454 $properties = array(
455 'Article' => array('title', 'body'),
456 );
457 $functions = array('range');
458 $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
459
460With the previous configuration, the security policy will only allow usage of
461the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be
462able to call the ``getTitle()`` and ``getBody()`` methods on ``Article``
463objects, and the ``title`` and ``body`` public properties. Everything else
464won't be allowed and will generate a ``Twig_Sandbox_SecurityError`` exception.
465
466The policy object is the first argument of the sandbox constructor::
467
468 $sandbox = new Twig_Extension_Sandbox($policy);
469 $twig->addExtension($sandbox);
470
471By default, the sandbox mode is disabled and should be enabled when including
472untrusted template code by using the ``sandbox`` tag:
473
474.. code-block:: jinja
475
476 {% sandbox %}
477 {% include 'user.html' %}
478 {% endsandbox %}
479
480You can sandbox all templates by passing ``true`` as the second argument of
481the extension constructor::
482
483 $sandbox = new Twig_Extension_Sandbox($policy, true);
484
485Optimizer Extension
486~~~~~~~~~~~~~~~~~~~
487
488The ``optimizer`` extension optimizes the node tree before compilation::
489
490 $twig->addExtension(new Twig_Extension_Optimizer());
491
492By default, all optimizations are turned on. You can select the ones you want
493to enable by passing them to the constructor::
494
495 $optimizer = new Twig_Extension_Optimizer(Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR);
496
497 $twig->addExtension($optimizer);
498
499Twig supports the following optimizations:
500
501* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_ALL``, enables all optimizations
502 (this is the default value).
503* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_NONE``, disables all optimizations.
504 This reduces the compilation time, but it can increase the execution time
505 and the consumed memory.
506* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_FOR``, optimizes the ``for`` tag by
507 removing the ``loop`` variable creation whenever possible.
508* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_RAW_FILTER``, removes the ``raw``
509 filter whenever possible.
510* ``Twig_NodeVisitor_Optimizer::OPTIMIZE_VAR_ACCESS``, simplifies the creation
511 and access of variables in the compiled templates whenever possible.
512
513Exceptions
514----------
515
516Twig can throw exceptions:
517
518* ``Twig_Error``: The base exception for all errors.
519
520* ``Twig_Error_Syntax``: Thrown to tell the user that there is a problem with
521 the template syntax.
522
523* ``Twig_Error_Runtime``: Thrown when an error occurs at runtime (when a filter
524 does not exist for instance).
525
526* ``Twig_Error_Loader``: Thrown when an error occurs during template loading.
527
528* ``Twig_Sandbox_SecurityError``: Thrown when an unallowed tag, filter, or
529 method is called in a sandboxed template.
diff --git a/vendor/twig/twig/doc/coding_standards.rst b/vendor/twig/twig/doc/coding_standards.rst
new file mode 100644
index 00000000..e0aab35e
--- /dev/null
+++ b/vendor/twig/twig/doc/coding_standards.rst
@@ -0,0 +1,101 @@
1Coding Standards
2================
3
4When writing Twig templates, we recommend you to follow these official coding
5standards:
6
7* Put one (and only one) space after the start of a delimiter (``{{``, ``{%``,
8 and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``):
9
10 .. code-block:: jinja
11
12 {{ foo }}
13 {# comment #}
14 {% if foo %}{% endif %}
15
16 When using the whitespace control character, do not put any spaces between
17 it and the delimiter:
18
19 .. code-block:: jinja
20
21 {{- foo -}}
22 {#- comment -#}
23 {%- if foo -%}{%- endif -%}
24
25* Put one (and only one) space before and after the following operators:
26 comparison operators (``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``), math
27 operators (``+``, ``-``, ``/``, ``*``, ``%``, ``//``, ``**``), logic
28 operators (``not``, ``and``, ``or``), ``~``, ``is``, ``in``, and the ternary
29 operator (``?:``):
30
31 .. code-block:: jinja
32
33 {{ 1 + 2 }}
34 {{ foo ~ bar }}
35 {{ true ? true : false }}
36
37* Put one (and only one) space after the ``:`` sign in hashes and ``,`` in
38 arrays and hashes:
39
40 .. code-block:: jinja
41
42 {{ [1, 2, 3] }}
43 {{ {'foo': 'bar'} }}
44
45* Do not put any spaces after an opening parenthesis and before a closing
46 parenthesis in expressions:
47
48 .. code-block:: jinja
49
50 {{ 1 + (2 * 3) }}
51
52* Do not put any spaces before and after string delimiters:
53
54 .. code-block:: jinja
55
56 {{ 'foo' }}
57 {{ "foo" }}
58
59* Do not put any spaces before and after the following operators: ``|``,
60 ``.``, ``..``, ``[]``:
61
62 .. code-block:: jinja
63
64 {{ foo|upper|lower }}
65 {{ user.name }}
66 {{ user[name] }}
67 {% for i in 1..12 %}{% endfor %}
68
69* Do not put any spaces before and after the parenthesis used for filter and
70 function calls:
71
72 .. code-block:: jinja
73
74 {{ foo|default('foo') }}
75 {{ range(1..10) }}
76
77* Do not put any spaces before and after the opening and the closing of arrays
78 and hashes:
79
80 .. code-block:: jinja
81
82 {{ [1, 2, 3] }}
83 {{ {'foo': 'bar'} }}
84
85* Use lower cased and underscored variable names:
86
87 .. code-block:: jinja
88
89 {% set foo = 'foo' %}
90 {% set foo_bar = 'foo' %}
91
92* Indent your code inside tags (use the same indentation as the one used for
93 the main language of the file):
94
95 .. code-block:: jinja
96
97 {% block foo %}
98 {% if true %}
99 true
100 {% endif %}
101 {% endblock %}
diff --git a/vendor/twig/twig/doc/deprecated.rst b/vendor/twig/twig/doc/deprecated.rst
new file mode 100644
index 00000000..f0a3a0f0
--- /dev/null
+++ b/vendor/twig/twig/doc/deprecated.rst
@@ -0,0 +1,98 @@
1Deprecated Features
2===================
3
4This document lists all deprecated features in Twig. Deprecated features are
5kept for backward compatibility and removed in the next major release (a
6feature that was deprecated in Twig 1.x is removed in Twig 2.0).
7
8Token Parsers
9-------------
10
11* As of Twig 1.x, the token parser broker sub-system is deprecated. The
12 following class and interface will be removed in 2.0:
13
14 * ``Twig_TokenParserBrokerInterface``
15 * ``Twig_TokenParserBroker``
16
17Extensions
18----------
19
20* As of Twig 1.x, the ability to remove an extension is deprecated and the
21 ``Twig_Environment::removeExtension()`` method will be removed in 2.0.
22
23PEAR
24----
25
26PEAR support will be discontinued in Twig 2.0, and no PEAR packages will be
27provided. Use Composer instead.
28
29Filters
30-------
31
32* As of Twig 1.x, use ``Twig_SimpleFilter`` to add a filter. The following
33 classes and interfaces will be removed in 2.0:
34
35 * ``Twig_FilterInterface``
36 * ``Twig_FilterCallableInterface``
37 * ``Twig_Filter``
38 * ``Twig_Filter_Function``
39 * ``Twig_Filter_Method``
40 * ``Twig_Filter_Node``
41
42* As of Twig 2.x, the ``Twig_SimpleFilter`` class is deprecated and will be
43 removed in Twig 3.x (use ``Twig_Filter`` instead). In Twig 2.x,
44 ``Twig_SimpleFilter`` is just an alias for ``Twig_Filter``.
45
46Functions
47---------
48
49* As of Twig 1.x, use ``Twig_SimpleFunction`` to add a function. The following
50 classes and interfaces will be removed in 2.0:
51
52 * ``Twig_FunctionInterface``
53 * ``Twig_FunctionCallableInterface``
54 * ``Twig_Function``
55 * ``Twig_Function_Function``
56 * ``Twig_Function_Method``
57 * ``Twig_Function_Node``
58
59* As of Twig 2.x, the ``Twig_SimpleFunction`` class is deprecated and will be
60 removed in Twig 3.x (use ``Twig_Function`` instead). In Twig 2.x,
61 ``Twig_SimpleFunction`` is just an alias for ``Twig_Function``.
62
63Tests
64-----
65
66* As of Twig 1.x, use ``Twig_SimpleTest`` to add a test. The following classes
67 and interfaces will be removed in 2.0:
68
69 * ``Twig_TestInterface``
70 * ``Twig_TestCallableInterface``
71 * ``Twig_Test``
72 * ``Twig_Test_Function``
73 * ``Twig_Test_Method``
74 * ``Twig_Test_Node``
75
76* As of Twig 2.x, the ``Twig_SimpleTest`` class is deprecated and will be
77 removed in Twig 3.x (use ``Twig_Test`` instead). In Twig 2.x,
78 ``Twig_SimpleTest`` is just an alias for ``Twig_Test``.
79
80Interfaces
81----------
82
83* As of Twig 2.x, the following interfaces are deprecated and empty (they will
84 be removed in Twig 3.0):
85
86* ``Twig_CompilerInterface`` (use ``Twig_Compiler`` instead)
87* ``Twig_LexerInterface`` (use ``Twig_Lexer`` instead)
88* ``Twig_NodeInterface`` (use ``Twig_Node`` instead)
89* ``Twig_ParserInterface`` (use ``Twig_Parser`` instead)
90* ``Twig_ExistsLoaderInterface`` (merged with ``Twig_LoaderInterface``)
91* ``Twig_TemplateInterface`` (use ``Twig_Template`` instead)
92
93Globals
94-------
95
96* As of Twig 2.x, the ability to register a global variable after the runtime
97 or the extensions have been initialized is not possible anymore (but
98 changing the value of an already registered global is possible).
diff --git a/vendor/twig/twig/doc/filters/abs.rst b/vendor/twig/twig/doc/filters/abs.rst
new file mode 100644
index 00000000..3a82f62e
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/abs.rst
@@ -0,0 +1,18 @@
1``abs``
2=======
3
4The ``abs`` filter returns the absolute value.
5
6.. code-block:: jinja
7
8 {# number = -5 #}
9
10 {{ number|abs }}
11
12 {# outputs 5 #}
13
14.. note::
15
16 Internally, Twig uses the PHP `abs`_ function.
17
18.. _`abs`: http://php.net/abs
diff --git a/vendor/twig/twig/doc/filters/batch.rst b/vendor/twig/twig/doc/filters/batch.rst
new file mode 100644
index 00000000..4366b57b
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/batch.rst
@@ -0,0 +1,45 @@
1``batch``
2=========
3
4.. versionadded:: 1.12.3
5 The batch filter was added in Twig 1.12.3.
6
7The ``batch`` filter "batches" items by returning a list of lists with the
8given number of items. If you provide a second parameter, it is used to fill
9missing items:
10
11.. code-block:: jinja
12
13 {% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %}
14
15 <table>
16 {% for row in items|batch(3, 'No item') %}
17 <tr>
18 {% for column in row %}
19 <td>{{ column }}</td>
20 {% endfor %}
21 </tr>
22 {% endfor %}
23 </table>
24
25The above example will be rendered as:
26
27.. code-block:: jinja
28
29 <table>
30 <tr>
31 <td>a</td>
32 <td>b</td>
33 <td>c</td>
34 </tr>
35 <tr>
36 <td>d</td>
37 <td>e</td>
38 <td>f</td>
39 </tr>
40 <tr>
41 <td>g</td>
42 <td>No item</td>
43 <td>No item</td>
44 </tr>
45 </table>
diff --git a/vendor/twig/twig/doc/filters/capitalize.rst b/vendor/twig/twig/doc/filters/capitalize.rst
new file mode 100644
index 00000000..10546a1f
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/capitalize.rst
@@ -0,0 +1,11 @@
1``capitalize``
2==============
3
4The ``capitalize`` filter capitalizes a value. The first character will be
5uppercase, all others lowercase:
6
7.. code-block:: jinja
8
9 {{ 'my first car'|capitalize }}
10
11 {# outputs 'My first car' #}
diff --git a/vendor/twig/twig/doc/filters/convert_encoding.rst b/vendor/twig/twig/doc/filters/convert_encoding.rst
new file mode 100644
index 00000000..1b0eb60c
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/convert_encoding.rst
@@ -0,0 +1,28 @@
1``convert_encoding``
2====================
3
4.. versionadded:: 1.4
5 The ``convert_encoding`` filter was added in Twig 1.4.
6
7The ``convert_encoding`` filter converts a string from one encoding to
8another. The first argument is the expected output charset and the second one
9is the input charset:
10
11.. code-block:: jinja
12
13 {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
14
15.. note::
16
17 This filter relies on the `iconv`_ or `mbstring`_ extension, so one of
18 them must be installed. In case both are installed, `mbstring`_ is used by
19 default (Twig before 1.8.1 uses `iconv`_ by default).
20
21Arguments
22---------
23
24 * ``from``: The input charset
25 * ``to``: The output charset
26
27.. _`iconv`: http://php.net/iconv
28.. _`mbstring`: http://php.net/mbstring
diff --git a/vendor/twig/twig/doc/filters/date.rst b/vendor/twig/twig/doc/filters/date.rst
new file mode 100644
index 00000000..8e2f31fa
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/date.rst
@@ -0,0 +1,88 @@
1``date``
2========
3
4.. versionadded:: 1.1
5 The timezone support has been added in Twig 1.1.
6
7.. versionadded:: 1.5
8 The default date format support has been added in Twig 1.5.
9
10.. versionadded:: 1.6.1
11 The default timezone support has been added in Twig 1.6.1.
12
13.. versionadded:: 1.11.0
14 The introduction of the false value for the timezone was introduced in Twig 1.11.0
15
16The ``date`` filter formats a date to a given format:
17
18.. code-block:: jinja
19
20 {{ post.published_at|date("m/d/Y") }}
21
22The ``date`` filter accepts strings (it must be in a format supported by the
23`strtotime`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For
24instance, to display the current date, filter the word "now":
25
26.. code-block:: jinja
27
28 {{ "now"|date("m/d/Y") }}
29
30To escape words and characters in the date format use ``\\`` in front of each
31character:
32
33.. code-block:: jinja
34
35 {{ post.published_at|date("F jS \\a\\t g:ia") }}
36
37If the value passed to the ``date`` filter is ``null``, it will return the
38current date by default. If an empty string is desired instead of the current
39date, use a ternary operator:
40
41.. code-block:: jinja
42
43 {{ post.published_at is empty ? "" : post.published_at|date("m/d/Y") }}
44
45If no format is provided, Twig will use the default one: ``F j, Y H:i``. This
46default can be easily changed by calling the ``setDateFormat()`` method on the
47``core`` extension instance. The first argument is the default format for
48dates and the second one is the default format for date intervals:
49
50.. code-block:: php
51
52 $twig = new Twig_Environment($loader);
53 $twig->getExtension('core')->setDateFormat('d/m/Y', '%d days');
54
55Timezone
56--------
57
58By default, the date is displayed by applying the default timezone (the one
59specified in php.ini or declared in Twig -- see below), but you can override
60it by explicitly specifying a timezone:
61
62.. code-block:: jinja
63
64 {{ post.published_at|date("m/d/Y", "Europe/Paris") }}
65
66If the date is already a DateTime object, and if you want to keep its current
67timezone, pass ``false`` as the timezone value:
68
69.. code-block:: jinja
70
71 {{ post.published_at|date("m/d/Y", false) }}
72
73The default timezone can also be set globally by calling ``setTimezone()``:
74
75.. code-block:: php
76
77 $twig = new Twig_Environment($loader);
78 $twig->getExtension('core')->setTimezone('Europe/Paris');
79
80Arguments
81---------
82
83 * ``format``: The date format
84 * ``timezone``: The date timezone
85
86.. _`strtotime`: http://www.php.net/strtotime
87.. _`DateTime`: http://www.php.net/DateTime
88.. _`DateInterval`: http://www.php.net/DateInterval
diff --git a/vendor/twig/twig/doc/filters/date_modify.rst b/vendor/twig/twig/doc/filters/date_modify.rst
new file mode 100644
index 00000000..6a5c73d6
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/date_modify.rst
@@ -0,0 +1,23 @@
1``date_modify``
2===============
3
4.. versionadded:: 1.9.0
5 The date_modify filter has been added in Twig 1.9.0.
6
7The ``date_modify`` filter modifies a date with a given modifier string:
8
9.. code-block:: jinja
10
11 {{ post.published_at|date_modify("+1 day")|date("m/d/Y") }}
12
13The ``date_modify`` filter accepts strings (it must be in a format supported
14by the `strtotime`_ function) or `DateTime`_ instances. You can easily combine
15it with the :doc:`date<date>` filter for formatting.
16
17Arguments
18---------
19
20 * ``modifier``: The modifier
21
22.. _`strtotime`: http://www.php.net/strtotime
23.. _`DateTime`: http://www.php.net/DateTime
diff --git a/vendor/twig/twig/doc/filters/default.rst b/vendor/twig/twig/doc/filters/default.rst
new file mode 100644
index 00000000..46ed9636
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/default.rst
@@ -0,0 +1,33 @@
1``default``
2===========
3
4The ``default`` filter returns the passed default value if the value is
5undefined or empty, otherwise the value of the variable:
6
7.. code-block:: jinja
8
9 {{ var|default('var is not defined') }}
10
11 {{ var.foo|default('foo item on var is not defined') }}
12
13 {{ var['foo']|default('foo item on var is not defined') }}
14
15 {{ ''|default('passed var is empty') }}
16
17When using the ``default`` filter on an expression that uses variables in some
18method calls, be sure to use the ``default`` filter whenever a variable can be
19undefined:
20
21.. code-block:: jinja
22
23 {{ var.method(foo|default('foo'))|default('foo') }}
24
25.. note::
26
27 Read the documentation for the :doc:`defined<../tests/defined>` and
28 :doc:`empty<../tests/empty>` tests to learn more about their semantics.
29
30Arguments
31---------
32
33 * ``default``: The default value
diff --git a/vendor/twig/twig/doc/filters/escape.rst b/vendor/twig/twig/doc/filters/escape.rst
new file mode 100644
index 00000000..5ade7d74
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/escape.rst
@@ -0,0 +1,93 @@
1``escape``
2==========
3
4.. versionadded:: 1.9.0
5 The ``css``, ``url``, and ``html_attr`` strategies were added in Twig
6 1.9.0.
7
8The ``escape`` filter escapes a string for safe insertion into the final
9output. It supports different escaping strategies depending on the template
10context.
11
12By default, it uses the HTML escaping strategy:
13
14.. code-block:: jinja
15
16 {{ user.username|escape }}
17
18For convenience, the ``e`` filter is defined as an alias:
19
20.. code-block:: jinja
21
22 {{ user.username|e }}
23
24The ``escape`` filter can also be used in other contexts than HTML thanks to
25an optional argument which defines the escaping strategy to use:
26
27.. code-block:: jinja
28
29 {{ user.username|e }}
30 {# is equivalent to #}
31 {{ user.username|e('html') }}
32
33And here is how to escape variables included in JavaScript code:
34
35.. code-block:: jinja
36
37 {{ user.username|escape('js') }}
38 {{ user.username|e('js') }}
39
40The ``escape`` filter supports the following escaping strategies:
41
42* ``html``: escapes a string for the **HTML body** context.
43
44* ``js``: escapes a string for the **JavaScript context**.
45
46* ``css``: escapes a string for the **CSS context**. CSS escaping can be
47 applied to any string being inserted into CSS and escapes everything except
48 alphanumerics.
49
50* ``url``: escapes a string for the **URI or parameter contexts**. This should
51 not be used to escape an entire URI; only a subcomponent being inserted.
52
53* ``html_attr``: escapes a string for the **HTML attribute** context.
54
55.. note::
56
57 Internally, ``escape`` uses the PHP native `htmlspecialchars`_ function
58 for the HTML escaping strategy.
59
60.. caution::
61
62 When using automatic escaping, Twig tries to not double-escape a variable
63 when the automatic escaping strategy is the same as the one applied by the
64 escape filter; but that does not work when using a variable as the
65 escaping strategy:
66
67 .. code-block:: jinja
68
69 {% set strategy = 'html' %}
70
71 {% autoescape 'html' %}
72 {{ var|escape('html') }} {# won't be double-escaped #}
73 {{ var|escape(strategy) }} {# will be double-escaped #}
74 {% endautoescape %}
75
76 When using a variable as the escaping strategy, you should disable
77 automatic escaping:
78
79 .. code-block:: jinja
80
81 {% set strategy = 'html' %}
82
83 {% autoescape 'html' %}
84 {{ var|escape(strategy)|raw }} {# won't be double-escaped #}
85 {% endautoescape %}
86
87Arguments
88---------
89
90 * ``strategy``: The escaping strategy
91 * ``charset``: The string charset
92
93.. _`htmlspecialchars`: http://php.net/htmlspecialchars
diff --git a/vendor/twig/twig/doc/filters/first.rst b/vendor/twig/twig/doc/filters/first.rst
new file mode 100644
index 00000000..4295e833
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/first.rst
@@ -0,0 +1,25 @@
1``first``
2=========
3
4.. versionadded:: 1.12.2
5 The first filter was added in Twig 1.12.2.
6
7The ``first`` filter returns the first "element" of a sequence, a mapping, or
8a string:
9
10.. code-block:: jinja
11
12 {{ [1, 2, 3, 4]|first }}
13 {# outputs 1 #}
14
15 {{ { a: 1, b: 2, c: 3, d: 4 }|first }}
16 {# outputs 1 #}
17
18 {{ '1234'|first }}
19 {# outputs 1 #}
20
21.. note::
22
23 It also works with objects implementing the `Traversable`_ interface.
24
25.. _`Traversable`: http://php.net/manual/en/class.traversable.php
diff --git a/vendor/twig/twig/doc/filters/format.rst b/vendor/twig/twig/doc/filters/format.rst
new file mode 100644
index 00000000..fe55a09e
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/format.rst
@@ -0,0 +1,16 @@
1``format``
2==========
3
4The ``format`` filter formats a given string by replacing the placeholders
5(placeholders follows the `sprintf`_ notation):
6
7.. code-block:: jinja
8
9 {{ "I like %s and %s."|format(foo, "bar") }}
10
11 {# returns I like foo and bar
12 if the foo parameter equals to the foo string. #}
13
14.. _`sprintf`: http://www.php.net/sprintf
15
16.. seealso:: :doc:`replace<replace>`
diff --git a/vendor/twig/twig/doc/filters/index.rst b/vendor/twig/twig/doc/filters/index.rst
new file mode 100644
index 00000000..b0c6b38d
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/index.rst
@@ -0,0 +1,36 @@
1Filters
2=======
3
4.. toctree::
5 :maxdepth: 1
6
7 abs
8 batch
9 capitalize
10 convert_encoding
11 date
12 date_modify
13 default
14 escape
15 first
16 format
17 join
18 json_encode
19 keys
20 last
21 length
22 lower
23 nl2br
24 number_format
25 merge
26 upper
27 raw
28 replace
29 reverse
30 slice
31 sort
32 split
33 striptags
34 title
35 trim
36 url_encode
diff --git a/vendor/twig/twig/doc/filters/join.rst b/vendor/twig/twig/doc/filters/join.rst
new file mode 100644
index 00000000..f4952421
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/join.rst
@@ -0,0 +1,23 @@
1``join``
2========
3
4The ``join`` filter returns a string which is the concatenation of the items
5of a sequence:
6
7.. code-block:: jinja
8
9 {{ [1, 2, 3]|join }}
10 {# returns 123 #}
11
12The separator between elements is an empty string per default, but you can
13define it with the optional first parameter:
14
15.. code-block:: jinja
16
17 {{ [1, 2, 3]|join('|') }}
18 {# returns 1|2|3 #}
19
20Arguments
21---------
22
23 * ``glue``: The separator
diff --git a/vendor/twig/twig/doc/filters/json_encode.rst b/vendor/twig/twig/doc/filters/json_encode.rst
new file mode 100644
index 00000000..a33fef1b
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/json_encode.rst
@@ -0,0 +1,21 @@
1``json_encode``
2===============
3
4The ``json_encode`` filter returns the JSON representation of a string:
5
6.. code-block:: jinja
7
8 {{ data|json_encode() }}
9
10.. note::
11
12 Internally, Twig uses the PHP `json_encode`_ function.
13
14Arguments
15---------
16
17 * ``options``: A bitmask of `json_encode options`_ (``{{
18 data|json_encode(constant(JSON_PRETTY_PRINT)) }}``)
19
20.. _`json_encode`: http://php.net/json_encode
21.. _`json_encode options`: http://www.php.net/manual/en/json.constants.php
diff --git a/vendor/twig/twig/doc/filters/keys.rst b/vendor/twig/twig/doc/filters/keys.rst
new file mode 100644
index 00000000..e4f090c6
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/keys.rst
@@ -0,0 +1,11 @@
1``keys``
2========
3
4The ``keys`` filter returns the keys of an array. It is useful when you want to
5iterate over the keys of an array:
6
7.. code-block:: jinja
8
9 {% for key in array|keys %}
10 ...
11 {% endfor %}
diff --git a/vendor/twig/twig/doc/filters/last.rst b/vendor/twig/twig/doc/filters/last.rst
new file mode 100644
index 00000000..723c0b57
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/last.rst
@@ -0,0 +1,25 @@
1``last``
2========
3
4.. versionadded:: 1.12.2
5 The last filter was added in Twig 1.12.2.
6
7The ``last`` filter returns the last "element" of a sequence, a mapping, or
8a string:
9
10.. code-block:: jinja
11
12 {{ [1, 2, 3, 4]|last }}
13 {# outputs 4 #}
14
15 {{ { a: 1, b: 2, c: 3, d: 4 }|last }}
16 {# outputs 4 #}
17
18 {{ '1234'|last }}
19 {# outputs 4 #}
20
21.. note::
22
23 It also works with objects implementing the `Traversable`_ interface.
24
25.. _`Traversable`: http://php.net/manual/en/class.traversable.php
diff --git a/vendor/twig/twig/doc/filters/length.rst b/vendor/twig/twig/doc/filters/length.rst
new file mode 100644
index 00000000..f79b9bdf
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/length.rst
@@ -0,0 +1,12 @@
1``length``
2==========
3
4The ``length`` filters returns the number of items of a sequence or mapping, or
5the length of a string:
6
7.. code-block:: jinja
8
9 {% if users|length > 10 %}
10 ...
11 {% endif %}
12
diff --git a/vendor/twig/twig/doc/filters/lower.rst b/vendor/twig/twig/doc/filters/lower.rst
new file mode 100644
index 00000000..ef9faa90
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/lower.rst
@@ -0,0 +1,10 @@
1``lower``
2=========
3
4The ``lower`` filter converts a value to lowercase:
5
6.. code-block:: jinja
7
8 {{ 'WELCOME'|lower }}
9
10 {# outputs 'welcome' #}
diff --git a/vendor/twig/twig/doc/filters/merge.rst b/vendor/twig/twig/doc/filters/merge.rst
new file mode 100644
index 00000000..05a2ae7d
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/merge.rst
@@ -0,0 +1,41 @@
1``merge``
2=========
3
4The ``merge`` filter merges an array with another array:
5
6.. code-block:: jinja
7
8 {% set values = [1, 2] %}
9
10 {% set values = values|merge(['apple', 'orange']) %}
11
12 {# values now contains [1, 2, 'apple', 'orange'] #}
13
14New values are added at the end of the existing ones.
15
16The ``merge`` filter also works on hashes:
17
18.. code-block:: jinja
19
20 {% set items = { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'unknown' } %}
21
22 {% set items = items|merge({ 'peugeot': 'car', 'renault': 'car' }) %}
23
24 {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'renault': 'car' } #}
25
26For hashes, the merging process occurs on the keys: if the key does not
27already exist, it is added but if the key already exists, its value is
28overridden.
29
30.. tip::
31
32 If you want to ensure that some values are defined in an array (by given
33 default values), reverse the two elements in the call:
34
35 .. code-block:: jinja
36
37 {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
38
39 {% set items = { 'apple': 'unknown' }|merge(items) %}
40
41 {# items now contains { 'apple': 'fruit', 'orange': 'fruit' } #}
diff --git a/vendor/twig/twig/doc/filters/nl2br.rst b/vendor/twig/twig/doc/filters/nl2br.rst
new file mode 100644
index 00000000..694c6724
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/nl2br.rst
@@ -0,0 +1,22 @@
1``nl2br``
2=========
3
4.. versionadded:: 1.5
5 The nl2br filter was added in Twig 1.5.
6
7The ``nl2br`` filter inserts HTML line breaks before all newlines in a string:
8
9.. code-block:: jinja
10
11 {{ "I like Twig.\nYou will like it too."|nl2br }}
12 {# outputs
13
14 I like Twig.<br />
15 You will like it too.
16
17 #}
18
19.. note::
20
21 The ``nl2br`` filter pre-escapes the input before applying the
22 transformation.
diff --git a/vendor/twig/twig/doc/filters/number_format.rst b/vendor/twig/twig/doc/filters/number_format.rst
new file mode 100644
index 00000000..fedacd9d
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/number_format.rst
@@ -0,0 +1,45 @@
1``number_format``
2=================
3
4.. versionadded:: 1.5
5 The number_format filter was added in Twig 1.5
6
7The ``number_format`` filter formats numbers. It is a wrapper around PHP's
8`number_format`_ function:
9
10.. code-block:: jinja
11
12 {{ 200.35|number_format }}
13
14You can control the number of decimal places, decimal point, and thousands
15separator using the additional arguments:
16
17.. code-block:: jinja
18
19 {{ 9800.333|number_format(2, '.', ',') }}
20
21If no formatting options are provided then Twig will use the default formatting
22options of:
23
24- 0 decimal places.
25- ``.`` as the decimal point.
26- ``,`` as the thousands separator.
27
28These defaults can be easily changed through the core extension:
29
30.. code-block:: php
31
32 $twig = new Twig_Environment($loader);
33 $twig->getExtension('core')->setNumberFormat(3, '.', ',');
34
35The defaults set for ``number_format`` can be over-ridden upon each call using the
36additional parameters.
37
38Arguments
39---------
40
41 * ``decimal``: The number of decimal points to display
42 * ``decimal_point``: The character(s) to use for the decimal point
43 * ``decimal_sep``: The character(s) to use for the thousands separator
44
45.. _`number_format`: http://php.net/number_format
diff --git a/vendor/twig/twig/doc/filters/raw.rst b/vendor/twig/twig/doc/filters/raw.rst
new file mode 100644
index 00000000..434dd246
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/raw.rst
@@ -0,0 +1,12 @@
1``raw``
2=======
3
4The ``raw`` filter marks the value as being "safe", which means that in an
5environment with automatic escaping enabled this variable will not be escaped
6if ``raw`` is the last filter applied to it:
7
8.. code-block:: jinja
9
10 {% autoescape true %}
11 {{ var|raw }} {# var won't be escaped #}
12 {% endautoescape %}
diff --git a/vendor/twig/twig/doc/filters/replace.rst b/vendor/twig/twig/doc/filters/replace.rst
new file mode 100644
index 00000000..e961f23d
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/replace.rst
@@ -0,0 +1,19 @@
1``replace``
2===========
3
4The ``replace`` filter formats a given string by replacing the placeholders
5(placeholders are free-form):
6
7.. code-block:: jinja
8
9 {{ "I like %this% and %that%."|replace({'%this%': foo, '%that%': "bar"}) }}
10
11 {# returns I like foo and bar
12 if the foo parameter equals to the foo string. #}
13
14Arguments
15---------
16
17 * ``replace_pairs``: The placeholder values
18
19.. seealso:: :doc:`format<format>`
diff --git a/vendor/twig/twig/doc/filters/reverse.rst b/vendor/twig/twig/doc/filters/reverse.rst
new file mode 100644
index 00000000..752192b8
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/reverse.rst
@@ -0,0 +1,47 @@
1``reverse``
2===========
3
4.. versionadded:: 1.6
5 Support for strings has been added in Twig 1.6.
6
7The ``reverse`` filter reverses a sequence, a mapping, or a string:
8
9.. code-block:: jinja
10
11 {% for user in users|reverse %}
12 ...
13 {% endfor %}
14
15 {{ '1234'|reverse }}
16
17 {# outputs 4321 #}
18
19.. tip::
20
21 For sequences and mappings, numeric keys are not preserved. To reverse
22 them as well, pass ``true`` as an argument to the ``reverse`` filter:
23
24 .. code-block:: jinja
25
26 {% for key, value in {1: "a", 2: "b", 3: "c"}|reverse %}
27 {{ key }}: {{ value }}
28 {%- endfor %}
29
30 {# output: 0: c 1: b 2: a #}
31
32 {% for key, value in {1: "a", 2: "b", 3: "c"}|reverse(true) %}
33 {{ key }}: {{ value }}
34 {%- endfor %}
35
36 {# output: 3: c 2: b 1: a #}
37
38.. note::
39
40 It also works with objects implementing the `Traversable`_ interface.
41
42Arguments
43---------
44
45 * ``preserve_keys``: Preserve keys when reversing a mapping or a sequence.
46
47.. _`Traversable`: http://php.net/Traversable
diff --git a/vendor/twig/twig/doc/filters/slice.rst b/vendor/twig/twig/doc/filters/slice.rst
new file mode 100644
index 00000000..dbd5db37
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/slice.rst
@@ -0,0 +1,70 @@
1``slice``
2===========
3
4.. versionadded:: 1.6
5 The slice filter was added in Twig 1.6.
6
7The ``slice`` filter extracts a slice of a sequence, a mapping, or a string:
8
9.. code-block:: jinja
10
11 {% for i in [1, 2, 3, 4, 5]|slice(1, 2) %}
12 {# will iterate over 2 and 3 #}
13 {% endfor %}
14
15 {{ '12345'|slice(1, 2) }}
16
17 {# outputs 23 #}
18
19You can use any valid expression for both the start and the length:
20
21.. code-block:: jinja
22
23 {% for i in [1, 2, 3, 4, 5]|slice(start, length) %}
24 {# ... #}
25 {% endfor %}
26
27As syntactic sugar, you can also use the ``[]`` notation:
28
29.. code-block:: jinja
30
31 {% for i in [1, 2, 3, 4, 5][start:length] %}
32 {# ... #}
33 {% endfor %}
34
35 {{ '12345'[1:2] }}
36
37 {# you can omit the first argument -- which is the same as 0 #}
38 {{ '12345'[:2] }} {# will display "12" #}
39
40 {# you can omit the last argument -- which will select everything till the end #}
41 {{ '12345'[2:] }} {# will display "345" #}
42
43The ``slice`` filter works as the `array_slice`_ PHP function for arrays and
44`substr`_ for strings.
45
46If the start is non-negative, the sequence will start at that start in the
47variable. If start is negative, the sequence will start that far from the end
48of the variable.
49
50If length is given and is positive, then the sequence will have up to that
51many elements in it. If the variable is shorter than the length, then only the
52available variable elements will be present. If length is given and is
53negative then the sequence will stop that many elements from the end of the
54variable. If it is omitted, then the sequence will have everything from offset
55up until the end of the variable.
56
57.. note::
58
59 It also works with objects implementing the `Traversable`_ interface.
60
61Arguments
62---------
63
64 * ``start``: The start of the slice
65 * ``length``: The size of the slice
66 * ``preserve_keys``: Whether to preserve key or not (when the input is an array)
67
68.. _`Traversable`: http://php.net/manual/en/class.traversable.php
69.. _`array_slice`: http://php.net/array_slice
70.. _`substr`: http://php.net/substr
diff --git a/vendor/twig/twig/doc/filters/sort.rst b/vendor/twig/twig/doc/filters/sort.rst
new file mode 100644
index 00000000..33311528
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/sort.rst
@@ -0,0 +1,17 @@
1``sort``
2========
3
4The ``sort`` filter sorts an array:
5
6.. code-block:: jinja
7
8 {% for user in users|sort %}
9 ...
10 {% endfor %}
11
12.. note::
13
14 Internally, Twig uses the PHP `asort`_ function to maintain index
15 association.
16
17.. _`asort`: http://php.net/asort
diff --git a/vendor/twig/twig/doc/filters/split.rst b/vendor/twig/twig/doc/filters/split.rst
new file mode 100644
index 00000000..7cd2ca5b
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/split.rst
@@ -0,0 +1,53 @@
1``split``
2=========
3
4.. versionadded:: 1.10.3
5 The split filter was added in Twig 1.10.3.
6
7The ``split`` filter splits a string by the given delimiter and returns a list
8of strings:
9
10.. code-block:: jinja
11
12 {{ "one,two,three"|split(',') }}
13 {# returns ['one', 'two', 'three'] #}
14
15You can also pass a ``limit`` argument:
16
17 * If ``limit`` is positive, the returned array will contain a maximum of
18 limit elements with the last element containing the rest of string;
19
20 * If ``limit`` is negative, all components except the last -limit are
21 returned;
22
23 * If ``limit`` is zero, then this is treated as 1.
24
25.. code-block:: jinja
26
27 {{ "one,two,three,four,five"|split(',', 3) }}
28 {# returns ['one', 'two', 'three,four,five'] #}
29
30If the ``delimiter`` is an empty string, then value will be split by equal
31chunks. Length is set by the ``limit`` argument (one character by default).
32
33.. code-block:: jinja
34
35 {{ "123"|split('') }}
36 {# returns ['1', '2', '3'] #}
37
38 {{ "aabbcc"|split('', 2) }}
39 {# returns ['aa', 'bb', 'cc'] #}
40
41.. note::
42
43 Internally, Twig uses the PHP `explode`_ or `str_split`_ (if delimiter is
44 empty) functions for string splitting.
45
46Arguments
47---------
48
49 * ``delimiter``: The delimiter
50 * ``limit``: The limit argument
51
52.. _`explode`: http://php.net/explode
53.. _`str_split`: http://php.net/str_split
diff --git a/vendor/twig/twig/doc/filters/striptags.rst b/vendor/twig/twig/doc/filters/striptags.rst
new file mode 100644
index 00000000..72c6f252
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/striptags.rst
@@ -0,0 +1,15 @@
1``striptags``
2=============
3
4The ``striptags`` filter strips SGML/XML tags and replace adjacent whitespace
5by one space:
6
7.. code-block:: jinja
8
9 {{ some_html|striptags }}
10
11.. note::
12
13 Internally, Twig uses the PHP `strip_tags`_ function.
14
15.. _`strip_tags`: http://php.net/strip_tags
diff --git a/vendor/twig/twig/doc/filters/title.rst b/vendor/twig/twig/doc/filters/title.rst
new file mode 100644
index 00000000..c5a318e8
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/title.rst
@@ -0,0 +1,11 @@
1``title``
2=========
3
4The ``title`` filter returns a titlecased version of the value. Words will
5start with uppercase letters, all remaining characters are lowercase:
6
7.. code-block:: jinja
8
9 {{ 'my first car'|title }}
10
11 {# outputs 'My First Car' #}
diff --git a/vendor/twig/twig/doc/filters/trim.rst b/vendor/twig/twig/doc/filters/trim.rst
new file mode 100644
index 00000000..f38afd55
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/trim.rst
@@ -0,0 +1,29 @@
1``trim``
2========
3
4.. versionadded:: 1.6.2
5 The trim filter was added in Twig 1.6.2.
6
7The ``trim`` filter strips whitespace (or other characters) from the beginning
8and end of a string:
9
10.. code-block:: jinja
11
12 {{ ' I like Twig. '|trim }}
13
14 {# outputs 'I like Twig.' #}
15
16 {{ ' I like Twig.'|trim('.') }}
17
18 {# outputs ' I like Twig' #}
19
20.. note::
21
22 Internally, Twig uses the PHP `trim`_ function.
23
24Arguments
25---------
26
27 * ``character_mask``: The characters to strip
28
29.. _`trim`: http://php.net/trim
diff --git a/vendor/twig/twig/doc/filters/upper.rst b/vendor/twig/twig/doc/filters/upper.rst
new file mode 100644
index 00000000..561cebe3
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/upper.rst
@@ -0,0 +1,10 @@
1``upper``
2=========
3
4The ``upper`` filter converts a value to uppercase:
5
6.. code-block:: jinja
7
8 {{ 'welcome'|upper }}
9
10 {# outputs 'WELCOME' #}
diff --git a/vendor/twig/twig/doc/filters/url_encode.rst b/vendor/twig/twig/doc/filters/url_encode.rst
new file mode 100644
index 00000000..b4f9a6ca
--- /dev/null
+++ b/vendor/twig/twig/doc/filters/url_encode.rst
@@ -0,0 +1,28 @@
1``url_encode``
2==============
3
4.. versionadded:: 1.12.3
5 Support for encoding an array as query string was added in Twig 1.12.3.
6
7The ``url_encode`` filter percent encodes a given string as URL segment
8or an array as query string:
9
10.. code-block:: jinja
11
12 {{ "path-seg*ment"|url_encode }}
13 {# outputs "path-seg%2Ament" #}
14
15 {{ "string with spaces"|url_encode(true) }}
16 {# outputs "string%20with%20spaces" #}
17
18 {{ {'param': 'value', 'foo': 'bar'}|url_encode }}
19 {# outputs "param=value&foo=bar" #}
20
21.. note::
22
23 Internally, Twig uses the PHP `urlencode`_ (or `rawurlencode`_ if you pass
24 ``true`` as the first parameter) or the `http_build_query`_ function.
25
26.. _`urlencode`: http://php.net/urlencode
27.. _`rawurlencode`: http://php.net/rawurlencode
28.. _`http_build_query`: http://php.net/http_build_query
diff --git a/vendor/twig/twig/doc/functions/attribute.rst b/vendor/twig/twig/doc/functions/attribute.rst
new file mode 100644
index 00000000..3051bdaa
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/attribute.rst
@@ -0,0 +1,18 @@
1``attribute``
2=============
3
4.. versionadded:: 1.2
5 The ``attribute`` function was added in Twig 1.2.
6
7``attribute`` can be used to access a "dynamic" attribute of a variable:
8
9.. code-block:: jinja
10
11 {{ attribute(object, method) }}
12 {{ attribute(object, method, arguments) }}
13 {{ attribute(array, item) }}
14
15.. note::
16
17 The resolution algorithm is the same as the one used for the ``.``
18 notation, except that the item can be any valid expression.
diff --git a/vendor/twig/twig/doc/functions/block.rst b/vendor/twig/twig/doc/functions/block.rst
new file mode 100644
index 00000000..fd571efb
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/block.rst
@@ -0,0 +1,15 @@
1``block``
2=========
3
4When a template uses inheritance and if you want to print a block multiple
5times, use the ``block`` function:
6
7.. code-block:: jinja
8
9 <title>{% block title %}{% endblock %}</title>
10
11 <h1>{{ block('title') }}</h1>
12
13 {% block body %}{% endblock %}
14
15.. seealso:: :doc:`extends<../tags/extends>`, :doc:`parent<../functions/parent>`
diff --git a/vendor/twig/twig/doc/functions/constant.rst b/vendor/twig/twig/doc/functions/constant.rst
new file mode 100644
index 00000000..bea0e9fc
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/constant.rst
@@ -0,0 +1,18 @@
1``constant``
2============
3
4.. versionadded: 1.12.1
5 constant now accepts object instances as the second argument.
6
7``constant`` returns the constant value for a given string:
8
9.. code-block:: jinja
10
11 {{ some_date|date(constant('DATE_W3C')) }}
12 {{ constant('Namespace\\Classname::CONSTANT_NAME') }}
13
14As of 1.12.1 you can read constants from object instances as well:
15
16.. code-block:: jinja
17
18 {{ constant('RSS', date) }}
diff --git a/vendor/twig/twig/doc/functions/cycle.rst b/vendor/twig/twig/doc/functions/cycle.rst
new file mode 100644
index 00000000..0015cae1
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/cycle.rst
@@ -0,0 +1,25 @@
1``cycle``
2=========
3
4The ``cycle`` function cycles on an array of values:
5
6.. code-block:: jinja
7
8 {% for i in 0..10 %}
9 {{ cycle(['odd', 'even'], i) }}
10 {% endfor %}
11
12The array can contain any number of values:
13
14.. code-block:: jinja
15
16 {% set fruits = ['apple', 'orange', 'citrus'] %}
17
18 {% for i in 0..10 %}
19 {{ cycle(fruits, i) }}
20 {% endfor %}
21
22Arguments
23---------
24
25 * ``position``: The cycle position
diff --git a/vendor/twig/twig/doc/functions/date.rst b/vendor/twig/twig/doc/functions/date.rst
new file mode 100644
index 00000000..f1c94819
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/date.rst
@@ -0,0 +1,52 @@
1``date``
2========
3
4.. versionadded:: 1.6
5 The date function has been added in Twig 1.6.
6
7.. versionadded:: 1.6.1
8 The default timezone support has been added in Twig 1.6.1.
9
10Converts an argument to a date to allow date comparison:
11
12.. code-block:: jinja
13
14 {% if date(user.created_at) < date('-2days') %}
15 {# do something #}
16 {% endif %}
17
18The argument must be in a format supported by the `date`_ function.
19
20You can pass a timezone as the second argument:
21
22.. code-block:: jinja
23
24 {% if date(user.created_at) < date('-2days', 'Europe/Paris') %}
25 {# do something #}
26 {% endif %}
27
28If no argument is passed, the function returns the current date:
29
30.. code-block:: jinja
31
32 {% if date(user.created_at) < date() %}
33 {# always! #}
34 {% endif %}
35
36.. note::
37
38 You can set the default timezone globally by calling ``setTimezone()`` on
39 the ``core`` extension instance:
40
41 .. code-block:: php
42
43 $twig = new Twig_Environment($loader);
44 $twig->getExtension('core')->setTimezone('Europe/Paris');
45
46Arguments
47---------
48
49 * ``date``: The date
50 * ``timezone``: The timezone
51
52.. _`date`: http://www.php.net/date
diff --git a/vendor/twig/twig/doc/functions/dump.rst b/vendor/twig/twig/doc/functions/dump.rst
new file mode 100644
index 00000000..1500b0f4
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/dump.rst
@@ -0,0 +1,69 @@
1``dump``
2========
3
4.. versionadded:: 1.5
5 The dump function was added in Twig 1.5.
6
7The ``dump`` function dumps information about a template variable. This is
8mostly useful to debug a template that does not behave as expected by
9introspecting its variables:
10
11.. code-block:: jinja
12
13 {{ dump(user) }}
14
15.. note::
16
17 The ``dump`` function is not available by default. You must add the
18 ``Twig_Extension_Debug`` extension explicitly when creating your Twig
19 environment::
20
21 $twig = new Twig_Environment($loader, array(
22 'debug' => true,
23 // ...
24 ));
25 $twig->addExtension(new Twig_Extension_Debug());
26
27 Even when enabled, the ``dump`` function won't display anything if the
28 ``debug`` option on the environment is not enabled (to avoid leaking debug
29 information on a production server).
30
31In an HTML context, wrap the output with a ``pre`` tag to make it easier to
32read:
33
34.. code-block:: jinja
35
36 <pre>
37 {{ dump(user) }}
38 </pre>
39
40.. tip::
41
42 Using a ``pre`` tag is not needed when `XDebug`_ is enabled and
43 ``html_errors`` is ``on``; as a bonus, the output is also nicer with
44 XDebug enabled.
45
46You can debug several variables by passing them as additional arguments:
47
48.. code-block:: jinja
49
50 {{ dump(user, categories) }}
51
52If you don't pass any value, all variables from the current context are
53dumped:
54
55.. code-block:: jinja
56
57 {{ dump() }}
58
59.. note::
60
61 Internally, Twig uses the PHP `var_dump`_ function.
62
63Arguments
64---------
65
66 * ``context``: The context to dump
67
68.. _`XDebug`: http://xdebug.org/docs/display
69.. _`var_dump`: http://php.net/var_dump
diff --git a/vendor/twig/twig/doc/functions/include.rst b/vendor/twig/twig/doc/functions/include.rst
new file mode 100644
index 00000000..eaddfe61
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/include.rst
@@ -0,0 +1,80 @@
1``include``
2===========
3
4.. versionadded:: 1.12
5 The include function was added in Twig 1.12.
6
7The ``include`` function returns the rendered content of a template:
8
9.. code-block:: jinja
10
11 {{ include('template.html') }}
12 {{ include(some_var) }}
13
14Included templates have access to the variables of the active context.
15
16If you are using the filesystem loader, the templates are looked for in the
17paths defined by it.
18
19The context is passed by default to the template but you can also pass
20additional variables:
21
22.. code-block:: jinja
23
24 {# template.html will have access to the variables from the current context and the additional ones provided #}
25 {{ include('template.html', {foo: 'bar'}) }}
26
27You can disable access to the context by setting ``with_context`` to
28``false``:
29
30.. code-block:: jinja
31
32 {# only the foo variable will be accessible #}
33 {{ include('template.html', {foo: 'bar'}, with_context = false) }}
34
35.. code-block:: jinja
36
37 {# no variables will be accessible #}
38 {{ include('template.html', with_context = false) }}
39
40And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
41directly::
42
43 // {{ include(template) }}
44
45 $template = $twig->loadTemplate('some_template.twig');
46
47 $twig->loadTemplate('template.twig')->display(array('template' => $template));
48
49When you set the ``ignore_missing`` flag, Twig will return an empty string if
50the template does not exist:
51
52.. code-block:: jinja
53
54 {{ include('sidebar.html', ignore_missing = true) }}
55
56You can also provide a list of templates that are checked for existence before
57inclusion. The first template that exists will be rendered:
58
59.. code-block:: jinja
60
61 {{ include(['page_detailed.html', 'page.html']) }}
62
63If ``ignore_missing`` is set, it will fall back to rendering nothing if none
64of the templates exist, otherwise it will throw an exception.
65
66When including a template created by an end user, you should consider
67sandboxing it:
68
69.. code-block:: jinja
70
71 {{ include('page.html', sandboxed = true) }}
72
73Arguments
74---------
75
76 * ``template``: The template to render
77 * ``variables``: The variables to pass to the template
78 * ``with_context``: Whether to pass the current context variables or not
79 * ``ignore_missing``: Whether to ignore missing templates or not
80 * ``sandboxed``: Whether to sandbox the template or not
diff --git a/vendor/twig/twig/doc/functions/index.rst b/vendor/twig/twig/doc/functions/index.rst
new file mode 100644
index 00000000..8650cbdb
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/index.rst
@@ -0,0 +1,17 @@
1Functions
2=========
3
4.. toctree::
5 :maxdepth: 1
6
7 attribute
8 block
9 constant
10 cycle
11 date
12 dump
13 include
14 parent
15 random
16 range
17 template_from_string
diff --git a/vendor/twig/twig/doc/functions/parent.rst b/vendor/twig/twig/doc/functions/parent.rst
new file mode 100644
index 00000000..f5bd2001
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/parent.rst
@@ -0,0 +1,20 @@
1``parent``
2==========
3
4When a template uses inheritance, it's possible to render the contents of the
5parent block when overriding a block by using the ``parent`` function:
6
7.. code-block:: jinja
8
9 {% extends "base.html" %}
10
11 {% block sidebar %}
12 <h3>Table Of Contents</h3>
13 ...
14 {{ parent() }}
15 {% endblock %}
16
17The ``parent()`` call will return the content of the ``sidebar`` block as
18defined in the ``base.html`` template.
19
20.. seealso:: :doc:`extends<../tags/extends>`, :doc:`block<../functions/block>`, :doc:`block<../tags/block>`
diff --git a/vendor/twig/twig/doc/functions/random.rst b/vendor/twig/twig/doc/functions/random.rst
new file mode 100644
index 00000000..a5a916bb
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/random.rst
@@ -0,0 +1,29 @@
1``random``
2==========
3
4.. versionadded:: 1.5
5 The random function was added in Twig 1.5.
6
7.. versionadded:: 1.6
8 String and integer handling was added in Twig 1.6.
9
10The ``random`` function returns a random value depending on the supplied
11parameter type:
12
13* a random item from a sequence;
14* a random character from a string;
15* a random integer between 0 and the integer parameter (inclusive).
16
17.. code-block:: jinja
18
19 {{ random(['apple', 'orange', 'citrus']) }} {# example output: orange #}
20 {{ random('ABC') }} {# example output: C #}
21 {{ random() }} {# example output: 15386094 (works as native PHP `mt_rand`_ function) #}
22 {{ random(5) }} {# example output: 3 #}
23
24Arguments
25---------
26
27 * ``values``: The values
28
29.. _`mt_rand`: http://php.net/mt_rand
diff --git a/vendor/twig/twig/doc/functions/range.rst b/vendor/twig/twig/doc/functions/range.rst
new file mode 100644
index 00000000..b1fa5471
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/range.rst
@@ -0,0 +1,45 @@
1``range``
2=========
3
4Returns a list containing an arithmetic progression of integers:
5
6.. code-block:: jinja
7
8 {% for i in range(0, 3) %}
9 {{ i }},
10 {% endfor %}
11
12 {# returns 0, 1, 2, 3 #}
13
14When step is given (as the third parameter), it specifies the increment (or
15decrement):
16
17.. code-block:: jinja
18
19 {% for i in range(0, 6, 2) %}
20 {{ i }},
21 {% endfor %}
22
23 {# returns 0, 2, 4, 6 #}
24
25The Twig built-in ``..`` operator is just syntactic sugar for the ``range``
26function (with a step of 1):
27
28.. code-block:: jinja
29
30 {% for i in 0..3 %}
31 {{ i }},
32 {% endfor %}
33
34.. tip::
35
36 The ``range`` function works as the native PHP `range`_ function.
37
38Arguments
39---------
40
41 * ``low``: The first value of the sequence.
42 * ``high``: The highest possible value of the sequence.
43 * ``step``: The increment between elements of the sequence.
44
45.. _`range`: http://php.net/range
diff --git a/vendor/twig/twig/doc/functions/template_from_string.rst b/vendor/twig/twig/doc/functions/template_from_string.rst
new file mode 100644
index 00000000..bbb06d86
--- /dev/null
+++ b/vendor/twig/twig/doc/functions/template_from_string.rst
@@ -0,0 +1,32 @@
1``template_from_string``
2========================
3
4.. versionadded:: 1.11
5 The template_from_string function was added in Twig 1.11.
6
7The ``template_from_string`` function loads a template from a string:
8
9.. code-block:: jinja
10
11 {{ include(template_from_string("Hello {{ name }}") }}
12 {{ include(template_from_string(page.template)) }}
13
14.. note::
15
16 The ``template_from_string`` function is not available by default. You
17 must add the ``Twig_Extension_StringLoader`` extension explicitly when
18 creating your Twig environment::
19
20 $twig = new Twig_Environment(...);
21 $twig->addExtension(new Twig_Extension_StringLoader());
22
23.. note::
24
25 Even if you will probably always use the ``template_from_string`` function
26 with the ``include`` function, you can use it with any tag or function that
27 takes a template as an argument (like the ``embed`` or ``extends`` tags).
28
29Arguments
30---------
31
32 * ``template``: The template
diff --git a/vendor/twig/twig/doc/index.rst b/vendor/twig/twig/doc/index.rst
new file mode 100644
index 00000000..3e5166c6
--- /dev/null
+++ b/vendor/twig/twig/doc/index.rst
@@ -0,0 +1,18 @@
1Twig
2====
3
4.. toctree::
5 :maxdepth: 2
6
7 intro
8 templates
9 api
10 advanced
11 internals
12 recipes
13 coding_standards
14 tags/index
15 filters/index
16 functions/index
17 tests/index
18 deprecated
diff --git a/vendor/twig/twig/doc/internals.rst b/vendor/twig/twig/doc/internals.rst
new file mode 100644
index 00000000..79a3c8d5
--- /dev/null
+++ b/vendor/twig/twig/doc/internals.rst
@@ -0,0 +1,140 @@
1Twig Internals
2==============
3
4Twig is very extensible and you can easily hack it. Keep in mind that you
5should probably try to create an extension before hacking the core, as most
6features and enhancements can be done with extensions. This chapter is also
7useful for people who want to understand how Twig works under the hood.
8
9How Twig works?
10---------------
11
12The rendering of a Twig template can be summarized into four key steps:
13
14* **Load** the template: If the template is already compiled, load it and go
15 to the *evaluation* step, otherwise:
16
17 * First, the **lexer** tokenizes the template source code into small pieces
18 for easier processing;
19 * Then, the **parser** converts the token stream into a meaningful tree
20 of nodes (the Abstract Syntax Tree);
21 * Eventually, the *compiler* transforms the AST into PHP code;
22
23* **Evaluate** the template: It basically means calling the ``display()``
24 method of the compiled template and passing it the context.
25
26The Lexer
27---------
28
29The lexer tokenizes a template source code into a token stream (each token is
30an instance of ``Twig_Token``, and the stream is an instance of
31``Twig_TokenStream``). The default lexer recognizes 13 different token types:
32
33* ``Twig_Token::BLOCK_START_TYPE``, ``Twig_Token::BLOCK_END_TYPE``: Delimiters for blocks (``{% %}``)
34* ``Twig_Token::VAR_START_TYPE``, ``Twig_Token::VAR_END_TYPE``: Delimiters for variables (``{{ }}``)
35* ``Twig_Token::TEXT_TYPE``: A text outside an expression;
36* ``Twig_Token::NAME_TYPE``: A name in an expression;
37* ``Twig_Token::NUMBER_TYPE``: A number in an expression;
38* ``Twig_Token::STRING_TYPE``: A string in an expression;
39* ``Twig_Token::OPERATOR_TYPE``: An operator;
40* ``Twig_Token::PUNCTUATION_TYPE``: A punctuation sign;
41* ``Twig_Token::INTERPOLATION_START_TYPE``, ``Twig_Token::INTERPOLATION_END_TYPE`` (as of Twig 1.5): Delimiters for string interpolation;
42* ``Twig_Token::EOF_TYPE``: Ends of template.
43
44You can manually convert a source code into a token stream by calling the
45``tokenize()`` of an environment::
46
47 $stream = $twig->tokenize($source, $identifier);
48
49As the stream has a ``__toString()`` method, you can have a textual
50representation of it by echoing the object::
51
52 echo $stream."\n";
53
54Here is the output for the ``Hello {{ name }}`` template:
55
56.. code-block:: text
57
58 TEXT_TYPE(Hello )
59 VAR_START_TYPE()
60 NAME_TYPE(name)
61 VAR_END_TYPE()
62 EOF_TYPE()
63
64.. note::
65
66 You can change the default lexer use by Twig (``Twig_Lexer``) by calling
67 the ``setLexer()`` method::
68
69 $twig->setLexer($lexer);
70
71The Parser
72----------
73
74The parser converts the token stream into an AST (Abstract Syntax Tree), or a
75node tree (an instance of ``Twig_Node_Module``). The core extension defines
76the basic nodes like: ``for``, ``if``, ... and the expression nodes.
77
78You can manually convert a token stream into a node tree by calling the
79``parse()`` method of an environment::
80
81 $nodes = $twig->parse($stream);
82
83Echoing the node object gives you a nice representation of the tree::
84
85 echo $nodes."\n";
86
87Here is the output for the ``Hello {{ name }}`` template:
88
89.. code-block:: text
90
91 Twig_Node_Module(
92 Twig_Node_Text(Hello )
93 Twig_Node_Print(
94 Twig_Node_Expression_Name(name)
95 )
96 )
97
98.. note::
99
100 The default parser (``Twig_TokenParser``) can be also changed by calling the
101 ``setParser()`` method::
102
103 $twig->setParser($parser);
104
105The Compiler
106------------
107
108The last step is done by the compiler. It takes a node tree as an input and
109generates PHP code usable for runtime execution of the template.
110
111You can call the compiler by hand with the ``compile()`` method of an
112environment::
113
114 $php = $twig->compile($nodes);
115
116The ``compile()`` method returns the PHP source code representing the node.
117
118The generated template for a ``Hello {{ name }}`` template reads as follows
119(the actual output can differ depending on the version of Twig you are
120using)::
121
122 /* Hello {{ name }} */
123 class __TwigTemplate_1121b6f109fe93ebe8c6e22e3712bceb extends Twig_Template
124 {
125 protected function doDisplay(array $context, array $blocks = array())
126 {
127 // line 1
128 echo "Hello ";
129 echo twig_escape_filter($this->env, $this->getContext($context, "name"), "ndex", null, true);
130 }
131
132 // some more code
133 }
134
135.. note::
136
137 As for the lexer and the parser, the default compiler (``Twig_Compiler``) can
138 be changed by calling the ``setCompiler()`` method::
139
140 $twig->setCompiler($compiler);
diff --git a/vendor/twig/twig/doc/intro.rst b/vendor/twig/twig/doc/intro.rst
new file mode 100644
index 00000000..bdcdb8a0
--- /dev/null
+++ b/vendor/twig/twig/doc/intro.rst
@@ -0,0 +1,164 @@
1Introduction
2============
3
4This is the documentation for Twig, the flexible, fast, and secure template
5engine for PHP.
6
7If you have any exposure to other text-based template languages, such as
8Smarty, Django, or Jinja, you should feel right at home with Twig. It's both
9designer and developer friendly by sticking to PHP's principles and adding
10functionality useful for templating environments.
11
12The key-features are...
13
14* *Fast*: Twig compiles templates down to plain optimized PHP code. The
15 overhead compared to regular PHP code was reduced to the very minimum.
16
17* *Secure*: Twig has a sandbox mode to evaluate untrusted template code. This
18 allows Twig to be used as a template language for applications where users
19 may modify the template design.
20
21* *Flexible*: Twig is powered by a flexible lexer and parser. This allows the
22 developer to define its own custom tags and filters, and create its own DSL.
23
24Prerequisites
25-------------
26
27Twig needs at least **PHP 5.2.4** to run.
28
29Installation
30------------
31
32You have multiple ways to install Twig.
33
34Installing via Composer (recommended)
35~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
36
371. Install composer in your project:
38
39.. code-block:: bash
40
41 curl -s http://getcomposer.org/installer | php
42
432. Create a ``composer.json`` file in your project root:
44
45.. code-block:: javascript
46
47 {
48 "require": {
49 "twig/twig": "1.*"
50 }
51 }
52
533. Install via composer
54
55.. code-block:: bash
56
57 php composer.phar install
58
59.. note::
60 If you want to learn more about Composer, the ``composer.json`` file syntax
61 and its usage, you can read the `online documentation`_.
62
63Installing from the tarball release
64~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
65
661. Download the most recent tarball from the `download page`_
672. Unpack the tarball
683. Move the files somewhere in your project
69
70Installing the development version
71~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
72
731. Install Git
742. ``git clone git://github.com/fabpot/Twig.git``
75
76Installing the PEAR package
77~~~~~~~~~~~~~~~~~~~~~~~~~~~
78
791. Install PEAR
802. ``pear channel-discover pear.twig-project.org``
813. ``pear install twig/Twig`` (or ``pear install twig/Twig-beta``)
82
83
84Installing the C extension
85~~~~~~~~~~~~~~~~~~~~~~~~~~
86
87.. versionadded:: 1.4
88 The C extension was added in Twig 1.4.
89
90Twig comes with a C extension that enhances the performance of the Twig
91runtime engine. You can install it like any other PHP extension:
92
93.. code-block:: bash
94
95 $ cd ext/twig
96 $ phpize
97 $ ./configure
98 $ make
99 $ make install
100
101Finally, enable the extension in your ``php.ini`` configuration file:
102
103.. code-block:: ini
104
105 extension=twig.so
106
107And from now on, Twig will automatically compile your templates to take
108advantage of the C extension. Note that this extension does not replace the
109PHP code but only provides an optimized version of the
110``Twig_Template::getAttribute()`` method.
111
112.. tip::
113
114 On Windows, you can also simply download and install a `pre-built DLL`_.
115
116Basic API Usage
117---------------
118
119This section gives you a brief introduction to the PHP API for Twig.
120
121The first step to use Twig is to register its autoloader::
122
123 require_once '/path/to/lib/Twig/Autoloader.php';
124 Twig_Autoloader::register();
125
126Replace the ``/path/to/lib/`` path with the path you used for Twig
127installation.
128
129If you have installed Twig via Composer you can take advantage of Composer's
130autoload mechanism by replacing the previous snippet for::
131
132 require_once '/path/to/vendor/autoload.php';
133
134.. note::
135
136 Twig follows the PEAR convention names for its classes, which means you
137 can easily integrate Twig classes loading in your own autoloader.
138
139.. code-block:: php
140
141 $loader = new Twig_Loader_String();
142 $twig = new Twig_Environment($loader);
143
144 echo $twig->render('Hello {{ name }}!', array('name' => 'Fabien'));
145
146Twig uses a loader (``Twig_Loader_String``) to locate templates, and an
147environment (``Twig_Environment``) to store the configuration.
148
149The ``render()`` method loads the template passed as a first argument and
150renders it with the variables passed as a second argument.
151
152As templates are generally stored on the filesystem, Twig also comes with a
153filesystem loader::
154
155 $loader = new Twig_Loader_Filesystem('/path/to/templates');
156 $twig = new Twig_Environment($loader, array(
157 'cache' => '/path/to/compilation_cache',
158 ));
159
160 echo $twig->render('index.html', array('name' => 'Fabien'));
161
162.. _`download page`: https://github.com/fabpot/Twig/tags
163.. _`online documentation`: http://getcomposer.org/doc
164.. _`pre-built DLL`: https://github.com/stealth35/stealth35.github.com/downloads
diff --git a/vendor/twig/twig/doc/recipes.rst b/vendor/twig/twig/doc/recipes.rst
new file mode 100644
index 00000000..dfcc9205
--- /dev/null
+++ b/vendor/twig/twig/doc/recipes.rst
@@ -0,0 +1,475 @@
1Recipes
2=======
3
4Making a Layout conditional
5---------------------------
6
7Working with Ajax means that the same content is sometimes displayed as is,
8and sometimes decorated with a layout. As Twig layout template names can be
9any valid expression, you can pass a variable that evaluates to ``true`` when
10the request is made via Ajax and choose the layout accordingly:
11
12.. code-block:: jinja
13
14 {% extends request.ajax ? "base_ajax.html" : "base.html" %}
15
16 {% block content %}
17 This is the content to be displayed.
18 {% endblock %}
19
20Making an Include dynamic
21-------------------------
22
23When including a template, its name does not need to be a string. For
24instance, the name can depend on the value of a variable:
25
26.. code-block:: jinja
27
28 {% include var ~ '_foo.html' %}
29
30If ``var`` evaluates to ``index``, the ``index_foo.html`` template will be
31rendered.
32
33As a matter of fact, the template name can be any valid expression, such as
34the following:
35
36.. code-block:: jinja
37
38 {% include var|default('index') ~ '_foo.html' %}
39
40Overriding a Template that also extends itself
41----------------------------------------------
42
43A template can be customized in two different ways:
44
45* *Inheritance*: A template *extends* a parent template and overrides some
46 blocks;
47
48* *Replacement*: If you use the filesystem loader, Twig loads the first
49 template it finds in a list of configured directories; a template found in a
50 directory *replaces* another one from a directory further in the list.
51
52But how do you combine both: *replace* a template that also extends itself
53(aka a template in a directory further in the list)?
54
55Let's say that your templates are loaded from both ``.../templates/mysite``
56and ``.../templates/default`` in this order. The ``page.twig`` template,
57stored in ``.../templates/default`` reads as follows:
58
59.. code-block:: jinja
60
61 {# page.twig #}
62 {% extends "layout.twig" %}
63
64 {% block content %}
65 {% endblock %}
66
67You can replace this template by putting a file with the same name in
68``.../templates/mysite``. And if you want to extend the original template, you
69might be tempted to write the following:
70
71.. code-block:: jinja
72
73 {# page.twig in .../templates/mysite #}
74 {% extends "page.twig" %} {# from .../templates/default #}
75
76Of course, this will not work as Twig will always load the template from
77``.../templates/mysite``.
78
79It turns out it is possible to get this to work, by adding a directory right
80at the end of your template directories, which is the parent of all of the
81other directories: ``.../templates`` in our case. This has the effect of
82making every template file within our system uniquely addressable. Most of the
83time you will use the "normal" paths, but in the special case of wanting to
84extend a template with an overriding version of itself we can reference its
85parent's full, unambiguous template path in the extends tag:
86
87.. code-block:: jinja
88
89 {# page.twig in .../templates/mysite #}
90 {% extends "default/page.twig" %} {# from .../templates #}
91
92.. note::
93
94 This recipe was inspired by the following Django wiki page:
95 http://code.djangoproject.com/wiki/ExtendingTemplates
96
97Customizing the Syntax
98----------------------
99
100Twig allows some syntax customization for the block delimiters. It's not
101recommended to use this feature as templates will be tied with your custom
102syntax. But for specific projects, it can make sense to change the defaults.
103
104To change the block delimiters, you need to create your own lexer object::
105
106 $twig = new Twig_Environment();
107
108 $lexer = new Twig_Lexer($twig, array(
109 'tag_comment' => array('{#', '#}'),
110 'tag_block' => array('{%', '%}'),
111 'tag_variable' => array('{{', '}}'),
112 'interpolation' => array('#{', '}'),
113 ));
114 $twig->setLexer($lexer);
115
116Here are some configuration example that simulates some other template engines
117syntax::
118
119 // Ruby erb syntax
120 $lexer = new Twig_Lexer($twig, array(
121 'tag_comment' => array('<%#', '%>'),
122 'tag_block' => array('<%', '%>'),
123 'tag_variable' => array('<%=', '%>'),
124 ));
125
126 // SGML Comment Syntax
127 $lexer = new Twig_Lexer($twig, array(
128 'tag_comment' => array('<!--#', '-->'),
129 'tag_block' => array('<!--', '-->'),
130 'tag_variable' => array('${', '}'),
131 ));
132
133 // Smarty like
134 $lexer = new Twig_Lexer($twig, array(
135 'tag_comment' => array('{*', '*}'),
136 'tag_block' => array('{', '}'),
137 'tag_variable' => array('{$', '}'),
138 ));
139
140Using dynamic Object Properties
141-------------------------------
142
143When Twig encounters a variable like ``article.title``, it tries to find a
144``title`` public property in the ``article`` object.
145
146It also works if the property does not exist but is rather defined dynamically
147thanks to the magic ``__get()`` method; you just need to also implement the
148``__isset()`` magic method like shown in the following snippet of code::
149
150 class Article
151 {
152 public function __get($name)
153 {
154 if ('title' == $name) {
155 return 'The title';
156 }
157
158 // throw some kind of error
159 }
160
161 public function __isset($name)
162 {
163 if ('title' == $name) {
164 return true;
165 }
166
167 return false;
168 }
169 }
170
171Accessing the parent Context in Nested Loops
172--------------------------------------------
173
174Sometimes, when using nested loops, you need to access the parent context. The
175parent context is always accessible via the ``loop.parent`` variable. For
176instance, if you have the following template data::
177
178 $data = array(
179 'topics' => array(
180 'topic1' => array('Message 1 of topic 1', 'Message 2 of topic 1'),
181 'topic2' => array('Message 1 of topic 2', 'Message 2 of topic 2'),
182 ),
183 );
184
185And the following template to display all messages in all topics:
186
187.. code-block:: jinja
188
189 {% for topic, messages in topics %}
190 * {{ loop.index }}: {{ topic }}
191 {% for message in messages %}
192 - {{ loop.parent.loop.index }}.{{ loop.index }}: {{ message }}
193 {% endfor %}
194 {% endfor %}
195
196The output will be similar to:
197
198.. code-block:: text
199
200 * 1: topic1
201 - 1.1: The message 1 of topic 1
202 - 1.2: The message 2 of topic 1
203 * 2: topic2
204 - 2.1: The message 1 of topic 2
205 - 2.2: The message 2 of topic 2
206
207In the inner loop, the ``loop.parent`` variable is used to access the outer
208context. So, the index of the current ``topic`` defined in the outer for loop
209is accessible via the ``loop.parent.loop.index`` variable.
210
211Defining undefined Functions and Filters on the Fly
212---------------------------------------------------
213
214When a function (or a filter) is not defined, Twig defaults to throw a
215``Twig_Error_Syntax`` exception. However, it can also call a `callback`_ (any
216valid PHP callable) which should return a function (or a filter).
217
218For filters, register callbacks with ``registerUndefinedFilterCallback()``.
219For functions, use ``registerUndefinedFunctionCallback()``::
220
221 // auto-register all native PHP functions as Twig functions
222 // don't try this at home as it's not secure at all!
223 $twig->registerUndefinedFunctionCallback(function ($name) {
224 if (function_exists($name)) {
225 return new Twig_Function_Function($name);
226 }
227
228 return false;
229 });
230
231If the callable is not able to return a valid function (or filter), it must
232return ``false``.
233
234If you register more than one callback, Twig will call them in turn until one
235does not return ``false``.
236
237.. tip::
238
239 As the resolution of functions and filters is done during compilation,
240 there is no overhead when registering these callbacks.
241
242Validating the Template Syntax
243------------------------------
244
245When template code is providing by a third-party (through a web interface for
246instance), it might be interesting to validate the template syntax before
247saving it. If the template code is stored in a `$template` variable, here is
248how you can do it::
249
250 try {
251 $twig->parse($twig->tokenize($template));
252
253 // the $template is valid
254 } catch (Twig_Error_Syntax $e) {
255 // $template contains one or more syntax errors
256 }
257
258If you iterate over a set of files, you can pass the filename to the
259``tokenize()`` method to get the filename in the exception message::
260
261 foreach ($files as $file) {
262 try {
263 $twig->parse($twig->tokenize($template, $file));
264
265 // the $template is valid
266 } catch (Twig_Error_Syntax $e) {
267 // $template contains one or more syntax errors
268 }
269 }
270
271.. note::
272
273 This method won't catch any sandbox policy violations because the policy
274 is enforced during template rendering (as Twig needs the context for some
275 checks like allowed methods on objects).
276
277Refreshing modified Templates when APC is enabled and apc.stat = 0
278------------------------------------------------------------------
279
280When using APC with ``apc.stat`` set to ``0`` and Twig cache enabled, clearing
281the template cache won't update the APC cache. To get around this, one can
282extend ``Twig_Environment`` and force the update of the APC cache when Twig
283rewrites the cache::
284
285 class Twig_Environment_APC extends Twig_Environment
286 {
287 protected function writeCacheFile($file, $content)
288 {
289 parent::writeCacheFile($file, $content);
290
291 // Compile cached file into bytecode cache
292 apc_compile_file($file);
293 }
294 }
295
296Reusing a stateful Node Visitor
297-------------------------------
298
299When attaching a visitor to a ``Twig_Environment`` instance, Twig uses it to
300visit *all* templates it compiles. If you need to keep some state information
301around, you probably want to reset it when visiting a new template.
302
303This can be easily achieved with the following code::
304
305 protected $someTemplateState = array();
306
307 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
308 {
309 if ($node instanceof Twig_Node_Module) {
310 // reset the state as we are entering a new template
311 $this->someTemplateState = array();
312 }
313
314 // ...
315
316 return $node;
317 }
318
319Using the Template name to set the default Escaping Strategy
320------------------------------------------------------------
321
322.. versionadded:: 1.8
323 This recipe requires Twig 1.8 or later.
324
325The ``autoescape`` option determines the default escaping strategy to use when
326no escaping is applied on a variable. When Twig is used to mostly generate
327HTML files, you can set it to ``html`` and explicitly change it to ``js`` when
328you have some dynamic JavaScript files thanks to the ``autoescape`` tag:
329
330.. code-block:: jinja
331
332 {% autoescape 'js' %}
333 ... some JS ...
334 {% endautoescape %}
335
336But if you have many HTML and JS files, and if your template names follow some
337conventions, you can instead determine the default escaping strategy to use
338based on the template name. Let's say that your template names always ends
339with ``.html`` for HTML files, ``.js`` for JavaScript ones, and ``.css`` for
340stylesheets, here is how you can configure Twig::
341
342 class TwigEscapingGuesser
343 {
344 function guess($filename)
345 {
346 // get the format
347 $format = substr($filename, strrpos($filename, '.') + 1);
348
349 switch ($format) {
350 case 'js':
351 return 'js';
352 case 'css':
353 return 'css';
354 case 'html':
355 default:
356 return 'html';
357 }
358 }
359 }
360
361 $loader = new Twig_Loader_Filesystem('/path/to/templates');
362 $twig = new Twig_Environment($loader, array(
363 'autoescape' => array(new TwigEscapingGuesser(), 'guess'),
364 ));
365
366This dynamic strategy does not incur any overhead at runtime as auto-escaping
367is done at compilation time.
368
369Using a Database to store Templates
370-----------------------------------
371
372If you are developing a CMS, templates are usually stored in a database. This
373recipe gives you a simple PDO template loader you can use as a starting point
374for your own.
375
376First, let's create a temporary in-memory SQLite3 database to work with::
377
378 $dbh = new PDO('sqlite::memory:');
379 $dbh->exec('CREATE TABLE templates (name STRING, source STRING, last_modified INTEGER)');
380 $base = '{% block content %}{% endblock %}';
381 $index = '
382 {% extends "base.twig" %}
383 {% block content %}Hello {{ name }}{% endblock %}
384 ';
385 $now = time();
386 $dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('base.twig', '$base', $now)");
387 $dbh->exec("INSERT INTO templates (name, source, last_modified) VALUES ('index.twig', '$index', $now)");
388
389We have created a simple ``templates`` table that hosts two templates:
390``base.twig`` and ``index.twig``.
391
392Now, let's define a loader able to use this database::
393
394 class DatabaseTwigLoader implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
395 {
396 protected $dbh;
397
398 public function __construct(PDO $dbh)
399 {
400 $this->dbh = $dbh;
401 }
402
403 public function getSource($name)
404 {
405 if (false === $source = $this->getValue('source', $name)) {
406 throw new Twig_Error_Loader(sprintf('Template "%s" does not exist.', $name));
407 }
408
409 return $source;
410 }
411
412 // Twig_ExistsLoaderInterface as of Twig 1.11
413 public function exists($name)
414 {
415 return $name === $this->getValue('name', $name);
416 }
417
418 public function getCacheKey($name)
419 {
420 return $name;
421 }
422
423 public function isFresh($name, $time)
424 {
425 if (false === $lastModified = $this->getValue('last_modified', $name)) {
426 return false;
427 }
428
429 return $lastModified <= $time;
430 }
431
432 protected function getValue($column, $name)
433 {
434 $sth = $this->dbh->prepare('SELECT '.$column.' FROM templates WHERE name = :name');
435 $sth->execute(array(':name' => (string) $name));
436
437 return $sth->fetchColumn();
438 }
439 }
440
441Finally, here is an example on how you can use it::
442
443 $loader = new DatabaseTwigLoader($dbh);
444 $twig = new Twig_Environment($loader);
445
446 echo $twig->render('index.twig', array('name' => 'Fabien'));
447
448Using different Template Sources
449--------------------------------
450
451This recipe is the continuation of the previous one. Even if you store the
452contributed templates in a database, you might want to keep the original/base
453templates on the filesystem. When templates can be loaded from different
454sources, you need to use the ``Twig_Loader_Chain`` loader.
455
456As you can see in the previous recipe, we reference the template in the exact
457same way as we would have done it with a regular filesystem loader. This is
458the key to be able to mix and match templates coming from the database, the
459filesystem, or any other loader for that matter: the template name should be a
460logical name, and not the path from the filesystem::
461
462 $loader1 = new DatabaseTwigLoader($dbh);
463 $loader2 = new Twig_Loader_Array(array(
464 'base.twig' => '{% block content %}{% endblock %}',
465 ));
466 $loader = new Twig_Loader_Chain(array($loader1, $loader2));
467
468 $twig = new Twig_Environment($loader);
469
470 echo $twig->render('index.twig', array('name' => 'Fabien'));
471
472Now that the ``base.twig`` templates is defined in an array loader, you can
473remove it from the database, and everything else will still work as before.
474
475.. _callback: http://www.php.net/manual/en/function.is-callable.php
diff --git a/vendor/twig/twig/doc/tags/autoescape.rst b/vendor/twig/twig/doc/tags/autoescape.rst
new file mode 100644
index 00000000..c5ff0c2c
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/autoescape.rst
@@ -0,0 +1,71 @@
1``autoescape``
2==============
3
4Whether automatic escaping is enabled or not, you can mark a section of a
5template to be escaped or not by using the ``autoescape`` tag:
6
7.. code-block:: jinja
8
9 {# The following syntax works as of Twig 1.8 -- see the note below for previous versions #}
10
11 {% autoescape %}
12 Everything will be automatically escaped in this block
13 using the HTML strategy
14 {% endautoescape %}
15
16 {% autoescape 'html' %}
17 Everything will be automatically escaped in this block
18 using the HTML strategy
19 {% endautoescape %}
20
21 {% autoescape 'js' %}
22 Everything will be automatically escaped in this block
23 using the js escaping strategy
24 {% endautoescape %}
25
26 {% autoescape false %}
27 Everything will be outputted as is in this block
28 {% endautoescape %}
29
30.. note::
31
32 Before Twig 1.8, the syntax was different:
33
34 .. code-block:: jinja
35
36 {% autoescape true %}
37 Everything will be automatically escaped in this block
38 using the HTML strategy
39 {% endautoescape %}
40
41 {% autoescape false %}
42 Everything will be outputted as is in this block
43 {% endautoescape %}
44
45 {% autoescape true js %}
46 Everything will be automatically escaped in this block
47 using the js escaping strategy
48 {% endautoescape %}
49
50When automatic escaping is enabled everything is escaped by default except for
51values explicitly marked as safe. Those can be marked in the template by using
52the :doc:`raw<../filters/raw>` filter:
53
54.. code-block:: jinja
55
56 {% autoescape %}
57 {{ safe_value|raw }}
58 {% endautoescape %}
59
60Functions returning template data (like :doc:`macros<macro>` and
61:doc:`parent<../functions/parent>`) always return safe markup.
62
63.. note::
64
65 Twig is smart enough to not escape an already escaped value by the
66 :doc:`escape<../filters/escape>` filter.
67
68.. note::
69
70 The chapter :doc:`Twig for Developers<../api>` gives more information
71 about when and how automatic escaping is applied.
diff --git a/vendor/twig/twig/doc/tags/block.rst b/vendor/twig/twig/doc/tags/block.rst
new file mode 100644
index 00000000..e3804823
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/block.rst
@@ -0,0 +1,11 @@
1``block``
2=========
3
4Blocks are used for inheritance and act as placeholders and replacements at
5the same time. They are documented in detail in the documentation for the
6:doc:`extends<../tags/extends>` tag.
7
8Block names should consist of alphanumeric characters, and underscores. Dashes
9are not permitted.
10
11.. seealso:: :doc:`block<../functions/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`, :doc:`extends<../tags/extends>`
diff --git a/vendor/twig/twig/doc/tags/do.rst b/vendor/twig/twig/doc/tags/do.rst
new file mode 100644
index 00000000..eca63d0a
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/do.rst
@@ -0,0 +1,12 @@
1``do``
2======
3
4.. versionadded:: 1.5
5 The do tag was added in Twig 1.5.
6
7The ``do`` tag works exactly like the regular variable expression (``{{ ...
8}}``) just that it doesn't print anything:
9
10.. code-block:: jinja
11
12 {% do 1 + 2 %}
diff --git a/vendor/twig/twig/doc/tags/embed.rst b/vendor/twig/twig/doc/tags/embed.rst
new file mode 100644
index 00000000..5a6a0299
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/embed.rst
@@ -0,0 +1,178 @@
1``embed``
2=========
3
4.. versionadded:: 1.8
5 The ``embed`` tag was added in Twig 1.8.
6
7The ``embed`` tag combines the behaviour of :doc:`include<include>` and
8:doc:`extends<extends>`.
9It allows you to include another template's contents, just like ``include``
10does. But it also allows you to override any block defined inside the
11included template, like when extending a template.
12
13Think of an embedded template as a "micro layout skeleton".
14
15.. code-block:: jinja
16
17 {% embed "teasers_skeleton.twig" %}
18 {# These blocks are defined in "teasers_skeleton.twig" #}
19 {# and we override them right here: #}
20 {% block left_teaser %}
21 Some content for the left teaser box
22 {% endblock %}
23 {% block right_teaser %}
24 Some content for the right teaser box
25 {% endblock %}
26 {% endembed %}
27
28The ``embed`` tag takes the idea of template inheritance to the level of
29content fragments. While template inheritance allows for "document skeletons",
30which are filled with life by child templates, the ``embed`` tag allows you to
31create "skeletons" for smaller units of content and re-use and fill them
32anywhere you like.
33
34Since the use case may not be obvious, let's look at a simplified example.
35Imagine a base template shared by multiple HTML pages, defining a single block
36named "content":
37
38.. code-block:: text
39
40 ┌─── page layout ─────────────────────┐
41 │ │
42 │ ┌── block "content" ──┐ │
43 │ │ │ │
44 │ │ │ │
45 │ │ (child template to │ │
46 │ │ put content here) │ │
47 │ │ │ │
48 │ │ │ │
49 │ └─────────────────────┘ │
50 │ │
51 └─────────────────────────────────────┘
52
53Some pages ("foo" and "bar") share the same content structure -
54two vertically stacked boxes:
55
56.. code-block:: text
57
58 ┌─── page layout ─────────────────────┐
59 │ │
60 │ ┌── block "content" ──┐ │
61 │ │ ┌─ block "top" ───┐ │ │
62 │ │ │ │ │ │
63 │ │ └─────────────────┘ │ │
64 │ │ ┌─ block "bottom" ┐ │ │
65 │ │ │ │ │ │
66 │ │ └─────────────────┘ │ │
67 │ └─────────────────────┘ │
68 │ │
69 └─────────────────────────────────────┘
70
71While other pages ("boom" and "baz") share a different content structure -
72two boxes side by side:
73
74.. code-block:: text
75
76 ┌─── page layout ─────────────────────┐
77 │ │
78 │ ┌── block "content" ──┐ │
79 │ │ │ │
80 │ │ ┌ block ┐ ┌ block ┐ │ │
81 │ │ │"left" │ │"right"│ │ │
82 │ │ │ │ │ │ │ │
83 │ │ │ │ │ │ │ │
84 │ │ └───────┘ └───────┘ │ │
85 │ └─────────────────────┘ │
86 │ │
87 └─────────────────────────────────────┘
88
89Without the ``embed`` tag, you have two ways to design your templates:
90
91 * Create two "intermediate" base templates that extend the master layout
92 template: one with vertically stacked boxes to be used by the "foo" and
93 "bar" pages and another one with side-by-side boxes for the "boom" and
94 "baz" pages.
95
96 * Embed the markup for the top/bottom and left/right boxes into each page
97 template directly.
98
99These two solutions do not scale well because they each have a major drawback:
100
101 * The first solution may indeed work for this simplified example. But imagine
102 we add a sidebar, which may again contain different, recurring structures
103 of content. Now we would need to create intermediate base templates for
104 all occurring combinations of content structure and sidebar structure...
105 and so on.
106
107 * The second solution involves duplication of common code with all its negative
108 consequences: any change involves finding and editing all affected copies
109 of the structure, correctness has to be verified for each copy, copies may
110 go out of sync by careless modifications etc.
111
112In such a situation, the ``embed`` tag comes in handy. The common layout
113code can live in a single base template, and the two different content structures,
114let's call them "micro layouts" go into separate templates which are embedded
115as necessary:
116
117Page template ``foo.twig``:
118
119.. code-block:: jinja
120
121 {% extends "layout_skeleton.twig" %}
122
123 {% block content %}
124 {% embed "vertical_boxes_skeleton.twig" %}
125 {% block top %}
126 Some content for the top box
127 {% endblock %}
128
129 {% block bottom %}
130 Some content for the bottom box
131 {% endblock %}
132 {% endembed %}
133 {% endblock %}
134
135And here is the code for ``vertical_boxes_skeleton.twig``:
136
137.. code-block:: html+jinja
138
139 <div class="top_box">
140 {% block top %}
141 Top box default content
142 {% endblock %}
143 </div>
144
145 <div class="bottom_box">
146 {% block bottom %}
147 Bottom box default content
148 {% endblock %}
149 </div>
150
151The goal of the ``vertical_boxes_skeleton.twig`` template being to factor
152out the HTML markup for the boxes.
153
154The ``embed`` tag takes the exact same arguments as the ``include`` tag:
155
156.. code-block:: jinja
157
158 {% embed "base" with {'foo': 'bar'} %}
159 ...
160 {% endembed %}
161
162 {% embed "base" with {'foo': 'bar'} only %}
163 ...
164 {% endembed %}
165
166 {% embed "base" ignore missing %}
167 ...
168 {% endembed %}
169
170.. warning::
171
172 As embedded templates do not have "names", auto-escaping strategies based
173 on the template "filename" won't work as expected if you change the
174 context (for instance, if you embed a CSS/JavaScript template into an HTML
175 one). In that case, explicitly set the default auto-escaping strategy with
176 the ``autoescape`` tag.
177
178.. seealso:: :doc:`include<../tags/include>`
diff --git a/vendor/twig/twig/doc/tags/extends.rst b/vendor/twig/twig/doc/tags/extends.rst
new file mode 100644
index 00000000..f995a5dc
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/extends.rst
@@ -0,0 +1,268 @@
1``extends``
2===========
3
4The ``extends`` tag can be used to extend a template from another one.
5
6.. note::
7
8 Like PHP, Twig does not support multiple inheritance. So you can only have
9 one extends tag called per rendering. However, Twig supports horizontal
10 :doc:`reuse<use>`.
11
12Let's define a base template, ``base.html``, which defines a simple HTML
13skeleton document:
14
15.. code-block:: html+jinja
16
17 <!DOCTYPE html>
18 <html>
19 <head>
20 {% block head %}
21 <link rel="stylesheet" href="style.css" />
22 <title>{% block title %}{% endblock %} - My Webpage</title>
23 {% endblock %}
24 </head>
25 <body>
26 <div id="content">{% block content %}{% endblock %}</div>
27 <div id="footer">
28 {% block footer %}
29 &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
30 {% endblock %}
31 </div>
32 </body>
33 </html>
34
35In this example, the :doc:`block<block>` tags define four blocks that child
36templates can fill in.
37
38All the ``block`` tag does is to tell the template engine that a child
39template may override those portions of the template.
40
41Child Template
42--------------
43
44A child template might look like this:
45
46.. code-block:: jinja
47
48 {% extends "base.html" %}
49
50 {% block title %}Index{% endblock %}
51 {% block head %}
52 {{ parent() }}
53 <style type="text/css">
54 .important { color: #336699; }
55 </style>
56 {% endblock %}
57 {% block content %}
58 <h1>Index</h1>
59 <p class="important">
60 Welcome on my awesome homepage.
61 </p>
62 {% endblock %}
63
64The ``extends`` tag is the key here. It tells the template engine that this
65template "extends" another template. When the template system evaluates this
66template, first it locates the parent. The extends tag should be the first tag
67in the template.
68
69Note that since the child template doesn't define the ``footer`` block, the
70value from the parent template is used instead.
71
72You can't define multiple ``block`` tags with the same name in the same
73template. This limitation exists because a block tag works in "both"
74directions. That is, a block tag doesn't just provide a hole to fill - it also
75defines the content that fills the hole in the *parent*. If there were two
76similarly-named ``block`` tags in a template, that template's parent wouldn't
77know which one of the blocks' content to use.
78
79If you want to print a block multiple times you can however use the
80``block`` function:
81
82.. code-block:: jinja
83
84 <title>{% block title %}{% endblock %}</title>
85 <h1>{{ block('title') }}</h1>
86 {% block body %}{% endblock %}
87
88Parent Blocks
89-------------
90
91It's possible to render the contents of the parent block by using the
92:doc:`parent<../functions/parent>` function. This gives back the results of
93the parent block:
94
95.. code-block:: jinja
96
97 {% block sidebar %}
98 <h3>Table Of Contents</h3>
99 ...
100 {{ parent() }}
101 {% endblock %}
102
103Named Block End-Tags
104--------------------
105
106Twig allows you to put the name of the block after the end tag for better
107readability:
108
109.. code-block:: jinja
110
111 {% block sidebar %}
112 {% block inner_sidebar %}
113 ...
114 {% endblock inner_sidebar %}
115 {% endblock sidebar %}
116
117Of course, the name after the ``endblock`` word must match the block name.
118
119Block Nesting and Scope
120-----------------------
121
122Blocks can be nested for more complex layouts. Per default, blocks have access
123to variables from outer scopes:
124
125.. code-block:: jinja
126
127 {% for item in seq %}
128 <li>{% block loop_item %}{{ item }}{% endblock %}</li>
129 {% endfor %}
130
131Block Shortcuts
132---------------
133
134For blocks with few content, it's possible to use a shortcut syntax. The
135following constructs do the same:
136
137.. code-block:: jinja
138
139 {% block title %}
140 {{ page_title|title }}
141 {% endblock %}
142
143.. code-block:: jinja
144
145 {% block title page_title|title %}
146
147Dynamic Inheritance
148-------------------
149
150Twig supports dynamic inheritance by using a variable as the base template:
151
152.. code-block:: jinja
153
154 {% extends some_var %}
155
156If the variable evaluates to a ``Twig_Template`` object, Twig will use it as
157the parent template::
158
159 // {% extends layout %}
160
161 $layout = $twig->loadTemplate('some_layout_template.twig');
162
163 $twig->display('template.twig', array('layout' => $layout));
164
165.. versionadded:: 1.2
166 The possibility to pass an array of templates has been added in Twig 1.2.
167
168You can also provide a list of templates that are checked for existence. The
169first template that exists will be used as a parent:
170
171.. code-block:: jinja
172
173 {% extends ['layout.html', 'base_layout.html'] %}
174
175Conditional Inheritance
176-----------------------
177
178As the template name for the parent can be any valid Twig expression, it's
179possible to make the inheritance mechanism conditional:
180
181.. code-block:: jinja
182
183 {% extends standalone ? "minimum.html" : "base.html" %}
184
185In this example, the template will extend the "minimum.html" layout template
186if the ``standalone`` variable evaluates to ``true``, and "base.html"
187otherwise.
188
189How blocks work?
190----------------
191
192A block provides a way to change how a certain part of a template is rendered
193but it does not interfere in any way with the logic around it.
194
195Let's take the following example to illustrate how a block work and more
196importantly, how it does not work:
197
198.. code-block:: jinja
199
200 {# base.twig #}
201
202 {% for post in posts %}
203 {% block post %}
204 <h1>{{ post.title }}</h1>
205 <p>{{ post.body }}</p>
206 {% endblock %}
207 {% endfor %}
208
209If you render this template, the result would be exactly the same with or
210without the ``block`` tag. The ``block`` inside the ``for`` loop is just a way
211to make it overridable by a child template:
212
213.. code-block:: jinja
214
215 {# child.twig #}
216
217 {% extends "base.twig" %}
218
219 {% block post %}
220 <article>
221 <header>{{ post.title }}</header>
222 <section>{{ post.text }}</section>
223 </article>
224 {% endblock %}
225
226Now, when rendering the child template, the loop is going to use the block
227defined in the child template instead of the one defined in the base one; the
228executed template is then equivalent to the following one:
229
230.. code-block:: jinja
231
232 {% for post in posts %}
233 <article>
234 <header>{{ post.title }}</header>
235 <section>{{ post.text }}</section>
236 </article>
237 {% endfor %}
238
239Let's take another example: a block included within an ``if`` statement:
240
241.. code-block:: jinja
242
243 {% if posts is empty %}
244 {% block head %}
245 {{ parent() }}
246
247 <meta name="robots" content="noindex, follow">
248 {% endblock head %}
249 {% endif %}
250
251Contrary to what you might think, this template does not define a block
252conditionally; it just makes overridable by a child template the output of
253what will be rendered when the condition is ``true``.
254
255If you want the output to be displayed conditionally, use the following
256instead:
257
258.. code-block:: jinja
259
260 {% block head %}
261 {{ parent() }}
262
263 {% if posts is empty %}
264 <meta name="robots" content="noindex, follow">
265 {% endif %}
266 {% endblock head %}
267
268.. seealso:: :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>`
diff --git a/vendor/twig/twig/doc/tags/filter.rst b/vendor/twig/twig/doc/tags/filter.rst
new file mode 100644
index 00000000..82ca5c62
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/filter.rst
@@ -0,0 +1,21 @@
1``filter``
2==========
3
4Filter sections allow you to apply regular Twig filters on a block of template
5data. Just wrap the code in the special ``filter`` section:
6
7.. code-block:: jinja
8
9 {% filter upper %}
10 This text becomes uppercase
11 {% endfilter %}
12
13You can also chain filters:
14
15.. code-block:: jinja
16
17 {% filter lower|escape %}
18 <strong>SOME TEXT</strong>
19 {% endfilter %}
20
21 {# outputs "&lt;strong&gt;some text&lt;/strong&gt;" #}
diff --git a/vendor/twig/twig/doc/tags/flush.rst b/vendor/twig/twig/doc/tags/flush.rst
new file mode 100644
index 00000000..55ef593a
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/flush.rst
@@ -0,0 +1,17 @@
1``flush``
2=========
3
4.. versionadded:: 1.5
5 The flush tag was added in Twig 1.5.
6
7The ``flush`` tag tells Twig to flush the output buffer:
8
9.. code-block:: jinja
10
11 {% flush %}
12
13.. note::
14
15 Internally, Twig uses the PHP `flush`_ function.
16
17.. _`flush`: http://php.net/flush
diff --git a/vendor/twig/twig/doc/tags/for.rst b/vendor/twig/twig/doc/tags/for.rst
new file mode 100644
index 00000000..0673b551
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/for.rst
@@ -0,0 +1,172 @@
1``for``
2=======
3
4Loop over each item in a sequence. For example, to display a list of users
5provided in a variable called ``users``:
6
7.. code-block:: jinja
8
9 <h1>Members</h1>
10 <ul>
11 {% for user in users %}
12 <li>{{ user.username|e }}</li>
13 {% endfor %}
14 </ul>
15
16.. note::
17
18 A sequence can be either an array or an object implementing the
19 ``Traversable`` interface.
20
21If you do need to iterate over a sequence of numbers, you can use the ``..``
22operator:
23
24.. code-block:: jinja
25
26 {% for i in 0..10 %}
27 * {{ i }}
28 {% endfor %}
29
30The above snippet of code would print all numbers from 0 to 10.
31
32It can be also useful with letters:
33
34.. code-block:: jinja
35
36 {% for letter in 'a'..'z' %}
37 * {{ letter }}
38 {% endfor %}
39
40The ``..`` operator can take any expression at both sides:
41
42.. code-block:: jinja
43
44 {% for letter in 'a'|upper..'z'|upper %}
45 * {{ letter }}
46 {% endfor %}
47
48.. tip:
49
50 If you need a step different from 1, you can use the ``range`` function
51 instead.
52
53The `loop` variable
54-------------------
55
56Inside of a ``for`` loop block you can access some special variables:
57
58===================== =============================================================
59Variable Description
60===================== =============================================================
61``loop.index`` The current iteration of the loop. (1 indexed)
62``loop.index0`` The current iteration of the loop. (0 indexed)
63``loop.revindex`` The number of iterations from the end of the loop (1 indexed)
64``loop.revindex0`` The number of iterations from the end of the loop (0 indexed)
65``loop.first`` True if first iteration
66``loop.last`` True if last iteration
67``loop.length`` The number of items in the sequence
68``loop.parent`` The parent context
69===================== =============================================================
70
71.. code-block:: jinja
72
73 {% for user in users %}
74 {{ loop.index }} - {{ user.username }}
75 {% endfor %}
76
77.. note::
78
79 The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and
80 ``loop.last`` variables are only available for PHP arrays, or objects that
81 implement the ``Countable`` interface. They are also not available when
82 looping with a condition.
83
84.. versionadded:: 1.2
85 The ``if`` modifier support has been added in Twig 1.2.
86
87Adding a condition
88------------------
89
90Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You
91can however filter the sequence during iteration which allows you to skip
92items. The following example skips all the users which are not active:
93
94.. code-block:: jinja
95
96 <ul>
97 {% for user in users if user.active %}
98 <li>{{ user.username|e }}</li>
99 {% endfor %}
100 </ul>
101
102The advantage is that the special loop variable will count correctly thus not
103counting the users not iterated over. Keep in mind that properties like
104``loop.last`` will not be defined when using loop conditions.
105
106.. note::
107
108 Using the ``loop`` variable within the condition is not recommended as it
109 will probably not be doing what you expect it to. For instance, adding a
110 condition like ``loop.index > 4`` won't work as the index is only
111 incremented when the condition is true (so the condition will never
112 match).
113
114The `else` Clause
115-----------------
116
117If no iteration took place because the sequence was empty, you can render a
118replacement block by using ``else``:
119
120.. code-block:: jinja
121
122 <ul>
123 {% for user in users %}
124 <li>{{ user.username|e }}</li>
125 {% else %}
126 <li><em>no user found</em></li>
127 {% endfor %}
128 </ul>
129
130Iterating over Keys
131-------------------
132
133By default, a loop iterates over the values of the sequence. You can iterate
134on keys by using the ``keys`` filter:
135
136.. code-block:: jinja
137
138 <h1>Members</h1>
139 <ul>
140 {% for key in users|keys %}
141 <li>{{ key }}</li>
142 {% endfor %}
143 </ul>
144
145Iterating over Keys and Values
146------------------------------
147
148You can also access both keys and values:
149
150.. code-block:: jinja
151
152 <h1>Members</h1>
153 <ul>
154 {% for key, user in users %}
155 <li>{{ key }}: {{ user.username|e }}</li>
156 {% endfor %}
157 </ul>
158
159Iterating over a Subset
160-----------------------
161
162You might want to iterate over a subset of values. This can be achieved using
163the :doc:`slice <../filters/slice>` filter:
164
165.. code-block:: jinja
166
167 <h1>Top Ten Members</h1>
168 <ul>
169 {% for user in users|slice(0, 10) %}
170 <li>{{ user.username|e }}</li>
171 {% endfor %}
172 </ul>
diff --git a/vendor/twig/twig/doc/tags/from.rst b/vendor/twig/twig/doc/tags/from.rst
new file mode 100644
index 00000000..5337a235
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/from.rst
@@ -0,0 +1,8 @@
1``from``
2========
3
4The ``from`` tags import :doc:`macro<../tags/macro>` names into the current
5namespace. The tag is documented in detail in the documentation for the
6:doc:`import<../tags/import>` tag.
7
8.. seealso:: :doc:`macro<../tags/macro>`, :doc:`import<../tags/import>`
diff --git a/vendor/twig/twig/doc/tags/if.rst b/vendor/twig/twig/doc/tags/if.rst
new file mode 100644
index 00000000..d7a1451c
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/if.rst
@@ -0,0 +1,43 @@
1``if``
2======
3
4The ``if`` statement in Twig is comparable with the if statements of PHP.
5
6In the simplest form you can use it to test if an expression evaluates to
7``true``:
8
9.. code-block:: jinja
10
11 {% if online == false %}
12 <p>Our website is in maintenance mode. Please, come back later.</p>
13 {% endif %}
14
15You can also test if an array is not empty:
16
17.. code-block:: jinja
18
19 {% if users %}
20 <ul>
21 {% for user in users %}
22 <li>{{ user.username|e }}</li>
23 {% endfor %}
24 </ul>
25 {% endif %}
26
27.. note::
28
29 If you want to test if the variable is defined, use ``if users is
30 defined`` instead.
31
32For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can use
33more complex ``expressions`` there too:
34
35.. code-block:: jinja
36
37 {% if kenny.sick %}
38 Kenny is sick.
39 {% elseif kenny.dead %}
40 You killed Kenny! You bastard!!!
41 {% else %}
42 Kenny looks okay --- so far
43 {% endif %}
diff --git a/vendor/twig/twig/doc/tags/import.rst b/vendor/twig/twig/doc/tags/import.rst
new file mode 100644
index 00000000..f6bf718e
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/import.rst
@@ -0,0 +1,57 @@
1``import``
2==========
3
4Twig supports putting often used code into :doc:`macros<../tags/macro>`. These
5macros can go into different templates and get imported from there.
6
7There are two ways to import templates. You can import the complete template
8into a variable or request specific macros from it.
9
10Imagine we have a helper module that renders forms (called ``forms.html``):
11
12.. code-block:: jinja
13
14 {% macro input(name, value, type, size) %}
15 <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
16 {% endmacro %}
17
18 {% macro textarea(name, value, rows) %}
19 <textarea name="{{ name }}" rows="{{ rows|default(10) }}" cols="{{ cols|default(40) }}">{{ value|e }}</textarea>
20 {% endmacro %}
21
22The easiest and most flexible is importing the whole module into a variable.
23That way you can access the attributes:
24
25.. code-block:: jinja
26
27 {% import 'forms.html' as forms %}
28
29 <dl>
30 <dt>Username</dt>
31 <dd>{{ forms.input('username') }}</dd>
32 <dt>Password</dt>
33 <dd>{{ forms.input('password', null, 'password') }}</dd>
34 </dl>
35 <p>{{ forms.textarea('comment') }}</p>
36
37Alternatively you can import names from the template into the current
38namespace:
39
40.. code-block:: jinja
41
42 {% from 'forms.html' import input as input_field, textarea %}
43
44 <dl>
45 <dt>Username</dt>
46 <dd>{{ input_field('username') }}</dd>
47 <dt>Password</dt>
48 <dd>{{ input_field('password', '', 'password') }}</dd>
49 </dl>
50 <p>{{ textarea('comment') }}</p>
51
52.. tip::
53
54 To import macros from the current file, use the special ``_self`` variable
55 for the source.
56
57.. seealso:: :doc:`macro<../tags/macro>`, :doc:`from<../tags/from>`
diff --git a/vendor/twig/twig/doc/tags/include.rst b/vendor/twig/twig/doc/tags/include.rst
new file mode 100644
index 00000000..10b262de
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/include.rst
@@ -0,0 +1,86 @@
1``include``
2===========
3
4The ``include`` statement includes a template and return the rendered content
5of that file into the current namespace:
6
7.. code-block:: jinja
8
9 {% include 'header.html' %}
10 Body
11 {% include 'footer.html' %}
12
13Included templates have access to the variables of the active context.
14
15If you are using the filesystem loader, the templates are looked for in the
16paths defined by it.
17
18You can add additional variables by passing them after the ``with`` keyword:
19
20.. code-block:: jinja
21
22 {# template.html will have access to the variables from the current context and the additional ones provided #}
23 {% include 'template.html' with {'foo': 'bar'} %}
24
25 {% set vars = {'foo': 'bar'} %}
26 {% include 'template.html' with vars %}
27
28You can disable access to the context by appending the ``only`` keyword:
29
30.. code-block:: jinja
31
32 {# only the foo variable will be accessible #}
33 {% include 'template.html' with {'foo': 'bar'} only %}
34
35.. code-block:: jinja
36
37 {# no variables will be accessible #}
38 {% include 'template.html' only %}
39
40.. tip::
41
42 When including a template created by an end user, you should consider
43 sandboxing it. More information in the :doc:`Twig for Developers<../api>`
44 chapter and in the :doc:`sandbox<../tags/sandbox>` tag documentation.
45
46The template name can be any valid Twig expression:
47
48.. code-block:: jinja
49
50 {% include some_var %}
51 {% include ajax ? 'ajax.html' : 'not_ajax.html' %}
52
53And if the expression evaluates to a ``Twig_Template`` object, Twig will use it
54directly::
55
56 // {% include template %}
57
58 $template = $twig->loadTemplate('some_template.twig');
59
60 $twig->loadTemplate('template.twig')->display(array('template' => $template));
61
62.. versionadded:: 1.2
63 The ``ignore missing`` feature has been added in Twig 1.2.
64
65You can mark an include with ``ignore missing`` in which case Twig will ignore
66the statement if the template to be included does not exist. It has to be
67placed just after the template name. Here some valid examples:
68
69.. code-block:: jinja
70
71 {% include 'sidebar.html' ignore missing %}
72 {% include 'sidebar.html' ignore missing with {'foo': 'bar'} %}
73 {% include 'sidebar.html' ignore missing only %}
74
75.. versionadded:: 1.2
76 The possibility to pass an array of templates has been added in Twig 1.2.
77
78You can also provide a list of templates that are checked for existence before
79inclusion. The first template that exists will be included:
80
81.. code-block:: jinja
82
83 {% include ['page_detailed.html', 'page.html'] %}
84
85If ``ignore missing`` is given, it will fall back to rendering nothing if none
86of the templates exist, otherwise it will throw an exception.
diff --git a/vendor/twig/twig/doc/tags/index.rst b/vendor/twig/twig/doc/tags/index.rst
new file mode 100644
index 00000000..64e88644
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/index.rst
@@ -0,0 +1,24 @@
1Tags
2====
3
4.. toctree::
5 :maxdepth: 1
6
7 autoescape
8 block
9 filter
10 do
11 embed
12 extends
13 flush
14 for
15 from
16 if
17 import
18 include
19 macro
20 sandbox
21 set
22 spaceless
23 use
24 verbatim
diff --git a/vendor/twig/twig/doc/tags/macro.rst b/vendor/twig/twig/doc/tags/macro.rst
new file mode 100644
index 00000000..11c115a0
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/macro.rst
@@ -0,0 +1,83 @@
1``macro``
2=========
3
4Macros are comparable with functions in regular programming languages. They
5are useful to put often used HTML idioms into reusable elements to not repeat
6yourself.
7
8Here is a small example of a macro that renders a form element:
9
10.. code-block:: jinja
11
12 {% macro input(name, value, type, size) %}
13 <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
14 {% endmacro %}
15
16Macros differs from native PHP functions in a few ways:
17
18* Default argument values are defined by using the ``default`` filter in the
19 macro body;
20
21* Arguments of a macro are always optional.
22
23But as with PHP functions, macros don't have access to the current template
24variables.
25
26.. tip::
27
28 You can pass the whole context as an argument by using the special
29 ``_context`` variable.
30
31Macros can be defined in any template, and need to be "imported" before being
32used (see the documentation for the :doc:`import<../tags/import>` tag for more
33information):
34
35.. code-block:: jinja
36
37 {% import "forms.html" as forms %}
38
39The above ``import`` call imports the "forms.html" file (which can contain only
40macros, or a template and some macros), and import the functions as items of
41the ``forms`` variable.
42
43The macro can then be called at will:
44
45.. code-block:: jinja
46
47 <p>{{ forms.input('username') }}</p>
48 <p>{{ forms.input('password', null, 'password') }}</p>
49
50If macros are defined and used in the same template, you can use the
51special ``_self`` variable to import them:
52
53.. code-block:: jinja
54
55 {% import _self as forms %}
56
57 <p>{{ forms.input('username') }}</p>
58
59.. warning::
60
61 When you define a macro in the template where you are going to use it, you
62 might be tempted to call the macro directly via ``_self.input()`` instead
63 of importing it; even if seems to work, this is just a side-effect of the
64 current implementation and it won't work anymore in Twig 2.x.
65
66When you want to use a macro in another macro from the same file, you need to
67import it locally:
68
69.. code-block:: jinja
70
71 {% macro input(name, value, type, size) %}
72 <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
73 {% endmacro %}
74
75 {% macro wrapped_input(name, value, type, size) %}
76 {% import _self as forms %}
77
78 <div class="field">
79 {{ forms.input(name, value, type, size) }}
80 </div>
81 {% endmacro %}
82
83.. seealso:: :doc:`from<../tags/from>`, :doc:`import<../tags/import>`
diff --git a/vendor/twig/twig/doc/tags/sandbox.rst b/vendor/twig/twig/doc/tags/sandbox.rst
new file mode 100644
index 00000000..e186726c
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/sandbox.rst
@@ -0,0 +1,30 @@
1``sandbox``
2===========
3
4The ``sandbox`` tag can be used to enable the sandboxing mode for an included
5template, when sandboxing is not enabled globally for the Twig environment:
6
7.. code-block:: jinja
8
9 {% sandbox %}
10 {% include 'user.html' %}
11 {% endsandbox %}
12
13.. warning::
14
15 The ``sandbox`` tag is only available when the sandbox extension is
16 enabled (see the :doc:`Twig for Developers<../api>` chapter).
17
18.. note::
19
20 The ``sandbox`` tag can only be used to sandbox an include tag and it
21 cannot be used to sandbox a section of a template. The following example
22 won't work:
23
24 .. code-block:: jinja
25
26 {% sandbox %}
27 {% for i in 1..2 %}
28 {{ i }}
29 {% endfor %}
30 {% endsandbox %}
diff --git a/vendor/twig/twig/doc/tags/set.rst b/vendor/twig/twig/doc/tags/set.rst
new file mode 100644
index 00000000..3eba239a
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/set.rst
@@ -0,0 +1,78 @@
1``set``
2=======
3
4Inside code blocks you can also assign values to variables. Assignments use
5the ``set`` tag and can have multiple targets.
6
7Here is how you can assign the ``bar`` value to the ``foo`` variable:
8
9.. code-block:: jinja
10
11 {% set foo = 'bar' %}
12
13After the ``set`` call, the ``foo`` variable is available in the template like
14any other ones:
15
16.. code-block:: jinja
17
18 {# displays bar #}
19 {{ foo }}
20
21The assigned value can be any valid :ref:`Twig expressions
22<twig-expressions>`:
23
24.. code-block:: jinja
25
26 {% set foo = [1, 2] %}
27 {% set foo = {'foo': 'bar'} %}
28 {% set foo = 'foo' ~ 'bar' %}
29
30Several variables can be assigned in one block:
31
32.. code-block:: jinja
33
34 {% set foo, bar = 'foo', 'bar' %}
35
36 {# is equivalent to #}
37
38 {% set foo = 'foo' %}
39 {% set bar = 'bar' %}
40
41The ``set`` tag can also be used to 'capture' chunks of text:
42
43.. code-block:: jinja
44
45 {% set foo %}
46 <div id="pagination">
47 ...
48 </div>
49 {% endset %}
50
51.. caution::
52
53 If you enable automatic output escaping, Twig will only consider the
54 content to be safe when capturing chunks of text.
55
56.. note::
57
58 Note that loops are scoped in Twig; therefore a variable declared inside a
59 ``for`` loop is not accessible outside the loop itself:
60
61 .. code-block:: jinja
62
63 {% for item in list %}
64 {% set foo = item %}
65 {% endfor %}
66
67 {# foo is NOT available #}
68
69 If you want to access the variable, just declare it before the loop:
70
71 .. code-block:: jinja
72
73 {% set foo = "" %}
74 {% for item in list %}
75 {% set foo = item %}
76 {% endfor %}
77
78 {# foo is available #}
diff --git a/vendor/twig/twig/doc/tags/spaceless.rst b/vendor/twig/twig/doc/tags/spaceless.rst
new file mode 100644
index 00000000..12e77b25
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/spaceless.rst
@@ -0,0 +1,37 @@
1``spaceless``
2=============
3
4Use the ``spaceless`` tag to remove whitespace *between HTML tags*, not
5whitespace within HTML tags or whitespace in plain text:
6
7.. code-block:: jinja
8
9 {% spaceless %}
10 <div>
11 <strong>foo</strong>
12 </div>
13 {% endspaceless %}
14
15 {# output will be <div><strong>foo</strong></div> #}
16
17This tag is not meant to "optimize" the size of the generated HTML content but
18merely to avoid extra whitespace between HTML tags to avoid browser rendering
19quirks under some circumstances.
20
21.. tip::
22
23 If you want to optimize the size of the generated HTML content, gzip
24 compress the output instead.
25
26.. tip::
27
28 If you want to create a tag that actually removes all extra whitespace in
29 an HTML string, be warned that this is not as easy as it seems to be
30 (think of ``textarea`` or ``pre`` tags for instance). Using a third-party
31 library like Tidy is probably a better idea.
32
33.. tip::
34
35 For more information on whitespace control, read the
36 :doc:`dedicated<../templates>` section of the documentation and learn how
37 you can also use the whitespace control modifier on your tags.
diff --git a/vendor/twig/twig/doc/tags/use.rst b/vendor/twig/twig/doc/tags/use.rst
new file mode 100644
index 00000000..085f9161
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/use.rst
@@ -0,0 +1,123 @@
1``use``
2=======
3
4.. versionadded:: 1.1
5 Horizontal reuse was added in Twig 1.1.
6
7.. note::
8
9 Horizontal reuse is an advanced Twig feature that is hardly ever needed in
10 regular templates. It is mainly used by projects that need to make
11 template blocks reusable without using inheritance.
12
13Template inheritance is one of the most powerful Twig's feature but it is
14limited to single inheritance; a template can only extend one other template.
15This limitation makes template inheritance simple to understand and easy to
16debug:
17
18.. code-block:: jinja
19
20 {% extends "base.html" %}
21
22 {% block title %}{% endblock %}
23 {% block content %}{% endblock %}
24
25Horizontal reuse is a way to achieve the same goal as multiple inheritance,
26but without the associated complexity:
27
28.. code-block:: jinja
29
30 {% extends "base.html" %}
31
32 {% use "blocks.html" %}
33
34 {% block title %}{% endblock %}
35 {% block content %}{% endblock %}
36
37The ``use`` statement tells Twig to import the blocks defined in
38```blocks.html`` into the current template (it's like macros, but for blocks):
39
40.. code-block:: jinja
41
42 # blocks.html
43 {% block sidebar %}{% endblock %}
44
45In this example, the ``use`` statement imports the ``sidebar`` block into the
46main template. The code is mostly equivalent to the following one (the
47imported blocks are not outputted automatically):
48
49.. code-block:: jinja
50
51 {% extends "base.html" %}
52
53 {% block sidebar %}{% endblock %}
54 {% block title %}{% endblock %}
55 {% block content %}{% endblock %}
56
57.. note::
58
59 The ``use`` tag only imports a template if it does not extend another
60 template, if it does not define macros, and if the body is empty. But it
61 can *use* other templates.
62
63.. note::
64
65 Because ``use`` statements are resolved independently of the context
66 passed to the template, the template reference cannot be an expression.
67
68The main template can also override any imported block. If the template
69already defines the ``sidebar`` block, then the one defined in ``blocks.html``
70is ignored. To avoid name conflicts, you can rename imported blocks:
71
72.. code-block:: jinja
73
74 {% extends "base.html" %}
75
76 {% use "blocks.html" with sidebar as base_sidebar %}
77
78 {% block sidebar %}{% endblock %}
79 {% block title %}{% endblock %}
80 {% block content %}{% endblock %}
81
82.. versionadded:: 1.3
83 The ``parent()`` support was added in Twig 1.3.
84
85The ``parent()`` function automatically determines the correct inheritance
86tree, so it can be used when overriding a block defined in an imported
87template:
88
89.. code-block:: jinja
90
91 {% extends "base.html" %}
92
93 {% use "blocks.html" %}
94
95 {% block sidebar %}
96 {{ parent() }}
97 {% endblock %}
98
99 {% block title %}{% endblock %}
100 {% block content %}{% endblock %}
101
102In this example, ``parent()`` will correctly call the ``sidebar`` block from
103the ``blocks.html`` template.
104
105.. tip::
106
107 In Twig 1.2, renaming allows you to simulate inheritance by calling the
108 "parent" block:
109
110 .. code-block:: jinja
111
112 {% extends "base.html" %}
113
114 {% use "blocks.html" with sidebar as parent_sidebar %}
115
116 {% block sidebar %}
117 {{ block('parent_sidebar') }}
118 {% endblock %}
119
120.. note::
121
122 You can use as many ``use`` statements as you want in any given template.
123 If two imported templates define the same block, the latest one wins.
diff --git a/vendor/twig/twig/doc/tags/verbatim.rst b/vendor/twig/twig/doc/tags/verbatim.rst
new file mode 100644
index 00000000..fe61ca1b
--- /dev/null
+++ b/vendor/twig/twig/doc/tags/verbatim.rst
@@ -0,0 +1,24 @@
1``verbatim``
2============
3
4.. versionadded:: 1.12
5 The ``verbatim`` tag was added in Twig 1.12 (it was named ``raw`` before).
6
7The ``verbatim`` tag marks sections as being raw text that should not be
8parsed. For example to put Twig syntax as example into a template you can use
9this snippet:
10
11.. code-block:: jinja
12
13 {% verbatim %}
14 <ul>
15 {% for item in seq %}
16 <li>{{ item }}</li>
17 {% endfor %}
18 </ul>
19 {% endverbatim %}
20
21.. note::
22
23 The ``verbatim`` tag works in the exact same way as the old ``raw`` tag,
24 but was renamed to avoid confusion with the ``raw`` filter. \ No newline at end of file
diff --git a/vendor/twig/twig/doc/templates.rst b/vendor/twig/twig/doc/templates.rst
new file mode 100644
index 00000000..542b8aef
--- /dev/null
+++ b/vendor/twig/twig/doc/templates.rst
@@ -0,0 +1,851 @@
1Twig for Template Designers
2===========================
3
4This document describes the syntax and semantics of the template engine and
5will be most useful as reference to those creating Twig templates.
6
7Synopsis
8--------
9
10A template is simply a text file. It can generate any text-based format (HTML,
11XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or
12``.xml`` are just fine.
13
14A template contains **variables** or **expressions**, which get replaced with
15values when the template is evaluated, and **tags**, which control the logic
16of the template.
17
18Below is a minimal template that illustrates a few basics. We will cover the
19details later on:
20
21.. code-block:: html+jinja
22
23 <!DOCTYPE html>
24 <html>
25 <head>
26 <title>My Webpage</title>
27 </head>
28 <body>
29 <ul id="navigation">
30 {% for item in navigation %}
31 <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
32 {% endfor %}
33 </ul>
34
35 <h1>My Webpage</h1>
36 {{ a_variable }}
37 </body>
38 </html>
39
40There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first
41one is used to execute statements such as for-loops, the latter prints the
42result of an expression to the template.
43
44IDEs Integration
45----------------
46
47Many IDEs support syntax highlighting and auto-completion for Twig:
48
49* *Textmate* via the `Twig bundle`_
50* *Vim* via the `Jinja syntax plugin`_
51* *Netbeans* via the `Twig syntax plugin`_ (until 7.1, native as of 7.2)
52* *PhpStorm* (native as of 2.1)
53* *Eclipse* via the `Twig plugin`_
54* *Sublime Text* via the `Twig bundle`_
55* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects)
56* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_
57* *Coda 2* via the `other Twig syntax mode`_
58* *Komodo* and *Komodo Edit* via the Twig highlight/syntax check mode
59* *Notepad++* via the `Notepad++ Twig Highlighter`_
60* *Emacs* via `web-mode.el`_
61
62Variables
63---------
64
65The application passes variables to the templates you can mess around in the
66template. Variables may have attributes or elements on them you can access
67too. How a variable looks like heavily depends on the application providing
68those.
69
70You can use a dot (``.``) to access attributes of a variable (methods or
71properties of a PHP object, or items of a PHP array), or the so-called
72"subscript" syntax (``[]``):
73
74.. code-block:: jinja
75
76 {{ foo.bar }}
77 {{ foo['bar'] }}
78
79When the attribute contains special characters (like ``-`` that would be
80interpreted as the minus operator), use the ``attribute`` function instead to
81access the variable attribute:
82
83.. code-block:: jinja
84
85 {# equivalent to the non-working foo.data-foo #}
86 {{ attribute(foo, 'data-foo') }}
87
88.. note::
89
90 It's important to know that the curly braces are *not* part of the
91 variable but the print statement. If you access variables inside tags
92 don't put the braces around.
93
94If a variable or attribute does not exist, you will get back a ``null`` value
95when the ``strict_variables`` option is set to ``false``, otherwise Twig will
96throw an error (see :ref:`environment options<environment_options>`).
97
98.. sidebar:: Implementation
99
100 For convenience sake ``foo.bar`` does the following things on the PHP
101 layer:
102
103 * check if ``foo`` is an array and ``bar`` a valid element;
104 * if not, and if ``foo`` is an object, check that ``bar`` is a valid property;
105 * if not, and if ``foo`` is an object, check that ``bar`` is a valid method
106 (even if ``bar`` is the constructor - use ``__construct()`` instead);
107 * if not, and if ``foo`` is an object, check that ``getBar`` is a valid method;
108 * if not, and if ``foo`` is an object, check that ``isBar`` is a valid method;
109 * if not, return a ``null`` value.
110
111 ``foo['bar']`` on the other hand only works with PHP arrays:
112
113 * check if ``foo`` is an array and ``bar`` a valid element;
114 * if not, return a ``null`` value.
115
116.. note::
117
118 If you want to get a dynamic attribute on a variable, use the
119 :doc:`attribute<functions/attribute>` function instead.
120
121Global Variables
122~~~~~~~~~~~~~~~~
123
124The following variables are always available in templates:
125
126* ``_self``: references the current template;
127* ``_context``: references the current context;
128* ``_charset``: references the current charset.
129
130Setting Variables
131~~~~~~~~~~~~~~~~~
132
133You can assign values to variables inside code blocks. Assignments use the
134:doc:`set<tags/set>` tag:
135
136.. code-block:: jinja
137
138 {% set foo = 'foo' %}
139 {% set foo = [1, 2] %}
140 {% set foo = {'foo': 'bar'} %}
141
142Filters
143-------
144
145Variables can be modified by **filters**. Filters are separated from the
146variable by a pipe symbol (``|``) and may have optional arguments in
147parentheses. Multiple filters can be chained. The output of one filter is
148applied to the next.
149
150The following example removes all HTML tags from the ``name`` and title-cases
151it:
152
153.. code-block:: jinja
154
155 {{ name|striptags|title }}
156
157Filters that accept arguments have parentheses around the arguments. This
158example will join a list by commas:
159
160.. code-block:: jinja
161
162 {{ list|join(', ') }}
163
164To apply a filter on a section of code, wrap it with the
165:doc:`filter<tags/filter>` tag:
166
167.. code-block:: jinja
168
169 {% filter upper %}
170 This text becomes uppercase
171 {% endfilter %}
172
173Go to the :doc:`filters<filters/index>` page to learn more about the built-in
174filters.
175
176Functions
177---------
178
179Functions can be called to generate content. Functions are called by their
180name followed by parentheses (``()``) and may have arguments.
181
182For instance, the ``range`` function returns a list containing an arithmetic
183progression of integers:
184
185.. code-block:: jinja
186
187 {% for i in range(0, 3) %}
188 {{ i }},
189 {% endfor %}
190
191Go to the :doc:`functions<functions/index>` page to learn more about the
192built-in functions.
193
194Named Arguments
195---------------
196
197.. versionadded:: 1.12
198 Support for named arguments was added in Twig 1.12.
199
200Arguments for filters and functions can also be passed as *named arguments*:
201
202.. code-block:: jinja
203
204 {% for i in range(low=1, high=10, step=2) %}
205 {{ i }},
206 {% endfor %}
207
208Using named arguments makes your templates more explicit about the meaning of
209the values you pass as arguments:
210
211.. code-block:: jinja
212
213 {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }}
214
215 {# versus #}
216
217 {{ data|convert_encoding(from='iso-2022-jp', to='UTF-8') }}
218
219Named arguments also allow you to skip some arguments for which you don't want
220to change the default value:
221
222.. code-block:: jinja
223
224 {# the first argument is the date format, which defaults to the global date format if null is passed #}
225 {{ "now"|date(null, "Europe/Paris") }}
226
227 {# or skip the format value by using a named argument for the timezone #}
228 {{ "now"|date(timezone="Europe/Paris") }}
229
230You can also use both positional and named arguments in one call, in which
231case positional arguments must always come before named arguments:
232
233.. code-block:: jinja
234
235 {{ "now"|date('d/m/Y H:i', timezone="Europe/Paris") }}
236
237.. tip::
238
239 Each function and filter documentation page has a section where the names
240 of all arguments are listed when supported.
241
242Control Structure
243-----------------
244
245A control structure refers to all those things that control the flow of a
246program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as
247well as things like blocks. Control structures appear inside ``{% ... %}``
248blocks.
249
250For example, to display a list of users provided in a variable called
251``users``, use the :doc:`for<tags/for>` tag:
252
253.. code-block:: jinja
254
255 <h1>Members</h1>
256 <ul>
257 {% for user in users %}
258 <li>{{ user.username|e }}</li>
259 {% endfor %}
260 </ul>
261
262The :doc:`if<tags/if>` tag can be used to test an expression:
263
264.. code-block:: jinja
265
266 {% if users|length > 0 %}
267 <ul>
268 {% for user in users %}
269 <li>{{ user.username|e }}</li>
270 {% endfor %}
271 </ul>
272 {% endif %}
273
274Go to the :doc:`tags<tags/index>` page to learn more about the built-in tags.
275
276Comments
277--------
278
279To comment-out part of a line in a template, use the comment syntax ``{# ...
280#}``. This is useful for debugging or to add information for other template
281designers or yourself:
282
283.. code-block:: jinja
284
285 {# note: disabled template because we no longer use this
286 {% for user in users %}
287 ...
288 {% endfor %}
289 #}
290
291Including other Templates
292-------------------------
293
294The :doc:`include<tags/include>` tag is useful to include a template and
295return the rendered content of that template into the current one:
296
297.. code-block:: jinja
298
299 {% include 'sidebar.html' %}
300
301Per default included templates are passed the current context.
302
303The context that is passed to the included template includes variables defined
304in the template:
305
306.. code-block:: jinja
307
308 {% for box in boxes %}
309 {% include "render_box.html" %}
310 {% endfor %}
311
312The included template ``render_box.html`` is able to access ``box``.
313
314The filename of the template depends on the template loader. For instance, the
315``Twig_Loader_Filesystem`` allows you to access other templates by giving the
316filename. You can access templates in subdirectories with a slash:
317
318.. code-block:: jinja
319
320 {% include "sections/articles/sidebar.html" %}
321
322This behavior depends on the application embedding Twig.
323
324Template Inheritance
325--------------------
326
327The most powerful part of Twig is template inheritance. Template inheritance
328allows you to build a base "skeleton" template that contains all the common
329elements of your site and defines **blocks** that child templates can
330override.
331
332Sounds complicated but is very basic. It's easier to understand it by
333starting with an example.
334
335Let's define a base template, ``base.html``, which defines a simple HTML
336skeleton document that you might use for a simple two-column page:
337
338.. code-block:: html+jinja
339
340 <!DOCTYPE html>
341 <html>
342 <head>
343 {% block head %}
344 <link rel="stylesheet" href="style.css" />
345 <title>{% block title %}{% endblock %} - My Webpage</title>
346 {% endblock %}
347 </head>
348 <body>
349 <div id="content">{% block content %}{% endblock %}</div>
350 <div id="footer">
351 {% block footer %}
352 &copy; Copyright 2011 by <a href="http://domain.invalid/">you</a>.
353 {% endblock %}
354 </div>
355 </body>
356 </html>
357
358In this example, the :doc:`block<tags/block>` tags define four blocks that
359child templates can fill in. All the ``block`` tag does is to tell the
360template engine that a child template may override those portions of the
361template.
362
363A child template might look like this:
364
365.. code-block:: jinja
366
367 {% extends "base.html" %}
368
369 {% block title %}Index{% endblock %}
370 {% block head %}
371 {{ parent() }}
372 <style type="text/css">
373 .important { color: #336699; }
374 </style>
375 {% endblock %}
376 {% block content %}
377 <h1>Index</h1>
378 <p class="important">
379 Welcome to my awesome homepage.
380 </p>
381 {% endblock %}
382
383The :doc:`extends<tags/extends>` tag is the key here. It tells the template
384engine that this template "extends" another template. When the template system
385evaluates this template, first it locates the parent. The extends tag should
386be the first tag in the template.
387
388Note that since the child template doesn't define the ``footer`` block, the
389value from the parent template is used instead.
390
391It's possible to render the contents of the parent block by using the
392:doc:`parent<functions/parent>` function. This gives back the results of the
393parent block:
394
395.. code-block:: jinja
396
397 {% block sidebar %}
398 <h3>Table Of Contents</h3>
399 ...
400 {{ parent() }}
401 {% endblock %}
402
403.. tip::
404
405 The documentation page for the :doc:`extends<tags/extends>` tag describes
406 more advanced features like block nesting, scope, dynamic inheritance, and
407 conditional inheritance.
408
409.. note::
410
411 Twig also supports multiple inheritance with the so called horizontal reuse
412 with the help of the :doc:`use<tags/use>` tag. This is an advanced feature
413 hardly ever needed in regular templates.
414
415HTML Escaping
416-------------
417
418When generating HTML from templates, there's always a risk that a variable
419will include characters that affect the resulting HTML. There are two
420approaches: manually escaping each variable or automatically escaping
421everything by default.
422
423Twig supports both, automatic escaping is enabled by default.
424
425.. note::
426
427 Automatic escaping is only supported if the *escaper* extension has been
428 enabled (which is the default).
429
430Working with Manual Escaping
431~~~~~~~~~~~~~~~~~~~~~~~~~~~~
432
433If manual escaping is enabled, it is **your** responsibility to escape
434variables if needed. What to escape? Any variable you don't trust.
435
436Escaping works by piping the variable through the
437:doc:`escape<filters/escape>` or ``e`` filter:
438
439.. code-block:: jinja
440
441 {{ user.username|e }}
442
443By default, the ``escape`` filter uses the ``html`` strategy, but depending on
444the escaping context, you might want to explicitly use any other available
445strategies:
446
447.. code-block:: jinja
448
449 {{ user.username|e('js') }}
450 {{ user.username|e('css') }}
451 {{ user.username|e('url') }}
452 {{ user.username|e('html_attr') }}
453
454Working with Automatic Escaping
455~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
456
457Whether automatic escaping is enabled or not, you can mark a section of a
458template to be escaped or not by using the :doc:`autoescape<tags/autoescape>`
459tag:
460
461.. code-block:: jinja
462
463 {% autoescape %}
464 Everything will be automatically escaped in this block (using the HTML strategy)
465 {% endautoescape %}
466
467By default, auto-escaping uses the ``html`` escaping strategy. If you output
468variables in other contexts, you need to explicitly escape them with the
469appropriate escaping strategy:
470
471.. code-block:: jinja
472
473 {% autoescape 'js' %}
474 Everything will be automatically escaped in this block (using the JS strategy)
475 {% endautoescape %}
476
477Escaping
478--------
479
480It is sometimes desirable or even necessary to have Twig ignore parts it would
481otherwise handle as variables or blocks. For example if the default syntax is
482used and you want to use ``{{`` as raw string in the template and not start a
483variable you have to use a trick.
484
485The easiest way is to output the variable delimiter (``{{``) by using a variable
486expression:
487
488.. code-block:: jinja
489
490 {{ '{{' }}
491
492For bigger sections it makes sense to mark a block
493:doc:`verbatim<tags/verbatim>`.
494
495Macros
496------
497
498.. versionadded:: 1.12
499 Support for default argument values was added in Twig 1.12.
500
501Macros are comparable with functions in regular programming languages. They
502are useful to reuse often used HTML fragments to not repeat yourself.
503
504A macro is defined via the :doc:`macro<tags/macro>` tag. Here is a small example
505(subsequently called ``forms.html``) of a macro that renders a form element:
506
507.. code-block:: jinja
508
509 {% macro input(name, value, type, size) %}
510 <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
511 {% endmacro %}
512
513Macros can be defined in any template, and need to be "imported" via the
514:doc:`import<tags/import>` tag before being used:
515
516.. code-block:: jinja
517
518 {% import "forms.html" as forms %}
519
520 <p>{{ forms.input('username') }}</p>
521
522Alternatively, you can import individual macro names from a template into the
523current namespace via the :doc:`from<tags/from>` tag and optionally alias them:
524
525.. code-block:: jinja
526
527 {% from 'forms.html' import input as input_field %}
528
529 <dl>
530 <dt>Username</dt>
531 <dd>{{ input_field('username') }}</dd>
532 <dt>Password</dt>
533 <dd>{{ input_field('password', '', 'password') }}</dd>
534 </dl>
535
536A default value can also be defined for macro arguments when not provided in a
537macro call:
538
539.. code-block:: jinja
540
541 {% macro input(name, value = "", type = "text", size = 20) %}
542 <input type="{{ type }}" name="{{ name }}" value="{{ value|e }}" size="{{ size }}" />
543 {% endmacro %}
544
545.. _twig-expressions:
546
547Expressions
548-----------
549
550Twig allows expressions everywhere. These work very similar to regular PHP and
551even if you're not working with PHP you should feel comfortable with it.
552
553.. note::
554
555 The operator precedence is as follows, with the lowest-precedence
556 operators listed first: ``b-and``, ``b-xor``, ``b-or``, ``or``, ``and``,
557 ``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``, ``in``, ``..``, ``+``,
558 ``-``, ``~``, ``*``, ``/``, ``//``, ``%``, ``is``, ``**``, ``|``, ``[]``,
559 and ``.``:
560
561 .. code-block:: jinja
562
563 {% set greeting = 'Hello' %}
564 {% set name = 'Fabien' %}
565
566 {{ greeting ~ name|lower }} {# Hello fabien #}
567
568 {# use parenthesis to change precedence #}
569 {{ (greeting ~ name)|lower }} {# hello fabien #}
570
571Literals
572~~~~~~~~
573
574.. versionadded:: 1.5
575 Support for hash keys as names and expressions was added in Twig 1.5.
576
577The simplest form of expressions are literals. Literals are representations
578for PHP types such as strings, numbers, and arrays. The following literals
579exist:
580
581* ``"Hello World"``: Everything between two double or single quotes is a
582 string. They are useful whenever you need a string in the template (for
583 example as arguments to function calls, filters or just to extend or include
584 a template). A string can contain a delimiter if it is preceded by a
585 backslash (``\``) -- like in ``'It\'s good'``.
586
587* ``42`` / ``42.23``: Integers and floating point numbers are created by just
588 writing the number down. If a dot is present the number is a float,
589 otherwise an integer.
590
591* ``["foo", "bar"]``: Arrays are defined by a sequence of expressions
592 separated by a comma (``,``) and wrapped with squared brackets (``[]``).
593
594* ``{"foo": "bar"}``: Hashes are defined by a list of keys and values
595 separated by a comma (``,``) and wrapped with curly braces (``{}``):
596
597 .. code-block:: jinja
598
599 {# keys as string #}
600 { 'foo': 'foo', 'bar': 'bar' }
601
602 {# keys as names (equivalent to the previous hash) -- as of Twig 1.5 #}
603 { foo: 'foo', bar: 'bar' }
604
605 {# keys as integer #}
606 { 2: 'foo', 4: 'bar' }
607
608 {# keys as expressions (the expression must be enclosed into parentheses) -- as of Twig 1.5 #}
609 { (1 + 1): 'foo', (a ~ 'b'): 'bar' }
610
611* ``true`` / ``false``: ``true`` represents the true value, ``false``
612 represents the false value.
613
614* ``null``: ``null`` represents no specific value. This is the value returned
615 when a variable does not exist. ``none`` is an alias for ``null``.
616
617Arrays and hashes can be nested:
618
619.. code-block:: jinja
620
621 {% set foo = [1, {"foo": "bar"}] %}
622
623.. tip::
624
625 Using double-quoted or single-quoted strings has no impact on performance
626 but string interpolation is only supported in double-quoted strings.
627
628Math
629~~~~
630
631Twig allows you to calculate with values. This is rarely useful in templates
632but exists for completeness' sake. The following operators are supported:
633
634* ``+``: Adds two objects together (the operands are casted to numbers). ``{{
635 1 + 1 }}`` is ``2``.
636
637* ``-``: Subtracts the second number from the first one. ``{{ 3 - 2 }}`` is
638 ``1``.
639
640* ``/``: Divides two numbers. The returned value will be a floating point
641 number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``.
642
643* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is
644 ``4``.
645
646* ``//``: Divides two numbers and returns the truncated integer result. ``{{
647 20 // 7 }}`` is ``2``.
648
649* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would
650 return ``4``.
651
652* ``**``: Raises the left operand to the power of the right operand. ``{{ 2 **
653 3 }}`` would return ``8``.
654
655Logic
656~~~~~
657
658You can combine multiple expressions with the following operators:
659
660* ``and``: Returns true if the left and the right operands are both true.
661
662* ``or``: Returns true if the left or the right operand is true.
663
664* ``not``: Negates a statement.
665
666* ``(expr)``: Groups an expression.
667
668.. note::
669
670 Twig also support bitwise operators (``b-and``, ``b-xor``, and ``b-or``).
671
672Comparisons
673~~~~~~~~~~~
674
675The following comparison operators are supported in any expression: ``==``,
676``!=``, ``<``, ``>``, ``>=``, and ``<=``.
677
678Containment Operator
679~~~~~~~~~~~~~~~~~~~~
680
681The ``in`` operator performs containment test.
682
683It returns ``true`` if the left operand is contained in the right:
684
685.. code-block:: jinja
686
687 {# returns true #}
688
689 {{ 1 in [1, 2, 3] }}
690
691 {{ 'cd' in 'abcde' }}
692
693.. tip::
694
695 You can use this filter to perform a containment test on strings, arrays,
696 or objects implementing the ``Traversable`` interface.
697
698To perform a negative test, use the ``not in`` operator:
699
700.. code-block:: jinja
701
702 {% if 1 not in [1, 2, 3] %}
703
704 {# is equivalent to #}
705 {% if not (1 in [1, 2, 3]) %}
706
707Test Operator
708~~~~~~~~~~~~~
709
710The ``is`` operator performs tests. Tests can be used to test a variable against
711a common expression. The right operand is name of the test:
712
713.. code-block:: jinja
714
715 {# find out if a variable is odd #}
716
717 {{ name is odd }}
718
719Tests can accept arguments too:
720
721.. code-block:: jinja
722
723 {% if loop.index is divisibleby(3) %}
724
725Tests can be negated by using the ``is not`` operator:
726
727.. code-block:: jinja
728
729 {% if loop.index is not divisibleby(3) %}
730
731 {# is equivalent to #}
732 {% if not (loop.index is divisibleby(3)) %}
733
734Go to the :doc:`tests<tests/index>` page to learn more about the built-in
735tests.
736
737Other Operators
738~~~~~~~~~~~~~~~
739
740.. versionadded:: 1.12.0
741 Support for the extended ternary operator was added in Twig 1.12.0.
742
743The following operators are very useful but don't fit into any of the other
744categories:
745
746* ``..``: Creates a sequence based on the operand before and after the
747 operator (this is just syntactic sugar for the :doc:`range<functions/range>`
748 function).
749
750* ``|``: Applies a filter.
751
752* ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello
753 " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello
754 John!``.
755
756* ``.``, ``[]``: Gets an attribute of an object.
757
758* ``?:``: The ternary operator:
759
760 .. code-block:: jinja
761
762 {{ foo ? 'yes' : 'no' }}
763
764 {# as of Twig 1.12.0 #}
765 {{ foo ?: 'no' }} == {{ foo ? foo : 'no' }}
766 {{ foo ? 'yes' }} == {{ foo ? 'yes' : '' }}
767
768String Interpolation
769~~~~~~~~~~~~~~~~~~~~
770
771.. versionadded:: 1.5
772 String interpolation was added in Twig 1.5.
773
774String interpolation (`#{expression}`) allows any valid expression to appear
775within a *double-quoted string*. The result of evaluating that expression is
776inserted into the string:
777
778.. code-block:: jinja
779
780 {{ "foo #{bar} baz" }}
781 {{ "foo #{1 + 2} baz" }}
782
783Whitespace Control
784------------------
785
786.. versionadded:: 1.1
787 Tag level whitespace control was added in Twig 1.1.
788
789The first newline after a template tag is removed automatically (like in PHP.)
790Whitespace is not further modified by the template engine, so each whitespace
791(spaces, tabs, newlines etc.) is returned unchanged.
792
793Use the ``spaceless`` tag to remove whitespace *between HTML tags*:
794
795.. code-block:: jinja
796
797 {% spaceless %}
798 <div>
799 <strong>foo bar</strong>
800 </div>
801 {% endspaceless %}
802
803 {# output will be <div><strong>foo bar</strong></div> #}
804
805In addition to the spaceless tag you can also control whitespace on a per tag
806level. By using the whitespace control modifier on your tags, you can trim
807leading and or trailing whitespace:
808
809.. code-block:: jinja
810
811 {% set value = 'no spaces' %}
812 {#- No leading/trailing whitespace -#}
813 {%- if true -%}
814 {{- value -}}
815 {%- endif -%}
816
817 {# output 'no spaces' #}
818
819The above sample shows the default whitespace control modifier, and how you can
820use it to remove whitespace around tags. Trimming space will consume all whitespace
821for that side of the tag. It is possible to use whitespace trimming on one side
822of a tag:
823
824.. code-block:: jinja
825
826 {% set value = 'no spaces' %}
827 <li> {{- value }} </li>
828
829 {# outputs '<li>no spaces </li>' #}
830
831Extensions
832----------
833
834Twig can be easily extended.
835
836If you are looking for new tags, filters, or functions, have a look at the Twig official
837`extension repository`_.
838
839If you want to create your own, read the :ref:`Creating an
840Extension<creating_extensions>` chapter.
841
842.. _`Twig bundle`: https://github.com/Anomareh/PHP-Twig.tmbundle
843.. _`Jinja syntax plugin`: http://jinja.pocoo.org/2/documentation/integration
844.. _`Twig syntax plugin`: http://plugins.netbeans.org/plugin/37069/php-twig
845.. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin
846.. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language
847.. _`extension repository`: http://github.com/fabpot/Twig-extensions
848.. _`Twig syntax mode`: https://github.com/bobthecow/Twig-HTML.mode
849.. _`other Twig syntax mode`: https://github.com/muxx/Twig-HTML.mode
850.. _`Notepad++ Twig Highlighter`: https://github.com/Banane9/notepadplusplus-twig
851.. _`web-mode.el`: http://web-mode.org/
diff --git a/vendor/twig/twig/doc/tests/constant.rst b/vendor/twig/twig/doc/tests/constant.rst
new file mode 100644
index 00000000..8d0724a8
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/constant.rst
@@ -0,0 +1,22 @@
1``constant``
2============
3
4.. versionadded: 1.13.1
5 constant now accepts object instances as the second argument.
6
7``constant`` checks if a variable has the exact same value as a constant. You
8can use either global constants or class constants:
9
10.. code-block:: jinja
11
12 {% if post.status is constant('Post::PUBLISHED') %}
13 the status attribute is exactly the same as Post::PUBLISHED
14 {% endif %}
15
16You can test constants from object instances as well:
17
18.. code-block:: jinja
19
20 {% if post.status is constant('PUBLISHED', post) %}
21 the status attribute is exactly the same as Post::PUBLISHED
22 {% endif %}
diff --git a/vendor/twig/twig/doc/tests/defined.rst b/vendor/twig/twig/doc/tests/defined.rst
new file mode 100644
index 00000000..702ce725
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/defined.rst
@@ -0,0 +1,30 @@
1``defined``
2===========
3
4``defined`` checks if a variable is defined in the current context. This is very
5useful if you use the ``strict_variables`` option:
6
7.. code-block:: jinja
8
9 {# defined works with variable names #}
10 {% if foo is defined %}
11 ...
12 {% endif %}
13
14 {# and attributes on variables names #}
15 {% if foo.bar is defined %}
16 ...
17 {% endif %}
18
19 {% if foo['bar'] is defined %}
20 ...
21 {% endif %}
22
23When using the ``defined`` test on an expression that uses variables in some
24method calls, be sure that they are all defined first:
25
26.. code-block:: jinja
27
28 {% if var is defined and foo.method(var) is defined %}
29 ...
30 {% endif %}
diff --git a/vendor/twig/twig/doc/tests/divisibleby.rst b/vendor/twig/twig/doc/tests/divisibleby.rst
new file mode 100644
index 00000000..9b0b9644
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/divisibleby.rst
@@ -0,0 +1,10 @@
1``divisibleby``
2===============
3
4``divisibleby`` checks if a variable is divisible by a number:
5
6.. code-block:: jinja
7
8 {% if loop.index is divisibleby(3) %}
9 ...
10 {% endif %}
diff --git a/vendor/twig/twig/doc/tests/empty.rst b/vendor/twig/twig/doc/tests/empty.rst
new file mode 100644
index 00000000..e5b55999
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/empty.rst
@@ -0,0 +1,11 @@
1``empty``
2=========
3
4``empty`` checks if a variable is empty:
5
6.. code-block:: jinja
7
8 {# evaluates to true if the foo variable is null, false, an empty array, or the empty string #}
9 {% if foo is empty %}
10 ...
11 {% endif %}
diff --git a/vendor/twig/twig/doc/tests/even.rst b/vendor/twig/twig/doc/tests/even.rst
new file mode 100644
index 00000000..6ab5cc39
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/even.rst
@@ -0,0 +1,10 @@
1``even``
2========
3
4``even`` returns ``true`` if the given number is even:
5
6.. code-block:: jinja
7
8 {{ var is even }}
9
10.. seealso:: :doc:`odd<../tests/odd>`
diff --git a/vendor/twig/twig/doc/tests/index.rst b/vendor/twig/twig/doc/tests/index.rst
new file mode 100644
index 00000000..c63208ee
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/index.rst
@@ -0,0 +1,15 @@
1Tests
2=====
3
4.. toctree::
5 :maxdepth: 1
6
7 constant
8 defined
9 divisibleby
10 empty
11 even
12 iterable
13 null
14 odd
15 sameas
diff --git a/vendor/twig/twig/doc/tests/iterable.rst b/vendor/twig/twig/doc/tests/iterable.rst
new file mode 100644
index 00000000..89a172f7
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/iterable.rst
@@ -0,0 +1,19 @@
1``iterable``
2============
3
4.. versionadded:: 1.7
5 The iterable test was added in Twig 1.7.
6
7``iterable`` checks if a variable is an array or a traversable object:
8
9.. code-block:: jinja
10
11 {# evaluates to true if the foo variable is iterable #}
12 {% if users is iterable %}
13 {% for user in users %}
14 Hello {{ user }}!
15 {% endfor %}
16 {% else %}
17 {# users is probably a string #}
18 Hello {{ users }}!
19 {% endif %}
diff --git a/vendor/twig/twig/doc/tests/null.rst b/vendor/twig/twig/doc/tests/null.rst
new file mode 100644
index 00000000..44eec62e
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/null.rst
@@ -0,0 +1,12 @@
1``null``
2========
3
4``null`` returns ``true`` if the variable is ``null``:
5
6.. code-block:: jinja
7
8 {{ var is null }}
9
10.. note::
11
12 ``none`` is an alias for ``null``.
diff --git a/vendor/twig/twig/doc/tests/odd.rst b/vendor/twig/twig/doc/tests/odd.rst
new file mode 100644
index 00000000..9eece777
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/odd.rst
@@ -0,0 +1,10 @@
1``odd``
2=======
3
4``odd`` returns ``true`` if the given number is odd:
5
6.. code-block:: jinja
7
8 {{ var is odd }}
9
10.. seealso:: :doc:`even<../tests/even>`
diff --git a/vendor/twig/twig/doc/tests/sameas.rst b/vendor/twig/twig/doc/tests/sameas.rst
new file mode 100644
index 00000000..efb15c35
--- /dev/null
+++ b/vendor/twig/twig/doc/tests/sameas.rst
@@ -0,0 +1,11 @@
1``sameas``
2==========
3
4``sameas`` checks if a variable points to the same memory address than another
5variable:
6
7.. code-block:: jinja
8
9 {% if foo.attribute is sameas(false) %}
10 the foo attribute really is the ``false`` PHP value
11 {% endif %}
diff --git a/vendor/twig/twig/ext/twig/.gitignore b/vendor/twig/twig/ext/twig/.gitignore
new file mode 100644
index 00000000..56ea76cc
--- /dev/null
+++ b/vendor/twig/twig/ext/twig/.gitignore
@@ -0,0 +1,30 @@
1*.sw*
2.deps
3Makefile
4Makefile.fragments
5Makefile.global
6Makefile.objects
7acinclude.m4
8aclocal.m4
9build/
10config.cache
11config.guess
12config.h
13config.h.in
14config.log
15config.nice
16config.status
17config.sub
18configure
19configure.in
20install-sh
21libtool
22ltmain.sh
23missing
24mkinstalldirs
25run-tests.php
26twig.loT
27.libs/
28modules/
29twig.la
30twig.lo
diff --git a/vendor/twig/twig/ext/twig/LICENSE b/vendor/twig/twig/ext/twig/LICENSE
new file mode 100644
index 00000000..66b8bb4c
--- /dev/null
+++ b/vendor/twig/twig/ext/twig/LICENSE
@@ -0,0 +1,22 @@
1Copyright (c) 2011, Derick Rethans <derick@derickrethans.nl>
2All rights reserved.
3
4Redistribution and use in source and binary forms, with or without
5modification, are permitted provided that the following conditions are met:
6
7 * Redistributions of source code must retain the above copyright notice,
8 this list of conditions and the following disclaimer.
9 * Redistributions in binary form must reproduce the above copyright
10 notice, this list of conditions and the following disclaimer in the
11 documentation and/or other materials provided with the distribution.
12
13THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
14AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
16DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
17FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
19SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
20CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
21OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
22OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/vendor/twig/twig/ext/twig/config.m4 b/vendor/twig/twig/ext/twig/config.m4
new file mode 100644
index 00000000..83486be4
--- /dev/null
+++ b/vendor/twig/twig/ext/twig/config.m4
@@ -0,0 +1,8 @@
1dnl config.m4 for extension twig
2
3PHP_ARG_ENABLE(twig, whether to enable twig support,
4[ --enable-twig Enable twig support])
5
6if test "$PHP_TWIG" != "no"; then
7 PHP_NEW_EXTENSION(twig, twig.c, $ext_shared)
8fi
diff --git a/vendor/twig/twig/ext/twig/config.w32 b/vendor/twig/twig/ext/twig/config.w32
new file mode 100644
index 00000000..cb287b99
--- /dev/null
+++ b/vendor/twig/twig/ext/twig/config.w32
@@ -0,0 +1,8 @@
1// vim:ft=javascript
2
3ARG_ENABLE("twig", "Twig support", "no");
4
5if (PHP_TWIG != "no") {
6 AC_DEFINE('HAVE_TWIG', 1);
7 EXTENSION('twig', 'twig.c');
8}
diff --git a/vendor/twig/twig/ext/twig/php_twig.h b/vendor/twig/twig/ext/twig/php_twig.h
new file mode 100644
index 00000000..1cf0ad44
--- /dev/null
+++ b/vendor/twig/twig/ext/twig/php_twig.h
@@ -0,0 +1,31 @@
1/*
2 +----------------------------------------------------------------------+
3 | Twig Extension |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2011 Derick Rethans |
6 +----------------------------------------------------------------------+
7 | Redistribution and use in source and binary forms, with or without |
8 | modification, are permitted provided that the conditions mentioned |
9 | in the accompanying LICENSE file are met (BSD, revised). |
10 +----------------------------------------------------------------------+
11 | Author: Derick Rethans <derick@derickrethans.nl> |
12 +----------------------------------------------------------------------+
13 */
14
15#ifndef PHP_TWIG_H
16#define PHP_TWIG_H
17
18#define PHP_TWIG_VERSION "1.13.2"
19
20#include "php.h"
21
22extern zend_module_entry twig_module_entry;
23#define phpext_twig_ptr &twig_module_entry
24
25#ifdef ZTS
26#include "TSRM.h"
27#endif
28
29PHP_FUNCTION(twig_template_get_attributes);
30
31#endif
diff --git a/vendor/twig/twig/ext/twig/twig.c b/vendor/twig/twig/ext/twig/twig.c
new file mode 100644
index 00000000..3ba9ff2c
--- /dev/null
+++ b/vendor/twig/twig/ext/twig/twig.c
@@ -0,0 +1,1076 @@
1/*
2 +----------------------------------------------------------------------+
3 | Twig Extension |
4 +----------------------------------------------------------------------+
5 | Copyright (c) 2011 Derick Rethans |
6 +----------------------------------------------------------------------+
7 | Redistribution and use in source and binary forms, with or without |
8 | modification, are permitted provided that the conditions mentioned |
9 | in the accompanying LICENSE file are met (BSD, revised). |
10 +----------------------------------------------------------------------+
11 | Author: Derick Rethans <derick@derickrethans.nl> |
12 +----------------------------------------------------------------------+
13 */
14
15#ifdef HAVE_CONFIG_H
16#include "config.h"
17#endif
18
19#include "php.h"
20#include "php_twig.h"
21#include "ext/standard/php_string.h"
22#include "ext/standard/php_smart_str.h"
23
24#include "Zend/zend_object_handlers.h"
25#include "Zend/zend_interfaces.h"
26#include "Zend/zend_exceptions.h"
27
28#ifndef Z_ADDREF_P
29#define Z_ADDREF_P(pz) (pz)->refcount++
30#endif
31
32#define FREE_DTOR(z) \
33 zval_dtor(z); \
34 efree(z);
35
36#if PHP_VERSION_ID >= 50300
37 #define APPLY_TSRMLS_DC TSRMLS_DC
38 #define APPLY_TSRMLS_CC TSRMLS_CC
39 #define APPLY_TSRMLS_FETCH()
40#else
41 #define APPLY_TSRMLS_DC
42 #define APPLY_TSRMLS_CC
43 #define APPLY_TSRMLS_FETCH() TSRMLS_FETCH()
44#endif
45
46ZEND_BEGIN_ARG_INFO_EX(twig_template_get_attribute_args, ZEND_SEND_BY_VAL, ZEND_RETURN_VALUE, 6)
47 ZEND_ARG_INFO(0, template)
48 ZEND_ARG_INFO(0, object)
49 ZEND_ARG_INFO(0, item)
50 ZEND_ARG_INFO(0, arguments)
51 ZEND_ARG_INFO(0, type)
52 ZEND_ARG_INFO(0, isDefinedTest)
53ZEND_END_ARG_INFO()
54
55zend_function_entry twig_functions[] = {
56 PHP_FE(twig_template_get_attributes, twig_template_get_attribute_args)
57 {NULL, NULL, NULL}
58};
59
60
61zend_module_entry twig_module_entry = {
62 STANDARD_MODULE_HEADER,
63 "twig",
64 twig_functions,
65 NULL,
66 NULL,
67 NULL,
68 NULL,
69 NULL,
70 PHP_TWIG_VERSION,
71 STANDARD_MODULE_PROPERTIES
72};
73
74
75#ifdef COMPILE_DL_TWIG
76ZEND_GET_MODULE(twig)
77#endif
78
79int TWIG_ARRAY_KEY_EXISTS(zval *array, zval *key)
80{
81 zval temp;
82 int result;
83
84 if (Z_TYPE_P(array) != IS_ARRAY) {
85 return 0;
86 }
87
88 switch (Z_TYPE_P(key)) {
89 case IS_NULL:
90 return zend_hash_exists(Z_ARRVAL_P(array), "", 1);
91
92 case IS_BOOL:
93 case IS_DOUBLE:
94 convert_to_long(key);
95 case IS_LONG:
96 return zend_hash_index_exists(Z_ARRVAL_P(array), Z_LVAL_P(key));
97
98 default:
99 convert_to_string(key);
100 return zend_symtable_exists(Z_ARRVAL_P(array), Z_STRVAL_P(key), Z_STRLEN_P(key) + 1);
101 }
102}
103
104int TWIG_INSTANCE_OF(zval *object, zend_class_entry *interface TSRMLS_DC)
105{
106 if (Z_TYPE_P(object) != IS_OBJECT) {
107 return 0;
108 }
109 return instanceof_function(Z_OBJCE_P(object), interface TSRMLS_CC);
110}
111
112int TWIG_INSTANCE_OF_USERLAND(zval *object, char *interface TSRMLS_DC)
113{
114 zend_class_entry **pce;
115 if (Z_TYPE_P(object) != IS_OBJECT) {
116 return 0;
117 }
118 if (zend_lookup_class(interface, strlen(interface), &pce TSRMLS_CC) == FAILURE) {
119 return 0;
120 }
121 return instanceof_function(Z_OBJCE_P(object), *pce TSRMLS_CC);
122}
123
124zval *TWIG_GET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
125{
126 zend_class_entry *ce = Z_OBJCE_P(object);
127 zval *retval;
128
129 if (Z_TYPE_P(object) == IS_OBJECT) {
130 SEPARATE_ARG_IF_REF(offset);
131 zend_call_method_with_1_params(&object, ce, NULL, "offsetget", &retval, offset);
132
133 zval_ptr_dtor(&offset);
134
135 if (!retval) {
136 if (!EG(exception)) {
137 zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
138 }
139 return NULL;
140 }
141
142 return retval;
143 }
144 return NULL;
145}
146
147int TWIG_ISSET_ARRAYOBJECT_ELEMENT(zval *object, zval *offset TSRMLS_DC)
148{
149 zend_class_entry *ce = Z_OBJCE_P(object);
150 zval *retval;
151
152 if (Z_TYPE_P(object) == IS_OBJECT) {
153 SEPARATE_ARG_IF_REF(offset);
154 zend_call_method_with_1_params(&object, ce, NULL, "offsetexists", &retval, offset);
155
156 zval_ptr_dtor(&offset);
157
158 if (!retval) {
159 if (!EG(exception)) {
160 zend_error(E_ERROR, "Undefined offset for object of type %s used as array", ce->name);
161 }
162 return 0;
163 }
164
165 return (retval && Z_TYPE_P(retval) == IS_BOOL && Z_LVAL_P(retval));
166 }
167 return 0;
168}
169
170char *TWIG_STRTOLOWER(const char *str, int str_len)
171{
172 char *item_dup;
173
174 item_dup = estrndup(str, str_len);
175 php_strtolower(item_dup, str_len);
176 return item_dup;
177}
178
179zval *TWIG_CALL_USER_FUNC_ARRAY(zval *object, char *function, zval *arguments TSRMLS_DC)
180{
181 zend_fcall_info fci;
182 zval ***args = NULL;
183 int arg_count = 0;
184 HashTable *table;
185 HashPosition pos;
186 int i = 0;
187 zval *retval_ptr;
188 zval *zfunction;
189
190 if (arguments) {
191 table = HASH_OF(arguments);
192 args = safe_emalloc(sizeof(zval **), table->nNumOfElements, 0);
193
194 zend_hash_internal_pointer_reset_ex(table, &pos);
195
196 while (zend_hash_get_current_data_ex(table, (void **)&args[i], &pos) == SUCCESS) {
197 i++;
198 zend_hash_move_forward_ex(table, &pos);
199 }
200 arg_count = table->nNumOfElements;
201 }
202
203 MAKE_STD_ZVAL(zfunction);
204 ZVAL_STRING(zfunction, function, 1);
205 fci.size = sizeof(fci);
206 fci.function_table = EG(function_table);
207 fci.function_name = zfunction;
208 fci.symbol_table = NULL;
209#if PHP_VERSION_ID >= 50300
210 fci.object_ptr = object;
211#else
212 fci.object_pp = &object;
213#endif
214 fci.retval_ptr_ptr = &retval_ptr;
215 fci.param_count = arg_count;
216 fci.params = args;
217 fci.no_separation = 0;
218
219 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
220 ALLOC_INIT_ZVAL(retval_ptr);
221 ZVAL_BOOL(retval_ptr, 0);
222 }
223
224 if (args) {
225 efree(fci.params);
226 }
227 FREE_DTOR(zfunction);
228 return retval_ptr;
229}
230
231int TWIG_CALL_BOOLEAN(zval *object, char *functionName TSRMLS_DC)
232{
233 zval *ret;
234 int res;
235
236 ret = TWIG_CALL_USER_FUNC_ARRAY(object, functionName, NULL TSRMLS_CC);
237 res = Z_LVAL_P(ret);
238 zval_ptr_dtor(&ret);
239 return res;
240}
241
242zval *TWIG_GET_STATIC_PROPERTY(zval *class, char *prop_name TSRMLS_DC)
243{
244 zval **tmp_zval;
245 zend_class_entry *ce;
246
247 if (class == NULL || Z_TYPE_P(class) != IS_OBJECT) {
248 return NULL;
249 }
250
251 ce = zend_get_class_entry(class TSRMLS_CC);
252#if PHP_VERSION_ID >= 50400
253 tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0, NULL TSRMLS_CC);
254#else
255 tmp_zval = zend_std_get_static_property(ce, prop_name, strlen(prop_name), 0 TSRMLS_CC);
256#endif
257 return *tmp_zval;
258}
259
260zval *TWIG_GET_ARRAY_ELEMENT_ZVAL(zval *class, zval *prop_name TSRMLS_DC)
261{
262 zval **tmp_zval;
263 char *tmp_name;
264
265 if (class == NULL || Z_TYPE_P(class) != IS_ARRAY) {
266 if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
267 // array access object
268 return TWIG_GET_ARRAYOBJECT_ELEMENT(class, prop_name TSRMLS_CC);
269 }
270 return NULL;
271 }
272
273 switch(Z_TYPE_P(prop_name)) {
274 case IS_NULL:
275 zend_hash_find(HASH_OF(class), "", 1, (void**) &tmp_zval);
276 return *tmp_zval;
277
278 case IS_BOOL:
279 case IS_DOUBLE:
280 convert_to_long(prop_name);
281 case IS_LONG:
282 zend_hash_index_find(HASH_OF(class), Z_LVAL_P(prop_name), (void **) &tmp_zval);
283 return *tmp_zval;
284
285 case IS_STRING:
286 zend_symtable_find(HASH_OF(class), Z_STRVAL_P(prop_name), Z_STRLEN_P(prop_name) + 1, (void**) &tmp_zval);
287 return *tmp_zval;
288 }
289
290 return NULL;
291}
292
293zval *TWIG_GET_ARRAY_ELEMENT(zval *class, char *prop_name, int prop_name_length TSRMLS_DC)
294{
295 zval **tmp_zval;
296
297 if (class == NULL/* || Z_TYPE_P(class) != IS_ARRAY*/) {
298 return NULL;
299 }
300
301 if (class != NULL && Z_TYPE_P(class) == IS_OBJECT && TWIG_INSTANCE_OF(class, zend_ce_arrayaccess TSRMLS_CC)) {
302 // array access object
303 zval *tmp_name_zval;
304 zval *tmp_ret_zval;
305
306 ALLOC_INIT_ZVAL(tmp_name_zval);
307 ZVAL_STRING(tmp_name_zval, prop_name, 1);
308 tmp_ret_zval = TWIG_GET_ARRAYOBJECT_ELEMENT(class, tmp_name_zval TSRMLS_CC);
309 FREE_DTOR(tmp_name_zval);
310 return tmp_ret_zval;
311 }
312
313 if (zend_symtable_find(HASH_OF(class), prop_name, prop_name_length+1, (void**)&tmp_zval) == SUCCESS) {
314 return *tmp_zval;
315 }
316 return NULL;
317}
318
319zval *TWIG_PROPERTY(zval *object, zval *propname TSRMLS_DC)
320{
321 zval *tmp = NULL;
322
323 if (Z_OBJ_HT_P(object)->read_property) {
324#if PHP_VERSION_ID >= 50400
325 tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS, NULL TSRMLS_CC);
326#else
327 tmp = Z_OBJ_HT_P(object)->read_property(object, propname, BP_VAR_IS TSRMLS_CC);
328#endif
329 if (tmp != EG(uninitialized_zval_ptr)) {
330 return tmp;
331 } else {
332 return NULL;
333 }
334 }
335 return tmp;
336}
337
338int TWIG_HAS_PROPERTY(zval *object, zval *propname TSRMLS_DC)
339{
340 if (Z_OBJ_HT_P(object)->has_property) {
341#if PHP_VERSION_ID >= 50400
342 return Z_OBJ_HT_P(object)->has_property(object, propname, 0, NULL TSRMLS_CC);
343#else
344 return Z_OBJ_HT_P(object)->has_property(object, propname, 0 TSRMLS_CC);
345#endif
346 }
347 return 0;
348}
349
350int TWIG_HAS_DYNAMIC_PROPERTY(zval *object, char *prop, int prop_len TSRMLS_DC)
351{
352 if (Z_OBJ_HT_P(object)->get_properties) {
353 return zend_hash_quick_exists(
354 Z_OBJ_HT_P(object)->get_properties(object TSRMLS_CC), // the properties hash
355 prop, // property name
356 prop_len + 1, // property length
357 zend_get_hash_value(prop, prop_len + 1) // hash value
358 );
359 }
360 return 0;
361}
362
363zval *TWIG_PROPERTY_CHAR(zval *object, char *propname TSRMLS_DC)
364{
365 zval *tmp_name_zval, *tmp;
366
367 ALLOC_INIT_ZVAL(tmp_name_zval);
368 ZVAL_STRING(tmp_name_zval, propname, 1);
369 tmp = TWIG_PROPERTY(object, tmp_name_zval TSRMLS_CC);
370 FREE_DTOR(tmp_name_zval);
371 return tmp;
372}
373
374int TWIG_CALL_B_0(zval *object, char *method)
375{
376 return 0;
377}
378
379zval *TWIG_CALL_S(zval *object, char *method, char *arg0 TSRMLS_DC)
380{
381 zend_fcall_info fci;
382 zval **args[1];
383 zval *argument;
384 zval *zfunction;
385 zval *retval_ptr;
386
387 MAKE_STD_ZVAL(argument);
388 ZVAL_STRING(argument, arg0, 1);
389 args[0] = &argument;
390
391 MAKE_STD_ZVAL(zfunction);
392 ZVAL_STRING(zfunction, method, 1);
393 fci.size = sizeof(fci);
394 fci.function_table = EG(function_table);
395 fci.function_name = zfunction;
396 fci.symbol_table = NULL;
397#if PHP_VERSION_ID >= 50300
398 fci.object_ptr = object;
399#else
400 fci.object_pp = &object;
401#endif
402 fci.retval_ptr_ptr = &retval_ptr;
403 fci.param_count = 1;
404 fci.params = args;
405 fci.no_separation = 0;
406
407 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
408 FREE_DTOR(zfunction);
409 zval_ptr_dtor(&argument);
410 return 0;
411 }
412 FREE_DTOR(zfunction);
413 zval_ptr_dtor(&argument);
414 return retval_ptr;
415}
416
417int TWIG_CALL_SB(zval *object, char *method, char *arg0 TSRMLS_DC)
418{
419 zval *retval_ptr;
420 int success;
421
422 retval_ptr = TWIG_CALL_S(object, method, arg0 TSRMLS_CC);
423 success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
424
425 if (retval_ptr) {
426 zval_ptr_dtor(&retval_ptr);
427 }
428
429 return success;
430}
431
432int TWIG_CALL_Z(zval *object, char *method, zval *arg1 TSRMLS_DC)
433{
434 zend_fcall_info fci;
435 zval **args[1];
436 zval *zfunction;
437 zval *retval_ptr;
438 int success;
439
440 args[0] = &arg1;
441
442 MAKE_STD_ZVAL(zfunction);
443 ZVAL_STRING(zfunction, method, 1);
444 fci.size = sizeof(fci);
445 fci.function_table = EG(function_table);
446 fci.function_name = zfunction;
447 fci.symbol_table = NULL;
448#if PHP_VERSION_ID >= 50300
449 fci.object_ptr = object;
450#else
451 fci.object_pp = &object;
452#endif
453 fci.retval_ptr_ptr = &retval_ptr;
454 fci.param_count = 1;
455 fci.params = args;
456 fci.no_separation = 0;
457
458 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
459 FREE_DTOR(zfunction);
460 if (retval_ptr) {
461 zval_ptr_dtor(&retval_ptr);
462 }
463 return 0;
464 }
465
466 FREE_DTOR(zfunction);
467
468 success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
469 if (retval_ptr) {
470 zval_ptr_dtor(&retval_ptr);
471 }
472
473 return success;
474}
475
476int TWIG_CALL_ZZ(zval *object, char *method, zval *arg1, zval *arg2 TSRMLS_DC)
477{
478 zend_fcall_info fci;
479 zval **args[2];
480 zval *zfunction;
481 zval *retval_ptr;
482 int success;
483
484 args[0] = &arg1;
485 args[1] = &arg2;
486
487 MAKE_STD_ZVAL(zfunction);
488 ZVAL_STRING(zfunction, method, 1);
489 fci.size = sizeof(fci);
490 fci.function_table = EG(function_table);
491 fci.function_name = zfunction;
492 fci.symbol_table = NULL;
493#if PHP_VERSION_ID >= 50300
494 fci.object_ptr = object;
495#else
496 fci.object_pp = &object;
497#endif
498 fci.retval_ptr_ptr = &retval_ptr;
499 fci.param_count = 2;
500 fci.params = args;
501 fci.no_separation = 0;
502
503 if (zend_call_function(&fci, NULL TSRMLS_CC) == FAILURE) {
504 FREE_DTOR(zfunction);
505 return 0;
506 }
507
508 FREE_DTOR(zfunction);
509
510 success = (retval_ptr && (Z_TYPE_P(retval_ptr) == IS_BOOL) && Z_LVAL_P(retval_ptr));
511 if (retval_ptr) {
512 zval_ptr_dtor(&retval_ptr);
513 }
514
515 return success;
516}
517
518#ifndef Z_SET_REFCOUNT_P
519# define Z_SET_REFCOUNT_P(pz, rc) pz->refcount = rc
520# define Z_UNSET_ISREF_P(pz) pz->is_ref = 0
521#endif
522
523void TWIG_NEW(zval *object, char *class, zval *arg0, zval *arg1 TSRMLS_DC)
524{
525 zend_class_entry **pce;
526
527 if (zend_lookup_class(class, strlen(class), &pce TSRMLS_CC) == FAILURE) {
528 return;
529 }
530
531 Z_TYPE_P(object) = IS_OBJECT;
532 object_init_ex(object, *pce);
533 Z_SET_REFCOUNT_P(object, 1);
534 Z_UNSET_ISREF_P(object);
535
536 TWIG_CALL_ZZ(object, "__construct", arg0, arg1 TSRMLS_CC);
537}
538
539static int twig_add_array_key_to_string(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
540{
541 smart_str *buf;
542 char *joiner;
543 APPLY_TSRMLS_FETCH();
544
545 buf = va_arg(args, smart_str*);
546 joiner = va_arg(args, char*);
547
548 if (buf->len != 0) {
549 smart_str_appends(buf, joiner);
550 }
551
552 if (hash_key->nKeyLength == 0) {
553 smart_str_append_long(buf, (long) hash_key->h);
554 } else {
555 char *key, *tmp_str;
556 int key_len, tmp_len;
557 key = php_addcslashes(hash_key->arKey, hash_key->nKeyLength - 1, &key_len, 0, "'\\", 2 TSRMLS_CC);
558 tmp_str = php_str_to_str_ex(key, key_len, "\0", 1, "' . \"\\0\" . '", 12, &tmp_len, 0, NULL);
559
560 smart_str_appendl(buf, tmp_str, tmp_len);
561 efree(key);
562 efree(tmp_str);
563 }
564
565 return 0;
566}
567
568char *TWIG_IMPLODE_ARRAY_KEYS(char *joiner, zval *array TSRMLS_DC)
569{
570 smart_str collector = { 0, 0, 0 };
571
572 smart_str_appendl(&collector, "", 0);
573 zend_hash_apply_with_arguments(HASH_OF(array) APPLY_TSRMLS_CC, twig_add_array_key_to_string, 2, &collector, joiner);
574 smart_str_0(&collector);
575
576 return collector.c;
577}
578
579static void TWIG_THROW_EXCEPTION(char *exception_name TSRMLS_DC, char *message, ...)
580{
581 char *buffer;
582 va_list args;
583 zend_class_entry **pce;
584
585 if (zend_lookup_class(exception_name, strlen(exception_name), &pce TSRMLS_CC) == FAILURE) {
586 return;
587 }
588
589 va_start(args, message);
590 vspprintf(&buffer, 0, message, args);
591 va_end(args);
592
593 zend_throw_exception_ex(*pce, 0 TSRMLS_CC, buffer);
594 efree(buffer);
595}
596
597static void TWIG_RUNTIME_ERROR(zval *template TSRMLS_DC, char *message, ...)
598{
599 char *buffer;
600 va_list args;
601 zend_class_entry **pce;
602 zval *ex;
603 zval *constructor;
604 zval *zmessage;
605 zval *lineno;
606 zval *filename_func;
607 zval *filename;
608 zval *constructor_args[3];
609 zval *constructor_retval;
610
611 if (zend_lookup_class("Twig_Error_Runtime", strlen("Twig_Error_Runtime"), &pce TSRMLS_CC) == FAILURE) {
612 return;
613 }
614
615 va_start(args, message);
616 vspprintf(&buffer, 0, message, args);
617 va_end(args);
618
619 MAKE_STD_ZVAL(ex);
620 object_init_ex(ex, *pce);
621
622 // Call Twig_Error constructor
623 MAKE_STD_ZVAL(constructor);
624 MAKE_STD_ZVAL(zmessage);
625 MAKE_STD_ZVAL(lineno);
626 MAKE_STD_ZVAL(filename);
627 MAKE_STD_ZVAL(filename_func);
628 MAKE_STD_ZVAL(constructor_retval);
629
630 ZVAL_STRINGL(constructor, "__construct", sizeof("__construct")-1, 1);
631 ZVAL_STRING(zmessage, buffer, 1);
632 ZVAL_LONG(lineno, -1);
633
634 // Get template filename
635 ZVAL_STRINGL(filename_func, "getTemplateName", sizeof("getTemplateName")-1, 1);
636 call_user_function(EG(function_table), &template, filename_func, filename, 0, 0 TSRMLS_CC);
637
638 constructor_args[0] = zmessage;
639 constructor_args[1] = lineno;
640 constructor_args[2] = filename;
641 call_user_function(EG(function_table), &ex, constructor, constructor_retval, 3, constructor_args TSRMLS_CC);
642
643 zval_ptr_dtor(&constructor_retval);
644 zval_ptr_dtor(&zmessage);
645 zval_ptr_dtor(&lineno);
646 zval_ptr_dtor(&filename);
647 FREE_DTOR(constructor);
648 FREE_DTOR(filename_func);
649 efree(buffer);
650
651 zend_throw_exception_object(ex TSRMLS_CC);
652}
653
654static char *TWIG_GET_CLASS_NAME(zval *object TSRMLS_DC)
655{
656 char *class_name;
657 zend_uint class_name_len;
658
659 if (Z_TYPE_P(object) != IS_OBJECT) {
660 return "";
661 }
662#if PHP_API_VERSION >= 20100412
663 zend_get_object_classname(object, (const char **) &class_name, &class_name_len TSRMLS_CC);
664#else
665 zend_get_object_classname(object, &class_name, &class_name_len TSRMLS_CC);
666#endif
667 return class_name;
668}
669
670static int twig_add_method_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
671{
672 zval *retval;
673 char *item;
674 size_t item_len;
675 zend_function *mptr = (zend_function *) pDest;
676 APPLY_TSRMLS_FETCH();
677
678 if (!(mptr->common.fn_flags & ZEND_ACC_PUBLIC)) {
679 return 0;
680 }
681
682 retval = va_arg(args, zval*);
683
684 item_len = strlen(mptr->common.function_name);
685 item = estrndup(mptr->common.function_name, item_len);
686 php_strtolower(item, item_len);
687
688 add_assoc_stringl_ex(retval, item, item_len+1, item, item_len, 0);
689
690 return 0;
691}
692
693static int twig_add_property_to_class(void *pDest APPLY_TSRMLS_DC, int num_args, va_list args, zend_hash_key *hash_key)
694{
695 zend_class_entry *ce;
696 zval *retval;
697 char *class_name, *prop_name;
698 zend_property_info *pptr = (zend_property_info *) pDest;
699 APPLY_TSRMLS_FETCH();
700
701 if (!(pptr->flags & ZEND_ACC_PUBLIC)) {
702 return 0;
703 }
704
705 ce = *va_arg(args, zend_class_entry**);
706 retval = va_arg(args, zval*);
707
708#if PHP_API_VERSION >= 20100412
709 zend_unmangle_property_name(pptr->name, pptr->name_length, (const char **) &class_name, (const char **) &prop_name);
710#else
711 zend_unmangle_property_name(pptr->name, pptr->name_length, &class_name, &prop_name);
712#endif
713
714 add_assoc_string(retval, prop_name, prop_name, 1);
715
716 return 0;
717}
718
719static void twig_add_class_to_cache(zval *cache, zval *object, char *class_name TSRMLS_DC)
720{
721 zval *class_info, *class_methods, *class_properties;
722 zend_class_entry *class_ce;
723
724 class_ce = zend_get_class_entry(object TSRMLS_CC);
725
726 ALLOC_INIT_ZVAL(class_info);
727 ALLOC_INIT_ZVAL(class_methods);
728 ALLOC_INIT_ZVAL(class_properties);
729 array_init(class_info);
730 array_init(class_methods);
731 array_init(class_properties);
732 // add all methods to self::cache[$class]['methods']
733 zend_hash_apply_with_arguments(&class_ce->function_table APPLY_TSRMLS_CC, twig_add_method_to_class, 1, class_methods);
734 zend_hash_apply_with_arguments(&class_ce->properties_info APPLY_TSRMLS_CC, twig_add_property_to_class, 2, &class_ce, class_properties);
735
736 add_assoc_zval(class_info, "methods", class_methods);
737 add_assoc_zval(class_info, "properties", class_properties);
738 add_assoc_zval(cache, class_name, class_info);
739}
740
741/* {{{ proto mixed twig_template_get_attributes(TwigTemplate template, mixed object, mixed item, array arguments, string type, boolean isDefinedTest, boolean ignoreStrictCheck)
742 A C implementation of TwigTemplate::getAttribute() */
743PHP_FUNCTION(twig_template_get_attributes)
744{
745 zval *template;
746 zval *object;
747 char *item;
748 int item_len;
749 zval *zitem, ztmpitem;
750 zval *arguments = NULL;
751 zval *ret = NULL;
752 char *type = NULL;
753 int type_len = 0;
754 zend_bool isDefinedTest = 0;
755 zend_bool ignoreStrictCheck = 0;
756 int free_ret = 0;
757 zval *tmp_self_cache;
758
759
760 if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ozz|asbb", &template, &object, &zitem, &arguments, &type, &type_len, &isDefinedTest, &ignoreStrictCheck) == FAILURE) {
761 return;
762 }
763
764 // convert the item to a string
765 ztmpitem = *zitem;
766 zval_copy_ctor(&ztmpitem);
767 convert_to_string(&ztmpitem);
768 item_len = Z_STRLEN(ztmpitem);
769 item = estrndup(Z_STRVAL(ztmpitem), item_len);
770 zval_dtor(&ztmpitem);
771
772 if (!type) {
773 type = "any";
774 }
775
776/*
777 // array
778 if (Twig_TemplateInterface::METHOD_CALL !== $type) {
779 $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
780
781 if ((is_array($object) && array_key_exists($arrayItem, $object))
782 || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
783 ) {
784 if ($isDefinedTest) {
785 return true;
786 }
787
788 return $object[$arrayItem];
789 }
790*/
791
792
793 if (strcmp("method", type) != 0) {
794 if ((TWIG_ARRAY_KEY_EXISTS(object, zitem))
795 || (TWIG_INSTANCE_OF(object, zend_ce_arrayaccess TSRMLS_CC) && TWIG_ISSET_ARRAYOBJECT_ELEMENT(object, zitem TSRMLS_CC))
796 ) {
797
798 if (isDefinedTest) {
799 RETURN_TRUE;
800 }
801
802 ret = TWIG_GET_ARRAY_ELEMENT_ZVAL(object, zitem TSRMLS_CC);
803
804 if (!ret) {
805 ret = &EG(uninitialized_zval);
806 }
807 RETVAL_ZVAL(ret, 1, 0);
808 if (free_ret) {
809 zval_ptr_dtor(&ret);
810 }
811 return;
812 }
813/*
814 if (Twig_TemplateInterface::ARRAY_CALL === $type) {
815 if ($isDefinedTest) {
816 return false;
817 }
818 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
819 return null;
820 }
821*/
822 if (strcmp("array", type) == 0 || Z_TYPE_P(object) != IS_OBJECT) {
823 if (isDefinedTest) {
824 RETURN_FALSE;
825 }
826 if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
827 return;
828 }
829/*
830 if (is_object($object)) {
831 throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
832 } elseif (is_array($object)) {
833 throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
834 } elseif (Twig_TemplateInterface::ARRAY_CALL === $type) {
835 throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
836 } else {
837 throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
838 }
839 }
840 }
841*/
842 if (Z_TYPE_P(object) == IS_OBJECT) {
843 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" in object (with ArrayAccess) of type \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
844 } else if (Z_TYPE_P(object) == IS_ARRAY) {
845 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Key \"%s\" for array with keys \"%s\" does not exist", item, TWIG_IMPLODE_ARRAY_KEYS(", ", object TSRMLS_CC));
846 } else {
847 char *type_name = zend_zval_type_name(object);
848 Z_ADDREF_P(object);
849 convert_to_string(object);
850 TWIG_RUNTIME_ERROR(template TSRMLS_CC,
851 (strcmp("array", type) == 0)
852 ? "Impossible to access a key (\"%s\") on a %s variable (\"%s\")"
853 : "Impossible to access an attribute (\"%s\") on a %s variable (\"%s\")",
854 item, type_name, Z_STRVAL_P(object));
855 zval_ptr_dtor(&object);
856 }
857 return;
858 }
859 }
860
861/*
862 if (!is_object($object)) {
863 if ($isDefinedTest) {
864 return false;
865 }
866*/
867
868 if (Z_TYPE_P(object) != IS_OBJECT) {
869 if (isDefinedTest) {
870 RETURN_FALSE;
871 }
872/*
873 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
874 return null;
875 }
876 throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
877 }
878*/
879 if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
880 return;
881 }
882
883 char *type_name = zend_zval_type_name(object);
884 Z_ADDREF_P(object);
885 convert_to_string_ex(&object);
886
887 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Impossible to invoke a method (\"%s\") on a %s variable (\"%s\")", item, type_name, Z_STRVAL_P(object));
888
889 zval_ptr_dtor(&object);
890
891 return;
892 }
893/*
894 $class = get_class($object);
895*/
896 char *class_name = NULL;
897 zval *tmp_class;
898
899 class_name = TWIG_GET_CLASS_NAME(object TSRMLS_CC);
900 tmp_self_cache = TWIG_GET_STATIC_PROPERTY(template, "cache" TSRMLS_CC);
901 tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
902
903 if (!tmp_class) {
904 twig_add_class_to_cache(tmp_self_cache, object, class_name TSRMLS_CC);
905 tmp_class = TWIG_GET_ARRAY_ELEMENT(tmp_self_cache, class_name, strlen(class_name) TSRMLS_CC);
906 }
907 efree(class_name);
908
909/*
910 // object property
911 if (Twig_TemplateInterface::METHOD_CALL !== $type) {
912 if (isset($object->$item) || array_key_exists((string) $item, $object)) {
913 if ($isDefinedTest) {
914 return true;
915 }
916
917 if ($this->env->hasExtension('sandbox')) {
918 $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
919 }
920
921 return $object->$item;
922 }
923 }
924*/
925 if (strcmp("method", type) != 0) {
926 zval *tmp_properties, *tmp_item;
927
928 tmp_properties = TWIG_GET_ARRAY_ELEMENT(tmp_class, "properties", strlen("properties") TSRMLS_CC);
929 tmp_item = TWIG_GET_ARRAY_ELEMENT(tmp_properties, item, item_len TSRMLS_CC);
930
931 if (tmp_item || TWIG_HAS_PROPERTY(object, zitem TSRMLS_CC) || TWIG_HAS_DYNAMIC_PROPERTY(object, item, item_len TSRMLS_CC)) {
932 if (isDefinedTest) {
933 RETURN_TRUE;
934 }
935 if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
936 TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkPropertyAllowed", object, zitem TSRMLS_CC);
937 }
938 if (EG(exception)) {
939 return;
940 }
941
942 ret = TWIG_PROPERTY(object, zitem TSRMLS_CC);
943 RETURN_ZVAL(ret, 1, 0);
944 }
945 }
946/*
947 // object method
948 if (!isset(self::$cache[$class]['methods'])) {
949 self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
950 }
951
952 $lcItem = strtolower($item);
953 if (isset(self::$cache[$class]['methods'][$lcItem])) {
954 $method = (string) $item;
955 } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
956 $method = 'get'.$item;
957 } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
958 $method = 'is'.$item;
959 } elseif (isset(self::$cache[$class]['methods']['__call'])) {
960 $method = (string) $item;
961*/
962 {
963 char *lcItem = TWIG_STRTOLOWER(item, item_len);
964 int lcItem_length;
965 char *method = NULL;
966 char *tmp_method_name_get;
967 char *tmp_method_name_is;
968 zval *tmp_methods;
969
970 lcItem_length = strlen(lcItem);
971 tmp_method_name_get = emalloc(4 + lcItem_length);
972 tmp_method_name_is = emalloc(3 + lcItem_length);
973
974 sprintf(tmp_method_name_get, "get%s", lcItem);
975 sprintf(tmp_method_name_is, "is%s", lcItem);
976
977 tmp_methods = TWIG_GET_ARRAY_ELEMENT(tmp_class, "methods", strlen("methods") TSRMLS_CC);
978
979 if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, lcItem, lcItem_length TSRMLS_CC)) {
980 method = item;
981 } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_get, lcItem_length + 3 TSRMLS_CC)) {
982 method = tmp_method_name_get;
983 } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, tmp_method_name_is, lcItem_length + 2 TSRMLS_CC)) {
984 method = tmp_method_name_is;
985 } else if (TWIG_GET_ARRAY_ELEMENT(tmp_methods, "__call", 6 TSRMLS_CC)) {
986 method = item;
987/*
988 } else {
989 if ($isDefinedTest) {
990 return false;
991 }
992
993 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
994 return null;
995 }
996
997 throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
998 }
999
1000 if ($isDefinedTest) {
1001 return true;
1002 }
1003*/
1004 } else {
1005 efree(tmp_method_name_get);
1006 efree(tmp_method_name_is);
1007 efree(lcItem);
1008
1009 if (isDefinedTest) {
1010 RETURN_FALSE;
1011 }
1012 if (ignoreStrictCheck || !TWIG_CALL_BOOLEAN(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "isStrictVariables" TSRMLS_CC)) {
1013 return;
1014 }
1015 TWIG_RUNTIME_ERROR(template TSRMLS_CC, "Method \"%s\" for object \"%s\" does not exist", item, TWIG_GET_CLASS_NAME(object TSRMLS_CC));
1016 return;
1017 }
1018
1019 if (isDefinedTest) {
1020 efree(tmp_method_name_get);
1021 efree(tmp_method_name_is);
1022 efree(lcItem);
1023 RETURN_TRUE;
1024 }
1025/*
1026 if ($this->env->hasExtension('sandbox')) {
1027 $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
1028 }
1029*/
1030 if (TWIG_CALL_SB(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "hasExtension", "sandbox" TSRMLS_CC)) {
1031 TWIG_CALL_ZZ(TWIG_CALL_S(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getExtension", "sandbox" TSRMLS_CC), "checkMethodAllowed", object, zitem TSRMLS_CC);
1032 }
1033 if (EG(exception)) {
1034 efree(tmp_method_name_get);
1035 efree(tmp_method_name_is);
1036 efree(lcItem);
1037 return;
1038 }
1039/*
1040 $ret = call_user_func_array(array($object, $method), $arguments);
1041*/
1042 ret = TWIG_CALL_USER_FUNC_ARRAY(object, method, arguments TSRMLS_CC);
1043 free_ret = 1;
1044 efree(tmp_method_name_get);
1045 efree(tmp_method_name_is);
1046 efree(lcItem);
1047 }
1048/*
1049 // useful when calling a template method from a template
1050 // this is not supported but unfortunately heavily used in the Symfony profiler
1051 if ($object instanceof Twig_TemplateInterface) {
1052 return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
1053 }
1054
1055 return $ret;
1056*/
1057 // ret can be null, if e.g. the called method throws an exception
1058 if (ret) {
1059 if (TWIG_INSTANCE_OF_USERLAND(object, "Twig_TemplateInterface" TSRMLS_CC)) {
1060 if (Z_STRLEN_P(ret) != 0) {
1061 zval *charset = TWIG_CALL_USER_FUNC_ARRAY(TWIG_PROPERTY_CHAR(template, "env" TSRMLS_CC), "getCharset", NULL TSRMLS_CC);
1062 TWIG_NEW(return_value, "Twig_Markup", ret, charset TSRMLS_CC);
1063 zval_ptr_dtor(&charset);
1064 if (ret) {
1065 zval_ptr_dtor(&ret);
1066 }
1067 return;
1068 }
1069 }
1070
1071 RETVAL_ZVAL(ret, 1, 0);
1072 if (free_ret) {
1073 zval_ptr_dtor(&ret);
1074 }
1075 }
1076}
diff --git a/vendor/twig/twig/lib/Twig/Autoloader.php b/vendor/twig/twig/lib/Twig/Autoloader.php
new file mode 100644
index 00000000..7007d315
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Autoloader.php
@@ -0,0 +1,48 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Autoloads Twig classes.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Autoloader
18{
19 /**
20 * Registers Twig_Autoloader as an SPL autoloader.
21 *
22 * @param Boolean $prepend Whether to prepend the autoloader or not.
23 */
24 public static function register($prepend = false)
25 {
26 if (version_compare(phpversion(), '5.3.0', '>=')) {
27 spl_autoload_register(array(new self, 'autoload'), true, $prepend);
28 } else {
29 spl_autoload_register(array(new self, 'autoload'));
30 }
31 }
32
33 /**
34 * Handles autoloading of classes.
35 *
36 * @param string $class A class name.
37 */
38 public static function autoload($class)
39 {
40 if (0 !== strpos($class, 'Twig')) {
41 return;
42 }
43
44 if (is_file($file = dirname(__FILE__).'/../'.str_replace(array('_', "\0"), array('/', ''), $class).'.php')) {
45 require $file;
46 }
47 }
48}
diff --git a/vendor/twig/twig/lib/Twig/Compiler.php b/vendor/twig/twig/lib/Twig/Compiler.php
new file mode 100644
index 00000000..99aecbcc
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Compiler.php
@@ -0,0 +1,267 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Compiles a node to PHP code.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Compiler implements Twig_CompilerInterface
19{
20 protected $lastLine;
21 protected $source;
22 protected $indentation;
23 protected $env;
24 protected $debugInfo;
25 protected $sourceOffset;
26 protected $sourceLine;
27 protected $filename;
28
29 /**
30 * Constructor.
31 *
32 * @param Twig_Environment $env The twig environment instance
33 */
34 public function __construct(Twig_Environment $env)
35 {
36 $this->env = $env;
37 $this->debugInfo = array();
38 }
39
40 public function getFilename()
41 {
42 return $this->filename;
43 }
44
45 /**
46 * Returns the environment instance related to this compiler.
47 *
48 * @return Twig_Environment The environment instance
49 */
50 public function getEnvironment()
51 {
52 return $this->env;
53 }
54
55 /**
56 * Gets the current PHP code after compilation.
57 *
58 * @return string The PHP code
59 */
60 public function getSource()
61 {
62 return $this->source;
63 }
64
65 /**
66 * Compiles a node.
67 *
68 * @param Twig_NodeInterface $node The node to compile
69 * @param integer $indentation The current indentation
70 *
71 * @return Twig_Compiler The current compiler instance
72 */
73 public function compile(Twig_NodeInterface $node, $indentation = 0)
74 {
75 $this->lastLine = null;
76 $this->source = '';
77 $this->sourceOffset = 0;
78 // source code starts at 1 (as we then increment it when we encounter new lines)
79 $this->sourceLine = 1;
80 $this->indentation = $indentation;
81
82 if ($node instanceof Twig_Node_Module) {
83 $this->filename = $node->getAttribute('filename');
84 }
85
86 $node->compile($this);
87
88 return $this;
89 }
90
91 public function subcompile(Twig_NodeInterface $node, $raw = true)
92 {
93 if (false === $raw) {
94 $this->addIndentation();
95 }
96
97 $node->compile($this);
98
99 return $this;
100 }
101
102 /**
103 * Adds a raw string to the compiled code.
104 *
105 * @param string $string The string
106 *
107 * @return Twig_Compiler The current compiler instance
108 */
109 public function raw($string)
110 {
111 $this->source .= $string;
112
113 return $this;
114 }
115
116 /**
117 * Writes a string to the compiled code by adding indentation.
118 *
119 * @return Twig_Compiler The current compiler instance
120 */
121 public function write()
122 {
123 $strings = func_get_args();
124 foreach ($strings as $string) {
125 $this->addIndentation();
126 $this->source .= $string;
127 }
128
129 return $this;
130 }
131
132 /**
133 * Appends an indentation to the current PHP code after compilation.
134 *
135 * @return Twig_Compiler The current compiler instance
136 */
137 public function addIndentation()
138 {
139 $this->source .= str_repeat(' ', $this->indentation * 4);
140
141 return $this;
142 }
143
144 /**
145 * Adds a quoted string to the compiled code.
146 *
147 * @param string $value The string
148 *
149 * @return Twig_Compiler The current compiler instance
150 */
151 public function string($value)
152 {
153 $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
154
155 return $this;
156 }
157
158 /**
159 * Returns a PHP representation of a given value.
160 *
161 * @param mixed $value The value to convert
162 *
163 * @return Twig_Compiler The current compiler instance
164 */
165 public function repr($value)
166 {
167 if (is_int($value) || is_float($value)) {
168 if (false !== $locale = setlocale(LC_NUMERIC, 0)) {
169 setlocale(LC_NUMERIC, 'C');
170 }
171
172 $this->raw($value);
173
174 if (false !== $locale) {
175 setlocale(LC_NUMERIC, $locale);
176 }
177 } elseif (null === $value) {
178 $this->raw('null');
179 } elseif (is_bool($value)) {
180 $this->raw($value ? 'true' : 'false');
181 } elseif (is_array($value)) {
182 $this->raw('array(');
183 $i = 0;
184 foreach ($value as $key => $value) {
185 if ($i++) {
186 $this->raw(', ');
187 }
188 $this->repr($key);
189 $this->raw(' => ');
190 $this->repr($value);
191 }
192 $this->raw(')');
193 } else {
194 $this->string($value);
195 }
196
197 return $this;
198 }
199
200 /**
201 * Adds debugging information.
202 *
203 * @param Twig_NodeInterface $node The related twig node
204 *
205 * @return Twig_Compiler The current compiler instance
206 */
207 public function addDebugInfo(Twig_NodeInterface $node)
208 {
209 if ($node->getLine() != $this->lastLine) {
210 $this->write("// line {$node->getLine()}\n");
211
212 // when mbstring.func_overload is set to 2
213 // mb_substr_count() replaces substr_count()
214 // but they have different signatures!
215 if (((int) ini_get('mbstring.func_overload')) & 2) {
216 // this is much slower than the "right" version
217 $this->sourceLine += mb_substr_count(mb_substr($this->source, $this->sourceOffset), "\n");
218 } else {
219 $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
220 }
221 $this->sourceOffset = strlen($this->source);
222 $this->debugInfo[$this->sourceLine] = $node->getLine();
223
224 $this->lastLine = $node->getLine();
225 }
226
227 return $this;
228 }
229
230 public function getDebugInfo()
231 {
232 return $this->debugInfo;
233 }
234
235 /**
236 * Indents the generated code.
237 *
238 * @param integer $step The number of indentation to add
239 *
240 * @return Twig_Compiler The current compiler instance
241 */
242 public function indent($step = 1)
243 {
244 $this->indentation += $step;
245
246 return $this;
247 }
248
249 /**
250 * Outdents the generated code.
251 *
252 * @param integer $step The number of indentation to remove
253 *
254 * @return Twig_Compiler The current compiler instance
255 */
256 public function outdent($step = 1)
257 {
258 // can't outdent by more steps than the current indentation level
259 if ($this->indentation < $step) {
260 throw new LogicException('Unable to call outdent() as the indentation would become negative');
261 }
262
263 $this->indentation -= $step;
264
265 return $this;
266 }
267}
diff --git a/vendor/twig/twig/lib/Twig/CompilerInterface.php b/vendor/twig/twig/lib/Twig/CompilerInterface.php
new file mode 100644
index 00000000..e293ec91
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/CompilerInterface.php
@@ -0,0 +1,35 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interface implemented by compiler classes.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_CompilerInterface
19{
20 /**
21 * Compiles a node.
22 *
23 * @param Twig_NodeInterface $node The node to compile
24 *
25 * @return Twig_CompilerInterface The current compiler instance
26 */
27 public function compile(Twig_NodeInterface $node);
28
29 /**
30 * Gets the current PHP code after compilation.
31 *
32 * @return string The PHP code
33 */
34 public function getSource();
35}
diff --git a/vendor/twig/twig/lib/Twig/Environment.php b/vendor/twig/twig/lib/Twig/Environment.php
new file mode 100644
index 00000000..6d4c5c57
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Environment.php
@@ -0,0 +1,1224 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Stores the Twig configuration.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Environment
18{
19 const VERSION = '1.13.2';
20
21 protected $charset;
22 protected $loader;
23 protected $debug;
24 protected $autoReload;
25 protected $cache;
26 protected $lexer;
27 protected $parser;
28 protected $compiler;
29 protected $baseTemplateClass;
30 protected $extensions;
31 protected $parsers;
32 protected $visitors;
33 protected $filters;
34 protected $tests;
35 protected $functions;
36 protected $globals;
37 protected $runtimeInitialized;
38 protected $extensionInitialized;
39 protected $loadedTemplates;
40 protected $strictVariables;
41 protected $unaryOperators;
42 protected $binaryOperators;
43 protected $templateClassPrefix = '__TwigTemplate_';
44 protected $functionCallbacks;
45 protected $filterCallbacks;
46 protected $staging;
47
48 /**
49 * Constructor.
50 *
51 * Available options:
52 *
53 * * debug: When set to true, it automatically set "auto_reload" to true as
54 * well (default to false).
55 *
56 * * charset: The charset used by the templates (default to UTF-8).
57 *
58 * * base_template_class: The base template class to use for generated
59 * templates (default to Twig_Template).
60 *
61 * * cache: An absolute path where to store the compiled templates, or
62 * false to disable compilation cache (default).
63 *
64 * * auto_reload: Whether to reload the template is the original source changed.
65 * If you don't provide the auto_reload option, it will be
66 * determined automatically base on the debug value.
67 *
68 * * strict_variables: Whether to ignore invalid variables in templates
69 * (default to false).
70 *
71 * * autoescape: Whether to enable auto-escaping (default to html):
72 * * false: disable auto-escaping
73 * * true: equivalent to html
74 * * html, js: set the autoescaping to one of the supported strategies
75 * * PHP callback: a PHP callback that returns an escaping strategy based on the template "filename"
76 *
77 * * optimizations: A flag that indicates which optimizations to apply
78 * (default to -1 which means that all optimizations are enabled;
79 * set it to 0 to disable).
80 *
81 * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
82 * @param array $options An array of options
83 */
84 public function __construct(Twig_LoaderInterface $loader = null, $options = array())
85 {
86 if (null !== $loader) {
87 $this->setLoader($loader);
88 }
89
90 $options = array_merge(array(
91 'debug' => false,
92 'charset' => 'UTF-8',
93 'base_template_class' => 'Twig_Template',
94 'strict_variables' => false,
95 'autoescape' => 'html',
96 'cache' => false,
97 'auto_reload' => null,
98 'optimizations' => -1,
99 ), $options);
100
101 $this->debug = (bool) $options['debug'];
102 $this->charset = strtoupper($options['charset']);
103 $this->baseTemplateClass = $options['base_template_class'];
104 $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
105 $this->strictVariables = (bool) $options['strict_variables'];
106 $this->runtimeInitialized = false;
107 $this->setCache($options['cache']);
108 $this->functionCallbacks = array();
109 $this->filterCallbacks = array();
110
111 $this->addExtension(new Twig_Extension_Core());
112 $this->addExtension(new Twig_Extension_Escaper($options['autoescape']));
113 $this->addExtension(new Twig_Extension_Optimizer($options['optimizations']));
114 $this->extensionInitialized = false;
115 $this->staging = new Twig_Extension_Staging();
116 }
117
118 /**
119 * Gets the base template class for compiled templates.
120 *
121 * @return string The base template class name
122 */
123 public function getBaseTemplateClass()
124 {
125 return $this->baseTemplateClass;
126 }
127
128 /**
129 * Sets the base template class for compiled templates.
130 *
131 * @param string $class The base template class name
132 */
133 public function setBaseTemplateClass($class)
134 {
135 $this->baseTemplateClass = $class;
136 }
137
138 /**
139 * Enables debugging mode.
140 */
141 public function enableDebug()
142 {
143 $this->debug = true;
144 }
145
146 /**
147 * Disables debugging mode.
148 */
149 public function disableDebug()
150 {
151 $this->debug = false;
152 }
153
154 /**
155 * Checks if debug mode is enabled.
156 *
157 * @return Boolean true if debug mode is enabled, false otherwise
158 */
159 public function isDebug()
160 {
161 return $this->debug;
162 }
163
164 /**
165 * Enables the auto_reload option.
166 */
167 public function enableAutoReload()
168 {
169 $this->autoReload = true;
170 }
171
172 /**
173 * Disables the auto_reload option.
174 */
175 public function disableAutoReload()
176 {
177 $this->autoReload = false;
178 }
179
180 /**
181 * Checks if the auto_reload option is enabled.
182 *
183 * @return Boolean true if auto_reload is enabled, false otherwise
184 */
185 public function isAutoReload()
186 {
187 return $this->autoReload;
188 }
189
190 /**
191 * Enables the strict_variables option.
192 */
193 public function enableStrictVariables()
194 {
195 $this->strictVariables = true;
196 }
197
198 /**
199 * Disables the strict_variables option.
200 */
201 public function disableStrictVariables()
202 {
203 $this->strictVariables = false;
204 }
205
206 /**
207 * Checks if the strict_variables option is enabled.
208 *
209 * @return Boolean true if strict_variables is enabled, false otherwise
210 */
211 public function isStrictVariables()
212 {
213 return $this->strictVariables;
214 }
215
216 /**
217 * Gets the cache directory or false if cache is disabled.
218 *
219 * @return string|false
220 */
221 public function getCache()
222 {
223 return $this->cache;
224 }
225
226 /**
227 * Sets the cache directory or false if cache is disabled.
228 *
229 * @param string|false $cache The absolute path to the compiled templates,
230 * or false to disable cache
231 */
232 public function setCache($cache)
233 {
234 $this->cache = $cache ? $cache : false;
235 }
236
237 /**
238 * Gets the cache filename for a given template.
239 *
240 * @param string $name The template name
241 *
242 * @return string The cache file name
243 */
244 public function getCacheFilename($name)
245 {
246 if (false === $this->cache) {
247 return false;
248 }
249
250 $class = substr($this->getTemplateClass($name), strlen($this->templateClassPrefix));
251
252 return $this->getCache().'/'.substr($class, 0, 2).'/'.substr($class, 2, 2).'/'.substr($class, 4).'.php';
253 }
254
255 /**
256 * Gets the template class associated with the given string.
257 *
258 * @param string $name The name for which to calculate the template class name
259 * @param integer $index The index if it is an embedded template
260 *
261 * @return string The template class name
262 */
263 public function getTemplateClass($name, $index = null)
264 {
265 return $this->templateClassPrefix.md5($this->getLoader()->getCacheKey($name)).(null === $index ? '' : '_'.$index);
266 }
267
268 /**
269 * Gets the template class prefix.
270 *
271 * @return string The template class prefix
272 */
273 public function getTemplateClassPrefix()
274 {
275 return $this->templateClassPrefix;
276 }
277
278 /**
279 * Renders a template.
280 *
281 * @param string $name The template name
282 * @param array $context An array of parameters to pass to the template
283 *
284 * @return string The rendered template
285 */
286 public function render($name, array $context = array())
287 {
288 return $this->loadTemplate($name)->render($context);
289 }
290
291 /**
292 * Displays a template.
293 *
294 * @param string $name The template name
295 * @param array $context An array of parameters to pass to the template
296 */
297 public function display($name, array $context = array())
298 {
299 $this->loadTemplate($name)->display($context);
300 }
301
302 /**
303 * Loads a template by name.
304 *
305 * @param string $name The template name
306 * @param integer $index The index if it is an embedded template
307 *
308 * @return Twig_TemplateInterface A template instance representing the given template name
309 */
310 public function loadTemplate($name, $index = null)
311 {
312 $cls = $this->getTemplateClass($name, $index);
313
314 if (isset($this->loadedTemplates[$cls])) {
315 return $this->loadedTemplates[$cls];
316 }
317
318 if (!class_exists($cls, false)) {
319 if (false === $cache = $this->getCacheFilename($name)) {
320 eval('?>'.$this->compileSource($this->getLoader()->getSource($name), $name));
321 } else {
322 if (!is_file($cache) || ($this->isAutoReload() && !$this->isTemplateFresh($name, filemtime($cache)))) {
323 $this->writeCacheFile($cache, $this->compileSource($this->getLoader()->getSource($name), $name));
324 }
325
326 require_once $cache;
327 }
328 }
329
330 if (!$this->runtimeInitialized) {
331 $this->initRuntime();
332 }
333
334 return $this->loadedTemplates[$cls] = new $cls($this);
335 }
336
337 /**
338 * Returns true if the template is still fresh.
339 *
340 * Besides checking the loader for freshness information,
341 * this method also checks if the enabled extensions have
342 * not changed.
343 *
344 * @param string $name The template name
345 * @param timestamp $time The last modification time of the cached template
346 *
347 * @return Boolean true if the template is fresh, false otherwise
348 */
349 public function isTemplateFresh($name, $time)
350 {
351 foreach ($this->extensions as $extension) {
352 $r = new ReflectionObject($extension);
353 if (filemtime($r->getFileName()) > $time) {
354 return false;
355 }
356 }
357
358 return $this->getLoader()->isFresh($name, $time);
359 }
360
361 public function resolveTemplate($names)
362 {
363 if (!is_array($names)) {
364 $names = array($names);
365 }
366
367 foreach ($names as $name) {
368 if ($name instanceof Twig_Template) {
369 return $name;
370 }
371
372 try {
373 return $this->loadTemplate($name);
374 } catch (Twig_Error_Loader $e) {
375 }
376 }
377
378 if (1 === count($names)) {
379 throw $e;
380 }
381
382 throw new Twig_Error_Loader(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
383 }
384
385 /**
386 * Clears the internal template cache.
387 */
388 public function clearTemplateCache()
389 {
390 $this->loadedTemplates = array();
391 }
392
393 /**
394 * Clears the template cache files on the filesystem.
395 */
396 public function clearCacheFiles()
397 {
398 if (false === $this->cache) {
399 return;
400 }
401
402 foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($this->cache), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
403 if ($file->isFile()) {
404 @unlink($file->getPathname());
405 }
406 }
407 }
408
409 /**
410 * Gets the Lexer instance.
411 *
412 * @return Twig_LexerInterface A Twig_LexerInterface instance
413 */
414 public function getLexer()
415 {
416 if (null === $this->lexer) {
417 $this->lexer = new Twig_Lexer($this);
418 }
419
420 return $this->lexer;
421 }
422
423 /**
424 * Sets the Lexer instance.
425 *
426 * @param Twig_LexerInterface A Twig_LexerInterface instance
427 */
428 public function setLexer(Twig_LexerInterface $lexer)
429 {
430 $this->lexer = $lexer;
431 }
432
433 /**
434 * Tokenizes a source code.
435 *
436 * @param string $source The template source code
437 * @param string $name The template name
438 *
439 * @return Twig_TokenStream A Twig_TokenStream instance
440 */
441 public function tokenize($source, $name = null)
442 {
443 return $this->getLexer()->tokenize($source, $name);
444 }
445
446 /**
447 * Gets the Parser instance.
448 *
449 * @return Twig_ParserInterface A Twig_ParserInterface instance
450 */
451 public function getParser()
452 {
453 if (null === $this->parser) {
454 $this->parser = new Twig_Parser($this);
455 }
456
457 return $this->parser;
458 }
459
460 /**
461 * Sets the Parser instance.
462 *
463 * @param Twig_ParserInterface A Twig_ParserInterface instance
464 */
465 public function setParser(Twig_ParserInterface $parser)
466 {
467 $this->parser = $parser;
468 }
469
470 /**
471 * Parses a token stream.
472 *
473 * @param Twig_TokenStream $tokens A Twig_TokenStream instance
474 *
475 * @return Twig_Node_Module A Node tree
476 */
477 public function parse(Twig_TokenStream $tokens)
478 {
479 return $this->getParser()->parse($tokens);
480 }
481
482 /**
483 * Gets the Compiler instance.
484 *
485 * @return Twig_CompilerInterface A Twig_CompilerInterface instance
486 */
487 public function getCompiler()
488 {
489 if (null === $this->compiler) {
490 $this->compiler = new Twig_Compiler($this);
491 }
492
493 return $this->compiler;
494 }
495
496 /**
497 * Sets the Compiler instance.
498 *
499 * @param Twig_CompilerInterface $compiler A Twig_CompilerInterface instance
500 */
501 public function setCompiler(Twig_CompilerInterface $compiler)
502 {
503 $this->compiler = $compiler;
504 }
505
506 /**
507 * Compiles a Node.
508 *
509 * @param Twig_NodeInterface $node A Twig_NodeInterface instance
510 *
511 * @return string The compiled PHP source code
512 */
513 public function compile(Twig_NodeInterface $node)
514 {
515 return $this->getCompiler()->compile($node)->getSource();
516 }
517
518 /**
519 * Compiles a template source code.
520 *
521 * @param string $source The template source code
522 * @param string $name The template name
523 *
524 * @return string The compiled PHP source code
525 */
526 public function compileSource($source, $name = null)
527 {
528 try {
529 return $this->compile($this->parse($this->tokenize($source, $name)));
530 } catch (Twig_Error $e) {
531 $e->setTemplateFile($name);
532 throw $e;
533 } catch (Exception $e) {
534 throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $name, $e);
535 }
536 }
537
538 /**
539 * Sets the Loader instance.
540 *
541 * @param Twig_LoaderInterface $loader A Twig_LoaderInterface instance
542 */
543 public function setLoader(Twig_LoaderInterface $loader)
544 {
545 $this->loader = $loader;
546 }
547
548 /**
549 * Gets the Loader instance.
550 *
551 * @return Twig_LoaderInterface A Twig_LoaderInterface instance
552 */
553 public function getLoader()
554 {
555 if (null === $this->loader) {
556 throw new LogicException('You must set a loader first.');
557 }
558
559 return $this->loader;
560 }
561
562 /**
563 * Sets the default template charset.
564 *
565 * @param string $charset The default charset
566 */
567 public function setCharset($charset)
568 {
569 $this->charset = strtoupper($charset);
570 }
571
572 /**
573 * Gets the default template charset.
574 *
575 * @return string The default charset
576 */
577 public function getCharset()
578 {
579 return $this->charset;
580 }
581
582 /**
583 * Initializes the runtime environment.
584 */
585 public function initRuntime()
586 {
587 $this->runtimeInitialized = true;
588
589 foreach ($this->getExtensions() as $extension) {
590 $extension->initRuntime($this);
591 }
592 }
593
594 /**
595 * Returns true if the given extension is registered.
596 *
597 * @param string $name The extension name
598 *
599 * @return Boolean Whether the extension is registered or not
600 */
601 public function hasExtension($name)
602 {
603 return isset($this->extensions[$name]);
604 }
605
606 /**
607 * Gets an extension by name.
608 *
609 * @param string $name The extension name
610 *
611 * @return Twig_ExtensionInterface A Twig_ExtensionInterface instance
612 */
613 public function getExtension($name)
614 {
615 if (!isset($this->extensions[$name])) {
616 throw new Twig_Error_Runtime(sprintf('The "%s" extension is not enabled.', $name));
617 }
618
619 return $this->extensions[$name];
620 }
621
622 /**
623 * Registers an extension.
624 *
625 * @param Twig_ExtensionInterface $extension A Twig_ExtensionInterface instance
626 */
627 public function addExtension(Twig_ExtensionInterface $extension)
628 {
629 if ($this->extensionInitialized) {
630 throw new LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $extension->getName()));
631 }
632
633 $this->extensions[$extension->getName()] = $extension;
634 }
635
636 /**
637 * Removes an extension by name.
638 *
639 * This method is deprecated and you should not use it.
640 *
641 * @param string $name The extension name
642 *
643 * @deprecated since 1.12 (to be removed in 2.0)
644 */
645 public function removeExtension($name)
646 {
647 if ($this->extensionInitialized) {
648 throw new LogicException(sprintf('Unable to remove extension "%s" as extensions have already been initialized.', $name));
649 }
650
651 unset($this->extensions[$name]);
652 }
653
654 /**
655 * Registers an array of extensions.
656 *
657 * @param array $extensions An array of extensions
658 */
659 public function setExtensions(array $extensions)
660 {
661 foreach ($extensions as $extension) {
662 $this->addExtension($extension);
663 }
664 }
665
666 /**
667 * Returns all registered extensions.
668 *
669 * @return array An array of extensions
670 */
671 public function getExtensions()
672 {
673 return $this->extensions;
674 }
675
676 /**
677 * Registers a Token Parser.
678 *
679 * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
680 */
681 public function addTokenParser(Twig_TokenParserInterface $parser)
682 {
683 if ($this->extensionInitialized) {
684 throw new LogicException('Unable to add a token parser as extensions have already been initialized.');
685 }
686
687 $this->staging->addTokenParser($parser);
688 }
689
690 /**
691 * Gets the registered Token Parsers.
692 *
693 * @return Twig_TokenParserBrokerInterface A broker containing token parsers
694 */
695 public function getTokenParsers()
696 {
697 if (!$this->extensionInitialized) {
698 $this->initExtensions();
699 }
700
701 return $this->parsers;
702 }
703
704 /**
705 * Gets registered tags.
706 *
707 * Be warned that this method cannot return tags defined by Twig_TokenParserBrokerInterface classes.
708 *
709 * @return Twig_TokenParserInterface[] An array of Twig_TokenParserInterface instances
710 */
711 public function getTags()
712 {
713 $tags = array();
714 foreach ($this->getTokenParsers()->getParsers() as $parser) {
715 if ($parser instanceof Twig_TokenParserInterface) {
716 $tags[$parser->getTag()] = $parser;
717 }
718 }
719
720 return $tags;
721 }
722
723 /**
724 * Registers a Node Visitor.
725 *
726 * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
727 */
728 public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
729 {
730 if ($this->extensionInitialized) {
731 throw new LogicException('Unable to add a node visitor as extensions have already been initialized.');
732 }
733
734 $this->staging->addNodeVisitor($visitor);
735 }
736
737 /**
738 * Gets the registered Node Visitors.
739 *
740 * @return Twig_NodeVisitorInterface[] An array of Twig_NodeVisitorInterface instances
741 */
742 public function getNodeVisitors()
743 {
744 if (!$this->extensionInitialized) {
745 $this->initExtensions();
746 }
747
748 return $this->visitors;
749 }
750
751 /**
752 * Registers a Filter.
753 *
754 * @param string|Twig_SimpleFilter $name The filter name or a Twig_SimpleFilter instance
755 * @param Twig_FilterInterface|Twig_SimpleFilter $filter A Twig_FilterInterface instance or a Twig_SimpleFilter instance
756 */
757 public function addFilter($name, $filter = null)
758 {
759 if (!$name instanceof Twig_SimpleFilter && !($filter instanceof Twig_SimpleFilter || $filter instanceof Twig_FilterInterface)) {
760 throw new LogicException('A filter must be an instance of Twig_FilterInterface or Twig_SimpleFilter');
761 }
762
763 if ($name instanceof Twig_SimpleFilter) {
764 $filter = $name;
765 $name = $filter->getName();
766 }
767
768 if ($this->extensionInitialized) {
769 throw new LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $name));
770 }
771
772 $this->staging->addFilter($name, $filter);
773 }
774
775 /**
776 * Get a filter by name.
777 *
778 * Subclasses may override this method and load filters differently;
779 * so no list of filters is available.
780 *
781 * @param string $name The filter name
782 *
783 * @return Twig_Filter|false A Twig_Filter instance or false if the filter does not exist
784 */
785 public function getFilter($name)
786 {
787 if (!$this->extensionInitialized) {
788 $this->initExtensions();
789 }
790
791 if (isset($this->filters[$name])) {
792 return $this->filters[$name];
793 }
794
795 foreach ($this->filters as $pattern => $filter) {
796 $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
797
798 if ($count) {
799 if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
800 array_shift($matches);
801 $filter->setArguments($matches);
802
803 return $filter;
804 }
805 }
806 }
807
808 foreach ($this->filterCallbacks as $callback) {
809 if (false !== $filter = call_user_func($callback, $name)) {
810 return $filter;
811 }
812 }
813
814 return false;
815 }
816
817 public function registerUndefinedFilterCallback($callable)
818 {
819 $this->filterCallbacks[] = $callable;
820 }
821
822 /**
823 * Gets the registered Filters.
824 *
825 * Be warned that this method cannot return filters defined with registerUndefinedFunctionCallback.
826 *
827 * @return Twig_FilterInterface[] An array of Twig_FilterInterface instances
828 *
829 * @see registerUndefinedFilterCallback
830 */
831 public function getFilters()
832 {
833 if (!$this->extensionInitialized) {
834 $this->initExtensions();
835 }
836
837 return $this->filters;
838 }
839
840 /**
841 * Registers a Test.
842 *
843 * @param string|Twig_SimpleTest $name The test name or a Twig_SimpleTest instance
844 * @param Twig_TestInterface|Twig_SimpleTest $test A Twig_TestInterface instance or a Twig_SimpleTest instance
845 */
846 public function addTest($name, $test = null)
847 {
848 if (!$name instanceof Twig_SimpleTest && !($test instanceof Twig_SimpleTest || $test instanceof Twig_TestInterface)) {
849 throw new LogicException('A test must be an instance of Twig_TestInterface or Twig_SimpleTest');
850 }
851
852 if ($name instanceof Twig_SimpleTest) {
853 $test = $name;
854 $name = $test->getName();
855 }
856
857 if ($this->extensionInitialized) {
858 throw new LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $name));
859 }
860
861 $this->staging->addTest($name, $test);
862 }
863
864 /**
865 * Gets the registered Tests.
866 *
867 * @return Twig_TestInterface[] An array of Twig_TestInterface instances
868 */
869 public function getTests()
870 {
871 if (!$this->extensionInitialized) {
872 $this->initExtensions();
873 }
874
875 return $this->tests;
876 }
877
878 /**
879 * Gets a test by name.
880 *
881 * @param string $name The test name
882 *
883 * @return Twig_Test|false A Twig_Test instance or false if the test does not exist
884 */
885 public function getTest($name)
886 {
887 if (!$this->extensionInitialized) {
888 $this->initExtensions();
889 }
890
891 if (isset($this->tests[$name])) {
892 return $this->tests[$name];
893 }
894
895 return false;
896 }
897
898 /**
899 * Registers a Function.
900 *
901 * @param string|Twig_SimpleFunction $name The function name or a Twig_SimpleFunction instance
902 * @param Twig_FunctionInterface|Twig_SimpleFunction $function A Twig_FunctionInterface instance or a Twig_SimpleFunction instance
903 */
904 public function addFunction($name, $function = null)
905 {
906 if (!$name instanceof Twig_SimpleFunction && !($function instanceof Twig_SimpleFunction || $function instanceof Twig_FunctionInterface)) {
907 throw new LogicException('A function must be an instance of Twig_FunctionInterface or Twig_SimpleFunction');
908 }
909
910 if ($name instanceof Twig_SimpleFunction) {
911 $function = $name;
912 $name = $function->getName();
913 }
914
915 if ($this->extensionInitialized) {
916 throw new LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $name));
917 }
918
919 $this->staging->addFunction($name, $function);
920 }
921
922 /**
923 * Get a function by name.
924 *
925 * Subclasses may override this method and load functions differently;
926 * so no list of functions is available.
927 *
928 * @param string $name function name
929 *
930 * @return Twig_Function|false A Twig_Function instance or false if the function does not exist
931 */
932 public function getFunction($name)
933 {
934 if (!$this->extensionInitialized) {
935 $this->initExtensions();
936 }
937
938 if (isset($this->functions[$name])) {
939 return $this->functions[$name];
940 }
941
942 foreach ($this->functions as $pattern => $function) {
943 $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count);
944
945 if ($count) {
946 if (preg_match('#^'.$pattern.'$#', $name, $matches)) {
947 array_shift($matches);
948 $function->setArguments($matches);
949
950 return $function;
951 }
952 }
953 }
954
955 foreach ($this->functionCallbacks as $callback) {
956 if (false !== $function = call_user_func($callback, $name)) {
957 return $function;
958 }
959 }
960
961 return false;
962 }
963
964 public function registerUndefinedFunctionCallback($callable)
965 {
966 $this->functionCallbacks[] = $callable;
967 }
968
969 /**
970 * Gets registered functions.
971 *
972 * Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
973 *
974 * @return Twig_FunctionInterface[] An array of Twig_FunctionInterface instances
975 *
976 * @see registerUndefinedFunctionCallback
977 */
978 public function getFunctions()
979 {
980 if (!$this->extensionInitialized) {
981 $this->initExtensions();
982 }
983
984 return $this->functions;
985 }
986
987 /**
988 * Registers a Global.
989 *
990 * New globals can be added before compiling or rendering a template;
991 * but after, you can only update existing globals.
992 *
993 * @param string $name The global name
994 * @param mixed $value The global value
995 */
996 public function addGlobal($name, $value)
997 {
998 if ($this->extensionInitialized || $this->runtimeInitialized) {
999 if (null === $this->globals) {
1000 $this->globals = $this->initGlobals();
1001 }
1002
1003 /* This condition must be uncommented in Twig 2.0
1004 if (!array_key_exists($name, $this->globals)) {
1005 throw new LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
1006 }
1007 */
1008 }
1009
1010 if ($this->extensionInitialized || $this->runtimeInitialized) {
1011 // update the value
1012 $this->globals[$name] = $value;
1013 } else {
1014 $this->staging->addGlobal($name, $value);
1015 }
1016 }
1017
1018 /**
1019 * Gets the registered Globals.
1020 *
1021 * @return array An array of globals
1022 */
1023 public function getGlobals()
1024 {
1025 if (!$this->runtimeInitialized && !$this->extensionInitialized) {
1026 return $this->initGlobals();
1027 }
1028
1029 if (null === $this->globals) {
1030 $this->globals = $this->initGlobals();
1031 }
1032
1033 return $this->globals;
1034 }
1035
1036 /**
1037 * Merges a context with the defined globals.
1038 *
1039 * @param array $context An array representing the context
1040 *
1041 * @return array The context merged with the globals
1042 */
1043 public function mergeGlobals(array $context)
1044 {
1045 // we don't use array_merge as the context being generally
1046 // bigger than globals, this code is faster.
1047 foreach ($this->getGlobals() as $key => $value) {
1048 if (!array_key_exists($key, $context)) {
1049 $context[$key] = $value;
1050 }
1051 }
1052
1053 return $context;
1054 }
1055
1056 /**
1057 * Gets the registered unary Operators.
1058 *
1059 * @return array An array of unary operators
1060 */
1061 public function getUnaryOperators()
1062 {
1063 if (!$this->extensionInitialized) {
1064 $this->initExtensions();
1065 }
1066
1067 return $this->unaryOperators;
1068 }
1069
1070 /**
1071 * Gets the registered binary Operators.
1072 *
1073 * @return array An array of binary operators
1074 */
1075 public function getBinaryOperators()
1076 {
1077 if (!$this->extensionInitialized) {
1078 $this->initExtensions();
1079 }
1080
1081 return $this->binaryOperators;
1082 }
1083
1084 public function computeAlternatives($name, $items)
1085 {
1086 $alternatives = array();
1087 foreach ($items as $item) {
1088 $lev = levenshtein($name, $item);
1089 if ($lev <= strlen($name) / 3 || false !== strpos($item, $name)) {
1090 $alternatives[$item] = $lev;
1091 }
1092 }
1093 asort($alternatives);
1094
1095 return array_keys($alternatives);
1096 }
1097
1098 protected function initGlobals()
1099 {
1100 $globals = array();
1101 foreach ($this->extensions as $extension) {
1102 $extGlob = $extension->getGlobals();
1103 if (!is_array($extGlob)) {
1104 throw new UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', get_class($extension)));
1105 }
1106
1107 $globals[] = $extGlob;
1108 }
1109
1110 $globals[] = $this->staging->getGlobals();
1111
1112 return call_user_func_array('array_merge', $globals);
1113 }
1114
1115 protected function initExtensions()
1116 {
1117 if ($this->extensionInitialized) {
1118 return;
1119 }
1120
1121 $this->extensionInitialized = true;
1122 $this->parsers = new Twig_TokenParserBroker();
1123 $this->filters = array();
1124 $this->functions = array();
1125 $this->tests = array();
1126 $this->visitors = array();
1127 $this->unaryOperators = array();
1128 $this->binaryOperators = array();
1129
1130 foreach ($this->extensions as $extension) {
1131 $this->initExtension($extension);
1132 }
1133 $this->initExtension($this->staging);
1134 }
1135
1136 protected function initExtension(Twig_ExtensionInterface $extension)
1137 {
1138 // filters
1139 foreach ($extension->getFilters() as $name => $filter) {
1140 if ($name instanceof Twig_SimpleFilter) {
1141 $filter = $name;
1142 $name = $filter->getName();
1143 } elseif ($filter instanceof Twig_SimpleFilter) {
1144 $name = $filter->getName();
1145 }
1146
1147 $this->filters[$name] = $filter;
1148 }
1149
1150 // functions
1151 foreach ($extension->getFunctions() as $name => $function) {
1152 if ($name instanceof Twig_SimpleFunction) {
1153 $function = $name;
1154 $name = $function->getName();
1155 } elseif ($function instanceof Twig_SimpleFunction) {
1156 $name = $function->getName();
1157 }
1158
1159 $this->functions[$name] = $function;
1160 }
1161
1162 // tests
1163 foreach ($extension->getTests() as $name => $test) {
1164 if ($name instanceof Twig_SimpleTest) {
1165 $test = $name;
1166 $name = $test->getName();
1167 } elseif ($test instanceof Twig_SimpleTest) {
1168 $name = $test->getName();
1169 }
1170
1171 $this->tests[$name] = $test;
1172 }
1173
1174 // token parsers
1175 foreach ($extension->getTokenParsers() as $parser) {
1176 if ($parser instanceof Twig_TokenParserInterface) {
1177 $this->parsers->addTokenParser($parser);
1178 } elseif ($parser instanceof Twig_TokenParserBrokerInterface) {
1179 $this->parsers->addTokenParserBroker($parser);
1180 } else {
1181 throw new LogicException('getTokenParsers() must return an array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances');
1182 }
1183 }
1184
1185 // node visitors
1186 foreach ($extension->getNodeVisitors() as $visitor) {
1187 $this->visitors[] = $visitor;
1188 }
1189
1190 // operators
1191 if ($operators = $extension->getOperators()) {
1192 if (2 !== count($operators)) {
1193 throw new InvalidArgumentException(sprintf('"%s::getOperators()" does not return a valid operators array.', get_class($extension)));
1194 }
1195
1196 $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]);
1197 $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]);
1198 }
1199 }
1200
1201 protected function writeCacheFile($file, $content)
1202 {
1203 $dir = dirname($file);
1204 if (!is_dir($dir)) {
1205 if (false === @mkdir($dir, 0777, true) && !is_dir($dir)) {
1206 throw new RuntimeException(sprintf("Unable to create the cache directory (%s).", $dir));
1207 }
1208 } elseif (!is_writable($dir)) {
1209 throw new RuntimeException(sprintf("Unable to write in the cache directory (%s).", $dir));
1210 }
1211
1212 $tmpFile = tempnam(dirname($file), basename($file));
1213 if (false !== @file_put_contents($tmpFile, $content)) {
1214 // rename does not work on Win32 before 5.2.6
1215 if (@rename($tmpFile, $file) || (@copy($tmpFile, $file) && unlink($tmpFile))) {
1216 @chmod($file, 0666 & ~umask());
1217
1218 return;
1219 }
1220 }
1221
1222 throw new RuntimeException(sprintf('Failed to write cache file "%s".', $file));
1223 }
1224}
diff --git a/vendor/twig/twig/lib/Twig/Error.php b/vendor/twig/twig/lib/Twig/Error.php
new file mode 100644
index 00000000..61a4cfa0
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Error.php
@@ -0,0 +1,243 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig base exception.
14 *
15 * This exception class and its children must only be used when
16 * an error occurs during the loading of a template, when a syntax error
17 * is detected in a template, or when rendering a template. Other
18 * errors must use regular PHP exception classes (like when the template
19 * cache directory is not writable for instance).
20 *
21 * To help debugging template issues, this class tracks the original template
22 * name and line where the error occurred.
23 *
24 * Whenever possible, you must set these information (original template name
25 * and line number) yourself by passing them to the constructor. If some or all
26 * these information are not available from where you throw the exception, then
27 * this class will guess them automatically (when the line number is set to -1
28 * and/or the filename is set to null). As this is a costly operation, this
29 * can be disabled by passing false for both the filename and the line number
30 * when creating a new instance of this class.
31 *
32 * @author Fabien Potencier <fabien@symfony.com>
33 */
34class Twig_Error extends Exception
35{
36 protected $lineno;
37 protected $filename;
38 protected $rawMessage;
39 protected $previous;
40
41 /**
42 * Constructor.
43 *
44 * Set both the line number and the filename to false to
45 * disable automatic guessing of the original template name
46 * and line number.
47 *
48 * Set the line number to -1 to enable its automatic guessing.
49 * Set the filename to null to enable its automatic guessing.
50 *
51 * By default, automatic guessing is enabled.
52 *
53 * @param string $message The error message
54 * @param integer $lineno The template line where the error occurred
55 * @param string $filename The template file name where the error occurred
56 * @param Exception $previous The previous exception
57 */
58 public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
59 {
60 if (version_compare(PHP_VERSION, '5.3.0', '<')) {
61 $this->previous = $previous;
62 parent::__construct('');
63 } else {
64 parent::__construct('', 0, $previous);
65 }
66
67 $this->lineno = $lineno;
68 $this->filename = $filename;
69
70 if (-1 === $this->lineno || null === $this->filename) {
71 $this->guessTemplateInfo();
72 }
73
74 $this->rawMessage = $message;
75
76 $this->updateRepr();
77 }
78
79 /**
80 * Gets the raw message.
81 *
82 * @return string The raw message
83 */
84 public function getRawMessage()
85 {
86 return $this->rawMessage;
87 }
88
89 /**
90 * Gets the filename where the error occurred.
91 *
92 * @return string The filename
93 */
94 public function getTemplateFile()
95 {
96 return $this->filename;
97 }
98
99 /**
100 * Sets the filename where the error occurred.
101 *
102 * @param string $filename The filename
103 */
104 public function setTemplateFile($filename)
105 {
106 $this->filename = $filename;
107
108 $this->updateRepr();
109 }
110
111 /**
112 * Gets the template line where the error occurred.
113 *
114 * @return integer The template line
115 */
116 public function getTemplateLine()
117 {
118 return $this->lineno;
119 }
120
121 /**
122 * Sets the template line where the error occurred.
123 *
124 * @param integer $lineno The template line
125 */
126 public function setTemplateLine($lineno)
127 {
128 $this->lineno = $lineno;
129
130 $this->updateRepr();
131 }
132
133 public function guess()
134 {
135 $this->guessTemplateInfo();
136 $this->updateRepr();
137 }
138
139 /**
140 * For PHP < 5.3.0, provides access to the getPrevious() method.
141 *
142 * @param string $method The method name
143 * @param array $arguments The parameters to be passed to the method
144 *
145 * @return Exception The previous exception or null
146 *
147 * @throws BadMethodCallException
148 */
149 public function __call($method, $arguments)
150 {
151 if ('getprevious' == strtolower($method)) {
152 return $this->previous;
153 }
154
155 throw new BadMethodCallException(sprintf('Method "Twig_Error::%s()" does not exist.', $method));
156 }
157
158 protected function updateRepr()
159 {
160 $this->message = $this->rawMessage;
161
162 $dot = false;
163 if ('.' === substr($this->message, -1)) {
164 $this->message = substr($this->message, 0, -1);
165 $dot = true;
166 }
167
168 if ($this->filename) {
169 if (is_string($this->filename) || (is_object($this->filename) && method_exists($this->filename, '__toString'))) {
170 $filename = sprintf('"%s"', $this->filename);
171 } else {
172 $filename = json_encode($this->filename);
173 }
174 $this->message .= sprintf(' in %s', $filename);
175 }
176
177 if ($this->lineno && $this->lineno >= 0) {
178 $this->message .= sprintf(' at line %d', $this->lineno);
179 }
180
181 if ($dot) {
182 $this->message .= '.';
183 }
184 }
185
186 protected function guessTemplateInfo()
187 {
188 $template = null;
189 $templateClass = null;
190
191 if (version_compare(phpversion(), '5.3.6', '>=')) {
192 $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT);
193 } else {
194 $backtrace = debug_backtrace();
195 }
196
197 foreach ($backtrace as $trace) {
198 if (isset($trace['object']) && $trace['object'] instanceof Twig_Template && 'Twig_Template' !== get_class($trace['object'])) {
199 $currentClass = get_class($trace['object']);
200 $isEmbedContainer = 0 === strpos($templateClass, $currentClass);
201 if (null === $this->filename || ($this->filename == $trace['object']->getTemplateName() && !$isEmbedContainer)) {
202 $template = $trace['object'];
203 $templateClass = get_class($trace['object']);
204 }
205 }
206 }
207
208 // update template filename
209 if (null !== $template && null === $this->filename) {
210 $this->filename = $template->getTemplateName();
211 }
212
213 if (null === $template || $this->lineno > -1) {
214 return;
215 }
216
217 $r = new ReflectionObject($template);
218 $file = $r->getFileName();
219
220 $exceptions = array($e = $this);
221 while (($e instanceof self || method_exists($e, 'getPrevious')) && $e = $e->getPrevious()) {
222 $exceptions[] = $e;
223 }
224
225 while ($e = array_pop($exceptions)) {
226 $traces = $e->getTrace();
227 while ($trace = array_shift($traces)) {
228 if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
229 continue;
230 }
231
232 foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
233 if ($codeLine <= $trace['line']) {
234 // update template line
235 $this->lineno = $templateLine;
236
237 return;
238 }
239 }
240 }
241 }
242 }
243}
diff --git a/vendor/twig/twig/lib/Twig/Error/Loader.php b/vendor/twig/twig/lib/Twig/Error/Loader.php
new file mode 100644
index 00000000..68efb574
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Error/Loader.php
@@ -0,0 +1,31 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Exception thrown when an error occurs during template loading.
14 *
15 * Automatic template information guessing is always turned off as
16 * if a template cannot be loaded, there is nothing to guess.
17 * However, when a template is loaded from another one, then, we need
18 * to find the current context and this is automatically done by
19 * Twig_Template::displayWithErrorHandling().
20 *
21 * This strategy makes Twig_Environment::resolveTemplate() much faster.
22 *
23 * @author Fabien Potencier <fabien@symfony.com>
24 */
25class Twig_Error_Loader extends Twig_Error
26{
27 public function __construct($message, $lineno = -1, $filename = null, Exception $previous = null)
28 {
29 parent::__construct($message, false, false, $previous);
30 }
31}
diff --git a/vendor/twig/twig/lib/Twig/Error/Runtime.php b/vendor/twig/twig/lib/Twig/Error/Runtime.php
new file mode 100644
index 00000000..8b6ceddb
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Error/Runtime.php
@@ -0,0 +1,20 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Exception thrown when an error occurs at runtime.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Error_Runtime extends Twig_Error
19{
20}
diff --git a/vendor/twig/twig/lib/Twig/Error/Syntax.php b/vendor/twig/twig/lib/Twig/Error/Syntax.php
new file mode 100644
index 00000000..0f5c5792
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Error/Syntax.php
@@ -0,0 +1,20 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Exception thrown when a syntax error occurs during lexing or parsing of a template.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Error_Syntax extends Twig_Error
19{
20}
diff --git a/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php b/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php
new file mode 100644
index 00000000..ce434765
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/ExistsLoaderInterface.php
@@ -0,0 +1,28 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Adds an exists() method for loaders.
14 *
15 * @author Florin Patan <florinpatan@gmail.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_ExistsLoaderInterface
19{
20 /**
21 * Check if we have the source code of a template, given its name.
22 *
23 * @param string $name The name of the template to check if we can load
24 *
25 * @return boolean If the template source code is handled by this loader or not
26 */
27 public function exists($name);
28}
diff --git a/vendor/twig/twig/lib/Twig/ExpressionParser.php b/vendor/twig/twig/lib/Twig/ExpressionParser.php
new file mode 100644
index 00000000..9cf19344
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/ExpressionParser.php
@@ -0,0 +1,600 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Parses expressions.
15 *
16 * This parser implements a "Precedence climbing" algorithm.
17 *
18 * @see http://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
19 * @see http://en.wikipedia.org/wiki/Operator-precedence_parser
20 *
21 * @author Fabien Potencier <fabien@symfony.com>
22 */
23class Twig_ExpressionParser
24{
25 const OPERATOR_LEFT = 1;
26 const OPERATOR_RIGHT = 2;
27
28 protected $parser;
29 protected $unaryOperators;
30 protected $binaryOperators;
31
32 public function __construct(Twig_Parser $parser, array $unaryOperators, array $binaryOperators)
33 {
34 $this->parser = $parser;
35 $this->unaryOperators = $unaryOperators;
36 $this->binaryOperators = $binaryOperators;
37 }
38
39 public function parseExpression($precedence = 0)
40 {
41 $expr = $this->getPrimary();
42 $token = $this->parser->getCurrentToken();
43 while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) {
44 $op = $this->binaryOperators[$token->getValue()];
45 $this->parser->getStream()->next();
46
47 if (isset($op['callable'])) {
48 $expr = call_user_func($op['callable'], $this->parser, $expr);
49 } else {
50 $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']);
51 $class = $op['class'];
52 $expr = new $class($expr, $expr1, $token->getLine());
53 }
54
55 $token = $this->parser->getCurrentToken();
56 }
57
58 if (0 === $precedence) {
59 return $this->parseConditionalExpression($expr);
60 }
61
62 return $expr;
63 }
64
65 protected function getPrimary()
66 {
67 $token = $this->parser->getCurrentToken();
68
69 if ($this->isUnary($token)) {
70 $operator = $this->unaryOperators[$token->getValue()];
71 $this->parser->getStream()->next();
72 $expr = $this->parseExpression($operator['precedence']);
73 $class = $operator['class'];
74
75 return $this->parsePostfixExpression(new $class($expr, $token->getLine()));
76 } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
77 $this->parser->getStream()->next();
78 $expr = $this->parseExpression();
79 $this->parser->getStream()->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
80
81 return $this->parsePostfixExpression($expr);
82 }
83
84 return $this->parsePrimaryExpression();
85 }
86
87 protected function parseConditionalExpression($expr)
88 {
89 while ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '?')) {
90 $this->parser->getStream()->next();
91 if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
92 $expr2 = $this->parseExpression();
93 if ($this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
94 $this->parser->getStream()->next();
95 $expr3 = $this->parseExpression();
96 } else {
97 $expr3 = new Twig_Node_Expression_Constant('', $this->parser->getCurrentToken()->getLine());
98 }
99 } else {
100 $this->parser->getStream()->next();
101 $expr2 = $expr;
102 $expr3 = $this->parseExpression();
103 }
104
105 $expr = new Twig_Node_Expression_Conditional($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine());
106 }
107
108 return $expr;
109 }
110
111 protected function isUnary(Twig_Token $token)
112 {
113 return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]);
114 }
115
116 protected function isBinary(Twig_Token $token)
117 {
118 return $token->test(Twig_Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]);
119 }
120
121 public function parsePrimaryExpression()
122 {
123 $token = $this->parser->getCurrentToken();
124 switch ($token->getType()) {
125 case Twig_Token::NAME_TYPE:
126 $this->parser->getStream()->next();
127 switch ($token->getValue()) {
128 case 'true':
129 case 'TRUE':
130 $node = new Twig_Node_Expression_Constant(true, $token->getLine());
131 break;
132
133 case 'false':
134 case 'FALSE':
135 $node = new Twig_Node_Expression_Constant(false, $token->getLine());
136 break;
137
138 case 'none':
139 case 'NONE':
140 case 'null':
141 case 'NULL':
142 $node = new Twig_Node_Expression_Constant(null, $token->getLine());
143 break;
144
145 default:
146 if ('(' === $this->parser->getCurrentToken()->getValue()) {
147 $node = $this->getFunctionNode($token->getValue(), $token->getLine());
148 } else {
149 $node = new Twig_Node_Expression_Name($token->getValue(), $token->getLine());
150 }
151 }
152 break;
153
154 case Twig_Token::NUMBER_TYPE:
155 $this->parser->getStream()->next();
156 $node = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
157 break;
158
159 case Twig_Token::STRING_TYPE:
160 case Twig_Token::INTERPOLATION_START_TYPE:
161 $node = $this->parseStringExpression();
162 break;
163
164 default:
165 if ($token->test(Twig_Token::PUNCTUATION_TYPE, '[')) {
166 $node = $this->parseArrayExpression();
167 } elseif ($token->test(Twig_Token::PUNCTUATION_TYPE, '{')) {
168 $node = $this->parseHashExpression();
169 } else {
170 throw new Twig_Error_Syntax(sprintf('Unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($token->getType(), $token->getLine()), $token->getValue()), $token->getLine(), $this->parser->getFilename());
171 }
172 }
173
174 return $this->parsePostfixExpression($node);
175 }
176
177 public function parseStringExpression()
178 {
179 $stream = $this->parser->getStream();
180
181 $nodes = array();
182 // a string cannot be followed by another string in a single expression
183 $nextCanBeString = true;
184 while (true) {
185 if ($stream->test(Twig_Token::STRING_TYPE) && $nextCanBeString) {
186 $token = $stream->next();
187 $nodes[] = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
188 $nextCanBeString = false;
189 } elseif ($stream->test(Twig_Token::INTERPOLATION_START_TYPE)) {
190 $stream->next();
191 $nodes[] = $this->parseExpression();
192 $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
193 $nextCanBeString = true;
194 } else {
195 break;
196 }
197 }
198
199 $expr = array_shift($nodes);
200 foreach ($nodes as $node) {
201 $expr = new Twig_Node_Expression_Binary_Concat($expr, $node, $node->getLine());
202 }
203
204 return $expr;
205 }
206
207 public function parseArrayExpression()
208 {
209 $stream = $this->parser->getStream();
210 $stream->expect(Twig_Token::PUNCTUATION_TYPE, '[', 'An array element was expected');
211
212 $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
213 $first = true;
214 while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
215 if (!$first) {
216 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'An array element must be followed by a comma');
217
218 // trailing ,?
219 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
220 break;
221 }
222 }
223 $first = false;
224
225 $node->addElement($this->parseExpression());
226 }
227 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']', 'An opened array is not properly closed');
228
229 return $node;
230 }
231
232 public function parseHashExpression()
233 {
234 $stream = $this->parser->getStream();
235 $stream->expect(Twig_Token::PUNCTUATION_TYPE, '{', 'A hash element was expected');
236
237 $node = new Twig_Node_Expression_Array(array(), $stream->getCurrent()->getLine());
238 $first = true;
239 while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
240 if (!$first) {
241 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'A hash value must be followed by a comma');
242
243 // trailing ,?
244 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '}')) {
245 break;
246 }
247 }
248 $first = false;
249
250 // a hash key can be:
251 //
252 // * a number -- 12
253 // * a string -- 'a'
254 // * a name, which is equivalent to a string -- a
255 // * an expression, which must be enclosed in parentheses -- (1 + 2)
256 if ($stream->test(Twig_Token::STRING_TYPE) || $stream->test(Twig_Token::NAME_TYPE) || $stream->test(Twig_Token::NUMBER_TYPE)) {
257 $token = $stream->next();
258 $key = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
259 } elseif ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
260 $key = $this->parseExpression();
261 } else {
262 $current = $stream->getCurrent();
263
264 throw new Twig_Error_Syntax(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s"', Twig_Token::typeToEnglish($current->getType(), $current->getLine()), $current->getValue()), $current->getLine(), $this->parser->getFilename());
265 }
266
267 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ':', 'A hash key must be followed by a colon (:)');
268 $value = $this->parseExpression();
269
270 $node->addElement($value, $key);
271 }
272 $stream->expect(Twig_Token::PUNCTUATION_TYPE, '}', 'An opened hash is not properly closed');
273
274 return $node;
275 }
276
277 public function parsePostfixExpression($node)
278 {
279 while (true) {
280 $token = $this->parser->getCurrentToken();
281 if ($token->getType() == Twig_Token::PUNCTUATION_TYPE) {
282 if ('.' == $token->getValue() || '[' == $token->getValue()) {
283 $node = $this->parseSubscriptExpression($node);
284 } elseif ('|' == $token->getValue()) {
285 $node = $this->parseFilterExpression($node);
286 } else {
287 break;
288 }
289 } else {
290 break;
291 }
292 }
293
294 return $node;
295 }
296
297 public function getFunctionNode($name, $line)
298 {
299 switch ($name) {
300 case 'parent':
301 $args = $this->parseArguments();
302 if (!count($this->parser->getBlockStack())) {
303 throw new Twig_Error_Syntax('Calling "parent" outside a block is forbidden', $line, $this->parser->getFilename());
304 }
305
306 if (!$this->parser->getParent() && !$this->parser->hasTraits()) {
307 throw new Twig_Error_Syntax('Calling "parent" on a template that does not extend nor "use" another template is forbidden', $line, $this->parser->getFilename());
308 }
309
310 return new Twig_Node_Expression_Parent($this->parser->peekBlockStack(), $line);
311 case 'block':
312 return new Twig_Node_Expression_BlockReference($this->parseArguments()->getNode(0), false, $line);
313 case 'attribute':
314 $args = $this->parseArguments();
315 if (count($args) < 2) {
316 throw new Twig_Error_Syntax('The "attribute" function takes at least two arguments (the variable and the attributes)', $line, $this->parser->getFilename());
317 }
318
319 return new Twig_Node_Expression_GetAttr($args->getNode(0), $args->getNode(1), count($args) > 2 ? $args->getNode(2) : new Twig_Node_Expression_Array(array(), $line), Twig_TemplateInterface::ANY_CALL, $line);
320 default:
321 if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) {
322 $arguments = new Twig_Node_Expression_Array(array(), $line);
323 foreach ($this->parseArguments() as $n) {
324 $arguments->addElement($n);
325 }
326
327 $node = new Twig_Node_Expression_MethodCall($alias['node'], $alias['name'], $arguments, $line);
328 $node->setAttribute('safe', true);
329
330 return $node;
331 }
332
333 $args = $this->parseArguments(true);
334 $class = $this->getFunctionNodeClass($name, $line);
335
336 return new $class($name, $args, $line);
337 }
338 }
339
340 public function parseSubscriptExpression($node)
341 {
342 $stream = $this->parser->getStream();
343 $token = $stream->next();
344 $lineno = $token->getLine();
345 $arguments = new Twig_Node_Expression_Array(array(), $lineno);
346 $type = Twig_TemplateInterface::ANY_CALL;
347 if ($token->getValue() == '.') {
348 $token = $stream->next();
349 if (
350 $token->getType() == Twig_Token::NAME_TYPE
351 ||
352 $token->getType() == Twig_Token::NUMBER_TYPE
353 ||
354 ($token->getType() == Twig_Token::OPERATOR_TYPE && preg_match(Twig_Lexer::REGEX_NAME, $token->getValue()))
355 ) {
356 $arg = new Twig_Node_Expression_Constant($token->getValue(), $lineno);
357
358 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
359 $type = Twig_TemplateInterface::METHOD_CALL;
360 foreach ($this->parseArguments() as $n) {
361 $arguments->addElement($n);
362 }
363 }
364 } else {
365 throw new Twig_Error_Syntax('Expected name or number', $lineno, $this->parser->getFilename());
366 }
367
368 if ($node instanceof Twig_Node_Expression_Name && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) {
369 if (!$arg instanceof Twig_Node_Expression_Constant) {
370 throw new Twig_Error_Syntax(sprintf('Dynamic macro names are not supported (called on "%s")', $node->getAttribute('name')), $token->getLine(), $this->parser->getFilename());
371 }
372
373 $node = new Twig_Node_Expression_MethodCall($node, 'get'.$arg->getAttribute('value'), $arguments, $lineno);
374 $node->setAttribute('safe', true);
375
376 return $node;
377 }
378 } else {
379 $type = Twig_TemplateInterface::ARRAY_CALL;
380
381 // slice?
382 $slice = false;
383 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
384 $slice = true;
385 $arg = new Twig_Node_Expression_Constant(0, $token->getLine());
386 } else {
387 $arg = $this->parseExpression();
388 }
389
390 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ':')) {
391 $slice = true;
392 $stream->next();
393 }
394
395 if ($slice) {
396 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, ']')) {
397 $length = new Twig_Node_Expression_Constant(null, $token->getLine());
398 } else {
399 $length = $this->parseExpression();
400 }
401
402 $class = $this->getFilterNodeClass('slice', $token->getLine());
403 $arguments = new Twig_Node(array($arg, $length));
404 $filter = new $class($node, new Twig_Node_Expression_Constant('slice', $token->getLine()), $arguments, $token->getLine());
405
406 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
407
408 return $filter;
409 }
410
411 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ']');
412 }
413
414 return new Twig_Node_Expression_GetAttr($node, $arg, $arguments, $type, $lineno);
415 }
416
417 public function parseFilterExpression($node)
418 {
419 $this->parser->getStream()->next();
420
421 return $this->parseFilterExpressionRaw($node);
422 }
423
424 public function parseFilterExpressionRaw($node, $tag = null)
425 {
426 while (true) {
427 $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE);
428
429 $name = new Twig_Node_Expression_Constant($token->getValue(), $token->getLine());
430 if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
431 $arguments = new Twig_Node();
432 } else {
433 $arguments = $this->parseArguments(true);
434 }
435
436 $class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine());
437
438 $node = new $class($node, $name, $arguments, $token->getLine(), $tag);
439
440 if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, '|')) {
441 break;
442 }
443
444 $this->parser->getStream()->next();
445 }
446
447 return $node;
448 }
449
450 /**
451 * Parses arguments.
452 *
453 * @param Boolean $namedArguments Whether to allow named arguments or not
454 * @param Boolean $definition Whether we are parsing arguments for a function definition
455 */
456 public function parseArguments($namedArguments = false, $definition = false)
457 {
458 $args = array();
459 $stream = $this->parser->getStream();
460
461 $stream->expect(Twig_Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
462 while (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ')')) {
463 if (!empty($args)) {
464 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
465 }
466
467 if ($definition) {
468 $token = $stream->expect(Twig_Token::NAME_TYPE, null, 'An argument must be a name');
469 $value = new Twig_Node_Expression_Name($token->getValue(), $this->parser->getCurrentToken()->getLine());
470 } else {
471 $value = $this->parseExpression();
472 }
473
474 $name = null;
475 if ($namedArguments && $stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
476 $token = $stream->next();
477 if (!$value instanceof Twig_Node_Expression_Name) {
478 throw new Twig_Error_Syntax(sprintf('A parameter name must be a string, "%s" given', get_class($value)), $token->getLine(), $this->parser->getFilename());
479 }
480 $name = $value->getAttribute('name');
481
482 if ($definition) {
483 $value = $this->parsePrimaryExpression();
484
485 if (!$this->checkConstantExpression($value)) {
486 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());
487 }
488 } else {
489 $value = $this->parseExpression();
490 }
491 }
492
493 if ($definition) {
494 if (null === $name) {
495 $name = $value->getAttribute('name');
496 $value = new Twig_Node_Expression_Constant(null, $this->parser->getCurrentToken()->getLine());
497 }
498 $args[$name] = $value;
499 } else {
500 if (null === $name) {
501 $args[] = $value;
502 } else {
503 $args[$name] = $value;
504 }
505 }
506 }
507 $stream->expect(Twig_Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
508
509 return new Twig_Node($args);
510 }
511
512 public function parseAssignmentExpression()
513 {
514 $targets = array();
515 while (true) {
516 $token = $this->parser->getStream()->expect(Twig_Token::NAME_TYPE, null, 'Only variables can be assigned to');
517 if (in_array($token->getValue(), array('true', 'false', 'none'))) {
518 throw new Twig_Error_Syntax(sprintf('You cannot assign a value to "%s"', $token->getValue()), $token->getLine(), $this->parser->getFilename());
519 }
520 $targets[] = new Twig_Node_Expression_AssignName($token->getValue(), $token->getLine());
521
522 if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
523 break;
524 }
525 $this->parser->getStream()->next();
526 }
527
528 return new Twig_Node($targets);
529 }
530
531 public function parseMultitargetExpression()
532 {
533 $targets = array();
534 while (true) {
535 $targets[] = $this->parseExpression();
536 if (!$this->parser->getStream()->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
537 break;
538 }
539 $this->parser->getStream()->next();
540 }
541
542 return new Twig_Node($targets);
543 }
544
545 protected function getFunctionNodeClass($name, $line)
546 {
547 $env = $this->parser->getEnvironment();
548
549 if (false === $function = $env->getFunction($name)) {
550 $message = sprintf('The function "%s" does not exist', $name);
551 if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFunctions()))) {
552 $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
553 }
554
555 throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
556 }
557
558 if ($function instanceof Twig_SimpleFunction) {
559 return $function->getNodeClass();
560 }
561
562 return $function instanceof Twig_Function_Node ? $function->getClass() : 'Twig_Node_Expression_Function';
563 }
564
565 protected function getFilterNodeClass($name, $line)
566 {
567 $env = $this->parser->getEnvironment();
568
569 if (false === $filter = $env->getFilter($name)) {
570 $message = sprintf('The filter "%s" does not exist', $name);
571 if ($alternatives = $env->computeAlternatives($name, array_keys($env->getFilters()))) {
572 $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
573 }
574
575 throw new Twig_Error_Syntax($message, $line, $this->parser->getFilename());
576 }
577
578 if ($filter instanceof Twig_SimpleFilter) {
579 return $filter->getNodeClass();
580 }
581
582 return $filter instanceof Twig_Filter_Node ? $filter->getClass() : 'Twig_Node_Expression_Filter';
583 }
584
585 // checks that the node only contains "constant" elements
586 protected function checkConstantExpression(Twig_NodeInterface $node)
587 {
588 if (!($node instanceof Twig_Node_Expression_Constant || $node instanceof Twig_Node_Expression_Array)) {
589 return false;
590 }
591
592 foreach ($node as $n) {
593 if (!$this->checkConstantExpression($n)) {
594 return false;
595 }
596 }
597
598 return true;
599 }
600}
diff --git a/vendor/twig/twig/lib/Twig/Extension.php b/vendor/twig/twig/lib/Twig/Extension.php
new file mode 100644
index 00000000..931fc033
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension.php
@@ -0,0 +1,93 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11abstract class Twig_Extension implements Twig_ExtensionInterface
12{
13 /**
14 * Initializes the runtime environment.
15 *
16 * This is where you can load some file that contains filter functions for instance.
17 *
18 * @param Twig_Environment $environment The current Twig_Environment instance
19 */
20 public function initRuntime(Twig_Environment $environment)
21 {
22 }
23
24 /**
25 * Returns the token parser instances to add to the existing list.
26 *
27 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
28 */
29 public function getTokenParsers()
30 {
31 return array();
32 }
33
34 /**
35 * Returns the node visitor instances to add to the existing list.
36 *
37 * @return array An array of Twig_NodeVisitorInterface instances
38 */
39 public function getNodeVisitors()
40 {
41 return array();
42 }
43
44 /**
45 * Returns a list of filters to add to the existing list.
46 *
47 * @return array An array of filters
48 */
49 public function getFilters()
50 {
51 return array();
52 }
53
54 /**
55 * Returns a list of tests to add to the existing list.
56 *
57 * @return array An array of tests
58 */
59 public function getTests()
60 {
61 return array();
62 }
63
64 /**
65 * Returns a list of functions to add to the existing list.
66 *
67 * @return array An array of functions
68 */
69 public function getFunctions()
70 {
71 return array();
72 }
73
74 /**
75 * Returns a list of operators to add to the existing list.
76 *
77 * @return array An array of operators
78 */
79 public function getOperators()
80 {
81 return array();
82 }
83
84 /**
85 * Returns a list of global variables to add to the existing list.
86 *
87 * @return array An array of global variables
88 */
89 public function getGlobals()
90 {
91 return array();
92 }
93}
diff --git a/vendor/twig/twig/lib/Twig/Extension/Core.php b/vendor/twig/twig/lib/Twig/Extension/Core.php
new file mode 100644
index 00000000..e68687b4
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension/Core.php
@@ -0,0 +1,1355 @@
1<?php
2
3if (!defined('ENT_SUBSTITUTE')) {
4 define('ENT_SUBSTITUTE', 8);
5}
6
7/*
8 * This file is part of Twig.
9 *
10 * (c) 2009 Fabien Potencier
11 *
12 * For the full copyright and license information, please view the LICENSE
13 * file that was distributed with this source code.
14 */
15class Twig_Extension_Core extends Twig_Extension
16{
17 protected $dateFormats = array('F j, Y H:i', '%d days');
18 protected $numberFormat = array(0, '.', ',');
19 protected $timezone = null;
20
21 /**
22 * Sets the default format to be used by the date filter.
23 *
24 * @param string $format The default date format string
25 * @param string $dateIntervalFormat The default date interval format string
26 */
27 public function setDateFormat($format = null, $dateIntervalFormat = null)
28 {
29 if (null !== $format) {
30 $this->dateFormats[0] = $format;
31 }
32
33 if (null !== $dateIntervalFormat) {
34 $this->dateFormats[1] = $dateIntervalFormat;
35 }
36 }
37
38 /**
39 * Gets the default format to be used by the date filter.
40 *
41 * @return array The default date format string and the default date interval format string
42 */
43 public function getDateFormat()
44 {
45 return $this->dateFormats;
46 }
47
48 /**
49 * Sets the default timezone to be used by the date filter.
50 *
51 * @param DateTimeZone|string $timezone The default timezone string or a DateTimeZone object
52 */
53 public function setTimezone($timezone)
54 {
55 $this->timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone);
56 }
57
58 /**
59 * Gets the default timezone to be used by the date filter.
60 *
61 * @return DateTimeZone The default timezone currently in use
62 */
63 public function getTimezone()
64 {
65 if (null === $this->timezone) {
66 $this->timezone = new DateTimeZone(date_default_timezone_get());
67 }
68
69 return $this->timezone;
70 }
71
72 /**
73 * Sets the default format to be used by the number_format filter.
74 *
75 * @param integer $decimal The number of decimal places to use.
76 * @param string $decimalPoint The character(s) to use for the decimal point.
77 * @param string $thousandSep The character(s) to use for the thousands separator.
78 */
79 public function setNumberFormat($decimal, $decimalPoint, $thousandSep)
80 {
81 $this->numberFormat = array($decimal, $decimalPoint, $thousandSep);
82 }
83
84 /**
85 * Get the default format used by the number_format filter.
86 *
87 * @return array The arguments for number_format()
88 */
89 public function getNumberFormat()
90 {
91 return $this->numberFormat;
92 }
93
94 /**
95 * Returns the token parser instance to add to the existing list.
96 *
97 * @return array An array of Twig_TokenParser instances
98 */
99 public function getTokenParsers()
100 {
101 return array(
102 new Twig_TokenParser_For(),
103 new Twig_TokenParser_If(),
104 new Twig_TokenParser_Extends(),
105 new Twig_TokenParser_Include(),
106 new Twig_TokenParser_Block(),
107 new Twig_TokenParser_Use(),
108 new Twig_TokenParser_Filter(),
109 new Twig_TokenParser_Macro(),
110 new Twig_TokenParser_Import(),
111 new Twig_TokenParser_From(),
112 new Twig_TokenParser_Set(),
113 new Twig_TokenParser_Spaceless(),
114 new Twig_TokenParser_Flush(),
115 new Twig_TokenParser_Do(),
116 new Twig_TokenParser_Embed(),
117 );
118 }
119
120 /**
121 * Returns a list of filters to add to the existing list.
122 *
123 * @return array An array of filters
124 */
125 public function getFilters()
126 {
127 $filters = array(
128 // formatting filters
129 new Twig_SimpleFilter('date', 'twig_date_format_filter', array('needs_environment' => true)),
130 new Twig_SimpleFilter('date_modify', 'twig_date_modify_filter', array('needs_environment' => true)),
131 new Twig_SimpleFilter('format', 'sprintf'),
132 new Twig_SimpleFilter('replace', 'strtr'),
133 new Twig_SimpleFilter('number_format', 'twig_number_format_filter', array('needs_environment' => true)),
134 new Twig_SimpleFilter('abs', 'abs'),
135
136 // encoding
137 new Twig_SimpleFilter('url_encode', 'twig_urlencode_filter'),
138 new Twig_SimpleFilter('json_encode', 'twig_jsonencode_filter'),
139 new Twig_SimpleFilter('convert_encoding', 'twig_convert_encoding'),
140
141 // string filters
142 new Twig_SimpleFilter('title', 'twig_title_string_filter', array('needs_environment' => true)),
143 new Twig_SimpleFilter('capitalize', 'twig_capitalize_string_filter', array('needs_environment' => true)),
144 new Twig_SimpleFilter('upper', 'strtoupper'),
145 new Twig_SimpleFilter('lower', 'strtolower'),
146 new Twig_SimpleFilter('striptags', 'strip_tags'),
147 new Twig_SimpleFilter('trim', 'trim'),
148 new Twig_SimpleFilter('nl2br', 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
149
150 // array helpers
151 new Twig_SimpleFilter('join', 'twig_join_filter'),
152 new Twig_SimpleFilter('split', 'twig_split_filter'),
153 new Twig_SimpleFilter('sort', 'twig_sort_filter'),
154 new Twig_SimpleFilter('merge', 'twig_array_merge'),
155 new Twig_SimpleFilter('batch', 'twig_array_batch'),
156
157 // string/array filters
158 new Twig_SimpleFilter('reverse', 'twig_reverse_filter', array('needs_environment' => true)),
159 new Twig_SimpleFilter('length', 'twig_length_filter', array('needs_environment' => true)),
160 new Twig_SimpleFilter('slice', 'twig_slice', array('needs_environment' => true)),
161 new Twig_SimpleFilter('first', 'twig_first', array('needs_environment' => true)),
162 new Twig_SimpleFilter('last', 'twig_last', array('needs_environment' => true)),
163
164 // iteration and runtime
165 new Twig_SimpleFilter('default', '_twig_default_filter', array('node_class' => 'Twig_Node_Expression_Filter_Default')),
166 new Twig_SimpleFilter('keys', 'twig_get_array_keys_filter'),
167
168 // escaping
169 new Twig_SimpleFilter('escape', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
170 new Twig_SimpleFilter('e', 'twig_escape_filter', array('needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe')),
171 );
172
173 if (function_exists('mb_get_info')) {
174 $filters[] = new Twig_SimpleFilter('upper', 'twig_upper_filter', array('needs_environment' => true));
175 $filters[] = new Twig_SimpleFilter('lower', 'twig_lower_filter', array('needs_environment' => true));
176 }
177
178 return $filters;
179 }
180
181 /**
182 * Returns a list of global functions to add to the existing list.
183 *
184 * @return array An array of global functions
185 */
186 public function getFunctions()
187 {
188 return array(
189 new Twig_SimpleFunction('range', 'range'),
190 new Twig_SimpleFunction('constant', 'twig_constant'),
191 new Twig_SimpleFunction('cycle', 'twig_cycle'),
192 new Twig_SimpleFunction('random', 'twig_random', array('needs_environment' => true)),
193 new Twig_SimpleFunction('date', 'twig_date_converter', array('needs_environment' => true)),
194 new Twig_SimpleFunction('include', 'twig_include', array('needs_environment' => true, 'needs_context' => true, 'is_safe' => array('all'))),
195 );
196 }
197
198 /**
199 * Returns a list of tests to add to the existing list.
200 *
201 * @return array An array of tests
202 */
203 public function getTests()
204 {
205 return array(
206 new Twig_SimpleTest('even', null, array('node_class' => 'Twig_Node_Expression_Test_Even')),
207 new Twig_SimpleTest('odd', null, array('node_class' => 'Twig_Node_Expression_Test_Odd')),
208 new Twig_SimpleTest('defined', null, array('node_class' => 'Twig_Node_Expression_Test_Defined')),
209 new Twig_SimpleTest('sameas', null, array('node_class' => 'Twig_Node_Expression_Test_Sameas')),
210 new Twig_SimpleTest('none', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
211 new Twig_SimpleTest('null', null, array('node_class' => 'Twig_Node_Expression_Test_Null')),
212 new Twig_SimpleTest('divisibleby', null, array('node_class' => 'Twig_Node_Expression_Test_Divisibleby')),
213 new Twig_SimpleTest('constant', null, array('node_class' => 'Twig_Node_Expression_Test_Constant')),
214 new Twig_SimpleTest('empty', 'twig_test_empty'),
215 new Twig_SimpleTest('iterable', 'twig_test_iterable'),
216 );
217 }
218
219 /**
220 * Returns a list of operators to add to the existing list.
221 *
222 * @return array An array of operators
223 */
224 public function getOperators()
225 {
226 return array(
227 array(
228 'not' => array('precedence' => 50, 'class' => 'Twig_Node_Expression_Unary_Not'),
229 '-' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Neg'),
230 '+' => array('precedence' => 500, 'class' => 'Twig_Node_Expression_Unary_Pos'),
231 ),
232 array(
233 'or' => array('precedence' => 10, 'class' => 'Twig_Node_Expression_Binary_Or', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
234 'and' => array('precedence' => 15, 'class' => 'Twig_Node_Expression_Binary_And', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
235 'b-or' => array('precedence' => 16, 'class' => 'Twig_Node_Expression_Binary_BitwiseOr', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
236 'b-xor' => array('precedence' => 17, 'class' => 'Twig_Node_Expression_Binary_BitwiseXor', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
237 'b-and' => array('precedence' => 18, 'class' => 'Twig_Node_Expression_Binary_BitwiseAnd', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
238 '==' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Equal', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
239 '!=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
240 '<' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Less', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
241 '>' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_Greater', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
242 '>=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_GreaterEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
243 '<=' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_LessEqual', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
244 'not in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_NotIn', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
245 'in' => array('precedence' => 20, 'class' => 'Twig_Node_Expression_Binary_In', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
246 '..' => array('precedence' => 25, 'class' => 'Twig_Node_Expression_Binary_Range', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
247 '+' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Add', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
248 '-' => array('precedence' => 30, 'class' => 'Twig_Node_Expression_Binary_Sub', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
249 '~' => array('precedence' => 40, 'class' => 'Twig_Node_Expression_Binary_Concat', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
250 '*' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mul', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
251 '/' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Div', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
252 '//' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_FloorDiv', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
253 '%' => array('precedence' => 60, 'class' => 'Twig_Node_Expression_Binary_Mod', 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
254 'is' => array('precedence' => 100, 'callable' => array($this, 'parseTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
255 'is not' => array('precedence' => 100, 'callable' => array($this, 'parseNotTestExpression'), 'associativity' => Twig_ExpressionParser::OPERATOR_LEFT),
256 '**' => array('precedence' => 200, 'class' => 'Twig_Node_Expression_Binary_Power', 'associativity' => Twig_ExpressionParser::OPERATOR_RIGHT),
257 ),
258 );
259 }
260
261 public function parseNotTestExpression(Twig_Parser $parser, $node)
262 {
263 return new Twig_Node_Expression_Unary_Not($this->parseTestExpression($parser, $node), $parser->getCurrentToken()->getLine());
264 }
265
266 public function parseTestExpression(Twig_Parser $parser, $node)
267 {
268 $stream = $parser->getStream();
269 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
270 $arguments = null;
271 if ($stream->test(Twig_Token::PUNCTUATION_TYPE, '(')) {
272 $arguments = $parser->getExpressionParser()->parseArguments(true);
273 }
274
275 $class = $this->getTestNodeClass($parser, $name, $node->getLine());
276
277 return new $class($node, $name, $arguments, $parser->getCurrentToken()->getLine());
278 }
279
280 protected function getTestNodeClass(Twig_Parser $parser, $name, $line)
281 {
282 $env = $parser->getEnvironment();
283 $testMap = $env->getTests();
284 if (!isset($testMap[$name])) {
285 $message = sprintf('The test "%s" does not exist', $name);
286 if ($alternatives = $env->computeAlternatives($name, array_keys($env->getTests()))) {
287 $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
288 }
289
290 throw new Twig_Error_Syntax($message, $line, $parser->getFilename());
291 }
292
293 if ($testMap[$name] instanceof Twig_SimpleTest) {
294 return $testMap[$name]->getNodeClass();
295 }
296
297 return $testMap[$name] instanceof Twig_Test_Node ? $testMap[$name]->getClass() : 'Twig_Node_Expression_Test';
298 }
299
300 /**
301 * Returns the name of the extension.
302 *
303 * @return string The extension name
304 */
305 public function getName()
306 {
307 return 'core';
308 }
309}
310
311/**
312 * Cycles over a value.
313 *
314 * @param ArrayAccess|array $values An array or an ArrayAccess instance
315 * @param integer $position The cycle position
316 *
317 * @return string The next value in the cycle
318 */
319function twig_cycle($values, $position)
320{
321 if (!is_array($values) && !$values instanceof ArrayAccess) {
322 return $values;
323 }
324
325 return $values[$position % count($values)];
326}
327
328/**
329 * Returns a random value depending on the supplied parameter type:
330 * - a random item from a Traversable or array
331 * - a random character from a string
332 * - a random integer between 0 and the integer parameter
333 *
334 * @param Twig_Environment $env A Twig_Environment instance
335 * @param Traversable|array|integer|string $values The values to pick a random item from
336 *
337 * @throws Twig_Error_Runtime When $values is an empty array (does not apply to an empty string which is returned as is).
338 *
339 * @return mixed A random value from the given sequence
340 */
341function twig_random(Twig_Environment $env, $values = null)
342{
343 if (null === $values) {
344 return mt_rand();
345 }
346
347 if (is_int($values) || is_float($values)) {
348 return $values < 0 ? mt_rand($values, 0) : mt_rand(0, $values);
349 }
350
351 if ($values instanceof Traversable) {
352 $values = iterator_to_array($values);
353 } elseif (is_string($values)) {
354 if ('' === $values) {
355 return '';
356 }
357 if (null !== $charset = $env->getCharset()) {
358 if ('UTF-8' != $charset) {
359 $values = twig_convert_encoding($values, 'UTF-8', $charset);
360 }
361
362 // unicode version of str_split()
363 // split at all positions, but not after the start and not before the end
364 $values = preg_split('/(?<!^)(?!$)/u', $values);
365
366 if ('UTF-8' != $charset) {
367 foreach ($values as $i => $value) {
368 $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8');
369 }
370 }
371 } else {
372 return $values[mt_rand(0, strlen($values) - 1)];
373 }
374 }
375
376 if (!is_array($values)) {
377 return $values;
378 }
379
380 if (0 === count($values)) {
381 throw new Twig_Error_Runtime('The random function cannot pick from an empty array.');
382 }
383
384 return $values[array_rand($values, 1)];
385}
386
387/**
388 * Converts a date to the given format.
389 *
390 * <pre>
391 * {{ post.published_at|date("m/d/Y") }}
392 * </pre>
393 *
394 * @param Twig_Environment $env A Twig_Environment instance
395 * @param DateTime|DateInterval|string $date A date
396 * @param string $format A format
397 * @param DateTimeZone|string $timezone A timezone
398 *
399 * @return string The formatted date
400 */
401function twig_date_format_filter(Twig_Environment $env, $date, $format = null, $timezone = null)
402{
403 if (null === $format) {
404 $formats = $env->getExtension('core')->getDateFormat();
405 $format = $date instanceof DateInterval ? $formats[1] : $formats[0];
406 }
407
408 if ($date instanceof DateInterval) {
409 return $date->format($format);
410 }
411
412 return twig_date_converter($env, $date, $timezone)->format($format);
413}
414
415/**
416 * Returns a new date object modified
417 *
418 * <pre>
419 * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }}
420 * </pre>
421 *
422 * @param Twig_Environment $env A Twig_Environment instance
423 * @param DateTime|string $date A date
424 * @param string $modifier A modifier string
425 *
426 * @return DateTime A new date object
427 */
428function twig_date_modify_filter(Twig_Environment $env, $date, $modifier)
429{
430 $date = twig_date_converter($env, $date, false);
431 $date->modify($modifier);
432
433 return $date;
434}
435
436/**
437 * Converts an input to a DateTime instance.
438 *
439 * <pre>
440 * {% if date(user.created_at) < date('+2days') %}
441 * {# do something #}
442 * {% endif %}
443 * </pre>
444 *
445 * @param Twig_Environment $env A Twig_Environment instance
446 * @param DateTime|string $date A date
447 * @param DateTimeZone|string $timezone A timezone
448 *
449 * @return DateTime A DateTime instance
450 */
451function twig_date_converter(Twig_Environment $env, $date = null, $timezone = null)
452{
453 // determine the timezone
454 if (!$timezone) {
455 $defaultTimezone = $env->getExtension('core')->getTimezone();
456 } elseif (!$timezone instanceof DateTimeZone) {
457 $defaultTimezone = new DateTimeZone($timezone);
458 } else {
459 $defaultTimezone = $timezone;
460 }
461
462 if ($date instanceof DateTime) {
463 $date = clone $date;
464 if (false !== $timezone) {
465 $date->setTimezone($defaultTimezone);
466 }
467
468 return $date;
469 }
470
471 $asString = (string) $date;
472 if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) {
473 $date = '@'.$date;
474 }
475
476 $date = new DateTime($date, $defaultTimezone);
477 if (false !== $timezone) {
478 $date->setTimezone($defaultTimezone);
479 }
480
481 return $date;
482}
483
484/**
485 * Number format filter.
486 *
487 * All of the formatting options can be left null, in that case the defaults will
488 * be used. Supplying any of the parameters will override the defaults set in the
489 * environment object.
490 *
491 * @param Twig_Environment $env A Twig_Environment instance
492 * @param mixed $number A float/int/string of the number to format
493 * @param integer $decimal The number of decimal points to display.
494 * @param string $decimalPoint The character(s) to use for the decimal point.
495 * @param string $thousandSep The character(s) to use for the thousands separator.
496 *
497 * @return string The formatted number
498 */
499function twig_number_format_filter(Twig_Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null)
500{
501 $defaults = $env->getExtension('core')->getNumberFormat();
502 if (null === $decimal) {
503 $decimal = $defaults[0];
504 }
505
506 if (null === $decimalPoint) {
507 $decimalPoint = $defaults[1];
508 }
509
510 if (null === $thousandSep) {
511 $thousandSep = $defaults[2];
512 }
513
514 return number_format((float) $number, $decimal, $decimalPoint, $thousandSep);
515}
516
517/**
518 * URL encodes a string as a path segment or an array as a query string.
519 *
520 * @param string|array $url A URL or an array of query parameters
521 * @param bool $raw true to use rawurlencode() instead of urlencode
522 *
523 * @return string The URL encoded value
524 */
525function twig_urlencode_filter($url, $raw = false)
526{
527 if (is_array($url)) {
528 return http_build_query($url, '', '&');
529 }
530
531 if ($raw) {
532 return rawurlencode($url);
533 }
534
535 return urlencode($url);
536}
537
538if (version_compare(PHP_VERSION, '5.3.0', '<')) {
539 /**
540 * JSON encodes a variable.
541 *
542 * @param mixed $value The value to encode.
543 * @param integer $options Not used on PHP 5.2.x
544 *
545 * @return mixed The JSON encoded value
546 */
547 function twig_jsonencode_filter($value, $options = 0)
548 {
549 if ($value instanceof Twig_Markup) {
550 $value = (string) $value;
551 } elseif (is_array($value)) {
552 array_walk_recursive($value, '_twig_markup2string');
553 }
554
555 return json_encode($value);
556 }
557} else {
558 /**
559 * JSON encodes a variable.
560 *
561 * @param mixed $value The value to encode.
562 * @param integer $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
563 *
564 * @return mixed The JSON encoded value
565 */
566 function twig_jsonencode_filter($value, $options = 0)
567 {
568 if ($value instanceof Twig_Markup) {
569 $value = (string) $value;
570 } elseif (is_array($value)) {
571 array_walk_recursive($value, '_twig_markup2string');
572 }
573
574 return json_encode($value, $options);
575 }
576}
577
578function _twig_markup2string(&$value)
579{
580 if ($value instanceof Twig_Markup) {
581 $value = (string) $value;
582 }
583}
584
585/**
586 * Merges an array with another one.
587 *
588 * <pre>
589 * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %}
590 *
591 * {% set items = items|merge({ 'peugeot': 'car' }) %}
592 *
593 * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #}
594 * </pre>
595 *
596 * @param array $arr1 An array
597 * @param array $arr2 An array
598 *
599 * @return array The merged array
600 */
601function twig_array_merge($arr1, $arr2)
602{
603 if (!is_array($arr1) || !is_array($arr2)) {
604 throw new Twig_Error_Runtime('The merge filter only works with arrays or hashes.');
605 }
606
607 return array_merge($arr1, $arr2);
608}
609
610/**
611 * Slices a variable.
612 *
613 * @param Twig_Environment $env A Twig_Environment instance
614 * @param mixed $item A variable
615 * @param integer $start Start of the slice
616 * @param integer $length Size of the slice
617 * @param Boolean $preserveKeys Whether to preserve key or not (when the input is an array)
618 *
619 * @return mixed The sliced variable
620 */
621function twig_slice(Twig_Environment $env, $item, $start, $length = null, $preserveKeys = false)
622{
623 if ($item instanceof Traversable) {
624 $item = iterator_to_array($item, false);
625 }
626
627 if (is_array($item)) {
628 return array_slice($item, $start, $length, $preserveKeys);
629 }
630
631 $item = (string) $item;
632
633 if (function_exists('mb_get_info') && null !== $charset = $env->getCharset()) {
634 return mb_substr($item, $start, null === $length ? mb_strlen($item, $charset) - $start : $length, $charset);
635 }
636
637 return null === $length ? substr($item, $start) : substr($item, $start, $length);
638}
639
640/**
641 * Returns the first element of the item.
642 *
643 * @param Twig_Environment $env A Twig_Environment instance
644 * @param mixed $item A variable
645 *
646 * @return mixed The first element of the item
647 */
648function twig_first(Twig_Environment $env, $item)
649{
650 $elements = twig_slice($env, $item, 0, 1, false);
651
652 return is_string($elements) ? $elements[0] : current($elements);
653}
654
655/**
656 * Returns the last element of the item.
657 *
658 * @param Twig_Environment $env A Twig_Environment instance
659 * @param mixed $item A variable
660 *
661 * @return mixed The last element of the item
662 */
663function twig_last(Twig_Environment $env, $item)
664{
665 $elements = twig_slice($env, $item, -1, 1, false);
666
667 return is_string($elements) ? $elements[0] : current($elements);
668}
669
670/**
671 * Joins the values to a string.
672 *
673 * The separator between elements is an empty string per default, you can define it with the optional parameter.
674 *
675 * <pre>
676 * {{ [1, 2, 3]|join('|') }}
677 * {# returns 1|2|3 #}
678 *
679 * {{ [1, 2, 3]|join }}
680 * {# returns 123 #}
681 * </pre>
682 *
683 * @param array $value An array
684 * @param string $glue The separator
685 *
686 * @return string The concatenated string
687 */
688function twig_join_filter($value, $glue = '')
689{
690 if ($value instanceof Traversable) {
691 $value = iterator_to_array($value, false);
692 }
693
694 return implode($glue, (array) $value);
695}
696
697/**
698 * Splits the string into an array.
699 *
700 * <pre>
701 * {{ "one,two,three"|split(',') }}
702 * {# returns [one, two, three] #}
703 *
704 * {{ "one,two,three,four,five"|split(',', 3) }}
705 * {# returns [one, two, "three,four,five"] #}
706 *
707 * {{ "123"|split('') }}
708 * {# returns [1, 2, 3] #}
709 *
710 * {{ "aabbcc"|split('', 2) }}
711 * {# returns [aa, bb, cc] #}
712 * </pre>
713 *
714 * @param string $value A string
715 * @param string $delimiter The delimiter
716 * @param integer $limit The limit
717 *
718 * @return array The split string as an array
719 */
720function twig_split_filter($value, $delimiter, $limit = null)
721{
722 if (empty($delimiter)) {
723 return str_split($value, null === $limit ? 1 : $limit);
724 }
725
726 return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit);
727}
728
729// The '_default' filter is used internally to avoid using the ternary operator
730// which costs a lot for big contexts (before PHP 5.4). So, on average,
731// a function call is cheaper.
732function _twig_default_filter($value, $default = '')
733{
734 if (twig_test_empty($value)) {
735 return $default;
736 }
737
738 return $value;
739}
740
741/**
742 * Returns the keys for the given array.
743 *
744 * It is useful when you want to iterate over the keys of an array:
745 *
746 * <pre>
747 * {% for key in array|keys %}
748 * {# ... #}
749 * {% endfor %}
750 * </pre>
751 *
752 * @param array $array An array
753 *
754 * @return array The keys
755 */
756function twig_get_array_keys_filter($array)
757{
758 if (is_object($array) && $array instanceof Traversable) {
759 return array_keys(iterator_to_array($array));
760 }
761
762 if (!is_array($array)) {
763 return array();
764 }
765
766 return array_keys($array);
767}
768
769/**
770 * Reverses a variable.
771 *
772 * @param Twig_Environment $env A Twig_Environment instance
773 * @param array|Traversable|string $item An array, a Traversable instance, or a string
774 * @param Boolean $preserveKeys Whether to preserve key or not
775 *
776 * @return mixed The reversed input
777 */
778function twig_reverse_filter(Twig_Environment $env, $item, $preserveKeys = false)
779{
780 if (is_object($item) && $item instanceof Traversable) {
781 return array_reverse(iterator_to_array($item), $preserveKeys);
782 }
783
784 if (is_array($item)) {
785 return array_reverse($item, $preserveKeys);
786 }
787
788 if (null !== $charset = $env->getCharset()) {
789 $string = (string) $item;
790
791 if ('UTF-8' != $charset) {
792 $item = twig_convert_encoding($string, 'UTF-8', $charset);
793 }
794
795 preg_match_all('/./us', $item, $matches);
796
797 $string = implode('', array_reverse($matches[0]));
798
799 if ('UTF-8' != $charset) {
800 $string = twig_convert_encoding($string, $charset, 'UTF-8');
801 }
802
803 return $string;
804 }
805
806 return strrev((string) $item);
807}
808
809/**
810 * Sorts an array.
811 *
812 * @param array $array An array
813 */
814function twig_sort_filter($array)
815{
816 asort($array);
817
818 return $array;
819}
820
821/* used internally */
822function twig_in_filter($value, $compare)
823{
824 if (is_array($compare)) {
825 return in_array($value, $compare, is_object($value));
826 } elseif (is_string($compare)) {
827 if (!strlen($value)) {
828 return empty($compare);
829 }
830
831 return false !== strpos($compare, (string) $value);
832 } elseif ($compare instanceof Traversable) {
833 return in_array($value, iterator_to_array($compare, false), is_object($value));
834 }
835
836 return false;
837}
838
839/**
840 * Escapes a string.
841 *
842 * @param Twig_Environment $env A Twig_Environment instance
843 * @param string $string The value to be escaped
844 * @param string $strategy The escaping strategy
845 * @param string $charset The charset
846 * @param Boolean $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false)
847 */
848function twig_escape_filter(Twig_Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false)
849{
850 if ($autoescape && $string instanceof Twig_Markup) {
851 return $string;
852 }
853
854 if (!is_string($string)) {
855 if (is_object($string) && method_exists($string, '__toString')) {
856 $string = (string) $string;
857 } else {
858 return $string;
859 }
860 }
861
862 if (null === $charset) {
863 $charset = $env->getCharset();
864 }
865
866 switch ($strategy) {
867 case 'html':
868 // see http://php.net/htmlspecialchars
869
870 // Using a static variable to avoid initializing the array
871 // each time the function is called. Moving the declaration on the
872 // top of the function slow downs other escaping strategies.
873 static $htmlspecialcharsCharsets = array(
874 'ISO-8859-1' => true, 'ISO8859-1' => true,
875 'ISO-8859-15' => true, 'ISO8859-15' => true,
876 'utf-8' => true, 'UTF-8' => true,
877 'CP866' => true, 'IBM866' => true, '866' => true,
878 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true,
879 '1251' => true,
880 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true,
881 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true,
882 'BIG5' => true, '950' => true,
883 'GB2312' => true, '936' => true,
884 'BIG5-HKSCS' => true,
885 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true,
886 'EUC-JP' => true, 'EUCJP' => true,
887 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true,
888 );
889
890 if (isset($htmlspecialcharsCharsets[$charset])) {
891 return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
892 }
893
894 if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) {
895 // cache the lowercase variant for future iterations
896 $htmlspecialcharsCharsets[$charset] = true;
897
898 return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset);
899 }
900
901 $string = twig_convert_encoding($string, 'UTF-8', $charset);
902 $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8');
903
904 return twig_convert_encoding($string, $charset, 'UTF-8');
905
906 case 'js':
907 // escape all non-alphanumeric characters
908 // into their \xHH or \uHHHH representations
909 if ('UTF-8' != $charset) {
910 $string = twig_convert_encoding($string, 'UTF-8', $charset);
911 }
912
913 if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
914 throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
915 }
916
917 $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', '_twig_escape_js_callback', $string);
918
919 if ('UTF-8' != $charset) {
920 $string = twig_convert_encoding($string, $charset, 'UTF-8');
921 }
922
923 return $string;
924
925 case 'css':
926 if ('UTF-8' != $charset) {
927 $string = twig_convert_encoding($string, 'UTF-8', $charset);
928 }
929
930 if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
931 throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
932 }
933
934 $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', '_twig_escape_css_callback', $string);
935
936 if ('UTF-8' != $charset) {
937 $string = twig_convert_encoding($string, $charset, 'UTF-8');
938 }
939
940 return $string;
941
942 case 'html_attr':
943 if ('UTF-8' != $charset) {
944 $string = twig_convert_encoding($string, 'UTF-8', $charset);
945 }
946
947 if (0 == strlen($string) ? false : (1 == preg_match('/^./su', $string) ? false : true)) {
948 throw new Twig_Error_Runtime('The string to escape is not a valid UTF-8 string.');
949 }
950
951 $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', '_twig_escape_html_attr_callback', $string);
952
953 if ('UTF-8' != $charset) {
954 $string = twig_convert_encoding($string, $charset, 'UTF-8');
955 }
956
957 return $string;
958
959 case 'url':
960 // hackish test to avoid version_compare that is much slower, this works unless PHP releases a 5.10.*
961 // at that point however PHP 5.2.* support can be removed
962 if (PHP_VERSION < '5.3.0') {
963 return str_replace('%7E', '~', rawurlencode($string));
964 }
965
966 return rawurlencode($string);
967
968 default:
969 throw new Twig_Error_Runtime(sprintf('Invalid escaping strategy "%s" (valid ones: html, js, url, css, and html_attr).', $strategy));
970 }
971}
972
973/* used internally */
974function twig_escape_filter_is_safe(Twig_Node $filterArgs)
975{
976 foreach ($filterArgs as $arg) {
977 if ($arg instanceof Twig_Node_Expression_Constant) {
978 return array($arg->getAttribute('value'));
979 }
980
981 return array();
982 }
983
984 return array('html');
985}
986
987if (function_exists('mb_convert_encoding')) {
988 function twig_convert_encoding($string, $to, $from)
989 {
990 return mb_convert_encoding($string, $to, $from);
991 }
992} elseif (function_exists('iconv')) {
993 function twig_convert_encoding($string, $to, $from)
994 {
995 return iconv($from, $to, $string);
996 }
997} else {
998 function twig_convert_encoding($string, $to, $from)
999 {
1000 throw new Twig_Error_Runtime('No suitable convert encoding function (use UTF-8 as your encoding or install the iconv or mbstring extension).');
1001 }
1002}
1003
1004function _twig_escape_js_callback($matches)
1005{
1006 $char = $matches[0];
1007
1008 // \xHH
1009 if (!isset($char[1])) {
1010 return '\\x'.strtoupper(substr('00'.bin2hex($char), -2));
1011 }
1012
1013 // \uHHHH
1014 $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
1015
1016 return '\\u'.strtoupper(substr('0000'.bin2hex($char), -4));
1017}
1018
1019function _twig_escape_css_callback($matches)
1020{
1021 $char = $matches[0];
1022
1023 // \xHH
1024 if (!isset($char[1])) {
1025 $hex = ltrim(strtoupper(bin2hex($char)), '0');
1026 if (0 === strlen($hex)) {
1027 $hex = '0';
1028 }
1029
1030 return '\\'.$hex.' ';
1031 }
1032
1033 // \uHHHH
1034 $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8');
1035
1036 return '\\'.ltrim(strtoupper(bin2hex($char)), '0').' ';
1037}
1038
1039/**
1040 * This function is adapted from code coming from Zend Framework.
1041 *
1042 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
1043 * @license http://framework.zend.com/license/new-bsd New BSD License
1044 */
1045function _twig_escape_html_attr_callback($matches)
1046{
1047 /*
1048 * While HTML supports far more named entities, the lowest common denominator
1049 * has become HTML5's XML Serialisation which is restricted to the those named
1050 * entities that XML supports. Using HTML entities would result in this error:
1051 * XML Parsing Error: undefined entity
1052 */
1053 static $entityMap = array(
1054 34 => 'quot', /* quotation mark */
1055 38 => 'amp', /* ampersand */
1056 60 => 'lt', /* less-than sign */
1057 62 => 'gt', /* greater-than sign */
1058 );
1059
1060 $chr = $matches[0];
1061 $ord = ord($chr);
1062
1063 /**
1064 * The following replaces characters undefined in HTML with the
1065 * hex entity for the Unicode replacement character.
1066 */
1067 if (($ord <= 0x1f && $chr != "\t" && $chr != "\n" && $chr != "\r") || ($ord >= 0x7f && $ord <= 0x9f)) {
1068 return '&#xFFFD;';
1069 }
1070
1071 /**
1072 * Check if the current character to escape has a name entity we should
1073 * replace it with while grabbing the hex value of the character.
1074 */
1075 if (strlen($chr) == 1) {
1076 $hex = strtoupper(substr('00'.bin2hex($chr), -2));
1077 } else {
1078 $chr = twig_convert_encoding($chr, 'UTF-16BE', 'UTF-8');
1079 $hex = strtoupper(substr('0000'.bin2hex($chr), -4));
1080 }
1081
1082 $int = hexdec($hex);
1083 if (array_key_exists($int, $entityMap)) {
1084 return sprintf('&%s;', $entityMap[$int]);
1085 }
1086
1087 /**
1088 * Per OWASP recommendations, we'll use hex entities for any other
1089 * characters where a named entity does not exist.
1090 */
1091
1092 return sprintf('&#x%s;', $hex);
1093}
1094
1095// add multibyte extensions if possible
1096if (function_exists('mb_get_info')) {
1097 /**
1098 * Returns the length of a variable.
1099 *
1100 * @param Twig_Environment $env A Twig_Environment instance
1101 * @param mixed $thing A variable
1102 *
1103 * @return integer The length of the value
1104 */
1105 function twig_length_filter(Twig_Environment $env, $thing)
1106 {
1107 return is_scalar($thing) ? mb_strlen($thing, $env->getCharset()) : count($thing);
1108 }
1109
1110 /**
1111 * Converts a string to uppercase.
1112 *
1113 * @param Twig_Environment $env A Twig_Environment instance
1114 * @param string $string A string
1115 *
1116 * @return string The uppercased string
1117 */
1118 function twig_upper_filter(Twig_Environment $env, $string)
1119 {
1120 if (null !== ($charset = $env->getCharset())) {
1121 return mb_strtoupper($string, $charset);
1122 }
1123
1124 return strtoupper($string);
1125 }
1126
1127 /**
1128 * Converts a string to lowercase.
1129 *
1130 * @param Twig_Environment $env A Twig_Environment instance
1131 * @param string $string A string
1132 *
1133 * @return string The lowercased string
1134 */
1135 function twig_lower_filter(Twig_Environment $env, $string)
1136 {
1137 if (null !== ($charset = $env->getCharset())) {
1138 return mb_strtolower($string, $charset);
1139 }
1140
1141 return strtolower($string);
1142 }
1143
1144 /**
1145 * Returns a titlecased string.
1146 *
1147 * @param Twig_Environment $env A Twig_Environment instance
1148 * @param string $string A string
1149 *
1150 * @return string The titlecased string
1151 */
1152 function twig_title_string_filter(Twig_Environment $env, $string)
1153 {
1154 if (null !== ($charset = $env->getCharset())) {
1155 return mb_convert_case($string, MB_CASE_TITLE, $charset);
1156 }
1157
1158 return ucwords(strtolower($string));
1159 }
1160
1161 /**
1162 * Returns a capitalized string.
1163 *
1164 * @param Twig_Environment $env A Twig_Environment instance
1165 * @param string $string A string
1166 *
1167 * @return string The capitalized string
1168 */
1169 function twig_capitalize_string_filter(Twig_Environment $env, $string)
1170 {
1171 if (null !== ($charset = $env->getCharset())) {
1172 return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).
1173 mb_strtolower(mb_substr($string, 1, mb_strlen($string, $charset), $charset), $charset);
1174 }
1175
1176 return ucfirst(strtolower($string));
1177 }
1178}
1179// and byte fallback
1180else {
1181 /**
1182 * Returns the length of a variable.
1183 *
1184 * @param Twig_Environment $env A Twig_Environment instance
1185 * @param mixed $thing A variable
1186 *
1187 * @return integer The length of the value
1188 */
1189 function twig_length_filter(Twig_Environment $env, $thing)
1190 {
1191 return is_scalar($thing) ? strlen($thing) : count($thing);
1192 }
1193
1194 /**
1195 * Returns a titlecased string.
1196 *
1197 * @param Twig_Environment $env A Twig_Environment instance
1198 * @param string $string A string
1199 *
1200 * @return string The titlecased string
1201 */
1202 function twig_title_string_filter(Twig_Environment $env, $string)
1203 {
1204 return ucwords(strtolower($string));
1205 }
1206
1207 /**
1208 * Returns a capitalized string.
1209 *
1210 * @param Twig_Environment $env A Twig_Environment instance
1211 * @param string $string A string
1212 *
1213 * @return string The capitalized string
1214 */
1215 function twig_capitalize_string_filter(Twig_Environment $env, $string)
1216 {
1217 return ucfirst(strtolower($string));
1218 }
1219}
1220
1221/* used internally */
1222function twig_ensure_traversable($seq)
1223{
1224 if ($seq instanceof Traversable || is_array($seq)) {
1225 return $seq;
1226 }
1227
1228 return array();
1229}
1230
1231/**
1232 * Checks if a variable is empty.
1233 *
1234 * <pre>
1235 * {# evaluates to true if the foo variable is null, false, or the empty string #}
1236 * {% if foo is empty %}
1237 * {# ... #}
1238 * {% endif %}
1239 * </pre>
1240 *
1241 * @param mixed $value A variable
1242 *
1243 * @return Boolean true if the value is empty, false otherwise
1244 */
1245function twig_test_empty($value)
1246{
1247 if ($value instanceof Countable) {
1248 return 0 == count($value);
1249 }
1250
1251 return '' === $value || false === $value || null === $value || array() === $value;
1252}
1253
1254/**
1255 * Checks if a variable is traversable.
1256 *
1257 * <pre>
1258 * {# evaluates to true if the foo variable is an array or a traversable object #}
1259 * {% if foo is traversable %}
1260 * {# ... #}
1261 * {% endif %}
1262 * </pre>
1263 *
1264 * @param mixed $value A variable
1265 *
1266 * @return Boolean true if the value is traversable
1267 */
1268function twig_test_iterable($value)
1269{
1270 return $value instanceof Traversable || is_array($value);
1271}
1272
1273/**
1274 * Renders a template.
1275 *
1276 * @param string $template The template to render
1277 * @param array $variables The variables to pass to the template
1278 * @param Boolean $with_context Whether to pass the current context variables or not
1279 * @param Boolean $ignore_missing Whether to ignore missing templates or not
1280 * @param Boolean $sandboxed Whether to sandbox the template or not
1281 *
1282 * @return string The rendered template
1283 */
1284function twig_include(Twig_Environment $env, $context, $template, $variables = array(), $withContext = true, $ignoreMissing = false, $sandboxed = false)
1285{
1286 if ($withContext) {
1287 $variables = array_merge($context, $variables);
1288 }
1289
1290 if ($isSandboxed = $sandboxed && $env->hasExtension('sandbox')) {
1291 $sandbox = $env->getExtension('sandbox');
1292 if (!$alreadySandboxed = $sandbox->isSandboxed()) {
1293 $sandbox->enableSandbox();
1294 }
1295 }
1296
1297 try {
1298 return $env->resolveTemplate($template)->render($variables);
1299 } catch (Twig_Error_Loader $e) {
1300 if (!$ignoreMissing) {
1301 throw $e;
1302 }
1303 }
1304
1305 if ($isSandboxed && !$alreadySandboxed) {
1306 $sandbox->disableSandbox();
1307 }
1308}
1309
1310/**
1311 * Provides the ability to get constants from instances as well as class/global constants.
1312 *
1313 * @param string $constant The name of the constant
1314 * @param null|object $object The object to get the constant from
1315 *
1316 * @return string
1317 */
1318function twig_constant($constant, $object = null)
1319{
1320 if (null !== $object) {
1321 $constant = get_class($object).'::'.$constant;
1322 }
1323
1324 return constant($constant);
1325}
1326
1327/**
1328 * Batches item.
1329 *
1330 * @param array $items An array of items
1331 * @param integer $size The size of the batch
1332 * @param string $fill A string to fill missing items
1333 *
1334 * @return array
1335 */
1336function twig_array_batch($items, $size, $fill = null)
1337{
1338 if ($items instanceof Traversable) {
1339 $items = iterator_to_array($items, false);
1340 }
1341
1342 $size = ceil($size);
1343
1344 $result = array_chunk($items, $size, true);
1345
1346 if (null !== $fill) {
1347 $last = count($result) - 1;
1348 $result[$last] = array_merge(
1349 $result[$last],
1350 array_fill(0, $size - count($result[$last]), $fill)
1351 );
1352 }
1353
1354 return $result;
1355}
diff --git a/vendor/twig/twig/lib/Twig/Extension/Debug.php b/vendor/twig/twig/lib/Twig/Extension/Debug.php
new file mode 100644
index 00000000..e3a85bfe
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension/Debug.php
@@ -0,0 +1,71 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extension_Debug extends Twig_Extension
12{
13 /**
14 * Returns a list of global functions to add to the existing list.
15 *
16 * @return array An array of global functions
17 */
18 public function getFunctions()
19 {
20 // dump is safe if var_dump is overridden by xdebug
21 $isDumpOutputHtmlSafe = extension_loaded('xdebug')
22 // false means that it was not set (and the default is on) or it explicitly enabled
23 && (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump'))
24 // false means that it was not set (and the default is on) or it explicitly enabled
25 // xdebug.overload_var_dump produces HTML only when html_errors is also enabled
26 && (false === ini_get('html_errors') || ini_get('html_errors'))
27 || 'cli' === php_sapi_name()
28 ;
29
30 return array(
31 new Twig_SimpleFunction('dump', 'twig_var_dump', array('is_safe' => $isDumpOutputHtmlSafe ? array('html') : array(), 'needs_context' => true, 'needs_environment' => true)),
32 );
33 }
34
35 /**
36 * Returns the name of the extension.
37 *
38 * @return string The extension name
39 */
40 public function getName()
41 {
42 return 'debug';
43 }
44}
45
46function twig_var_dump(Twig_Environment $env, $context)
47{
48 if (!$env->isDebug()) {
49 return;
50 }
51
52 ob_start();
53
54 $count = func_num_args();
55 if (2 === $count) {
56 $vars = array();
57 foreach ($context as $key => $value) {
58 if (!$value instanceof Twig_Template) {
59 $vars[$key] = $value;
60 }
61 }
62
63 var_dump($vars);
64 } else {
65 for ($i = 2; $i < $count; $i++) {
66 var_dump(func_get_arg($i));
67 }
68 }
69
70 return ob_get_clean();
71}
diff --git a/vendor/twig/twig/lib/Twig/Extension/Escaper.php b/vendor/twig/twig/lib/Twig/Extension/Escaper.php
new file mode 100644
index 00000000..c9a7f68e
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension/Escaper.php
@@ -0,0 +1,107 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extension_Escaper extends Twig_Extension
12{
13 protected $defaultStrategy;
14
15 public function __construct($defaultStrategy = 'html')
16 {
17 $this->setDefaultStrategy($defaultStrategy);
18 }
19
20 /**
21 * Returns the token parser instances to add to the existing list.
22 *
23 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
24 */
25 public function getTokenParsers()
26 {
27 return array(new Twig_TokenParser_AutoEscape());
28 }
29
30 /**
31 * Returns the node visitor instances to add to the existing list.
32 *
33 * @return array An array of Twig_NodeVisitorInterface instances
34 */
35 public function getNodeVisitors()
36 {
37 return array(new Twig_NodeVisitor_Escaper());
38 }
39
40 /**
41 * Returns a list of filters to add to the existing list.
42 *
43 * @return array An array of filters
44 */
45 public function getFilters()
46 {
47 return array(
48 new Twig_SimpleFilter('raw', 'twig_raw_filter', array('is_safe' => array('all'))),
49 );
50 }
51
52 /**
53 * Sets the default strategy to use when not defined by the user.
54 *
55 * The strategy can be a valid PHP callback that takes the template
56 * "filename" as an argument and returns the strategy to use.
57 *
58 * @param mixed $defaultStrategy An escaping strategy
59 */
60 public function setDefaultStrategy($defaultStrategy)
61 {
62 // for BC
63 if (true === $defaultStrategy) {
64 $defaultStrategy = 'html';
65 }
66
67 $this->defaultStrategy = $defaultStrategy;
68 }
69
70 /**
71 * Gets the default strategy to use when not defined by the user.
72 *
73 * @param string $filename The template "filename"
74 *
75 * @return string The default strategy to use for the template
76 */
77 public function getDefaultStrategy($filename)
78 {
79 // disable string callables to avoid calling a function named html or js,
80 // or any other upcoming escaping strategy
81 if (!is_string($this->defaultStrategy) && is_callable($this->defaultStrategy)) {
82 return call_user_func($this->defaultStrategy, $filename);
83 }
84
85 return $this->defaultStrategy;
86 }
87
88 /**
89 * Returns the name of the extension.
90 *
91 * @return string The extension name
92 */
93 public function getName()
94 {
95 return 'escaper';
96 }
97}
98
99/**
100 * Marks a variable as being safe.
101 *
102 * @param string $string A PHP variable
103 */
104function twig_raw_filter($string)
105{
106 return $string;
107}
diff --git a/vendor/twig/twig/lib/Twig/Extension/Optimizer.php b/vendor/twig/twig/lib/Twig/Extension/Optimizer.php
new file mode 100644
index 00000000..013fcb62
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension/Optimizer.php
@@ -0,0 +1,35 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extension_Optimizer extends Twig_Extension
12{
13 protected $optimizers;
14
15 public function __construct($optimizers = -1)
16 {
17 $this->optimizers = $optimizers;
18 }
19
20 /**
21 * {@inheritdoc}
22 */
23 public function getNodeVisitors()
24 {
25 return array(new Twig_NodeVisitor_Optimizer($this->optimizers));
26 }
27
28 /**
29 * {@inheritdoc}
30 */
31 public function getName()
32 {
33 return 'optimizer';
34 }
35}
diff --git a/vendor/twig/twig/lib/Twig/Extension/Sandbox.php b/vendor/twig/twig/lib/Twig/Extension/Sandbox.php
new file mode 100644
index 00000000..bf76c11a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension/Sandbox.php
@@ -0,0 +1,112 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extension_Sandbox extends Twig_Extension
12{
13 protected $sandboxedGlobally;
14 protected $sandboxed;
15 protected $policy;
16
17 public function __construct(Twig_Sandbox_SecurityPolicyInterface $policy, $sandboxed = false)
18 {
19 $this->policy = $policy;
20 $this->sandboxedGlobally = $sandboxed;
21 }
22
23 /**
24 * Returns the token parser instances to add to the existing list.
25 *
26 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
27 */
28 public function getTokenParsers()
29 {
30 return array(new Twig_TokenParser_Sandbox());
31 }
32
33 /**
34 * Returns the node visitor instances to add to the existing list.
35 *
36 * @return array An array of Twig_NodeVisitorInterface instances
37 */
38 public function getNodeVisitors()
39 {
40 return array(new Twig_NodeVisitor_Sandbox());
41 }
42
43 public function enableSandbox()
44 {
45 $this->sandboxed = true;
46 }
47
48 public function disableSandbox()
49 {
50 $this->sandboxed = false;
51 }
52
53 public function isSandboxed()
54 {
55 return $this->sandboxedGlobally || $this->sandboxed;
56 }
57
58 public function isSandboxedGlobally()
59 {
60 return $this->sandboxedGlobally;
61 }
62
63 public function setSecurityPolicy(Twig_Sandbox_SecurityPolicyInterface $policy)
64 {
65 $this->policy = $policy;
66 }
67
68 public function getSecurityPolicy()
69 {
70 return $this->policy;
71 }
72
73 public function checkSecurity($tags, $filters, $functions)
74 {
75 if ($this->isSandboxed()) {
76 $this->policy->checkSecurity($tags, $filters, $functions);
77 }
78 }
79
80 public function checkMethodAllowed($obj, $method)
81 {
82 if ($this->isSandboxed()) {
83 $this->policy->checkMethodAllowed($obj, $method);
84 }
85 }
86
87 public function checkPropertyAllowed($obj, $method)
88 {
89 if ($this->isSandboxed()) {
90 $this->policy->checkPropertyAllowed($obj, $method);
91 }
92 }
93
94 public function ensureToStringAllowed($obj)
95 {
96 if (is_object($obj)) {
97 $this->policy->checkMethodAllowed($obj, '__toString');
98 }
99
100 return $obj;
101 }
102
103 /**
104 * Returns the name of the extension.
105 *
106 * @return string The extension name
107 */
108 public function getName()
109 {
110 return 'sandbox';
111 }
112}
diff --git a/vendor/twig/twig/lib/Twig/Extension/Staging.php b/vendor/twig/twig/lib/Twig/Extension/Staging.php
new file mode 100644
index 00000000..8ab0f459
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension/Staging.php
@@ -0,0 +1,113 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Internal class.
14 *
15 * This class is used by Twig_Environment as a staging area and must not be used directly.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19class Twig_Extension_Staging extends Twig_Extension
20{
21 protected $functions = array();
22 protected $filters = array();
23 protected $visitors = array();
24 protected $tokenParsers = array();
25 protected $globals = array();
26 protected $tests = array();
27
28 public function addFunction($name, $function)
29 {
30 $this->functions[$name] = $function;
31 }
32
33 /**
34 * {@inheritdoc}
35 */
36 public function getFunctions()
37 {
38 return $this->functions;
39 }
40
41 public function addFilter($name, $filter)
42 {
43 $this->filters[$name] = $filter;
44 }
45
46 /**
47 * {@inheritdoc}
48 */
49 public function getFilters()
50 {
51 return $this->filters;
52 }
53
54 public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
55 {
56 $this->visitors[] = $visitor;
57 }
58
59 /**
60 * {@inheritdoc}
61 */
62 public function getNodeVisitors()
63 {
64 return $this->visitors;
65 }
66
67 public function addTokenParser(Twig_TokenParserInterface $parser)
68 {
69 $this->tokenParsers[] = $parser;
70 }
71
72 /**
73 * {@inheritdoc}
74 */
75 public function getTokenParsers()
76 {
77 return $this->tokenParsers;
78 }
79
80 public function addGlobal($name, $value)
81 {
82 $this->globals[$name] = $value;
83 }
84
85 /**
86 * {@inheritdoc}
87 */
88 public function getGlobals()
89 {
90 return $this->globals;
91 }
92
93 public function addTest($name, $test)
94 {
95 $this->tests[$name] = $test;
96 }
97
98 /**
99 * {@inheritdoc}
100 */
101 public function getTests()
102 {
103 return $this->tests;
104 }
105
106 /**
107 * {@inheritdoc}
108 */
109 public function getName()
110 {
111 return 'staging';
112 }
113}
diff --git a/vendor/twig/twig/lib/Twig/Extension/StringLoader.php b/vendor/twig/twig/lib/Twig/Extension/StringLoader.php
new file mode 100644
index 00000000..20f3f994
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Extension/StringLoader.php
@@ -0,0 +1,64 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Extension_StringLoader extends Twig_Extension
12{
13 /**
14 * {@inheritdoc}
15 */
16 public function getFunctions()
17 {
18 return array(
19 new Twig_SimpleFunction('template_from_string', 'twig_template_from_string', array('needs_environment' => true)),
20 );
21 }
22
23 /**
24 * {@inheritdoc}
25 */
26 public function getName()
27 {
28 return 'string_loader';
29 }
30}
31
32/**
33 * Loads a template from a string.
34 *
35 * <pre>
36 * {{ include(template_from_string("Hello {{ name }}")) }}
37 * </pre>
38 *
39 * @param Twig_Environment $env A Twig_Environment instance
40 * @param string $template A template as a string
41 *
42 * @return Twig_Template A Twig_Template instance
43 */
44function twig_template_from_string(Twig_Environment $env, $template)
45{
46 static $loader;
47
48 if (null === $loader) {
49 $loader = new Twig_Loader_String();
50 }
51
52 $current = $env->getLoader();
53 $env->setLoader($loader);
54 try {
55 $template = $env->loadTemplate($template);
56 } catch (Exception $e) {
57 $env->setLoader($current);
58
59 throw $e;
60 }
61 $env->setLoader($current);
62
63 return $template;
64}
diff --git a/vendor/twig/twig/lib/Twig/ExtensionInterface.php b/vendor/twig/twig/lib/Twig/ExtensionInterface.php
new file mode 100644
index 00000000..f189e9d9
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/ExtensionInterface.php
@@ -0,0 +1,83 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interface implemented by extension classes.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17interface Twig_ExtensionInterface
18{
19 /**
20 * Initializes the runtime environment.
21 *
22 * This is where you can load some file that contains filter functions for instance.
23 *
24 * @param Twig_Environment $environment The current Twig_Environment instance
25 */
26 public function initRuntime(Twig_Environment $environment);
27
28 /**
29 * Returns the token parser instances to add to the existing list.
30 *
31 * @return array An array of Twig_TokenParserInterface or Twig_TokenParserBrokerInterface instances
32 */
33 public function getTokenParsers();
34
35 /**
36 * Returns the node visitor instances to add to the existing list.
37 *
38 * @return array An array of Twig_NodeVisitorInterface instances
39 */
40 public function getNodeVisitors();
41
42 /**
43 * Returns a list of filters to add to the existing list.
44 *
45 * @return array An array of filters
46 */
47 public function getFilters();
48
49 /**
50 * Returns a list of tests to add to the existing list.
51 *
52 * @return array An array of tests
53 */
54 public function getTests();
55
56 /**
57 * Returns a list of functions to add to the existing list.
58 *
59 * @return array An array of functions
60 */
61 public function getFunctions();
62
63 /**
64 * Returns a list of operators to add to the existing list.
65 *
66 * @return array An array of operators
67 */
68 public function getOperators();
69
70 /**
71 * Returns a list of global variables to add to the existing list.
72 *
73 * @return array An array of global variables
74 */
75 public function getGlobals();
76
77 /**
78 * Returns the name of the extension.
79 *
80 * @return string The extension name
81 */
82 public function getName();
83}
diff --git a/vendor/twig/twig/lib/Twig/Filter.php b/vendor/twig/twig/lib/Twig/Filter.php
new file mode 100644
index 00000000..5cfbb662
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Filter.php
@@ -0,0 +1,81 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template filter.
14 *
15 * Use Twig_SimpleFilter instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20abstract class Twig_Filter implements Twig_FilterInterface, Twig_FilterCallableInterface
21{
22 protected $options;
23 protected $arguments = array();
24
25 public function __construct(array $options = array())
26 {
27 $this->options = array_merge(array(
28 'needs_environment' => false,
29 'needs_context' => false,
30 'pre_escape' => null,
31 'preserves_safety' => null,
32 'callable' => null,
33 ), $options);
34 }
35
36 public function setArguments($arguments)
37 {
38 $this->arguments = $arguments;
39 }
40
41 public function getArguments()
42 {
43 return $this->arguments;
44 }
45
46 public function needsEnvironment()
47 {
48 return $this->options['needs_environment'];
49 }
50
51 public function needsContext()
52 {
53 return $this->options['needs_context'];
54 }
55
56 public function getSafe(Twig_Node $filterArgs)
57 {
58 if (isset($this->options['is_safe'])) {
59 return $this->options['is_safe'];
60 }
61
62 if (isset($this->options['is_safe_callback'])) {
63 return call_user_func($this->options['is_safe_callback'], $filterArgs);
64 }
65 }
66
67 public function getPreservesSafety()
68 {
69 return $this->options['preserves_safety'];
70 }
71
72 public function getPreEscape()
73 {
74 return $this->options['pre_escape'];
75 }
76
77 public function getCallable()
78 {
79 return $this->options['callable'];
80 }
81}
diff --git a/vendor/twig/twig/lib/Twig/Filter/Function.php b/vendor/twig/twig/lib/Twig/Filter/Function.php
new file mode 100644
index 00000000..ad374a55
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Filter/Function.php
@@ -0,0 +1,37 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a function template filter.
14 *
15 * Use Twig_SimpleFilter instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20class Twig_Filter_Function extends Twig_Filter
21{
22 protected $function;
23
24 public function __construct($function, array $options = array())
25 {
26 $options['callable'] = $function;
27
28 parent::__construct($options);
29
30 $this->function = $function;
31 }
32
33 public function compile()
34 {
35 return $this->function;
36 }
37}
diff --git a/vendor/twig/twig/lib/Twig/Filter/Method.php b/vendor/twig/twig/lib/Twig/Filter/Method.php
new file mode 100644
index 00000000..63c8c3be
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Filter/Method.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a method template filter.
14 *
15 * Use Twig_SimpleFilter instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20class Twig_Filter_Method extends Twig_Filter
21{
22 protected $extension;
23 protected $method;
24
25 public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
26 {
27 $options['callable'] = array($extension, $method);
28
29 parent::__construct($options);
30
31 $this->extension = $extension;
32 $this->method = $method;
33 }
34
35 public function compile()
36 {
37 return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
38 }
39}
diff --git a/vendor/twig/twig/lib/Twig/Filter/Node.php b/vendor/twig/twig/lib/Twig/Filter/Node.php
new file mode 100644
index 00000000..8744c5e0
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Filter/Node.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template filter as a node.
14 *
15 * Use Twig_SimpleFilter instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20class Twig_Filter_Node extends Twig_Filter
21{
22 protected $class;
23
24 public function __construct($class, array $options = array())
25 {
26 parent::__construct($options);
27
28 $this->class = $class;
29 }
30
31 public function getClass()
32 {
33 return $this->class;
34 }
35
36 public function compile()
37 {
38 }
39}
diff --git a/vendor/twig/twig/lib/Twig/FilterCallableInterface.php b/vendor/twig/twig/lib/Twig/FilterCallableInterface.php
new file mode 100644
index 00000000..145534df
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/FilterCallableInterface.php
@@ -0,0 +1,23 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a callable template filter.
14 *
15 * Use Twig_SimpleFilter instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20interface Twig_FilterCallableInterface
21{
22 public function getCallable();
23}
diff --git a/vendor/twig/twig/lib/Twig/FilterInterface.php b/vendor/twig/twig/lib/Twig/FilterInterface.php
new file mode 100644
index 00000000..5319ecc9
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/FilterInterface.php
@@ -0,0 +1,42 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template filter.
14 *
15 * Use Twig_SimpleFilter instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20interface Twig_FilterInterface
21{
22 /**
23 * Compiles a filter.
24 *
25 * @return string The PHP code for the filter
26 */
27 public function compile();
28
29 public function needsEnvironment();
30
31 public function needsContext();
32
33 public function getSafe(Twig_Node $filterArgs);
34
35 public function getPreservesSafety();
36
37 public function getPreEscape();
38
39 public function setArguments($arguments);
40
41 public function getArguments();
42}
diff --git a/vendor/twig/twig/lib/Twig/Function.php b/vendor/twig/twig/lib/Twig/Function.php
new file mode 100644
index 00000000..b5ffb2b0
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Function.php
@@ -0,0 +1,71 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template function.
14 *
15 * Use Twig_SimpleFunction instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20abstract class Twig_Function implements Twig_FunctionInterface, Twig_FunctionCallableInterface
21{
22 protected $options;
23 protected $arguments = array();
24
25 public function __construct(array $options = array())
26 {
27 $this->options = array_merge(array(
28 'needs_environment' => false,
29 'needs_context' => false,
30 'callable' => null,
31 ), $options);
32 }
33
34 public function setArguments($arguments)
35 {
36 $this->arguments = $arguments;
37 }
38
39 public function getArguments()
40 {
41 return $this->arguments;
42 }
43
44 public function needsEnvironment()
45 {
46 return $this->options['needs_environment'];
47 }
48
49 public function needsContext()
50 {
51 return $this->options['needs_context'];
52 }
53
54 public function getSafe(Twig_Node $functionArgs)
55 {
56 if (isset($this->options['is_safe'])) {
57 return $this->options['is_safe'];
58 }
59
60 if (isset($this->options['is_safe_callback'])) {
61 return call_user_func($this->options['is_safe_callback'], $functionArgs);
62 }
63
64 return array();
65 }
66
67 public function getCallable()
68 {
69 return $this->options['callable'];
70 }
71}
diff --git a/vendor/twig/twig/lib/Twig/Function/Function.php b/vendor/twig/twig/lib/Twig/Function/Function.php
new file mode 100644
index 00000000..d1e1b96a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Function/Function.php
@@ -0,0 +1,38 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2010 Arnaud Le Blanc
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a function template function.
15 *
16 * Use Twig_SimpleFunction instead.
17 *
18 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
19 * @deprecated since 1.12 (to be removed in 2.0)
20 */
21class Twig_Function_Function extends Twig_Function
22{
23 protected $function;
24
25 public function __construct($function, array $options = array())
26 {
27 $options['callable'] = $function;
28
29 parent::__construct($options);
30
31 $this->function = $function;
32 }
33
34 public function compile()
35 {
36 return $this->function;
37 }
38}
diff --git a/vendor/twig/twig/lib/Twig/Function/Method.php b/vendor/twig/twig/lib/Twig/Function/Method.php
new file mode 100644
index 00000000..67039a95
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Function/Method.php
@@ -0,0 +1,40 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2010 Arnaud Le Blanc
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a method template function.
15 *
16 * Use Twig_SimpleFunction instead.
17 *
18 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
19 * @deprecated since 1.12 (to be removed in 2.0)
20 */
21class Twig_Function_Method extends Twig_Function
22{
23 protected $extension;
24 protected $method;
25
26 public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
27 {
28 $options['callable'] = array($extension, $method);
29
30 parent::__construct($options);
31
32 $this->extension = $extension;
33 $this->method = $method;
34 }
35
36 public function compile()
37 {
38 return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
39 }
40}
diff --git a/vendor/twig/twig/lib/Twig/Function/Node.php b/vendor/twig/twig/lib/Twig/Function/Node.php
new file mode 100644
index 00000000..06a0d0db
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Function/Node.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template function as a node.
14 *
15 * Use Twig_SimpleFunction instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20class Twig_Function_Node extends Twig_Function
21{
22 protected $class;
23
24 public function __construct($class, array $options = array())
25 {
26 parent::__construct($options);
27
28 $this->class = $class;
29 }
30
31 public function getClass()
32 {
33 return $this->class;
34 }
35
36 public function compile()
37 {
38 }
39}
diff --git a/vendor/twig/twig/lib/Twig/FunctionCallableInterface.php b/vendor/twig/twig/lib/Twig/FunctionCallableInterface.php
new file mode 100644
index 00000000..0aab4f5e
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/FunctionCallableInterface.php
@@ -0,0 +1,23 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a callable template function.
14 *
15 * Use Twig_SimpleFunction instead.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 * @deprecated since 1.12 (to be removed in 2.0)
19 */
20interface Twig_FunctionCallableInterface
21{
22 public function getCallable();
23}
diff --git a/vendor/twig/twig/lib/Twig/FunctionInterface.php b/vendor/twig/twig/lib/Twig/FunctionInterface.php
new file mode 100644
index 00000000..67f4f89c
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/FunctionInterface.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 * (c) 2010 Arnaud Le Blanc
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a template function.
15 *
16 * Use Twig_SimpleFunction instead.
17 *
18 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
19 * @deprecated since 1.12 (to be removed in 2.0)
20 */
21interface Twig_FunctionInterface
22{
23 /**
24 * Compiles a function.
25 *
26 * @return string The PHP code for the function
27 */
28 public function compile();
29
30 public function needsEnvironment();
31
32 public function needsContext();
33
34 public function getSafe(Twig_Node $filterArgs);
35
36 public function setArguments($arguments);
37
38 public function getArguments();
39}
diff --git a/vendor/twig/twig/lib/Twig/Lexer.php b/vendor/twig/twig/lib/Twig/Lexer.php
new file mode 100644
index 00000000..000b038e
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Lexer.php
@@ -0,0 +1,408 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Lexes a template string.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Lexer implements Twig_LexerInterface
19{
20 protected $tokens;
21 protected $code;
22 protected $cursor;
23 protected $lineno;
24 protected $end;
25 protected $state;
26 protected $states;
27 protected $brackets;
28 protected $env;
29 protected $filename;
30 protected $options;
31 protected $regexes;
32 protected $position;
33 protected $positions;
34 protected $currentVarBlockLine;
35
36 const STATE_DATA = 0;
37 const STATE_BLOCK = 1;
38 const STATE_VAR = 2;
39 const STATE_STRING = 3;
40 const STATE_INTERPOLATION = 4;
41
42 const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
43 const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?/A';
44 const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
45 const REGEX_DQ_STRING_DELIM = '/"/A';
46 const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
47 const PUNCTUATION = '()[]{}?:.,|';
48
49 public function __construct(Twig_Environment $env, array $options = array())
50 {
51 $this->env = $env;
52
53 $this->options = array_merge(array(
54 'tag_comment' => array('{#', '#}'),
55 'tag_block' => array('{%', '%}'),
56 'tag_variable' => array('{{', '}}'),
57 'whitespace_trim' => '-',
58 'interpolation' => array('#{', '}'),
59 ), $options);
60
61 $this->regexes = array(
62 'lex_var' => '/\s*'.preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '/').'\s*|\s*'.preg_quote($this->options['tag_variable'][1], '/').'/A',
63 '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',
64 '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',
65 'operator' => $this->getOperatorRegex(),
66 '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',
67 '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',
68 'lex_block_line' => '/\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '/').'/As',
69 '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',
70 'interpolation_start' => '/'.preg_quote($this->options['interpolation'][0], '/').'\s*/A',
71 'interpolation_end' => '/\s*'.preg_quote($this->options['interpolation'][1], '/').'/A',
72 );
73 }
74
75 /**
76 * Tokenizes a source code.
77 *
78 * @param string $code The source code
79 * @param string $filename A unique identifier for the source code
80 *
81 * @return Twig_TokenStream A token stream instance
82 */
83 public function tokenize($code, $filename = null)
84 {
85 if (function_exists('mb_internal_encoding') && ((int) ini_get('mbstring.func_overload')) & 2) {
86 $mbEncoding = mb_internal_encoding();
87 mb_internal_encoding('ASCII');
88 }
89
90 $this->code = str_replace(array("\r\n", "\r"), "\n", $code);
91 $this->filename = $filename;
92 $this->cursor = 0;
93 $this->lineno = 1;
94 $this->end = strlen($this->code);
95 $this->tokens = array();
96 $this->state = self::STATE_DATA;
97 $this->states = array();
98 $this->brackets = array();
99 $this->position = -1;
100
101 // find all token starts in one go
102 preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE);
103 $this->positions = $matches;
104
105 while ($this->cursor < $this->end) {
106 // dispatch to the lexing functions depending
107 // on the current state
108 switch ($this->state) {
109 case self::STATE_DATA:
110 $this->lexData();
111 break;
112
113 case self::STATE_BLOCK:
114 $this->lexBlock();
115 break;
116
117 case self::STATE_VAR:
118 $this->lexVar();
119 break;
120
121 case self::STATE_STRING:
122 $this->lexString();
123 break;
124
125 case self::STATE_INTERPOLATION:
126 $this->lexInterpolation();
127 break;
128 }
129 }
130
131 $this->pushToken(Twig_Token::EOF_TYPE);
132
133 if (!empty($this->brackets)) {
134 list($expect, $lineno) = array_pop($this->brackets);
135 throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
136 }
137
138 if (isset($mbEncoding)) {
139 mb_internal_encoding($mbEncoding);
140 }
141
142 return new Twig_TokenStream($this->tokens, $this->filename);
143 }
144
145 protected function lexData()
146 {
147 // if no matches are left we return the rest of the template as simple text token
148 if ($this->position == count($this->positions[0]) - 1) {
149 $this->pushToken(Twig_Token::TEXT_TYPE, substr($this->code, $this->cursor));
150 $this->cursor = $this->end;
151
152 return;
153 }
154
155 // Find the first token after the current cursor
156 $position = $this->positions[0][++$this->position];
157 while ($position[1] < $this->cursor) {
158 if ($this->position == count($this->positions[0]) - 1) {
159 return;
160 }
161 $position = $this->positions[0][++$this->position];
162 }
163
164 // push the template text first
165 $text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
166 if (isset($this->positions[2][$this->position][0])) {
167 $text = rtrim($text);
168 }
169 $this->pushToken(Twig_Token::TEXT_TYPE, $text);
170 $this->moveCursor($textContent.$position[0]);
171
172 switch ($this->positions[1][$this->position][0]) {
173 case $this->options['tag_comment'][0]:
174 $this->lexComment();
175 break;
176
177 case $this->options['tag_block'][0]:
178 // raw data?
179 if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, null, $this->cursor)) {
180 $this->moveCursor($match[0]);
181 $this->lexRawData($match[1]);
182 // {% line \d+ %}
183 } elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, null, $this->cursor)) {
184 $this->moveCursor($match[0]);
185 $this->lineno = (int) $match[1];
186 } else {
187 $this->pushToken(Twig_Token::BLOCK_START_TYPE);
188 $this->pushState(self::STATE_BLOCK);
189 $this->currentVarBlockLine = $this->lineno;
190 }
191 break;
192
193 case $this->options['tag_variable'][0]:
194 $this->pushToken(Twig_Token::VAR_START_TYPE);
195 $this->pushState(self::STATE_VAR);
196 $this->currentVarBlockLine = $this->lineno;
197 break;
198 }
199 }
200
201 protected function lexBlock()
202 {
203 if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, null, $this->cursor)) {
204 $this->pushToken(Twig_Token::BLOCK_END_TYPE);
205 $this->moveCursor($match[0]);
206 $this->popState();
207 } else {
208 $this->lexExpression();
209 }
210 }
211
212 protected function lexVar()
213 {
214 if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, null, $this->cursor)) {
215 $this->pushToken(Twig_Token::VAR_END_TYPE);
216 $this->moveCursor($match[0]);
217 $this->popState();
218 } else {
219 $this->lexExpression();
220 }
221 }
222
223 protected function lexExpression()
224 {
225 // whitespace
226 if (preg_match('/\s+/A', $this->code, $match, null, $this->cursor)) {
227 $this->moveCursor($match[0]);
228
229 if ($this->cursor >= $this->end) {
230 throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $this->state === self::STATE_BLOCK ? 'block' : 'variable'), $this->currentVarBlockLine, $this->filename);
231 }
232 }
233
234 // operators
235 if (preg_match($this->regexes['operator'], $this->code, $match, null, $this->cursor)) {
236 $this->pushToken(Twig_Token::OPERATOR_TYPE, $match[0]);
237 $this->moveCursor($match[0]);
238 }
239 // names
240 elseif (preg_match(self::REGEX_NAME, $this->code, $match, null, $this->cursor)) {
241 $this->pushToken(Twig_Token::NAME_TYPE, $match[0]);
242 $this->moveCursor($match[0]);
243 }
244 // numbers
245 elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, null, $this->cursor)) {
246 $number = (float) $match[0]; // floats
247 if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) {
248 $number = (int) $match[0]; // integers lower than the maximum
249 }
250 $this->pushToken(Twig_Token::NUMBER_TYPE, $number);
251 $this->moveCursor($match[0]);
252 }
253 // punctuation
254 elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) {
255 // opening bracket
256 if (false !== strpos('([{', $this->code[$this->cursor])) {
257 $this->brackets[] = array($this->code[$this->cursor], $this->lineno);
258 }
259 // closing bracket
260 elseif (false !== strpos(')]}', $this->code[$this->cursor])) {
261 if (empty($this->brackets)) {
262 throw new Twig_Error_Syntax(sprintf('Unexpected "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename);
263 }
264
265 list($expect, $lineno) = array_pop($this->brackets);
266 if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) {
267 throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
268 }
269 }
270
271 $this->pushToken(Twig_Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
272 ++$this->cursor;
273 }
274 // strings
275 elseif (preg_match(self::REGEX_STRING, $this->code, $match, null, $this->cursor)) {
276 $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes(substr($match[0], 1, -1)));
277 $this->moveCursor($match[0]);
278 }
279 // opening double quoted string
280 elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
281 $this->brackets[] = array('"', $this->lineno);
282 $this->pushState(self::STATE_STRING);
283 $this->moveCursor($match[0]);
284 }
285 // unlexable
286 else {
287 throw new Twig_Error_Syntax(sprintf('Unexpected character "%s"', $this->code[$this->cursor]), $this->lineno, $this->filename);
288 }
289 }
290
291 protected function lexRawData($tag)
292 {
293 if (!preg_match(str_replace('%s', $tag, $this->regexes['lex_raw_data']), $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
294 throw new Twig_Error_Syntax(sprintf('Unexpected end of file: Unclosed "%s" block', $tag), $this->lineno, $this->filename);
295 }
296
297 $text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
298 $this->moveCursor($text.$match[0][0]);
299
300 if (false !== strpos($match[1][0], $this->options['whitespace_trim'])) {
301 $text = rtrim($text);
302 }
303
304 $this->pushToken(Twig_Token::TEXT_TYPE, $text);
305 }
306
307 protected function lexComment()
308 {
309 if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) {
310 throw new Twig_Error_Syntax('Unclosed comment', $this->lineno, $this->filename);
311 }
312
313 $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
314 }
315
316 protected function lexString()
317 {
318 if (preg_match($this->regexes['interpolation_start'], $this->code, $match, null, $this->cursor)) {
319 $this->brackets[] = array($this->options['interpolation'][0], $this->lineno);
320 $this->pushToken(Twig_Token::INTERPOLATION_START_TYPE);
321 $this->moveCursor($match[0]);
322 $this->pushState(self::STATE_INTERPOLATION);
323
324 } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, null, $this->cursor) && strlen($match[0]) > 0) {
325 $this->pushToken(Twig_Token::STRING_TYPE, stripcslashes($match[0]));
326 $this->moveCursor($match[0]);
327
328 } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, null, $this->cursor)) {
329
330 list($expect, $lineno) = array_pop($this->brackets);
331 if ($this->code[$this->cursor] != '"') {
332 throw new Twig_Error_Syntax(sprintf('Unclosed "%s"', $expect), $lineno, $this->filename);
333 }
334
335 $this->popState();
336 ++$this->cursor;
337 }
338 }
339
340 protected function lexInterpolation()
341 {
342 $bracket = end($this->brackets);
343 if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, null, $this->cursor)) {
344 array_pop($this->brackets);
345 $this->pushToken(Twig_Token::INTERPOLATION_END_TYPE);
346 $this->moveCursor($match[0]);
347 $this->popState();
348 } else {
349 $this->lexExpression();
350 }
351 }
352
353 protected function pushToken($type, $value = '')
354 {
355 // do not push empty text tokens
356 if (Twig_Token::TEXT_TYPE === $type && '' === $value) {
357 return;
358 }
359
360 $this->tokens[] = new Twig_Token($type, $value, $this->lineno);
361 }
362
363 protected function moveCursor($text)
364 {
365 $this->cursor += strlen($text);
366 $this->lineno += substr_count($text, "\n");
367 }
368
369 protected function getOperatorRegex()
370 {
371 $operators = array_merge(
372 array('='),
373 array_keys($this->env->getUnaryOperators()),
374 array_keys($this->env->getBinaryOperators())
375 );
376
377 $operators = array_combine($operators, array_map('strlen', $operators));
378 arsort($operators);
379
380 $regex = array();
381 foreach ($operators as $operator => $length) {
382 // an operator that ends with a character must be followed by
383 // a whitespace or a parenthesis
384 if (ctype_alpha($operator[$length - 1])) {
385 $regex[] = preg_quote($operator, '/').'(?=[\s()])';
386 } else {
387 $regex[] = preg_quote($operator, '/');
388 }
389 }
390
391 return '/'.implode('|', $regex).'/A';
392 }
393
394 protected function pushState($state)
395 {
396 $this->states[] = $this->state;
397 $this->state = $state;
398 }
399
400 protected function popState()
401 {
402 if (0 === count($this->states)) {
403 throw new Exception('Cannot pop state without a previous state');
404 }
405
406 $this->state = array_pop($this->states);
407 }
408}
diff --git a/vendor/twig/twig/lib/Twig/LexerInterface.php b/vendor/twig/twig/lib/Twig/LexerInterface.php
new file mode 100644
index 00000000..4b83f81b
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/LexerInterface.php
@@ -0,0 +1,29 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interface implemented by lexer classes.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_LexerInterface
19{
20 /**
21 * Tokenizes a source code.
22 *
23 * @param string $code The source code
24 * @param string $filename A unique identifier for the source code
25 *
26 * @return Twig_TokenStream A token stream instance
27 */
28 public function tokenize($code, $filename = null);
29}
diff --git a/vendor/twig/twig/lib/Twig/Loader/Array.php b/vendor/twig/twig/lib/Twig/Loader/Array.php
new file mode 100644
index 00000000..89087aea
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Loader/Array.php
@@ -0,0 +1,98 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Loads a template from an array.
14 *
15 * When using this loader with a cache mechanism, you should know that a new cache
16 * key is generated each time a template content "changes" (the cache key being the
17 * source code of the template). If you don't want to see your cache grows out of
18 * control, you need to take care of clearing the old cache file by yourself.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 */
22class Twig_Loader_Array implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
23{
24 protected $templates;
25
26 /**
27 * Constructor.
28 *
29 * @param array $templates An array of templates (keys are the names, and values are the source code)
30 *
31 * @see Twig_Loader
32 */
33 public function __construct(array $templates)
34 {
35 $this->templates = array();
36 foreach ($templates as $name => $template) {
37 $this->templates[$name] = $template;
38 }
39 }
40
41 /**
42 * Adds or overrides a template.
43 *
44 * @param string $name The template name
45 * @param string $template The template source
46 */
47 public function setTemplate($name, $template)
48 {
49 $this->templates[(string) $name] = $template;
50 }
51
52 /**
53 * {@inheritdoc}
54 */
55 public function getSource($name)
56 {
57 $name = (string) $name;
58 if (!isset($this->templates[$name])) {
59 throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
60 }
61
62 return $this->templates[$name];
63 }
64
65 /**
66 * {@inheritdoc}
67 */
68 public function exists($name)
69 {
70 return isset($this->templates[(string) $name]);
71 }
72
73 /**
74 * {@inheritdoc}
75 */
76 public function getCacheKey($name)
77 {
78 $name = (string) $name;
79 if (!isset($this->templates[$name])) {
80 throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
81 }
82
83 return $this->templates[$name];
84 }
85
86 /**
87 * {@inheritdoc}
88 */
89 public function isFresh($name, $time)
90 {
91 $name = (string) $name;
92 if (!isset($this->templates[$name])) {
93 throw new Twig_Error_Loader(sprintf('Template "%s" is not defined.', $name));
94 }
95
96 return true;
97 }
98}
diff --git a/vendor/twig/twig/lib/Twig/Loader/Chain.php b/vendor/twig/twig/lib/Twig/Loader/Chain.php
new file mode 100644
index 00000000..1f1cf065
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Loader/Chain.php
@@ -0,0 +1,139 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Loads templates from other loaders.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Loader_Chain implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
18{
19 private $hasSourceCache = array();
20 protected $loaders;
21
22 /**
23 * Constructor.
24 *
25 * @param Twig_LoaderInterface[] $loaders An array of loader instances
26 */
27 public function __construct(array $loaders = array())
28 {
29 $this->loaders = array();
30 foreach ($loaders as $loader) {
31 $this->addLoader($loader);
32 }
33 }
34
35 /**
36 * Adds a loader instance.
37 *
38 * @param Twig_LoaderInterface $loader A Loader instance
39 */
40 public function addLoader(Twig_LoaderInterface $loader)
41 {
42 $this->loaders[] = $loader;
43 $this->hasSourceCache = array();
44 }
45
46 /**
47 * {@inheritdoc}
48 */
49 public function getSource($name)
50 {
51 $exceptions = array();
52 foreach ($this->loaders as $loader) {
53 if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
54 continue;
55 }
56
57 try {
58 return $loader->getSource($name);
59 } catch (Twig_Error_Loader $e) {
60 $exceptions[] = $e->getMessage();
61 }
62 }
63
64 throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(', ', $exceptions)));
65 }
66
67 /**
68 * {@inheritdoc}
69 */
70 public function exists($name)
71 {
72 $name = (string) $name;
73
74 if (isset($this->hasSourceCache[$name])) {
75 return $this->hasSourceCache[$name];
76 }
77
78 foreach ($this->loaders as $loader) {
79 if ($loader instanceof Twig_ExistsLoaderInterface) {
80 if ($loader->exists($name)) {
81 return $this->hasSourceCache[$name] = true;
82 }
83
84 continue;
85 }
86
87 try {
88 $loader->getSource($name);
89
90 return $this->hasSourceCache[$name] = true;
91 } catch (Twig_Error_Loader $e) {
92 }
93 }
94
95 return $this->hasSourceCache[$name] = false;
96 }
97
98 /**
99 * {@inheritdoc}
100 */
101 public function getCacheKey($name)
102 {
103 $exceptions = array();
104 foreach ($this->loaders as $loader) {
105 if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
106 continue;
107 }
108
109 try {
110 return $loader->getCacheKey($name);
111 } catch (Twig_Error_Loader $e) {
112 $exceptions[] = get_class($loader).': '.$e->getMessage();
113 }
114 }
115
116 throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
117 }
118
119 /**
120 * {@inheritdoc}
121 */
122 public function isFresh($name, $time)
123 {
124 $exceptions = array();
125 foreach ($this->loaders as $loader) {
126 if ($loader instanceof Twig_ExistsLoaderInterface && !$loader->exists($name)) {
127 continue;
128 }
129
130 try {
131 return $loader->isFresh($name, $time);
132 } catch (Twig_Error_Loader $e) {
133 $exceptions[] = get_class($loader).': '.$e->getMessage();
134 }
135 }
136
137 throw new Twig_Error_Loader(sprintf('Template "%s" is not defined (%s).', $name, implode(' ', $exceptions)));
138 }
139}
diff --git a/vendor/twig/twig/lib/Twig/Loader/Filesystem.php b/vendor/twig/twig/lib/Twig/Loader/Filesystem.php
new file mode 100644
index 00000000..d47781ab
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Loader/Filesystem.php
@@ -0,0 +1,226 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Loads template from the filesystem.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Loader_Filesystem implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
18{
19 /** Identifier of the main namespace. */
20 const MAIN_NAMESPACE = '__main__';
21
22 protected $paths;
23 protected $cache;
24
25 /**
26 * Constructor.
27 *
28 * @param string|array $paths A path or an array of paths where to look for templates
29 */
30 public function __construct($paths = array())
31 {
32 if ($paths) {
33 $this->setPaths($paths);
34 }
35 }
36
37 /**
38 * Returns the paths to the templates.
39 *
40 * @param string $namespace A path namespace
41 *
42 * @return array The array of paths where to look for templates
43 */
44 public function getPaths($namespace = self::MAIN_NAMESPACE)
45 {
46 return isset($this->paths[$namespace]) ? $this->paths[$namespace] : array();
47 }
48
49 /**
50 * Returns the path namespaces.
51 *
52 * The main namespace is always defined.
53 *
54 * @return array The array of defined namespaces
55 */
56 public function getNamespaces()
57 {
58 return array_keys($this->paths);
59 }
60
61 /**
62 * Sets the paths where templates are stored.
63 *
64 * @param string|array $paths A path or an array of paths where to look for templates
65 * @param string $namespace A path namespace
66 */
67 public function setPaths($paths, $namespace = self::MAIN_NAMESPACE)
68 {
69 if (!is_array($paths)) {
70 $paths = array($paths);
71 }
72
73 $this->paths[$namespace] = array();
74 foreach ($paths as $path) {
75 $this->addPath($path, $namespace);
76 }
77 }
78
79 /**
80 * Adds a path where templates are stored.
81 *
82 * @param string $path A path where to look for templates
83 * @param string $namespace A path name
84 *
85 * @throws Twig_Error_Loader
86 */
87 public function addPath($path, $namespace = self::MAIN_NAMESPACE)
88 {
89 // invalidate the cache
90 $this->cache = array();
91
92 if (!is_dir($path)) {
93 throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
94 }
95
96 $this->paths[$namespace][] = rtrim($path, '/\\');
97 }
98
99 /**
100 * Prepends a path where templates are stored.
101 *
102 * @param string $path A path where to look for templates
103 * @param string $namespace A path name
104 *
105 * @throws Twig_Error_Loader
106 */
107 public function prependPath($path, $namespace = self::MAIN_NAMESPACE)
108 {
109 // invalidate the cache
110 $this->cache = array();
111
112 if (!is_dir($path)) {
113 throw new Twig_Error_Loader(sprintf('The "%s" directory does not exist.', $path));
114 }
115
116 $path = rtrim($path, '/\\');
117
118 if (!isset($this->paths[$namespace])) {
119 $this->paths[$namespace][] = $path;
120 } else {
121 array_unshift($this->paths[$namespace], $path);
122 }
123 }
124
125 /**
126 * {@inheritdoc}
127 */
128 public function getSource($name)
129 {
130 return file_get_contents($this->findTemplate($name));
131 }
132
133 /**
134 * {@inheritdoc}
135 */
136 public function getCacheKey($name)
137 {
138 return $this->findTemplate($name);
139 }
140
141 /**
142 * {@inheritdoc}
143 */
144 public function exists($name)
145 {
146 $name = (string) $name;
147 if (isset($this->cache[$name])) {
148 return true;
149 }
150
151 try {
152 $this->findTemplate($name);
153
154 return true;
155 } catch (Twig_Error_Loader $exception) {
156 return false;
157 }
158 }
159
160 /**
161 * {@inheritdoc}
162 */
163 public function isFresh($name, $time)
164 {
165 return filemtime($this->findTemplate($name)) <= $time;
166 }
167
168 protected function findTemplate($name)
169 {
170 $name = (string) $name;
171
172 // normalize name
173 $name = preg_replace('#/{2,}#', '/', strtr($name, '\\', '/'));
174
175 if (isset($this->cache[$name])) {
176 return $this->cache[$name];
177 }
178
179 $this->validateName($name);
180
181 $namespace = self::MAIN_NAMESPACE;
182 if (isset($name[0]) && '@' == $name[0]) {
183 if (false === $pos = strpos($name, '/')) {
184 throw new Twig_Error_Loader(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
185 }
186
187 $namespace = substr($name, 1, $pos - 1);
188
189 $name = substr($name, $pos + 1);
190 }
191
192 if (!isset($this->paths[$namespace])) {
193 throw new Twig_Error_Loader(sprintf('There are no registered paths for namespace "%s".', $namespace));
194 }
195
196 foreach ($this->paths[$namespace] as $path) {
197 if (is_file($path.'/'.$name)) {
198 return $this->cache[$name] = $path.'/'.$name;
199 }
200 }
201
202 throw new Twig_Error_Loader(sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])));
203 }
204
205 protected function validateName($name)
206 {
207 if (false !== strpos($name, "\0")) {
208 throw new Twig_Error_Loader('A template name cannot contain NUL bytes.');
209 }
210
211 $name = ltrim($name, '/');
212 $parts = explode('/', $name);
213 $level = 0;
214 foreach ($parts as $part) {
215 if ('..' === $part) {
216 --$level;
217 } elseif ('.' !== $part) {
218 ++$level;
219 }
220
221 if ($level < 0) {
222 throw new Twig_Error_Loader(sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
223 }
224 }
225 }
226}
diff --git a/vendor/twig/twig/lib/Twig/Loader/String.php b/vendor/twig/twig/lib/Twig/Loader/String.php
new file mode 100644
index 00000000..8ad9856c
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Loader/String.php
@@ -0,0 +1,59 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Loads a template from a string.
14 *
15 * This loader should only be used for unit testing as it has many limitations
16 * (for instance, the include or extends tag does not make any sense for a string
17 * loader).
18 *
19 * When using this loader with a cache mechanism, you should know that a new cache
20 * key is generated each time a template content "changes" (the cache key being the
21 * source code of the template). If you don't want to see your cache grows out of
22 * control, you need to take care of clearing the old cache file by yourself.
23 *
24 * @author Fabien Potencier <fabien@symfony.com>
25 */
26class Twig_Loader_String implements Twig_LoaderInterface, Twig_ExistsLoaderInterface
27{
28 /**
29 * {@inheritdoc}
30 */
31 public function getSource($name)
32 {
33 return $name;
34 }
35
36 /**
37 * {@inheritdoc}
38 */
39 public function exists($name)
40 {
41 return true;
42 }
43
44 /**
45 * {@inheritdoc}
46 */
47 public function getCacheKey($name)
48 {
49 return $name;
50 }
51
52 /**
53 * {@inheritdoc}
54 */
55 public function isFresh($name, $time)
56 {
57 return true;
58 }
59}
diff --git a/vendor/twig/twig/lib/Twig/LoaderInterface.php b/vendor/twig/twig/lib/Twig/LoaderInterface.php
new file mode 100644
index 00000000..927786d1
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/LoaderInterface.php
@@ -0,0 +1,52 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interface all loaders must implement.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17interface Twig_LoaderInterface
18{
19 /**
20 * Gets the source code of a template, given its name.
21 *
22 * @param string $name The name of the template to load
23 *
24 * @return string The template source code
25 *
26 * @throws Twig_Error_Loader When $name is not found
27 */
28 public function getSource($name);
29
30 /**
31 * Gets the cache key to use for the cache for a given template name.
32 *
33 * @param string $name The name of the template to load
34 *
35 * @return string The cache key
36 *
37 * @throws Twig_Error_Loader When $name is not found
38 */
39 public function getCacheKey($name);
40
41 /**
42 * Returns true if the template is still fresh.
43 *
44 * @param string $name The template name
45 * @param timestamp $time The last modification time of the cached template
46 *
47 * @return Boolean true if the template is fresh, false otherwise
48 *
49 * @throws Twig_Error_Loader When $name is not found
50 */
51 public function isFresh($name, $time);
52}
diff --git a/vendor/twig/twig/lib/Twig/Markup.php b/vendor/twig/twig/lib/Twig/Markup.php
new file mode 100644
index 00000000..69871fcb
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Markup.php
@@ -0,0 +1,37 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Marks a content as safe.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Markup implements Countable
18{
19 protected $content;
20 protected $charset;
21
22 public function __construct($content, $charset)
23 {
24 $this->content = (string) $content;
25 $this->charset = $charset;
26 }
27
28 public function __toString()
29 {
30 return $this->content;
31 }
32
33 public function count()
34 {
35 return function_exists('mb_get_info') ? mb_strlen($this->content, $this->charset) : strlen($this->content);
36 }
37}
diff --git a/vendor/twig/twig/lib/Twig/Node.php b/vendor/twig/twig/lib/Twig/Node.php
new file mode 100644
index 00000000..931b4635
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node.php
@@ -0,0 +1,226 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a node in the AST.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node implements Twig_NodeInterface
19{
20 protected $nodes;
21 protected $attributes;
22 protected $lineno;
23 protected $tag;
24
25 /**
26 * Constructor.
27 *
28 * The nodes are automatically made available as properties ($this->node).
29 * The attributes are automatically made available as array items ($this['name']).
30 *
31 * @param array $nodes An array of named nodes
32 * @param array $attributes An array of attributes (should not be nodes)
33 * @param integer $lineno The line number
34 * @param string $tag The tag name associated with the Node
35 */
36 public function __construct(array $nodes = array(), array $attributes = array(), $lineno = 0, $tag = null)
37 {
38 $this->nodes = $nodes;
39 $this->attributes = $attributes;
40 $this->lineno = $lineno;
41 $this->tag = $tag;
42 }
43
44 public function __toString()
45 {
46 $attributes = array();
47 foreach ($this->attributes as $name => $value) {
48 $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true)));
49 }
50
51 $repr = array(get_class($this).'('.implode(', ', $attributes));
52
53 if (count($this->nodes)) {
54 foreach ($this->nodes as $name => $node) {
55 $len = strlen($name) + 4;
56 $noderepr = array();
57 foreach (explode("\n", (string) $node) as $line) {
58 $noderepr[] = str_repeat(' ', $len).$line;
59 }
60
61 $repr[] = sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr)));
62 }
63
64 $repr[] = ')';
65 } else {
66 $repr[0] .= ')';
67 }
68
69 return implode("\n", $repr);
70 }
71
72 public function toXml($asDom = false)
73 {
74 $dom = new DOMDocument('1.0', 'UTF-8');
75 $dom->formatOutput = true;
76 $dom->appendChild($xml = $dom->createElement('twig'));
77
78 $xml->appendChild($node = $dom->createElement('node'));
79 $node->setAttribute('class', get_class($this));
80
81 foreach ($this->attributes as $name => $value) {
82 $node->appendChild($attribute = $dom->createElement('attribute'));
83 $attribute->setAttribute('name', $name);
84 $attribute->appendChild($dom->createTextNode($value));
85 }
86
87 foreach ($this->nodes as $name => $n) {
88 if (null === $n) {
89 continue;
90 }
91
92 $child = $n->toXml(true)->getElementsByTagName('node')->item(0);
93 $child = $dom->importNode($child, true);
94 $child->setAttribute('name', $name);
95
96 $node->appendChild($child);
97 }
98
99 return $asDom ? $dom : $dom->saveXml();
100 }
101
102 public function compile(Twig_Compiler $compiler)
103 {
104 foreach ($this->nodes as $node) {
105 $node->compile($compiler);
106 }
107 }
108
109 public function getLine()
110 {
111 return $this->lineno;
112 }
113
114 public function getNodeTag()
115 {
116 return $this->tag;
117 }
118
119 /**
120 * Returns true if the attribute is defined.
121 *
122 * @param string The attribute name
123 *
124 * @return Boolean true if the attribute is defined, false otherwise
125 */
126 public function hasAttribute($name)
127 {
128 return array_key_exists($name, $this->attributes);
129 }
130
131 /**
132 * Gets an attribute.
133 *
134 * @param string The attribute name
135 *
136 * @return mixed The attribute value
137 */
138 public function getAttribute($name)
139 {
140 if (!array_key_exists($name, $this->attributes)) {
141 throw new LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, get_class($this)));
142 }
143
144 return $this->attributes[$name];
145 }
146
147 /**
148 * Sets an attribute.
149 *
150 * @param string The attribute name
151 * @param mixed The attribute value
152 */
153 public function setAttribute($name, $value)
154 {
155 $this->attributes[$name] = $value;
156 }
157
158 /**
159 * Removes an attribute.
160 *
161 * @param string The attribute name
162 */
163 public function removeAttribute($name)
164 {
165 unset($this->attributes[$name]);
166 }
167
168 /**
169 * Returns true if the node with the given identifier exists.
170 *
171 * @param string The node name
172 *
173 * @return Boolean true if the node with the given name exists, false otherwise
174 */
175 public function hasNode($name)
176 {
177 return array_key_exists($name, $this->nodes);
178 }
179
180 /**
181 * Gets a node by name.
182 *
183 * @param string The node name
184 *
185 * @return Twig_Node A Twig_Node instance
186 */
187 public function getNode($name)
188 {
189 if (!array_key_exists($name, $this->nodes)) {
190 throw new LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, get_class($this)));
191 }
192
193 return $this->nodes[$name];
194 }
195
196 /**
197 * Sets a node.
198 *
199 * @param string The node name
200 * @param Twig_Node A Twig_Node instance
201 */
202 public function setNode($name, $node = null)
203 {
204 $this->nodes[$name] = $node;
205 }
206
207 /**
208 * Removes a node by name.
209 *
210 * @param string The node name
211 */
212 public function removeNode($name)
213 {
214 unset($this->nodes[$name]);
215 }
216
217 public function count()
218 {
219 return count($this->nodes);
220 }
221
222 public function getIterator()
223 {
224 return new ArrayIterator($this->nodes);
225 }
226}
diff --git a/vendor/twig/twig/lib/Twig/Node/AutoEscape.php b/vendor/twig/twig/lib/Twig/Node/AutoEscape.php
new file mode 100644
index 00000000..8f190e0b
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/AutoEscape.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents an autoescape node.
14 *
15 * The value is the escaping strategy (can be html, js, ...)
16 *
17 * The true value is equivalent to html.
18 *
19 * If autoescaping is disabled, then the value is false.
20 *
21 * @author Fabien Potencier <fabien@symfony.com>
22 */
23class Twig_Node_AutoEscape extends Twig_Node
24{
25 public function __construct($value, Twig_NodeInterface $body, $lineno, $tag = 'autoescape')
26 {
27 parent::__construct(array('body' => $body), array('value' => $value), $lineno, $tag);
28 }
29
30 /**
31 * Compiles the node to PHP.
32 *
33 * @param Twig_Compiler A Twig_Compiler instance
34 */
35 public function compile(Twig_Compiler $compiler)
36 {
37 $compiler->subcompile($this->getNode('body'));
38 }
39}
diff --git a/vendor/twig/twig/lib/Twig/Node/Block.php b/vendor/twig/twig/lib/Twig/Node/Block.php
new file mode 100644
index 00000000..50eb67ed
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Block.php
@@ -0,0 +1,44 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a block node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_Block extends Twig_Node
19{
20 public function __construct($name, Twig_NodeInterface $body, $lineno, $tag = null)
21 {
22 parent::__construct(array('body' => $body), array('name' => $name), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 $compiler
33 ->addDebugInfo($this)
34 ->write(sprintf("public function block_%s(\$context, array \$blocks = array())\n", $this->getAttribute('name')), "{\n")
35 ->indent()
36 ;
37
38 $compiler
39 ->subcompile($this->getNode('body'))
40 ->outdent()
41 ->write("}\n\n")
42 ;
43 }
44}
diff --git a/vendor/twig/twig/lib/Twig/Node/BlockReference.php b/vendor/twig/twig/lib/Twig/Node/BlockReference.php
new file mode 100644
index 00000000..013e369e
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/BlockReference.php
@@ -0,0 +1,37 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a block call node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_BlockReference extends Twig_Node implements Twig_NodeOutputInterface
19{
20 public function __construct($name, $lineno, $tag = null)
21 {
22 parent::__construct(array(), array('name' => $name), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 $compiler
33 ->addDebugInfo($this)
34 ->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
35 ;
36 }
37}
diff --git a/vendor/twig/twig/lib/Twig/Node/Body.php b/vendor/twig/twig/lib/Twig/Node/Body.php
new file mode 100644
index 00000000..3ffb1342
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Body.php
@@ -0,0 +1,19 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a body node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Body extends Twig_Node
18{
19}
diff --git a/vendor/twig/twig/lib/Twig/Node/Do.php b/vendor/twig/twig/lib/Twig/Node/Do.php
new file mode 100644
index 00000000..c528066b
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Do.php
@@ -0,0 +1,38 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a do node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Do extends Twig_Node
18{
19 public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
20 {
21 parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
22 }
23
24 /**
25 * Compiles the node to PHP.
26 *
27 * @param Twig_Compiler A Twig_Compiler instance
28 */
29 public function compile(Twig_Compiler $compiler)
30 {
31 $compiler
32 ->addDebugInfo($this)
33 ->write('')
34 ->subcompile($this->getNode('expr'))
35 ->raw(";\n")
36 ;
37 }
38}
diff --git a/vendor/twig/twig/lib/Twig/Node/Embed.php b/vendor/twig/twig/lib/Twig/Node/Embed.php
new file mode 100644
index 00000000..4c9456dc
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Embed.php
@@ -0,0 +1,38 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents an embed node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Embed extends Twig_Node_Include
18{
19 // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
20 public function __construct($filename, $index, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
21 {
22 parent::__construct(new Twig_Node_Expression_Constant('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno, $tag);
23
24 $this->setAttribute('filename', $filename);
25 $this->setAttribute('index', $index);
26 }
27
28 protected function addGetTemplate(Twig_Compiler $compiler)
29 {
30 $compiler
31 ->write("\$this->env->loadTemplate(")
32 ->string($this->getAttribute('filename'))
33 ->raw(', ')
34 ->string($this->getAttribute('index'))
35 ->raw(")")
36 ;
37 }
38}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression.php b/vendor/twig/twig/lib/Twig/Node/Expression.php
new file mode 100644
index 00000000..a7382e7d
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression.php
@@ -0,0 +1,20 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Abstract class for all nodes that represents an expression.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18abstract class Twig_Node_Expression extends Twig_Node
19{
20}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Array.php b/vendor/twig/twig/lib/Twig/Node/Expression/Array.php
new file mode 100644
index 00000000..1da785fe
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Array.php
@@ -0,0 +1,86 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Array extends Twig_Node_Expression
12{
13 protected $index;
14
15 public function __construct(array $elements, $lineno)
16 {
17 parent::__construct($elements, array(), $lineno);
18
19 $this->index = -1;
20 foreach ($this->getKeyValuePairs() as $pair) {
21 if ($pair['key'] instanceof Twig_Node_Expression_Constant && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) {
22 $this->index = $pair['key']->getAttribute('value');
23 }
24 }
25 }
26
27 public function getKeyValuePairs()
28 {
29 $pairs = array();
30
31 foreach (array_chunk($this->nodes, 2) as $pair) {
32 $pairs[] = array(
33 'key' => $pair[0],
34 'value' => $pair[1],
35 );
36 }
37
38 return $pairs;
39 }
40
41 public function hasElement(Twig_Node_Expression $key)
42 {
43 foreach ($this->getKeyValuePairs() as $pair) {
44 // we compare the string representation of the keys
45 // to avoid comparing the line numbers which are not relevant here.
46 if ((string) $key == (string) $pair['key']) {
47 return true;
48 }
49 }
50
51 return false;
52 }
53
54 public function addElement(Twig_Node_Expression $value, Twig_Node_Expression $key = null)
55 {
56 if (null === $key) {
57 $key = new Twig_Node_Expression_Constant(++$this->index, $value->getLine());
58 }
59
60 array_push($this->nodes, $key, $value);
61 }
62
63 /**
64 * Compiles the node to PHP.
65 *
66 * @param Twig_Compiler A Twig_Compiler instance
67 */
68 public function compile(Twig_Compiler $compiler)
69 {
70 $compiler->raw('array(');
71 $first = true;
72 foreach ($this->getKeyValuePairs() as $pair) {
73 if (!$first) {
74 $compiler->raw(', ');
75 }
76 $first = false;
77
78 $compiler
79 ->subcompile($pair['key'])
80 ->raw(' => ')
81 ->subcompile($pair['value'])
82 ;
83 }
84 $compiler->raw(')');
85 }
86}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php b/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php
new file mode 100644
index 00000000..2ddea78c
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/AssignName.php
@@ -0,0 +1,28 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13class Twig_Node_Expression_AssignName extends Twig_Node_Expression_Name
14{
15 /**
16 * Compiles the node to PHP.
17 *
18 * @param Twig_Compiler A Twig_Compiler instance
19 */
20 public function compile(Twig_Compiler $compiler)
21 {
22 $compiler
23 ->raw('$context[')
24 ->string($this->getAttribute('name'))
25 ->raw(']')
26 ;
27 }
28}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php
new file mode 100644
index 00000000..9dd5de2c
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary.php
@@ -0,0 +1,40 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12abstract class Twig_Node_Expression_Binary extends Twig_Node_Expression
13{
14 public function __construct(Twig_NodeInterface $left, Twig_NodeInterface $right, $lineno)
15 {
16 parent::__construct(array('left' => $left, 'right' => $right), array(), $lineno);
17 }
18
19 /**
20 * Compiles the node to PHP.
21 *
22 * @param Twig_Compiler A Twig_Compiler instance
23 */
24 public function compile(Twig_Compiler $compiler)
25 {
26 $compiler
27 ->raw('(')
28 ->subcompile($this->getNode('left'))
29 ->raw(' ')
30 ;
31 $this->operator($compiler);
32 $compiler
33 ->raw(' ')
34 ->subcompile($this->getNode('right'))
35 ->raw(')')
36 ;
37 }
38
39 abstract public function operator(Twig_Compiler $compiler);
40}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php
new file mode 100644
index 00000000..0ef8e117
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Add.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_Add extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('+');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php
new file mode 100644
index 00000000..d5752ebb
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/And.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_And extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('&&');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php
new file mode 100644
index 00000000..9a46d845
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseAnd.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_BitwiseAnd extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('&');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php
new file mode 100644
index 00000000..058a20bf
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseOr.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_BitwiseOr extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('|');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php
new file mode 100644
index 00000000..f4da73d4
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/BitwiseXor.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_BitwiseXor extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('^');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php
new file mode 100644
index 00000000..f9a64627
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Concat.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_Concat extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('.');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php
new file mode 100644
index 00000000..e0797a61
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Div.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_Div extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('/');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php
new file mode 100644
index 00000000..7b1236d0
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Equal.php
@@ -0,0 +1,17 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_Equal extends Twig_Node_Expression_Binary
12{
13 public function operator(Twig_Compiler $compiler)
14 {
15 return $compiler->raw('==');
16 }
17}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php
new file mode 100644
index 00000000..7fbd0556
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/FloorDiv.php
@@ -0,0 +1,29 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_FloorDiv extends Twig_Node_Expression_Binary
12{
13 /**
14 * Compiles the node to PHP.
15 *
16 * @param Twig_Compiler A Twig_Compiler instance
17 */
18 public function compile(Twig_Compiler $compiler)
19 {
20 $compiler->raw('intval(floor(');
21 parent::compile($compiler);
22 $compiler->raw('))');
23 }
24
25 public function operator(Twig_Compiler $compiler)
26 {
27 return $compiler->raw('/');
28 }
29}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php
new file mode 100644
index 00000000..a110bd92
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Greater.php
@@ -0,0 +1,17 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_Greater extends Twig_Node_Expression_Binary
12{
13 public function operator(Twig_Compiler $compiler)
14 {
15 return $compiler->raw('>');
16 }
17}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php
new file mode 100644
index 00000000..3754fed2
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/GreaterEqual.php
@@ -0,0 +1,17 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_GreaterEqual extends Twig_Node_Expression_Binary
12{
13 public function operator(Twig_Compiler $compiler)
14 {
15 return $compiler->raw('>=');
16 }
17}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php
new file mode 100644
index 00000000..788f9377
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/In.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_In extends Twig_Node_Expression_Binary
12{
13 /**
14 * Compiles the node to PHP.
15 *
16 * @param Twig_Compiler A Twig_Compiler instance
17 */
18 public function compile(Twig_Compiler $compiler)
19 {
20 $compiler
21 ->raw('twig_in_filter(')
22 ->subcompile($this->getNode('left'))
23 ->raw(', ')
24 ->subcompile($this->getNode('right'))
25 ->raw(')')
26 ;
27 }
28
29 public function operator(Twig_Compiler $compiler)
30 {
31 return $compiler->raw('in');
32 }
33}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php
new file mode 100644
index 00000000..45fd3004
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Less.php
@@ -0,0 +1,17 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_Less extends Twig_Node_Expression_Binary
12{
13 public function operator(Twig_Compiler $compiler)
14 {
15 return $compiler->raw('<');
16 }
17}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php
new file mode 100644
index 00000000..e38e257c
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/LessEqual.php
@@ -0,0 +1,17 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_LessEqual extends Twig_Node_Expression_Binary
12{
13 public function operator(Twig_Compiler $compiler)
14 {
15 return $compiler->raw('<=');
16 }
17}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php
new file mode 100644
index 00000000..9924114f
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mod.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_Mod extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('%');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php
new file mode 100644
index 00000000..c91529ca
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Mul.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_Mul extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('*');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php
new file mode 100644
index 00000000..26867ba2
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotEqual.php
@@ -0,0 +1,17 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_NotEqual extends Twig_Node_Expression_Binary
12{
13 public function operator(Twig_Compiler $compiler)
14 {
15 return $compiler->raw('!=');
16 }
17}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php
new file mode 100644
index 00000000..f347b7b6
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/NotIn.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_NotIn extends Twig_Node_Expression_Binary
12{
13 /**
14 * Compiles the node to PHP.
15 *
16 * @param Twig_Compiler A Twig_Compiler instance
17 */
18 public function compile(Twig_Compiler $compiler)
19 {
20 $compiler
21 ->raw('!twig_in_filter(')
22 ->subcompile($this->getNode('left'))
23 ->raw(', ')
24 ->subcompile($this->getNode('right'))
25 ->raw(')')
26 ;
27 }
28
29 public function operator(Twig_Compiler $compiler)
30 {
31 return $compiler->raw('not in');
32 }
33}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php
new file mode 100644
index 00000000..adba49c6
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Or.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_Or extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('||');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php
new file mode 100644
index 00000000..b2c59040
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Power.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_Power extends Twig_Node_Expression_Binary
12{
13 /**
14 * Compiles the node to PHP.
15 *
16 * @param Twig_Compiler A Twig_Compiler instance
17 */
18 public function compile(Twig_Compiler $compiler)
19 {
20 $compiler
21 ->raw('pow(')
22 ->subcompile($this->getNode('left'))
23 ->raw(', ')
24 ->subcompile($this->getNode('right'))
25 ->raw(')')
26 ;
27 }
28
29 public function operator(Twig_Compiler $compiler)
30 {
31 return $compiler->raw('**');
32 }
33}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php
new file mode 100644
index 00000000..bea4f2a6
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Range.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Binary_Range extends Twig_Node_Expression_Binary
12{
13 /**
14 * Compiles the node to PHP.
15 *
16 * @param Twig_Compiler A Twig_Compiler instance
17 */
18 public function compile(Twig_Compiler $compiler)
19 {
20 $compiler
21 ->raw('range(')
22 ->subcompile($this->getNode('left'))
23 ->raw(', ')
24 ->subcompile($this->getNode('right'))
25 ->raw(')')
26 ;
27 }
28
29 public function operator(Twig_Compiler $compiler)
30 {
31 return $compiler->raw('..');
32 }
33}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php
new file mode 100644
index 00000000..d4463991
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Binary/Sub.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Binary_Sub extends Twig_Node_Expression_Binary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 return $compiler->raw('-');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php b/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php
new file mode 100644
index 00000000..647196eb
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/BlockReference.php
@@ -0,0 +1,51 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a block call node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_Expression_BlockReference extends Twig_Node_Expression
19{
20 public function __construct(Twig_NodeInterface $name, $asString = false, $lineno, $tag = null)
21 {
22 parent::__construct(array('name' => $name), array('as_string' => $asString, 'output' => false), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 if ($this->getAttribute('as_string')) {
33 $compiler->raw('(string) ');
34 }
35
36 if ($this->getAttribute('output')) {
37 $compiler
38 ->addDebugInfo($this)
39 ->write("\$this->displayBlock(")
40 ->subcompile($this->getNode('name'))
41 ->raw(", \$context, \$blocks);\n")
42 ;
43 } else {
44 $compiler
45 ->raw("\$this->renderBlock(")
46 ->subcompile($this->getNode('name'))
47 ->raw(", \$context, \$blocks)")
48 ;
49 }
50 }
51}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Call.php b/vendor/twig/twig/lib/Twig/Node/Expression/Call.php
new file mode 100644
index 00000000..dba9b0e6
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Call.php
@@ -0,0 +1,178 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11abstract class Twig_Node_Expression_Call extends Twig_Node_Expression
12{
13 protected function compileCallable(Twig_Compiler $compiler)
14 {
15 $callable = $this->getAttribute('callable');
16
17 $closingParenthesis = false;
18 if ($callable) {
19 if (is_string($callable)) {
20 $compiler->raw($callable);
21 } elseif (is_array($callable) && $callable[0] instanceof Twig_ExtensionInterface) {
22 $compiler->raw(sprintf('$this->env->getExtension(\'%s\')->%s', $callable[0]->getName(), $callable[1]));
23 } else {
24 $type = ucfirst($this->getAttribute('type'));
25 $compiler->raw(sprintf('call_user_func_array($this->env->get%s(\'%s\')->getCallable(), array', $type, $this->getAttribute('name')));
26 $closingParenthesis = true;
27 }
28 } else {
29 $compiler->raw($this->getAttribute('thing')->compile());
30 }
31
32 $this->compileArguments($compiler);
33
34 if ($closingParenthesis) {
35 $compiler->raw(')');
36 }
37 }
38
39 protected function compileArguments(Twig_Compiler $compiler)
40 {
41 $compiler->raw('(');
42
43 $first = true;
44
45 if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
46 $compiler->raw('$this->env');
47 $first = false;
48 }
49
50 if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
51 if (!$first) {
52 $compiler->raw(', ');
53 }
54 $compiler->raw('$context');
55 $first = false;
56 }
57
58 if ($this->hasAttribute('arguments')) {
59 foreach ($this->getAttribute('arguments') as $argument) {
60 if (!$first) {
61 $compiler->raw(', ');
62 }
63 $compiler->string($argument);
64 $first = false;
65 }
66 }
67
68 if ($this->hasNode('node')) {
69 if (!$first) {
70 $compiler->raw(', ');
71 }
72 $compiler->subcompile($this->getNode('node'));
73 $first = false;
74 }
75
76 if ($this->hasNode('arguments') && null !== $this->getNode('arguments')) {
77 $callable = $this->hasAttribute('callable') ? $this->getAttribute('callable') : null;
78
79 $arguments = $this->getArguments($callable, $this->getNode('arguments'));
80
81 foreach ($arguments as $node) {
82 if (!$first) {
83 $compiler->raw(', ');
84 }
85 $compiler->subcompile($node);
86 $first = false;
87 }
88 }
89
90 $compiler->raw(')');
91 }
92
93 protected function getArguments($callable, $arguments)
94 {
95 $parameters = array();
96 $named = false;
97 foreach ($arguments as $name => $node) {
98 if (!is_int($name)) {
99 $named = true;
100 $name = $this->normalizeName($name);
101 } elseif ($named) {
102 throw new Twig_Error_Syntax(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
103 }
104
105 $parameters[$name] = $node;
106 }
107
108 if (!$named) {
109 return $parameters;
110 }
111
112 if (!$callable) {
113 throw new LogicException(sprintf('Named arguments are not supported for %s "%s".', $this->getAttribute('type'), $this->getAttribute('name')));
114 }
115
116 // manage named arguments
117 if (is_array($callable)) {
118 $r = new ReflectionMethod($callable[0], $callable[1]);
119 } elseif (is_object($callable) && !$callable instanceof Closure) {
120 $r = new ReflectionObject($callable);
121 $r = $r->getMethod('__invoke');
122 } else {
123 $r = new ReflectionFunction($callable);
124 }
125
126 $definition = $r->getParameters();
127 if ($this->hasNode('node')) {
128 array_shift($definition);
129 }
130 if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) {
131 array_shift($definition);
132 }
133 if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) {
134 array_shift($definition);
135 }
136 if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) {
137 foreach ($this->getAttribute('arguments') as $argument) {
138 array_shift($definition);
139 }
140 }
141
142 $arguments = array();
143 $pos = 0;
144 foreach ($definition as $param) {
145 $name = $this->normalizeName($param->name);
146
147 if (array_key_exists($name, $parameters)) {
148 if (array_key_exists($pos, $parameters)) {
149 throw new Twig_Error_Syntax(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
150 }
151
152 $arguments[] = $parameters[$name];
153 unset($parameters[$name]);
154 } elseif (array_key_exists($pos, $parameters)) {
155 $arguments[] = $parameters[$pos];
156 unset($parameters[$pos]);
157 ++$pos;
158 } elseif ($param->isDefaultValueAvailable()) {
159 $arguments[] = new Twig_Node_Expression_Constant($param->getDefaultValue(), -1);
160 } elseif ($param->isOptional()) {
161 break;
162 } else {
163 throw new Twig_Error_Syntax(sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->getAttribute('type'), $this->getAttribute('name')));
164 }
165 }
166
167 if (!empty($parameters)) {
168 throw new Twig_Error_Syntax(sprintf('Unknown argument%s "%s" for %s "%s".', count($parameters) > 1 ? 's' : '' , implode('", "', array_keys($parameters)), $this->getAttribute('type'), $this->getAttribute('name')));
169 }
170
171 return $arguments;
172 }
173
174 protected function normalizeName($name)
175 {
176 return strtolower(preg_replace(array('/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'), array('\\1_\\2', '\\1_\\2'), $name));
177 }
178}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php b/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php
new file mode 100644
index 00000000..edcb1e2d
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Conditional.php
@@ -0,0 +1,31 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Conditional extends Twig_Node_Expression
13{
14 public function __construct(Twig_Node_Expression $expr1, Twig_Node_Expression $expr2, Twig_Node_Expression $expr3, $lineno)
15 {
16 parent::__construct(array('expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3), array(), $lineno);
17 }
18
19 public function compile(Twig_Compiler $compiler)
20 {
21 $compiler
22 ->raw('((')
23 ->subcompile($this->getNode('expr1'))
24 ->raw(') ? (')
25 ->subcompile($this->getNode('expr2'))
26 ->raw(') : (')
27 ->subcompile($this->getNode('expr3'))
28 ->raw('))')
29 ;
30 }
31}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php b/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php
new file mode 100644
index 00000000..a91dc698
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Constant.php
@@ -0,0 +1,23 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Constant extends Twig_Node_Expression
13{
14 public function __construct($value, $lineno)
15 {
16 parent::__construct(array(), array('value' => $value), $lineno);
17 }
18
19 public function compile(Twig_Compiler $compiler)
20 {
21 $compiler->repr($this->getAttribute('value'));
22 }
23}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php b/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php
new file mode 100644
index 00000000..00ac6701
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/ExtensionReference.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents an extension call node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Expression_ExtensionReference extends Twig_Node_Expression
18{
19 public function __construct($name, $lineno, $tag = null)
20 {
21 parent::__construct(array(), array('name' => $name), $lineno, $tag);
22 }
23
24 /**
25 * Compiles the node to PHP.
26 *
27 * @param Twig_Compiler A Twig_Compiler instance
28 */
29 public function compile(Twig_Compiler $compiler)
30 {
31 $compiler->raw(sprintf("\$this->env->getExtension('%s')", $this->getAttribute('name')));
32 }
33}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php b/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php
new file mode 100644
index 00000000..207b062a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Filter.php
@@ -0,0 +1,36 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Filter extends Twig_Node_Expression_Call
13{
14 public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
15 {
16 parent::__construct(array('node' => $node, 'filter' => $filterName, 'arguments' => $arguments), array(), $lineno, $tag);
17 }
18
19 public function compile(Twig_Compiler $compiler)
20 {
21 $name = $this->getNode('filter')->getAttribute('value');
22 $filter = $compiler->getEnvironment()->getFilter($name);
23
24 $this->setAttribute('name', $name);
25 $this->setAttribute('type', 'filter');
26 $this->setAttribute('thing', $filter);
27 $this->setAttribute('needs_environment', $filter->needsEnvironment());
28 $this->setAttribute('needs_context', $filter->needsContext());
29 $this->setAttribute('arguments', $filter->getArguments());
30 if ($filter instanceof Twig_FilterCallableInterface || $filter instanceof Twig_SimpleFilter) {
31 $this->setAttribute('callable', $filter->getCallable());
32 }
33
34 $this->compileCallable($compiler);
35 }
36}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php b/vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php
new file mode 100644
index 00000000..1827c888
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Filter/Default.php
@@ -0,0 +1,43 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Returns the value or the default value when it is undefined or empty.
14 *
15 * <pre>
16 * {{ var.foo|default('foo item on var is not defined') }}
17 * </pre>
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class Twig_Node_Expression_Filter_Default extends Twig_Node_Expression_Filter
22{
23 public function __construct(Twig_NodeInterface $node, Twig_Node_Expression_Constant $filterName, Twig_NodeInterface $arguments, $lineno, $tag = null)
24 {
25 $default = new Twig_Node_Expression_Filter($node, new Twig_Node_Expression_Constant('default', $node->getLine()), $arguments, $node->getLine());
26
27 if ('default' === $filterName->getAttribute('value') && ($node instanceof Twig_Node_Expression_Name || $node instanceof Twig_Node_Expression_GetAttr)) {
28 $test = new Twig_Node_Expression_Test_Defined(clone $node, 'defined', new Twig_Node(), $node->getLine());
29 $false = count($arguments) ? $arguments->getNode(0) : new Twig_Node_Expression_Constant('', $node->getLine());
30
31 $node = new Twig_Node_Expression_Conditional($test, $default, $false, $node->getLine());
32 } else {
33 $node = $default;
34 }
35
36 parent::__construct($node, $filterName, $arguments, $lineno, $tag);
37 }
38
39 public function compile(Twig_Compiler $compiler)
40 {
41 $compiler->subcompile($this->getNode('node'));
42 }
43}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Function.php b/vendor/twig/twig/lib/Twig/Node/Expression/Function.php
new file mode 100644
index 00000000..3e1f6b55
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Function.php
@@ -0,0 +1,35 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Function extends Twig_Node_Expression_Call
12{
13 public function __construct($name, Twig_NodeInterface $arguments, $lineno)
14 {
15 parent::__construct(array('arguments' => $arguments), array('name' => $name), $lineno);
16 }
17
18 public function compile(Twig_Compiler $compiler)
19 {
20 $name = $this->getAttribute('name');
21 $function = $compiler->getEnvironment()->getFunction($name);
22
23 $this->setAttribute('name', $name);
24 $this->setAttribute('type', 'function');
25 $this->setAttribute('thing', $function);
26 $this->setAttribute('needs_environment', $function->needsEnvironment());
27 $this->setAttribute('needs_context', $function->needsContext());
28 $this->setAttribute('arguments', $function->getArguments());
29 if ($function instanceof Twig_FunctionCallableInterface || $function instanceof Twig_SimpleFunction) {
30 $this->setAttribute('callable', $function->getCallable());
31 }
32
33 $this->compileCallable($compiler);
34 }
35}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php b/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php
new file mode 100644
index 00000000..81a9b137
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/GetAttr.php
@@ -0,0 +1,53 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_GetAttr extends Twig_Node_Expression
13{
14 public function __construct(Twig_Node_Expression $node, Twig_Node_Expression $attribute, Twig_Node_Expression_Array $arguments, $type, $lineno)
15 {
16 parent::__construct(array('node' => $node, 'attribute' => $attribute, 'arguments' => $arguments), array('type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'disable_c_ext' => false), $lineno);
17 }
18
19 public function compile(Twig_Compiler $compiler)
20 {
21 if (function_exists('twig_template_get_attributes') && !$this->getAttribute('disable_c_ext')) {
22 $compiler->raw('twig_template_get_attributes($this, ');
23 } else {
24 $compiler->raw('$this->getAttribute(');
25 }
26
27 if ($this->getAttribute('ignore_strict_check')) {
28 $this->getNode('node')->setAttribute('ignore_strict_check', true);
29 }
30
31 $compiler->subcompile($this->getNode('node'));
32
33 $compiler->raw(', ')->subcompile($this->getNode('attribute'));
34
35 if (count($this->getNode('arguments')) || Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
36 $compiler->raw(', ')->subcompile($this->getNode('arguments'));
37
38 if (Twig_TemplateInterface::ANY_CALL !== $this->getAttribute('type') || $this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
39 $compiler->raw(', ')->repr($this->getAttribute('type'));
40 }
41
42 if ($this->getAttribute('is_defined_test') || $this->getAttribute('ignore_strict_check')) {
43 $compiler->raw(', '.($this->getAttribute('is_defined_test') ? 'true' : 'false'));
44 }
45
46 if ($this->getAttribute('ignore_strict_check')) {
47 $compiler->raw(', '.($this->getAttribute('ignore_strict_check') ? 'true' : 'false'));
48 }
49 }
50
51 $compiler->raw(')');
52 }
53}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php b/vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php
new file mode 100644
index 00000000..620b02bf
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/MethodCall.php
@@ -0,0 +1,41 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_MethodCall extends Twig_Node_Expression
12{
13 public function __construct(Twig_Node_Expression $node, $method, Twig_Node_Expression_Array $arguments, $lineno)
14 {
15 parent::__construct(array('node' => $node, 'arguments' => $arguments), array('method' => $method, 'safe' => false), $lineno);
16
17 if ($node instanceof Twig_Node_Expression_Name) {
18 $node->setAttribute('always_defined', true);
19 }
20 }
21
22 public function compile(Twig_Compiler $compiler)
23 {
24 $compiler
25 ->subcompile($this->getNode('node'))
26 ->raw('->')
27 ->raw($this->getAttribute('method'))
28 ->raw('(')
29 ;
30 $first = true;
31 foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) {
32 if (!$first) {
33 $compiler->raw(', ');
34 }
35 $first = false;
36
37 $compiler->subcompile($pair['value']);
38 }
39 $compiler->raw(')');
40 }
41}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Name.php b/vendor/twig/twig/lib/Twig/Node/Expression/Name.php
new file mode 100644
index 00000000..3b8fae01
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Name.php
@@ -0,0 +1,88 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Name extends Twig_Node_Expression
13{
14 protected $specialVars = array(
15 '_self' => '$this',
16 '_context' => '$context',
17 '_charset' => '$this->env->getCharset()',
18 );
19
20 public function __construct($name, $lineno)
21 {
22 parent::__construct(array(), array('name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false), $lineno);
23 }
24
25 public function compile(Twig_Compiler $compiler)
26 {
27 $name = $this->getAttribute('name');
28
29 if ($this->getAttribute('is_defined_test')) {
30 if ($this->isSpecial()) {
31 $compiler->repr(true);
32 } else {
33 $compiler->raw('array_key_exists(')->repr($name)->raw(', $context)');
34 }
35 } elseif ($this->isSpecial()) {
36 $compiler->raw($this->specialVars[$name]);
37 } elseif ($this->getAttribute('always_defined')) {
38 $compiler
39 ->raw('$context[')
40 ->string($name)
41 ->raw(']')
42 ;
43 } else {
44 // remove the non-PHP 5.4 version when PHP 5.3 support is dropped
45 // as the non-optimized version is just a workaround for slow ternary operator
46 // when the context has a lot of variables
47 if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
48 // PHP 5.4 ternary operator performance was optimized
49 $compiler
50 ->raw('(isset($context[')
51 ->string($name)
52 ->raw(']) ? $context[')
53 ->string($name)
54 ->raw('] : ')
55 ;
56
57 if ($this->getAttribute('ignore_strict_check') || !$compiler->getEnvironment()->isStrictVariables()) {
58 $compiler->raw('null)');
59 } else {
60 $compiler->raw('$this->getContext($context, ')->string($name)->raw('))');
61 }
62 } else {
63 $compiler
64 ->raw('$this->getContext($context, ')
65 ->string($name)
66 ;
67
68 if ($this->getAttribute('ignore_strict_check')) {
69 $compiler->raw(', true');
70 }
71
72 $compiler
73 ->raw(')')
74 ;
75 }
76 }
77 }
78
79 public function isSpecial()
80 {
81 return isset($this->specialVars[$this->getAttribute('name')]);
82 }
83
84 public function isSimple()
85 {
86 return !$this->isSpecial() && !$this->getAttribute('is_defined_test');
87 }
88}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php b/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php
new file mode 100644
index 00000000..dcf618c0
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Parent.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a parent node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_Expression_Parent extends Twig_Node_Expression
19{
20 public function __construct($name, $lineno, $tag = null)
21 {
22 parent::__construct(array(), array('output' => false, 'name' => $name), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 if ($this->getAttribute('output')) {
33 $compiler
34 ->addDebugInfo($this)
35 ->write("\$this->displayParentBlock(")
36 ->string($this->getAttribute('name'))
37 ->raw(", \$context, \$blocks);\n")
38 ;
39 } else {
40 $compiler
41 ->raw("\$this->renderParentBlock(")
42 ->string($this->getAttribute('name'))
43 ->raw(", \$context, \$blocks)")
44 ;
45 }
46 }
47}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/TempName.php b/vendor/twig/twig/lib/Twig/Node/Expression/TempName.php
new file mode 100644
index 00000000..e6b058e8
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/TempName.php
@@ -0,0 +1,26 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_TempName extends Twig_Node_Expression
12{
13 public function __construct($name, $lineno)
14 {
15 parent::__construct(array(), array('name' => $name), $lineno);
16 }
17
18 public function compile(Twig_Compiler $compiler)
19 {
20 $compiler
21 ->raw('$_')
22 ->raw($this->getAttribute('name'))
23 ->raw('_')
24 ;
25 }
26}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test.php
new file mode 100644
index 00000000..639f501a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test.php
@@ -0,0 +1,32 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Node_Expression_Test extends Twig_Node_Expression_Call
12{
13 public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
14 {
15 parent::__construct(array('node' => $node, 'arguments' => $arguments), array('name' => $name), $lineno);
16 }
17
18 public function compile(Twig_Compiler $compiler)
19 {
20 $name = $this->getAttribute('name');
21 $test = $compiler->getEnvironment()->getTest($name);
22
23 $this->setAttribute('name', $name);
24 $this->setAttribute('type', 'test');
25 $this->setAttribute('thing', $test);
26 if ($test instanceof Twig_TestCallableInterface || $test instanceof Twig_SimpleTest) {
27 $this->setAttribute('callable', $test->getCallable());
28 }
29
30 $this->compileCallable($compiler);
31 }
32}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php
new file mode 100644
index 00000000..de55f5f5
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Constant.php
@@ -0,0 +1,46 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Checks if a variable is the exact same value as a constant.
14 *
15 * <pre>
16 * {% if post.status is constant('Post::PUBLISHED') %}
17 * the status attribute is exactly the same as Post::PUBLISHED
18 * {% endif %}
19 * </pre>
20 *
21 * @author Fabien Potencier <fabien@symfony.com>
22 */
23class Twig_Node_Expression_Test_Constant extends Twig_Node_Expression_Test
24{
25 public function compile(Twig_Compiler $compiler)
26 {
27 $compiler
28 ->raw('(')
29 ->subcompile($this->getNode('node'))
30 ->raw(' === constant(')
31 ;
32
33 if ($this->getNode('arguments')->hasNode(1)) {
34 $compiler
35 ->raw('get_class(')
36 ->subcompile($this->getNode('arguments')->getNode(1))
37 ->raw(')."::".')
38 ;
39 }
40
41 $compiler
42 ->subcompile($this->getNode('arguments')->getNode(0))
43 ->raw('))')
44 ;
45 }
46}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php
new file mode 100644
index 00000000..247b2e23
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Defined.php
@@ -0,0 +1,54 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Checks if a variable is defined in the current context.
14 *
15 * <pre>
16 * {# defined works with variable names and variable attributes #}
17 * {% if foo is defined %}
18 * {# ... #}
19 * {% endif %}
20 * </pre>
21 *
22 * @author Fabien Potencier <fabien@symfony.com>
23 */
24class Twig_Node_Expression_Test_Defined extends Twig_Node_Expression_Test
25{
26 public function __construct(Twig_NodeInterface $node, $name, Twig_NodeInterface $arguments = null, $lineno)
27 {
28 parent::__construct($node, $name, $arguments, $lineno);
29
30 if ($node instanceof Twig_Node_Expression_Name) {
31 $node->setAttribute('is_defined_test', true);
32 } elseif ($node instanceof Twig_Node_Expression_GetAttr) {
33 $node->setAttribute('is_defined_test', true);
34
35 $this->changeIgnoreStrictCheck($node);
36 } else {
37 throw new Twig_Error_Syntax('The "defined" test only works with simple variables', $this->getLine());
38 }
39 }
40
41 protected function changeIgnoreStrictCheck(Twig_Node_Expression_GetAttr $node)
42 {
43 $node->setAttribute('ignore_strict_check', true);
44
45 if ($node->getNode('node') instanceof Twig_Node_Expression_GetAttr) {
46 $this->changeIgnoreStrictCheck($node->getNode('node'));
47 }
48 }
49
50 public function compile(Twig_Compiler $compiler)
51 {
52 $compiler->subcompile($this->getNode('node'));
53 }
54}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php
new file mode 100644
index 00000000..0aceb530
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Divisibleby.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Checks if a variable is divisible by a number.
14 *
15 * <pre>
16 * {% if loop.index is divisibleby(3) %}
17 * </pre>
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class Twig_Node_Expression_Test_Divisibleby extends Twig_Node_Expression_Test
22{
23 public function compile(Twig_Compiler $compiler)
24 {
25 $compiler
26 ->raw('(0 == ')
27 ->subcompile($this->getNode('node'))
28 ->raw(' % ')
29 ->subcompile($this->getNode('arguments')->getNode(0))
30 ->raw(')')
31 ;
32 }
33}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php
new file mode 100644
index 00000000..d7853e89
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Even.php
@@ -0,0 +1,32 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Checks if a number is even.
14 *
15 * <pre>
16 * {{ var is even }}
17 * </pre>
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class Twig_Node_Expression_Test_Even extends Twig_Node_Expression_Test
22{
23 public function compile(Twig_Compiler $compiler)
24 {
25 $compiler
26 ->raw('(')
27 ->subcompile($this->getNode('node'))
28 ->raw(' % 2 == 0')
29 ->raw(')')
30 ;
31 }
32}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php
new file mode 100644
index 00000000..1c83825a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Null.php
@@ -0,0 +1,31 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Checks that a variable is null.
14 *
15 * <pre>
16 * {{ var is none }}
17 * </pre>
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class Twig_Node_Expression_Test_Null extends Twig_Node_Expression_Test
22{
23 public function compile(Twig_Compiler $compiler)
24 {
25 $compiler
26 ->raw('(null === ')
27 ->subcompile($this->getNode('node'))
28 ->raw(')')
29 ;
30 }
31}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php
new file mode 100644
index 00000000..421c19e8
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Odd.php
@@ -0,0 +1,32 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Checks if a number is odd.
14 *
15 * <pre>
16 * {{ var is odd }}
17 * </pre>
18 *
19 * @author Fabien Potencier <fabien@symfony.com>
20 */
21class Twig_Node_Expression_Test_Odd extends Twig_Node_Expression_Test
22{
23 public function compile(Twig_Compiler $compiler)
24 {
25 $compiler
26 ->raw('(')
27 ->subcompile($this->getNode('node'))
28 ->raw(' % 2 == 1')
29 ->raw(')')
30 ;
31 }
32}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php
new file mode 100644
index 00000000..b48905ee
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Test/Sameas.php
@@ -0,0 +1,29 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Checks if a variable is the same as another one (=== in PHP).
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Expression_Test_Sameas extends Twig_Node_Expression_Test
18{
19 public function compile(Twig_Compiler $compiler)
20 {
21 $compiler
22 ->raw('(')
23 ->subcompile($this->getNode('node'))
24 ->raw(' === ')
25 ->subcompile($this->getNode('arguments')->getNode(0))
26 ->raw(')')
27 ;
28 }
29}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php b/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php
new file mode 100644
index 00000000..c514388e
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Unary.php
@@ -0,0 +1,30 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12abstract class Twig_Node_Expression_Unary extends Twig_Node_Expression
13{
14 public function __construct(Twig_NodeInterface $node, $lineno)
15 {
16 parent::__construct(array('node' => $node), array(), $lineno);
17 }
18
19 public function compile(Twig_Compiler $compiler)
20 {
21 $compiler->raw('(');
22 $this->operator($compiler);
23 $compiler
24 ->subcompile($this->getNode('node'))
25 ->raw(')')
26 ;
27 }
28
29 abstract public function operator(Twig_Compiler $compiler);
30}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php b/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php
new file mode 100644
index 00000000..2a3937ec
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Neg.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Unary_Neg extends Twig_Node_Expression_Unary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 $compiler->raw('-');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php b/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php
new file mode 100644
index 00000000..f94073cf
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Not.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Unary_Not extends Twig_Node_Expression_Unary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 $compiler->raw('!');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php b/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php
new file mode 100644
index 00000000..04edb52a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Expression/Unary/Pos.php
@@ -0,0 +1,18 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12class Twig_Node_Expression_Unary_Pos extends Twig_Node_Expression_Unary
13{
14 public function operator(Twig_Compiler $compiler)
15 {
16 $compiler->raw('+');
17 }
18}
diff --git a/vendor/twig/twig/lib/Twig/Node/Flush.php b/vendor/twig/twig/lib/Twig/Node/Flush.php
new file mode 100644
index 00000000..0467ddce
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Flush.php
@@ -0,0 +1,36 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a flush node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Flush extends Twig_Node
18{
19 public function __construct($lineno, $tag)
20 {
21 parent::__construct(array(), array(), $lineno, $tag);
22 }
23
24 /**
25 * Compiles the node to PHP.
26 *
27 * @param Twig_Compiler A Twig_Compiler instance
28 */
29 public function compile(Twig_Compiler $compiler)
30 {
31 $compiler
32 ->addDebugInfo($this)
33 ->write("flush();\n")
34 ;
35 }
36}
diff --git a/vendor/twig/twig/lib/Twig/Node/For.php b/vendor/twig/twig/lib/Twig/Node/For.php
new file mode 100644
index 00000000..d1ff371d
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/For.php
@@ -0,0 +1,112 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a for node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_For extends Twig_Node
19{
20 protected $loop;
21
22 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)
23 {
24 $body = new Twig_Node(array($body, $this->loop = new Twig_Node_ForLoop($lineno, $tag)));
25
26 if (null !== $ifexpr) {
27 $body = new Twig_Node_If(new Twig_Node(array($ifexpr, $body)), null, $lineno, $tag);
28 }
29
30 parent::__construct(array('key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body, 'else' => $else), array('with_loop' => true, 'ifexpr' => null !== $ifexpr), $lineno, $tag);
31 }
32
33 /**
34 * Compiles the node to PHP.
35 *
36 * @param Twig_Compiler A Twig_Compiler instance
37 */
38 public function compile(Twig_Compiler $compiler)
39 {
40 $compiler
41 ->addDebugInfo($this)
42 // the (array) cast bypasses a PHP 5.2.6 bug
43 ->write("\$context['_parent'] = (array) \$context;\n")
44 ->write("\$context['_seq'] = twig_ensure_traversable(")
45 ->subcompile($this->getNode('seq'))
46 ->raw(");\n")
47 ;
48
49 if (null !== $this->getNode('else')) {
50 $compiler->write("\$context['_iterated'] = false;\n");
51 }
52
53 if ($this->getAttribute('with_loop')) {
54 $compiler
55 ->write("\$context['loop'] = array(\n")
56 ->write(" 'parent' => \$context['_parent'],\n")
57 ->write(" 'index0' => 0,\n")
58 ->write(" 'index' => 1,\n")
59 ->write(" 'first' => true,\n")
60 ->write(");\n")
61 ;
62
63 if (!$this->getAttribute('ifexpr')) {
64 $compiler
65 ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {\n")
66 ->indent()
67 ->write("\$length = count(\$context['_seq']);\n")
68 ->write("\$context['loop']['revindex0'] = \$length - 1;\n")
69 ->write("\$context['loop']['revindex'] = \$length;\n")
70 ->write("\$context['loop']['length'] = \$length;\n")
71 ->write("\$context['loop']['last'] = 1 === \$length;\n")
72 ->outdent()
73 ->write("}\n")
74 ;
75 }
76 }
77
78 $this->loop->setAttribute('else', null !== $this->getNode('else'));
79 $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop'));
80 $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr'));
81
82 $compiler
83 ->write("foreach (\$context['_seq'] as ")
84 ->subcompile($this->getNode('key_target'))
85 ->raw(" => ")
86 ->subcompile($this->getNode('value_target'))
87 ->raw(") {\n")
88 ->indent()
89 ->subcompile($this->getNode('body'))
90 ->outdent()
91 ->write("}\n")
92 ;
93
94 if (null !== $this->getNode('else')) {
95 $compiler
96 ->write("if (!\$context['_iterated']) {\n")
97 ->indent()
98 ->subcompile($this->getNode('else'))
99 ->outdent()
100 ->write("}\n")
101 ;
102 }
103
104 $compiler->write("\$_parent = \$context['_parent'];\n");
105
106 // remove some "private" loop variables (needed for nested loops)
107 $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");
108
109 // keep the values set in the inner context for variables defined in the outer context
110 $compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n");
111 }
112}
diff --git a/vendor/twig/twig/lib/Twig/Node/ForLoop.php b/vendor/twig/twig/lib/Twig/Node/ForLoop.php
new file mode 100644
index 00000000..b8841583
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/ForLoop.php
@@ -0,0 +1,55 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Internal node used by the for node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_ForLoop extends Twig_Node
18{
19 public function __construct($lineno, $tag = null)
20 {
21 parent::__construct(array(), array('with_loop' => false, 'ifexpr' => false, 'else' => false), $lineno, $tag);
22 }
23
24 /**
25 * Compiles the node to PHP.
26 *
27 * @param Twig_Compiler A Twig_Compiler instance
28 */
29 public function compile(Twig_Compiler $compiler)
30 {
31 if ($this->getAttribute('else')) {
32 $compiler->write("\$context['_iterated'] = true;\n");
33 }
34
35 if ($this->getAttribute('with_loop')) {
36 $compiler
37 ->write("++\$context['loop']['index0'];\n")
38 ->write("++\$context['loop']['index'];\n")
39 ->write("\$context['loop']['first'] = false;\n")
40 ;
41
42 if (!$this->getAttribute('ifexpr')) {
43 $compiler
44 ->write("if (isset(\$context['loop']['length'])) {\n")
45 ->indent()
46 ->write("--\$context['loop']['revindex0'];\n")
47 ->write("--\$context['loop']['revindex'];\n")
48 ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n")
49 ->outdent()
50 ->write("}\n")
51 ;
52 }
53 }
54 }
55}
diff --git a/vendor/twig/twig/lib/Twig/Node/If.php b/vendor/twig/twig/lib/Twig/Node/If.php
new file mode 100644
index 00000000..4296a8d6
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/If.php
@@ -0,0 +1,66 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents an if node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_If extends Twig_Node
19{
20 public function __construct(Twig_NodeInterface $tests, Twig_NodeInterface $else = null, $lineno, $tag = null)
21 {
22 parent::__construct(array('tests' => $tests, 'else' => $else), array(), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 $compiler->addDebugInfo($this);
33 for ($i = 0; $i < count($this->getNode('tests')); $i += 2) {
34 if ($i > 0) {
35 $compiler
36 ->outdent()
37 ->write("} elseif (")
38 ;
39 } else {
40 $compiler
41 ->write('if (')
42 ;
43 }
44
45 $compiler
46 ->subcompile($this->getNode('tests')->getNode($i))
47 ->raw(") {\n")
48 ->indent()
49 ->subcompile($this->getNode('tests')->getNode($i + 1))
50 ;
51 }
52
53 if ($this->hasNode('else') && null !== $this->getNode('else')) {
54 $compiler
55 ->outdent()
56 ->write("} else {\n")
57 ->indent()
58 ->subcompile($this->getNode('else'))
59 ;
60 }
61
62 $compiler
63 ->outdent()
64 ->write("}\n");
65 }
66}
diff --git a/vendor/twig/twig/lib/Twig/Node/Import.php b/vendor/twig/twig/lib/Twig/Node/Import.php
new file mode 100644
index 00000000..99efc091
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Import.php
@@ -0,0 +1,50 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents an import node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Import extends Twig_Node
18{
19 public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $var, $lineno, $tag = null)
20 {
21 parent::__construct(array('expr' => $expr, 'var' => $var), array(), $lineno, $tag);
22 }
23
24 /**
25 * Compiles the node to PHP.
26 *
27 * @param Twig_Compiler A Twig_Compiler instance
28 */
29 public function compile(Twig_Compiler $compiler)
30 {
31 $compiler
32 ->addDebugInfo($this)
33 ->write('')
34 ->subcompile($this->getNode('var'))
35 ->raw(' = ')
36 ;
37
38 if ($this->getNode('expr') instanceof Twig_Node_Expression_Name && '_self' === $this->getNode('expr')->getAttribute('name')) {
39 $compiler->raw("\$this");
40 } else {
41 $compiler
42 ->raw('$this->env->loadTemplate(')
43 ->subcompile($this->getNode('expr'))
44 ->raw(")")
45 ;
46 }
47
48 $compiler->raw(";\n");
49 }
50}
diff --git a/vendor/twig/twig/lib/Twig/Node/Include.php b/vendor/twig/twig/lib/Twig/Node/Include.php
new file mode 100644
index 00000000..ed4a3751
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Include.php
@@ -0,0 +1,99 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents an include node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_Include extends Twig_Node implements Twig_NodeOutputInterface
19{
20 public function __construct(Twig_Node_Expression $expr, Twig_Node_Expression $variables = null, $only = false, $ignoreMissing = false, $lineno, $tag = null)
21 {
22 parent::__construct(array('expr' => $expr, 'variables' => $variables), array('only' => (Boolean) $only, 'ignore_missing' => (Boolean) $ignoreMissing), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 $compiler->addDebugInfo($this);
33
34 if ($this->getAttribute('ignore_missing')) {
35 $compiler
36 ->write("try {\n")
37 ->indent()
38 ;
39 }
40
41 $this->addGetTemplate($compiler);
42
43 $compiler->raw('->display(');
44
45 $this->addTemplateArguments($compiler);
46
47 $compiler->raw(");\n");
48
49 if ($this->getAttribute('ignore_missing')) {
50 $compiler
51 ->outdent()
52 ->write("} catch (Twig_Error_Loader \$e) {\n")
53 ->indent()
54 ->write("// ignore missing template\n")
55 ->outdent()
56 ->write("}\n\n")
57 ;
58 }
59 }
60
61 protected function addGetTemplate(Twig_Compiler $compiler)
62 {
63 if ($this->getNode('expr') instanceof Twig_Node_Expression_Constant) {
64 $compiler
65 ->write("\$this->env->loadTemplate(")
66 ->subcompile($this->getNode('expr'))
67 ->raw(")")
68 ;
69 } else {
70 $compiler
71 ->write("\$template = \$this->env->resolveTemplate(")
72 ->subcompile($this->getNode('expr'))
73 ->raw(");\n")
74 ->write('$template')
75 ;
76 }
77 }
78
79 protected function addTemplateArguments(Twig_Compiler $compiler)
80 {
81 if (false === $this->getAttribute('only')) {
82 if (null === $this->getNode('variables')) {
83 $compiler->raw('$context');
84 } else {
85 $compiler
86 ->raw('array_merge($context, ')
87 ->subcompile($this->getNode('variables'))
88 ->raw(')')
89 ;
90 }
91 } else {
92 if (null === $this->getNode('variables')) {
93 $compiler->raw('array()');
94 } else {
95 $compiler->subcompile($this->getNode('variables'));
96 }
97 }
98 }
99}
diff --git a/vendor/twig/twig/lib/Twig/Node/Macro.php b/vendor/twig/twig/lib/Twig/Node/Macro.php
new file mode 100644
index 00000000..89910618
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Macro.php
@@ -0,0 +1,96 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a macro node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Macro extends Twig_Node
18{
19 public function __construct($name, Twig_NodeInterface $body, Twig_NodeInterface $arguments, $lineno, $tag = null)
20 {
21 parent::__construct(array('body' => $body, 'arguments' => $arguments), array('name' => $name), $lineno, $tag);
22 }
23
24 /**
25 * Compiles the node to PHP.
26 *
27 * @param Twig_Compiler A Twig_Compiler instance
28 */
29 public function compile(Twig_Compiler $compiler)
30 {
31 $compiler
32 ->addDebugInfo($this)
33 ->write(sprintf("public function get%s(", $this->getAttribute('name')))
34 ;
35
36 $count = count($this->getNode('arguments'));
37 $pos = 0;
38 foreach ($this->getNode('arguments') as $name => $default) {
39 $compiler
40 ->raw('$_'.$name.' = ')
41 ->subcompile($default)
42 ;
43
44 if (++$pos < $count) {
45 $compiler->raw(', ');
46 }
47 }
48
49 $compiler
50 ->raw(")\n")
51 ->write("{\n")
52 ->indent()
53 ;
54
55 if (!count($this->getNode('arguments'))) {
56 $compiler->write("\$context = \$this->env->getGlobals();\n\n");
57 } else {
58 $compiler
59 ->write("\$context = \$this->env->mergeGlobals(array(\n")
60 ->indent()
61 ;
62
63 foreach ($this->getNode('arguments') as $name => $default) {
64 $compiler
65 ->write('')
66 ->string($name)
67 ->raw(' => $_'.$name)
68 ->raw(",\n")
69 ;
70 }
71
72 $compiler
73 ->outdent()
74 ->write("));\n\n")
75 ;
76 }
77
78 $compiler
79 ->write("\$blocks = array();\n\n")
80 ->write("ob_start();\n")
81 ->write("try {\n")
82 ->indent()
83 ->subcompile($this->getNode('body'))
84 ->outdent()
85 ->write("} catch (Exception \$e) {\n")
86 ->indent()
87 ->write("ob_end_clean();\n\n")
88 ->write("throw \$e;\n")
89 ->outdent()
90 ->write("}\n\n")
91 ->write("return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());\n")
92 ->outdent()
93 ->write("}\n\n")
94 ;
95 }
96}
diff --git a/vendor/twig/twig/lib/Twig/Node/Module.php b/vendor/twig/twig/lib/Twig/Node/Module.php
new file mode 100644
index 00000000..585048b8
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Module.php
@@ -0,0 +1,371 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a module node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_Module extends Twig_Node
19{
20 public function __construct(Twig_NodeInterface $body, Twig_Node_Expression $parent = null, Twig_NodeInterface $blocks, Twig_NodeInterface $macros, Twig_NodeInterface $traits, $embeddedTemplates, $filename)
21 {
22 // embedded templates are set as attributes so that they are only visited once by the visitors
23 parent::__construct(array('parent' => $parent, 'body' => $body, 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits), array('filename' => $filename, 'index' => null, 'embedded_templates' => $embeddedTemplates), 1);
24 }
25
26 public function setIndex($index)
27 {
28 $this->setAttribute('index', $index);
29 }
30
31 /**
32 * Compiles the node to PHP.
33 *
34 * @param Twig_Compiler A Twig_Compiler instance
35 */
36 public function compile(Twig_Compiler $compiler)
37 {
38 $this->compileTemplate($compiler);
39
40 foreach ($this->getAttribute('embedded_templates') as $template) {
41 $compiler->subcompile($template);
42 }
43 }
44
45 protected function compileTemplate(Twig_Compiler $compiler)
46 {
47 if (!$this->getAttribute('index')) {
48 $compiler->write('<?php');
49 }
50
51 $this->compileClassHeader($compiler);
52
53 if (count($this->getNode('blocks')) || count($this->getNode('traits')) || null === $this->getNode('parent') || $this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
54 $this->compileConstructor($compiler);
55 }
56
57 $this->compileGetParent($compiler);
58
59 $this->compileDisplayHeader($compiler);
60
61 $this->compileDisplayBody($compiler);
62
63 $this->compileDisplayFooter($compiler);
64
65 $compiler->subcompile($this->getNode('blocks'));
66
67 $this->compileMacros($compiler);
68
69 $this->compileGetTemplateName($compiler);
70
71 $this->compileIsTraitable($compiler);
72
73 $this->compileDebugInfo($compiler);
74
75 $this->compileClassFooter($compiler);
76 }
77
78 protected function compileGetParent(Twig_Compiler $compiler)
79 {
80 if (null === $this->getNode('parent')) {
81 return;
82 }
83
84 $compiler
85 ->write("protected function doGetParent(array \$context)\n", "{\n")
86 ->indent()
87 ->write("return ")
88 ;
89
90 if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
91 $compiler->subcompile($this->getNode('parent'));
92 } else {
93 $compiler
94 ->raw("\$this->env->resolveTemplate(")
95 ->subcompile($this->getNode('parent'))
96 ->raw(")")
97 ;
98 }
99
100 $compiler
101 ->raw(";\n")
102 ->outdent()
103 ->write("}\n\n")
104 ;
105 }
106
107 protected function compileDisplayBody(Twig_Compiler $compiler)
108 {
109 $compiler->subcompile($this->getNode('body'));
110
111 if (null !== $this->getNode('parent')) {
112 if ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
113 $compiler->write("\$this->parent");
114 } else {
115 $compiler->write("\$this->getParent(\$context)");
116 }
117 $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n");
118 }
119 }
120
121 protected function compileClassHeader(Twig_Compiler $compiler)
122 {
123 $compiler
124 ->write("\n\n")
125 // if the filename contains */, add a blank to avoid a PHP parse error
126 ->write("/* ".str_replace('*/', '* /', $this->getAttribute('filename'))." */\n")
127 ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getAttribute('filename'), $this->getAttribute('index')))
128 ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass()))
129 ->write("{\n")
130 ->indent()
131 ;
132 }
133
134 protected function compileConstructor(Twig_Compiler $compiler)
135 {
136 $compiler
137 ->write("public function __construct(Twig_Environment \$env)\n", "{\n")
138 ->indent()
139 ->write("parent::__construct(\$env);\n\n")
140 ;
141
142 // parent
143 if (null === $this->getNode('parent')) {
144 $compiler->write("\$this->parent = false;\n\n");
145 } elseif ($this->getNode('parent') instanceof Twig_Node_Expression_Constant) {
146 $compiler
147 ->write("\$this->parent = \$this->env->loadTemplate(")
148 ->subcompile($this->getNode('parent'))
149 ->raw(");\n\n")
150 ;
151 }
152
153 $countTraits = count($this->getNode('traits'));
154 if ($countTraits) {
155 // traits
156 foreach ($this->getNode('traits') as $i => $trait) {
157 $this->compileLoadTemplate($compiler, $trait->getNode('template'), sprintf('$_trait_%s', $i));
158
159 $compiler
160 ->addDebugInfo($trait->getNode('template'))
161 ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i))
162 ->indent()
163 ->write("throw new Twig_Error_Runtime('Template \"'.")
164 ->subcompile($trait->getNode('template'))
165 ->raw(".'\" cannot be used as a trait.');\n")
166 ->outdent()
167 ->write("}\n")
168 ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i))
169 ;
170
171 foreach ($trait->getNode('targets') as $key => $value) {
172 $compiler
173 ->write(sprintf("\$_trait_%s_blocks[", $i))
174 ->subcompile($value)
175 ->raw(sprintf("] = \$_trait_%s_blocks[", $i))
176 ->string($key)
177 ->raw(sprintf("]; unset(\$_trait_%s_blocks[", $i))
178 ->string($key)
179 ->raw("]);\n\n")
180 ;
181 }
182 }
183
184 if ($countTraits > 1) {
185 $compiler
186 ->write("\$this->traits = array_merge(\n")
187 ->indent()
188 ;
189
190 for ($i = 0; $i < $countTraits; $i++) {
191 $compiler
192 ->write(sprintf("\$_trait_%s_blocks".($i == $countTraits - 1 ? '' : ',')."\n", $i))
193 ;
194 }
195
196 $compiler
197 ->outdent()
198 ->write(");\n\n")
199 ;
200 } else {
201 $compiler
202 ->write("\$this->traits = \$_trait_0_blocks;\n\n")
203 ;
204 }
205
206 $compiler
207 ->write("\$this->blocks = array_merge(\n")
208 ->indent()
209 ->write("\$this->traits,\n")
210 ->write("array(\n")
211 ;
212 } else {
213 $compiler
214 ->write("\$this->blocks = array(\n")
215 ;
216 }
217
218 // blocks
219 $compiler
220 ->indent()
221 ;
222
223 foreach ($this->getNode('blocks') as $name => $node) {
224 $compiler
225 ->write(sprintf("'%s' => array(\$this, 'block_%s'),\n", $name, $name))
226 ;
227 }
228
229 if ($countTraits) {
230 $compiler
231 ->outdent()
232 ->write(")\n")
233 ;
234 }
235
236 $compiler
237 ->outdent()
238 ->write(");\n")
239 ->outdent()
240 ->write("}\n\n");
241 ;
242 }
243
244 protected function compileDisplayHeader(Twig_Compiler $compiler)
245 {
246 $compiler
247 ->write("protected function doDisplay(array \$context, array \$blocks = array())\n", "{\n")
248 ->indent()
249 ;
250 }
251
252 protected function compileDisplayFooter(Twig_Compiler $compiler)
253 {
254 $compiler
255 ->outdent()
256 ->write("}\n\n")
257 ;
258 }
259
260 protected function compileClassFooter(Twig_Compiler $compiler)
261 {
262 $compiler
263 ->outdent()
264 ->write("}\n")
265 ;
266 }
267
268 protected function compileMacros(Twig_Compiler $compiler)
269 {
270 $compiler->subcompile($this->getNode('macros'));
271 }
272
273 protected function compileGetTemplateName(Twig_Compiler $compiler)
274 {
275 $compiler
276 ->write("public function getTemplateName()\n", "{\n")
277 ->indent()
278 ->write('return ')
279 ->repr($this->getAttribute('filename'))
280 ->raw(";\n")
281 ->outdent()
282 ->write("}\n\n")
283 ;
284 }
285
286 protected function compileIsTraitable(Twig_Compiler $compiler)
287 {
288 // A template can be used as a trait if:
289 // * it has no parent
290 // * it has no macros
291 // * it has no body
292 //
293 // Put another way, a template can be used as a trait if it
294 // only contains blocks and use statements.
295 $traitable = null === $this->getNode('parent') && 0 === count($this->getNode('macros'));
296 if ($traitable) {
297 if ($this->getNode('body') instanceof Twig_Node_Body) {
298 $nodes = $this->getNode('body')->getNode(0);
299 } else {
300 $nodes = $this->getNode('body');
301 }
302
303 if (!count($nodes)) {
304 $nodes = new Twig_Node(array($nodes));
305 }
306
307 foreach ($nodes as $node) {
308 if (!count($node)) {
309 continue;
310 }
311
312 if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
313 continue;
314 }
315
316 if ($node instanceof Twig_Node_BlockReference) {
317 continue;
318 }
319
320 $traitable = false;
321 break;
322 }
323 }
324
325 if ($traitable) {
326 return;
327 }
328
329 $compiler
330 ->write("public function isTraitable()\n", "{\n")
331 ->indent()
332 ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false'))
333 ->outdent()
334 ->write("}\n\n")
335 ;
336 }
337
338 protected function compileDebugInfo(Twig_Compiler $compiler)
339 {
340 $compiler
341 ->write("public function getDebugInfo()\n", "{\n")
342 ->indent()
343 ->write(sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true))))
344 ->outdent()
345 ->write("}\n")
346 ;
347 }
348
349 protected function compileLoadTemplate(Twig_Compiler $compiler, $node, $var)
350 {
351 if ($node instanceof Twig_Node_Expression_Constant) {
352 $compiler
353 ->write(sprintf("%s = \$this->env->loadTemplate(", $var))
354 ->subcompile($node)
355 ->raw(");\n")
356 ;
357 } else {
358 $compiler
359 ->write(sprintf("%s = ", $var))
360 ->subcompile($node)
361 ->raw(";\n")
362 ->write(sprintf("if (!%s", $var))
363 ->raw(" instanceof Twig_Template) {\n")
364 ->indent()
365 ->write(sprintf("%s = \$this->env->loadTemplate(%s);\n", $var, $var))
366 ->outdent()
367 ->write("}\n")
368 ;
369 }
370 }
371}
diff --git a/vendor/twig/twig/lib/Twig/Node/Print.php b/vendor/twig/twig/lib/Twig/Node/Print.php
new file mode 100644
index 00000000..b0c41d1d
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Print.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a node that outputs an expression.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_Print extends Twig_Node implements Twig_NodeOutputInterface
19{
20 public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
21 {
22 parent::__construct(array('expr' => $expr), array(), $lineno, $tag);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 $compiler
33 ->addDebugInfo($this)
34 ->write('echo ')
35 ->subcompile($this->getNode('expr'))
36 ->raw(";\n")
37 ;
38 }
39}
diff --git a/vendor/twig/twig/lib/Twig/Node/Sandbox.php b/vendor/twig/twig/lib/Twig/Node/Sandbox.php
new file mode 100644
index 00000000..8cf3ed44
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Sandbox.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a sandbox node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Sandbox extends Twig_Node
18{
19 public function __construct(Twig_NodeInterface $body, $lineno, $tag = null)
20 {
21 parent::__construct(array('body' => $body), array(), $lineno, $tag);
22 }
23
24 /**
25 * Compiles the node to PHP.
26 *
27 * @param Twig_Compiler A Twig_Compiler instance
28 */
29 public function compile(Twig_Compiler $compiler)
30 {
31 $compiler
32 ->addDebugInfo($this)
33 ->write("\$sandbox = \$this->env->getExtension('sandbox');\n")
34 ->write("if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {\n")
35 ->indent()
36 ->write("\$sandbox->enableSandbox();\n")
37 ->outdent()
38 ->write("}\n")
39 ->subcompile($this->getNode('body'))
40 ->write("if (!\$alreadySandboxed) {\n")
41 ->indent()
42 ->write("\$sandbox->disableSandbox();\n")
43 ->outdent()
44 ->write("}\n")
45 ;
46 }
47}
diff --git a/vendor/twig/twig/lib/Twig/Node/SandboxedModule.php b/vendor/twig/twig/lib/Twig/Node/SandboxedModule.php
new file mode 100644
index 00000000..be1f5daa
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/SandboxedModule.php
@@ -0,0 +1,60 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a module node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_SandboxedModule extends Twig_Node_Module
19{
20 protected $usedFilters;
21 protected $usedTags;
22 protected $usedFunctions;
23
24 public function __construct(Twig_Node_Module $node, array $usedFilters, array $usedTags, array $usedFunctions)
25 {
26 parent::__construct($node->getNode('body'), $node->getNode('parent'), $node->getNode('blocks'), $node->getNode('macros'), $node->getNode('traits'), $node->getAttribute('embedded_templates'), $node->getAttribute('filename'), $node->getLine(), $node->getNodeTag());
27
28 $this->setAttribute('index', $node->getAttribute('index'));
29
30 $this->usedFilters = $usedFilters;
31 $this->usedTags = $usedTags;
32 $this->usedFunctions = $usedFunctions;
33 }
34
35 protected function compileDisplayBody(Twig_Compiler $compiler)
36 {
37 $compiler->write("\$this->checkSecurity();\n");
38
39 parent::compileDisplayBody($compiler);
40 }
41
42 protected function compileDisplayFooter(Twig_Compiler $compiler)
43 {
44 parent::compileDisplayFooter($compiler);
45
46 $compiler
47 ->write("protected function checkSecurity()\n", "{\n")
48 ->indent()
49 ->write("\$this->env->getExtension('sandbox')->checkSecurity(\n")
50 ->indent()
51 ->write(!$this->usedTags ? "array(),\n" : "array('".implode('\', \'', $this->usedTags)."'),\n")
52 ->write(!$this->usedFilters ? "array(),\n" : "array('".implode('\', \'', $this->usedFilters)."'),\n")
53 ->write(!$this->usedFunctions ? "array()\n" : "array('".implode('\', \'', $this->usedFunctions)."')\n")
54 ->outdent()
55 ->write(");\n")
56 ->outdent()
57 ->write("}\n\n")
58 ;
59 }
60}
diff --git a/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php b/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php
new file mode 100644
index 00000000..73dfaa96
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/SandboxedPrint.php
@@ -0,0 +1,59 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig_Node_SandboxedPrint adds a check for the __toString() method
14 * when the variable is an object and the sandbox is activated.
15 *
16 * When there is a simple Print statement, like {{ article }},
17 * and if the sandbox is enabled, we need to check that the __toString()
18 * method is allowed if 'article' is an object.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 */
22class Twig_Node_SandboxedPrint extends Twig_Node_Print
23{
24 public function __construct(Twig_Node_Expression $expr, $lineno, $tag = null)
25 {
26 parent::__construct($expr, $lineno, $tag);
27 }
28
29 /**
30 * Compiles the node to PHP.
31 *
32 * @param Twig_Compiler A Twig_Compiler instance
33 */
34 public function compile(Twig_Compiler $compiler)
35 {
36 $compiler
37 ->addDebugInfo($this)
38 ->write('echo $this->env->getExtension(\'sandbox\')->ensureToStringAllowed(')
39 ->subcompile($this->getNode('expr'))
40 ->raw(");\n")
41 ;
42 }
43
44 /**
45 * Removes node filters.
46 *
47 * This is mostly needed when another visitor adds filters (like the escaper one).
48 *
49 * @param Twig_Node $node A Node
50 */
51 protected function removeNodeFilter($node)
52 {
53 if ($node instanceof Twig_Node_Expression_Filter) {
54 return $this->removeNodeFilter($node->getNode('node'));
55 }
56
57 return $node;
58 }
59}
diff --git a/vendor/twig/twig/lib/Twig/Node/Set.php b/vendor/twig/twig/lib/Twig/Node/Set.php
new file mode 100644
index 00000000..4c9c16ce
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Set.php
@@ -0,0 +1,101 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a set node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Node_Set extends Twig_Node
18{
19 public function __construct($capture, Twig_NodeInterface $names, Twig_NodeInterface $values, $lineno, $tag = null)
20 {
21 parent::__construct(array('names' => $names, 'values' => $values), array('capture' => $capture, 'safe' => false), $lineno, $tag);
22
23 /*
24 * Optimizes the node when capture is used for a large block of text.
25 *
26 * {% set foo %}foo{% endset %} is compiled to $context['foo'] = new Twig_Markup("foo");
27 */
28 if ($this->getAttribute('capture')) {
29 $this->setAttribute('safe', true);
30
31 $values = $this->getNode('values');
32 if ($values instanceof Twig_Node_Text) {
33 $this->setNode('values', new Twig_Node_Expression_Constant($values->getAttribute('data'), $values->getLine()));
34 $this->setAttribute('capture', false);
35 }
36 }
37 }
38
39 /**
40 * Compiles the node to PHP.
41 *
42 * @param Twig_Compiler A Twig_Compiler instance
43 */
44 public function compile(Twig_Compiler $compiler)
45 {
46 $compiler->addDebugInfo($this);
47
48 if (count($this->getNode('names')) > 1) {
49 $compiler->write('list(');
50 foreach ($this->getNode('names') as $idx => $node) {
51 if ($idx) {
52 $compiler->raw(', ');
53 }
54
55 $compiler->subcompile($node);
56 }
57 $compiler->raw(')');
58 } else {
59 if ($this->getAttribute('capture')) {
60 $compiler
61 ->write("ob_start();\n")
62 ->subcompile($this->getNode('values'))
63 ;
64 }
65
66 $compiler->subcompile($this->getNode('names'), false);
67
68 if ($this->getAttribute('capture')) {
69 $compiler->raw(" = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())");
70 }
71 }
72
73 if (!$this->getAttribute('capture')) {
74 $compiler->raw(' = ');
75
76 if (count($this->getNode('names')) > 1) {
77 $compiler->write('array(');
78 foreach ($this->getNode('values') as $idx => $value) {
79 if ($idx) {
80 $compiler->raw(', ');
81 }
82
83 $compiler->subcompile($value);
84 }
85 $compiler->raw(')');
86 } else {
87 if ($this->getAttribute('safe')) {
88 $compiler
89 ->raw("('' === \$tmp = ")
90 ->subcompile($this->getNode('values'))
91 ->raw(") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset())")
92 ;
93 } else {
94 $compiler->subcompile($this->getNode('values'));
95 }
96 }
97 }
98
99 $compiler->raw(";\n");
100 }
101}
diff --git a/vendor/twig/twig/lib/Twig/Node/SetTemp.php b/vendor/twig/twig/lib/Twig/Node/SetTemp.php
new file mode 100644
index 00000000..3bdd1cb7
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/SetTemp.php
@@ -0,0 +1,35 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Node_SetTemp extends Twig_Node
13{
14 public function __construct($name, $lineno)
15 {
16 parent::__construct(array(), array('name' => $name), $lineno);
17 }
18
19 public function compile(Twig_Compiler $compiler)
20 {
21 $name = $this->getAttribute('name');
22 $compiler
23 ->addDebugInfo($this)
24 ->write('if (isset($context[')
25 ->string($name)
26 ->raw('])) { $_')
27 ->raw($name)
28 ->raw('_ = $context[')
29 ->repr($name)
30 ->raw(']; } else { $_')
31 ->raw($name)
32 ->raw("_ = null; }\n")
33 ;
34 }
35}
diff --git a/vendor/twig/twig/lib/Twig/Node/Spaceless.php b/vendor/twig/twig/lib/Twig/Node/Spaceless.php
new file mode 100644
index 00000000..7555fa0f
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Spaceless.php
@@ -0,0 +1,40 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a spaceless node.
14 *
15 * It removes spaces between HTML tags.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19class Twig_Node_Spaceless extends Twig_Node
20{
21 public function __construct(Twig_NodeInterface $body, $lineno, $tag = 'spaceless')
22 {
23 parent::__construct(array('body' => $body), array(), $lineno, $tag);
24 }
25
26 /**
27 * Compiles the node to PHP.
28 *
29 * @param Twig_Compiler A Twig_Compiler instance
30 */
31 public function compile(Twig_Compiler $compiler)
32 {
33 $compiler
34 ->addDebugInfo($this)
35 ->write("ob_start();\n")
36 ->subcompile($this->getNode('body'))
37 ->write("echo trim(preg_replace('/>\s+</', '><', ob_get_clean()));\n")
38 ;
39 }
40}
diff --git a/vendor/twig/twig/lib/Twig/Node/Text.php b/vendor/twig/twig/lib/Twig/Node/Text.php
new file mode 100644
index 00000000..21bdcea1
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Node/Text.php
@@ -0,0 +1,39 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a text node.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Node_Text extends Twig_Node implements Twig_NodeOutputInterface
19{
20 public function __construct($data, $lineno)
21 {
22 parent::__construct(array(), array('data' => $data), $lineno);
23 }
24
25 /**
26 * Compiles the node to PHP.
27 *
28 * @param Twig_Compiler A Twig_Compiler instance
29 */
30 public function compile(Twig_Compiler $compiler)
31 {
32 $compiler
33 ->addDebugInfo($this)
34 ->write('echo ')
35 ->string($this->getAttribute('data'))
36 ->raw(";\n")
37 ;
38 }
39}
diff --git a/vendor/twig/twig/lib/Twig/NodeInterface.php b/vendor/twig/twig/lib/Twig/NodeInterface.php
new file mode 100644
index 00000000..f0ef7258
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeInterface.php
@@ -0,0 +1,30 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a node in the AST.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_NodeInterface extends Countable, IteratorAggregate
19{
20 /**
21 * Compiles the node to PHP.
22 *
23 * @param Twig_Compiler A Twig_Compiler instance
24 */
25 public function compile(Twig_Compiler $compiler);
26
27 public function getLine();
28
29 public function getNodeTag();
30}
diff --git a/vendor/twig/twig/lib/Twig/NodeOutputInterface.php b/vendor/twig/twig/lib/Twig/NodeOutputInterface.php
new file mode 100644
index 00000000..22172c09
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeOutputInterface.php
@@ -0,0 +1,19 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a displayable node in the AST.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17interface Twig_NodeOutputInterface
18{
19}
diff --git a/vendor/twig/twig/lib/Twig/NodeTraverser.php b/vendor/twig/twig/lib/Twig/NodeTraverser.php
new file mode 100644
index 00000000..28cba1ad
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeTraverser.php
@@ -0,0 +1,88 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig_NodeTraverser is a node traverser.
14 *
15 * It visits all nodes and their children and call the given visitor for each.
16 *
17 * @author Fabien Potencier <fabien@symfony.com>
18 */
19class Twig_NodeTraverser
20{
21 protected $env;
22 protected $visitors;
23
24 /**
25 * Constructor.
26 *
27 * @param Twig_Environment $env A Twig_Environment instance
28 * @param array $visitors An array of Twig_NodeVisitorInterface instances
29 */
30 public function __construct(Twig_Environment $env, array $visitors = array())
31 {
32 $this->env = $env;
33 $this->visitors = array();
34 foreach ($visitors as $visitor) {
35 $this->addVisitor($visitor);
36 }
37 }
38
39 /**
40 * Adds a visitor.
41 *
42 * @param Twig_NodeVisitorInterface $visitor A Twig_NodeVisitorInterface instance
43 */
44 public function addVisitor(Twig_NodeVisitorInterface $visitor)
45 {
46 if (!isset($this->visitors[$visitor->getPriority()])) {
47 $this->visitors[$visitor->getPriority()] = array();
48 }
49
50 $this->visitors[$visitor->getPriority()][] = $visitor;
51 }
52
53 /**
54 * Traverses a node and calls the registered visitors.
55 *
56 * @param Twig_NodeInterface $node A Twig_NodeInterface instance
57 */
58 public function traverse(Twig_NodeInterface $node)
59 {
60 ksort($this->visitors);
61 foreach ($this->visitors as $visitors) {
62 foreach ($visitors as $visitor) {
63 $node = $this->traverseForVisitor($visitor, $node);
64 }
65 }
66
67 return $node;
68 }
69
70 protected function traverseForVisitor(Twig_NodeVisitorInterface $visitor, Twig_NodeInterface $node = null)
71 {
72 if (null === $node) {
73 return null;
74 }
75
76 $node = $visitor->enterNode($node, $this->env);
77
78 foreach ($node as $k => $n) {
79 if (false !== $n = $this->traverseForVisitor($visitor, $n)) {
80 $node->setNode($k, $n);
81 } else {
82 $node->removeNode($k);
83 }
84 }
85
86 return $visitor->leaveNode($node, $this->env);
87 }
88}
diff --git a/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php b/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php
new file mode 100644
index 00000000..cc4b3d71
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeVisitor/Escaper.php
@@ -0,0 +1,167 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig_NodeVisitor_Escaper implements output escaping.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_NodeVisitor_Escaper implements Twig_NodeVisitorInterface
18{
19 protected $statusStack = array();
20 protected $blocks = array();
21 protected $safeAnalysis;
22 protected $traverser;
23 protected $defaultStrategy = false;
24 protected $safeVars = array();
25
26 public function __construct()
27 {
28 $this->safeAnalysis = new Twig_NodeVisitor_SafeAnalysis();
29 }
30
31 /**
32 * Called before child nodes are visited.
33 *
34 * @param Twig_NodeInterface $node The node to visit
35 * @param Twig_Environment $env The Twig environment instance
36 *
37 * @return Twig_NodeInterface The modified node
38 */
39 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
40 {
41 if ($node instanceof Twig_Node_Module) {
42 if ($env->hasExtension('escaper') && $defaultStrategy = $env->getExtension('escaper')->getDefaultStrategy($node->getAttribute('filename'))) {
43 $this->defaultStrategy = $defaultStrategy;
44 }
45 $this->safeVars = array();
46 } elseif ($node instanceof Twig_Node_AutoEscape) {
47 $this->statusStack[] = $node->getAttribute('value');
48 } elseif ($node instanceof Twig_Node_Block) {
49 $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env);
50 } elseif ($node instanceof Twig_Node_Import) {
51 $this->safeVars[] = $node->getNode('var')->getAttribute('name');
52 }
53
54 return $node;
55 }
56
57 /**
58 * Called after child nodes are visited.
59 *
60 * @param Twig_NodeInterface $node The node to visit
61 * @param Twig_Environment $env The Twig environment instance
62 *
63 * @return Twig_NodeInterface The modified node
64 */
65 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
66 {
67 if ($node instanceof Twig_Node_Module) {
68 $this->defaultStrategy = false;
69 $this->safeVars = array();
70 } elseif ($node instanceof Twig_Node_Expression_Filter) {
71 return $this->preEscapeFilterNode($node, $env);
72 } elseif ($node instanceof Twig_Node_Print) {
73 return $this->escapePrintNode($node, $env, $this->needEscaping($env));
74 }
75
76 if ($node instanceof Twig_Node_AutoEscape || $node instanceof Twig_Node_Block) {
77 array_pop($this->statusStack);
78 } elseif ($node instanceof Twig_Node_BlockReference) {
79 $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env);
80 }
81
82 return $node;
83 }
84
85 protected function escapePrintNode(Twig_Node_Print $node, Twig_Environment $env, $type)
86 {
87 if (false === $type) {
88 return $node;
89 }
90
91 $expression = $node->getNode('expr');
92
93 if ($this->isSafeFor($type, $expression, $env)) {
94 return $node;
95 }
96
97 $class = get_class($node);
98
99 return new $class(
100 $this->getEscaperFilter($type, $expression),
101 $node->getLine()
102 );
103 }
104
105 protected function preEscapeFilterNode(Twig_Node_Expression_Filter $filter, Twig_Environment $env)
106 {
107 $name = $filter->getNode('filter')->getAttribute('value');
108
109 $type = $env->getFilter($name)->getPreEscape();
110 if (null === $type) {
111 return $filter;
112 }
113
114 $node = $filter->getNode('node');
115 if ($this->isSafeFor($type, $node, $env)) {
116 return $filter;
117 }
118
119 $filter->setNode('node', $this->getEscaperFilter($type, $node));
120
121 return $filter;
122 }
123
124 protected function isSafeFor($type, Twig_NodeInterface $expression, $env)
125 {
126 $safe = $this->safeAnalysis->getSafe($expression);
127
128 if (null === $safe) {
129 if (null === $this->traverser) {
130 $this->traverser = new Twig_NodeTraverser($env, array($this->safeAnalysis));
131 }
132
133 $this->safeAnalysis->setSafeVars($this->safeVars);
134
135 $this->traverser->traverse($expression);
136 $safe = $this->safeAnalysis->getSafe($expression);
137 }
138
139 return in_array($type, $safe) || in_array('all', $safe);
140 }
141
142 protected function needEscaping(Twig_Environment $env)
143 {
144 if (count($this->statusStack)) {
145 return $this->statusStack[count($this->statusStack) - 1];
146 }
147
148 return $this->defaultStrategy ? $this->defaultStrategy : false;
149 }
150
151 protected function getEscaperFilter($type, Twig_NodeInterface $node)
152 {
153 $line = $node->getLine();
154 $name = new Twig_Node_Expression_Constant('escape', $line);
155 $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)));
156
157 return new Twig_Node_Expression_Filter($node, $name, $args, $line);
158 }
159
160 /**
161 * {@inheritdoc}
162 */
163 public function getPriority()
164 {
165 return 0;
166 }
167}
diff --git a/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php b/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php
new file mode 100644
index 00000000..a254def7
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeVisitor/Optimizer.php
@@ -0,0 +1,246 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig_NodeVisitor_Optimizer tries to optimizes the AST.
14 *
15 * This visitor is always the last registered one.
16 *
17 * You can configure which optimizations you want to activate via the
18 * optimizer mode.
19 *
20 * @author Fabien Potencier <fabien@symfony.com>
21 */
22class Twig_NodeVisitor_Optimizer implements Twig_NodeVisitorInterface
23{
24 const OPTIMIZE_ALL = -1;
25 const OPTIMIZE_NONE = 0;
26 const OPTIMIZE_FOR = 2;
27 const OPTIMIZE_RAW_FILTER = 4;
28 const OPTIMIZE_VAR_ACCESS = 8;
29
30 protected $loops = array();
31 protected $optimizers;
32 protected $prependedNodes = array();
33 protected $inABody = false;
34
35 /**
36 * Constructor.
37 *
38 * @param integer $optimizers The optimizer mode
39 */
40 public function __construct($optimizers = -1)
41 {
42 if (!is_int($optimizers) || $optimizers > 2) {
43 throw new InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers));
44 }
45
46 $this->optimizers = $optimizers;
47 }
48
49 /**
50 * {@inheritdoc}
51 */
52 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
53 {
54 if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
55 $this->enterOptimizeFor($node, $env);
56 }
57
58 if (!version_compare(phpversion(), '5.4.0RC1', '>=') && self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
59 if ($this->inABody) {
60 if (!$node instanceof Twig_Node_Expression) {
61 if (get_class($node) !== 'Twig_Node') {
62 array_unshift($this->prependedNodes, array());
63 }
64 } else {
65 $node = $this->optimizeVariables($node, $env);
66 }
67 } elseif ($node instanceof Twig_Node_Body) {
68 $this->inABody = true;
69 }
70 }
71
72 return $node;
73 }
74
75 /**
76 * {@inheritdoc}
77 */
78 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
79 {
80 $expression = $node instanceof Twig_Node_Expression;
81
82 if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) {
83 $this->leaveOptimizeFor($node, $env);
84 }
85
86 if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) {
87 $node = $this->optimizeRawFilter($node, $env);
88 }
89
90 $node = $this->optimizePrintNode($node, $env);
91
92 if (self::OPTIMIZE_VAR_ACCESS === (self::OPTIMIZE_VAR_ACCESS & $this->optimizers) && !$env->isStrictVariables() && !$env->hasExtension('sandbox')) {
93 if ($node instanceof Twig_Node_Body) {
94 $this->inABody = false;
95 } elseif ($this->inABody) {
96 if (!$expression && get_class($node) !== 'Twig_Node' && $prependedNodes = array_shift($this->prependedNodes)) {
97 $nodes = array();
98 foreach (array_unique($prependedNodes) as $name) {
99 $nodes[] = new Twig_Node_SetTemp($name, $node->getLine());
100 }
101
102 $nodes[] = $node;
103 $node = new Twig_Node($nodes);
104 }
105 }
106 }
107
108 return $node;
109 }
110
111 protected function optimizeVariables($node, $env)
112 {
113 if ('Twig_Node_Expression_Name' === get_class($node) && $node->isSimple()) {
114 $this->prependedNodes[0][] = $node->getAttribute('name');
115
116 return new Twig_Node_Expression_TempName($node->getAttribute('name'), $node->getLine());
117 }
118
119 return $node;
120 }
121
122 /**
123 * Optimizes print nodes.
124 *
125 * It replaces:
126 *
127 * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()"
128 *
129 * @param Twig_NodeInterface $node A Node
130 * @param Twig_Environment $env The current Twig environment
131 */
132 protected function optimizePrintNode($node, $env)
133 {
134 if (!$node instanceof Twig_Node_Print) {
135 return $node;
136 }
137
138 if (
139 $node->getNode('expr') instanceof Twig_Node_Expression_BlockReference ||
140 $node->getNode('expr') instanceof Twig_Node_Expression_Parent
141 ) {
142 $node->getNode('expr')->setAttribute('output', true);
143
144 return $node->getNode('expr');
145 }
146
147 return $node;
148 }
149
150 /**
151 * Removes "raw" filters.
152 *
153 * @param Twig_NodeInterface $node A Node
154 * @param Twig_Environment $env The current Twig environment
155 */
156 protected function optimizeRawFilter($node, $env)
157 {
158 if ($node instanceof Twig_Node_Expression_Filter && 'raw' == $node->getNode('filter')->getAttribute('value')) {
159 return $node->getNode('node');
160 }
161
162 return $node;
163 }
164
165 /**
166 * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
167 *
168 * @param Twig_NodeInterface $node A Node
169 * @param Twig_Environment $env The current Twig environment
170 */
171 protected function enterOptimizeFor($node, $env)
172 {
173 if ($node instanceof Twig_Node_For) {
174 // disable the loop variable by default
175 $node->setAttribute('with_loop', false);
176 array_unshift($this->loops, $node);
177 } elseif (!$this->loops) {
178 // we are outside a loop
179 return;
180 }
181
182 // when do we need to add the loop variable back?
183
184 // the loop variable is referenced for the current loop
185 elseif ($node instanceof Twig_Node_Expression_Name && 'loop' === $node->getAttribute('name')) {
186 $this->addLoopToCurrent();
187 }
188
189 // block reference
190 elseif ($node instanceof Twig_Node_BlockReference || $node instanceof Twig_Node_Expression_BlockReference) {
191 $this->addLoopToCurrent();
192 }
193
194 // include without the only attribute
195 elseif ($node instanceof Twig_Node_Include && !$node->getAttribute('only')) {
196 $this->addLoopToAll();
197 }
198
199 // the loop variable is referenced via an attribute
200 elseif ($node instanceof Twig_Node_Expression_GetAttr
201 && (!$node->getNode('attribute') instanceof Twig_Node_Expression_Constant
202 || 'parent' === $node->getNode('attribute')->getAttribute('value')
203 )
204 && (true === $this->loops[0]->getAttribute('with_loop')
205 || ($node->getNode('node') instanceof Twig_Node_Expression_Name
206 && 'loop' === $node->getNode('node')->getAttribute('name')
207 )
208 )
209 ) {
210 $this->addLoopToAll();
211 }
212 }
213
214 /**
215 * Optimizes "for" tag by removing the "loop" variable creation whenever possible.
216 *
217 * @param Twig_NodeInterface $node A Node
218 * @param Twig_Environment $env The current Twig environment
219 */
220 protected function leaveOptimizeFor($node, $env)
221 {
222 if ($node instanceof Twig_Node_For) {
223 array_shift($this->loops);
224 }
225 }
226
227 protected function addLoopToCurrent()
228 {
229 $this->loops[0]->setAttribute('with_loop', true);
230 }
231
232 protected function addLoopToAll()
233 {
234 foreach ($this->loops as $loop) {
235 $loop->setAttribute('with_loop', true);
236 }
237 }
238
239 /**
240 * {@inheritdoc}
241 */
242 public function getPriority()
243 {
244 return 255;
245 }
246}
diff --git a/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php b/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php
new file mode 100644
index 00000000..c4bbd812
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeVisitor/SafeAnalysis.php
@@ -0,0 +1,131 @@
1<?php
2
3class Twig_NodeVisitor_SafeAnalysis implements Twig_NodeVisitorInterface
4{
5 protected $data = array();
6 protected $safeVars = array();
7
8 public function setSafeVars($safeVars)
9 {
10 $this->safeVars = $safeVars;
11 }
12
13 public function getSafe(Twig_NodeInterface $node)
14 {
15 $hash = spl_object_hash($node);
16 if (isset($this->data[$hash])) {
17 foreach ($this->data[$hash] as $bucket) {
18 if ($bucket['key'] === $node) {
19 return $bucket['value'];
20 }
21 }
22 }
23 }
24
25 protected function setSafe(Twig_NodeInterface $node, array $safe)
26 {
27 $hash = spl_object_hash($node);
28 if (isset($this->data[$hash])) {
29 foreach ($this->data[$hash] as &$bucket) {
30 if ($bucket['key'] === $node) {
31 $bucket['value'] = $safe;
32
33 return;
34 }
35 }
36 }
37 $this->data[$hash][] = array(
38 'key' => $node,
39 'value' => $safe,
40 );
41 }
42
43 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
44 {
45 return $node;
46 }
47
48 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
49 {
50 if ($node instanceof Twig_Node_Expression_Constant) {
51 // constants are marked safe for all
52 $this->setSafe($node, array('all'));
53 } elseif ($node instanceof Twig_Node_Expression_BlockReference) {
54 // blocks are safe by definition
55 $this->setSafe($node, array('all'));
56 } elseif ($node instanceof Twig_Node_Expression_Parent) {
57 // parent block is safe by definition
58 $this->setSafe($node, array('all'));
59 } elseif ($node instanceof Twig_Node_Expression_Conditional) {
60 // intersect safeness of both operands
61 $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3')));
62 $this->setSafe($node, $safe);
63 } elseif ($node instanceof Twig_Node_Expression_Filter) {
64 // filter expression is safe when the filter is safe
65 $name = $node->getNode('filter')->getAttribute('value');
66 $args = $node->getNode('arguments');
67 if (false !== $filter = $env->getFilter($name)) {
68 $safe = $filter->getSafe($args);
69 if (null === $safe) {
70 $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety());
71 }
72 $this->setSafe($node, $safe);
73 } else {
74 $this->setSafe($node, array());
75 }
76 } elseif ($node instanceof Twig_Node_Expression_Function) {
77 // function expression is safe when the function is safe
78 $name = $node->getAttribute('name');
79 $args = $node->getNode('arguments');
80 $function = $env->getFunction($name);
81 if (false !== $function) {
82 $this->setSafe($node, $function->getSafe($args));
83 } else {
84 $this->setSafe($node, array());
85 }
86 } elseif ($node instanceof Twig_Node_Expression_MethodCall) {
87 if ($node->getAttribute('safe')) {
88 $this->setSafe($node, array('all'));
89 } else {
90 $this->setSafe($node, array());
91 }
92 } elseif ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name) {
93 $name = $node->getNode('node')->getAttribute('name');
94 // attributes on template instances are safe
95 if ('_self' == $name || in_array($name, $this->safeVars)) {
96 $this->setSafe($node, array('all'));
97 } else {
98 $this->setSafe($node, array());
99 }
100 } else {
101 $this->setSafe($node, array());
102 }
103
104 return $node;
105 }
106
107 protected function intersectSafe(array $a = null, array $b = null)
108 {
109 if (null === $a || null === $b) {
110 return array();
111 }
112
113 if (in_array('all', $a)) {
114 return $b;
115 }
116
117 if (in_array('all', $b)) {
118 return $a;
119 }
120
121 return array_intersect($a, $b);
122 }
123
124 /**
125 * {@inheritdoc}
126 */
127 public function getPriority()
128 {
129 return 0;
130 }
131}
diff --git a/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php b/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php
new file mode 100644
index 00000000..fb27045b
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeVisitor/Sandbox.php
@@ -0,0 +1,92 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig_NodeVisitor_Sandbox implements sandboxing.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_NodeVisitor_Sandbox implements Twig_NodeVisitorInterface
18{
19 protected $inAModule = false;
20 protected $tags;
21 protected $filters;
22 protected $functions;
23
24 /**
25 * Called before child nodes are visited.
26 *
27 * @param Twig_NodeInterface $node The node to visit
28 * @param Twig_Environment $env The Twig environment instance
29 *
30 * @return Twig_NodeInterface The modified node
31 */
32 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
33 {
34 if ($node instanceof Twig_Node_Module) {
35 $this->inAModule = true;
36 $this->tags = array();
37 $this->filters = array();
38 $this->functions = array();
39
40 return $node;
41 } elseif ($this->inAModule) {
42 // look for tags
43 if ($node->getNodeTag()) {
44 $this->tags[] = $node->getNodeTag();
45 }
46
47 // look for filters
48 if ($node instanceof Twig_Node_Expression_Filter) {
49 $this->filters[] = $node->getNode('filter')->getAttribute('value');
50 }
51
52 // look for functions
53 if ($node instanceof Twig_Node_Expression_Function) {
54 $this->functions[] = $node->getAttribute('name');
55 }
56
57 // wrap print to check __toString() calls
58 if ($node instanceof Twig_Node_Print) {
59 return new Twig_Node_SandboxedPrint($node->getNode('expr'), $node->getLine(), $node->getNodeTag());
60 }
61 }
62
63 return $node;
64 }
65
66 /**
67 * Called after child nodes are visited.
68 *
69 * @param Twig_NodeInterface $node The node to visit
70 * @param Twig_Environment $env The Twig environment instance
71 *
72 * @return Twig_NodeInterface The modified node
73 */
74 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
75 {
76 if ($node instanceof Twig_Node_Module) {
77 $this->inAModule = false;
78
79 return new Twig_Node_SandboxedModule($node, array_unique($this->filters), array_unique($this->tags), array_unique($this->functions));
80 }
81
82 return $node;
83 }
84
85 /**
86 * {@inheritdoc}
87 */
88 public function getPriority()
89 {
90 return 0;
91 }
92}
diff --git a/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php b/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php
new file mode 100644
index 00000000..f33c13fc
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/NodeVisitorInterface.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Twig_NodeVisitorInterface is the interface the all node visitor classes must implement.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17interface Twig_NodeVisitorInterface
18{
19 /**
20 * Called before child nodes are visited.
21 *
22 * @param Twig_NodeInterface $node The node to visit
23 * @param Twig_Environment $env The Twig environment instance
24 *
25 * @return Twig_NodeInterface The modified node
26 */
27 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env);
28
29 /**
30 * Called after child nodes are visited.
31 *
32 * @param Twig_NodeInterface $node The node to visit
33 * @param Twig_Environment $env The Twig environment instance
34 *
35 * @return Twig_NodeInterface|false The modified node or false if the node must be removed
36 */
37 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env);
38
39 /**
40 * Returns the priority for this visitor.
41 *
42 * Priority should be between -10 and 10 (0 is the default).
43 *
44 * @return integer The priority level
45 */
46 public function getPriority();
47}
diff --git a/vendor/twig/twig/lib/Twig/Parser.php b/vendor/twig/twig/lib/Twig/Parser.php
new file mode 100644
index 00000000..958e46b3
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Parser.php
@@ -0,0 +1,394 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Default parser implementation.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Parser implements Twig_ParserInterface
19{
20 protected $stack = array();
21 protected $stream;
22 protected $parent;
23 protected $handlers;
24 protected $visitors;
25 protected $expressionParser;
26 protected $blocks;
27 protected $blockStack;
28 protected $macros;
29 protected $env;
30 protected $reservedMacroNames;
31 protected $importedSymbols;
32 protected $traits;
33 protected $embeddedTemplates = array();
34
35 /**
36 * Constructor.
37 *
38 * @param Twig_Environment $env A Twig_Environment instance
39 */
40 public function __construct(Twig_Environment $env)
41 {
42 $this->env = $env;
43 }
44
45 public function getEnvironment()
46 {
47 return $this->env;
48 }
49
50 public function getVarName()
51 {
52 return sprintf('__internal_%s', hash('sha1', uniqid(mt_rand(), true), false));
53 }
54
55 public function getFilename()
56 {
57 return $this->stream->getFilename();
58 }
59
60 /**
61 * Converts a token stream to a node tree.
62 *
63 * @param Twig_TokenStream $stream A token stream instance
64 *
65 * @return Twig_Node_Module A node tree
66 */
67 public function parse(Twig_TokenStream $stream, $test = null, $dropNeedle = false)
68 {
69 // push all variables into the stack to keep the current state of the parser
70 $vars = get_object_vars($this);
71 unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser']);
72 $this->stack[] = $vars;
73
74 // tag handlers
75 if (null === $this->handlers) {
76 $this->handlers = $this->env->getTokenParsers();
77 $this->handlers->setParser($this);
78 }
79
80 // node visitors
81 if (null === $this->visitors) {
82 $this->visitors = $this->env->getNodeVisitors();
83 }
84
85 if (null === $this->expressionParser) {
86 $this->expressionParser = new Twig_ExpressionParser($this, $this->env->getUnaryOperators(), $this->env->getBinaryOperators());
87 }
88
89 $this->stream = $stream;
90 $this->parent = null;
91 $this->blocks = array();
92 $this->macros = array();
93 $this->traits = array();
94 $this->blockStack = array();
95 $this->importedSymbols = array(array());
96 $this->embeddedTemplates = array();
97
98 try {
99 $body = $this->subparse($test, $dropNeedle);
100
101 if (null !== $this->parent) {
102 if (null === $body = $this->filterBodyNodes($body)) {
103 $body = new Twig_Node();
104 }
105 }
106 } catch (Twig_Error_Syntax $e) {
107 if (!$e->getTemplateFile()) {
108 $e->setTemplateFile($this->getFilename());
109 }
110
111 if (!$e->getTemplateLine()) {
112 $e->setTemplateLine($this->stream->getCurrent()->getLine());
113 }
114
115 throw $e;
116 }
117
118 $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());
119
120 $traverser = new Twig_NodeTraverser($this->env, $this->visitors);
121
122 $node = $traverser->traverse($node);
123
124 // restore previous stack so previous parse() call can resume working
125 foreach (array_pop($this->stack) as $key => $val) {
126 $this->$key = $val;
127 }
128
129 return $node;
130 }
131
132 public function subparse($test, $dropNeedle = false)
133 {
134 $lineno = $this->getCurrentToken()->getLine();
135 $rv = array();
136 while (!$this->stream->isEOF()) {
137 switch ($this->getCurrentToken()->getType()) {
138 case Twig_Token::TEXT_TYPE:
139 $token = $this->stream->next();
140 $rv[] = new Twig_Node_Text($token->getValue(), $token->getLine());
141 break;
142
143 case Twig_Token::VAR_START_TYPE:
144 $token = $this->stream->next();
145 $expr = $this->expressionParser->parseExpression();
146 $this->stream->expect(Twig_Token::VAR_END_TYPE);
147 $rv[] = new Twig_Node_Print($expr, $token->getLine());
148 break;
149
150 case Twig_Token::BLOCK_START_TYPE:
151 $this->stream->next();
152 $token = $this->getCurrentToken();
153
154 if ($token->getType() !== Twig_Token::NAME_TYPE) {
155 throw new Twig_Error_Syntax('A block must start with a tag name', $token->getLine(), $this->getFilename());
156 }
157
158 if (null !== $test && call_user_func($test, $token)) {
159 if ($dropNeedle) {
160 $this->stream->next();
161 }
162
163 if (1 === count($rv)) {
164 return $rv[0];
165 }
166
167 return new Twig_Node($rv, array(), $lineno);
168 }
169
170 $subparser = $this->handlers->getTokenParser($token->getValue());
171 if (null === $subparser) {
172 if (null !== $test) {
173 $error = sprintf('Unexpected tag name "%s"', $token->getValue());
174 if (is_array($test) && isset($test[0]) && $test[0] instanceof Twig_TokenParserInterface) {
175 $error .= sprintf(' (expecting closing tag for the "%s" tag defined near line %s)', $test[0]->getTag(), $lineno);
176 }
177
178 throw new Twig_Error_Syntax($error, $token->getLine(), $this->getFilename());
179 }
180
181 $message = sprintf('Unknown tag name "%s"', $token->getValue());
182 if ($alternatives = $this->env->computeAlternatives($token->getValue(), array_keys($this->env->getTags()))) {
183 $message = sprintf('%s. Did you mean "%s"', $message, implode('", "', $alternatives));
184 }
185
186 throw new Twig_Error_Syntax($message, $token->getLine(), $this->getFilename());
187 }
188
189 $this->stream->next();
190
191 $node = $subparser->parse($token);
192 if (null !== $node) {
193 $rv[] = $node;
194 }
195 break;
196
197 default:
198 throw new Twig_Error_Syntax('Lexer or parser ended up in unsupported state.', 0, $this->getFilename());
199 }
200 }
201
202 if (1 === count($rv)) {
203 return $rv[0];
204 }
205
206 return new Twig_Node($rv, array(), $lineno);
207 }
208
209 public function addHandler($name, $class)
210 {
211 $this->handlers[$name] = $class;
212 }
213
214 public function addNodeVisitor(Twig_NodeVisitorInterface $visitor)
215 {
216 $this->visitors[] = $visitor;
217 }
218
219 public function getBlockStack()
220 {
221 return $this->blockStack;
222 }
223
224 public function peekBlockStack()
225 {
226 return $this->blockStack[count($this->blockStack) - 1];
227 }
228
229 public function popBlockStack()
230 {
231 array_pop($this->blockStack);
232 }
233
234 public function pushBlockStack($name)
235 {
236 $this->blockStack[] = $name;
237 }
238
239 public function hasBlock($name)
240 {
241 return isset($this->blocks[$name]);
242 }
243
244 public function getBlock($name)
245 {
246 return $this->blocks[$name];
247 }
248
249 public function setBlock($name, $value)
250 {
251 $this->blocks[$name] = new Twig_Node_Body(array($value), array(), $value->getLine());
252 }
253
254 public function hasMacro($name)
255 {
256 return isset($this->macros[$name]);
257 }
258
259 public function setMacro($name, Twig_Node_Macro $node)
260 {
261 if (null === $this->reservedMacroNames) {
262 $this->reservedMacroNames = array();
263 $r = new ReflectionClass($this->env->getBaseTemplateClass());
264 foreach ($r->getMethods() as $method) {
265 $this->reservedMacroNames[] = $method->getName();
266 }
267 }
268
269 if (in_array($name, $this->reservedMacroNames)) {
270 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());
271 }
272
273 $this->macros[$name] = $node;
274 }
275
276 public function addTrait($trait)
277 {
278 $this->traits[] = $trait;
279 }
280
281 public function hasTraits()
282 {
283 return count($this->traits) > 0;
284 }
285
286 public function embedTemplate(Twig_Node_Module $template)
287 {
288 $template->setIndex(mt_rand());
289
290 $this->embeddedTemplates[] = $template;
291 }
292
293 public function addImportedSymbol($type, $alias, $name = null, Twig_Node_Expression $node = null)
294 {
295 $this->importedSymbols[0][$type][$alias] = array('name' => $name, 'node' => $node);
296 }
297
298 public function getImportedSymbol($type, $alias)
299 {
300 foreach ($this->importedSymbols as $functions) {
301 if (isset($functions[$type][$alias])) {
302 return $functions[$type][$alias];
303 }
304 }
305 }
306
307 public function isMainScope()
308 {
309 return 1 === count($this->importedSymbols);
310 }
311
312 public function pushLocalScope()
313 {
314 array_unshift($this->importedSymbols, array());
315 }
316
317 public function popLocalScope()
318 {
319 array_shift($this->importedSymbols);
320 }
321
322 /**
323 * Gets the expression parser.
324 *
325 * @return Twig_ExpressionParser The expression parser
326 */
327 public function getExpressionParser()
328 {
329 return $this->expressionParser;
330 }
331
332 public function getParent()
333 {
334 return $this->parent;
335 }
336
337 public function setParent($parent)
338 {
339 $this->parent = $parent;
340 }
341
342 /**
343 * Gets the token stream.
344 *
345 * @return Twig_TokenStream The token stream
346 */
347 public function getStream()
348 {
349 return $this->stream;
350 }
351
352 /**
353 * Gets the current token.
354 *
355 * @return Twig_Token The current token
356 */
357 public function getCurrentToken()
358 {
359 return $this->stream->getCurrent();
360 }
361
362 protected function filterBodyNodes(Twig_NodeInterface $node)
363 {
364 // check that the body does not contain non-empty output nodes
365 if (
366 ($node instanceof Twig_Node_Text && !ctype_space($node->getAttribute('data')))
367 ||
368 (!$node instanceof Twig_Node_Text && !$node instanceof Twig_Node_BlockReference && $node instanceof Twig_NodeOutputInterface)
369 ) {
370 if (false !== strpos((string) $node, chr(0xEF).chr(0xBB).chr(0xBF))) {
371 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());
372 }
373
374 throw new Twig_Error_Syntax('A template that extends another one cannot have a body.', $node->getLine(), $this->getFilename());
375 }
376
377 // bypass "set" nodes as they "capture" the output
378 if ($node instanceof Twig_Node_Set) {
379 return $node;
380 }
381
382 if ($node instanceof Twig_NodeOutputInterface) {
383 return;
384 }
385
386 foreach ($node as $k => $n) {
387 if (null !== $n && null === $n = $this->filterBodyNodes($n)) {
388 $node->removeNode($k);
389 }
390 }
391
392 return $node;
393 }
394}
diff --git a/vendor/twig/twig/lib/Twig/ParserInterface.php b/vendor/twig/twig/lib/Twig/ParserInterface.php
new file mode 100644
index 00000000..f0d79009
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/ParserInterface.php
@@ -0,0 +1,28 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interface implemented by parser classes.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_ParserInterface
19{
20 /**
21 * Converts a token stream to a node tree.
22 *
23 * @param Twig_TokenStream $stream A token stream instance
24 *
25 * @return Twig_Node_Module A node tree
26 */
27 public function parse(Twig_TokenStream $stream);
28}
diff --git a/vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php b/vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php
new file mode 100644
index 00000000..015bfaea
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Sandbox/SecurityError.php
@@ -0,0 +1,19 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Exception thrown when a security error occurs at runtime.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Sandbox_SecurityError extends Twig_Error
18{
19}
diff --git a/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php b/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php
new file mode 100644
index 00000000..66ee2332
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicy.php
@@ -0,0 +1,119 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a security policy which need to be enforced when sandbox mode is enabled.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_Sandbox_SecurityPolicy implements Twig_Sandbox_SecurityPolicyInterface
18{
19 protected $allowedTags;
20 protected $allowedFilters;
21 protected $allowedMethods;
22 protected $allowedProperties;
23 protected $allowedFunctions;
24
25 public function __construct(array $allowedTags = array(), array $allowedFilters = array(), array $allowedMethods = array(), array $allowedProperties = array(), array $allowedFunctions = array())
26 {
27 $this->allowedTags = $allowedTags;
28 $this->allowedFilters = $allowedFilters;
29 $this->setAllowedMethods($allowedMethods);
30 $this->allowedProperties = $allowedProperties;
31 $this->allowedFunctions = $allowedFunctions;
32 }
33
34 public function setAllowedTags(array $tags)
35 {
36 $this->allowedTags = $tags;
37 }
38
39 public function setAllowedFilters(array $filters)
40 {
41 $this->allowedFilters = $filters;
42 }
43
44 public function setAllowedMethods(array $methods)
45 {
46 $this->allowedMethods = array();
47 foreach ($methods as $class => $m) {
48 $this->allowedMethods[$class] = array_map('strtolower', is_array($m) ? $m : array($m));
49 }
50 }
51
52 public function setAllowedProperties(array $properties)
53 {
54 $this->allowedProperties = $properties;
55 }
56
57 public function setAllowedFunctions(array $functions)
58 {
59 $this->allowedFunctions = $functions;
60 }
61
62 public function checkSecurity($tags, $filters, $functions)
63 {
64 foreach ($tags as $tag) {
65 if (!in_array($tag, $this->allowedTags)) {
66 throw new Twig_Sandbox_SecurityError(sprintf('Tag "%s" is not allowed.', $tag));
67 }
68 }
69
70 foreach ($filters as $filter) {
71 if (!in_array($filter, $this->allowedFilters)) {
72 throw new Twig_Sandbox_SecurityError(sprintf('Filter "%s" is not allowed.', $filter));
73 }
74 }
75
76 foreach ($functions as $function) {
77 if (!in_array($function, $this->allowedFunctions)) {
78 throw new Twig_Sandbox_SecurityError(sprintf('Function "%s" is not allowed.', $function));
79 }
80 }
81 }
82
83 public function checkMethodAllowed($obj, $method)
84 {
85 if ($obj instanceof Twig_TemplateInterface || $obj instanceof Twig_Markup) {
86 return true;
87 }
88
89 $allowed = false;
90 $method = strtolower($method);
91 foreach ($this->allowedMethods as $class => $methods) {
92 if ($obj instanceof $class) {
93 $allowed = in_array($method, $methods);
94
95 break;
96 }
97 }
98
99 if (!$allowed) {
100 throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, get_class($obj)));
101 }
102 }
103
104 public function checkPropertyAllowed($obj, $property)
105 {
106 $allowed = false;
107 foreach ($this->allowedProperties as $class => $properties) {
108 if ($obj instanceof $class) {
109 $allowed = in_array($property, is_array($properties) ? $properties : array($properties));
110
111 break;
112 }
113 }
114
115 if (!$allowed) {
116 throw new Twig_Sandbox_SecurityError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, get_class($obj)));
117 }
118 }
119}
diff --git a/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php b/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php
new file mode 100644
index 00000000..6ab48e3c
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Sandbox/SecurityPolicyInterface.php
@@ -0,0 +1,24 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interfaces that all security policy classes must implements.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17interface Twig_Sandbox_SecurityPolicyInterface
18{
19 public function checkSecurity($tags, $filters, $functions);
20
21 public function checkMethodAllowed($obj, $method);
22
23 public function checkPropertyAllowed($obj, $method);
24}
diff --git a/vendor/twig/twig/lib/Twig/SimpleFilter.php b/vendor/twig/twig/lib/Twig/SimpleFilter.php
new file mode 100644
index 00000000..d35c5633
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/SimpleFilter.php
@@ -0,0 +1,94 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009-2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template filter.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_SimpleFilter
18{
19 protected $name;
20 protected $callable;
21 protected $options;
22 protected $arguments = array();
23
24 public function __construct($name, $callable, array $options = array())
25 {
26 $this->name = $name;
27 $this->callable = $callable;
28 $this->options = array_merge(array(
29 'needs_environment' => false,
30 'needs_context' => false,
31 'is_safe' => null,
32 'is_safe_callback' => null,
33 'pre_escape' => null,
34 'preserves_safety' => null,
35 'node_class' => 'Twig_Node_Expression_Filter',
36 ), $options);
37 }
38
39 public function getName()
40 {
41 return $this->name;
42 }
43
44 public function getCallable()
45 {
46 return $this->callable;
47 }
48
49 public function getNodeClass()
50 {
51 return $this->options['node_class'];
52 }
53
54 public function setArguments($arguments)
55 {
56 $this->arguments = $arguments;
57 }
58
59 public function getArguments()
60 {
61 return $this->arguments;
62 }
63
64 public function needsEnvironment()
65 {
66 return $this->options['needs_environment'];
67 }
68
69 public function needsContext()
70 {
71 return $this->options['needs_context'];
72 }
73
74 public function getSafe(Twig_Node $filterArgs)
75 {
76 if (null !== $this->options['is_safe']) {
77 return $this->options['is_safe'];
78 }
79
80 if (null !== $this->options['is_safe_callback']) {
81 return call_user_func($this->options['is_safe_callback'], $filterArgs);
82 }
83 }
84
85 public function getPreservesSafety()
86 {
87 return $this->options['preserves_safety'];
88 }
89
90 public function getPreEscape()
91 {
92 return $this->options['pre_escape'];
93 }
94}
diff --git a/vendor/twig/twig/lib/Twig/SimpleFunction.php b/vendor/twig/twig/lib/Twig/SimpleFunction.php
new file mode 100644
index 00000000..8ef6aca2
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/SimpleFunction.php
@@ -0,0 +1,84 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010-2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template function.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_SimpleFunction
18{
19 protected $name;
20 protected $callable;
21 protected $options;
22 protected $arguments = array();
23
24 public function __construct($name, $callable, array $options = array())
25 {
26 $this->name = $name;
27 $this->callable = $callable;
28 $this->options = array_merge(array(
29 'needs_environment' => false,
30 'needs_context' => false,
31 'is_safe' => null,
32 'is_safe_callback' => null,
33 'node_class' => 'Twig_Node_Expression_Function',
34 ), $options);
35 }
36
37 public function getName()
38 {
39 return $this->name;
40 }
41
42 public function getCallable()
43 {
44 return $this->callable;
45 }
46
47 public function getNodeClass()
48 {
49 return $this->options['node_class'];
50 }
51
52 public function setArguments($arguments)
53 {
54 $this->arguments = $arguments;
55 }
56
57 public function getArguments()
58 {
59 return $this->arguments;
60 }
61
62 public function needsEnvironment()
63 {
64 return $this->options['needs_environment'];
65 }
66
67 public function needsContext()
68 {
69 return $this->options['needs_context'];
70 }
71
72 public function getSafe(Twig_Node $functionArgs)
73 {
74 if (null !== $this->options['is_safe']) {
75 return $this->options['is_safe'];
76 }
77
78 if (null !== $this->options['is_safe_callback']) {
79 return call_user_func($this->options['is_safe_callback'], $functionArgs);
80 }
81
82 return array();
83 }
84}
diff --git a/vendor/twig/twig/lib/Twig/SimpleTest.php b/vendor/twig/twig/lib/Twig/SimpleTest.php
new file mode 100644
index 00000000..225459c9
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/SimpleTest.php
@@ -0,0 +1,46 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010-2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template test.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17class Twig_SimpleTest
18{
19 protected $name;
20 protected $callable;
21 protected $options;
22
23 public function __construct($name, $callable, array $options = array())
24 {
25 $this->name = $name;
26 $this->callable = $callable;
27 $this->options = array_merge(array(
28 'node_class' => 'Twig_Node_Expression_Test',
29 ), $options);
30 }
31
32 public function getName()
33 {
34 return $this->name;
35 }
36
37 public function getCallable()
38 {
39 return $this->callable;
40 }
41
42 public function getNodeClass()
43 {
44 return $this->options['node_class'];
45 }
46}
diff --git a/vendor/twig/twig/lib/Twig/Template.php b/vendor/twig/twig/lib/Twig/Template.php
new file mode 100644
index 00000000..a001ca03
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Template.php
@@ -0,0 +1,455 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Default base class for compiled templates.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18abstract class Twig_Template implements Twig_TemplateInterface
19{
20 protected static $cache = array();
21
22 protected $parent;
23 protected $parents;
24 protected $env;
25 protected $blocks;
26 protected $traits;
27
28 /**
29 * Constructor.
30 *
31 * @param Twig_Environment $env A Twig_Environment instance
32 */
33 public function __construct(Twig_Environment $env)
34 {
35 $this->env = $env;
36 $this->blocks = array();
37 $this->traits = array();
38 }
39
40 /**
41 * Returns the template name.
42 *
43 * @return string The template name
44 */
45 abstract public function getTemplateName();
46
47 /**
48 * {@inheritdoc}
49 */
50 public function getEnvironment()
51 {
52 return $this->env;
53 }
54
55 /**
56 * Returns the parent template.
57 *
58 * This method is for internal use only and should never be called
59 * directly.
60 *
61 * @return Twig_TemplateInterface|false The parent template or false if there is no parent
62 */
63 public function getParent(array $context)
64 {
65 if (null !== $this->parent) {
66 return $this->parent;
67 }
68
69 $parent = $this->doGetParent($context);
70 if (false === $parent) {
71 return false;
72 } elseif ($parent instanceof Twig_Template) {
73 $name = $parent->getTemplateName();
74 $this->parents[$name] = $parent;
75 $parent = $name;
76 } elseif (!isset($this->parents[$parent])) {
77 $this->parents[$parent] = $this->env->loadTemplate($parent);
78 }
79
80 return $this->parents[$parent];
81 }
82
83 protected function doGetParent(array $context)
84 {
85 return false;
86 }
87
88 public function isTraitable()
89 {
90 return true;
91 }
92
93 /**
94 * Displays a parent block.
95 *
96 * This method is for internal use only and should never be called
97 * directly.
98 *
99 * @param string $name The block name to display from the parent
100 * @param array $context The context
101 * @param array $blocks The current set of blocks
102 */
103 public function displayParentBlock($name, array $context, array $blocks = array())
104 {
105 $name = (string) $name;
106
107 if (isset($this->traits[$name])) {
108 $this->traits[$name][0]->displayBlock($name, $context, $blocks);
109 } elseif (false !== $parent = $this->getParent($context)) {
110 $parent->displayBlock($name, $context, $blocks);
111 } else {
112 throw new Twig_Error_Runtime(sprintf('The template has no parent and no traits defining the "%s" block', $name), -1, $this->getTemplateName());
113 }
114 }
115
116 /**
117 * Displays a block.
118 *
119 * This method is for internal use only and should never be called
120 * directly.
121 *
122 * @param string $name The block name to display
123 * @param array $context The context
124 * @param array $blocks The current set of blocks
125 */
126 public function displayBlock($name, array $context, array $blocks = array())
127 {
128 $name = (string) $name;
129
130 if (isset($blocks[$name])) {
131 $b = $blocks;
132 unset($b[$name]);
133 call_user_func($blocks[$name], $context, $b);
134 } elseif (isset($this->blocks[$name])) {
135 call_user_func($this->blocks[$name], $context, $blocks);
136 } elseif (false !== $parent = $this->getParent($context)) {
137 $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks));
138 }
139 }
140
141 /**
142 * Renders a parent block.
143 *
144 * This method is for internal use only and should never be called
145 * directly.
146 *
147 * @param string $name The block name to render from the parent
148 * @param array $context The context
149 * @param array $blocks The current set of blocks
150 *
151 * @return string The rendered block
152 */
153 public function renderParentBlock($name, array $context, array $blocks = array())
154 {
155 ob_start();
156 $this->displayParentBlock($name, $context, $blocks);
157
158 return ob_get_clean();
159 }
160
161 /**
162 * Renders a block.
163 *
164 * This method is for internal use only and should never be called
165 * directly.
166 *
167 * @param string $name The block name to render
168 * @param array $context The context
169 * @param array $blocks The current set of blocks
170 *
171 * @return string The rendered block
172 */
173 public function renderBlock($name, array $context, array $blocks = array())
174 {
175 ob_start();
176 $this->displayBlock($name, $context, $blocks);
177
178 return ob_get_clean();
179 }
180
181 /**
182 * Returns whether a block exists or not.
183 *
184 * This method is for internal use only and should never be called
185 * directly.
186 *
187 * This method does only return blocks defined in the current template
188 * or defined in "used" traits.
189 *
190 * It does not return blocks from parent templates as the parent
191 * template name can be dynamic, which is only known based on the
192 * current context.
193 *
194 * @param string $name The block name
195 *
196 * @return Boolean true if the block exists, false otherwise
197 */
198 public function hasBlock($name)
199 {
200 return isset($this->blocks[(string) $name]);
201 }
202
203 /**
204 * Returns all block names.
205 *
206 * This method is for internal use only and should never be called
207 * directly.
208 *
209 * @return array An array of block names
210 *
211 * @see hasBlock
212 */
213 public function getBlockNames()
214 {
215 return array_keys($this->blocks);
216 }
217
218 /**
219 * Returns all blocks.
220 *
221 * This method is for internal use only and should never be called
222 * directly.
223 *
224 * @return array An array of blocks
225 *
226 * @see hasBlock
227 */
228 public function getBlocks()
229 {
230 return $this->blocks;
231 }
232
233 /**
234 * {@inheritdoc}
235 */
236 public function display(array $context, array $blocks = array())
237 {
238 $this->displayWithErrorHandling($this->env->mergeGlobals($context), $blocks);
239 }
240
241 /**
242 * {@inheritdoc}
243 */
244 public function render(array $context)
245 {
246 $level = ob_get_level();
247 ob_start();
248 try {
249 $this->display($context);
250 } catch (Exception $e) {
251 while (ob_get_level() > $level) {
252 ob_end_clean();
253 }
254
255 throw $e;
256 }
257
258 return ob_get_clean();
259 }
260
261 protected function displayWithErrorHandling(array $context, array $blocks = array())
262 {
263 try {
264 $this->doDisplay($context, $blocks);
265 } catch (Twig_Error $e) {
266 if (!$e->getTemplateFile()) {
267 $e->setTemplateFile($this->getTemplateName());
268 }
269
270 // this is mostly useful for Twig_Error_Loader exceptions
271 // see Twig_Error_Loader
272 if (false === $e->getTemplateLine()) {
273 $e->setTemplateLine(-1);
274 $e->guess();
275 }
276
277 throw $e;
278 } catch (Exception $e) {
279 throw new Twig_Error_Runtime(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, null, $e);
280 }
281 }
282
283 /**
284 * Auto-generated method to display the template with the given context.
285 *
286 * @param array $context An array of parameters to pass to the template
287 * @param array $blocks An array of blocks to pass to the template
288 */
289 abstract protected function doDisplay(array $context, array $blocks = array());
290
291 /**
292 * Returns a variable from the context.
293 *
294 * This method is for internal use only and should never be called
295 * directly.
296 *
297 * This method should not be overridden in a sub-class as this is an
298 * implementation detail that has been introduced to optimize variable
299 * access for versions of PHP before 5.4. This is not a way to override
300 * the way to get a variable value.
301 *
302 * @param array $context The context
303 * @param string $item The variable to return from the context
304 * @param Boolean $ignoreStrictCheck Whether to ignore the strict variable check or not
305 *
306 * @return The content of the context variable
307 *
308 * @throws Twig_Error_Runtime if the variable does not exist and Twig is running in strict mode
309 */
310 final protected function getContext($context, $item, $ignoreStrictCheck = false)
311 {
312 if (!array_key_exists($item, $context)) {
313 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
314 return null;
315 }
316
317 throw new Twig_Error_Runtime(sprintf('Variable "%s" does not exist', $item), -1, $this->getTemplateName());
318 }
319
320 return $context[$item];
321 }
322
323 /**
324 * Returns the attribute value for a given array/object.
325 *
326 * @param mixed $object The object or array from where to get the item
327 * @param mixed $item The item to get from the array or object
328 * @param array $arguments An array of arguments to pass if the item is an object method
329 * @param string $type The type of attribute (@see Twig_TemplateInterface)
330 * @param Boolean $isDefinedTest Whether this is only a defined check
331 * @param Boolean $ignoreStrictCheck Whether to ignore the strict attribute check or not
332 *
333 * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true
334 *
335 * @throws Twig_Error_Runtime if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false
336 */
337 protected function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
338 {
339 // array
340 if (Twig_TemplateInterface::METHOD_CALL !== $type) {
341 $arrayItem = is_bool($item) || is_float($item) ? (int) $item : $item;
342
343 if ((is_array($object) && array_key_exists($arrayItem, $object))
344 || ($object instanceof ArrayAccess && isset($object[$arrayItem]))
345 ) {
346 if ($isDefinedTest) {
347 return true;
348 }
349
350 return $object[$arrayItem];
351 }
352
353 if (Twig_TemplateInterface::ARRAY_CALL === $type || !is_object($object)) {
354 if ($isDefinedTest) {
355 return false;
356 }
357
358 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
359 return null;
360 }
361
362 if (is_object($object)) {
363 throw new Twig_Error_Runtime(sprintf('Key "%s" in object (with ArrayAccess) of type "%s" does not exist', $arrayItem, get_class($object)), -1, $this->getTemplateName());
364 } elseif (is_array($object)) {
365 throw new Twig_Error_Runtime(sprintf('Key "%s" for array with keys "%s" does not exist', $arrayItem, implode(', ', array_keys($object))), -1, $this->getTemplateName());
366 } elseif (Twig_TemplateInterface::ARRAY_CALL === $type) {
367 throw new Twig_Error_Runtime(sprintf('Impossible to access a key ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
368 } else {
369 throw new Twig_Error_Runtime(sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
370 }
371 }
372 }
373
374 if (!is_object($object)) {
375 if ($isDefinedTest) {
376 return false;
377 }
378
379 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
380 return null;
381 }
382
383 throw new Twig_Error_Runtime(sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s")', $item, gettype($object), $object), -1, $this->getTemplateName());
384 }
385
386 $class = get_class($object);
387
388 // object property
389 if (Twig_TemplateInterface::METHOD_CALL !== $type) {
390 if (isset($object->$item) || array_key_exists((string) $item, $object)) {
391 if ($isDefinedTest) {
392 return true;
393 }
394
395 if ($this->env->hasExtension('sandbox')) {
396 $this->env->getExtension('sandbox')->checkPropertyAllowed($object, $item);
397 }
398
399 return $object->$item;
400 }
401 }
402
403 // object method
404 if (!isset(self::$cache[$class]['methods'])) {
405 self::$cache[$class]['methods'] = array_change_key_case(array_flip(get_class_methods($object)));
406 }
407
408 $lcItem = strtolower($item);
409 if (isset(self::$cache[$class]['methods'][$lcItem])) {
410 $method = (string) $item;
411 } elseif (isset(self::$cache[$class]['methods']['get'.$lcItem])) {
412 $method = 'get'.$item;
413 } elseif (isset(self::$cache[$class]['methods']['is'.$lcItem])) {
414 $method = 'is'.$item;
415 } elseif (isset(self::$cache[$class]['methods']['__call'])) {
416 $method = (string) $item;
417 } else {
418 if ($isDefinedTest) {
419 return false;
420 }
421
422 if ($ignoreStrictCheck || !$this->env->isStrictVariables()) {
423 return null;
424 }
425
426 throw new Twig_Error_Runtime(sprintf('Method "%s" for object "%s" does not exist', $item, get_class($object)), -1, $this->getTemplateName());
427 }
428
429 if ($isDefinedTest) {
430 return true;
431 }
432
433 if ($this->env->hasExtension('sandbox')) {
434 $this->env->getExtension('sandbox')->checkMethodAllowed($object, $method);
435 }
436
437 $ret = call_user_func_array(array($object, $method), $arguments);
438
439 // useful when calling a template method from a template
440 // this is not supported but unfortunately heavily used in the Symfony profiler
441 if ($object instanceof Twig_TemplateInterface) {
442 return $ret === '' ? '' : new Twig_Markup($ret, $this->env->getCharset());
443 }
444
445 return $ret;
446 }
447
448 /**
449 * This method is only useful when testing Twig. Do not use it.
450 */
451 public static function clearCache()
452 {
453 self::$cache = array();
454 }
455}
diff --git a/vendor/twig/twig/lib/Twig/TemplateInterface.php b/vendor/twig/twig/lib/Twig/TemplateInterface.php
new file mode 100644
index 00000000..879f503e
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TemplateInterface.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interface implemented by all compiled templates.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_TemplateInterface
19{
20 const ANY_CALL = 'any';
21 const ARRAY_CALL = 'array';
22 const METHOD_CALL = 'method';
23
24 /**
25 * Renders the template with the given context and returns it as string.
26 *
27 * @param array $context An array of parameters to pass to the template
28 *
29 * @return string The rendered template
30 */
31 public function render(array $context);
32
33 /**
34 * Displays the template with the given context.
35 *
36 * @param array $context An array of parameters to pass to the template
37 * @param array $blocks An array of blocks to pass to the template
38 */
39 public function display(array $context, array $blocks = array());
40
41 /**
42 * Returns the bound environment for this template.
43 *
44 * @return Twig_Environment The current environment
45 */
46 public function getEnvironment();
47}
diff --git a/vendor/twig/twig/lib/Twig/Test.php b/vendor/twig/twig/lib/Twig/Test.php
new file mode 100644
index 00000000..3baff885
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Test.php
@@ -0,0 +1,34 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template test.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18abstract class Twig_Test implements Twig_TestInterface, Twig_TestCallableInterface
19{
20 protected $options;
21 protected $arguments = array();
22
23 public function __construct(array $options = array())
24 {
25 $this->options = array_merge(array(
26 'callable' => null,
27 ), $options);
28 }
29
30 public function getCallable()
31 {
32 return $this->options['callable'];
33 }
34}
diff --git a/vendor/twig/twig/lib/Twig/Test/Function.php b/vendor/twig/twig/lib/Twig/Test/Function.php
new file mode 100644
index 00000000..4be6b9b9
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Test/Function.php
@@ -0,0 +1,35 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a function template test.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18class Twig_Test_Function extends Twig_Test
19{
20 protected $function;
21
22 public function __construct($function, array $options = array())
23 {
24 $options['callable'] = $function;
25
26 parent::__construct($options);
27
28 $this->function = $function;
29 }
30
31 public function compile()
32 {
33 return $this->function;
34 }
35}
diff --git a/vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php b/vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php
new file mode 100644
index 00000000..724f0941
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Test/IntegrationTestCase.php
@@ -0,0 +1,154 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Integration test helper
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @author Karma Dordrak <drak@zikula.org>
17 */
18abstract class Twig_Test_IntegrationTestCase extends PHPUnit_Framework_TestCase
19{
20 abstract protected function getExtensions();
21 abstract protected function getFixturesDir();
22
23 /**
24 * @dataProvider getTests
25 */
26 public function testIntegration($file, $message, $condition, $templates, $exception, $outputs)
27 {
28 $this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs);
29 }
30
31 public function getTests()
32 {
33 $fixturesDir = realpath($this->getFixturesDir());
34 $tests = array();
35
36 foreach (new RecursiveIteratorIterator(new RecursiveDirectoryIterator($fixturesDir), RecursiveIteratorIterator::LEAVES_ONLY) as $file) {
37 if (!preg_match('/\.test$/', $file)) {
38 continue;
39 }
40
41 $test = file_get_contents($file->getRealpath());
42
43 if (preg_match('/
44 --TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)\s*(?:--DATA--\s*(.*))?\s*--EXCEPTION--\s*(.*)/sx', $test, $match)) {
45 $message = $match[1];
46 $condition = $match[2];
47 $templates = $this->parseTemplates($match[3]);
48 $exception = $match[5];
49 $outputs = array(array(null, $match[4], null, ''));
50 } elseif (preg_match('/--TEST--\s*(.*?)\s*(?:--CONDITION--\s*(.*))?\s*((?:--TEMPLATE(?:\(.*?\))?--(?:.*?))+)--DATA--.*?--EXPECT--.*/s', $test, $match)) {
51 $message = $match[1];
52 $condition = $match[2];
53 $templates = $this->parseTemplates($match[3]);
54 $exception = false;
55 preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, PREG_SET_ORDER);
56 } else {
57 throw new InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file)));
58 }
59
60 $tests[] = array(str_replace($fixturesDir.'/', '', $file), $message, $condition, $templates, $exception, $outputs);
61 }
62
63 return $tests;
64 }
65
66 protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs)
67 {
68 if ($condition) {
69 eval('$ret = '.$condition.';');
70 if (!$ret) {
71 $this->markTestSkipped($condition);
72 }
73 }
74
75 $loader = new Twig_Loader_Array($templates);
76
77 foreach ($outputs as $match) {
78 $config = array_merge(array(
79 'cache' => false,
80 'strict_variables' => true,
81 ), $match[2] ? eval($match[2].';') : array());
82 $twig = new Twig_Environment($loader, $config);
83 $twig->addGlobal('global', 'global');
84 foreach ($this->getExtensions() as $extension) {
85 $twig->addExtension($extension);
86 }
87
88 try {
89 $template = $twig->loadTemplate('index.twig');
90 } catch (Exception $e) {
91 if (false !== $exception) {
92 $this->assertEquals(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage())));
93
94 return;
95 }
96
97 if ($e instanceof Twig_Error_Syntax) {
98 $e->setTemplateFile($file);
99
100 throw $e;
101 }
102
103 throw new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
104 }
105
106 try {
107 $output = trim($template->render(eval($match[1].';')), "\n ");
108 } catch (Exception $e) {
109 if (false !== $exception) {
110 $this->assertEquals(trim($exception), trim(sprintf('%s: %s', get_class($e), $e->getMessage())));
111
112 return;
113 }
114
115 if ($e instanceof Twig_Error_Syntax) {
116 $e->setTemplateFile($file);
117 } else {
118 $e = new Twig_Error(sprintf('%s: %s', get_class($e), $e->getMessage()), -1, $file, $e);
119 }
120
121 $output = trim(sprintf('%s: %s', get_class($e), $e->getMessage()));
122 }
123
124 if (false !== $exception) {
125 list($class, ) = explode(':', $exception);
126 $this->assertThat(NULL, new PHPUnit_Framework_Constraint_Exception($class));
127 }
128
129 $expected = trim($match[3], "\n ");
130
131 if ($expected != $output) {
132 echo 'Compiled template that failed:';
133
134 foreach (array_keys($templates) as $name) {
135 echo "Template: $name\n";
136 $source = $loader->getSource($name);
137 echo $twig->compile($twig->parse($twig->tokenize($source, $name)));
138 }
139 }
140 $this->assertEquals($expected, $output, $message.' (in '.$file.')');
141 }
142 }
143
144 protected static function parseTemplates($test)
145 {
146 $templates = array();
147 preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, PREG_SET_ORDER);
148 foreach ($matches as $match) {
149 $templates[($match[1] ? $match[1] : 'index.twig')] = $match[2];
150 }
151
152 return $templates;
153 }
154}
diff --git a/vendor/twig/twig/lib/Twig/Test/Method.php b/vendor/twig/twig/lib/Twig/Test/Method.php
new file mode 100644
index 00000000..17c6c041
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Test/Method.php
@@ -0,0 +1,37 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a method template test.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18class Twig_Test_Method extends Twig_Test
19{
20 protected $extension;
21 protected $method;
22
23 public function __construct(Twig_ExtensionInterface $extension, $method, array $options = array())
24 {
25 $options['callable'] = array($extension, $method);
26
27 parent::__construct($options);
28
29 $this->extension = $extension;
30 $this->method = $method;
31 }
32
33 public function compile()
34 {
35 return sprintf('$this->env->getExtension(\'%s\')->%s', $this->extension->getName(), $this->method);
36 }
37}
diff --git a/vendor/twig/twig/lib/Twig/Test/Node.php b/vendor/twig/twig/lib/Twig/Test/Node.php
new file mode 100644
index 00000000..c832a57b
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Test/Node.php
@@ -0,0 +1,37 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template test as a Node.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18class Twig_Test_Node extends Twig_Test
19{
20 protected $class;
21
22 public function __construct($class, array $options = array())
23 {
24 parent::__construct($options);
25
26 $this->class = $class;
27 }
28
29 public function getClass()
30 {
31 return $this->class;
32 }
33
34 public function compile()
35 {
36 }
37}
diff --git a/vendor/twig/twig/lib/Twig/Test/NodeTestCase.php b/vendor/twig/twig/lib/Twig/Test/NodeTestCase.php
new file mode 100644
index 00000000..b15c85ff
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Test/NodeTestCase.php
@@ -0,0 +1,58 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11abstract class Twig_Test_NodeTestCase extends PHPUnit_Framework_TestCase
12{
13 abstract public function getTests();
14
15 /**
16 * @dataProvider getTests
17 */
18 public function testCompile($node, $source, $environment = null)
19 {
20 $this->assertNodeCompilation($source, $node, $environment);
21 }
22
23 public function assertNodeCompilation($source, Twig_Node $node, Twig_Environment $environment = null)
24 {
25 $compiler = $this->getCompiler($environment);
26 $compiler->compile($node);
27
28 $this->assertEquals($source, trim($compiler->getSource()));
29 }
30
31 protected function getCompiler(Twig_Environment $environment = null)
32 {
33 return new Twig_Compiler(null === $environment ? $this->getEnvironment() : $environment);
34 }
35
36 protected function getEnvironment()
37 {
38 return new Twig_Environment();
39 }
40
41 protected function getVariableGetter($name)
42 {
43 if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
44 return sprintf('(isset($context["%s"]) ? $context["%s"] : null)', $name, $name);
45 }
46
47 return sprintf('$this->getContext($context, "%s")', $name);
48 }
49
50 protected function getAttributeGetter()
51 {
52 if (function_exists('twig_template_get_attributes')) {
53 return 'twig_template_get_attributes($this, ';
54 }
55
56 return '$this->getAttribute(';
57 }
58}
diff --git a/vendor/twig/twig/lib/Twig/TestCallableInterface.php b/vendor/twig/twig/lib/Twig/TestCallableInterface.php
new file mode 100644
index 00000000..0db43682
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TestCallableInterface.php
@@ -0,0 +1,21 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a callable template test.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_TestCallableInterface
19{
20 public function getCallable();
21}
diff --git a/vendor/twig/twig/lib/Twig/TestInterface.php b/vendor/twig/twig/lib/Twig/TestInterface.php
new file mode 100644
index 00000000..30d8a2c4
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TestInterface.php
@@ -0,0 +1,26 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Represents a template test.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 * @deprecated since 1.12 (to be removed in 2.0)
17 */
18interface Twig_TestInterface
19{
20 /**
21 * Compiles a test.
22 *
23 * @return string The PHP code for the test
24 */
25 public function compile();
26}
diff --git a/vendor/twig/twig/lib/Twig/Token.php b/vendor/twig/twig/lib/Twig/Token.php
new file mode 100644
index 00000000..bbca90db
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/Token.php
@@ -0,0 +1,218 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a Token.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_Token
19{
20 protected $value;
21 protected $type;
22 protected $lineno;
23
24 const EOF_TYPE = -1;
25 const TEXT_TYPE = 0;
26 const BLOCK_START_TYPE = 1;
27 const VAR_START_TYPE = 2;
28 const BLOCK_END_TYPE = 3;
29 const VAR_END_TYPE = 4;
30 const NAME_TYPE = 5;
31 const NUMBER_TYPE = 6;
32 const STRING_TYPE = 7;
33 const OPERATOR_TYPE = 8;
34 const PUNCTUATION_TYPE = 9;
35 const INTERPOLATION_START_TYPE = 10;
36 const INTERPOLATION_END_TYPE = 11;
37
38 /**
39 * Constructor.
40 *
41 * @param integer $type The type of the token
42 * @param string $value The token value
43 * @param integer $lineno The line position in the source
44 */
45 public function __construct($type, $value, $lineno)
46 {
47 $this->type = $type;
48 $this->value = $value;
49 $this->lineno = $lineno;
50 }
51
52 /**
53 * Returns a string representation of the token.
54 *
55 * @return string A string representation of the token
56 */
57 public function __toString()
58 {
59 return sprintf('%s(%s)', self::typeToString($this->type, true, $this->lineno), $this->value);
60 }
61
62 /**
63 * Tests the current token for a type and/or a value.
64 *
65 * Parameters may be:
66 * * just type
67 * * type and value (or array of possible values)
68 * * just value (or array of possible values) (NAME_TYPE is used as type)
69 *
70 * @param array|integer $type The type to test
71 * @param array|string|null $values The token value
72 *
73 * @return Boolean
74 */
75 public function test($type, $values = null)
76 {
77 if (null === $values && !is_int($type)) {
78 $values = $type;
79 $type = self::NAME_TYPE;
80 }
81
82 return ($this->type === $type) && (
83 null === $values ||
84 (is_array($values) && in_array($this->value, $values)) ||
85 $this->value == $values
86 );
87 }
88
89 /**
90 * Gets the line.
91 *
92 * @return integer The source line
93 */
94 public function getLine()
95 {
96 return $this->lineno;
97 }
98
99 /**
100 * Gets the token type.
101 *
102 * @return integer The token type
103 */
104 public function getType()
105 {
106 return $this->type;
107 }
108
109 /**
110 * Gets the token value.
111 *
112 * @return string The token value
113 */
114 public function getValue()
115 {
116 return $this->value;
117 }
118
119 /**
120 * Returns the constant representation (internal) of a given type.
121 *
122 * @param integer $type The type as an integer
123 * @param Boolean $short Whether to return a short representation or not
124 * @param integer $line The code line
125 *
126 * @return string The string representation
127 */
128 public static function typeToString($type, $short = false, $line = -1)
129 {
130 switch ($type) {
131 case self::EOF_TYPE:
132 $name = 'EOF_TYPE';
133 break;
134 case self::TEXT_TYPE:
135 $name = 'TEXT_TYPE';
136 break;
137 case self::BLOCK_START_TYPE:
138 $name = 'BLOCK_START_TYPE';
139 break;
140 case self::VAR_START_TYPE:
141 $name = 'VAR_START_TYPE';
142 break;
143 case self::BLOCK_END_TYPE:
144 $name = 'BLOCK_END_TYPE';
145 break;
146 case self::VAR_END_TYPE:
147 $name = 'VAR_END_TYPE';
148 break;
149 case self::NAME_TYPE:
150 $name = 'NAME_TYPE';
151 break;
152 case self::NUMBER_TYPE:
153 $name = 'NUMBER_TYPE';
154 break;
155 case self::STRING_TYPE:
156 $name = 'STRING_TYPE';
157 break;
158 case self::OPERATOR_TYPE:
159 $name = 'OPERATOR_TYPE';
160 break;
161 case self::PUNCTUATION_TYPE:
162 $name = 'PUNCTUATION_TYPE';
163 break;
164 case self::INTERPOLATION_START_TYPE:
165 $name = 'INTERPOLATION_START_TYPE';
166 break;
167 case self::INTERPOLATION_END_TYPE:
168 $name = 'INTERPOLATION_END_TYPE';
169 break;
170 default:
171 throw new LogicException(sprintf('Token of type "%s" does not exist.', $type));
172 }
173
174 return $short ? $name : 'Twig_Token::'.$name;
175 }
176
177 /**
178 * Returns the english representation of a given type.
179 *
180 * @param integer $type The type as an integer
181 * @param integer $line The code line
182 *
183 * @return string The string representation
184 */
185 public static function typeToEnglish($type, $line = -1)
186 {
187 switch ($type) {
188 case self::EOF_TYPE:
189 return 'end of template';
190 case self::TEXT_TYPE:
191 return 'text';
192 case self::BLOCK_START_TYPE:
193 return 'begin of statement block';
194 case self::VAR_START_TYPE:
195 return 'begin of print statement';
196 case self::BLOCK_END_TYPE:
197 return 'end of statement block';
198 case self::VAR_END_TYPE:
199 return 'end of print statement';
200 case self::NAME_TYPE:
201 return 'name';
202 case self::NUMBER_TYPE:
203 return 'number';
204 case self::STRING_TYPE:
205 return 'string';
206 case self::OPERATOR_TYPE:
207 return 'operator';
208 case self::PUNCTUATION_TYPE:
209 return 'punctuation';
210 case self::INTERPOLATION_START_TYPE:
211 return 'begin of string interpolation';
212 case self::INTERPOLATION_END_TYPE:
213 return 'end of string interpolation';
214 default:
215 throw new LogicException(sprintf('Token of type "%s" does not exist.', $type));
216 }
217 }
218}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser.php b/vendor/twig/twig/lib/Twig/TokenParser.php
new file mode 100644
index 00000000..decebd5e
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Base class for all token parsers.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17abstract class Twig_TokenParser implements Twig_TokenParserInterface
18{
19 /**
20 * @var Twig_Parser
21 */
22 protected $parser;
23
24 /**
25 * Sets the parser associated with this token parser
26 *
27 * @param $parser A Twig_Parser instance
28 */
29 public function setParser(Twig_Parser $parser)
30 {
31 $this->parser = $parser;
32 }
33}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php b/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php
new file mode 100644
index 00000000..27560288
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/AutoEscape.php
@@ -0,0 +1,89 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Marks a section of a template to be escaped or not.
14 *
15 * <pre>
16 * {% autoescape true %}
17 * Everything will be automatically escaped in this block
18 * {% endautoescape %}
19 *
20 * {% autoescape false %}
21 * Everything will be outputed as is in this block
22 * {% endautoescape %}
23 *
24 * {% autoescape true js %}
25 * Everything will be automatically escaped in this block
26 * using the js escaping strategy
27 * {% endautoescape %}
28 * </pre>
29 */
30class Twig_TokenParser_AutoEscape extends Twig_TokenParser
31{
32 /**
33 * Parses a token and returns a node.
34 *
35 * @param Twig_Token $token A Twig_Token instance
36 *
37 * @return Twig_NodeInterface A Twig_NodeInterface instance
38 */
39 public function parse(Twig_Token $token)
40 {
41 $lineno = $token->getLine();
42 $stream = $this->parser->getStream();
43
44 if ($stream->test(Twig_Token::BLOCK_END_TYPE)) {
45 $value = 'html';
46 } else {
47 $expr = $this->parser->getExpressionParser()->parseExpression();
48 if (!$expr instanceof Twig_Node_Expression_Constant) {
49 throw new Twig_Error_Syntax('An escaping strategy must be a string or a Boolean.', $stream->getCurrent()->getLine(), $stream->getFilename());
50 }
51 $value = $expr->getAttribute('value');
52
53 $compat = true === $value || false === $value;
54
55 if (true === $value) {
56 $value = 'html';
57 }
58
59 if ($compat && $stream->test(Twig_Token::NAME_TYPE)) {
60 if (false === $value) {
61 throw new Twig_Error_Syntax('Unexpected escaping strategy as you set autoescaping to false.', $stream->getCurrent()->getLine(), $stream->getFilename());
62 }
63
64 $value = $stream->next()->getValue();
65 }
66 }
67
68 $stream->expect(Twig_Token::BLOCK_END_TYPE);
69 $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
70 $stream->expect(Twig_Token::BLOCK_END_TYPE);
71
72 return new Twig_Node_AutoEscape($value, $body, $lineno, $this->getTag());
73 }
74
75 public function decideBlockEnd(Twig_Token $token)
76 {
77 return $token->test('endautoescape');
78 }
79
80 /**
81 * Gets the tag name associated with this token parser.
82 *
83 * @return string The tag name
84 */
85 public function getTag()
86 {
87 return 'autoescape';
88 }
89}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Block.php b/vendor/twig/twig/lib/Twig/TokenParser/Block.php
new file mode 100644
index 00000000..a2e017f3
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Block.php
@@ -0,0 +1,83 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Marks a section of a template as being reusable.
15 *
16 * <pre>
17 * {% block head %}
18 * <link rel="stylesheet" href="style.css" />
19 * <title>{% block title %}{% endblock %} - My Webpage</title>
20 * {% endblock %}
21 * </pre>
22 */
23class Twig_TokenParser_Block extends Twig_TokenParser
24{
25 /**
26 * Parses a token and returns a node.
27 *
28 * @param Twig_Token $token A Twig_Token instance
29 *
30 * @return Twig_NodeInterface A Twig_NodeInterface instance
31 */
32 public function parse(Twig_Token $token)
33 {
34 $lineno = $token->getLine();
35 $stream = $this->parser->getStream();
36 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
37 if ($this->parser->hasBlock($name)) {
38 throw new Twig_Error_Syntax(sprintf("The block '$name' has already been defined line %d", $this->parser->getBlock($name)->getLine()), $stream->getCurrent()->getLine(), $stream->getFilename());
39 }
40 $this->parser->setBlock($name, $block = new Twig_Node_Block($name, new Twig_Node(array()), $lineno));
41 $this->parser->pushLocalScope();
42 $this->parser->pushBlockStack($name);
43
44 if ($stream->test(Twig_Token::BLOCK_END_TYPE)) {
45 $stream->next();
46
47 $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
48 if ($stream->test(Twig_Token::NAME_TYPE)) {
49 $value = $stream->next()->getValue();
50
51 if ($value != $name) {
52 throw new Twig_Error_Syntax(sprintf("Expected endblock for block '$name' (but %s given)", $value), $stream->getCurrent()->getLine(), $stream->getFilename());
53 }
54 }
55 } else {
56 $body = new Twig_Node(array(
57 new Twig_Node_Print($this->parser->getExpressionParser()->parseExpression(), $lineno),
58 ));
59 }
60 $stream->expect(Twig_Token::BLOCK_END_TYPE);
61
62 $block->setNode('body', $body);
63 $this->parser->popBlockStack();
64 $this->parser->popLocalScope();
65
66 return new Twig_Node_BlockReference($name, $lineno, $this->getTag());
67 }
68
69 public function decideBlockEnd(Twig_Token $token)
70 {
71 return $token->test('endblock');
72 }
73
74 /**
75 * Gets the tag name associated with this token parser.
76 *
77 * @return string The tag name
78 */
79 public function getTag()
80 {
81 return 'block';
82 }
83}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Do.php b/vendor/twig/twig/lib/Twig/TokenParser/Do.php
new file mode 100644
index 00000000..f50939dd
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Do.php
@@ -0,0 +1,42 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Evaluates an expression, discarding the returned value.
14 */
15class Twig_TokenParser_Do extends Twig_TokenParser
16{
17 /**
18 * Parses a token and returns a node.
19 *
20 * @param Twig_Token $token A Twig_Token instance
21 *
22 * @return Twig_NodeInterface A Twig_NodeInterface instance
23 */
24 public function parse(Twig_Token $token)
25 {
26 $expr = $this->parser->getExpressionParser()->parseExpression();
27
28 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
29
30 return new Twig_Node_Do($expr, $token->getLine(), $this->getTag());
31 }
32
33 /**
34 * Gets the tag name associated with this token parser.
35 *
36 * @return string The tag name
37 */
38 public function getTag()
39 {
40 return 'do';
41 }
42}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Embed.php b/vendor/twig/twig/lib/Twig/TokenParser/Embed.php
new file mode 100644
index 00000000..69cb5f35
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Embed.php
@@ -0,0 +1,66 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2012 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Embeds a template.
14 */
15class Twig_TokenParser_Embed extends Twig_TokenParser_Include
16{
17 /**
18 * Parses a token and returns a node.
19 *
20 * @param Twig_Token $token A Twig_Token instance
21 *
22 * @return Twig_NodeInterface A Twig_NodeInterface instance
23 */
24 public function parse(Twig_Token $token)
25 {
26 $stream = $this->parser->getStream();
27
28 $parent = $this->parser->getExpressionParser()->parseExpression();
29
30 list($variables, $only, $ignoreMissing) = $this->parseArguments();
31
32 // inject a fake parent to make the parent() function work
33 $stream->injectTokens(array(
34 new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', $token->getLine()),
35 new Twig_Token(Twig_Token::NAME_TYPE, 'extends', $token->getLine()),
36 new Twig_Token(Twig_Token::STRING_TYPE, '__parent__', $token->getLine()),
37 new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', $token->getLine()),
38 ));
39
40 $module = $this->parser->parse($stream, array($this, 'decideBlockEnd'), true);
41
42 // override the parent with the correct one
43 $module->setNode('parent', $parent);
44
45 $this->parser->embedTemplate($module);
46
47 $stream->expect(Twig_Token::BLOCK_END_TYPE);
48
49 return new Twig_Node_Embed($module->getAttribute('filename'), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
50 }
51
52 public function decideBlockEnd(Twig_Token $token)
53 {
54 return $token->test('endembed');
55 }
56
57 /**
58 * Gets the tag name associated with this token parser.
59 *
60 * @return string The tag name
61 */
62 public function getTag()
63 {
64 return 'embed';
65 }
66}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Extends.php b/vendor/twig/twig/lib/Twig/TokenParser/Extends.php
new file mode 100644
index 00000000..f5ecee21
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Extends.php
@@ -0,0 +1,52 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Extends a template by another one.
15 *
16 * <pre>
17 * {% extends "base.html" %}
18 * </pre>
19 */
20class Twig_TokenParser_Extends extends Twig_TokenParser
21{
22 /**
23 * Parses a token and returns a node.
24 *
25 * @param Twig_Token $token A Twig_Token instance
26 *
27 * @return Twig_NodeInterface A Twig_NodeInterface instance
28 */
29 public function parse(Twig_Token $token)
30 {
31 if (!$this->parser->isMainScope()) {
32 throw new Twig_Error_Syntax('Cannot extend from a block', $token->getLine(), $this->parser->getFilename());
33 }
34
35 if (null !== $this->parser->getParent()) {
36 throw new Twig_Error_Syntax('Multiple extends tags are forbidden', $token->getLine(), $this->parser->getFilename());
37 }
38 $this->parser->setParent($this->parser->getExpressionParser()->parseExpression());
39
40 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
41 }
42
43 /**
44 * Gets the tag name associated with this token parser.
45 *
46 * @return string The tag name
47 */
48 public function getTag()
49 {
50 return 'extends';
51 }
52}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Filter.php b/vendor/twig/twig/lib/Twig/TokenParser/Filter.php
new file mode 100644
index 00000000..2b97475a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Filter.php
@@ -0,0 +1,61 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Filters a section of a template by applying filters.
14 *
15 * <pre>
16 * {% filter upper %}
17 * This text becomes uppercase
18 * {% endfilter %}
19 * </pre>
20 */
21class Twig_TokenParser_Filter extends Twig_TokenParser
22{
23 /**
24 * Parses a token and returns a node.
25 *
26 * @param Twig_Token $token A Twig_Token instance
27 *
28 * @return Twig_NodeInterface A Twig_NodeInterface instance
29 */
30 public function parse(Twig_Token $token)
31 {
32 $name = $this->parser->getVarName();
33 $ref = new Twig_Node_Expression_BlockReference(new Twig_Node_Expression_Constant($name, $token->getLine()), true, $token->getLine(), $this->getTag());
34
35 $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag());
36 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
37
38 $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
39 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
40
41 $block = new Twig_Node_Block($name, $body, $token->getLine());
42 $this->parser->setBlock($name, $block);
43
44 return new Twig_Node_Print($filter, $token->getLine(), $this->getTag());
45 }
46
47 public function decideBlockEnd(Twig_Token $token)
48 {
49 return $token->test('endfilter');
50 }
51
52 /**
53 * Gets the tag name associated with this token parser.
54 *
55 * @return string The tag name
56 */
57 public function getTag()
58 {
59 return 'filter';
60 }
61}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Flush.php b/vendor/twig/twig/lib/Twig/TokenParser/Flush.php
new file mode 100644
index 00000000..4e15e785
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Flush.php
@@ -0,0 +1,42 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Flushes the output to the client.
14 *
15 * @see flush()
16 */
17class Twig_TokenParser_Flush extends Twig_TokenParser
18{
19 /**
20 * Parses a token and returns a node.
21 *
22 * @param Twig_Token $token A Twig_Token instance
23 *
24 * @return Twig_NodeInterface A Twig_NodeInterface instance
25 */
26 public function parse(Twig_Token $token)
27 {
28 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
29
30 return new Twig_Node_Flush($token->getLine(), $this->getTag());
31 }
32
33 /**
34 * Gets the tag name associated with this token parser.
35 *
36 * @return string The tag name
37 */
38 public function getTag()
39 {
40 return 'flush';
41 }
42}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/For.php b/vendor/twig/twig/lib/Twig/TokenParser/For.php
new file mode 100644
index 00000000..98a6d079
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/For.php
@@ -0,0 +1,136 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Loops over each item of a sequence.
15 *
16 * <pre>
17 * <ul>
18 * {% for user in users %}
19 * <li>{{ user.username|e }}</li>
20 * {% endfor %}
21 * </ul>
22 * </pre>
23 */
24class Twig_TokenParser_For extends Twig_TokenParser
25{
26 /**
27 * Parses a token and returns a node.
28 *
29 * @param Twig_Token $token A Twig_Token instance
30 *
31 * @return Twig_NodeInterface A Twig_NodeInterface instance
32 */
33 public function parse(Twig_Token $token)
34 {
35 $lineno = $token->getLine();
36 $stream = $this->parser->getStream();
37 $targets = $this->parser->getExpressionParser()->parseAssignmentExpression();
38 $stream->expect(Twig_Token::OPERATOR_TYPE, 'in');
39 $seq = $this->parser->getExpressionParser()->parseExpression();
40
41 $ifexpr = null;
42 if ($stream->test(Twig_Token::NAME_TYPE, 'if')) {
43 $stream->next();
44 $ifexpr = $this->parser->getExpressionParser()->parseExpression();
45 }
46
47 $stream->expect(Twig_Token::BLOCK_END_TYPE);
48 $body = $this->parser->subparse(array($this, 'decideForFork'));
49 if ($stream->next()->getValue() == 'else') {
50 $stream->expect(Twig_Token::BLOCK_END_TYPE);
51 $else = $this->parser->subparse(array($this, 'decideForEnd'), true);
52 } else {
53 $else = null;
54 }
55 $stream->expect(Twig_Token::BLOCK_END_TYPE);
56
57 if (count($targets) > 1) {
58 $keyTarget = $targets->getNode(0);
59 $keyTarget = new Twig_Node_Expression_AssignName($keyTarget->getAttribute('name'), $keyTarget->getLine());
60 $valueTarget = $targets->getNode(1);
61 $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
62 } else {
63 $keyTarget = new Twig_Node_Expression_AssignName('_key', $lineno);
64 $valueTarget = $targets->getNode(0);
65 $valueTarget = new Twig_Node_Expression_AssignName($valueTarget->getAttribute('name'), $valueTarget->getLine());
66 }
67
68 if ($ifexpr) {
69 $this->checkLoopUsageCondition($stream, $ifexpr);
70 $this->checkLoopUsageBody($stream, $body);
71 }
72
73 return new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag());
74 }
75
76 public function decideForFork(Twig_Token $token)
77 {
78 return $token->test(array('else', 'endfor'));
79 }
80
81 public function decideForEnd(Twig_Token $token)
82 {
83 return $token->test('endfor');
84 }
85
86 // the loop variable cannot be used in the condition
87 protected function checkLoopUsageCondition(Twig_TokenStream $stream, Twig_NodeInterface $node)
88 {
89 if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
90 throw new Twig_Error_Syntax('The "loop" variable cannot be used in a looping condition', $node->getLine(), $stream->getFilename());
91 }
92
93 foreach ($node as $n) {
94 if (!$n) {
95 continue;
96 }
97
98 $this->checkLoopUsageCondition($stream, $n);
99 }
100 }
101
102 // check usage of non-defined loop-items
103 // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include)
104 protected function checkLoopUsageBody(Twig_TokenStream $stream, Twig_NodeInterface $node)
105 {
106 if ($node instanceof Twig_Node_Expression_GetAttr && $node->getNode('node') instanceof Twig_Node_Expression_Name && 'loop' == $node->getNode('node')->getAttribute('name')) {
107 $attribute = $node->getNode('attribute');
108 if ($attribute instanceof Twig_Node_Expression_Constant && in_array($attribute->getAttribute('value'), array('length', 'revindex0', 'revindex', 'last'))) {
109 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());
110 }
111 }
112
113 // should check for parent.loop.XXX usage
114 if ($node instanceof Twig_Node_For) {
115 return;
116 }
117
118 foreach ($node as $n) {
119 if (!$n) {
120 continue;
121 }
122
123 $this->checkLoopUsageBody($stream, $n);
124 }
125 }
126
127 /**
128 * Gets the tag name associated with this token parser.
129 *
130 * @return string The tag name
131 */
132 public function getTag()
133 {
134 return 'for';
135 }
136}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/From.php b/vendor/twig/twig/lib/Twig/TokenParser/From.php
new file mode 100644
index 00000000..a54054db
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/From.php
@@ -0,0 +1,74 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Imports macros.
14 *
15 * <pre>
16 * {% from 'forms.html' import forms %}
17 * </pre>
18 */
19class Twig_TokenParser_From extends Twig_TokenParser
20{
21 /**
22 * Parses a token and returns a node.
23 *
24 * @param Twig_Token $token A Twig_Token instance
25 *
26 * @return Twig_NodeInterface A Twig_NodeInterface instance
27 */
28 public function parse(Twig_Token $token)
29 {
30 $macro = $this->parser->getExpressionParser()->parseExpression();
31 $stream = $this->parser->getStream();
32 $stream->expect('import');
33
34 $targets = array();
35 do {
36 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
37
38 $alias = $name;
39 if ($stream->test('as')) {
40 $stream->next();
41
42 $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
43 }
44
45 $targets[$name] = $alias;
46
47 if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
48 break;
49 }
50
51 $stream->next();
52 } while (true);
53
54 $stream->expect(Twig_Token::BLOCK_END_TYPE);
55
56 $node = new Twig_Node_Import($macro, new Twig_Node_Expression_AssignName($this->parser->getVarName(), $token->getLine()), $token->getLine(), $this->getTag());
57
58 foreach ($targets as $name => $alias) {
59 $this->parser->addImportedSymbol('function', $alias, 'get'.$name, $node->getNode('var'));
60 }
61
62 return $node;
63 }
64
65 /**
66 * Gets the tag name associated with this token parser.
67 *
68 * @return string The tag name
69 */
70 public function getTag()
71 {
72 return 'from';
73 }
74}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/If.php b/vendor/twig/twig/lib/Twig/TokenParser/If.php
new file mode 100644
index 00000000..3d7d1f51
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/If.php
@@ -0,0 +1,94 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Tests a condition.
15 *
16 * <pre>
17 * {% if users %}
18 * <ul>
19 * {% for user in users %}
20 * <li>{{ user.username|e }}</li>
21 * {% endfor %}
22 * </ul>
23 * {% endif %}
24 * </pre>
25 */
26class Twig_TokenParser_If extends Twig_TokenParser
27{
28 /**
29 * Parses a token and returns a node.
30 *
31 * @param Twig_Token $token A Twig_Token instance
32 *
33 * @return Twig_NodeInterface A Twig_NodeInterface instance
34 */
35 public function parse(Twig_Token $token)
36 {
37 $lineno = $token->getLine();
38 $expr = $this->parser->getExpressionParser()->parseExpression();
39 $stream = $this->parser->getStream();
40 $stream->expect(Twig_Token::BLOCK_END_TYPE);
41 $body = $this->parser->subparse(array($this, 'decideIfFork'));
42 $tests = array($expr, $body);
43 $else = null;
44
45 $end = false;
46 while (!$end) {
47 switch ($stream->next()->getValue()) {
48 case 'else':
49 $stream->expect(Twig_Token::BLOCK_END_TYPE);
50 $else = $this->parser->subparse(array($this, 'decideIfEnd'));
51 break;
52
53 case 'elseif':
54 $expr = $this->parser->getExpressionParser()->parseExpression();
55 $stream->expect(Twig_Token::BLOCK_END_TYPE);
56 $body = $this->parser->subparse(array($this, 'decideIfFork'));
57 $tests[] = $expr;
58 $tests[] = $body;
59 break;
60
61 case 'endif':
62 $end = true;
63 break;
64
65 default:
66 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());
67 }
68 }
69
70 $stream->expect(Twig_Token::BLOCK_END_TYPE);
71
72 return new Twig_Node_If(new Twig_Node($tests), $else, $lineno, $this->getTag());
73 }
74
75 public function decideIfFork(Twig_Token $token)
76 {
77 return $token->test(array('elseif', 'else', 'endif'));
78 }
79
80 public function decideIfEnd(Twig_Token $token)
81 {
82 return $token->test(array('endif'));
83 }
84
85 /**
86 * Gets the tag name associated with this token parser.
87 *
88 * @return string The tag name
89 */
90 public function getTag()
91 {
92 return 'if';
93 }
94}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Import.php b/vendor/twig/twig/lib/Twig/TokenParser/Import.php
new file mode 100644
index 00000000..e7050c70
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Import.php
@@ -0,0 +1,49 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Imports macros.
14 *
15 * <pre>
16 * {% import 'forms.html' as forms %}
17 * </pre>
18 */
19class Twig_TokenParser_Import extends Twig_TokenParser
20{
21 /**
22 * Parses a token and returns a node.
23 *
24 * @param Twig_Token $token A Twig_Token instance
25 *
26 * @return Twig_NodeInterface A Twig_NodeInterface instance
27 */
28 public function parse(Twig_Token $token)
29 {
30 $macro = $this->parser->getExpressionParser()->parseExpression();
31 $this->parser->getStream()->expect('as');
32 $var = new Twig_Node_Expression_AssignName($this->parser->getStream()->expect(Twig_Token::NAME_TYPE)->getValue(), $token->getLine());
33 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
34
35 $this->parser->addImportedSymbol('template', $var->getAttribute('name'));
36
37 return new Twig_Node_Import($macro, $var, $token->getLine(), $this->getTag());
38 }
39
40 /**
41 * Gets the tag name associated with this token parser.
42 *
43 * @return string The tag name
44 */
45 public function getTag()
46 {
47 return 'import';
48 }
49}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Include.php b/vendor/twig/twig/lib/Twig/TokenParser/Include.php
new file mode 100644
index 00000000..4a317868
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Include.php
@@ -0,0 +1,80 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Includes a template.
15 *
16 * <pre>
17 * {% include 'header.html' %}
18 * Body
19 * {% include 'footer.html' %}
20 * </pre>
21 */
22class Twig_TokenParser_Include extends Twig_TokenParser
23{
24 /**
25 * Parses a token and returns a node.
26 *
27 * @param Twig_Token $token A Twig_Token instance
28 *
29 * @return Twig_NodeInterface A Twig_NodeInterface instance
30 */
31 public function parse(Twig_Token $token)
32 {
33 $expr = $this->parser->getExpressionParser()->parseExpression();
34
35 list($variables, $only, $ignoreMissing) = $this->parseArguments();
36
37 return new Twig_Node_Include($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag());
38 }
39
40 protected function parseArguments()
41 {
42 $stream = $this->parser->getStream();
43
44 $ignoreMissing = false;
45 if ($stream->test(Twig_Token::NAME_TYPE, 'ignore')) {
46 $stream->next();
47 $stream->expect(Twig_Token::NAME_TYPE, 'missing');
48
49 $ignoreMissing = true;
50 }
51
52 $variables = null;
53 if ($stream->test(Twig_Token::NAME_TYPE, 'with')) {
54 $stream->next();
55
56 $variables = $this->parser->getExpressionParser()->parseExpression();
57 }
58
59 $only = false;
60 if ($stream->test(Twig_Token::NAME_TYPE, 'only')) {
61 $stream->next();
62
63 $only = true;
64 }
65
66 $stream->expect(Twig_Token::BLOCK_END_TYPE);
67
68 return array($variables, $only, $ignoreMissing);
69 }
70
71 /**
72 * Gets the tag name associated with this token parser.
73 *
74 * @return string The tag name
75 */
76 public function getTag()
77 {
78 return 'include';
79 }
80}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Macro.php b/vendor/twig/twig/lib/Twig/TokenParser/Macro.php
new file mode 100644
index 00000000..82b4fa6d
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Macro.php
@@ -0,0 +1,68 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Defines a macro.
14 *
15 * <pre>
16 * {% macro input(name, value, type, size) %}
17 * <input type="{{ type|default('text') }}" name="{{ name }}" value="{{ value|e }}" size="{{ size|default(20) }}" />
18 * {% endmacro %}
19 * </pre>
20 */
21class Twig_TokenParser_Macro extends Twig_TokenParser
22{
23 /**
24 * Parses a token and returns a node.
25 *
26 * @param Twig_Token $token A Twig_Token instance
27 *
28 * @return Twig_NodeInterface A Twig_NodeInterface instance
29 */
30 public function parse(Twig_Token $token)
31 {
32 $lineno = $token->getLine();
33 $stream = $this->parser->getStream();
34 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
35
36 $arguments = $this->parser->getExpressionParser()->parseArguments(true, true);
37
38 $stream->expect(Twig_Token::BLOCK_END_TYPE);
39 $this->parser->pushLocalScope();
40 $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
41 if ($stream->test(Twig_Token::NAME_TYPE)) {
42 $value = $stream->next()->getValue();
43
44 if ($value != $name) {
45 throw new Twig_Error_Syntax(sprintf("Expected endmacro for macro '$name' (but %s given)", $value), $stream->getCurrent()->getLine(), $stream->getFilename());
46 }
47 }
48 $this->parser->popLocalScope();
49 $stream->expect(Twig_Token::BLOCK_END_TYPE);
50
51 $this->parser->setMacro($name, new Twig_Node_Macro($name, new Twig_Node_Body(array($body)), $arguments, $lineno, $this->getTag()));
52 }
53
54 public function decideBlockEnd(Twig_Token $token)
55 {
56 return $token->test('endmacro');
57 }
58
59 /**
60 * Gets the tag name associated with this token parser.
61 *
62 * @return string The tag name
63 */
64 public function getTag()
65 {
66 return 'macro';
67 }
68}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php b/vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php
new file mode 100644
index 00000000..9457325a
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Sandbox.php
@@ -0,0 +1,68 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Marks a section of a template as untrusted code that must be evaluated in the sandbox mode.
14 *
15 * <pre>
16 * {% sandbox %}
17 * {% include 'user.html' %}
18 * {% endsandbox %}
19 * </pre>
20 *
21 * @see http://www.twig-project.org/doc/api.html#sandbox-extension for details
22 */
23class Twig_TokenParser_Sandbox extends Twig_TokenParser
24{
25 /**
26 * Parses a token and returns a node.
27 *
28 * @param Twig_Token $token A Twig_Token instance
29 *
30 * @return Twig_NodeInterface A Twig_NodeInterface instance
31 */
32 public function parse(Twig_Token $token)
33 {
34 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
35 $body = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
36 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
37
38 // in a sandbox tag, only include tags are allowed
39 if (!$body instanceof Twig_Node_Include) {
40 foreach ($body as $node) {
41 if ($node instanceof Twig_Node_Text && ctype_space($node->getAttribute('data'))) {
42 continue;
43 }
44
45 if (!$node instanceof Twig_Node_Include) {
46 throw new Twig_Error_Syntax('Only "include" tags are allowed within a "sandbox" section', $node->getLine(), $this->parser->getFilename());
47 }
48 }
49 }
50
51 return new Twig_Node_Sandbox($body, $token->getLine(), $this->getTag());
52 }
53
54 public function decideBlockEnd(Twig_Token $token)
55 {
56 return $token->test('endsandbox');
57 }
58
59 /**
60 * Gets the tag name associated with this token parser.
61 *
62 * @return string The tag name
63 */
64 public function getTag()
65 {
66 return 'sandbox';
67 }
68}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Set.php b/vendor/twig/twig/lib/Twig/TokenParser/Set.php
new file mode 100644
index 00000000..70e0b41b
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Set.php
@@ -0,0 +1,84 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Defines a variable.
14 *
15 * <pre>
16 * {% set foo = 'foo' %}
17 *
18 * {% set foo = [1, 2] %}
19 *
20 * {% set foo = {'foo': 'bar'} %}
21 *
22 * {% set foo = 'foo' ~ 'bar' %}
23 *
24 * {% set foo, bar = 'foo', 'bar' %}
25 *
26 * {% set foo %}Some content{% endset %}
27 * </pre>
28 */
29class Twig_TokenParser_Set extends Twig_TokenParser
30{
31 /**
32 * Parses a token and returns a node.
33 *
34 * @param Twig_Token $token A Twig_Token instance
35 *
36 * @return Twig_NodeInterface A Twig_NodeInterface instance
37 */
38 public function parse(Twig_Token $token)
39 {
40 $lineno = $token->getLine();
41 $stream = $this->parser->getStream();
42 $names = $this->parser->getExpressionParser()->parseAssignmentExpression();
43
44 $capture = false;
45 if ($stream->test(Twig_Token::OPERATOR_TYPE, '=')) {
46 $stream->next();
47 $values = $this->parser->getExpressionParser()->parseMultitargetExpression();
48
49 $stream->expect(Twig_Token::BLOCK_END_TYPE);
50
51 if (count($names) !== count($values)) {
52 throw new Twig_Error_Syntax("When using set, you must have the same number of variables and assignments.", $stream->getCurrent()->getLine(), $stream->getFilename());
53 }
54 } else {
55 $capture = true;
56
57 if (count($names) > 1) {
58 throw new Twig_Error_Syntax("When using set with a block, you cannot have a multi-target.", $stream->getCurrent()->getLine(), $stream->getFilename());
59 }
60
61 $stream->expect(Twig_Token::BLOCK_END_TYPE);
62
63 $values = $this->parser->subparse(array($this, 'decideBlockEnd'), true);
64 $stream->expect(Twig_Token::BLOCK_END_TYPE);
65 }
66
67 return new Twig_Node_Set($capture, $names, $values, $lineno, $this->getTag());
68 }
69
70 public function decideBlockEnd(Twig_Token $token)
71 {
72 return $token->test('endset');
73 }
74
75 /**
76 * Gets the tag name associated with this token parser.
77 *
78 * @return string The tag name
79 */
80 public function getTag()
81 {
82 return 'set';
83 }
84}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php b/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php
new file mode 100644
index 00000000..1e3fa8f3
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Spaceless.php
@@ -0,0 +1,59 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Remove whitespaces between HTML tags.
14 *
15 * <pre>
16 * {% spaceless %}
17 * <div>
18 * <strong>foo</strong>
19 * </div>
20 * {% endspaceless %}
21 *
22 * {# output will be <div><strong>foo</strong></div> #}
23 * </pre>
24 */
25class Twig_TokenParser_Spaceless extends Twig_TokenParser
26{
27 /**
28 * Parses a token and returns a node.
29 *
30 * @param Twig_Token $token A Twig_Token instance
31 *
32 * @return Twig_NodeInterface A Twig_NodeInterface instance
33 */
34 public function parse(Twig_Token $token)
35 {
36 $lineno = $token->getLine();
37
38 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
39 $body = $this->parser->subparse(array($this, 'decideSpacelessEnd'), true);
40 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
41
42 return new Twig_Node_Spaceless($body, $lineno, $this->getTag());
43 }
44
45 public function decideSpacelessEnd(Twig_Token $token)
46 {
47 return $token->test('endspaceless');
48 }
49
50 /**
51 * Gets the tag name associated with this token parser.
52 *
53 * @return string The tag name
54 */
55 public function getTag()
56 {
57 return 'spaceless';
58 }
59}
diff --git a/vendor/twig/twig/lib/Twig/TokenParser/Use.php b/vendor/twig/twig/lib/Twig/TokenParser/Use.php
new file mode 100644
index 00000000..bc0e09ef
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParser/Use.php
@@ -0,0 +1,82 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2011 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Imports blocks defined in another template into the current template.
14 *
15 * <pre>
16 * {% extends "base.html" %}
17 *
18 * {% use "blocks.html" %}
19 *
20 * {% block title %}{% endblock %}
21 * {% block content %}{% endblock %}
22 * </pre>
23 *
24 * @see http://www.twig-project.org/doc/templates.html#horizontal-reuse for details.
25 */
26class Twig_TokenParser_Use extends Twig_TokenParser
27{
28 /**
29 * Parses a token and returns a node.
30 *
31 * @param Twig_Token $token A Twig_Token instance
32 *
33 * @return Twig_NodeInterface A Twig_NodeInterface instance
34 */
35 public function parse(Twig_Token $token)
36 {
37 $template = $this->parser->getExpressionParser()->parseExpression();
38 $stream = $this->parser->getStream();
39
40 if (!$template instanceof Twig_Node_Expression_Constant) {
41 throw new Twig_Error_Syntax('The template references in a "use" statement must be a string.', $stream->getCurrent()->getLine(), $stream->getFilename());
42 }
43
44 $targets = array();
45 if ($stream->test('with')) {
46 $stream->next();
47
48 do {
49 $name = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
50
51 $alias = $name;
52 if ($stream->test('as')) {
53 $stream->next();
54
55 $alias = $stream->expect(Twig_Token::NAME_TYPE)->getValue();
56 }
57
58 $targets[$name] = new Twig_Node_Expression_Constant($alias, -1);
59
60 if (!$stream->test(Twig_Token::PUNCTUATION_TYPE, ',')) {
61 break;
62 }
63
64 $stream->next();
65 } while (true);
66 }
67
68 $stream->expect(Twig_Token::BLOCK_END_TYPE);
69
70 $this->parser->addTrait(new Twig_Node(array('template' => $template, 'targets' => new Twig_Node($targets))));
71 }
72
73 /**
74 * Gets the tag name associated with this token parser.
75 *
76 * @return string The tag name
77 */
78 public function getTag()
79 {
80 return 'use';
81 }
82}
diff --git a/vendor/twig/twig/lib/Twig/TokenParserBroker.php b/vendor/twig/twig/lib/Twig/TokenParserBroker.php
new file mode 100644
index 00000000..ec3fba67
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParserBroker.php
@@ -0,0 +1,136 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 * (c) 2010 Arnaud Le Blanc
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Default implementation of a token parser broker.
15 *
16 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
17 * @deprecated since 1.12 (to be removed in 2.0)
18 */
19class Twig_TokenParserBroker implements Twig_TokenParserBrokerInterface
20{
21 protected $parser;
22 protected $parsers = array();
23 protected $brokers = array();
24
25 /**
26 * Constructor.
27 *
28 * @param array|Traversable $parsers A Traversable of Twig_TokenParserInterface instances
29 * @param array|Traversable $brokers A Traversable of Twig_TokenParserBrokerInterface instances
30 */
31 public function __construct($parsers = array(), $brokers = array())
32 {
33 foreach ($parsers as $parser) {
34 if (!$parser instanceof Twig_TokenParserInterface) {
35 throw new LogicException('$parsers must a an array of Twig_TokenParserInterface');
36 }
37 $this->parsers[$parser->getTag()] = $parser;
38 }
39 foreach ($brokers as $broker) {
40 if (!$broker instanceof Twig_TokenParserBrokerInterface) {
41 throw new LogicException('$brokers must a an array of Twig_TokenParserBrokerInterface');
42 }
43 $this->brokers[] = $broker;
44 }
45 }
46
47 /**
48 * Adds a TokenParser.
49 *
50 * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
51 */
52 public function addTokenParser(Twig_TokenParserInterface $parser)
53 {
54 $this->parsers[$parser->getTag()] = $parser;
55 }
56
57 /**
58 * Removes a TokenParser.
59 *
60 * @param Twig_TokenParserInterface $parser A Twig_TokenParserInterface instance
61 */
62 public function removeTokenParser(Twig_TokenParserInterface $parser)
63 {
64 $name = $parser->getTag();
65 if (isset($this->parsers[$name]) && $parser === $this->parsers[$name]) {
66 unset($this->parsers[$name]);
67 }
68 }
69
70 /**
71 * Adds a TokenParserBroker.
72 *
73 * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance
74 */
75 public function addTokenParserBroker(Twig_TokenParserBroker $broker)
76 {
77 $this->brokers[] = $broker;
78 }
79
80 /**
81 * Removes a TokenParserBroker.
82 *
83 * @param Twig_TokenParserBroker $broker A Twig_TokenParserBroker instance
84 */
85 public function removeTokenParserBroker(Twig_TokenParserBroker $broker)
86 {
87 if (false !== $pos = array_search($broker, $this->brokers)) {
88 unset($this->brokers[$pos]);
89 }
90 }
91
92 /**
93 * Gets a suitable TokenParser for a tag.
94 *
95 * First looks in parsers, then in brokers.
96 *
97 * @param string $tag A tag name
98 *
99 * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found
100 */
101 public function getTokenParser($tag)
102 {
103 if (isset($this->parsers[$tag])) {
104 return $this->parsers[$tag];
105 }
106 $broker = end($this->brokers);
107 while (false !== $broker) {
108 $parser = $broker->getTokenParser($tag);
109 if (null !== $parser) {
110 return $parser;
111 }
112 $broker = prev($this->brokers);
113 }
114 }
115
116 public function getParsers()
117 {
118 return $this->parsers;
119 }
120
121 public function getParser()
122 {
123 return $this->parser;
124 }
125
126 public function setParser(Twig_ParserInterface $parser)
127 {
128 $this->parser = $parser;
129 foreach ($this->parsers as $tokenParser) {
130 $tokenParser->setParser($parser);
131 }
132 foreach ($this->brokers as $broker) {
133 $broker->setParser($parser);
134 }
135 }
136}
diff --git a/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php b/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php
new file mode 100644
index 00000000..3f006e33
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParserBrokerInterface.php
@@ -0,0 +1,45 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 * (c) 2010 Arnaud Le Blanc
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Interface implemented by token parser brokers.
15 *
16 * Token parser brokers allows to implement custom logic in the process of resolving a token parser for a given tag name.
17 *
18 * @author Arnaud Le Blanc <arnaud.lb@gmail.com>
19 * @deprecated since 1.12 (to be removed in 2.0)
20 */
21interface Twig_TokenParserBrokerInterface
22{
23 /**
24 * Gets a TokenParser suitable for a tag.
25 *
26 * @param string $tag A tag name
27 *
28 * @return null|Twig_TokenParserInterface A Twig_TokenParserInterface or null if no suitable TokenParser was found
29 */
30 public function getTokenParser($tag);
31
32 /**
33 * Calls Twig_TokenParserInterface::setParser on all parsers the implementation knows of.
34 *
35 * @param Twig_ParserInterface $parser A Twig_ParserInterface interface
36 */
37 public function setParser(Twig_ParserInterface $parser);
38
39 /**
40 * Gets the Twig_ParserInterface.
41 *
42 * @return null|Twig_ParserInterface A Twig_ParserInterface instance or null
43 */
44 public function getParser();
45}
diff --git a/vendor/twig/twig/lib/Twig/TokenParserInterface.php b/vendor/twig/twig/lib/Twig/TokenParserInterface.php
new file mode 100644
index 00000000..bbde7714
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenParserInterface.php
@@ -0,0 +1,41 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2010 Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12/**
13 * Interface implemented by token parsers.
14 *
15 * @author Fabien Potencier <fabien@symfony.com>
16 */
17interface Twig_TokenParserInterface
18{
19 /**
20 * Sets the parser associated with this token parser
21 *
22 * @param $parser A Twig_Parser instance
23 */
24 public function setParser(Twig_Parser $parser);
25
26 /**
27 * Parses a token and returns a node.
28 *
29 * @param Twig_Token $token A Twig_Token instance
30 *
31 * @return Twig_NodeInterface A Twig_NodeInterface instance
32 */
33 public function parse(Twig_Token $token);
34
35 /**
36 * Gets the tag name associated with this token parser.
37 *
38 * @return string The tag name
39 */
40 public function getTag();
41}
diff --git a/vendor/twig/twig/lib/Twig/TokenStream.php b/vendor/twig/twig/lib/Twig/TokenStream.php
new file mode 100644
index 00000000..a78189f6
--- /dev/null
+++ b/vendor/twig/twig/lib/Twig/TokenStream.php
@@ -0,0 +1,144 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) 2009 Fabien Potencier
7 * (c) 2009 Armin Ronacher
8 *
9 * For the full copyright and license information, please view the LICENSE
10 * file that was distributed with this source code.
11 */
12
13/**
14 * Represents a token stream.
15 *
16 * @author Fabien Potencier <fabien@symfony.com>
17 */
18class Twig_TokenStream
19{
20 protected $tokens;
21 protected $current;
22 protected $filename;
23
24 /**
25 * Constructor.
26 *
27 * @param array $tokens An array of tokens
28 * @param string $filename The name of the filename which tokens are associated with
29 */
30 public function __construct(array $tokens, $filename = null)
31 {
32 $this->tokens = $tokens;
33 $this->current = 0;
34 $this->filename = $filename;
35 }
36
37 /**
38 * Returns a string representation of the token stream.
39 *
40 * @return string
41 */
42 public function __toString()
43 {
44 return implode("\n", $this->tokens);
45 }
46
47 public function injectTokens(array $tokens)
48 {
49 $this->tokens = array_merge(array_slice($this->tokens, 0, $this->current), $tokens, array_slice($this->tokens, $this->current));
50 }
51
52 /**
53 * Sets the pointer to the next token and returns the old one.
54 *
55 * @return Twig_Token
56 */
57 public function next()
58 {
59 if (!isset($this->tokens[++$this->current])) {
60 throw new Twig_Error_Syntax('Unexpected end of template', $this->tokens[$this->current - 1]->getLine(), $this->filename);
61 }
62
63 return $this->tokens[$this->current - 1];
64 }
65
66 /**
67 * Tests a token and returns it or throws a syntax error.
68 *
69 * @return Twig_Token
70 */
71 public function expect($type, $value = null, $message = null)
72 {
73 $token = $this->tokens[$this->current];
74 if (!$token->test($type, $value)) {
75 $line = $token->getLine();
76 throw new Twig_Error_Syntax(sprintf('%sUnexpected token "%s" of value "%s" ("%s" expected%s)',
77 $message ? $message.'. ' : '',
78 Twig_Token::typeToEnglish($token->getType(), $line), $token->getValue(),
79 Twig_Token::typeToEnglish($type, $line), $value ? sprintf(' with value "%s"', $value) : ''),
80 $line,
81 $this->filename
82 );
83 }
84 $this->next();
85
86 return $token;
87 }
88
89 /**
90 * Looks at the next token.
91 *
92 * @param integer $number
93 *
94 * @return Twig_Token
95 */
96 public function look($number = 1)
97 {
98 if (!isset($this->tokens[$this->current + $number])) {
99 throw new Twig_Error_Syntax('Unexpected end of template', $this->tokens[$this->current + $number - 1]->getLine(), $this->filename);
100 }
101
102 return $this->tokens[$this->current + $number];
103 }
104
105 /**
106 * Tests the current token
107 *
108 * @return bool
109 */
110 public function test($primary, $secondary = null)
111 {
112 return $this->tokens[$this->current]->test($primary, $secondary);
113 }
114
115 /**
116 * Checks if end of stream was reached
117 *
118 * @return bool
119 */
120 public function isEOF()
121 {
122 return $this->tokens[$this->current]->getType() === Twig_Token::EOF_TYPE;
123 }
124
125 /**
126 * Gets the current token
127 *
128 * @return Twig_Token
129 */
130 public function getCurrent()
131 {
132 return $this->tokens[$this->current];
133 }
134
135 /**
136 * Gets the filename associated with this stream
137 *
138 * @return string
139 */
140 public function getFilename()
141 {
142 return $this->filename;
143 }
144}
diff --git a/vendor/twig/twig/phpunit.xml.dist b/vendor/twig/twig/phpunit.xml.dist
new file mode 100644
index 00000000..6c5046f1
--- /dev/null
+++ b/vendor/twig/twig/phpunit.xml.dist
@@ -0,0 +1,25 @@
1<?xml version="1.0" encoding="UTF-8"?>
2
3<phpunit backupGlobals="false"
4 backupStaticAttributes="false"
5 colors="true"
6 convertErrorsToExceptions="true"
7 convertNoticesToExceptions="true"
8 convertWarningsToExceptions="true"
9 processIsolation="false"
10 stopOnFailure="false"
11 syntaxCheck="false"
12 bootstrap="test/bootstrap.php"
13>
14 <testsuites>
15 <testsuite name="Twig Test Suite">
16 <directory>./test/Twig/</directory>
17 </testsuite>
18 </testsuites>
19
20 <filter>
21 <whitelist>
22 <directory suffix=".php">./lib/Twig/</directory>
23 </whitelist>
24 </filter>
25</phpunit>
diff --git a/vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php b/vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php
new file mode 100644
index 00000000..c8b7999a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/AutoloaderTest.php
@@ -0,0 +1,21 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_AutoloaderTest extends PHPUnit_Framework_TestCase
13{
14 public function testAutoload()
15 {
16 $this->assertFalse(class_exists('FooBarFoo'), '->autoload() does not try to load classes that does not begin with Twig');
17
18 $autoloader = new Twig_Autoloader();
19 $this->assertNull($autoloader->autoload('Foo'), '->autoload() returns false if it is not able to load a class');
20 }
21}
diff --git a/vendor/twig/twig/test/Twig/Tests/CompilerTest.php b/vendor/twig/twig/test/Twig/Tests/CompilerTest.php
new file mode 100644
index 00000000..ebe79aef
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/CompilerTest.php
@@ -0,0 +1,33 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_CompilerTest extends PHPUnit_Framework_TestCase
13{
14 public function testReprNumericValueWithLocale()
15 {
16 $compiler = new Twig_Compiler(new Twig_Environment());
17
18 $locale = setlocale(LC_NUMERIC, 0);
19 if (false === $locale) {
20 $this->markTestSkipped('Your platform does not support locales.');
21 }
22
23 $required_locales = array('fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252');
24 if (false === setlocale(LC_ALL, $required_locales)) {
25 $this->markTestSkipped('Could not set any of required locales: ' . implode(", ", $required_locales));
26 }
27
28 $this->assertEquals('1.2', $compiler->repr(1.2)->getSource());
29 $this->assertContains('fr', strtolower(setlocale(LC_NUMERIC, 0)));
30
31 setlocale(LC_ALL, $locale);
32 }
33}
diff --git a/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php b/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php
new file mode 100644
index 00000000..22461b5d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/EnvironmentTest.php
@@ -0,0 +1,288 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_EnvironmentTest extends PHPUnit_Framework_TestCase
13{
14 /**
15 * @expectedException LogicException
16 * @expectedExceptionMessage You must set a loader first.
17 */
18 public function testRenderNoLoader()
19 {
20 $env = new Twig_Environment();
21 $env->render('test');
22 }
23
24 public function testAutoescapeOption()
25 {
26 $loader = new Twig_Loader_Array(array(
27 'html' => '{{ foo }} {{ foo }}',
28 'js' => '{{ bar }} {{ bar }}',
29 ));
30
31 $twig = new Twig_Environment($loader, array(
32 'debug' => true,
33 'cache' => false,
34 'autoescape' => array($this, 'escapingStrategyCallback'),
35 ));
36
37 $this->assertEquals('foo&lt;br/ &gt; foo&lt;br/ &gt;', $twig->render('html', array('foo' => 'foo<br/ >')));
38 $this->assertEquals('foo\x3Cbr\x2F\x20\x3E foo\x3Cbr\x2F\x20\x3E', $twig->render('js', array('bar' => 'foo<br/ >')));
39 }
40
41 public function escapingStrategyCallback($filename)
42 {
43 return $filename;
44 }
45
46 public function testGlobals()
47 {
48 // globals can be added after calling getGlobals
49 $twig = new Twig_Environment(new Twig_Loader_String());
50 $twig->addGlobal('foo', 'foo');
51 $globals = $twig->getGlobals();
52 $twig->addGlobal('foo', 'bar');
53 $globals = $twig->getGlobals();
54 $this->assertEquals('bar', $globals['foo']);
55
56 // globals can be modified after runtime init
57 $twig = new Twig_Environment(new Twig_Loader_String());
58 $twig->addGlobal('foo', 'foo');
59 $globals = $twig->getGlobals();
60 $twig->initRuntime();
61 $twig->addGlobal('foo', 'bar');
62 $globals = $twig->getGlobals();
63 $this->assertEquals('bar', $globals['foo']);
64
65 // globals can be modified after extensions init
66 $twig = new Twig_Environment(new Twig_Loader_String());
67 $twig->addGlobal('foo', 'foo');
68 $globals = $twig->getGlobals();
69 $twig->getFunctions();
70 $twig->addGlobal('foo', 'bar');
71 $globals = $twig->getGlobals();
72 $this->assertEquals('bar', $globals['foo']);
73
74 // globals can be modified after extensions and runtime init
75 $twig = new Twig_Environment(new Twig_Loader_String());
76 $twig->addGlobal('foo', 'foo');
77 $globals = $twig->getGlobals();
78 $twig->getFunctions();
79 $twig->initRuntime();
80 $twig->addGlobal('foo', 'bar');
81 $globals = $twig->getGlobals();
82 $this->assertEquals('bar', $globals['foo']);
83
84 $twig = new Twig_Environment(new Twig_Loader_String());
85 $twig->getGlobals();
86 $twig->addGlobal('foo', 'bar');
87 $template = $twig->loadTemplate('{{foo}}');
88 $this->assertEquals('bar', $template->render(array()));
89
90 /* to be uncomment in Twig 2.0
91 // globals cannot be added after runtime init
92 $twig = new Twig_Environment(new Twig_Loader_String());
93 $twig->addGlobal('foo', 'foo');
94 $globals = $twig->getGlobals();
95 $twig->initRuntime();
96 try {
97 $twig->addGlobal('bar', 'bar');
98 $this->fail();
99 } catch (LogicException $e) {
100 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
101 }
102
103 // globals cannot be added after extensions init
104 $twig = new Twig_Environment(new Twig_Loader_String());
105 $twig->addGlobal('foo', 'foo');
106 $globals = $twig->getGlobals();
107 $twig->getFunctions();
108 try {
109 $twig->addGlobal('bar', 'bar');
110 $this->fail();
111 } catch (LogicException $e) {
112 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
113 }
114
115 // globals cannot be added after extensions and runtime init
116 $twig = new Twig_Environment(new Twig_Loader_String());
117 $twig->addGlobal('foo', 'foo');
118 $globals = $twig->getGlobals();
119 $twig->getFunctions();
120 $twig->initRuntime();
121 try {
122 $twig->addGlobal('bar', 'bar');
123 $this->fail();
124 } catch (LogicException $e) {
125 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
126 }
127
128 // test adding globals after initRuntime without call to getGlobals
129 $twig = new Twig_Environment(new Twig_Loader_String());
130 $twig->initRuntime();
131 try {
132 $twig->addGlobal('bar', 'bar');
133 $this->fail();
134 } catch (LogicException $e) {
135 $this->assertFalse(array_key_exists('bar', $twig->getGlobals()));
136 }
137 */
138 }
139
140 public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate()
141 {
142 $options = array('cache' => sys_get_temp_dir().'/twig', 'auto_reload' => false, 'debug' => false);
143
144 // force compilation
145 $twig = new Twig_Environment(new Twig_Loader_String(), $options);
146 $cache = $twig->getCacheFilename('{{ foo }}');
147 if (!is_dir(dirname($cache))) {
148 mkdir(dirname($cache), 0777, true);
149 }
150 file_put_contents($cache, $twig->compileSource('{{ foo }}', '{{ foo }}'));
151
152 // check that extensions won't be initialized when rendering a template that is already in the cache
153 $twig = $this
154 ->getMockBuilder('Twig_Environment')
155 ->setConstructorArgs(array(new Twig_Loader_String(), $options))
156 ->setMethods(array('initExtensions'))
157 ->getMock()
158 ;
159
160 $twig->expects($this->never())->method('initExtensions');
161
162 // render template
163 $output = $twig->render('{{ foo }}', array('foo' => 'bar'));
164 $this->assertEquals('bar', $output);
165
166 unlink($cache);
167 }
168
169 public function testAddExtension()
170 {
171 $twig = new Twig_Environment(new Twig_Loader_String());
172 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
173
174 $this->assertArrayHasKey('test', $twig->getTags());
175 $this->assertArrayHasKey('foo_filter', $twig->getFilters());
176 $this->assertArrayHasKey('foo_function', $twig->getFunctions());
177 $this->assertArrayHasKey('foo_test', $twig->getTests());
178 $this->assertArrayHasKey('foo_unary', $twig->getUnaryOperators());
179 $this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators());
180 $this->assertArrayHasKey('foo_global', $twig->getGlobals());
181 $visitors = $twig->getNodeVisitors();
182 $this->assertEquals('Twig_Tests_EnvironmentTest_NodeVisitor', get_class($visitors[2]));
183 }
184
185 public function testRemoveExtension()
186 {
187 $twig = new Twig_Environment(new Twig_Loader_String());
188 $twig->addExtension(new Twig_Tests_EnvironmentTest_Extension());
189 $twig->removeExtension('environment_test');
190
191 $this->assertFalse(array_key_exists('test', $twig->getTags()));
192 $this->assertFalse(array_key_exists('foo_filter', $twig->getFilters()));
193 $this->assertFalse(array_key_exists('foo_function', $twig->getFunctions()));
194 $this->assertFalse(array_key_exists('foo_test', $twig->getTests()));
195 $this->assertFalse(array_key_exists('foo_unary', $twig->getUnaryOperators()));
196 $this->assertFalse(array_key_exists('foo_binary', $twig->getBinaryOperators()));
197 $this->assertFalse(array_key_exists('foo_global', $twig->getGlobals()));
198 $this->assertCount(2, $twig->getNodeVisitors());
199 }
200}
201
202class Twig_Tests_EnvironmentTest_Extension extends Twig_Extension
203{
204 public function getTokenParsers()
205 {
206 return array(
207 new Twig_Tests_EnvironmentTest_TokenParser(),
208 );
209 }
210
211 public function getNodeVisitors()
212 {
213 return array(
214 new Twig_Tests_EnvironmentTest_NodeVisitor(),
215 );
216 }
217
218 public function getFilters()
219 {
220 return array(
221 'foo_filter' => new Twig_Filter_Function('foo_filter'),
222 );
223 }
224
225 public function getTests()
226 {
227 return array(
228 'foo_test' => new Twig_Test_Function('foo_test'),
229 );
230 }
231
232 public function getFunctions()
233 {
234 return array(
235 'foo_function' => new Twig_Function_Function('foo_function'),
236 );
237 }
238
239 public function getOperators()
240 {
241 return array(
242 array('foo_unary' => array()),
243 array('foo_binary' => array()),
244 );
245 }
246
247 public function getGlobals()
248 {
249 return array(
250 'foo_global' => 'foo_global',
251 );
252 }
253
254 public function getName()
255 {
256 return 'environment_test';
257 }
258}
259
260class Twig_Tests_EnvironmentTest_TokenParser extends Twig_TokenParser
261{
262 public function parse(Twig_Token $token)
263 {
264 }
265
266 public function getTag()
267 {
268 return 'test';
269 }
270}
271
272class Twig_Tests_EnvironmentTest_NodeVisitor implements Twig_NodeVisitorInterface
273{
274 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
275 {
276 return $node;
277 }
278
279 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
280 {
281 return $node;
282 }
283
284 public function getPriority()
285 {
286 return 0;
287 }
288}
diff --git a/vendor/twig/twig/test/Twig/Tests/ErrorTest.php b/vendor/twig/twig/test/Twig/Tests/ErrorTest.php
new file mode 100644
index 00000000..9b286974
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/ErrorTest.php
@@ -0,0 +1,159 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_ErrorTest extends PHPUnit_Framework_TestCase
13{
14 public function testErrorWithObjectFilename()
15 {
16 $error = new Twig_Error('foo');
17 $error->setTemplateFile(new SplFileInfo(__FILE__));
18
19 $this->assertContains('test'.DIRECTORY_SEPARATOR.'Twig'.DIRECTORY_SEPARATOR.'Tests'.DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage());
20 }
21
22 public function testErrorWithArrayFilename()
23 {
24 $error = new Twig_Error('foo');
25 $error->setTemplateFile(array('foo' => 'bar'));
26
27 $this->assertEquals('foo in {"foo":"bar"}', $error->getMessage());
28 }
29
30 public function testTwigExceptionAddsFileAndLineWhenMissing()
31 {
32 $loader = new Twig_Loader_Array(array('index' => "\n\n{{ foo.bar }}\n\n\n{{ 'foo' }}"));
33 $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
34
35 $template = $twig->loadTemplate('index');
36
37 try {
38 $template->render(array());
39
40 $this->fail();
41 } catch (Twig_Error_Runtime $e) {
42 $this->assertEquals('Variable "foo" does not exist in "index" at line 3', $e->getMessage());
43 $this->assertEquals(3, $e->getTemplateLine());
44 $this->assertEquals('index', $e->getTemplateFile());
45 }
46 }
47
48 public function testRenderWrapsExceptions()
49 {
50 $loader = new Twig_Loader_Array(array('index' => "\n\n\n{{ foo.bar }}\n\n\n\n{{ 'foo' }}"));
51 $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
52
53 $template = $twig->loadTemplate('index');
54
55 try {
56 $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
57
58 $this->fail();
59 } catch (Twig_Error_Runtime $e) {
60 $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index" at line 4.', $e->getMessage());
61 $this->assertEquals(4, $e->getTemplateLine());
62 $this->assertEquals('index', $e->getTemplateFile());
63 }
64 }
65
66 public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritance()
67 {
68 $loader = new Twig_Loader_Array(array(
69 'index' => "{% extends 'base' %}
70 {% block content %}
71 {{ foo.bar }}
72 {% endblock %}
73 {% block foo %}
74 {{ foo.bar }}
75 {% endblock %}",
76 'base' => '{% block content %}{% endblock %}'
77 ));
78 $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
79
80 $template = $twig->loadTemplate('index');
81 try {
82 $template->render(array());
83
84 $this->fail();
85 } catch (Twig_Error_Runtime $e) {
86 $this->assertEquals('Variable "foo" does not exist in "index" at line 3', $e->getMessage());
87 $this->assertEquals(3, $e->getTemplateLine());
88 $this->assertEquals('index', $e->getTemplateFile());
89 }
90
91 try {
92 $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
93
94 $this->fail();
95 } catch (Twig_Error_Runtime $e) {
96 $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index" at line 3.', $e->getMessage());
97 $this->assertEquals(3, $e->getTemplateLine());
98 $this->assertEquals('index', $e->getTemplateFile());
99 }
100 }
101
102 public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritanceAgain()
103 {
104 $loader = new Twig_Loader_Array(array(
105 'index' => "{% extends 'base' %}
106 {% block content %}
107 {{ parent() }}
108 {% endblock %}",
109 'base' => '{% block content %}{{ foo }}{% endblock %}'
110 ));
111 $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
112
113 $template = $twig->loadTemplate('index');
114 try {
115 $template->render(array());
116
117 $this->fail();
118 } catch (Twig_Error_Runtime $e) {
119 $this->assertEquals('Variable "foo" does not exist in "base" at line 1', $e->getMessage());
120 $this->assertEquals(1, $e->getTemplateLine());
121 $this->assertEquals('base', $e->getTemplateFile());
122 }
123 }
124
125 public function testTwigExceptionAddsFileAndLineWhenMissingWithInheritanceOnDisk()
126 {
127 $loader = new Twig_Loader_Filesystem(dirname(__FILE__).'/Fixtures/errors');
128 $twig = new Twig_Environment($loader, array('strict_variables' => true, 'debug' => true, 'cache' => false));
129
130 $template = $twig->loadTemplate('index.html');
131 try {
132 $template->render(array());
133
134 $this->fail();
135 } catch (Twig_Error_Runtime $e) {
136 $this->assertEquals('Variable "foo" does not exist in "index.html" at line 3', $e->getMessage());
137 $this->assertEquals(3, $e->getTemplateLine());
138 $this->assertEquals('index.html', $e->getTemplateFile());
139 }
140
141 try {
142 $template->render(array('foo' => new Twig_Tests_ErrorTest_Foo()));
143
144 $this->fail();
145 } catch (Twig_Error_Runtime $e) {
146 $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage());
147 $this->assertEquals(3, $e->getTemplateLine());
148 $this->assertEquals('index.html', $e->getTemplateFile());
149 }
150 }
151}
152
153class Twig_Tests_ErrorTest_Foo
154{
155 public function bar()
156 {
157 throw new Exception('Runtime error...');
158 }
159}
diff --git a/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php b/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php
new file mode 100644
index 00000000..8ec6537a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/ExpressionParserTest.php
@@ -0,0 +1,332 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_ExpressionParserTest extends PHPUnit_Framework_TestCase
13{
14 /**
15 * @expectedException Twig_Error_Syntax
16 * @dataProvider getFailingTestsForAssignment
17 */
18 public function testCanOnlyAssignToNames($template)
19 {
20 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
21 $parser = new Twig_Parser($env);
22
23 $parser->parse($env->tokenize($template, 'index'));
24 }
25
26 public function getFailingTestsForAssignment()
27 {
28 return array(
29 array('{% set false = "foo" %}'),
30 array('{% set true = "foo" %}'),
31 array('{% set none = "foo" %}'),
32 array('{% set 3 = "foo" %}'),
33 array('{% set 1 + 2 = "foo" %}'),
34 array('{% set "bar" = "foo" %}'),
35 array('{% set %}{% endset %}')
36 );
37 }
38
39 /**
40 * @dataProvider getTestsForArray
41 */
42 public function testArrayExpression($template, $expected)
43 {
44 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
45 $stream = $env->tokenize($template, 'index');
46 $parser = new Twig_Parser($env);
47
48 $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
49 }
50
51 /**
52 * @expectedException Twig_Error_Syntax
53 * @dataProvider getFailingTestsForArray
54 */
55 public function testArraySyntaxError($template)
56 {
57 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
58 $parser = new Twig_Parser($env);
59
60 $parser->parse($env->tokenize($template, 'index'));
61 }
62
63 public function getFailingTestsForArray()
64 {
65 return array(
66 array('{{ [1, "a": "b"] }}'),
67 array('{{ {"a": "b", 2} }}'),
68 );
69 }
70
71 public function getTestsForArray()
72 {
73 return array(
74 // simple array
75 array('{{ [1, 2] }}', new Twig_Node_Expression_Array(array(
76 new Twig_Node_Expression_Constant(0, 1),
77 new Twig_Node_Expression_Constant(1, 1),
78
79 new Twig_Node_Expression_Constant(1, 1),
80 new Twig_Node_Expression_Constant(2, 1),
81 ), 1),
82 ),
83
84 // array with trailing ,
85 array('{{ [1, 2, ] }}', new Twig_Node_Expression_Array(array(
86 new Twig_Node_Expression_Constant(0, 1),
87 new Twig_Node_Expression_Constant(1, 1),
88
89 new Twig_Node_Expression_Constant(1, 1),
90 new Twig_Node_Expression_Constant(2, 1),
91 ), 1),
92 ),
93
94 // simple hash
95 array('{{ {"a": "b", "b": "c"} }}', new Twig_Node_Expression_Array(array(
96 new Twig_Node_Expression_Constant('a', 1),
97 new Twig_Node_Expression_Constant('b', 1),
98
99 new Twig_Node_Expression_Constant('b', 1),
100 new Twig_Node_Expression_Constant('c', 1),
101 ), 1),
102 ),
103
104 // hash with trailing ,
105 array('{{ {"a": "b", "b": "c", } }}', new Twig_Node_Expression_Array(array(
106 new Twig_Node_Expression_Constant('a', 1),
107 new Twig_Node_Expression_Constant('b', 1),
108
109 new Twig_Node_Expression_Constant('b', 1),
110 new Twig_Node_Expression_Constant('c', 1),
111 ), 1),
112 ),
113
114 // hash in an array
115 array('{{ [1, {"a": "b", "b": "c"}] }}', new Twig_Node_Expression_Array(array(
116 new Twig_Node_Expression_Constant(0, 1),
117 new Twig_Node_Expression_Constant(1, 1),
118
119 new Twig_Node_Expression_Constant(1, 1),
120 new Twig_Node_Expression_Array(array(
121 new Twig_Node_Expression_Constant('a', 1),
122 new Twig_Node_Expression_Constant('b', 1),
123
124 new Twig_Node_Expression_Constant('b', 1),
125 new Twig_Node_Expression_Constant('c', 1),
126 ), 1),
127 ), 1),
128 ),
129
130 // array in a hash
131 array('{{ {"a": [1, 2], "b": "c"} }}', new Twig_Node_Expression_Array(array(
132 new Twig_Node_Expression_Constant('a', 1),
133 new Twig_Node_Expression_Array(array(
134 new Twig_Node_Expression_Constant(0, 1),
135 new Twig_Node_Expression_Constant(1, 1),
136
137 new Twig_Node_Expression_Constant(1, 1),
138 new Twig_Node_Expression_Constant(2, 1),
139 ), 1),
140 new Twig_Node_Expression_Constant('b', 1),
141 new Twig_Node_Expression_Constant('c', 1),
142 ), 1),
143 ),
144 );
145 }
146
147 /**
148 * @expectedException Twig_Error_Syntax
149 */
150 public function testStringExpressionDoesNotConcatenateTwoConsecutiveStrings()
151 {
152 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
153 $stream = $env->tokenize('{{ "a" "b" }}', 'index');
154 $parser = new Twig_Parser($env);
155
156 $parser->parse($stream);
157 }
158
159 /**
160 * @dataProvider getTestsForString
161 */
162 public function testStringExpression($template, $expected)
163 {
164 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false, 'optimizations' => 0));
165 $stream = $env->tokenize($template, 'index');
166 $parser = new Twig_Parser($env);
167
168 $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode(0)->getNode('expr'));
169 }
170
171 public function getTestsForString()
172 {
173 return array(
174 array(
175 '{{ "foo" }}', new Twig_Node_Expression_Constant('foo', 1),
176 ),
177 array(
178 '{{ "foo #{bar}" }}', new Twig_Node_Expression_Binary_Concat(
179 new Twig_Node_Expression_Constant('foo ', 1),
180 new Twig_Node_Expression_Name('bar', 1),
181 1
182 ),
183 ),
184 array(
185 '{{ "foo #{bar} baz" }}', new Twig_Node_Expression_Binary_Concat(
186 new Twig_Node_Expression_Binary_Concat(
187 new Twig_Node_Expression_Constant('foo ', 1),
188 new Twig_Node_Expression_Name('bar', 1),
189 1
190 ),
191 new Twig_Node_Expression_Constant(' baz', 1),
192 1
193 )
194 ),
195
196 array(
197 '{{ "foo #{"foo #{bar} baz"} baz" }}', new Twig_Node_Expression_Binary_Concat(
198 new Twig_Node_Expression_Binary_Concat(
199 new Twig_Node_Expression_Constant('foo ', 1),
200 new Twig_Node_Expression_Binary_Concat(
201 new Twig_Node_Expression_Binary_Concat(
202 new Twig_Node_Expression_Constant('foo ', 1),
203 new Twig_Node_Expression_Name('bar', 1),
204 1
205 ),
206 new Twig_Node_Expression_Constant(' baz', 1),
207 1
208 ),
209 1
210 ),
211 new Twig_Node_Expression_Constant(' baz', 1),
212 1
213 ),
214 ),
215 );
216 }
217
218 /**
219 * @expectedException Twig_Error_Syntax
220 */
221 public function testAttributeCallDoesNotSupportNamedArguments()
222 {
223 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
224 $parser = new Twig_Parser($env);
225
226 $parser->parse($env->tokenize('{{ foo.bar(name="Foo") }}', 'index'));
227 }
228
229 /**
230 * @expectedException Twig_Error_Syntax
231 */
232 public function testMacroCallDoesNotSupportNamedArguments()
233 {
234 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
235 $parser = new Twig_Parser($env);
236
237 $parser->parse($env->tokenize('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'));
238 }
239
240 /**
241 * @expectedException Twig_Error_Syntax
242 * @expectedExceptionMessage An argument must be a name. Unexpected token "string" of value "a" ("name" expected) in "index" at line 1
243 */
244 public function testMacroDefinitionDoesNotSupportNonNameVariableName()
245 {
246 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
247 $parser = new Twig_Parser($env);
248
249 $parser->parse($env->tokenize('{% macro foo("a") %}{% endmacro %}', 'index'));
250 }
251
252 /**
253 * @expectedException Twig_Error_Syntax
254 * @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
255 * @dataProvider getMacroDefinitionDoesNotSupportNonConstantDefaultValues
256 */
257 public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template)
258 {
259 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
260 $parser = new Twig_Parser($env);
261
262 $parser->parse($env->tokenize($template, 'index'));
263 }
264
265 public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues()
266 {
267 return array(
268 array('{% macro foo(name = "a #{foo} a") %}{% endmacro %}'),
269 array('{% macro foo(name = [["b", "a #{foo} a"]]) %}{% endmacro %}'),
270 );
271 }
272
273 /**
274 * @dataProvider getMacroDefinitionSupportsConstantDefaultValues
275 */
276 public function testMacroDefinitionSupportsConstantDefaultValues($template)
277 {
278 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
279 $parser = new Twig_Parser($env);
280
281 $parser->parse($env->tokenize($template, 'index'));
282 }
283
284 public function getMacroDefinitionSupportsConstantDefaultValues()
285 {
286 return array(
287 array('{% macro foo(name = "aa") %}{% endmacro %}'),
288 array('{% macro foo(name = 12) %}{% endmacro %}'),
289 array('{% macro foo(name = true) %}{% endmacro %}'),
290 array('{% macro foo(name = ["a"]) %}{% endmacro %}'),
291 array('{% macro foo(name = [["a"]]) %}{% endmacro %}'),
292 array('{% macro foo(name = {a: "a"}) %}{% endmacro %}'),
293 array('{% macro foo(name = {a: {b: "a"}}) %}{% endmacro %}'),
294 );
295 }
296
297 /**
298 * @expectedException Twig_Error_Syntax
299 * @expectedExceptionMessage The function "cycl" does not exist. Did you mean "cycle" in "index" at line 1
300 */
301 public function testUnknownFunction()
302 {
303 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
304 $parser = new Twig_Parser($env);
305
306 $parser->parse($env->tokenize('{{ cycl() }}', 'index'));
307 }
308
309 /**
310 * @expectedException Twig_Error_Syntax
311 * @expectedExceptionMessage The filter "lowe" does not exist. Did you mean "lower" in "index" at line 1
312 */
313 public function testUnknownFilter()
314 {
315 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
316 $parser = new Twig_Parser($env);
317
318 $parser->parse($env->tokenize('{{ 1|lowe }}', 'index'));
319 }
320
321 /**
322 * @expectedException Twig_Error_Syntax
323 * @expectedExceptionMessage The test "nul" does not exist. Did you mean "null" in "index" at line 1
324 */
325 public function testUnknownTest()
326 {
327 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
328 $parser = new Twig_Parser($env);
329
330 $parser->parse($env->tokenize('{{ 1 is nul }}', 'index'));
331 }
332}
diff --git a/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php b/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php
new file mode 100644
index 00000000..5743e343
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Extension/CoreTest.php
@@ -0,0 +1,117 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Extension_CoreTest extends PHPUnit_Framework_TestCase
13{
14 /**
15 * @dataProvider getRandomFunctionTestData
16 */
17 public function testRandomFunction($value, $expectedInArray)
18 {
19 $env = new Twig_Environment();
20
21 for ($i = 0; $i < 100; $i++) {
22 $this->assertTrue(in_array(twig_random($env, $value), $expectedInArray, true)); // assertContains() would not consider the type
23 }
24 }
25
26 public function getRandomFunctionTestData()
27 {
28 return array(
29 array( // array
30 array('apple', 'orange', 'citrus'),
31 array('apple', 'orange', 'citrus'),
32 ),
33 array( // Traversable
34 new ArrayObject(array('apple', 'orange', 'citrus')),
35 array('apple', 'orange', 'citrus'),
36 ),
37 array( // unicode string
38 'Ä€é',
39 array('Ä', '€', 'é'),
40 ),
41 array( // numeric but string
42 '123',
43 array('1', '2', '3'),
44 ),
45 array( // integer
46 5,
47 range(0, 5, 1),
48 ),
49 array( // float
50 5.9,
51 range(0, 5, 1),
52 ),
53 array( // negative
54 -2,
55 array(0, -1, -2),
56 ),
57 );
58 }
59
60 public function testRandomFunctionWithoutParameter()
61 {
62 $max = mt_getrandmax();
63
64 for ($i = 0; $i < 100; $i++) {
65 $val = twig_random(new Twig_Environment());
66 $this->assertTrue(is_int($val) && $val >= 0 && $val <= $max);
67 }
68 }
69
70 public function testRandomFunctionReturnsAsIs()
71 {
72 $this->assertSame('', twig_random(new Twig_Environment(), ''));
73 $this->assertSame('', twig_random(new Twig_Environment(null, array('charset' => null)), ''));
74
75 $instance = new stdClass();
76 $this->assertSame($instance, twig_random(new Twig_Environment(), $instance));
77 }
78
79 /**
80 * @expectedException Twig_Error_Runtime
81 */
82 public function testRandomFunctionOfEmptyArrayThrowsException()
83 {
84 twig_random(new Twig_Environment(), array());
85 }
86
87 public function testRandomFunctionOnNonUTF8String()
88 {
89 if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
90 $this->markTestSkipped('needs iconv or mbstring');
91 }
92
93 $twig = new Twig_Environment();
94 $twig->setCharset('ISO-8859-1');
95
96 $text = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8');
97 for ($i = 0; $i < 30; $i++) {
98 $rand = twig_random($twig, $text);
99 $this->assertTrue(in_array(twig_convert_encoding($rand, 'UTF-8', 'ISO-8859-1'), array('Ä', 'é'), true));
100 }
101 }
102
103 public function testReverseFilterOnNonUTF8String()
104 {
105 if (!function_exists('iconv') && !function_exists('mb_convert_encoding')) {
106 $this->markTestSkipped('needs iconv or mbstring');
107 }
108
109 $twig = new Twig_Environment();
110 $twig->setCharset('ISO-8859-1');
111
112 $input = twig_convert_encoding('Äé', 'ISO-8859-1', 'UTF-8');
113 $output = twig_convert_encoding(twig_reverse_filter($twig, $input), 'UTF-8', 'ISO-8859-1');
114
115 $this->assertEquals($output, 'éÄ');
116 }
117}
diff --git a/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php b/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php
new file mode 100644
index 00000000..72253c88
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Extension/SandboxTest.php
@@ -0,0 +1,212 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Extension_SandboxTest extends PHPUnit_Framework_TestCase
13{
14 protected static $params, $templates;
15
16 public function setUp()
17 {
18 self::$params = array(
19 'name' => 'Fabien',
20 'obj' => new FooObject(),
21 'arr' => array('obj' => new FooObject()),
22 );
23
24 self::$templates = array(
25 '1_basic1' => '{{ obj.foo }}',
26 '1_basic2' => '{{ name|upper }}',
27 '1_basic3' => '{% if name %}foo{% endif %}',
28 '1_basic4' => '{{ obj.bar }}',
29 '1_basic5' => '{{ obj }}',
30 '1_basic6' => '{{ arr.obj }}',
31 '1_basic7' => '{{ cycle(["foo","bar"], 1) }}',
32 '1_basic8' => '{{ obj.getfoobar }}{{ obj.getFooBar }}',
33 '1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
34 '1_layout' => '{% block content %}{% endblock %}',
35 '1_child' => '{% extends "1_layout" %}{% block content %}{{ "a"|json_encode }}{% endblock %}',
36 );
37 }
38
39 /**
40 * @expectedException Twig_Sandbox_SecurityError
41 * @expectedExceptionMessage Filter "json_encode" is not allowed in "1_child".
42 */
43 public function testSandboxWithInheritance()
44 {
45 $twig = $this->getEnvironment(true, array(), self::$templates, array('block'));
46 $twig->loadTemplate('1_child')->render(array());
47 }
48
49 public function testSandboxGloballySet()
50 {
51 $twig = $this->getEnvironment(false, array(), self::$templates);
52 $this->assertEquals('FOO', $twig->loadTemplate('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally');
53
54 $twig = $this->getEnvironment(true, array(), self::$templates);
55 try {
56 $twig->loadTemplate('1_basic1')->render(self::$params);
57 $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called');
58 } catch (Twig_Sandbox_SecurityError $e) {
59 }
60
61 $twig = $this->getEnvironment(true, array(), self::$templates);
62 try {
63 $twig->loadTemplate('1_basic2')->render(self::$params);
64 $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called');
65 } catch (Twig_Sandbox_SecurityError $e) {
66 }
67
68 $twig = $this->getEnvironment(true, array(), self::$templates);
69 try {
70 $twig->loadTemplate('1_basic3')->render(self::$params);
71 $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template');
72 } catch (Twig_Sandbox_SecurityError $e) {
73 }
74
75 $twig = $this->getEnvironment(true, array(), self::$templates);
76 try {
77 $twig->loadTemplate('1_basic4')->render(self::$params);
78 $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template');
79 } catch (Twig_Sandbox_SecurityError $e) {
80 }
81
82 $twig = $this->getEnvironment(true, array(), self::$templates);
83 try {
84 $twig->loadTemplate('1_basic5')->render(self::$params);
85 $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
86 } catch (Twig_Sandbox_SecurityError $e) {
87 }
88
89 $twig = $this->getEnvironment(true, array(), self::$templates);
90 try {
91 $twig->loadTemplate('1_basic6')->render(self::$params);
92 $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template');
93 } catch (Twig_Sandbox_SecurityError $e) {
94 }
95
96 $twig = $this->getEnvironment(true, array(), self::$templates);
97 try {
98 $twig->loadTemplate('1_basic7')->render(self::$params);
99 $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template');
100 } catch (Twig_Sandbox_SecurityError $e) {
101 }
102
103 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => 'foo'));
104 FooObject::reset();
105 $this->assertEquals('foo', $twig->loadTemplate('1_basic1')->render(self::$params), 'Sandbox allow some methods');
106 $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once');
107
108 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => '__toString'));
109 FooObject::reset();
110 $this->assertEquals('foo', $twig->loadTemplate('1_basic5')->render(self::$params), 'Sandbox allow some methods');
111 $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once');
112
113 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array('upper'));
114 $this->assertEquals('FABIEN', $twig->loadTemplate('1_basic2')->render(self::$params), 'Sandbox allow some filters');
115
116 $twig = $this->getEnvironment(true, array(), self::$templates, array('if'));
117 $this->assertEquals('foo', $twig->loadTemplate('1_basic3')->render(self::$params), 'Sandbox allow some tags');
118
119 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array('FooObject' => 'bar'));
120 $this->assertEquals('bar', $twig->loadTemplate('1_basic4')->render(self::$params), 'Sandbox allow some properties');
121
122 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array(), array(), array('cycle'));
123 $this->assertEquals('bar', $twig->loadTemplate('1_basic7')->render(self::$params), 'Sandbox allow some functions');
124
125 foreach (array('getfoobar', 'getFoobar', 'getFooBar') as $name) {
126 $twig = $this->getEnvironment(true, array(), self::$templates, array(), array(), array('FooObject' => $name));
127 FooObject::reset();
128 $this->assertEquals('foobarfoobar', $twig->loadTemplate('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way');
129 $this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once');
130 }
131 }
132
133 public function testSandboxLocallySetForAnInclude()
134 {
135 self::$templates = array(
136 '2_basic' => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}',
137 '2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
138 );
139
140 $twig = $this->getEnvironment(false, array(), self::$templates);
141 $this->assertEquals('fooFOOfoo', $twig->loadTemplate('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include');
142
143 self::$templates = array(
144 '3_basic' => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}',
145 '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}',
146 );
147
148 $twig = $this->getEnvironment(true, array(), self::$templates);
149 try {
150 $twig->loadTemplate('3_basic')->render(self::$params);
151 $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed');
152 } catch (Twig_Sandbox_SecurityError $e) {
153 }
154 }
155
156 public function testMacrosInASandbox()
157 {
158 $twig = $this->getEnvironment(true, array('autoescape' => true), array('index' => <<<EOF
159{%- import _self as macros %}
160
161{%- macro test(text) %}<p>{{ text }}</p>{% endmacro %}
162
163{{- macros.test('username') }}
164EOF
165 ), array('macro', 'import'), array('escape'));
166
167 $this->assertEquals('<p>username</p>', $twig->loadTemplate('index')->render(array()));
168 }
169
170 protected function getEnvironment($sandboxed, $options, $templates, $tags = array(), $filters = array(), $methods = array(), $properties = array(), $functions = array())
171 {
172 $loader = new Twig_Loader_Array($templates);
173 $twig = new Twig_Environment($loader, array_merge(array('debug' => true, 'cache' => false, 'autoescape' => false), $options));
174 $policy = new Twig_Sandbox_SecurityPolicy($tags, $filters, $methods, $properties, $functions);
175 $twig->addExtension(new Twig_Extension_Sandbox($policy, $sandboxed));
176
177 return $twig;
178 }
179}
180
181class FooObject
182{
183 public static $called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
184
185 public $bar = 'bar';
186
187 public static function reset()
188 {
189 self::$called = array('__toString' => 0, 'foo' => 0, 'getFooBar' => 0);
190 }
191
192 public function __toString()
193 {
194 ++self::$called['__toString'];
195
196 return 'foo';
197 }
198
199 public function foo()
200 {
201 ++self::$called['foo'];
202
203 return 'foo';
204 }
205
206 public function getFooBar()
207 {
208 ++self::$called['getFooBar'];
209
210 return 'foobar';
211 }
212}
diff --git a/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php b/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php
new file mode 100644
index 00000000..8efc948f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/FileCachingTest.php
@@ -0,0 +1,70 @@
1<?php
2
3class Twig_Tests_FileCachingTest extends PHPUnit_Framework_TestCase
4{
5 protected $fileName;
6 protected $env;
7 protected $tmpDir;
8
9 public function setUp()
10 {
11 $this->tmpDir = sys_get_temp_dir().'/TwigTests';
12 if (!file_exists($this->tmpDir)) {
13 @mkdir($this->tmpDir, 0777, true);
14 }
15
16 if (!is_writable($this->tmpDir)) {
17 $this->markTestSkipped(sprintf('Unable to run the tests as "%s" is not writable.', $this->tmpDir));
18 }
19
20 $this->env = new Twig_Environment(new Twig_Loader_String(), array('cache' => $this->tmpDir));
21 }
22
23 public function tearDown()
24 {
25 if ($this->fileName) {
26 unlink($this->fileName);
27 }
28
29 $this->removeDir($this->tmpDir);
30 }
31
32 public function testWritingCacheFiles()
33 {
34 $name = 'This is just text.';
35 $template = $this->env->loadTemplate($name);
36 $cacheFileName = $this->env->getCacheFilename($name);
37
38 $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
39 $this->fileName = $cacheFileName;
40 }
41
42 public function testClearingCacheFiles()
43 {
44 $name = 'I will be deleted.';
45 $template = $this->env->loadTemplate($name);
46 $cacheFileName = $this->env->getCacheFilename($name);
47
48 $this->assertTrue(file_exists($cacheFileName), 'Cache file does not exist.');
49 $this->env->clearCacheFiles();
50 $this->assertFalse(file_exists($cacheFileName), 'Cache file was not cleared.');
51 }
52
53 private function removeDir($target)
54 {
55 $fp = opendir($target);
56 while (false !== $file = readdir($fp)) {
57 if (in_array($file, array('.', '..'))) {
58 continue;
59 }
60
61 if (is_dir($target.'/'.$file)) {
62 self::removeDir($target.'/'.$file);
63 } else {
64 unlink($target.'/'.$file);
65 }
66 }
67 closedir($fp);
68 rmdir($target);
69 }
70}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html b/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html
new file mode 100644
index 00000000..cb0dbe44
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/base.html
@@ -0,0 +1 @@
{% block content %}{% endblock %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html b/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html
new file mode 100644
index 00000000..df57c822
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/errors/index.html
@@ -0,0 +1,7 @@
1{% extends 'base.html' %}
2{% block content %}
3 {{ foo.bar }}
4{% endblock %}
5{% block foo %}
6 {{ foo.bar }}
7{% endblock %}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test
new file mode 100644
index 00000000..02245e93
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/exceptions/unclosed_tag.test
@@ -0,0 +1,20 @@
1--TEST--
2Exception for an unclosed tag
3--TEMPLATE--
4{% block foo %}
5 {% if foo %}
6
7
8
9
10 {% for i in fo %}
11
12
13
14 {% endfor %}
15
16
17
18{% endblock %}
19--EXCEPTION--
20Twig_Error_Syntax: Unexpected tag name "endblock" (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test
new file mode 100644
index 00000000..c69b1192
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array.test
@@ -0,0 +1,61 @@
1--TEST--
2Twig supports array notation
3--TEMPLATE--
4{# empty array #}
5{{ []|join(',') }}
6
7{{ [1, 2]|join(',') }}
8{{ ['foo', "bar"]|join(',') }}
9{{ {0: 1, 'foo': 'bar'}|join(',') }}
10{{ {0: 1, 'foo': 'bar'}|keys|join(',') }}
11
12{{ {0: 1, foo: 'bar'}|join(',') }}
13{{ {0: 1, foo: 'bar'}|keys|join(',') }}
14
15{# nested arrays #}
16{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %}
17{{ a[2]|join(',') }}
18{{ a[3]["foo"]|join(',') }}
19
20{# works even if [] is used inside the array #}
21{{ [foo[bar]]|join(',') }}
22
23{# elements can be any expression #}
24{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }}
25
26{# arrays can have a trailing , like in PHP #}
27{{
28 [
29 1,
30 2,
31 ]|join(',')
32}}
33
34{# keys can be any expression #}
35{% set a = 1 %}
36{% set b = "foo" %}
37{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %}
38{{ ary|keys|join(',') }}
39{{ ary|join(',') }}
40--DATA--
41return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
42--EXPECT--
431,2
44foo,bar
451,bar
460,foo
47
481,bar
490,foo
50
511,2
52bar
53
54bar
55
56FOO,BAR,
57
581,2
59
601,foo,c,1foo
61a,b,c,d
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test
new file mode 100644
index 00000000..f3df328f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/array_call.test
@@ -0,0 +1,14 @@
1--TEST--
2Twig supports method calls
3--TEMPLATE--
4{{ items.foo }}
5{{ items['foo'] }}
6{{ items[foo] }}
7{{ items[items[foo]] }}
8--DATA--
9return array('foo' => 'bar', 'items' => array('foo' => 'bar', 'bar' => 'foo'))
10--EXPECT--
11bar
12bar
13foo
14bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test
new file mode 100644
index 00000000..f5e68456
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/binary.test
@@ -0,0 +1,46 @@
1--TEST--
2Twig supports binary operations (+, -, *, /, ~, %, and, or)
3--TEMPLATE--
4{{ 1 + 1 }}
5{{ 2 - 1 }}
6{{ 2 * 2 }}
7{{ 2 / 2 }}
8{{ 3 % 2 }}
9{{ 1 and 1 }}
10{{ 1 and 0 }}
11{{ 0 and 1 }}
12{{ 0 and 0 }}
13{{ 1 or 1 }}
14{{ 1 or 0 }}
15{{ 0 or 1 }}
16{{ 0 or 0 }}
17{{ 0 or 1 and 0 }}
18{{ 1 or 0 and 1 }}
19{{ "foo" ~ "bar" }}
20{{ foo ~ "bar" }}
21{{ "foo" ~ bar }}
22{{ foo ~ bar }}
23{{ 20 // 7 }}
24--DATA--
25return array('foo' => 'bar', 'bar' => 'foo')
26--EXPECT--
272
281
294
301
311
321
33
34
35
361
371
381
39
40
411
42foobar
43barbar
44foofoo
45barfoo
462
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test
new file mode 100644
index 00000000..7b56b761
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/bitwise.test
@@ -0,0 +1,14 @@
1--TEST--
2Twig supports bitwise operations
3--TEMPLATE--
4{{ 1 b-and 5 }}
5{{ 1 b-or 5 }}
6{{ 1 b-xor 5 }}
7{{ (1 and 0 b-or 0) is sameas(1 and (0 b-or 0)) ? 'ok' : 'ko' }}
8--DATA--
9return array()
10--EXPECT--
111
125
134
14ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test
new file mode 100644
index 00000000..726b8507
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/comparison.test
@@ -0,0 +1,14 @@
1--TEST--
2Twig supports comparison operators (==, !=, <, >, >=, <=)
3--TEMPLATE--
4{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }}
5{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }}
6{{ 1 == 1 }}/{{ 1 == 2 }}
7{{ 1 != 1 }}/{{ 1 != 2 }}
8--DATA--
9return array()
10--EXPECT--
11///1
121//1/1
131/
14/1
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test
new file mode 100644
index 00000000..9cd0676c
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/dotdot.test
@@ -0,0 +1,20 @@
1--TEST--
2Twig supports the .. operator
3--TEMPLATE--
4{% for i in 0..10 %}{{ i }} {% endfor %}
5
6{% for letter in 'a'..'z' %}{{ letter }} {% endfor %}
7
8{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %}
9
10{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %}
11
12{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %}
13--DATA--
14return array('foo' => array(1, 10))
15--EXPECT--
160 1 2 3 4 5 6 7 8 9 10
17a 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
18A 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
191 2 3 4 5 6 7 8 9 10
201 2 3 4 5 6 7 8 9
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test
new file mode 100644
index 00000000..79f8e0b0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/grouping.test
@@ -0,0 +1,8 @@
1--TEST--
2Twig supports grouping of expressions
3--TEMPLATE--
4{{ (2 + 2) / 2 }}
5--DATA--
6return array()
7--EXPECT--
82
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test
new file mode 100644
index 00000000..7ae3bae9
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/literals.test
@@ -0,0 +1,22 @@
1--TEST--
2Twig supports literals
3--TEMPLATE--
41 {{ true }}
52 {{ TRUE }}
63 {{ false }}
74 {{ FALSE }}
85 {{ none }}
96 {{ NONE }}
107 {{ null }}
118 {{ NULL }}
12--DATA--
13return array()
14--EXPECT--
151 1
162 1
173
184
195
206
217
228
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test
new file mode 100644
index 00000000..159db96f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/magic_call.test
@@ -0,0 +1,27 @@
1--TEST--
2Twig supports __call() for attributes
3--TEMPLATE--
4{{ foo.foo }}
5{{ foo.bar }}
6--DATA--
7class TestClassForMagicCallAttributes
8{
9 public function getBar()
10 {
11 return 'bar_from_getbar';
12 }
13
14 public function __call($method, $arguments)
15 {
16 if ('foo' === $method)
17 {
18 return 'foo_from_call';
19 }
20
21 return false;
22 }
23}
24return array('foo' => new TestClassForMagicCallAttributes())
25--EXPECT--
26foo_from_call
27bar_from_getbar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test
new file mode 100644
index 00000000..5f801e63
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/method_call.test
@@ -0,0 +1,28 @@
1--TEST--
2Twig supports method calls
3--TEMPLATE--
4{{ items.foo.foo }}
5{{ items.foo.getFoo() }}
6{{ items.foo.bar }}
7{{ items.foo['bar'] }}
8{{ items.foo.bar('a', 43) }}
9{{ items.foo.bar(foo) }}
10{{ items.foo.self.foo() }}
11{{ items.foo.is }}
12{{ items.foo.in }}
13{{ items.foo.not }}
14--DATA--
15return array('foo' => 'bar', 'items' => array('foo' => new TwigTestFoo(), 'bar' => 'foo'))
16--CONFIG--
17return array('strict_variables' => false)
18--EXPECT--
19foo
20foo
21bar
22
23bar_a-43
24bar_bar
25foo
26is
27in
28not
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test
new file mode 100644
index 00000000..542c3504
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/postfix.test
@@ -0,0 +1,22 @@
1--TEST--
2Twig parses postfix expressions
3--TEMPLATE--
4{% import _self as macros %}
5
6{% macro foo() %}foo{% endmacro %}
7
8{{ 'a' }}
9{{ 'a'|upper }}
10{{ ('a')|upper }}
11{{ -1|upper }}
12{{ macros.foo() }}
13{{ (macros).foo() }}
14--DATA--
15return array();
16--EXPECT--
17a
18A
19A
20-1
21foo
22foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test
new file mode 100644
index 00000000..a9116613
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/strings.test
@@ -0,0 +1,10 @@
1--TEST--
2Twig supports string interpolation
3--TEMPLATE--
4{{ "foo #{"foo #{bar} baz"} baz" }}
5{{ "foo #{bar}#{bar} baz" }}
6--DATA--
7return array('bar' => 'BAR');
8--EXPECT--
9foo foo BAR baz baz
10foo BARBAR baz
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test
new file mode 100644
index 00000000..0e6fa96e
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator.test
@@ -0,0 +1,18 @@
1--TEST--
2Twig supports the ternary operator
3--TEMPLATE--
4{{ 1 ? 'YES' : 'NO' }}
5{{ 0 ? 'YES' : 'NO' }}
6{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }}
7{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }}
8{{ 1 == 1 ? 'foo<br />':'' }}
9{{ foo ~ (bar ? ('-' ~ bar) : '') }}
10--DATA--
11return array('foo' => 'foo', 'bar' => 'bar')
12--EXPECT--
13YES
14NO
15YES1
16NO1
17foo<br />
18foo-bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test
new file mode 100644
index 00000000..fdc660fc
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_noelse.test
@@ -0,0 +1,10 @@
1--TEST--
2Twig supports the ternary operator
3--TEMPLATE--
4{{ 1 ? 'YES' }}
5{{ 0 ? 'YES' }}
6--DATA--
7return array()
8--EXPECT--
9YES
10
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test
new file mode 100644
index 00000000..9057e837
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/ternary_operator_nothen.test
@@ -0,0 +1,10 @@
1--TEST--
2Twig supports the ternary operator
3--TEMPLATE--
4{{ 'YES' ?: 'NO' }}
5{{ 0 ?: 'NO' }}
6--DATA--
7return array()
8--EXPECT--
9YES
10NO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test
new file mode 100644
index 00000000..b79219a2
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary.test
@@ -0,0 +1,12 @@
1--TEST--
2Twig supports unary operators (not, -, +)
3--TEMPLATE--
4{{ not 1 }}/{{ not 0 }}
5{{ +1 + 1 }}/{{ -1 - 1 }}
6{{ not (false or true) }}
7--DATA--
8return array()
9--EXPECT--
10/1
112/-2
12
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test
new file mode 100644
index 00000000..cc6eef8d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/expressions/unary_precedence.test
@@ -0,0 +1,14 @@
1--TEST--
2Twig unary operators precedence
3--TEMPLATE--
4{{ -1 - 1 }}
5{{ -1 - -1 }}
6{{ -1 * -1 }}
7{{ 4 / -1 * 5 }}
8--DATA--
9return array()
10--EXPECT--
11-2
120
131
14-20
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test
new file mode 100644
index 00000000..27e93fd6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/abs.test
@@ -0,0 +1,30 @@
1--TEST--
2"abs" filter
3--TEMPLATE--
4{{ (-5.5)|abs }}
5{{ (-5)|abs }}
6{{ (-0)|abs }}
7{{ 0|abs }}
8{{ 5|abs }}
9{{ 5.5|abs }}
10{{ number1|abs }}
11{{ number2|abs }}
12{{ number3|abs }}
13{{ number4|abs }}
14{{ number5|abs }}
15{{ number6|abs }}
16--DATA--
17return array('number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5)
18--EXPECT--
195.5
205
210
220
235
245.5
255.5
265
270
280
295
305.5
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test
new file mode 100644
index 00000000..cb6de7f9
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch.test
@@ -0,0 +1,31 @@
1--TEST--
2"batch" filter
3--TEMPLATE--
4{% for row in items|batch(3) %}
5 <div class=row>
6 {% for column in row %}
7 <div class=item>{{ column }}</div>
8 {% endfor %}
9 </div>
10{% endfor %}
11--DATA--
12return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
13--EXPECT--
14<div class=row>
15 <div class=item>a</div>
16 <div class=item>b</div>
17 <div class=item>c</div>
18 </div>
19 <div class=row>
20 <div class=item>d</div>
21 <div class=item>e</div>
22 <div class=item>f</div>
23 </div>
24 <div class=row>
25 <div class=item>g</div>
26 <div class=item>h</div>
27 <div class=item>i</div>
28 </div>
29 <div class=row>
30 <div class=item>j</div>
31 </div>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php
new file mode 100644
index 00000000..52de39cb
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_float.php
@@ -0,0 +1,31 @@
1--TEST--
2"batch" filter
3--TEMPLATE--
4{% for row in items|batch(3.1) %}
5 <div class=row>
6 {% for column in row %}
7 <div class=item>{{ column }}</div>
8 {% endfor %}
9 </div>
10{% endfor %}
11--DATA--
12return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
13--EXPECT--
14<div class=row>
15 <div class=item>a</div>
16 <div class=item>b</div>
17 <div class=item>c</div>
18 </div>
19 <div class=row>
20 <div class=item>d</div>
21 <div class=item>e</div>
22 <div class=item>f</div>
23 </div>
24 <div class=row>
25 <div class=item>g</div>
26 <div class=item>h</div>
27 <div class=item>i</div>
28 </div>
29 <div class=row>
30 <div class=item>j</div>
31 </div>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test
new file mode 100644
index 00000000..af996f24
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_empty_fill.test
@@ -0,0 +1,37 @@
1--TEST--
2"batch" filter
3--TEMPLATE--
4<table>
5{% for row in items|batch(3, '') %}
6 <tr>
7 {% for column in row %}
8 <td>{{ column }}</td>
9 {% endfor %}
10 </tr>
11{% endfor %}
12</table>
13--DATA--
14return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
15--EXPECT--
16<table>
17 <tr>
18 <td>a</td>
19 <td>b</td>
20 <td>c</td>
21 </tr>
22 <tr>
23 <td>d</td>
24 <td>e</td>
25 <td>f</td>
26 </tr>
27 <tr>
28 <td>g</td>
29 <td>h</td>
30 <td>i</td>
31 </tr>
32 <tr>
33 <td>j</td>
34 <td></td>
35 <td></td>
36 </tr>
37</table>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test
new file mode 100644
index 00000000..746295f1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/batch_with_fill.test
@@ -0,0 +1,37 @@
1--TEST--
2"batch" filter
3--TEMPLATE--
4<table>
5{% for row in items|batch(3, 'fill') %}
6 <tr>
7 {% for column in row %}
8 <td>{{ column }}</td>
9 {% endfor %}
10 </tr>
11{% endfor %}
12</table>
13--DATA--
14return array('items' => array('a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'))
15--EXPECT--
16<table>
17 <tr>
18 <td>a</td>
19 <td>b</td>
20 <td>c</td>
21 </tr>
22 <tr>
23 <td>d</td>
24 <td>e</td>
25 <td>f</td>
26 </tr>
27 <tr>
28 <td>g</td>
29 <td>h</td>
30 <td>i</td>
31 </tr>
32 <tr>
33 <td>j</td>
34 <td>fill</td>
35 <td>fill</td>
36 </tr>
37</table>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test
new file mode 100644
index 00000000..380b04bb
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/convert_encoding.test
@@ -0,0 +1,10 @@
1--TEST--
2"convert_encoding" filter
3--CONDITION--
4function_exists('iconv') || function_exists('mb_convert_encoding')
5--TEMPLATE--
6{{ "愛していますか?"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}
7--DATA--
8return array()
9--EXPECT--
10愛していますか?
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test
new file mode 100644
index 00000000..d40bb04a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date.test
@@ -0,0 +1,76 @@
1--TEST--
2"date" filter
3--TEMPLATE--
4{{ date1|date }}
5{{ date1|date('d/m/Y') }}
6{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
7{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
8{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }}
9{{ date1|date('e') }}
10{{ date1|date('d/m/Y H:i:s') }}
11
12{{ date2|date }}
13{{ date2|date('d/m/Y') }}
14{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }}
15{{ date2|date('d/m/Y H:i:s', timezone1) }}
16{{ date2|date('d/m/Y H:i:s') }}
17
18{{ date3|date }}
19{{ date3|date('d/m/Y') }}
20
21{{ date4|date }}
22{{ date4|date('d/m/Y') }}
23
24{{ date5|date }}
25{{ date5|date('d/m/Y') }}
26
27{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }}
28{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }}
29{{ date6|date('d/m/Y H:i:s P', false) }}
30{{ date6|date('e', 'Europe/Paris') }}
31{{ date6|date('e', false) }}
32
33{{ date7|date }}
34--DATA--
35date_default_timezone_set('Europe/Paris');
36return array(
37 'date1' => mktime(13, 45, 0, 10, 4, 2010),
38 'date2' => new DateTime('2010-10-04 13:45'),
39 'date3' => '2010-10-04 13:45',
40 'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
41 'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
42 'date6' => new DateTime('2010-10-04 13:45', new DateTimeZone('America/New_York')),
43 'date7' => '2010-01-28T15:00:00+05:00',
44 'timezone1' => new DateTimeZone('America/New_York'),
45)
46--EXPECT--
47October 4, 2010 13:45
4804/10/2010
4904/10/2010 19:45:00
5004/10/2010 19:45:00 +08:00
5104/10/2010 06:45:00 -05:00
52Europe/Paris
5304/10/2010 13:45:00
54
55October 4, 2010 13:45
5604/10/2010
5704/10/2010 19:45:00
5804/10/2010 07:45:00
5904/10/2010 13:45:00
60
61October 4, 2010 13:45
6204/10/2010
63
64October 4, 2010 15:45
6504/10/2010
66
67January 2, 1964 04:04
6802/01/1964
69
7004/10/2010 19:45:00 +02:00
7105/10/2010 01:45:00 +08:00
7204/10/2010 13:45:00 -04:00
73Europe/Paris
74America/New_York
75
76January 28, 2010 11:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test
new file mode 100644
index 00000000..11a1ef4b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format.test
@@ -0,0 +1,14 @@
1--TEST--
2"date" filter
3--TEMPLATE--
4{{ date1|date }}
5{{ date1|date('d/m/Y') }}
6--DATA--
7date_default_timezone_set('UTC');
8$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
9return array(
10 'date1' => mktime(13, 45, 0, 10, 4, 2010),
11)
12--EXPECT--
132010-10-04
1404/10/2010
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test
new file mode 100644
index 00000000..e6d3707d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_default_format_interval.test
@@ -0,0 +1,16 @@
1--TEST--
2"date" filter (interval support as of PHP 5.3)
3--CONDITION--
4version_compare(phpversion(), '5.3.0', '>=')
5--TEMPLATE--
6{{ date2|date }}
7{{ date2|date('%d days') }}
8--DATA--
9date_default_timezone_set('UTC');
10$twig->getExtension('core')->setDateFormat('Y-m-d', '%d days %h hours');
11return array(
12 'date2' => new DateInterval('P2D'),
13)
14--EXPECT--
152 days 0 hours
162 days
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test
new file mode 100644
index 00000000..0c8c6f1a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_interval.test
@@ -0,0 +1,19 @@
1--TEST--
2"date" filter (interval support as of PHP 5.3)
3--CONDITION--
4version_compare(phpversion(), '5.3.0', '>=')
5--TEMPLATE--
6{{ date1|date }}
7{{ date1|date('%d days %h hours') }}
8{{ date1|date('%d days %h hours', timezone1) }}
9--DATA--
10date_default_timezone_set('UTC');
11return array(
12 'date1' => new DateInterval('P2D'),
13 // This should have no effect on DateInterval formatting
14 'timezone1' => new DateTimeZone('America/New_York'),
15)
16--EXPECT--
172 days
182 days 0 hours
192 days 0 hours
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test
new file mode 100644
index 00000000..53d3a69c
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_modify.test
@@ -0,0 +1,14 @@
1--TEST--
2"date_modify" filter
3--TEMPLATE--
4{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }}
5{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }}
6--DATA--
7date_default_timezone_set('UTC');
8return array(
9 'date1' => '2010-10-04 13:45',
10 'date2' => new DateTime('2010-10-04 13:45'),
11)
12--EXPECT--
132010-10-03 13:45:00
142010-10-03 13:45:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test
new file mode 100644
index 00000000..4ecde8a1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/date_namedargs.test
@@ -0,0 +1,13 @@
1--TEST--
2"date" filter
3--TEMPLATE--
4{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }}
5{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }}
6{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }}
7--DATA--
8date_default_timezone_set('UTC');
9return array('date' => mktime(13, 45, 0, 10, 4, 2010))
10--EXPECT--
1104/10/2010 08:45:00 -05:00
1204/10/2010 08:45:00 -05:00
1304/10/2010 08:45:00 -05:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test
new file mode 100644
index 00000000..0e4404b1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/default.test
@@ -0,0 +1,150 @@
1--TEST--
2"default" filter
3--TEMPLATE--
4Variable:
5{{ definedVar |default('default') is sameas('default') ? 'ko' : 'ok' }}
6{{ zeroVar |default('default') is sameas('default') ? 'ko' : 'ok' }}
7{{ emptyVar |default('default') is sameas('default') ? 'ok' : 'ko' }}
8{{ nullVar |default('default') is sameas('default') ? 'ok' : 'ko' }}
9{{ undefinedVar |default('default') is sameas('default') ? 'ok' : 'ko' }}
10Array access:
11{{ nested.definedVar |default('default') is sameas('default') ? 'ko' : 'ok' }}
12{{ nested['definedVar'] |default('default') is sameas('default') ? 'ko' : 'ok' }}
13{{ nested.zeroVar |default('default') is sameas('default') ? 'ko' : 'ok' }}
14{{ nested.emptyVar |default('default') is sameas('default') ? 'ok' : 'ko' }}
15{{ nested.nullVar |default('default') is sameas('default') ? 'ok' : 'ko' }}
16{{ nested.undefinedVar |default('default') is sameas('default') ? 'ok' : 'ko' }}
17{{ nested['undefinedVar'] |default('default') is sameas('default') ? 'ok' : 'ko' }}
18{{ undefinedVar.foo |default('default') is sameas('default') ? 'ok' : 'ko' }}
19Plain values:
20{{ 'defined' |default('default') is sameas('default') ? 'ko' : 'ok' }}
21{{ 0 |default('default') is sameas('default') ? 'ko' : 'ok' }}
22{{ '' |default('default') is sameas('default') ? 'ok' : 'ko' }}
23{{ null |default('default') is sameas('default') ? 'ok' : 'ko' }}
24Precedence:
25{{ 'o' ~ nullVar |default('k') }}
26{{ 'o' ~ nested.nullVar |default('k') }}
27Object methods:
28{{ object.foo |default('default') is sameas('default') ? 'ko' : 'ok' }}
29{{ object.undefinedMethod |default('default') is sameas('default') ? 'ok' : 'ko' }}
30{{ object.getFoo() |default('default') is sameas('default') ? 'ko' : 'ok' }}
31{{ object.getFoo('a') |default('default') is sameas('default') ? 'ko' : 'ok' }}
32{{ object.undefinedMethod() |default('default') is sameas('default') ? 'ok' : 'ko' }}
33{{ object.undefinedMethod('a') |default('default') is sameas('default') ? 'ok' : 'ko' }}
34Deep nested:
35{{ nested.undefinedVar.foo.bar |default('default') is sameas('default') ? 'ok' : 'ko' }}
36{{ nested.definedArray.0 |default('default') is sameas('default') ? 'ko' : 'ok' }}
37{{ nested['definedArray'][0] |default('default') is sameas('default') ? 'ko' : 'ok' }}
38{{ object.self.foo |default('default') is sameas('default') ? 'ko' : 'ok' }}
39{{ object.self.undefinedMethod |default('default') is sameas('default') ? 'ok' : 'ko' }}
40{{ object.undefinedMethod.self |default('default') is sameas('default') ? 'ok' : 'ko' }}
41--DATA--
42return array(
43 'definedVar' => 'defined',
44 'zeroVar' => 0,
45 'emptyVar' => '',
46 'nullVar' => null,
47 'nested' => array(
48 'definedVar' => 'defined',
49 'zeroVar' => 0,
50 'emptyVar' => '',
51 'nullVar' => null,
52 'definedArray' => array(0),
53 ),
54 'object' => new TwigTestFoo(),
55)
56--CONFIG--
57return array('strict_variables' => false)
58--EXPECT--
59Variable:
60ok
61ok
62ok
63ok
64ok
65Array access:
66ok
67ok
68ok
69ok
70ok
71ok
72ok
73ok
74Plain values:
75ok
76ok
77ok
78ok
79Precedence:
80ok
81ok
82Object methods:
83ok
84ok
85ok
86ok
87ok
88ok
89Deep nested:
90ok
91ok
92ok
93ok
94ok
95ok
96--DATA--
97return array(
98 'definedVar' => 'defined',
99 'zeroVar' => 0,
100 'emptyVar' => '',
101 'nullVar' => null,
102 'nested' => array(
103 'definedVar' => 'defined',
104 'zeroVar' => 0,
105 'emptyVar' => '',
106 'nullVar' => null,
107 'definedArray' => array(0),
108 ),
109 'object' => new TwigTestFoo(),
110)
111--CONFIG--
112return array('strict_variables' => true)
113--EXPECT--
114Variable:
115ok
116ok
117ok
118ok
119ok
120Array access:
121ok
122ok
123ok
124ok
125ok
126ok
127ok
128ok
129Plain values:
130ok
131ok
132ok
133ok
134Precedence:
135ok
136ok
137Object methods:
138ok
139ok
140ok
141ok
142ok
143ok
144Deep nested:
145ok
146ok
147ok
148ok
149ok
150ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test
new file mode 100644
index 00000000..93c5913f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/dynamic_filter.test
@@ -0,0 +1,10 @@
1--TEST--
2dynamic filter
3--TEMPLATE--
4{{ 'bar'|foo_path }}
5{{ 'bar'|a_foo_b_bar }}
6--DATA--
7return array()
8--EXPECT--
9foo/bar
10a/b/bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test
new file mode 100644
index 00000000..a606c106
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape.test
@@ -0,0 +1,8 @@
1--TEST--
2"escape" filter
3--TEMPLATE--
4{{ "foo <br />"|e }}
5--DATA--
6return array()
7--EXPECT--
8foo &lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test
new file mode 100644
index 00000000..bba26a0d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/escape_non_supported_charset.test
@@ -0,0 +1,8 @@
1--TEST--
2"escape" filter
3--TEMPLATE--
4{{ "愛していますか? <br />"|e }}
5--DATA--
6return array()
7--EXPECT--
8愛していますか? &lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test
new file mode 100644
index 00000000..853465b6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/first.test
@@ -0,0 +1,14 @@
1--TEST--
2"first" filter
3--TEMPLATE--
4{{ [1, 2, 3, 4]|first }}
5{{ {a: 1, b: 2, c: 3, d: 4}|first }}
6{{ '1234'|first }}
7{{ arr|first }}
8--DATA--
9return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
10--EXPECT--
111
121
131
141
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test
new file mode 100644
index 00000000..85a9b717
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/force_escape.test
@@ -0,0 +1,18 @@
1--TEST--
2"escape" filter
3--TEMPLATE--
4{% set foo %}
5 foo<br />
6{% endset %}
7
8{{ foo|e('html') -}}
9{{ foo|e('js') }}
10{% autoescape true %}
11 {{ foo }}
12{% endautoescape %}
13--DATA--
14return array()
15--EXPECT--
16 foo&lt;br /&gt;
17\x20\x20\x20\x20foo\x3Cbr\x20\x2F\x3E\x0A
18 foo<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test
new file mode 100644
index 00000000..97221ff8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/format.test
@@ -0,0 +1,8 @@
1--TEST--
2"format" filter
3--TEMPLATE--
4{{ string|format(foo, 3) }}
5--DATA--
6return array('string' => '%s/%d', 'foo' => 'bar')
7--EXPECT--
8bar/3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test
new file mode 100644
index 00000000..b342c174
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/join.test
@@ -0,0 +1,12 @@
1--TEST--
2"join" filter
3--TEMPLATE--
4{{ ["foo", "bar"]|join(', ') }}
5{{ foo|join(', ') }}
6{{ bar|join(', ') }}
7--DATA--
8return array('foo' => new TwigTestFoo(), 'bar' => new ArrayObject(array(3, 4)))
9--EXPECT--
10foo, bar
111, 2
123, 4
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test
new file mode 100644
index 00000000..1738d40c
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/json_encode.test
@@ -0,0 +1,12 @@
1--TEST--
2"json_encode" filter
3--TEMPLATE--
4{{ "foo"|json_encode|raw }}
5{{ foo|json_encode|raw }}
6{{ [foo, "foo"]|json_encode|raw }}
7--DATA--
8return array('foo' => new Twig_Markup('foo', 'UTF-8'))
9--EXPECT--
10"foo"
11"foo"
12["foo","foo"]
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test
new file mode 100644
index 00000000..ca3ac0cf
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/last.test
@@ -0,0 +1,14 @@
1--TEST--
2"last" filter
3--TEMPLATE--
4{{ [1, 2, 3, 4]|last }}
5{{ {a: 1, b: 2, c: 3, d: 4}|last }}
6{{ '1234'|last }}
7{{ arr|last }}
8--DATA--
9return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
10--EXPECT--
114
124
134
144
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test
new file mode 100644
index 00000000..3347474d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length.test
@@ -0,0 +1,14 @@
1--TEST--
2"length" filter
3--TEMPLATE--
4{{ array|length }}
5{{ string|length }}
6{{ number|length }}
7{{ markup|length }}
8--DATA--
9return array('array' => array(1, 4), 'string' => 'foo', 'number' => 1000, 'markup' => new Twig_Markup('foo', 'UTF-8'))
10--EXPECT--
112
123
134
143
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test
new file mode 100644
index 00000000..5d5e2436
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/length_utf8.test
@@ -0,0 +1,12 @@
1--TEST--
2"length" filter
3--CONDITION--
4function_exists('mb_get_info')
5--TEMPLATE--
6{{ string|length }}
7{{ markup|length }}
8--DATA--
9return array('string' => 'été', 'markup' => new Twig_Markup('foo', 'UTF-8'))
10--EXPECT--
113
123
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test
new file mode 100644
index 00000000..2bd3d4c0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/merge.test
@@ -0,0 +1,16 @@
1--TEST--
2"merge" filter
3--TEMPLATE--
4{{ items|merge({'bar': 'foo'})|join }}
5{{ items|merge({'bar': 'foo'})|keys|join }}
6{{ {'bar': 'foo'}|merge(items)|join }}
7{{ {'bar': 'foo'}|merge(items)|keys|join }}
8{{ numerics|merge([4, 5, 6])|join }}
9--DATA--
10return array('items' => array('foo' => 'bar'), 'numerics' => array(1, 2, 3))
11--EXPECT--
12barfoo
13foobar
14foobar
15barfoo
16123456
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test
new file mode 100644
index 00000000..6545a9bb
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/nl2br.test
@@ -0,0 +1,14 @@
1--TEST--
2"nl2br" filter
3--TEMPLATE--
4{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }}
5{{ text|nl2br }}
6--DATA--
7return array('text' => "If you have some <strong>HTML</strong>\nit will be escaped.")
8--EXPECT--
9I like Twig.<br />
10You will like it too.<br />
11<br />
12Everybody like it!
13If you have some &lt;strong&gt;HTML&lt;/strong&gt;<br />
14it will be escaped.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test
new file mode 100644
index 00000000..639a8659
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format.test
@@ -0,0 +1,18 @@
1--TEST--
2"number_format" filter
3--TEMPLATE--
4{{ 20|number_format }}
5{{ 20.25|number_format }}
6{{ 20.25|number_format(2) }}
7{{ 20.25|number_format(2, ',') }}
8{{ 1020.25|number_format(2, ',') }}
9{{ 1020.25|number_format(2, ',', '.') }}
10--DATA--
11return array();
12--EXPECT--
1320
1420
1520.25
1620,25
171,020,25
181.020,25
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test
new file mode 100644
index 00000000..c6903cc7
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/number_format_default.test
@@ -0,0 +1,21 @@
1--TEST--
2"number_format" filter with defaults.
3--TEMPLATE--
4{{ 20|number_format }}
5{{ 20.25|number_format }}
6{{ 20.25|number_format(1) }}
7{{ 20.25|number_format(2, ',') }}
8{{ 1020.25|number_format }}
9{{ 1020.25|number_format(2, ',') }}
10{{ 1020.25|number_format(2, ',', '.') }}
11--DATA--
12$twig->getExtension('core')->setNumberFormat(2, '!', '=');
13return array();
14--EXPECT--
1520!00
1620!25
1720!3
1820,25
191=020!25
201=020,25
211.020,25
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test
new file mode 100644
index 00000000..4021660b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/replace.test
@@ -0,0 +1,8 @@
1--TEST--
2"replace" filter
3--TEMPLATE--
4{{ "I like %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }}
5--DATA--
6return array()
7--EXPECT--
8I like foo and bar.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test
new file mode 100644
index 00000000..7948ac45
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/reverse.test
@@ -0,0 +1,18 @@
1--TEST--
2"reverse" filter
3--TEMPLATE--
4{{ [1, 2, 3, 4]|reverse|join('') }}
5{{ '1234évènement'|reverse }}
6{{ arr|reverse|join('') }}
7{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }}
8{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }}
9{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }}
10--DATA--
11return array('arr' => new ArrayObject(array(1, 2, 3, 4)))
12--EXPECT--
134321
14tnemenèvé4321
154321
16a,c
17a,c
18a,c
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test
new file mode 100644
index 00000000..b37ad651
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/slice.test
@@ -0,0 +1,42 @@
1--TEST--
2"slice" filter
3--TEMPLATE--
4{{ [1, 2, 3, 4][1:2]|join('') }}
5{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }}
6{{ [1, 2, 3, 4][start:length]|join('') }}
7{{ [1, 2, 3, 4]|slice(1, 2)|join('') }}
8{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }}
9{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }}
10{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }}
11{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }}
12{{ '1234'|slice(1, 2) }}
13{{ '1234'[1:2] }}
14{{ arr|slice(1, 2)|join('') }}
15{{ arr[1:2]|join('') }}
16
17{{ [1, 2, 3, 4]|slice(1)|join('') }}
18{{ [1, 2, 3, 4][1:]|join('') }}
19{{ '1234'|slice(1) }}
20{{ '1234'[1:] }}
21{{ '1234'[:1] }}
22--DATA--
23return array('start' => 1, 'length' => 2, 'arr' => new ArrayObject(array(1, 2, 3, 4)))
24--EXPECT--
2523
2623
2723
2823
2901
3012
3123
32bc
3323
3423
3523
3623
37
38234
39234
40234
41234
421
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test
new file mode 100644
index 00000000..21d575f1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/sort.test
@@ -0,0 +1,10 @@
1--TEST--
2"sort" filter
3--TEMPLATE--
4{{ array1|sort|join }}
5{{ array2|sort|join }}
6--DATA--
7return array('array1' => array(4, 1), 'array2' => array('foo', 'bar'))
8--EXPECT--
914
10barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test
new file mode 100644
index 00000000..dbaf7dc9
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/special_chars.test
@@ -0,0 +1,8 @@
1--TEST--
2"§" custom filter
3--TEMPLATE--
4{{ 'foo'|§ }}
5--DATA--
6return array()
7--EXPECT--
8§foo§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test
new file mode 100644
index 00000000..ce8ec9ce
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/split.test
@@ -0,0 +1,18 @@
1--TEST--
2"split" filter
3--TEMPLATE--
4{{ "one,two,three,four,five"|split(',')|join('-') }}
5{{ foo|split(',')|join('-') }}
6{{ foo|split(',', 3)|join('-') }}
7{{ baz|split('')|join('-') }}
8{{ baz|split('', 2)|join('-') }}
9{{ foo|split(',', -2)|join('-') }}
10--DATA--
11return array('foo' => "one,two,three,four,five", 'baz' => '12345',)
12--EXPECT--
13one-two-three-four-five
14one-two-three-four-five
15one-two-three,four,five
161-2-3-4-5
1712-34-5
18one-two-three \ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test
new file mode 100644
index 00000000..31920625
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/trim.test
@@ -0,0 +1,12 @@
1--TEST--
2"trim" filter
3--TEMPLATE--
4{{ " I like Twig. "|trim }}
5{{ text|trim }}
6{{ " foo/"|trim("/") }}
7--DATA--
8return array('text' => " If you have some <strong>HTML</strong> it will be escaped. ")
9--EXPECT--
10I like Twig.
11If you have some &lt;strong&gt;HTML&lt;/strong&gt; it will be escaped.
12 foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test
new file mode 100644
index 00000000..de956e7a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/filters/urlencode.test
@@ -0,0 +1,12 @@
1--TEST--
2"url_encode" filter
3--TEMPLATE--
4{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }}
5{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }}
6{{ {}|url_encode|default("default") }}
7--DATA--
8return array()
9--EXPECT--
10foo=bar&amp;number=3&amp;sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&amp;spa+ce=
11foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa+ce=
12default
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test
new file mode 100644
index 00000000..16ae1e8b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/attribute.test
@@ -0,0 +1,12 @@
1--TEST--
2"attribute" function
3--TEMPLATE--
4{{ attribute(obj, method) }}
5{{ attribute(array, item) }}
6{{ attribute(obj, "bar", ["a", "b"]) }}
7--DATA--
8return array('obj' => new TwigTestFoo(), 'method' => 'foo', 'array' => array('foo' => 'bar'), 'item' => 'foo')
9--EXPECT--
10foo
11bar
12bar_a-b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test
new file mode 100644
index 00000000..8e54059a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/block.test
@@ -0,0 +1,12 @@
1--TEST--
2"block" function
3--TEMPLATE--
4{% extends 'base.twig' %}
5{% block bar %}BAR{% endblock %}
6--TEMPLATE(base.twig)--
7{% block foo %}{{ block('bar') }}{% endblock %}
8{% block bar %}BAR_BASE{% endblock %}
9--DATA--
10return array()
11--EXPECT--
12BARBAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test
new file mode 100644
index 00000000..63128791
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/constant.test
@@ -0,0 +1,10 @@
1--TEST--
2"constant" function
3--TEMPLATE--
4{{ constant('DATE_W3C') == expect ? 'true' : 'false' }}
5{{ constant('ARRAY_AS_PROPS', object) }}
6--DATA--
7return array('expect' => DATE_W3C, 'object' => new ArrayObject(array('hi')));
8--EXPECT--
9true
102
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test
new file mode 100644
index 00000000..522a63b8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/cycle.test
@@ -0,0 +1,16 @@
1--TEST--
2"cycle" function
3--TEMPLATE--
4{% for i in 0..6 %}
5{{ cycle(array1, i) }}-{{ cycle(array2, i) }}
6{% endfor %}
7--DATA--
8return array('array1' => array('odd', 'even'), 'array2' => array('apple', 'orange', 'citrus'))
9--EXPECT--
10odd-apple
11even-orange
12odd-citrus
13even-apple
14odd-orange
15even-citrus
16odd-apple
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test
new file mode 100644
index 00000000..a4c97167
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date.test
@@ -0,0 +1,27 @@
1--TEST--
2"date" function
3--TEMPLATE--
4{{ date() == date('now') ? 'OK' : 'KO' }}
5{{ date() > date('-1day') ? 'OK' : 'KO' }}
6{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
7{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
8{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
9{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }}
10{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }}
11--DATA--
12date_default_timezone_set('UTC');
13return array(
14 'date1' => mktime(13, 45, 0, 10, 4, 2010),
15 'date2' => new DateTime('2010-10-04 13:45'),
16 'date3' => '2010-10-04 13:45',
17 'date4' => 1286199900, // DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT
18 'date5' => -189291360, // DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new DateTimeZone('UTC'))->getTimestamp(),
19)
20--EXPECT--
21OK
22OK
23OK
24OK
25OK
26OK
27OK
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test
new file mode 100644
index 00000000..b9dd9e38
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/date_namedargs.test
@@ -0,0 +1,11 @@
1--TEST--
2"date" function
3--TEMPLATE--
4{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }}
5{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }}
6--DATA--
7date_default_timezone_set('UTC');
8return array('date' => mktime(13, 45, 0, 10, 4, 2010))
9--EXPECT--
1004/10/2010 09:45:00 -04:00
1104/10/2010 09:45:00 -04:00
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test
new file mode 100644
index 00000000..f4072375
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump.test
@@ -0,0 +1,16 @@
1--TEST--
2"dump" function
3--CONDITION--
4!extension_loaded('xdebug')
5--TEMPLATE--
6{{ dump('foo') }}
7{{ dump('foo', 'bar') }}
8--DATA--
9return array('foo' => 'foo', 'bar' => 'bar')
10--CONFIG--
11return array('debug' => true, 'autoescape' => false);
12--EXPECT--
13string(3) "foo"
14
15string(3) "foo"
16string(3) "bar"
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test
new file mode 100644
index 00000000..889b7a92
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dump_array.test
@@ -0,0 +1,19 @@
1--TEST--
2"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded
3--CONDITION--
4!extension_loaded('xdebug') || (($r = new ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<'))
5--TEMPLATE--
6{{ dump() }}
7--DATA--
8return array('foo' => 'foo', 'bar' => 'bar')
9--CONFIG--
10return array('debug' => true, 'autoescape' => false);
11--EXPECT--
12array(3) {
13 ["foo"]=>
14 string(3) "foo"
15 ["bar"]=>
16 string(3) "bar"
17 ["global"]=>
18 string(6) "global"
19}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test
new file mode 100644
index 00000000..913fbc99
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/dynamic_function.test
@@ -0,0 +1,10 @@
1--TEST--
2dynamic function
3--TEMPLATE--
4{{ foo_path('bar') }}
5{{ a_foo_b_bar('bar') }}
6--DATA--
7return array()
8--EXPECT--
9foo/bar
10a/b/bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test
new file mode 100644
index 00000000..b7653b4e
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/assignment.test
@@ -0,0 +1,13 @@
1--TEST--
2"include" function
3--TEMPLATE--
4{% set tmp = include("foo.twig") %}
5
6FOO{{ tmp }}BAR
7--TEMPLATE(foo.twig)--
8FOOBAR
9--DATA--
10return array()
11--EXPECT--
12FOO
13FOOBARBAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test
new file mode 100644
index 00000000..56f8f3b5
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/autoescaping.test
@@ -0,0 +1,10 @@
1--TEST--
2"include" function is safe for auto-escaping
3--TEMPLATE--
4{{ include("foo.twig") }}
5--TEMPLATE(foo.twig)--
6<p>Test</p>
7--DATA--
8return array()
9--EXPECT--
10<p>Test</p>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test
new file mode 100644
index 00000000..a434182a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/basic.test
@@ -0,0 +1,17 @@
1--TEST--
2"include" function
3--TEMPLATE--
4FOO
5{{ include("foo.twig") }}
6
7BAR
8--TEMPLATE(foo.twig)--
9FOOBAR
10--DATA--
11return array()
12--EXPECT--
13FOO
14
15FOOBAR
16
17BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test
new file mode 100644
index 00000000..aba30ce3
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/expression.test
@@ -0,0 +1,17 @@
1--TEST--
2"include" function allows expressions for the template to include
3--TEMPLATE--
4FOO
5{{ include(foo) }}
6
7BAR
8--TEMPLATE(foo.twig)--
9FOOBAR
10--DATA--
11return array('foo' => 'foo.twig')
12--EXPECT--
13FOO
14
15FOOBAR
16
17BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test
new file mode 100644
index 00000000..43a2ccc2
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/ignore_missing.test
@@ -0,0 +1,10 @@
1--TEST--
2"include" function
3--TEMPLATE--
4{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }}
5{{ include("foo.twig", ignore_missing = true) }}
6{{ include("foo.twig", ignore_missing = true, variables = {}) }}
7{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }}
8--DATA--
9return array()
10--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test
new file mode 100644
index 00000000..4d2f6cf1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing.test
@@ -0,0 +1,8 @@
1--TEST--
2"include" function
3--TEMPLATE--
4{{ include("foo.twig") }}
5--DATA--
6return array();
7--EXCEPTION--
8Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test
new file mode 100644
index 00000000..78fddc7a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/missing_nested.test
@@ -0,0 +1,16 @@
1--TEST--
2"include" function
3--TEMPLATE--
4{% extends "base.twig" %}
5
6{% block content %}
7 {{ parent() }}
8{% endblock %}
9--TEMPLATE(base.twig)--
10{% block content %}
11 {{ include("foo.twig") }}
12{% endblock %}
13--DATA--
14return array();
15--EXCEPTION--
16Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test
new file mode 100644
index 00000000..788a2ab0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/sandbox.test
@@ -0,0 +1,10 @@
1--TEST--
2"include" tag sandboxed
3--TEMPLATE--
4{{ include("foo.twig", sandboxed = true) }}
5--TEMPLATE(foo.twig)--
6{{ foo|e }}
7--DATA--
8return array()
9--EXCEPTION--
10Twig_Sandbox_SecurityError: Filter "e" is not allowed in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test
new file mode 100644
index 00000000..18d405a0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/template_instance.test
@@ -0,0 +1,10 @@
1--TEST--
2"include" function accepts Twig_Template instance
3--TEMPLATE--
4{{ include(foo) }} FOO
5--TEMPLATE(foo.twig)--
6BAR
7--DATA--
8return array('foo' => $twig->loadTemplate('foo.twig'))
9--EXPECT--
10BAR FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test
new file mode 100644
index 00000000..1a810068
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/templates_as_array.test
@@ -0,0 +1,12 @@
1--TEST--
2"include" function
3--TEMPLATE--
4{{ include(["foo.twig", "bar.twig"]) }}
5{{- include(["bar.twig", "foo.twig"]) }}
6--TEMPLATE(foo.twig)--
7foo
8--DATA--
9return array()
10--EXPECT--
11foo
12foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test
new file mode 100644
index 00000000..35611fbb
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_context.test
@@ -0,0 +1,16 @@
1--TEST--
2"include" function accept variables and with_context
3--TEMPLATE--
4{{ include("foo.twig") }}
5{{- include("foo.twig", with_context = false) }}
6{{- include("foo.twig", {'foo1': 'bar'}) }}
7{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }}
8--TEMPLATE(foo.twig)--
9{% for k, v in _context %}{{ k }},{% endfor %}
10--DATA--
11return array('foo' => 'bar')
12--EXPECT--
13foo,global,_parent,
14global,_parent,
15foo,global,foo1,_parent,
16foo1,global,_parent,
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test
new file mode 100644
index 00000000..b2ace940
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/include/with_variables.test
@@ -0,0 +1,12 @@
1--TEST--
2"include" function accept variables
3--TEMPLATE--
4{{ include("foo.twig", {'foo': 'bar'}) }}
5{{- include("foo.twig", vars) }}
6--TEMPLATE(foo.twig)--
7{{ foo }}
8--DATA--
9return array('vars' => array('foo' => 'bar'))
10--EXPECT--
11bar
12bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test
new file mode 100644
index 00000000..e0377c8d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/range.test
@@ -0,0 +1,8 @@
1--TEST--
2"range" function
3--TEMPLATE--
4{{ range(low=0+1, high=10+0, step=2)|join(',') }}
5--DATA--
6return array()
7--EXPECT--
81,3,5,7,9
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test
new file mode 100644
index 00000000..30c3df51
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/special_chars.test
@@ -0,0 +1,8 @@
1--TEST--
2"§" custom function
3--TEMPLATE--
4{{ §('foo') }}
5--DATA--
6return array()
7--EXPECT--
8§foo§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test
new file mode 100644
index 00000000..41428da1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/functions/template_from_string.test
@@ -0,0 +1,11 @@
1--TEST--
2"template_from_string" function
3--TEMPLATE--
4{% include template_from_string(template) %}
5
6{% include template_from_string("Hello {{ name }}") %}
7--DATA--
8return array('name' => 'Fabien', 'template' => "Hello {{ name }}")
9--EXPECT--
10Hello Fabien
11Hello Fabien
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test
new file mode 100644
index 00000000..4ccff7b6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/default_values.test
@@ -0,0 +1,16 @@
1--TEST--
2macro
3--TEMPLATE--
4{% from _self import test %}
5
6{% macro test(a, b = 'bar') -%}
7{{ a }}{{ b }}
8{%- endmacro %}
9
10{{ test('foo') }}
11{{ test('bar', 'foo') }}
12--DATA--
13return array();
14--EXPECT--
15foobar
16barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test
new file mode 100644
index 00000000..cd254281
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/nested_calls.test
@@ -0,0 +1,18 @@
1--TEST--
2macro
3--TEMPLATE--
4{% import _self as macros %}
5
6{% macro foo(data) %}
7 {{ data }}
8{% endmacro %}
9
10{% macro bar() %}
11 <br />
12{% endmacro %}
13
14{{ macros.foo(macros.bar()) }}
15--DATA--
16return array();
17--EXPECT--
18<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test
new file mode 100644
index 00000000..cbfb921b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/reserved_variables.test
@@ -0,0 +1,14 @@
1--TEST--
2macro
3--TEMPLATE--
4{% from _self import test %}
5
6{% macro test(this) -%}
7 {{ this }}
8{%- endmacro %}
9
10{{ test(this) }}
11--DATA--
12return array('this' => 'foo');
13--EXPECT--
14foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test
new file mode 100644
index 00000000..6a366cdf
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/simple.test
@@ -0,0 +1,22 @@
1--TEST--
2macro
3--TEMPLATE--
4{% import _self as test %}
5{% from _self import test %}
6
7{% macro test(a, b) -%}
8 {{ a|default('a') }}<br />
9 {{- b|default('b') }}<br />
10{%- endmacro %}
11
12{{ test.test() }}
13{{ test() }}
14{{ test.test(1, "c") }}
15{{ test(1, "c") }}
16--DATA--
17return array();
18--EXPECT--
19a<br />b<br />
20a<br />b<br />
211<br />c<br />
221<br />c<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test
new file mode 100644
index 00000000..685626f2
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/macros/with_filters.test
@@ -0,0 +1,14 @@
1--TEST--
2macro with a filter
3--TEMPLATE--
4{% import _self as test %}
5
6{% macro test() %}
7 {% filter escape %}foo<br />{% endfilter %}
8{% endmacro %}
9
10{{ test.test() }}
11--DATA--
12return array();
13--EXPECT--
14foo&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test
new file mode 100644
index 00000000..65f6cd2b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/empty_token.test
@@ -0,0 +1,8 @@
1--TEST--
2Twig outputs 0 nodes correctly
3--TEMPLATE--
4{{ foo }}0{{ foo }}
5--DATA--
6return array('foo' => 'foo')
7--EXPECT--
8foo0foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test
new file mode 100644
index 00000000..110aef82
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/simple_xml_element.test
@@ -0,0 +1,17 @@
1--TEST--
2Twig is able to deal with SimpleXMLElement instances as variables
3--CONDITION--
4version_compare(phpversion(), '5.3.0', '>=')
5--TEMPLATE--
6Hello '{{ images.image.0.group }}'!
7{{ images.children().count() }}
8{% for image in images %}
9 - {{ image.group }}
10{% endfor %}
11--DATA--
12return array('images' => new SimpleXMLElement('<images><image><group>foo</group></image><image><group>bar</group></image></images>'))
13--EXPECT--
14Hello 'foo'!
152
16 - foo
17 - bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test
new file mode 100644
index 00000000..e18e1107
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/regression/strings_like_numbers.test
@@ -0,0 +1,8 @@
1--TEST--
2Twig does not confuse strings with integers in getAttribute()
3--TEMPLATE--
4{{ hash['2e2'] }}
5--DATA--
6return array('hash' => array('2e2' => 'works'))
7--EXPECT--
8works
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test
new file mode 100644
index 00000000..2f6a3e1a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/basic.test
@@ -0,0 +1,26 @@
1--TEST--
2"autoescape" tag applies escaping on its children
3--TEMPLATE--
4{% autoescape %}
5{{ var }}<br />
6{% endautoescape %}
7{% autoescape 'html' %}
8{{ var }}<br />
9{% endautoescape %}
10{% autoescape false %}
11{{ var }}<br />
12{% endautoescape %}
13{% autoescape true %}
14{{ var }}<br />
15{% endautoescape %}
16{% autoescape false %}
17{{ var }}<br />
18{% endautoescape %}
19--DATA--
20return array('var' => '<br />')
21--EXPECT--
22&lt;br /&gt;<br />
23&lt;br /&gt;<br />
24<br /><br />
25&lt;br /&gt;<br />
26<br /><br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test
new file mode 100644
index 00000000..05ab83ce
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/blocks.test
@@ -0,0 +1,12 @@
1--TEST--
2"autoescape" tag applies escaping on embedded blocks
3--TEMPLATE--
4{% autoescape 'html' %}
5 {% block foo %}
6 {{ var }}
7 {% endblock %}
8{% endautoescape %}
9--DATA--
10return array('var' => '<br />')
11--EXPECT--
12&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test
new file mode 100644
index 00000000..9c097246
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/double_escaping.test
@@ -0,0 +1,10 @@
1--TEST--
2"autoescape" tag does not double-escape
3--TEMPLATE--
4{% autoescape 'html' %}
5{{ var|escape }}
6{% endautoescape %}
7--DATA--
8return array('var' => '<br />')
9--EXPECT--
10&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test
new file mode 100644
index 00000000..ce7ea789
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/functions.test
@@ -0,0 +1,83 @@
1--TEST--
2"autoescape" tag applies escaping after calling functions
3--TEMPLATE--
4
5autoescape false
6{% autoescape false %}
7
8safe_br
9{{ safe_br() }}
10
11unsafe_br
12{{ unsafe_br() }}
13
14{% endautoescape %}
15
16autoescape 'html'
17{% autoescape 'html' %}
18
19safe_br
20{{ safe_br() }}
21
22unsafe_br
23{{ unsafe_br() }}
24
25unsafe_br()|raw
26{{ (unsafe_br())|raw }}
27
28safe_br()|escape
29{{ (safe_br())|escape }}
30
31safe_br()|raw
32{{ (safe_br())|raw }}
33
34unsafe_br()|escape
35{{ (unsafe_br())|escape }}
36
37{% endautoescape %}
38
39autoescape js
40{% autoescape 'js' %}
41
42safe_br
43{{ safe_br() }}
44
45{% endautoescape %}
46--DATA--
47return array()
48--EXPECT--
49
50autoescape false
51
52safe_br
53<br />
54
55unsafe_br
56<br />
57
58
59autoescape 'html'
60
61safe_br
62<br />
63
64unsafe_br
65&lt;br /&gt;
66
67unsafe_br()|raw
68<br />
69
70safe_br()|escape
71&lt;br /&gt;
72
73safe_br()|raw
74<br />
75
76unsafe_br()|escape
77&lt;br /&gt;
78
79
80autoescape js
81
82safe_br
83\x3Cbr\x20\x2F\x3E
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test
new file mode 100644
index 00000000..e389d4dd
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/literal.test
@@ -0,0 +1,45 @@
1--TEST--
2"autoescape" tag does not apply escaping on literals
3--TEMPLATE--
4{% autoescape 'html' %}
5
61. Simple literal
7{{ "<br />" }}
8
92. Conditional expression with only literals
10{{ true ? "<br />" : "<br>" }}
11
123. Conditional expression with a variable
13{{ true ? "<br />" : someVar }}
14
154. Nested conditionals with only literals
16{{ true ? (true ? "<br />" : "<br>") : "\n" }}
17
185. Nested conditionals with a variable
19{{ true ? (true ? "<br />" : someVar) : "\n" }}
20
216. Nested conditionals with a variable marked safe
22{{ true ? (true ? "<br />" : someVar|raw) : "\n" }}
23
24{% endautoescape %}
25--DATA--
26return array()
27--EXPECT--
28
291. Simple literal
30<br />
31
322. Conditional expression with only literals
33<br />
34
353. Conditional expression with a variable
36&lt;br /&gt;
37
384. Nested conditionals with only literals
39<br />
40
415. Nested conditionals with a variable
42&lt;br /&gt;
43
446. Nested conditionals with a variable marked safe
45<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test
new file mode 100644
index 00000000..798e6fea
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/nested.test
@@ -0,0 +1,26 @@
1--TEST--
2"autoescape" tags can be nested at will
3--TEMPLATE--
4{{ var }}
5{% autoescape 'html' %}
6 {{ var }}
7 {% autoescape false %}
8 {{ var }}
9 {% autoescape 'html' %}
10 {{ var }}
11 {% endautoescape %}
12 {{ var }}
13 {% endautoescape %}
14 {{ var }}
15{% endautoescape %}
16{{ var }}
17--DATA--
18return array('var' => '<br />')
19--EXPECT--
20&lt;br /&gt;
21 &lt;br /&gt;
22 <br />
23 &lt;br /&gt;
24 <br />
25 &lt;br /&gt;
26&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test
new file mode 100644
index 00000000..e896aa41
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/objects.test
@@ -0,0 +1,26 @@
1--TEST--
2"autoescape" tag applies escaping to object method calls
3--TEMPLATE--
4{% autoescape 'html' %}
5{{ user.name }}
6{{ user.name|lower }}
7{{ user }}
8{% endautoescape %}
9--DATA--
10class UserForAutoEscapeTest
11{
12 public function getName()
13 {
14 return 'Fabien<br />';
15 }
16
17 public function __toString()
18 {
19 return 'Fabien<br />';
20 }
21}
22return array('user' => new UserForAutoEscapeTest())
23--EXPECT--
24Fabien&lt;br /&gt;
25fabien&lt;br /&gt;
26Fabien&lt;br /&gt;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test
new file mode 100644
index 00000000..9f1cedd3
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/raw.test
@@ -0,0 +1,10 @@
1--TEST--
2"autoescape" tag does not escape when raw is used as a filter
3--TEMPLATE--
4{% autoescape 'html' %}
5{{ var|raw }}
6{% endautoescape %}
7--DATA--
8return array('var' => '<br />')
9--EXPECT--
10<br />
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test
new file mode 100644
index 00000000..cf8cceef
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/strategy.test
@@ -0,0 +1,17 @@
1--TEST--
2"autoescape" tag accepts an escaping strategy
3--TEMPLATE--
4{% autoescape true js %}{{ var }}{% endautoescape %}
5
6{% autoescape true html %}{{ var }}{% endautoescape %}
7
8{% autoescape 'js' %}{{ var }}{% endautoescape %}
9
10{% autoescape 'html' %}{{ var }}{% endautoescape %}
11--DATA--
12return array('var' => '<br />"')
13--EXPECT--
14\x3Cbr\x20\x2F\x3E\x22
15&lt;br /&gt;&quot;
16\x3Cbr\x20\x2F\x3E\x22
17&lt;br /&gt;&quot;
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test
new file mode 100644
index 00000000..4f415201
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/type.test
@@ -0,0 +1,69 @@
1--TEST--
2escape types
3--TEMPLATE--
4
51. autoescape 'html' |escape('js')
6
7{% autoescape 'html' %}
8<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
9{% endautoescape %}
10
112. autoescape 'html' |escape('js')
12
13{% autoescape 'html' %}
14<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
15{% endautoescape %}
16
173. autoescape 'js' |escape('js')
18
19{% autoescape 'js' %}
20<a onclick="alert(&quot;{{ msg|escape('js') }}&quot;)"></a>
21{% endautoescape %}
22
234. no escape
24
25{% autoescape false %}
26<a onclick="alert(&quot;{{ msg }}&quot;)"></a>
27{% endautoescape %}
28
295. |escape('js')|escape('html')
30
31{% autoescape false %}
32<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
33{% endautoescape %}
34
356. autoescape 'html' |escape('js')|escape('html')
36
37{% autoescape 'html' %}
38<a onclick="alert(&quot;{{ msg|escape('js')|escape('html') }}&quot;)"></a>
39{% endautoescape %}
40
41--DATA--
42return array('msg' => "<>\n'\"")
43--EXPECT--
44
451. autoescape 'html' |escape('js')
46
47<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
48
492. autoescape 'html' |escape('js')
50
51<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
52
533. autoescape 'js' |escape('js')
54
55<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
56
574. no escape
58
59<a onclick="alert(&quot;<>
60'"&quot;)"></a>
61
625. |escape('js')|escape('html')
63
64<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
65
666. autoescape 'html' |escape('js')|escape('html')
67
68<a onclick="alert(&quot;\x3C\x3E\x0A\x27\x22&quot;)"></a>
69
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test
new file mode 100644
index 00000000..7821a9aa
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters.test
@@ -0,0 +1,131 @@
1--TEST--
2"autoescape" tag applies escaping after calling filters
3--TEMPLATE--
4{% autoescape 'html' %}
5
6(escape_and_nl2br is an escaper filter)
7
81. Don't escape escaper filter output
9( var is escaped by |escape_and_nl2br, line-breaks are added,
10 the output is not escaped )
11{{ var|escape_and_nl2br }}
12
132. Don't escape escaper filter output
14( var is escaped by |escape_and_nl2br, line-breaks are added,
15 the output is not escaped, |raw is redundant )
16{{ var|escape_and_nl2br|raw }}
17
183. Explicit escape
19( var is escaped by |escape_and_nl2br, line-breaks are added,
20 the output is explicitly escaped by |escape )
21{{ var|escape_and_nl2br|escape }}
22
234. Escape non-escaper filter output
24( var is upper-cased by |upper,
25 the output is auto-escaped )
26{{ var|upper }}
27
285. Escape if last filter is not an escaper
29( var is escaped by |escape_and_nl2br, line-breaks are added,
30 the output is upper-cased by |upper,
31 the output is auto-escaped as |upper is not an escaper )
32{{ var|escape_and_nl2br|upper }}
33
346. Don't escape escaper filter output
35( var is upper cased by upper,
36 the output is escaped by |escape_and_nl2br, line-breaks are added,
37 the output is not escaped as |escape_and_nl2br is an escaper )
38{{ var|upper|escape_and_nl2br }}
39
407. Escape if last filter is not an escaper
41( the output of |format is "<b>" ~ var ~ "</b>",
42 the output is auto-escaped )
43{{ "<b>%s</b>"|format(var) }}
44
458. Escape if last filter is not an escaper
46( the output of |format is "<b>" ~ var ~ "</b>",
47 |raw is redundant,
48 the output is auto-escaped )
49{{ "<b>%s</b>"|raw|format(var) }}
50
519. Don't escape escaper filter output
52( the output of |format is "<b>" ~ var ~ "</b>",
53 the output is not escaped due to |raw filter at the end )
54{{ "<b>%s</b>"|format(var)|raw }}
55
5610. Don't escape escaper filter output
57( the output of |format is "<b>" ~ var ~ "</b>",
58 the output is not escaped due to |raw filter at the end,
59 the |raw filter on var is redundant )
60{{ "<b>%s</b>"|format(var|raw)|raw }}
61
62{% endautoescape %}
63--DATA--
64return array('var' => "<Fabien>\nTwig")
65--EXPECT--
66
67(escape_and_nl2br is an escaper filter)
68
691. Don't escape escaper filter output
70( var is escaped by |escape_and_nl2br, line-breaks are added,
71 the output is not escaped )
72&lt;Fabien&gt;<br />
73Twig
74
752. Don't escape escaper filter output
76( var is escaped by |escape_and_nl2br, line-breaks are added,
77 the output is not escaped, |raw is redundant )
78&lt;Fabien&gt;<br />
79Twig
80
813. Explicit escape
82( var is escaped by |escape_and_nl2br, line-breaks are added,
83 the output is explicitly escaped by |escape )
84&amp;lt;Fabien&amp;gt;&lt;br /&gt;
85Twig
86
874. Escape non-escaper filter output
88( var is upper-cased by |upper,
89 the output is auto-escaped )
90&lt;FABIEN&gt;
91TWIG
92
935. Escape if last filter is not an escaper
94( var is escaped by |escape_and_nl2br, line-breaks are added,
95 the output is upper-cased by |upper,
96 the output is auto-escaped as |upper is not an escaper )
97&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
98TWIG
99
1006. Don't escape escaper filter output
101( var is upper cased by upper,
102 the output is escaped by |escape_and_nl2br, line-breaks are added,
103 the output is not escaped as |escape_and_nl2br is an escaper )
104&lt;FABIEN&gt;<br />
105TWIG
106
1077. Escape if last filter is not an escaper
108( the output of |format is "<b>" ~ var ~ "</b>",
109 the output is auto-escaped )
110&lt;b&gt;&lt;Fabien&gt;
111Twig&lt;/b&gt;
112
1138. Escape if last filter is not an escaper
114( the output of |format is "<b>" ~ var ~ "</b>",
115 |raw is redundant,
116 the output is auto-escaped )
117&lt;b&gt;&lt;Fabien&gt;
118Twig&lt;/b&gt;
119
1209. Don't escape escaper filter output
121( the output of |format is "<b>" ~ var ~ "</b>",
122 the output is not escaped due to |raw filter at the end )
123<b><Fabien>
124Twig</b>
125
12610. Don't escape escaper filter output
127( the output of |format is "<b>" ~ var ~ "</b>",
128 the output is not escaped due to |raw filter at the end,
129 the |raw filter on var is redundant )
130<b><Fabien>
131Twig</b>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test
new file mode 100644
index 00000000..f58a1e09
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_filters_arguments.test
@@ -0,0 +1,23 @@
1--TEST--
2"autoescape" tag do not applies escaping on filter arguments
3--TEMPLATE--
4{% autoescape 'html' %}
5{{ var|nl2br("<br />") }}
6{{ var|nl2br("<br />"|escape) }}
7{{ var|nl2br(sep) }}
8{{ var|nl2br(sep|raw) }}
9{{ var|nl2br(sep|escape) }}
10{% endautoescape %}
11--DATA--
12return array('var' => "<Fabien>\nTwig", 'sep' => '<br />')
13--EXPECT--
14&lt;Fabien&gt;<br />
15Twig
16&lt;Fabien&gt;&lt;br /&gt;
17Twig
18&lt;Fabien&gt;<br />
19Twig
20&lt;Fabien&gt;<br />
21Twig
22&lt;Fabien&gt;&lt;br /&gt;
23Twig
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test
new file mode 100644
index 00000000..134c77ea
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_pre_escape_filters.test
@@ -0,0 +1,68 @@
1--TEST--
2"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters
3--TEMPLATE--
4{% autoescape 'html' %}
5
6(nl2br is pre_escaped for "html" and declared safe for "html")
7
81. Pre-escape and don't post-escape
9( var|escape|nl2br )
10{{ var|nl2br }}
11
122. Don't double-pre-escape
13( var|escape|nl2br )
14{{ var|escape|nl2br }}
15
163. Don't escape safe values
17( var|raw|nl2br )
18{{ var|raw|nl2br }}
19
204. Don't escape safe values
21( var|escape|nl2br|nl2br )
22{{ var|nl2br|nl2br }}
23
245. Re-escape values that are escaped for an other contexts
25( var|escape_something|escape|nl2br )
26{{ var|escape_something|nl2br }}
27
286. Still escape when using filters not declared safe
29( var|escape|nl2br|upper|escape )
30{{ var|nl2br|upper }}
31
32{% endautoescape %}
33--DATA--
34return array('var' => "<Fabien>\nTwig")
35--EXPECT--
36
37(nl2br is pre_escaped for "html" and declared safe for "html")
38
391. Pre-escape and don't post-escape
40( var|escape|nl2br )
41&lt;Fabien&gt;<br />
42Twig
43
442. Don't double-pre-escape
45( var|escape|nl2br )
46&lt;Fabien&gt;<br />
47Twig
48
493. Don't escape safe values
50( var|raw|nl2br )
51<Fabien><br />
52Twig
53
544. Don't escape safe values
55( var|escape|nl2br|nl2br )
56&lt;Fabien&gt;<br /><br />
57Twig
58
595. Re-escape values that are escaped for an other contexts
60( var|escape_something|escape|nl2br )
61&lt;FABIEN&gt;<br />
62TWIG
63
646. Still escape when using filters not declared safe
65( var|escape|nl2br|upper|escape )
66&amp;LT;FABIEN&amp;GT;&lt;BR /&gt;
67TWIG
68
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test
new file mode 100644
index 00000000..32d3943b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test
@@ -0,0 +1,50 @@
1--TEST--
2"autoescape" tag handles filters preserving the safety
3--TEMPLATE--
4{% autoescape 'html' %}
5
6(preserves_safety is preserving safety for "html")
7
81. Unsafe values are still unsafe
9( var|preserves_safety|escape )
10{{ var|preserves_safety }}
11
122. Safe values are still safe
13( var|escape|preserves_safety )
14{{ var|escape|preserves_safety }}
15
163. Re-escape values that are escaped for an other contexts
17( var|escape_something|preserves_safety|escape )
18{{ var|escape_something|preserves_safety }}
19
204. Still escape when using filters not declared safe
21( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
22{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }}
23
24{% endautoescape %}
25--DATA--
26return array('var' => "<Fabien>\nTwig")
27--EXPECT--
28
29(preserves_safety is preserving safety for "html")
30
311. Unsafe values are still unsafe
32( var|preserves_safety|escape )
33&lt;FABIEN&gt;
34TWIG
35
362. Safe values are still safe
37( var|escape|preserves_safety )
38&LT;FABIEN&GT;
39TWIG
40
413. Re-escape values that are escaped for an other contexts
42( var|escape_something|preserves_safety|escape )
43&lt;FABIEN&gt;
44TWIG
45
464. Still escape when using filters not declared safe
47( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape )
48&amp;LT;FABPOT&amp;GT;
49TWIG
50
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test
new file mode 100644
index 00000000..360dcf03
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/basic.test
@@ -0,0 +1,11 @@
1--TEST--
2"block" tag
3--TEMPLATE--
4{% block title1 %}FOO{% endblock %}
5{% block title2 foo|lower %}
6--TEMPLATE(foo.twig)--
7{% block content %}{% endblock %}
8--DATA--
9return array('foo' => 'bar')
10--EXPECT--
11FOObar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test
new file mode 100644
index 00000000..5c205c0a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/block_unique_name.test
@@ -0,0 +1,11 @@
1--TEST--
2"block" tag
3--TEMPLATE--
4{% block content %}
5 {% block content %}
6 {% endblock %}
7{% endblock %}
8--DATA--
9return array()
10--EXCEPTION--
11Twig_Error_Syntax: The block 'content' has already been defined line 2 in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test
new file mode 100644
index 00000000..be17fedf
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/block/special_chars.test
@@ -0,0 +1,10 @@
1--TEST--
2"§" special chars in a block name
3--TEMPLATE--
4{% block § %}
5§
6{% endblock § %}
7--DATA--
8return array()
9--EXPECT--
10§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test
new file mode 100644
index 00000000..f44296ea
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/basic.test
@@ -0,0 +1,35 @@
1--TEST--
2"embed" tag
3--TEMPLATE--
4FOO
5{% embed "foo.twig" %}
6 {% block c1 %}
7 {{ parent() }}
8 block1extended
9 {% endblock %}
10{% endembed %}
11
12BAR
13--TEMPLATE(foo.twig)--
14A
15{% block c1 %}
16 block1
17{% endblock %}
18B
19{% block c2 %}
20 block2
21{% endblock %}
22C
23--DATA--
24return array()
25--EXPECT--
26FOO
27
28A
29 block1
30
31 block1extended
32 B
33 block2
34C
35BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test
new file mode 100644
index 00000000..71ab2e01
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/error_line.test
@@ -0,0 +1,16 @@
1--TEST--
2"embed" tag
3--TEMPLATE(index.twig)--
4FOO
5{% embed "foo.twig" %}
6 {% block c1 %}
7 {{ nothing }}
8 {% endblock %}
9{% endembed %}
10BAR
11--TEMPLATE(foo.twig)--
12{% block c1 %}{% endblock %}
13--DATA--
14return array()
15--EXCEPTION--
16Twig_Error_Runtime: Variable "nothing" does not exist in "index.twig" at line 5
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test
new file mode 100644
index 00000000..da161e6d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/multiple.test
@@ -0,0 +1,50 @@
1--TEST--
2"embed" tag
3--TEMPLATE--
4FOO
5{% embed "foo.twig" %}
6 {% block c1 %}
7 {{ parent() }}
8 block1extended
9 {% endblock %}
10{% endembed %}
11
12{% embed "foo.twig" %}
13 {% block c1 %}
14 {{ parent() }}
15 block1extended
16 {% endblock %}
17{% endembed %}
18
19BAR
20--TEMPLATE(foo.twig)--
21A
22{% block c1 %}
23 block1
24{% endblock %}
25B
26{% block c2 %}
27 block2
28{% endblock %}
29C
30--DATA--
31return array()
32--EXPECT--
33FOO
34
35A
36 block1
37
38 block1extended
39 B
40 block2
41C
42
43A
44 block1
45
46 block1extended
47 B
48 block2
49C
50BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test
new file mode 100644
index 00000000..81563dce
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/nested.test
@@ -0,0 +1,42 @@
1--TEST--
2"embed" tag
3--TEMPLATE--
4{% embed "foo.twig" %}
5 {% block c1 %}
6 {{ parent() }}
7 {% embed "foo.twig" %}
8 {% block c1 %}
9 {{ parent() }}
10 block1extended
11 {% endblock %}
12 {% endembed %}
13
14 {% endblock %}
15{% endembed %}
16--TEMPLATE(foo.twig)--
17A
18{% block c1 %}
19 block1
20{% endblock %}
21B
22{% block c2 %}
23 block2
24{% endblock %}
25C
26--DATA--
27return array()
28--EXPECT--
29A
30 block1
31
32
33A
34 block1
35
36 block1extended
37 B
38 block2
39C
40 B
41 block2
42C
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test
new file mode 100644
index 00000000..cf7953d3
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/embed/with_extends.test
@@ -0,0 +1,57 @@
1--TEST--
2"embed" tag
3--TEMPLATE--
4{% extends "base.twig" %}
5
6{% block c1 %}
7 {{ parent() }}
8 blockc1baseextended
9{% endblock %}
10
11{% block c2 %}
12 {{ parent() }}
13
14 {% embed "foo.twig" %}
15 {% block c1 %}
16 {{ parent() }}
17 block1extended
18 {% endblock %}
19 {% endembed %}
20{% endblock %}
21--TEMPLATE(base.twig)--
22A
23{% block c1 %}
24 blockc1base
25{% endblock %}
26{% block c2 %}
27 blockc2base
28{% endblock %}
29B
30--TEMPLATE(foo.twig)--
31A
32{% block c1 %}
33 block1
34{% endblock %}
35B
36{% block c2 %}
37 block2
38{% endblock %}
39C
40--DATA--
41return array()
42--EXPECT--
43A
44 blockc1base
45
46 blockc1baseextended
47 blockc2base
48
49
50
51A
52 block1
53
54 block1extended
55 B
56 block2
57CB \ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test
new file mode 100644
index 00000000..82094f2f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/basic.test
@@ -0,0 +1,10 @@
1--TEST--
2"filter" tag applies a filter on its children
3--TEMPLATE--
4{% filter upper %}
5Some text with a {{ var }}
6{% endfilter %}
7--DATA--
8return array('var' => 'var')
9--EXPECT--
10SOME TEXT WITH A VAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test
new file mode 100644
index 00000000..3e7148bf
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/json_encode.test
@@ -0,0 +1,8 @@
1--TEST--
2"filter" tag applies a filter on its children
3--TEMPLATE--
4{% filter json_encode|raw %}test{% endfilter %}
5--DATA--
6return array()
7--EXPECT--
8"test"
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test
new file mode 100644
index 00000000..75512ef9
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/multiple.test
@@ -0,0 +1,10 @@
1--TEST--
2"filter" tags accept multiple chained filters
3--TEMPLATE--
4{% filter lower|title %}
5 {{ var }}
6{% endfilter %}
7--DATA--
8return array('var' => 'VAR')
9--EXPECT--
10 Var
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test
new file mode 100644
index 00000000..7e4e4eb3
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/nested.test
@@ -0,0 +1,16 @@
1--TEST--
2"filter" tags can be nested at will
3--TEMPLATE--
4{% filter lower|title %}
5 {{ var }}
6 {% filter upper %}
7 {{ var }}
8 {% endfilter %}
9 {{ var }}
10{% endfilter %}
11--DATA--
12return array('var' => 'var')
13--EXPECT--
14 Var
15 Var
16 Var
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test
new file mode 100644
index 00000000..22745ead
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_for_tag.test
@@ -0,0 +1,13 @@
1--TEST--
2"filter" tag applies the filter on "for" tags
3--TEMPLATE--
4{% filter upper %}
5{% for item in items %}
6{{ item }}
7{% endfor %}
8{% endfilter %}
9--DATA--
10return array('items' => array('a', 'b'))
11--EXPECT--
12A
13B
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test
new file mode 100644
index 00000000..afd95b29
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/filter/with_if_tag.test
@@ -0,0 +1,29 @@
1--TEST--
2"filter" tag applies the filter on "if" tags
3--TEMPLATE--
4{% filter upper %}
5{% if items %}
6{{ items|join(', ') }}
7{% endif %}
8
9{% if items.3 is defined %}
10FOO
11{% else %}
12{{ items.1 }}
13{% endif %}
14
15{% if items.3 is defined %}
16FOO
17{% elseif items.1 %}
18{{ items.0 }}
19{% endif %}
20
21{% endfilter %}
22--DATA--
23return array('items' => array('a', 'b'))
24--EXPECT--
25A, B
26
27B
28
29A
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test
new file mode 100644
index 00000000..380531f7
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/condition.test
@@ -0,0 +1,14 @@
1--TEST--
2"for" tag takes a condition
3--TEMPLATE--
4{% for i in 1..5 if i is odd -%}
5 {{ loop.index }}.{{ i }}{{ foo.bar }}
6{% endfor %}
7--DATA--
8return array('foo' => array('bar' => 'X'))
9--CONFIG--
10return array('strict_variables' => false)
11--EXPECT--
121.1X
132.3X
143.5X
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test
new file mode 100644
index 00000000..ddc69307
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/context.test
@@ -0,0 +1,18 @@
1--TEST--
2"for" tag keeps the context safe
3--TEMPLATE--
4{% for item in items %}
5 {% for item in items %}
6 * {{ item }}
7 {% endfor %}
8 * {{ item }}
9{% endfor %}
10--DATA--
11return array('items' => array('a', 'b'))
12--EXPECT--
13 * a
14 * b
15 * a
16 * a
17 * b
18 * b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test
new file mode 100644
index 00000000..20ccc880
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/else.test
@@ -0,0 +1,23 @@
1--TEST--
2"for" tag can use an "else" clause
3--TEMPLATE--
4{% for item in items %}
5 * {{ item }}
6{% else %}
7 no item
8{% endfor %}
9--DATA--
10return array('items' => array('a', 'b'))
11--EXPECT--
12 * a
13 * b
14--DATA--
15return array('items' => array())
16--EXPECT--
17 no item
18--DATA--
19return array()
20--CONFIG--
21return array('strict_variables' => false)
22--EXPECT--
23 no item
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test
new file mode 100644
index 00000000..49fb9ca6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/inner_variables.test
@@ -0,0 +1,17 @@
1--TEST--
2"for" tag does not reset inner variables
3--TEMPLATE--
4{% for i in 1..2 %}
5 {% for j in 0..2 %}
6 {{k}}{% set k = k+1 %} {{ loop.parent.loop.index }}
7 {% endfor %}
8{% endfor %}
9--DATA--
10return array('k' => 0)
11--EXPECT--
12 0 1
13 1 1
14 2 1
15 3 2
16 4 2
17 5 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test
new file mode 100644
index 00000000..4e22cb47
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys.test
@@ -0,0 +1,11 @@
1--TEST--
2"for" tag can iterate over keys
3--TEMPLATE--
4{% for key in items|keys %}
5 * {{ key }}
6{% endfor %}
7--DATA--
8return array('items' => array('a', 'b'))
9--EXPECT--
10 * 0
11 * 1
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test
new file mode 100644
index 00000000..4c211689
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/keys_and_values.test
@@ -0,0 +1,11 @@
1--TEST--
2"for" tag can iterate over keys and values
3--TEMPLATE--
4{% for key, item in items %}
5 * {{ key }}/{{ item }}
6{% endfor %}
7--DATA--
8return array('items' => array('a', 'b'))
9--EXPECT--
10 * 0/a
11 * 1/b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test
new file mode 100644
index 00000000..93bc76a1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context.test
@@ -0,0 +1,19 @@
1--TEST--
2"for" tag adds a loop variable to the context
3--TEMPLATE--
4{% for item in items %}
5 * {{ loop.index }}/{{ loop.index0 }}
6 * {{ loop.revindex }}/{{ loop.revindex0 }}
7 * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
8
9{% endfor %}
10--DATA--
11return array('items' => array('a', 'b'))
12--EXPECT--
13 * 1/0
14 * 2/1
15 * 1//2
16
17 * 2/1
18 * 1/0
19 * /1/2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test
new file mode 100644
index 00000000..58af2c32
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_context_local.test
@@ -0,0 +1,10 @@
1--TEST--
2"for" tag adds a loop variable to the context locally
3--TEMPLATE--
4{% for item in items %}
5{% endfor %}
6{% if loop is not defined %}WORKS{% endif %}
7--DATA--
8return array('items' => array())
9--EXPECT--
10WORKS
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test
new file mode 100644
index 00000000..4301ef2f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined.test
@@ -0,0 +1,10 @@
1--TEST--
2"for" tag
3--TEMPLATE--
4{% for i, item in items if i > 0 %}
5 {{ loop.last }}
6{% endfor %}
7--DATA--
8return array('items' => array('a', 'b'))
9--EXCEPTION--
10Twig_Error_Syntax: The "loop.last" variable is not defined when looping with a condition in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test
new file mode 100644
index 00000000..c7e723a5
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/loop_not_defined_cond.test
@@ -0,0 +1,9 @@
1--TEST--
2"for" tag
3--TEMPLATE--
4{% for i, item in items if loop.last > 0 %}
5{% endfor %}
6--DATA--
7return array('items' => array('a', 'b'))
8--EXCEPTION--
9Twig_Error_Syntax: The "loop" variable cannot be used in a looping condition in "index.twig" at line 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test
new file mode 100644
index 00000000..f8b9f6bc
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/nested_else.test
@@ -0,0 +1,17 @@
1--TEST--
2"for" tag can use an "else" clause
3--TEMPLATE--
4{% for item in items %}
5 {% for item in items1 %}
6 * {{ item }}
7 {% else %}
8 no {{ item }}
9 {% endfor %}
10{% else %}
11 no item1
12{% endfor %}
13--DATA--
14return array('items' => array('a', 'b'), 'items1' => array())
15--EXPECT--
16no a
17 no b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test
new file mode 100644
index 00000000..50344379
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects.test
@@ -0,0 +1,43 @@
1--TEST--
2"for" tag iterates over iterable objects
3--TEMPLATE--
4{% for item in items %}
5 * {{ item }}
6 * {{ loop.index }}/{{ loop.index0 }}
7 * {{ loop.first }}
8
9{% endfor %}
10
11{% for key, value in items %}
12 * {{ key }}/{{ value }}
13{% endfor %}
14
15{% for key in items|keys %}
16 * {{ key }}
17{% endfor %}
18--DATA--
19class ItemsIterator implements Iterator
20{
21 protected $values = array('foo' => 'bar', 'bar' => 'foo');
22 public function current() { return current($this->values); }
23 public function key() { return key($this->values); }
24 public function next() { return next($this->values); }
25 public function rewind() { return reset($this->values); }
26 public function valid() { return false !== current($this->values); }
27}
28return array('items' => new ItemsIterator())
29--EXPECT--
30 * bar
31 * 1/0
32 * 1
33
34 * foo
35 * 2/1
36 *
37
38
39 * foo/bar
40 * bar/foo
41
42 * foo
43 * bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test
new file mode 100644
index 00000000..4a1ff611
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/objects_countable.test
@@ -0,0 +1,47 @@
1--TEST--
2"for" tag iterates over iterable and countable objects
3--TEMPLATE--
4{% for item in items %}
5 * {{ item }}
6 * {{ loop.index }}/{{ loop.index0 }}
7 * {{ loop.revindex }}/{{ loop.revindex0 }}
8 * {{ loop.first }}/{{ loop.last }}/{{ loop.length }}
9
10{% endfor %}
11
12{% for key, value in items %}
13 * {{ key }}/{{ value }}
14{% endfor %}
15
16{% for key in items|keys %}
17 * {{ key }}
18{% endfor %}
19--DATA--
20class ItemsIteratorCountable implements Iterator, Countable
21{
22 protected $values = array('foo' => 'bar', 'bar' => 'foo');
23 public function current() { return current($this->values); }
24 public function key() { return key($this->values); }
25 public function next() { return next($this->values); }
26 public function rewind() { return reset($this->values); }
27 public function valid() { return false !== current($this->values); }
28 public function count() { return count($this->values); }
29}
30return array('items' => new ItemsIteratorCountable())
31--EXPECT--
32 * bar
33 * 1/0
34 * 2/1
35 * 1//2
36
37 * foo
38 * 2/1
39 * 1/0
40 * /1/2
41
42
43 * foo/bar
44 * bar/foo
45
46 * foo
47 * bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test
new file mode 100644
index 00000000..17b2e222
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/recursive.test
@@ -0,0 +1,18 @@
1--TEST--
2"for" tags can be nested
3--TEMPLATE--
4{% for key, item in items %}
5* {{ key }} ({{ loop.length }}):
6{% for value in item %}
7 * {{ value }} ({{ loop.length }})
8{% endfor %}
9{% endfor %}
10--DATA--
11return array('items' => array('a' => array('a1', 'a2', 'a3'), 'b' => array('b1')))
12--EXPECT--
13* a (2):
14 * a1 (3)
15 * a2 (3)
16 * a3 (3)
17* b (2):
18 * b1 (1)
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test
new file mode 100644
index 00000000..82f2ae8a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/for/values.test
@@ -0,0 +1,11 @@
1--TEST--
2"for" tag iterates over item values
3--TEMPLATE--
4{% for item in items %}
5 * {{ item }}
6{% endfor %}
7--DATA--
8return array('items' => array('a', 'b'))
9--EXPECT--
10 * a
11 * b
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test
new file mode 100644
index 00000000..5f5da0ec
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/from.test
@@ -0,0 +1,14 @@
1--TEST--
2global variables
3--TEMPLATE--
4{% include "included.twig" %}
5{% from "included.twig" import foobar %}
6{{ foobar() }}
7--TEMPLATE(included.twig)--
8{% macro foobar() %}
9called foobar
10{% endmacro %}
11--DATA--
12return array();
13--EXPECT--
14called foobar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test
new file mode 100644
index 00000000..c1c3d276
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/basic.test
@@ -0,0 +1,22 @@
1--TEST--
2"if" creates a condition
3--TEMPLATE--
4{% if a is defined %}
5 {{ a }}
6{% elseif b is defined %}
7 {{ b }}
8{% else %}
9 NOTHING
10{% endif %}
11--DATA--
12return array('a' => 'a')
13--EXPECT--
14 a
15--DATA--
16return array('b' => 'b')
17--EXPECT--
18 b
19--DATA--
20return array()
21--EXPECT--
22 NOTHING
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test
new file mode 100644
index 00000000..edfb73df
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/if/expression.test
@@ -0,0 +1,22 @@
1--TEST--
2"if" takes an expression as a test
3--TEMPLATE--
4{% if a < 2 %}
5 A1
6{% elseif a > 10 %}
7 A2
8{% else %}
9 A3
10{% endif %}
11--DATA--
12return array('a' => 1)
13--EXPECT--
14 A1
15--DATA--
16return array('a' => 12)
17--EXPECT--
18 A2
19--DATA--
20return array('a' => 7)
21--EXPECT--
22 A3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test
new file mode 100644
index 00000000..8fe1a6c1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/basic.test
@@ -0,0 +1,16 @@
1--TEST--
2"include" tag
3--TEMPLATE--
4FOO
5{% include "foo.twig" %}
6
7BAR
8--TEMPLATE(foo.twig)--
9FOOBAR
10--DATA--
11return array()
12--EXPECT--
13FOO
14
15FOOBAR
16BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test
new file mode 100644
index 00000000..eaeeb112
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/expression.test
@@ -0,0 +1,16 @@
1--TEST--
2"include" tag allows expressions for the template to include
3--TEMPLATE--
4FOO
5{% include foo %}
6
7BAR
8--TEMPLATE(foo.twig)--
9FOOBAR
10--DATA--
11return array('foo' => 'foo.twig')
12--EXPECT--
13FOO
14
15FOOBAR
16BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test
new file mode 100644
index 00000000..24aed06d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/ignore_missing.test
@@ -0,0 +1,10 @@
1--TEST--
2"include" tag
3--TEMPLATE--
4{% include ["foo.twig", "bar.twig"] ignore missing %}
5{% include "foo.twig" ignore missing %}
6{% include "foo.twig" ignore missing with {} %}
7{% include "foo.twig" ignore missing with {} only %}
8--DATA--
9return array()
10--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test
new file mode 100644
index 00000000..f25e8715
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing.test
@@ -0,0 +1,8 @@
1--TEST--
2"include" tag
3--TEMPLATE--
4{% include "foo.twig" %}
5--DATA--
6return array();
7--EXCEPTION--
8Twig_Error_Loader: Template "foo.twig" is not defined in "index.twig" at line 2.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test
new file mode 100644
index 00000000..86c18644
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/missing_nested.test
@@ -0,0 +1,16 @@
1--TEST--
2"include" tag
3--TEMPLATE--
4{% extends "base.twig" %}
5
6{% block content %}
7 {{ parent() }}
8{% endblock %}
9--TEMPLATE(base.twig)--
10{% block content %}
11 {% include "foo.twig" %}
12{% endblock %}
13--DATA--
14return array();
15--EXCEPTION--
16Twig_Error_Loader: Template "foo.twig" is not defined in "base.twig" at line 3.
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test
new file mode 100644
index 00000000..77760a09
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/only.test
@@ -0,0 +1,16 @@
1--TEST--
2"include" tag accept variables and only
3--TEMPLATE--
4{% include "foo.twig" %}
5{% include "foo.twig" only %}
6{% include "foo.twig" with {'foo1': 'bar'} %}
7{% include "foo.twig" with {'foo1': 'bar'} only %}
8--TEMPLATE(foo.twig)--
9{% for k, v in _context %}{{ k }},{% endfor %}
10--DATA--
11return array('foo' => 'bar')
12--EXPECT--
13foo,global,_parent,
14global,_parent,
15foo,global,foo1,_parent,
16foo1,global,_parent,
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test
new file mode 100644
index 00000000..6ba064a3
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/template_instance.test
@@ -0,0 +1,10 @@
1--TEST--
2"include" tag accepts Twig_Template instance
3--TEMPLATE--
4{% include foo %} FOO
5--TEMPLATE(foo.twig)--
6BAR
7--DATA--
8return array('foo' => $twig->loadTemplate('foo.twig'))
9--EXPECT--
10BAR FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test
new file mode 100644
index 00000000..ab670ee0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/templates_as_array.test
@@ -0,0 +1,12 @@
1--TEST--
2"include" tag
3--TEMPLATE--
4{% include ["foo.twig", "bar.twig"] %}
5{% include ["bar.twig", "foo.twig"] %}
6--TEMPLATE(foo.twig)--
7foo
8--DATA--
9return array()
10--EXPECT--
11foo
12foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test
new file mode 100644
index 00000000..41384ac7
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/include/with_variables.test
@@ -0,0 +1,12 @@
1--TEST--
2"include" tag accept variables
3--TEMPLATE--
4{% include "foo.twig" with {'foo': 'bar'} %}
5{% include "foo.twig" with vars %}
6--TEMPLATE(foo.twig)--
7{{ foo }}
8--DATA--
9return array('vars' => array('foo' => 'bar'))
10--EXPECT--
11bar
12bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test
new file mode 100644
index 00000000..0778a4b4
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/basic.test
@@ -0,0 +1,14 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends "foo.twig" %}
5
6{% block content %}
7FOO
8{% endblock %}
9--TEMPLATE(foo.twig)--
10{% block content %}{% endblock %}
11--DATA--
12return array()
13--EXPECT--
14FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test
new file mode 100644
index 00000000..8576e773
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/conditional.test
@@ -0,0 +1,14 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends standalone ? foo : 'bar.twig' %}
5
6{% block content %}{{ parent() }}FOO{% endblock %}
7--TEMPLATE(foo.twig)--
8{% block content %}FOO{% endblock %}
9--TEMPLATE(bar.twig)--
10{% block content %}BAR{% endblock %}
11--DATA--
12return array('foo' => 'foo.twig', 'standalone' => true)
13--EXPECT--
14FOOFOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test
new file mode 100644
index 00000000..ee06ddce
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/dynamic.test
@@ -0,0 +1,14 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends foo %}
5
6{% block content %}
7FOO
8{% endblock %}
9--TEMPLATE(foo.twig)--
10{% block content %}{% endblock %}
11--DATA--
12return array('foo' => 'foo.twig')
13--EXPECT--
14FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test
new file mode 100644
index 00000000..784f3571
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/empty.test
@@ -0,0 +1,10 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends "foo.twig" %}
5--TEMPLATE(foo.twig)--
6{% block content %}FOO{% endblock %}
7--DATA--
8return array()
9--EXPECT--
10FOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test
new file mode 100644
index 00000000..a1cb1ce8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/extends_as_array.test
@@ -0,0 +1,12 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends ["foo.twig", "bar.twig"] %}
5--TEMPLATE(bar.twig)--
6{% block content %}
7foo
8{% endblock %}
9--DATA--
10return array()
11--EXPECT--
12foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test
new file mode 100644
index 00000000..dfc2b6c4
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/multiple.test
@@ -0,0 +1,12 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %}
5--TEMPLATE(layout.twig)--
6{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %}
7--TEMPLATE(base.twig)--
8{% block content %}base {% endblock %}
9--DATA--
10return array()
11--EXPECT--
12base layout index
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test
new file mode 100644
index 00000000..faca9259
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks.test
@@ -0,0 +1,22 @@
1--TEST--
2"block" tag
3--TEMPLATE--
4{% extends "foo.twig" %}
5
6{% block content %}
7 {% block subcontent %}
8 {% block subsubcontent %}
9 SUBSUBCONTENT
10 {% endblock %}
11 {% endblock %}
12{% endblock %}
13--TEMPLATE(foo.twig)--
14{% block content %}
15 {% block subcontent %}
16 SUBCONTENT
17 {% endblock %}
18{% endblock %}
19--DATA--
20return array()
21--EXPECT--
22SUBSUBCONTENT
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test
new file mode 100644
index 00000000..0ad11d0c
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test
@@ -0,0 +1,15 @@
1--TEST--
2"block" tag
3--TEMPLATE--
4{% block content %}
5 CONTENT
6 {%- block subcontent -%}
7 SUBCONTENT
8 {%- endblock -%}
9 ENDCONTENT
10{% endblock %}
11--TEMPLATE(foo.twig)--
12--DATA--
13return array()
14--EXPECT--
15CONTENTSUBCONTENTENDCONTENT
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test
new file mode 100644
index 00000000..71e3cdfd
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/nested_inheritance.test
@@ -0,0 +1,16 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends "layout.twig" %}
5{% block inside %}INSIDE{% endblock inside %}
6--TEMPLATE(layout.twig)--
7{% extends "base.twig" %}
8{% block body %}
9 {% block inside '' %}
10{% endblock body %}
11--TEMPLATE(base.twig)--
12{% block body '' %}
13--DATA--
14return array()
15--EXPECT--
16INSIDE
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test
new file mode 100644
index 00000000..4f975db8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent.test
@@ -0,0 +1,12 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends "foo.twig" %}
5
6{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %}
7--TEMPLATE(foo.twig)--
8{% block content %}BAR{% endblock %}
9--DATA--
10return array()
11--EXPECT--
12BARFOOBAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test
new file mode 100644
index 00000000..a8bc90ce
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_change.test
@@ -0,0 +1,16 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends foo ? 'foo.twig' : 'bar.twig' %}
5--TEMPLATE(foo.twig)--
6FOO
7--TEMPLATE(bar.twig)--
8BAR
9--DATA--
10return array('foo' => true)
11--EXPECT--
12FOO
13--DATA--
14return array('foo' => false)
15--EXPECT--
16BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test
new file mode 100644
index 00000000..c9e86b1a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_in_a_block.test
@@ -0,0 +1,8 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% block content %}
5 {% extends "foo.twig" %}
6{% endblock %}
7--EXCEPTION--
8Twig_Error_Syntax: Cannot extend from a block in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test
new file mode 100644
index 00000000..62816713
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_isolation.test
@@ -0,0 +1,20 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends "base.twig" %}
5{% block content %}{% include "included.twig" %}{% endblock %}
6
7{% block footer %}Footer{% endblock %}
8--TEMPLATE(included.twig)--
9{% extends "base.twig" %}
10{% block content %}Included Content{% endblock %}
11--TEMPLATE(base.twig)--
12{% block content %}Default Content{% endblock %}
13
14{% block footer %}Default Footer{% endblock %}
15--DATA--
16return array()
17--EXPECT--
18Included Content
19Default Footer
20Footer
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test
new file mode 100644
index 00000000..71e7c208
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_nested.test
@@ -0,0 +1,28 @@
1--TEST--
2"extends" tag
3--TEMPLATE--
4{% extends "foo.twig" %}
5
6{% block content %}
7 {% block inside %}
8 INSIDE OVERRIDDEN
9 {% endblock %}
10
11 BEFORE
12 {{ parent() }}
13 AFTER
14{% endblock %}
15--TEMPLATE(foo.twig)--
16{% block content %}
17 BAR
18{% endblock %}
19--DATA--
20return array()
21--EXPECT--
22
23INSIDE OVERRIDDEN
24
25 BEFORE
26 BAR
27
28 AFTER
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test
new file mode 100644
index 00000000..a9eaa4c1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends.test
@@ -0,0 +1,8 @@
1--TEST--
2"parent" tag
3--TEMPLATE--
4{% block content %}
5 {{ parent() }}
6{% endblock %}
7--EXCEPTION--
8Twig_Error_Syntax: Calling "parent" on a template that does not extend nor "use" another template is forbidden in "index.twig" at line 3
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test
new file mode 100644
index 00000000..63c73055
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test
@@ -0,0 +1,14 @@
1--TEST--
2"parent" tag
3--TEMPLATE--
4{% use 'foo.twig' %}
5
6{% block content %}
7 {{ parent() }}
8{% endblock %}
9--TEMPLATE(foo.twig)--
10{% block content %}BAR{% endblock %}
11--DATA--
12return array()
13--EXPECT--
14BAR
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test
new file mode 100644
index 00000000..d1876a52
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/template_instance.test
@@ -0,0 +1,14 @@
1--TEST--
2"extends" tag accepts Twig_Template instance
3--TEMPLATE--
4{% extends foo %}
5
6{% block content %}
7{{ parent() }}FOO
8{% endblock %}
9--TEMPLATE(foo.twig)--
10{% block content %}BAR{% endblock %}
11--DATA--
12return array('foo' => $twig->loadTemplate('foo.twig'))
13--EXPECT--
14BARFOO
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test
new file mode 100644
index 00000000..8f9ece7c
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/inheritance/use.test
@@ -0,0 +1,44 @@
1--TEST--
2"parent" function
3--TEMPLATE--
4{% extends "parent.twig" %}
5
6{% use "use1.twig" %}
7{% use "use2.twig" %}
8
9{% block content_parent %}
10 {{ parent() }}
11{% endblock %}
12
13{% block content_use1 %}
14 {{ parent() }}
15{% endblock %}
16
17{% block content_use2 %}
18 {{ parent() }}
19{% endblock %}
20
21{% block content %}
22 {{ block('content_use1_only') }}
23 {{ block('content_use2_only') }}
24{% endblock %}
25--TEMPLATE(parent.twig)--
26{% block content_parent 'content_parent' %}
27{% block content_use1 'content_parent' %}
28{% block content_use2 'content_parent' %}
29{% block content '' %}
30--TEMPLATE(use1.twig)--
31{% block content_use1 'content_use1' %}
32{% block content_use2 'content_use1' %}
33{% block content_use1_only 'content_use1_only' %}
34--TEMPLATE(use2.twig)--
35{% block content_use2 'content_use2' %}
36{% block content_use2_only 'content_use2_only' %}
37--DATA--
38return array()
39--EXPECT--
40 content_parent
41 content_use1
42 content_use2
43 content_use1_only
44 content_use2_only
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test
new file mode 100644
index 00000000..eef0c10d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/basic.test
@@ -0,0 +1,17 @@
1--TEST--
2"macro" tag
3--TEMPLATE--
4{% import _self as macros %}
5
6{{ macros.input('username') }}
7{{ macros.input('password', null, 'password', 1) }}
8
9{% macro input(name, value, type, size) %}
10 <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
11{% endmacro %}
12--DATA--
13return array()
14--EXPECT--
15 <input type="text" name="username" value="" size="20">
16
17 <input type="password" name="password" value="" size="1">
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test
new file mode 100644
index 00000000..ae6203bb
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/endmacro_name.test
@@ -0,0 +1,16 @@
1--TEST--
2"macro" tag supports name for endmacro
3--TEMPLATE--
4{% import _self as macros %}
5
6{{ macros.foo() }}
7{{ macros.bar() }}
8
9{% macro foo() %}foo{% endmacro %}
10{% macro bar() %}bar{% endmacro bar %}
11--DATA--
12return array()
13--EXPECT--
14foo
15bar
16
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test
new file mode 100644
index 00000000..5cd3dae6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/external.test
@@ -0,0 +1,17 @@
1--TEST--
2"macro" tag
3--TEMPLATE--
4{% import 'forms.twig' as forms %}
5
6{{ forms.input('username') }}
7{{ forms.input('password', null, 'password', 1) }}
8--TEMPLATE(forms.twig)--
9{% macro input(name, value, type, size) %}
10 <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
11{% endmacro %}
12--DATA--
13return array()
14--EXPECT--
15 <input type="text" name="username" value="" size="20">
16
17 <input type="password" name="password" value="" size="1">
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test
new file mode 100644
index 00000000..205f5918
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/from.test
@@ -0,0 +1,18 @@
1--TEST--
2"macro" tag
3--TEMPLATE--
4{% from 'forms.twig' import foo %}
5{% from 'forms.twig' import foo as foobar, bar %}
6
7{{ foo('foo') }}
8{{ foobar('foo') }}
9{{ bar('foo') }}
10--TEMPLATE(forms.twig)--
11{% macro foo(name) %}foo{{ name }}{% endmacro %}
12{% macro bar(name) %}bar{{ name }}{% endmacro %}
13--DATA--
14return array()
15--EXPECT--
16foofoo
17foofoo
18barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test
new file mode 100644
index 00000000..6b371768
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/global.test
@@ -0,0 +1,14 @@
1--TEST--
2"macro" tag
3--TEMPLATE--
4{% from 'forms.twig' import foo %}
5
6{{ foo('foo') }}
7{{ foo() }}
8--TEMPLATE(forms.twig)--
9{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %}
10--DATA--
11return array()
12--EXPECT--
13fooglobal
14fooglobal
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test
new file mode 100644
index 00000000..17756cb6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/self_import.test
@@ -0,0 +1,17 @@
1--TEST--
2"macro" tag
3--TEMPLATE--
4{% import _self as forms %}
5
6{{ forms.input('username') }}
7{{ forms.input('password', null, 'password', 1) }}
8
9{% macro input(name, value, type, size) %}
10 <input type="{{ type|default("text") }}" name="{{ name }}" value="{{ value|e|default('') }}" size="{{ size|default(20) }}">
11{% endmacro %}
12--DATA--
13return array()
14--EXPECT--
15 <input type="text" name="username" value="" size="20">
16
17 <input type="password" name="password" value="" size="1">
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test
new file mode 100644
index 00000000..37217707
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/macro/special_chars.test
@@ -0,0 +1,14 @@
1--TEST--
2"§" as a macro name
3--TEMPLATE--
4{% import _self as macros %}
5
6{{ macros.§('foo') }}
7
8{% macro §(foo) %}
9 §{{ foo }}§
10{% endmacro %}
11--DATA--
12return array()
13--EXPECT--
14§foo§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test
new file mode 100644
index 00000000..0445e853
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/basic.test
@@ -0,0 +1,10 @@
1--TEST--
2"raw" tag
3--TEMPLATE--
4{% raw %}
5{{ foo }}
6{% endraw %}
7--DATA--
8return array()
9--EXPECT--
10{{ foo }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test
new file mode 100644
index 00000000..2fd9fb26
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/mixed_usage_with_raw.test
@@ -0,0 +1,10 @@
1--TEST--
2"raw" tag
3--TEMPLATE--
4{% raw %}
5{{ foo }}
6{% endverbatim %}
7--DATA--
8return array()
9--EXCEPTION--
10Twig_Error_Syntax: Unexpected end of file: Unclosed "raw" block in "index.twig" at line 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test
new file mode 100644
index 00000000..352bb187
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/raw/whitespace_control.test
@@ -0,0 +1,56 @@
1--TEST--
2"raw" tag
3--TEMPLATE--
41***
5
6{%- raw %}
7 {{ 'bla' }}
8{% endraw %}
9
101***
112***
12
13{%- raw -%}
14 {{ 'bla' }}
15{% endraw %}
16
172***
183***
19
20{%- raw -%}
21 {{ 'bla' }}
22{% endraw -%}
23
243***
254***
26
27{%- raw -%}
28 {{ 'bla' }}
29{%- endraw %}
30
314***
325***
33
34{%- raw -%}
35 {{ 'bla' }}
36{%- endraw -%}
37
385***
39--DATA--
40return array()
41--EXPECT--
421***
43 {{ 'bla' }}
44
45
461***
472***{{ 'bla' }}
48
49
502***
513***{{ 'bla' }}
523***
534***{{ 'bla' }}
54
554***
565***{{ 'bla' }}5***
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test
new file mode 100644
index 00000000..683c59a0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid1.test
@@ -0,0 +1,11 @@
1--TEST--
2sandbox tag
3--TEMPLATE--
4{%- sandbox %}
5 {%- include "foo.twig" %}
6 a
7{%- endsandbox %}
8--TEMPLATE(foo.twig)--
9foo
10--EXCEPTION--
11Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test
new file mode 100644
index 00000000..3dcfa88c
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/not_valid2.test
@@ -0,0 +1,14 @@
1--TEST--
2sandbox tag
3--TEMPLATE--
4{%- sandbox %}
5 {%- include "foo.twig" %}
6
7 {% if 1 %}
8 {%- include "foo.twig" %}
9 {% endif %}
10{%- endsandbox %}
11--TEMPLATE(foo.twig)--
12foo
13--EXCEPTION--
14Twig_Error_Syntax: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test
new file mode 100644
index 00000000..de20f3db
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/sandbox/simple.test
@@ -0,0 +1,22 @@
1--TEST--
2sandbox tag
3--TEMPLATE--
4{%- sandbox %}
5 {%- include "foo.twig" %}
6{%- endsandbox %}
7
8{%- sandbox %}
9 {%- include "foo.twig" %}
10 {%- include "foo.twig" %}
11{%- endsandbox %}
12
13{%- sandbox %}{% include "foo.twig" %}{% endsandbox %}
14--TEMPLATE(foo.twig)--
15foo
16--DATA--
17return array()
18--EXPECT--
19foo
20foo
21foo
22foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test
new file mode 100644
index 00000000..a5a9f830
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/basic.test
@@ -0,0 +1,20 @@
1--TEST--
2"set" tag
3--TEMPLATE--
4{% set foo = 'foo' %}
5{% set bar = 'foo<br />' %}
6
7{{ foo }}
8{{ bar }}
9
10{% set foo, bar = 'foo', 'bar' %}
11
12{{ foo }}{{ bar }}
13--DATA--
14return array()
15--EXPECT--
16foo
17foo&lt;br /&gt;
18
19
20foobar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test
new file mode 100644
index 00000000..ec657f00
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture-empty.test
@@ -0,0 +1,9 @@
1--TEST--
2"set" tag block empty capture
3--TEMPLATE--
4{% set foo %}{% endset %}
5
6{% if foo %}FAIL{% endif %}
7--DATA--
8return array()
9--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test
new file mode 100644
index 00000000..f156a1a7
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/capture.test
@@ -0,0 +1,10 @@
1--TEST--
2"set" tag block capture
3--TEMPLATE--
4{% set foo %}f<br />o<br />o{% endset %}
5
6{{ foo }}
7--DATA--
8return array()
9--EXPECT--
10f<br />o<br />o
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test
new file mode 100644
index 00000000..8ff434a0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/set/expression.test
@@ -0,0 +1,12 @@
1--TEST--
2"set" tag
3--TEMPLATE--
4{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %}
5
6{{ foo }}
7{{ bar }}
8--DATA--
9return array()
10--EXPECT--
11foobar
12barfoo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test
new file mode 100644
index 00000000..dd06dec2
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/spaceless/simple.test
@@ -0,0 +1,12 @@
1--TEST--
2"spaceless" tag removes whites between HTML tags
3--TEMPLATE--
4{% spaceless %}
5
6 <div> <div> foo </div> </div>
7
8{% endspaceless %}
9--DATA--
10return array()
11--EXPECT--
12<div><div> foo </div></div>
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test
new file mode 100644
index 00000000..789b4ba8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/special_chars.test
@@ -0,0 +1,8 @@
1--TEST--
2"§" custom tag
3--TEMPLATE--
4{% § %}
5--DATA--
6return array()
7--EXPECT--
8§
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test
new file mode 100644
index 00000000..1d2273f8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/trim_block.test
@@ -0,0 +1,74 @@
1--TEST--
2Whitespace trimming on tags.
3--TEMPLATE--
4{{ 5 * '{#-'|length }}
5{{ '{{-'|length * 5 + '{%-'|length }}
6
7Trim on control tag:
8{% for i in range(1, 9) -%}
9 {{ i }}
10{%- endfor %}
11
12
13Trim on output tag:
14{% for i in range(1, 9) %}
15 {{- i -}}
16{% endfor %}
17
18
19Trim comments:
20
21{#- Invisible -#}
22
23After the comment.
24
25Trim leading space:
26{% if leading %}
27
28 {{- leading }}
29{% endif %}
30
31{%- if leading %}
32 {{- leading }}
33
34{%- endif %}
35
36
37Trim trailing space:
38{% if trailing -%}
39 {{ trailing -}}
40
41{% endif -%}
42
43Combined:
44
45{%- if both -%}
46<ul>
47 <li> {{- both -}} </li>
48</ul>
49
50{%- endif -%}
51
52end
53--DATA--
54return array('leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both')
55--EXPECT--
5615
5718
58
59Trim on control tag:
60123456789
61
62Trim on output tag:
63123456789
64
65Trim comments:After the comment.
66
67Trim leading space:
68leading space
69leading space
70
71Trim trailing space:
72trailing spaceCombined:<ul>
73 <li>both</li>
74</ul>end
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test
new file mode 100644
index 00000000..f887006f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/aliases.test
@@ -0,0 +1,12 @@
1--TEST--
2"use" tag
3--TEMPLATE--
4{% use "blocks.twig" with content as foo %}
5
6{{ block('foo') }}
7--TEMPLATE(blocks.twig)--
8{% block content 'foo' %}
9--DATA--
10return array()
11--EXPECT--
12foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test
new file mode 100644
index 00000000..7364d76d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/basic.test
@@ -0,0 +1,12 @@
1--TEST--
2"use" tag
3--TEMPLATE--
4{% use "blocks.twig" %}
5
6{{ block('content') }}
7--TEMPLATE(blocks.twig)--
8{% block content 'foo' %}
9--DATA--
10return array()
11--EXPECT--
12foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test
new file mode 100644
index 00000000..b551a1e6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep.test
@@ -0,0 +1,22 @@
1--TEST--
2"use" tag
3--TEMPLATE--
4{% use "foo.twig" %}
5
6{{ block('content') }}
7{{ block('foo') }}
8{{ block('bar') }}
9--TEMPLATE(foo.twig)--
10{% use "bar.twig" %}
11
12{% block content 'foo' %}
13{% block foo 'foo' %}
14--TEMPLATE(bar.twig)--
15{% block content 'bar' %}
16{% block bar 'bar' %}
17--DATA--
18return array()
19--EXPECT--
20foo
21foo
22bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test
new file mode 100644
index 00000000..05cca682
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/deep_empty.test
@@ -0,0 +1,10 @@
1--TEST--
2"use" tag
3--TEMPLATE--
4{% use "foo.twig" %}
5--TEMPLATE(foo.twig)--
6{% use "bar.twig" %}
7--TEMPLATE(bar.twig)--
8--DATA--
9return array()
10--EXPECT--
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test
new file mode 100644
index 00000000..198be0c5
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple.test
@@ -0,0 +1,21 @@
1--TEST--
2"use" tag
3--TEMPLATE--
4{% use "foo.twig" %}
5{% use "bar.twig" %}
6
7{{ block('content') }}
8{{ block('foo') }}
9{{ block('bar') }}
10--TEMPLATE(foo.twig)--
11{% block content 'foo' %}
12{% block foo 'foo' %}
13--TEMPLATE(bar.twig)--
14{% block content 'bar' %}
15{% block bar 'bar' %}
16--DATA--
17return array()
18--EXPECT--
19bar
20foo
21bar
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test
new file mode 100644
index 00000000..8de871a8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/use/multiple_aliases.test
@@ -0,0 +1,23 @@
1--TEST--
2"use" tag
3--TEMPLATE--
4{% use "foo.twig" with content as foo_content %}
5{% use "bar.twig" %}
6
7{{ block('content') }}
8{{ block('foo') }}
9{{ block('bar') }}
10{{ block('foo_content') }}
11--TEMPLATE(foo.twig)--
12{% block content 'foo' %}
13{% block foo 'foo' %}
14--TEMPLATE(bar.twig)--
15{% block content 'bar' %}
16{% block bar 'bar' %}
17--DATA--
18return array()
19--EXPECT--
20bar
21foo
22bar
23foo
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test
new file mode 100644
index 00000000..a95be557
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/basic.test
@@ -0,0 +1,10 @@
1--TEST--
2"verbatim" tag
3--TEMPLATE--
4{% verbatim %}
5{{ foo }}
6{% endverbatim %}
7--DATA--
8return array()
9--EXPECT--
10{{ foo }}
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test
new file mode 100644
index 00000000..941dddcc
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/mixed_usage_with_raw.test
@@ -0,0 +1,10 @@
1--TEST--
2"verbatim" tag
3--TEMPLATE--
4{% verbatim %}
5{{ foo }}
6{% endraw %}
7--DATA--
8return array()
9--EXCEPTION--
10Twig_Error_Syntax: Unexpected end of file: Unclosed "verbatim" block in "index.twig" at line 2
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test
new file mode 100644
index 00000000..eb610444
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tags/verbatim/whitespace_control.test
@@ -0,0 +1,56 @@
1--TEST--
2"verbatim" tag
3--TEMPLATE--
41***
5
6{%- verbatim %}
7 {{ 'bla' }}
8{% endverbatim %}
9
101***
112***
12
13{%- verbatim -%}
14 {{ 'bla' }}
15{% endverbatim %}
16
172***
183***
19
20{%- verbatim -%}
21 {{ 'bla' }}
22{% endverbatim -%}
23
243***
254***
26
27{%- verbatim -%}
28 {{ 'bla' }}
29{%- endverbatim %}
30
314***
325***
33
34{%- verbatim -%}
35 {{ 'bla' }}
36{%- endverbatim -%}
37
385***
39--DATA--
40return array()
41--EXPECT--
421***
43 {{ 'bla' }}
44
45
461***
472***{{ 'bla' }}
48
49
502***
513***{{ 'bla' }}
523***
534***{{ 'bla' }}
54
554***
565***{{ 'bla' }}5***
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test
new file mode 100644
index 00000000..1429d375
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/array.test
@@ -0,0 +1,24 @@
1--TEST--
2array index test
3--TEMPLATE--
4{% for key, value in days %}
5{{ key }}
6{% endfor %}
7--DATA--
8return array('days' => array(
9 1 => array('money' => 9),
10 2 => array('money' => 21),
11 3 => array('money' => 38),
12 4 => array('money' => 6),
13 18 => array('money' => 6),
14 19 => array('money' => 3),
15 31 => array('money' => 11),
16));
17--EXPECT--
181
192
203
214
2218
2319
2431
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test
new file mode 100644
index 00000000..60218ac0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/constant.test
@@ -0,0 +1,14 @@
1--TEST--
2"const" test
3--TEMPLATE--
4{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }}
5{{ 'bar' is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
6{{ value is constant('TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }}
7{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }}
8--DATA--
9return array('value' => 'bar', 'object' => new ArrayObject(array('hi')));
10--EXPECT--
11ok
12ok
13ok
14ok \ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test
new file mode 100644
index 00000000..cbfe03de
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/defined.test
@@ -0,0 +1,108 @@
1--TEST--
2"defined" test
3--TEMPLATE--
4{{ definedVar is defined ? 'ok' : 'ko' }}
5{{ definedVar is not defined ? 'ko' : 'ok' }}
6{{ undefinedVar is defined ? 'ko' : 'ok' }}
7{{ undefinedVar is not defined ? 'ok' : 'ko' }}
8{{ zeroVar is defined ? 'ok' : 'ko' }}
9{{ nullVar is defined ? 'ok' : 'ko' }}
10{{ nested.definedVar is defined ? 'ok' : 'ko' }}
11{{ nested['definedVar'] is defined ? 'ok' : 'ko' }}
12{{ nested.definedVar is not defined ? 'ko' : 'ok' }}
13{{ nested.undefinedVar is defined ? 'ko' : 'ok' }}
14{{ nested['undefinedVar'] is defined ? 'ko' : 'ok' }}
15{{ nested.undefinedVar is not defined ? 'ok' : 'ko' }}
16{{ nested.zeroVar is defined ? 'ok' : 'ko' }}
17{{ nested.nullVar is defined ? 'ok' : 'ko' }}
18{{ nested.definedArray.0 is defined ? 'ok' : 'ko' }}
19{{ nested['definedArray'][0] is defined ? 'ok' : 'ko' }}
20{{ object.foo is defined ? 'ok' : 'ko' }}
21{{ object.undefinedMethod is defined ? 'ko' : 'ok' }}
22{{ object.getFoo() is defined ? 'ok' : 'ko' }}
23{{ object.getFoo('a') is defined ? 'ok' : 'ko' }}
24{{ object.undefinedMethod() is defined ? 'ko' : 'ok' }}
25{{ object.undefinedMethod('a') is defined ? 'ko' : 'ok' }}
26{{ object.self.foo is defined ? 'ok' : 'ko' }}
27{{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }}
28{{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }}
29--DATA--
30return array(
31 'definedVar' => 'defined',
32 'zeroVar' => 0,
33 'nullVar' => null,
34 'nested' => array(
35 'definedVar' => 'defined',
36 'zeroVar' => 0,
37 'nullVar' => null,
38 'definedArray' => array(0),
39 ),
40 'object' => new TwigTestFoo(),
41);
42--EXPECT--
43ok
44ok
45ok
46ok
47ok
48ok
49ok
50ok
51ok
52ok
53ok
54ok
55ok
56ok
57ok
58ok
59ok
60ok
61ok
62ok
63ok
64ok
65ok
66ok
67ok
68--DATA--
69return array(
70 'definedVar' => 'defined',
71 'zeroVar' => 0,
72 'nullVar' => null,
73 'nested' => array(
74 'definedVar' => 'defined',
75 'zeroVar' => 0,
76 'nullVar' => null,
77 'definedArray' => array(0),
78 ),
79 'object' => new TwigTestFoo(),
80);
81--CONFIG--
82return array('strict_variables' => false)
83--EXPECT--
84ok
85ok
86ok
87ok
88ok
89ok
90ok
91ok
92ok
93ok
94ok
95ok
96ok
97ok
98ok
99ok
100ok
101ok
102ok
103ok
104ok
105ok
106ok
107ok
108ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test
new file mode 100644
index 00000000..a776d032
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/empty.test
@@ -0,0 +1,45 @@
1--TEST--
2"empty" test
3--TEMPLATE--
4{{ foo is empty ? 'ok' : 'ko' }}
5{{ bar is empty ? 'ok' : 'ko' }}
6{{ foobar is empty ? 'ok' : 'ko' }}
7{{ array is empty ? 'ok' : 'ko' }}
8{{ zero is empty ? 'ok' : 'ko' }}
9{{ string is empty ? 'ok' : 'ko' }}
10{{ countable_empty is empty ? 'ok' : 'ko' }}
11{{ countable_not_empty is empty ? 'ok' : 'ko' }}
12{{ markup_empty is empty ? 'ok' : 'ko' }}
13{{ markup_not_empty is empty ? 'ok' : 'ko' }}
14--DATA--
15
16class CountableStub implements Countable
17{
18 private $items;
19
20 public function __construct(array $items)
21 {
22 $this->items = $items;
23 }
24
25 public function count()
26 {
27 return count($this->items);
28 }
29}
30return array(
31 'foo' => '', 'bar' => null, 'foobar' => false, 'array' => array(), 'zero' => 0, 'string' => '0',
32 'countable_empty' => new CountableStub(array()), 'countable_not_empty' => new CountableStub(array(1, 2)),
33 'markup_empty' => new Twig_Markup('', 'UTF-8'), 'markup_not_empty' => new Twig_Markup('test', 'UTF-8'),
34);
35--EXPECT--
36ok
37ok
38ok
39ok
40ko
41ko
42ok
43ko
44ok
45ko
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test
new file mode 100644
index 00000000..695b4c2f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/even.test
@@ -0,0 +1,14 @@
1--TEST--
2"even" test
3--TEMPLATE--
4{{ 1 is even ? 'ko' : 'ok' }}
5{{ 2 is even ? 'ok' : 'ko' }}
6{{ 1 is not even ? 'ok' : 'ko' }}
7{{ 2 is not even ? 'ko' : 'ok' }}
8--DATA--
9return array()
10--EXPECT--
11ok
12ok
13ok
14ok
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test
new file mode 100644
index 00000000..45c72fd2
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in.test
@@ -0,0 +1,48 @@
1--TEST--
2Twig supports the in operator
3--TEMPLATE--
4{% if bar in foo %}
5TRUE
6{% endif %}
7{% if not (bar in foo) %}
8{% else %}
9TRUE
10{% endif %}
11{% if bar not in foo %}
12{% else %}
13TRUE
14{% endif %}
15{% if 'a' in bar %}
16TRUE
17{% endif %}
18{% if 'c' not in bar %}
19TRUE
20{% endif %}
21{% if '' not in bar %}
22TRUE
23{% endif %}
24{% if '' in '' %}
25TRUE
26{% endif %}
27{% if '0' not in '' %}
28TRUE
29{% endif %}
30{% if 'a' not in '0' %}
31TRUE
32{% endif %}
33{% if '0' in '0' %}
34TRUE
35{% endif %}
36--DATA--
37return array('bar' => 'bar', 'foo' => array('bar' => 'bar'))
38--EXPECT--
39TRUE
40TRUE
41TRUE
42TRUE
43TRUE
44TRUE
45TRUE
46TRUE
47TRUE
48TRUE
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test
new file mode 100644
index 00000000..8e08061b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/in_with_objects.test
@@ -0,0 +1,19 @@
1--TEST--
2Twig supports the in operator when using objects
3--TEMPLATE--
4{% if object in object_list %}
5TRUE
6{% endif %}
7--DATA--
8$foo = new TwigTestFoo();
9$foo1 = new TwigTestFoo();
10
11$foo->position = $foo1;
12$foo1->position = $foo;
13
14return array(
15 'object' => $foo,
16 'object_list' => array($foo1, $foo),
17);
18--EXPECT--
19TRUE
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test
new file mode 100644
index 00000000..ec525501
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/iterable.test
@@ -0,0 +1,19 @@
1--TEST--
2"iterable" test
3--TEMPLATE--
4{{ foo is iterable ? 'ok' : 'ko' }}
5{{ traversable is iterable ? 'ok' : 'ko' }}
6{{ obj is iterable ? 'ok' : 'ko' }}
7{{ val is iterable ? 'ok' : 'ko' }}
8--DATA--
9return array(
10 'foo' => array(),
11 'traversable' => new ArrayIterator(array()),
12 'obj' => new stdClass(),
13 'val' => 'test',
14);
15--EXPECT--
16ok
17ok
18ko
19ko \ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test
new file mode 100644
index 00000000..1b8311e3
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Fixtures/tests/odd.test
@@ -0,0 +1,10 @@
1--TEST--
2"odd" test
3--TEMPLATE--
4{{ 1 is odd ? 'ok' : 'ko' }}
5{{ 2 is odd ? 'ko' : 'ok' }}
6--DATA--
7return array()
8--EXPECT--
9ok
10ok \ No newline at end of file
diff --git a/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php b/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php
new file mode 100644
index 00000000..5feb8f4e
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/IntegrationTest.php
@@ -0,0 +1,217 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12// This function is defined to check that escaping strategies
13// like html works even if a function with the same name is defined.
14function html()
15{
16 return 'foo';
17}
18
19class Twig_Tests_IntegrationTest extends Twig_Test_IntegrationTestCase
20{
21 public function getExtensions()
22 {
23 $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(), array(), array());
24
25 return array(
26 new Twig_Extension_Debug(),
27 new Twig_Extension_Sandbox($policy, false),
28 new Twig_Extension_StringLoader(),
29 new TwigTestExtension(),
30 );
31 }
32
33 public function getFixturesDir()
34 {
35 return dirname(__FILE__).'/Fixtures/';
36 }
37}
38
39function test_foo($value = 'foo')
40{
41 return $value;
42}
43
44class TwigTestFoo implements Iterator
45{
46 const BAR_NAME = 'bar';
47
48 public $position = 0;
49 public $array = array(1, 2);
50
51 public function bar($param1 = null, $param2 = null)
52 {
53 return 'bar'.($param1 ? '_'.$param1 : '').($param2 ? '-'.$param2 : '');
54 }
55
56 public function getFoo()
57 {
58 return 'foo';
59 }
60
61 public function getSelf()
62 {
63 return $this;
64 }
65
66 public function is()
67 {
68 return 'is';
69 }
70
71 public function in()
72 {
73 return 'in';
74 }
75
76 public function not()
77 {
78 return 'not';
79 }
80
81 public function strToLower($value)
82 {
83 return strtolower($value);
84 }
85
86 public function rewind()
87 {
88 $this->position = 0;
89 }
90
91 public function current()
92 {
93 return $this->array[$this->position];
94 }
95
96 public function key()
97 {
98 return 'a';
99 }
100
101 public function next()
102 {
103 ++$this->position;
104 }
105
106 public function valid()
107 {
108 return isset($this->array[$this->position]);
109 }
110}
111
112class TwigTestTokenParser_§ extends Twig_TokenParser
113{
114 public function parse(Twig_Token $token)
115 {
116 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
117
118 return new Twig_Node_Print(new Twig_Node_Expression_Constant('§', -1), -1);
119 }
120
121 public function getTag()
122 {
123 return '§';
124 }
125}
126
127class TwigTestExtension extends Twig_Extension
128{
129 public function getTokenParsers()
130 {
131 return array(
132 new TwigTestTokenParser_§(),
133 );
134 }
135
136 public function getFilters()
137 {
138 return array(
139 '§' => new Twig_Filter_Method($this, '§Filter'),
140 'escape_and_nl2br' => new Twig_Filter_Method($this, 'escape_and_nl2br', array('needs_environment' => true, 'is_safe' => array('html'))),
141 'nl2br' => new Twig_Filter_Method($this, 'nl2br', array('pre_escape' => 'html', 'is_safe' => array('html'))),
142 'escape_something' => new Twig_Filter_Method($this, 'escape_something', array('is_safe' => array('something'))),
143 'preserves_safety' => new Twig_Filter_Method($this, 'preserves_safety', array('preserves_safety' => array('html'))),
144 '*_path' => new Twig_Filter_Method($this, 'dynamic_path'),
145 '*_foo_*_bar' => new Twig_Filter_Method($this, 'dynamic_foo'),
146 );
147 }
148
149 public function getFunctions()
150 {
151 return array(
152 '§' => new Twig_Function_Method($this, '§Function'),
153 'safe_br' => new Twig_Function_Method($this, 'br', array('is_safe' => array('html'))),
154 'unsafe_br' => new Twig_Function_Method($this, 'br'),
155 '*_path' => new Twig_Function_Method($this, 'dynamic_path'),
156 '*_foo_*_bar' => new Twig_Function_Method($this, 'dynamic_foo'),
157 );
158 }
159
160 public function §Filter($value)
161 {
162 return "§{$value}§";
163 }
164
165 public function §Function($value)
166 {
167 return "§{$value}§";
168 }
169
170 /**
171 * nl2br which also escapes, for testing escaper filters
172 */
173 public function escape_and_nl2br($env, $value, $sep = '<br />')
174 {
175 return $this->nl2br(twig_escape_filter($env, $value, 'html'), $sep);
176 }
177
178 /**
179 * nl2br only, for testing filters with pre_escape
180 */
181 public function nl2br($value, $sep = '<br />')
182 {
183 // not secure if $value contains html tags (not only entities)
184 // don't use
185 return str_replace("\n", "$sep\n", $value);
186 }
187
188 public function dynamic_path($element, $item)
189 {
190 return $element.'/'.$item;
191 }
192
193 public function dynamic_foo($foo, $bar, $item)
194 {
195 return $foo.'/'.$bar.'/'.$item;
196 }
197
198 public function escape_something($value)
199 {
200 return strtoupper($value);
201 }
202
203 public function preserves_safety($value)
204 {
205 return strtoupper($value);
206 }
207
208 public function br()
209 {
210 return '<br />';
211 }
212
213 public function getName()
214 {
215 return 'integration_test';
216 }
217}
diff --git a/vendor/twig/twig/test/Twig/Tests/LexerTest.php b/vendor/twig/twig/test/Twig/Tests/LexerTest.php
new file mode 100644
index 00000000..9f3c7510
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/LexerTest.php
@@ -0,0 +1,301 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Tests_LexerTest extends PHPUnit_Framework_TestCase
12{
13 public function testNameLabelForTag()
14 {
15 $template = '{% § %}';
16
17 $lexer = new Twig_Lexer(new Twig_Environment());
18 $stream = $lexer->tokenize($template);
19
20 $stream->expect(Twig_Token::BLOCK_START_TYPE);
21 $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
22 }
23
24 public function testNameLabelForFunction()
25 {
26 $template = '{{ §() }}';
27
28 $lexer = new Twig_Lexer(new Twig_Environment());
29 $stream = $lexer->tokenize($template);
30
31 $stream->expect(Twig_Token::VAR_START_TYPE);
32 $this->assertSame('§', $stream->expect(Twig_Token::NAME_TYPE)->getValue());
33 }
34
35 public function testBracketsNesting()
36 {
37 $template = '{{ {"a":{"b":"c"}} }}';
38
39 $this->assertEquals(2, $this->countToken($template, Twig_Token::PUNCTUATION_TYPE, '{'));
40 $this->assertEquals(2, $this->countToken($template, Twig_Token::PUNCTUATION_TYPE, '}'));
41 }
42
43 protected function countToken($template, $type, $value = null)
44 {
45 $lexer = new Twig_Lexer(new Twig_Environment());
46 $stream = $lexer->tokenize($template);
47
48 $count = 0;
49 $tokens = array();
50 while (!$stream->isEOF()) {
51 $token = $stream->next();
52 if ($type === $token->getType()) {
53 if (null === $value || $value === $token->getValue()) {
54 ++$count;
55 }
56 }
57 }
58
59 return $count;
60 }
61
62 public function testLineDirective()
63 {
64 $template = "foo\n"
65 . "bar\n"
66 . "{% line 10 %}\n"
67 . "{{\n"
68 . "baz\n"
69 . "}}\n";
70
71 $lexer = new Twig_Lexer(new Twig_Environment());
72 $stream = $lexer->tokenize($template);
73
74 // foo\nbar\n
75 $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
76 // \n (after {% line %})
77 $this->assertSame(10, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
78 // {{
79 $this->assertSame(11, $stream->expect(Twig_Token::VAR_START_TYPE)->getLine());
80 // baz
81 $this->assertSame(12, $stream->expect(Twig_Token::NAME_TYPE)->getLine());
82 }
83
84 public function testLineDirectiveInline()
85 {
86 $template = "foo\n"
87 . "bar{% line 10 %}{{\n"
88 . "baz\n"
89 . "}}\n";
90
91 $lexer = new Twig_Lexer(new Twig_Environment());
92 $stream = $lexer->tokenize($template);
93
94 // foo\nbar
95 $this->assertSame(1, $stream->expect(Twig_Token::TEXT_TYPE)->getLine());
96 // {{
97 $this->assertSame(10, $stream->expect(Twig_Token::VAR_START_TYPE)->getLine());
98 // baz
99 $this->assertSame(11, $stream->expect(Twig_Token::NAME_TYPE)->getLine());
100 }
101
102 public function testLongComments()
103 {
104 $template = '{# '.str_repeat('*', 100000).' #}';
105
106 $lexer = new Twig_Lexer(new Twig_Environment());
107 $lexer->tokenize($template);
108
109 // should not throw an exception
110 }
111
112 public function testLongRaw()
113 {
114 $template = '{% raw %}'.str_repeat('*', 100000).'{% endraw %}';
115
116 $lexer = new Twig_Lexer(new Twig_Environment());
117 $stream = $lexer->tokenize($template);
118
119 // should not throw an exception
120 }
121
122 public function testLongVar()
123 {
124 $template = '{{ '.str_repeat('x', 100000).' }}';
125
126 $lexer = new Twig_Lexer(new Twig_Environment());
127 $stream = $lexer->tokenize($template);
128
129 // should not throw an exception
130 }
131
132 public function testLongBlock()
133 {
134 $template = '{% '.str_repeat('x', 100000).' %}';
135
136 $lexer = new Twig_Lexer(new Twig_Environment());
137 $stream = $lexer->tokenize($template);
138
139 // should not throw an exception
140 }
141
142 public function testBigNumbers()
143 {
144 $template = '{{ 922337203685477580700 }}';
145
146 $lexer = new Twig_Lexer(new Twig_Environment());
147 $stream = $lexer->tokenize($template);
148 $node = $stream->next();
149 $node = $stream->next();
150 $this->assertEquals(922337203685477580700, $node->getValue());
151 }
152
153 public function testStringWithEscapedDelimiter()
154 {
155 $tests = array(
156 "{{ 'foo \' bar' }}" => 'foo \' bar',
157 '{{ "foo \" bar" }}' => "foo \" bar",
158 );
159 $lexer = new Twig_Lexer(new Twig_Environment());
160 foreach ($tests as $template => $expected) {
161 $stream = $lexer->tokenize($template);
162 $stream->expect(Twig_Token::VAR_START_TYPE);
163 $stream->expect(Twig_Token::STRING_TYPE, $expected);
164 }
165 }
166
167 public function testStringWithInterpolation()
168 {
169 $template = 'foo {{ "bar #{ baz + 1 }" }}';
170
171 $lexer = new Twig_Lexer(new Twig_Environment());
172 $stream = $lexer->tokenize($template);
173 $stream->expect(Twig_Token::TEXT_TYPE, 'foo ');
174 $stream->expect(Twig_Token::VAR_START_TYPE);
175 $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
176 $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
177 $stream->expect(Twig_Token::NAME_TYPE, 'baz');
178 $stream->expect(Twig_Token::OPERATOR_TYPE, '+');
179 $stream->expect(Twig_Token::NUMBER_TYPE, '1');
180 $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
181 $stream->expect(Twig_Token::VAR_END_TYPE);
182 }
183
184 public function testStringWithEscapedInterpolation()
185 {
186 $template = '{{ "bar \#{baz+1}" }}';
187
188 $lexer = new Twig_Lexer(new Twig_Environment());
189 $stream = $lexer->tokenize($template);
190 $stream->expect(Twig_Token::VAR_START_TYPE);
191 $stream->expect(Twig_Token::STRING_TYPE, 'bar #{baz+1}');
192 $stream->expect(Twig_Token::VAR_END_TYPE);
193 }
194
195 public function testStringWithHash()
196 {
197 $template = '{{ "bar # baz" }}';
198
199 $lexer = new Twig_Lexer(new Twig_Environment());
200 $stream = $lexer->tokenize($template);
201 $stream->expect(Twig_Token::VAR_START_TYPE);
202 $stream->expect(Twig_Token::STRING_TYPE, 'bar # baz');
203 $stream->expect(Twig_Token::VAR_END_TYPE);
204 }
205
206 /**
207 * @expectedException Twig_Error_Syntax
208 * @expectedExceptionMessage Unclosed """
209 */
210 public function testStringWithUnterminatedInterpolation()
211 {
212 $template = '{{ "bar #{x" }}';
213
214 $lexer = new Twig_Lexer(new Twig_Environment());
215 $stream = $lexer->tokenize($template);
216 }
217
218 public function testStringWithNestedInterpolations()
219 {
220 $template = '{{ "bar #{ "foo#{bar}" }" }}';
221
222 $lexer = new Twig_Lexer(new Twig_Environment());
223 $stream = $lexer->tokenize($template);
224 $stream->expect(Twig_Token::VAR_START_TYPE);
225 $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
226 $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
227 $stream->expect(Twig_Token::STRING_TYPE, 'foo');
228 $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
229 $stream->expect(Twig_Token::NAME_TYPE, 'bar');
230 $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
231 $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
232 $stream->expect(Twig_Token::VAR_END_TYPE);
233 }
234
235 public function testStringWithNestedInterpolationsInBlock()
236 {
237 $template = '{% foo "bar #{ "foo#{bar}" }" %}';
238
239 $lexer = new Twig_Lexer(new Twig_Environment());
240 $stream = $lexer->tokenize($template);
241 $stream->expect(Twig_Token::BLOCK_START_TYPE);
242 $stream->expect(Twig_Token::NAME_TYPE, 'foo');
243 $stream->expect(Twig_Token::STRING_TYPE, 'bar ');
244 $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
245 $stream->expect(Twig_Token::STRING_TYPE, 'foo');
246 $stream->expect(Twig_Token::INTERPOLATION_START_TYPE);
247 $stream->expect(Twig_Token::NAME_TYPE, 'bar');
248 $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
249 $stream->expect(Twig_Token::INTERPOLATION_END_TYPE);
250 $stream->expect(Twig_Token::BLOCK_END_TYPE);
251 }
252
253 public function testOperatorEndingWithALetterAtTheEndOfALine()
254 {
255 $template = "{{ 1 and\n0}}";
256
257 $lexer = new Twig_Lexer(new Twig_Environment());
258 $stream = $lexer->tokenize($template);
259 $stream->expect(Twig_Token::VAR_START_TYPE);
260 $stream->expect(Twig_Token::NUMBER_TYPE, 1);
261 $stream->expect(Twig_Token::OPERATOR_TYPE, 'and');
262 }
263
264 /**
265 * @expectedException Twig_Error_Syntax
266 * @expectedExceptionMessage Unclosed "variable" at line 3
267 */
268 public function testUnterminatedVariable()
269 {
270 $template = '
271
272{{
273
274bar
275
276
277';
278
279 $lexer = new Twig_Lexer(new Twig_Environment());
280 $stream = $lexer->tokenize($template);
281 }
282
283 /**
284 * @expectedException Twig_Error_Syntax
285 * @expectedExceptionMessage Unclosed "block" at line 3
286 */
287 public function testUnterminatedBlock()
288 {
289 $template = '
290
291{%
292
293bar
294
295
296';
297
298 $lexer = new Twig_Lexer(new Twig_Environment());
299 $stream = $lexer->tokenize($template);
300 }
301}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php b/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php
new file mode 100644
index 00000000..1369a6bd
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/ArrayTest.php
@@ -0,0 +1,97 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Loader_ArrayTest extends PHPUnit_Framework_TestCase
13{
14 public function testGetSource()
15 {
16 $loader = new Twig_Loader_Array(array('foo' => 'bar'));
17
18 $this->assertEquals('bar', $loader->getSource('foo'));
19 }
20
21 /**
22 * @expectedException Twig_Error_Loader
23 */
24 public function testGetSourceWhenTemplateDoesNotExist()
25 {
26 $loader = new Twig_Loader_Array(array());
27
28 $loader->getSource('foo');
29 }
30
31 public function testGetCacheKey()
32 {
33 $loader = new Twig_Loader_Array(array('foo' => 'bar'));
34
35 $this->assertEquals('bar', $loader->getCacheKey('foo'));
36 }
37
38 /**
39 * @expectedException Twig_Error_Loader
40 */
41 public function testGetCacheKeyWhenTemplateDoesNotExist()
42 {
43 $loader = new Twig_Loader_Array(array());
44
45 $loader->getCacheKey('foo');
46 }
47
48 public function testSetTemplate()
49 {
50 $loader = new Twig_Loader_Array(array());
51 $loader->setTemplate('foo', 'bar');
52
53 $this->assertEquals('bar', $loader->getSource('foo'));
54 }
55
56 public function testIsFresh()
57 {
58 $loader = new Twig_Loader_Array(array('foo' => 'bar'));
59 $this->assertTrue($loader->isFresh('foo', time()));
60 }
61
62 /**
63 * @expectedException Twig_Error_Loader
64 */
65 public function testIsFreshWhenTemplateDoesNotExist()
66 {
67 $loader = new Twig_Loader_Array(array());
68
69 $loader->isFresh('foo', time());
70 }
71
72 public function testTemplateReference()
73 {
74 $name = new Twig_Test_Loader_TemplateReference('foo');
75 $loader = new Twig_Loader_Array(array('foo' => 'bar'));
76
77 $loader->getCacheKey($name);
78 $loader->getSource($name);
79 $loader->isFresh($name, time());
80 $loader->setTemplate($name, 'foobar');
81 }
82}
83
84class Twig_Test_Loader_TemplateReference
85{
86 private $name;
87
88 public function __construct($name)
89 {
90 $this->name = $name;
91 }
92
93 public function __toString()
94 {
95 return $this->name;
96 }
97}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php b/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php
new file mode 100644
index 00000000..4fe0db94
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/ChainTest.php
@@ -0,0 +1,79 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Loader_ChainTest extends PHPUnit_Framework_TestCase
13{
14 public function testGetSource()
15 {
16 $loader = new Twig_Loader_Chain(array(
17 new Twig_Loader_Array(array('foo' => 'bar')),
18 new Twig_Loader_Array(array('foo' => 'foobar', 'bar' => 'foo')),
19 ));
20
21 $this->assertEquals('bar', $loader->getSource('foo'));
22 $this->assertEquals('foo', $loader->getSource('bar'));
23 }
24
25 /**
26 * @expectedException Twig_Error_Loader
27 */
28 public function testGetSourceWhenTemplateDoesNotExist()
29 {
30 $loader = new Twig_Loader_Chain(array());
31
32 $loader->getSource('foo');
33 }
34
35 public function testGetCacheKey()
36 {
37 $loader = new Twig_Loader_Chain(array(
38 new Twig_Loader_Array(array('foo' => 'bar')),
39 new Twig_Loader_Array(array('foo' => 'foobar', 'bar' => 'foo')),
40 ));
41
42 $this->assertEquals('bar', $loader->getCacheKey('foo'));
43 $this->assertEquals('foo', $loader->getCacheKey('bar'));
44 }
45
46 /**
47 * @expectedException Twig_Error_Loader
48 */
49 public function testGetCacheKeyWhenTemplateDoesNotExist()
50 {
51 $loader = new Twig_Loader_Chain(array());
52
53 $loader->getCacheKey('foo');
54 }
55
56 public function testAddLoader()
57 {
58 $loader = new Twig_Loader_Chain();
59 $loader->addLoader(new Twig_Loader_Array(array('foo' => 'bar')));
60
61 $this->assertEquals('bar', $loader->getSource('foo'));
62 }
63
64 public function testExists()
65 {
66 $loader1 = $this->getMock('Twig_Loader_Array', array('exists', 'getSource'), array(), '', false);
67 $loader1->expects($this->once())->method('exists')->will($this->returnValue(false));
68 $loader1->expects($this->never())->method('getSource');
69
70 $loader2 = $this->getMock('Twig_LoaderInterface');
71 $loader2->expects($this->once())->method('getSource')->will($this->returnValue('content'));
72
73 $loader = new Twig_Loader_Chain();
74 $loader->addLoader($loader1);
75 $loader->addLoader($loader2);
76
77 $this->assertTrue($loader->exists('foo'));
78 }
79}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php b/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php
new file mode 100644
index 00000000..4c874b6b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/FilesystemTest.php
@@ -0,0 +1,97 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Loader_FilesystemTest extends PHPUnit_Framework_TestCase
13{
14 /**
15 * @dataProvider getSecurityTests
16 */
17 public function testSecurity($template)
18 {
19 $loader = new Twig_Loader_Filesystem(array(dirname(__FILE__).'/../Fixtures'));
20
21 try {
22 $loader->getCacheKey($template);
23 $this->fail();
24 } catch (Twig_Error_Loader $e) {
25 $this->assertNotContains('Unable to find template', $e->getMessage());
26 }
27 }
28
29 public function getSecurityTests()
30 {
31 return array(
32 array("AutoloaderTest\0.php"),
33 array('..\\AutoloaderTest.php'),
34 array('..\\\\\\AutoloaderTest.php'),
35 array('../AutoloaderTest.php'),
36 array('..////AutoloaderTest.php'),
37 array('./../AutoloaderTest.php'),
38 array('.\\..\\AutoloaderTest.php'),
39 array('././././././../AutoloaderTest.php'),
40 array('.\\./.\\./.\\./../AutoloaderTest.php'),
41 array('foo/../../AutoloaderTest.php'),
42 array('foo\\..\\..\\AutoloaderTest.php'),
43 array('foo/../bar/../../AutoloaderTest.php'),
44 array('foo/bar/../../../AutoloaderTest.php'),
45 array('filters/../../AutoloaderTest.php'),
46 array('filters//..//..//AutoloaderTest.php'),
47 array('filters\\..\\..\\AutoloaderTest.php'),
48 array('filters\\\\..\\\\..\\\\AutoloaderTest.php'),
49 array('filters\\//../\\/\\..\\AutoloaderTest.php'),
50 array('/../AutoloaderTest.php'),
51 );
52 }
53
54 public function testPaths()
55 {
56 $basePath = dirname(__FILE__).'/Fixtures';
57
58 $loader = new Twig_Loader_Filesystem(array($basePath.'/normal', $basePath.'/normal_bis'));
59 $loader->setPaths(array($basePath.'/named', $basePath.'/named_bis'), 'named');
60 $loader->addPath($basePath.'/named_ter', 'named');
61 $loader->addPath($basePath.'/normal_ter');
62 $loader->prependPath($basePath.'/normal_final');
63 $loader->prependPath($basePath.'/named_final', 'named');
64
65 $this->assertEquals(array(
66 $basePath.'/normal_final',
67 $basePath.'/normal',
68 $basePath.'/normal_bis',
69 $basePath.'/normal_ter',
70 ), $loader->getPaths());
71 $this->assertEquals(array(
72 $basePath.'/named_final',
73 $basePath.'/named',
74 $basePath.'/named_bis',
75 $basePath.'/named_ter',
76 ), $loader->getPaths('named'));
77
78 $this->assertEquals("path (final)\n", $loader->getSource('index.html'));
79 $this->assertEquals("path (final)\n", $loader->getSource('@__main__/index.html'));
80 $this->assertEquals("named path (final)\n", $loader->getSource('@named/index.html'));
81 }
82
83 public function testEmptyConstructor()
84 {
85 $loader = new Twig_Loader_Filesystem();
86 $this->assertEquals(array(), $loader->getPaths());
87 }
88
89 public function testGetNamespaces()
90 {
91 $loader = new Twig_Loader_Filesystem(sys_get_temp_dir());
92 $this->assertEquals(array(Twig_Loader_Filesystem::MAIN_NAMESPACE), $loader->getNamespaces());
93
94 $loader->addPath(sys_get_temp_dir(), 'named');
95 $this->assertEquals(array(Twig_Loader_Filesystem::MAIN_NAMESPACE, 'named'), $loader->getNamespaces());
96 }
97}
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html
new file mode 100644
index 00000000..9e5449c7
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named/index.html
@@ -0,0 +1 @@
named path
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html
new file mode 100644
index 00000000..d3a272b1
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_bis/index.html
@@ -0,0 +1 @@
named path (bis)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html
new file mode 100644
index 00000000..9f05d150
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_final/index.html
@@ -0,0 +1 @@
named path (final)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html
new file mode 100644
index 00000000..24fb68ad
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/named_ter/index.html
@@ -0,0 +1 @@
named path (ter)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html
new file mode 100644
index 00000000..e7a8fd4d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal/index.html
@@ -0,0 +1 @@
path
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html
new file mode 100644
index 00000000..bfa91604
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_bis/index.html
@@ -0,0 +1 @@
path (bis)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html
new file mode 100644
index 00000000..73a089bb
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_final/index.html
@@ -0,0 +1 @@
path (final)
diff --git a/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html
new file mode 100644
index 00000000..b7ad97d8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Loader/Fixtures/normal_ter/index.html
@@ -0,0 +1 @@
path (ter)
diff --git a/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php b/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php
new file mode 100644
index 00000000..3fafd335
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/NativeExtensionTest.php
@@ -0,0 +1,29 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_NativeExtensionTest extends PHPUnit_Framework_TestCase
13{
14 public function testGetProperties()
15 {
16 $twig = new Twig_Environment(new Twig_Loader_String(), array(
17 'debug' => true,
18 'cache' => false,
19 'autoescape' => false
20 ));
21
22 $d1 = new DateTime();
23 $d2 = new DateTime();
24 $output = $twig->render('{{ d1.date }}{{ d2.date }}', compact('d1', 'd2'));
25
26 // If it fails, PHP will crash.
27 $this->assertEquals($output, $d1->date . $d2->date);
28 }
29}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php b/vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php
new file mode 100644
index 00000000..608446bc
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/AutoEscapeTest.php
@@ -0,0 +1,44 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_AutoEscapeTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_AutoEscape::__construct
16 */
17 public function testConstructor()
18 {
19 $body = new Twig_Node(array(new Twig_Node_Text('foo', 1)));
20 $node = new Twig_Node_AutoEscape(true, $body, 1);
21
22 $this->assertEquals($body, $node->getNode('body'));
23 $this->assertEquals(true, $node->getAttribute('value'));
24 }
25
26 /**
27 * @covers Twig_Node_AutoEscape::compile
28 * @dataProvider getTests
29 */
30 public function testCompile($node, $source, $environment = null)
31 {
32 parent::testCompile($node, $source, $environment);
33 }
34
35 public function getTests()
36 {
37 $body = new Twig_Node(array(new Twig_Node_Text('foo', 1)));
38 $node = new Twig_Node_AutoEscape(true, $body, 1);
39
40 return array(
41 array($node, "// line 1\necho \"foo\";"),
42 );
43 }
44}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php b/vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php
new file mode 100644
index 00000000..96d0e101
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/BlockReferenceTest.php
@@ -0,0 +1,43 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_BlockReferenceTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_BlockReference::__construct
16 */
17 public function testConstructor()
18 {
19 $node = new Twig_Node_BlockReference('foo', 1);
20
21 $this->assertEquals('foo', $node->getAttribute('name'));
22 }
23
24 /**
25 * @covers Twig_Node_BlockReference::compile
26 * @dataProvider getTests
27 */
28 public function testCompile($node, $source, $environment = null)
29 {
30 parent::testCompile($node, $source, $environment);
31 }
32
33 public function getTests()
34 {
35 return array(
36 array(new Twig_Node_BlockReference('foo', 1), <<<EOF
37// line 1
38\$this->displayBlock('foo', \$context, \$blocks);
39EOF
40 ),
41 );
42 }
43}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php b/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php
new file mode 100644
index 00000000..024049de
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/BlockTest.php
@@ -0,0 +1,51 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_BlockTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Block::__construct
16 */
17 public function testConstructor()
18 {
19 $body = new Twig_Node_Text('foo', 1);
20 $node = new Twig_Node_Block('foo', $body, 1);
21
22 $this->assertEquals($body, $node->getNode('body'));
23 $this->assertEquals('foo', $node->getAttribute('name'));
24 }
25
26 /**
27 * @covers Twig_Node_Block::compile
28 * @dataProvider getTests
29 */
30 public function testCompile($node, $source, $environment = null)
31 {
32 parent::testCompile($node, $source, $environment);
33 }
34
35 public function getTests()
36 {
37 $body = new Twig_Node_Text('foo', 1);
38 $node = new Twig_Node_Block('foo', $body, 1);
39
40 return array(
41 array($node, <<<EOF
42// line 1
43public function block_foo(\$context, array \$blocks = array())
44{
45 echo "foo";
46}
47EOF
48 ),
49 );
50 }
51}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/DoTest.php b/vendor/twig/twig/test/Twig/Tests/Node/DoTest.php
new file mode 100644
index 00000000..a406e22d
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/DoTest.php
@@ -0,0 +1,44 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_DoTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Do::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant('foo', 1);
20 $node = new Twig_Node_Do($expr, 1);
21
22 $this->assertEquals($expr, $node->getNode('expr'));
23 }
24
25 /**
26 * @covers Twig_Node_Do::compile
27 * @dataProvider getTests
28 */
29 public function testCompile($node, $source, $environment = null)
30 {
31 parent::testCompile($node, $source, $environment);
32 }
33
34 public function getTests()
35 {
36 $tests = array();
37
38 $expr = new Twig_Node_Expression_Constant('foo', 1);
39 $node = new Twig_Node_Do($expr, 1);
40 $tests[] = array($node, "// line 1\n\"foo\";");
41
42 return $tests;
43 }
44}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php
new file mode 100644
index 00000000..c6a9044b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ArrayTest.php
@@ -0,0 +1,49 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_ArrayTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Array::__construct
16 */
17 public function testConstructor()
18 {
19 $elements = array(new Twig_Node_Expression_Constant('foo', 1), $foo = new Twig_Node_Expression_Constant('bar', 1));
20 $node = new Twig_Node_Expression_Array($elements, 1);
21
22 $this->assertEquals($foo, $node->getNode(1));
23 }
24
25 /**
26 * @covers Twig_Node_Expression_Array::compile
27 * @dataProvider getTests
28 */
29 public function testCompile($node, $source, $environment = null)
30 {
31 parent::testCompile($node, $source, $environment);
32 }
33
34 public function getTests()
35 {
36 $elements = array(
37 new Twig_Node_Expression_Constant('foo', 1),
38 new Twig_Node_Expression_Constant('bar', 1),
39
40 new Twig_Node_Expression_Constant('bar', 1),
41 new Twig_Node_Expression_Constant('foo', 1),
42 );
43 $node = new Twig_Node_Expression_Array($elements, 1);
44
45 return array(
46 array($node, 'array("foo" => "bar", "bar" => "foo")'),
47 );
48 }
49}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php
new file mode 100644
index 00000000..b156dcc0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/AssignNameTest.php
@@ -0,0 +1,41 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_AssignNameTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_AssignName::__construct
16 */
17 public function testConstructor()
18 {
19 $node = new Twig_Node_Expression_AssignName('foo', 1);
20
21 $this->assertEquals('foo', $node->getAttribute('name'));
22 }
23
24 /**
25 * @covers Twig_Node_Expression_AssignName::compile
26 * @dataProvider getTests
27 */
28 public function testCompile($node, $source, $environment = null)
29 {
30 parent::testCompile($node, $source, $environment);
31 }
32
33 public function getTests()
34 {
35 $node = new Twig_Node_Expression_AssignName('foo', 1);
36
37 return array(
38 array($node, '$context["foo"]'),
39 );
40 }
41}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php
new file mode 100644
index 00000000..a0f49cb3
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AddTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_AddTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_Add::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_Add($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_Add::compile
29 * @covers Twig_Node_Expression_Binary_Add::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_Add($left, $right, 1);
42
43 return array(
44 array($node, '(1 + 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php
new file mode 100644
index 00000000..50e551a7
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/AndTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_AndTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_And::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_And($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_And::compile
29 * @covers Twig_Node_Expression_Binary_And::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_And($left, $right, 1);
42
43 return array(
44 array($node, '(1 && 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php
new file mode 100644
index 00000000..140329fa
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ConcatTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_ConcatTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_Concat::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_Concat($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_Concat::compile
29 * @covers Twig_Node_Expression_Binary_Concat::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_Concat($left, $right, 1);
42
43 return array(
44 array($node, '(1 . 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php
new file mode 100644
index 00000000..0c1a3c7f
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/DivTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_DivTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_Div::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_Div($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_Div::compile
29 * @covers Twig_Node_Expression_Binary_Div::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_Div($left, $right, 1);
42
43 return array(
44 array($node, '(1 / 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php
new file mode 100644
index 00000000..ead1fde8
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/FloorDivTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_FloorDivTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_FloorDiv::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_FloorDiv($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_FloorDiv::compile
29 * @covers Twig_Node_Expression_Binary_FloorDiv::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_FloorDiv($left, $right, 1);
42
43 return array(
44 array($node, 'intval(floor((1 / 2)))'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php
new file mode 100644
index 00000000..4fe1a1fc
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/ModTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_ModTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_Mod::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_Mod($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_Mod::compile
29 * @covers Twig_Node_Expression_Binary_Mod::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_Mod($left, $right, 1);
42
43 return array(
44 array($node, '(1 % 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php
new file mode 100644
index 00000000..12bb35c9
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/MulTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_MulTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_Mul::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_Mul($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_Mul::compile
29 * @covers Twig_Node_Expression_Binary_Mul::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_Mul($left, $right, 1);
42
43 return array(
44 array($node, '(1 * 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php
new file mode 100644
index 00000000..9534c41c
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/OrTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_OrTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_Or::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_Or($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_Or::compile
29 * @covers Twig_Node_Expression_Binary_Or::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_Or($left, $right, 1);
42
43 return array(
44 array($node, '(1 || 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php
new file mode 100644
index 00000000..9074893b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Binary/SubTest.php
@@ -0,0 +1,47 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Binary_SubTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Binary_Sub::__construct
16 */
17 public function testConstructor()
18 {
19 $left = new Twig_Node_Expression_Constant(1, 1);
20 $right = new Twig_Node_Expression_Constant(2, 1);
21 $node = new Twig_Node_Expression_Binary_Sub($left, $right, 1);
22
23 $this->assertEquals($left, $node->getNode('left'));
24 $this->assertEquals($right, $node->getNode('right'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Binary_Sub::compile
29 * @covers Twig_Node_Expression_Binary_Sub::operator
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $left = new Twig_Node_Expression_Constant(1, 1);
40 $right = new Twig_Node_Expression_Constant(2, 1);
41 $node = new Twig_Node_Expression_Binary_Sub($left, $right, 1);
42
43 return array(
44 array($node, '(1 - 2)'),
45 );
46 }
47}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php
new file mode 100644
index 00000000..53b5e6ee
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/CallTest.php
@@ -0,0 +1,67 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_CallTest extends PHPUnit_Framework_TestCase
13{
14 public function testGetArguments()
15 {
16 $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
17 $this->assertEquals(array('U'), $node->getArguments('date', array('format' => 'U')));
18 }
19
20 /**
21 * @expectedException Twig_Error_Syntax
22 * @expectedExceptionMessage Positional arguments cannot be used after named arguments for function "date".
23 */
24 public function testGetArgumentsWhenPositionalArgumentsAfterNamedArguments()
25 {
26 $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
27 $node->getArguments('date', array('timestamp' => 123456, 'Y-m-d'));
28 }
29
30 /**
31 * @expectedException Twig_Error_Syntax
32 * @expectedExceptionMessage Argument "format" is defined twice for function "date".
33 */
34 public function testGetArgumentsWhenArgumentIsDefinedTwice()
35 {
36 $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
37 $node->getArguments('date', array('Y-m-d', 'format' => 'U'));
38 }
39
40 /**
41 * @expectedException Twig_Error_Syntax
42 * @expectedExceptionMessage Unknown argument "unknown" for function "date".
43 */
44 public function testGetArgumentsWithWrongNamedArgumentName()
45 {
46 $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
47 $node->getArguments('date', array('Y-m-d', 'unknown' => ''));
48 }
49
50 /**
51 * @expectedException Twig_Error_Syntax
52 * @expectedExceptionMessage Unknown arguments "unknown1", "unknown2" for function "date".
53 */
54 public function testGetArgumentsWithWrongNamedArgumentNames()
55 {
56 $node = new Twig_Tests_Node_Expression_Call(array(), array('type' => 'function', 'name' => 'date'));
57 $node->getArguments('date', array('Y-m-d', 'unknown1' => '', 'unknown2' => ''));
58 }
59}
60
61class Twig_Tests_Node_Expression_Call extends Twig_Node_Expression_Call
62{
63 public function getArguments($callable, $arguments)
64 {
65 return parent::getArguments($callable, $arguments);
66 }
67}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php
new file mode 100644
index 00000000..9906d512
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConditionalTest.php
@@ -0,0 +1,50 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_ConditionalTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Conditional::__construct
16 */
17 public function testConstructor()
18 {
19 $expr1 = new Twig_Node_Expression_Constant(1, 1);
20 $expr2 = new Twig_Node_Expression_Constant(2, 1);
21 $expr3 = new Twig_Node_Expression_Constant(3, 1);
22 $node = new Twig_Node_Expression_Conditional($expr1, $expr2, $expr3, 1);
23
24 $this->assertEquals($expr1, $node->getNode('expr1'));
25 $this->assertEquals($expr2, $node->getNode('expr2'));
26 $this->assertEquals($expr3, $node->getNode('expr3'));
27 }
28
29 /**
30 * @covers Twig_Node_Expression_Conditional::compile
31 * @dataProvider getTests
32 */
33 public function testCompile($node, $source, $environment = null)
34 {
35 parent::testCompile($node, $source, $environment);
36 }
37
38 public function getTests()
39 {
40 $tests = array();
41
42 $expr1 = new Twig_Node_Expression_Constant(1, 1);
43 $expr2 = new Twig_Node_Expression_Constant(2, 1);
44 $expr3 = new Twig_Node_Expression_Constant(3, 1);
45 $node = new Twig_Node_Expression_Conditional($expr1, $expr2, $expr3, 1);
46 $tests[] = array($node, '((1) ? (2) : (3))');
47
48 return $tests;
49 }
50}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php
new file mode 100644
index 00000000..d0dec531
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ConstantTest.php
@@ -0,0 +1,42 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_ConstantTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Constant::__construct
16 */
17 public function testConstructor()
18 {
19 $node = new Twig_Node_Expression_Constant('foo', 1);
20
21 $this->assertEquals('foo', $node->getAttribute('value'));
22 }
23
24 /**
25 * @covers Twig_Node_Expression_Constant::compile
26 * @dataProvider getTests
27 */
28 public function testCompile($node, $source, $environment = null)
29 {
30 parent::testCompile($node, $source, $environment);
31 }
32
33 public function getTests()
34 {
35 $tests = array();
36
37 $node = new Twig_Node_Expression_Constant('foo', 1);
38 $tests[] = array($node, '"foo"');
39
40 return $tests;
41 }
42}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php
new file mode 100644
index 00000000..8089b9cb
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/FilterTest.php
@@ -0,0 +1,133 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_FilterTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Filter::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant('foo', 1);
20 $name = new Twig_Node_Expression_Constant('upper', 1);
21 $args = new Twig_Node();
22 $node = new Twig_Node_Expression_Filter($expr, $name, $args, 1);
23
24 $this->assertEquals($expr, $node->getNode('node'));
25 $this->assertEquals($name, $node->getNode('filter'));
26 $this->assertEquals($args, $node->getNode('arguments'));
27 }
28
29 /**
30 * @covers Twig_Node_Expression_Filter::compile
31 * @dataProvider getTests
32 */
33 public function testCompile($node, $source, $environment = null)
34 {
35 parent::testCompile($node, $source, $environment);
36 }
37
38 public function getTests()
39 {
40 $tests = array();
41
42 $expr = new Twig_Node_Expression_Constant('foo', 1);
43 $node = $this->createFilter($expr, 'upper');
44 $node = $this->createFilter($node, 'number_format', array(new Twig_Node_Expression_Constant(2, 1), new Twig_Node_Expression_Constant('.', 1), new Twig_Node_Expression_Constant(',', 1)));
45
46 if (function_exists('mb_get_info')) {
47 $tests[] = array($node, 'twig_number_format_filter($this->env, twig_upper_filter($this->env, "foo"), 2, ".", ",")');
48 } else {
49 $tests[] = array($node, 'twig_number_format_filter($this->env, strtoupper("foo"), 2, ".", ",")');
50 }
51
52 // named arguments
53 $date = new Twig_Node_Expression_Constant(0, 1);
54 $node = $this->createFilter($date, 'date', array(
55 'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
56 'format' => new Twig_Node_Expression_Constant('d/m/Y H:i:s P', 1),
57 ));
58 $tests[] = array($node, 'twig_date_format_filter($this->env, 0, "d/m/Y H:i:s P", "America/Chicago")');
59
60 // skip an optional argument
61 $date = new Twig_Node_Expression_Constant(0, 1);
62 $node = $this->createFilter($date, 'date', array(
63 'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
64 ));
65 $tests[] = array($node, 'twig_date_format_filter($this->env, 0, null, "America/Chicago")');
66
67 // underscores vs camelCase for named arguments
68 $string = new Twig_Node_Expression_Constant('abc', 1);
69 $node = $this->createFilter($string, 'reverse', array(
70 'preserve_keys' => new Twig_Node_Expression_Constant(true, 1),
71 ));
72 $tests[] = array($node, 'twig_reverse_filter($this->env, "abc", true)');
73 $node = $this->createFilter($string, 'reverse', array(
74 'preserveKeys' => new Twig_Node_Expression_Constant(true, 1),
75 ));
76 $tests[] = array($node, 'twig_reverse_filter($this->env, "abc", true)');
77
78 // filter as an anonymous function
79 if (version_compare(phpversion(), '5.3.0', '>=')) {
80 $node = $this->createFilter(new Twig_Node_Expression_Constant('foo', 1), 'anonymous');
81 $tests[] = array($node, 'call_user_func_array($this->env->getFilter(\'anonymous\')->getCallable(), array("foo"))');
82 }
83
84 return $tests;
85 }
86
87 /**
88 * @expectedException Twig_Error_Syntax
89 * @expectedExceptionMessage Unknown argument "foobar" for filter "date".
90 */
91 public function testCompileWithWrongNamedArgumentName()
92 {
93 $date = new Twig_Node_Expression_Constant(0, 1);
94 $node = $this->createFilter($date, 'date', array(
95 'foobar' => new Twig_Node_Expression_Constant('America/Chicago', 1),
96 ));
97
98 $compiler = $this->getCompiler();
99 $compiler->compile($node);
100 }
101
102 /**
103 * @expectedException Twig_Error_Syntax
104 * @expectedExceptionMessage Value for argument "from" is required for filter "replace".
105 */
106 public function testCompileWithMissingNamedArgument()
107 {
108 $value = new Twig_Node_Expression_Constant(0, 1);
109 $node = $this->createFilter($value, 'replace', array(
110 'to' => new Twig_Node_Expression_Constant('foo', 1),
111 ));
112
113 $compiler = $this->getCompiler();
114 $compiler->compile($node);
115 }
116
117 protected function createFilter($node, $name, array $arguments = array())
118 {
119 $name = new Twig_Node_Expression_Constant($name, 1);
120 $arguments = new Twig_Node($arguments);
121
122 return new Twig_Node_Expression_Filter($node, $name, $arguments, 1);
123 }
124
125 protected function getEnvironment()
126 {
127 if (version_compare(phpversion(), '5.3.0', '>=')) {
128 return include 'PHP53/FilterInclude.php';
129 }
130
131 return parent::getEnvironment();
132 }
133}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php
new file mode 100644
index 00000000..431dc387
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/FunctionTest.php
@@ -0,0 +1,99 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_FunctionTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Function::__construct
16 */
17 public function testConstructor()
18 {
19 $name = 'function';
20 $args = new Twig_Node();
21 $node = new Twig_Node_Expression_Function($name, $args, 1);
22
23 $this->assertEquals($name, $node->getAttribute('name'));
24 $this->assertEquals($args, $node->getNode('arguments'));
25 }
26
27 /**
28 * @covers Twig_Node_Expression_Function::compile
29 * @dataProvider getTests
30 */
31 public function testCompile($node, $source, $environment = null)
32 {
33 parent::testCompile($node, $source, $environment);
34 }
35
36 public function getTests()
37 {
38 $environment = new Twig_Environment();
39 $environment->addFunction('foo', new Twig_Function_Function('foo', array()));
40 $environment->addFunction('bar', new Twig_Function_Function('bar', array('needs_environment' => true)));
41 $environment->addFunction('foofoo', new Twig_Function_Function('foofoo', array('needs_context' => true)));
42 $environment->addFunction('foobar', new Twig_Function_Function('foobar', array('needs_environment' => true, 'needs_context' => true)));
43
44 $tests = array();
45
46 $node = $this->createFunction('foo');
47 $tests[] = array($node, 'foo()', $environment);
48
49 $node = $this->createFunction('foo', array(new Twig_Node_Expression_Constant('bar', 1), new Twig_Node_Expression_Constant('foobar', 1)));
50 $tests[] = array($node, 'foo("bar", "foobar")', $environment);
51
52 $node = $this->createFunction('bar');
53 $tests[] = array($node, 'bar($this->env)', $environment);
54
55 $node = $this->createFunction('bar', array(new Twig_Node_Expression_Constant('bar', 1)));
56 $tests[] = array($node, 'bar($this->env, "bar")', $environment);
57
58 $node = $this->createFunction('foofoo');
59 $tests[] = array($node, 'foofoo($context)', $environment);
60
61 $node = $this->createFunction('foofoo', array(new Twig_Node_Expression_Constant('bar', 1)));
62 $tests[] = array($node, 'foofoo($context, "bar")', $environment);
63
64 $node = $this->createFunction('foobar');
65 $tests[] = array($node, 'foobar($this->env, $context)', $environment);
66
67 $node = $this->createFunction('foobar', array(new Twig_Node_Expression_Constant('bar', 1)));
68 $tests[] = array($node, 'foobar($this->env, $context, "bar")', $environment);
69
70 // named arguments
71 $node = $this->createFunction('date', array(
72 'timezone' => new Twig_Node_Expression_Constant('America/Chicago', 1),
73 'date' => new Twig_Node_Expression_Constant(0, 1),
74 ));
75 $tests[] = array($node, 'twig_date_converter($this->env, 0, "America/Chicago")');
76
77 // function as an anonymous function
78 if (version_compare(phpversion(), '5.3.0', '>=')) {
79 $node = $this->createFunction('anonymous', array(new Twig_Node_Expression_Constant('foo', 1)));
80 $tests[] = array($node, 'call_user_func_array($this->env->getFunction(\'anonymous\')->getCallable(), array("foo"))');
81 }
82
83 return $tests;
84 }
85
86 protected function createFunction($name, array $arguments = array())
87 {
88 return new Twig_Node_Expression_Function($name, new Twig_Node($arguments), 1);
89 }
90
91 protected function getEnvironment()
92 {
93 if (version_compare(phpversion(), '5.3.0', '>=')) {
94 return include 'PHP53/FunctionInclude.php';
95 }
96
97 return parent::getEnvironment();
98 }
99}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php
new file mode 100644
index 00000000..6a63cce6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/GetAttrTest.php
@@ -0,0 +1,62 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_GetAttrTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_GetAttr::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Name('foo', 1);
20 $attr = new Twig_Node_Expression_Constant('bar', 1);
21 $args = new Twig_Node_Expression_Array(array(), 1);
22 $args->addElement(new Twig_Node_Expression_Name('foo', 1));
23 $args->addElement(new Twig_Node_Expression_Constant('bar', 1));
24 $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ARRAY_CALL, 1);
25
26 $this->assertEquals($expr, $node->getNode('node'));
27 $this->assertEquals($attr, $node->getNode('attribute'));
28 $this->assertEquals($args, $node->getNode('arguments'));
29 $this->assertEquals(Twig_TemplateInterface::ARRAY_CALL, $node->getAttribute('type'));
30 }
31
32 /**
33 * @covers Twig_Node_Expression_GetAttr::compile
34 * @dataProvider getTests
35 */
36 public function testCompile($node, $source, $environment = null)
37 {
38 parent::testCompile($node, $source, $environment);
39 }
40
41 public function getTests()
42 {
43 $tests = array();
44
45 $expr = new Twig_Node_Expression_Name('foo', 1);
46 $attr = new Twig_Node_Expression_Constant('bar', 1);
47 $args = new Twig_Node_Expression_Array(array(), 1);
48 $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ANY_CALL, 1);
49 $tests[] = array($node, sprintf('%s%s, "bar")', $this->getAttributeGetter(), $this->getVariableGetter('foo')));
50
51 $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::ARRAY_CALL, 1);
52 $tests[] = array($node, sprintf('%s%s, "bar", array(), "array")', $this->getAttributeGetter(), $this->getVariableGetter('foo')));
53
54 $args = new Twig_Node_Expression_Array(array(), 1);
55 $args->addElement(new Twig_Node_Expression_Name('foo', 1));
56 $args->addElement(new Twig_Node_Expression_Constant('bar', 1));
57 $node = new Twig_Node_Expression_GetAttr($expr, $attr, $args, Twig_TemplateInterface::METHOD_CALL, 1);
58 $tests[] = array($node, sprintf('%s%s, "bar", array(0 => %s, 1 => "bar"), "method")', $this->getAttributeGetter(), $this->getVariableGetter('foo'), $this->getVariableGetter('foo')));
59
60 return $tests;
61 }
62}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php
new file mode 100644
index 00000000..76d109b6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/NameTest.php
@@ -0,0 +1,49 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_NameTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Name::__construct
16 */
17 public function testConstructor()
18 {
19 $node = new Twig_Node_Expression_Name('foo', 1);
20
21 $this->assertEquals('foo', $node->getAttribute('name'));
22 }
23
24 /**
25 * @covers Twig_Node_Expression_Name::compile
26 * @dataProvider getTests
27 */
28 public function testCompile($node, $source, $environment = null)
29 {
30 parent::testCompile($node, $source, $environment);
31 }
32
33 public function getTests()
34 {
35 $node = new Twig_Node_Expression_Name('foo', 1);
36 $self = new Twig_Node_Expression_Name('_self', 1);
37 $context = new Twig_Node_Expression_Name('_context', 1);
38
39 $env = new Twig_Environment(null, array('strict_variables' => true));
40 $env1 = new Twig_Environment(null, array('strict_variables' => false));
41
42 return array(
43 version_compare(PHP_VERSION, '5.4.0') >= 0 ? array($node, '(isset($context["foo"]) ? $context["foo"] : $this->getContext($context, "foo"))', $env) : array($node, '$this->getContext($context, "foo")', $env),
44 array($node, $this->getVariableGetter('foo'), $env1),
45 array($self, '$this'),
46 array($context, '$context'),
47 );
48 }
49}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php
new file mode 100644
index 00000000..15e3aa96
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FilterInclude.php
@@ -0,0 +1,6 @@
1<?php
2
3$env = new Twig_Environment();
4$env->addFilter(new Twig_SimpleFilter('anonymous', function () {}));
5
6return $env;
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php
new file mode 100644
index 00000000..d2170ed2
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/FunctionInclude.php
@@ -0,0 +1,6 @@
1<?php
2
3$env = new Twig_Environment();
4$env->addFunction(new Twig_SimpleFunction('anonymous', function () {}));
5
6return $env;
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php
new file mode 100644
index 00000000..63662864
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/PHP53/TestInclude.php
@@ -0,0 +1,6 @@
1<?php
2
3$env = new Twig_Environment();
4$env->addTest(new Twig_SimpleTest('anonymous', function () {}));
5
6return $env;
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php
new file mode 100644
index 00000000..4d40419b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/ParentTest.php
@@ -0,0 +1,40 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_ParentTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Parent::__construct
16 */
17 public function testConstructor()
18 {
19 $node = new Twig_Node_Expression_Parent('foo', 1);
20
21 $this->assertEquals('foo', $node->getAttribute('name'));
22 }
23
24 /**
25 * @covers Twig_Node_Expression_Parent::compile
26 * @dataProvider getTests
27 */
28 public function testCompile($node, $source, $environment = null)
29 {
30 parent::testCompile($node, $source, $environment);
31 }
32
33 public function getTests()
34 {
35 $tests = array();
36 $tests[] = array(new Twig_Node_Expression_Parent('foo', 1), '$this->renderParentBlock("foo", $context, $blocks)');
37
38 return $tests;
39 }
40}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php
new file mode 100644
index 00000000..0664150a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/TestTest.php
@@ -0,0 +1,68 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_TestTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Test::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant('foo', 1);
20 $name = new Twig_Node_Expression_Constant('null', 1);
21 $args = new Twig_Node();
22 $node = new Twig_Node_Expression_Test($expr, $name, $args, 1);
23
24 $this->assertEquals($expr, $node->getNode('node'));
25 $this->assertEquals($args, $node->getNode('arguments'));
26 $this->assertEquals($name, $node->getAttribute('name'));
27 }
28
29 /**
30 * @covers Twig_Node_Expression_Test::compile
31 * @dataProvider getTests
32 */
33 public function testCompile($node, $source, $environment = null)
34 {
35 parent::testCompile($node, $source, $environment);
36 }
37
38 public function getTests()
39 {
40 $tests = array();
41
42 $expr = new Twig_Node_Expression_Constant('foo', 1);
43 $node = new Twig_Node_Expression_Test_Null($expr, 'null', new Twig_Node(array()), 1);
44 $tests[] = array($node, '(null === "foo")');
45
46 // test as an anonymous function
47 if (version_compare(phpversion(), '5.3.0', '>=')) {
48 $node = $this->createTest(new Twig_Node_Expression_Constant('foo', 1), 'anonymous', array(new Twig_Node_Expression_Constant('foo', 1)));
49 $tests[] = array($node, 'call_user_func_array($this->env->getTest(\'anonymous\')->getCallable(), array("foo", "foo"))');
50 }
51
52 return $tests;
53 }
54
55 protected function createTest($node, $name, array $arguments = array())
56 {
57 return new Twig_Node_Expression_Test($node, $name, new Twig_Node($arguments), 1);
58 }
59
60 protected function getEnvironment()
61 {
62 if (version_compare(phpversion(), '5.3.0', '>=')) {
63 return include 'PHP53/TestInclude.php';
64 }
65
66 return parent::getEnvironment();
67 }
68}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php
new file mode 100644
index 00000000..d55ab333
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NegTest.php
@@ -0,0 +1,44 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Unary_NegTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Unary_Neg::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant(1, 1);
20 $node = new Twig_Node_Expression_Unary_Neg($expr, 1);
21
22 $this->assertEquals($expr, $node->getNode('node'));
23 }
24
25 /**
26 * @covers Twig_Node_Expression_Unary_Neg::compile
27 * @covers Twig_Node_Expression_Unary_Neg::operator
28 * @dataProvider getTests
29 */
30 public function testCompile($node, $source, $environment = null)
31 {
32 parent::testCompile($node, $source, $environment);
33 }
34
35 public function getTests()
36 {
37 $node = new Twig_Node_Expression_Constant(1, 1);
38 $node = new Twig_Node_Expression_Unary_Neg($node, 1);
39
40 return array(
41 array($node, '(-1)'),
42 );
43 }
44}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php
new file mode 100644
index 00000000..625c2527
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/NotTest.php
@@ -0,0 +1,44 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Unary_NotTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Unary_Not::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant(1, 1);
20 $node = new Twig_Node_Expression_Unary_Not($expr, 1);
21
22 $this->assertEquals($expr, $node->getNode('node'));
23 }
24
25 /**
26 * @covers Twig_Node_Expression_Unary_Not::compile
27 * @covers Twig_Node_Expression_Unary_Not::operator
28 * @dataProvider getTests
29 */
30 public function testCompile($node, $source, $environment = null)
31 {
32 parent::testCompile($node, $source, $environment);
33 }
34
35 public function getTests()
36 {
37 $node = new Twig_Node_Expression_Constant(1, 1);
38 $node = new Twig_Node_Expression_Unary_Not($node, 1);
39
40 return array(
41 array($node, '(!1)'),
42 );
43 }
44}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php
new file mode 100644
index 00000000..047a0977
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/Expression/Unary/PosTest.php
@@ -0,0 +1,44 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_Expression_Unary_PosTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Expression_Unary_Pos::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant(1, 1);
20 $node = new Twig_Node_Expression_Unary_Pos($expr, 1);
21
22 $this->assertEquals($expr, $node->getNode('node'));
23 }
24
25 /**
26 * @covers Twig_Node_Expression_Unary_Pos::compile
27 * @covers Twig_Node_Expression_Unary_Pos::operator
28 * @dataProvider getTests
29 */
30 public function testCompile($node, $source, $environment = null)
31 {
32 parent::testCompile($node, $source, $environment);
33 }
34
35 public function getTests()
36 {
37 $node = new Twig_Node_Expression_Constant(1, 1);
38 $node = new Twig_Node_Expression_Unary_Pos($node, 1);
39
40 return array(
41 array($node, '(+1)'),
42 );
43 }
44}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php
new file mode 100644
index 00000000..21cc84e6
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/ForTest.php
@@ -0,0 +1,203 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_ForTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_For::__construct
16 */
17 public function testConstructor()
18 {
19 $keyTarget = new Twig_Node_Expression_AssignName('key', 1);
20 $valueTarget = new Twig_Node_Expression_AssignName('item', 1);
21 $seq = new Twig_Node_Expression_Name('items', 1);
22 $ifexpr = new Twig_Node_Expression_Constant(true, 1);
23 $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
24 $else = null;
25 $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
26 $node->setAttribute('with_loop', false);
27
28 $this->assertEquals($keyTarget, $node->getNode('key_target'));
29 $this->assertEquals($valueTarget, $node->getNode('value_target'));
30 $this->assertEquals($seq, $node->getNode('seq'));
31 $this->assertTrue($node->getAttribute('ifexpr'));
32 $this->assertEquals('Twig_Node_If', get_class($node->getNode('body')));
33 $this->assertEquals($body, $node->getNode('body')->getNode('tests')->getNode(1)->getNode(0));
34 $this->assertEquals(null, $node->getNode('else'));
35
36 $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1);
37 $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
38 $node->setAttribute('with_loop', false);
39 $this->assertEquals($else, $node->getNode('else'));
40 }
41
42 /**
43 * @covers Twig_Node_For::compile
44 * @dataProvider getTests
45 */
46 public function testCompile($node, $source, $environment = null)
47 {
48 parent::testCompile($node, $source, $environment);
49 }
50
51 public function getTests()
52 {
53 $tests = array();
54
55 $keyTarget = new Twig_Node_Expression_AssignName('key', 1);
56 $valueTarget = new Twig_Node_Expression_AssignName('item', 1);
57 $seq = new Twig_Node_Expression_Name('items', 1);
58 $ifexpr = null;
59 $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
60 $else = null;
61 $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
62 $node->setAttribute('with_loop', false);
63
64 $tests[] = array($node, <<<EOF
65// line 1
66\$context['_parent'] = (array) \$context;
67\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('items')});
68foreach (\$context['_seq'] as \$context["key"] => \$context["item"]) {
69 echo {$this->getVariableGetter('foo')};
70}
71\$_parent = \$context['_parent'];
72unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent'], \$context['loop']);
73\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
74EOF
75 );
76
77 $keyTarget = new Twig_Node_Expression_AssignName('k', 1);
78 $valueTarget = new Twig_Node_Expression_AssignName('v', 1);
79 $seq = new Twig_Node_Expression_Name('values', 1);
80 $ifexpr = null;
81 $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
82 $else = null;
83 $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
84 $node->setAttribute('with_loop', true);
85
86 $tests[] = array($node, <<<EOF
87// line 1
88\$context['_parent'] = (array) \$context;
89\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
90\$context['loop'] = array(
91 'parent' => \$context['_parent'],
92 'index0' => 0,
93 'index' => 1,
94 'first' => true,
95);
96if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {
97 \$length = count(\$context['_seq']);
98 \$context['loop']['revindex0'] = \$length - 1;
99 \$context['loop']['revindex'] = \$length;
100 \$context['loop']['length'] = \$length;
101 \$context['loop']['last'] = 1 === \$length;
102}
103foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
104 echo {$this->getVariableGetter('foo')};
105 ++\$context['loop']['index0'];
106 ++\$context['loop']['index'];
107 \$context['loop']['first'] = false;
108 if (isset(\$context['loop']['length'])) {
109 --\$context['loop']['revindex0'];
110 --\$context['loop']['revindex'];
111 \$context['loop']['last'] = 0 === \$context['loop']['revindex0'];
112 }
113}
114\$_parent = \$context['_parent'];
115unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
116\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
117EOF
118 );
119
120 $keyTarget = new Twig_Node_Expression_AssignName('k', 1);
121 $valueTarget = new Twig_Node_Expression_AssignName('v', 1);
122 $seq = new Twig_Node_Expression_Name('values', 1);
123 $ifexpr = new Twig_Node_Expression_Constant(true, 1);
124 $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
125 $else = null;
126 $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
127 $node->setAttribute('with_loop', true);
128
129 $tests[] = array($node, <<<EOF
130// line 1
131\$context['_parent'] = (array) \$context;
132\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
133\$context['loop'] = array(
134 'parent' => \$context['_parent'],
135 'index0' => 0,
136 'index' => 1,
137 'first' => true,
138);
139foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
140 if (true) {
141 echo {$this->getVariableGetter('foo')};
142 ++\$context['loop']['index0'];
143 ++\$context['loop']['index'];
144 \$context['loop']['first'] = false;
145 }
146}
147\$_parent = \$context['_parent'];
148unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
149\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
150EOF
151 );
152
153 $keyTarget = new Twig_Node_Expression_AssignName('k', 1);
154 $valueTarget = new Twig_Node_Expression_AssignName('v', 1);
155 $seq = new Twig_Node_Expression_Name('values', 1);
156 $ifexpr = null;
157 $body = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1)), array(), 1);
158 $else = new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1);
159 $node = new Twig_Node_For($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, 1);
160 $node->setAttribute('with_loop', true);
161
162 $tests[] = array($node, <<<EOF
163// line 1
164\$context['_parent'] = (array) \$context;
165\$context['_seq'] = twig_ensure_traversable({$this->getVariableGetter('values')});
166\$context['_iterated'] = false;
167\$context['loop'] = array(
168 'parent' => \$context['_parent'],
169 'index0' => 0,
170 'index' => 1,
171 'first' => true,
172);
173if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof Countable)) {
174 \$length = count(\$context['_seq']);
175 \$context['loop']['revindex0'] = \$length - 1;
176 \$context['loop']['revindex'] = \$length;
177 \$context['loop']['length'] = \$length;
178 \$context['loop']['last'] = 1 === \$length;
179}
180foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) {
181 echo {$this->getVariableGetter('foo')};
182 \$context['_iterated'] = true;
183 ++\$context['loop']['index0'];
184 ++\$context['loop']['index'];
185 \$context['loop']['first'] = false;
186 if (isset(\$context['loop']['length'])) {
187 --\$context['loop']['revindex0'];
188 --\$context['loop']['revindex'];
189 \$context['loop']['last'] = 0 === \$context['loop']['revindex0'];
190 }
191}
192if (!\$context['_iterated']) {
193 echo {$this->getVariableGetter('foo')};
194}
195\$_parent = \$context['_parent'];
196unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']);
197\$context = array_intersect_key(\$context, \$_parent) + \$_parent;
198EOF
199 );
200
201 return $tests;
202 }
203}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php b/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php
new file mode 100644
index 00000000..92fc29dc
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/IfTest.php
@@ -0,0 +1,100 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_IfTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_If::__construct
16 */
17 public function testConstructor()
18 {
19 $t = new Twig_Node(array(
20 new Twig_Node_Expression_Constant(true, 1),
21 new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
22 ), array(), 1);
23 $else = null;
24 $node = new Twig_Node_If($t, $else, 1);
25
26 $this->assertEquals($t, $node->getNode('tests'));
27 $this->assertEquals(null, $node->getNode('else'));
28
29 $else = new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 1), 1);
30 $node = new Twig_Node_If($t, $else, 1);
31 $this->assertEquals($else, $node->getNode('else'));
32 }
33
34 /**
35 * @covers Twig_Node_If::compile
36 * @dataProvider getTests
37 */
38 public function testCompile($node, $source, $environment = null)
39 {
40 parent::testCompile($node, $source, $environment);
41 }
42
43 public function getTests()
44 {
45 $tests = array();
46
47 $t = new Twig_Node(array(
48 new Twig_Node_Expression_Constant(true, 1),
49 new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
50 ), array(), 1);
51 $else = null;
52 $node = new Twig_Node_If($t, $else, 1);
53
54 $tests[] = array($node, <<<EOF
55// line 1
56if (true) {
57 echo {$this->getVariableGetter('foo')};
58}
59EOF
60 );
61
62 $t = new Twig_Node(array(
63 new Twig_Node_Expression_Constant(true, 1),
64 new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
65 new Twig_Node_Expression_Constant(false, 1),
66 new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 1), 1),
67 ), array(), 1);
68 $else = null;
69 $node = new Twig_Node_If($t, $else, 1);
70
71 $tests[] = array($node, <<<EOF
72// line 1
73if (true) {
74 echo {$this->getVariableGetter('foo')};
75} elseif (false) {
76 echo {$this->getVariableGetter('bar')};
77}
78EOF
79 );
80
81 $t = new Twig_Node(array(
82 new Twig_Node_Expression_Constant(true, 1),
83 new Twig_Node_Print(new Twig_Node_Expression_Name('foo', 1), 1),
84 ), array(), 1);
85 $else = new Twig_Node_Print(new Twig_Node_Expression_Name('bar', 1), 1);
86 $node = new Twig_Node_If($t, $else, 1);
87
88 $tests[] = array($node, <<<EOF
89// line 1
90if (true) {
91 echo {$this->getVariableGetter('foo')};
92} else {
93 echo {$this->getVariableGetter('bar')};
94}
95EOF
96 );
97
98 return $tests;
99 }
100}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php
new file mode 100644
index 00000000..db36581a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/ImportTest.php
@@ -0,0 +1,52 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_ImportTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Import::__construct
16 */
17 public function testConstructor()
18 {
19 $macro = new Twig_Node_Expression_Constant('foo.twig', 1);
20 $var = new Twig_Node_Expression_AssignName('macro', 1);
21 $node = new Twig_Node_Import($macro, $var, 1);
22
23 $this->assertEquals($macro, $node->getNode('expr'));
24 $this->assertEquals($var, $node->getNode('var'));
25 }
26
27 /**
28 * @covers Twig_Node_Import::compile
29 * @dataProvider getTests
30 */
31 public function testCompile($node, $source, $environment = null)
32 {
33 parent::testCompile($node, $source, $environment);
34 }
35
36 public function getTests()
37 {
38 $tests = array();
39
40 $macro = new Twig_Node_Expression_Constant('foo.twig', 1);
41 $var = new Twig_Node_Expression_AssignName('macro', 1);
42 $node = new Twig_Node_Import($macro, $var, 1);
43
44 $tests[] = array($node, <<<EOF
45// line 1
46\$context["macro"] = \$this->env->loadTemplate("foo.twig");
47EOF
48 );
49
50 return $tests;
51 }
52}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php b/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php
new file mode 100644
index 00000000..3b7da6e0
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/IncludeTest.php
@@ -0,0 +1,96 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_IncludeTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Include::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant('foo.twig', 1);
20 $node = new Twig_Node_Include($expr, null, false, false, 1);
21
22 $this->assertEquals(null, $node->getNode('variables'));
23 $this->assertEquals($expr, $node->getNode('expr'));
24 $this->assertFalse($node->getAttribute('only'));
25
26 $vars = new Twig_Node_Expression_Array(array(new Twig_Node_Expression_Constant('foo', 1), new Twig_Node_Expression_Constant(true, 1)), 1);
27 $node = new Twig_Node_Include($expr, $vars, true, false, 1);
28 $this->assertEquals($vars, $node->getNode('variables'));
29 $this->assertTrue($node->getAttribute('only'));
30 }
31
32 /**
33 * @covers Twig_Node_Include::compile
34 * @dataProvider getTests
35 */
36 public function testCompile($node, $source, $environment = null)
37 {
38 parent::testCompile($node, $source, $environment);
39 }
40
41 public function getTests()
42 {
43 $tests = array();
44
45 $expr = new Twig_Node_Expression_Constant('foo.twig', 1);
46 $node = new Twig_Node_Include($expr, null, false, false, 1);
47 $tests[] = array($node, <<<EOF
48// line 1
49\$this->env->loadTemplate("foo.twig")->display(\$context);
50EOF
51 );
52
53 $expr = new Twig_Node_Expression_Conditional(
54 new Twig_Node_Expression_Constant(true, 1),
55 new Twig_Node_Expression_Constant('foo', 1),
56 new Twig_Node_Expression_Constant('foo', 1),
57 0
58 );
59 $node = new Twig_Node_Include($expr, null, false, false, 1);
60 $tests[] = array($node, <<<EOF
61// line 1
62\$template = \$this->env->resolveTemplate(((true) ? ("foo") : ("foo")));
63\$template->display(\$context);
64EOF
65 );
66
67 $expr = new Twig_Node_Expression_Constant('foo.twig', 1);
68 $vars = new Twig_Node_Expression_Array(array(new Twig_Node_Expression_Constant('foo', 1), new Twig_Node_Expression_Constant(true, 1)), 1);
69 $node = new Twig_Node_Include($expr, $vars, false, false, 1);
70 $tests[] = array($node, <<<EOF
71// line 1
72\$this->env->loadTemplate("foo.twig")->display(array_merge(\$context, array("foo" => true)));
73EOF
74 );
75
76 $node = new Twig_Node_Include($expr, $vars, true, false, 1);
77 $tests[] = array($node, <<<EOF
78// line 1
79\$this->env->loadTemplate("foo.twig")->display(array("foo" => true));
80EOF
81 );
82
83 $node = new Twig_Node_Include($expr, $vars, true, true, 1);
84 $tests[] = array($node, <<<EOF
85// line 1
86try {
87 \$this->env->loadTemplate("foo.twig")->display(array("foo" => true));
88} catch (Twig_Error_Loader \$e) {
89 // ignore missing template
90}
91EOF
92 );
93
94 return $tests;
95 }
96}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php b/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php
new file mode 100644
index 00000000..4d2f641b
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/MacroTest.php
@@ -0,0 +1,73 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_MacroTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Macro::__construct
16 */
17 public function testConstructor()
18 {
19 $body = new Twig_Node_Text('foo', 1);
20 $arguments = new Twig_Node(array(new Twig_Node_Expression_Name('foo', 1)), array(), 1);
21 $node = new Twig_Node_Macro('foo', $body, $arguments, 1);
22
23 $this->assertEquals($body, $node->getNode('body'));
24 $this->assertEquals($arguments, $node->getNode('arguments'));
25 $this->assertEquals('foo', $node->getAttribute('name'));
26 }
27
28 /**
29 * @covers Twig_Node_Macro::compile
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $body = new Twig_Node_Text('foo', 1);
40 $arguments = new Twig_Node(array(
41 'foo' => new Twig_Node_Expression_Constant(null, 1),
42 'bar' => new Twig_Node_Expression_Constant('Foo', 1),
43 ), array(), 1);
44 $node = new Twig_Node_Macro('foo', $body, $arguments, 1);
45
46 return array(
47 array($node, <<<EOF
48// line 1
49public function getfoo(\$_foo = null, \$_bar = "Foo")
50{
51 \$context = \$this->env->mergeGlobals(array(
52 "foo" => \$_foo,
53 "bar" => \$_bar,
54 ));
55
56 \$blocks = array();
57
58 ob_start();
59 try {
60 echo "foo";
61 } catch (Exception \$e) {
62 ob_end_clean();
63
64 throw \$e;
65 }
66
67 return ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());
68}
69EOF
70 ),
71 );
72 }
73}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php b/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php
new file mode 100644
index 00000000..9411e99e
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/ModuleTest.php
@@ -0,0 +1,196 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_ModuleTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Module::__construct
16 */
17 public function testConstructor()
18 {
19 $body = new Twig_Node_Text('foo', 1);
20 $parent = new Twig_Node_Expression_Constant('layout.twig', 1);
21 $blocks = new Twig_Node();
22 $macros = new Twig_Node();
23 $traits = new Twig_Node();
24 $filename = 'foo.twig';
25 $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
26
27 $this->assertEquals($body, $node->getNode('body'));
28 $this->assertEquals($blocks, $node->getNode('blocks'));
29 $this->assertEquals($macros, $node->getNode('macros'));
30 $this->assertEquals($parent, $node->getNode('parent'));
31 $this->assertEquals($filename, $node->getAttribute('filename'));
32 }
33
34 /**
35 * @covers Twig_Node_Module::compile
36 * @covers Twig_Node_Module::compileTemplate
37 * @covers Twig_Node_Module::compileMacros
38 * @covers Twig_Node_Module::compileClassHeader
39 * @covers Twig_Node_Module::compileDisplayHeader
40 * @covers Twig_Node_Module::compileDisplayBody
41 * @covers Twig_Node_Module::compileDisplayFooter
42 * @covers Twig_Node_Module::compileClassFooter
43 * @dataProvider getTests
44 */
45 public function testCompile($node, $source, $environment = null)
46 {
47 parent::testCompile($node, $source, $environment);
48 }
49
50 public function getTests()
51 {
52 $twig = new Twig_Environment(new Twig_Loader_String());
53
54 $tests = array();
55
56 $body = new Twig_Node_Text('foo', 1);
57 $extends = null;
58 $blocks = new Twig_Node();
59 $macros = new Twig_Node();
60 $traits = new Twig_Node();
61 $filename = 'foo.twig';
62
63 $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
64 $tests[] = array($node, <<<EOF
65<?php
66
67/* foo.twig */
68class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
69{
70 public function __construct(Twig_Environment \$env)
71 {
72 parent::__construct(\$env);
73
74 \$this->parent = false;
75
76 \$this->blocks = array(
77 );
78 }
79
80 protected function doDisplay(array \$context, array \$blocks = array())
81 {
82 // line 1
83 echo "foo";
84 }
85
86 public function getTemplateName()
87 {
88 return "foo.twig";
89 }
90
91 public function getDebugInfo()
92 {
93 return array ( 19 => 1,);
94 }
95}
96EOF
97 , $twig);
98
99 $import = new Twig_Node_Import(new Twig_Node_Expression_Constant('foo.twig', 1), new Twig_Node_Expression_AssignName('macro', 1), 1);
100
101 $body = new Twig_Node(array($import));
102 $extends = new Twig_Node_Expression_Constant('layout.twig', 1);
103
104 $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
105 $tests[] = array($node, <<<EOF
106<?php
107
108/* foo.twig */
109class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
110{
111 public function __construct(Twig_Environment \$env)
112 {
113 parent::__construct(\$env);
114
115 \$this->parent = \$this->env->loadTemplate("layout.twig");
116
117 \$this->blocks = array(
118 );
119 }
120
121 protected function doGetParent(array \$context)
122 {
123 return "layout.twig";
124 }
125
126 protected function doDisplay(array \$context, array \$blocks = array())
127 {
128 // line 1
129 \$context["macro"] = \$this->env->loadTemplate("foo.twig");
130 \$this->parent->display(\$context, array_merge(\$this->blocks, \$blocks));
131 }
132
133 public function getTemplateName()
134 {
135 return "foo.twig";
136 }
137
138 public function isTraitable()
139 {
140 return false;
141 }
142
143 public function getDebugInfo()
144 {
145 return array ( 24 => 1,);
146 }
147}
148EOF
149 , $twig);
150
151 $body = new Twig_Node();
152 $extends = new Twig_Node_Expression_Conditional(
153 new Twig_Node_Expression_Constant(true, 1),
154 new Twig_Node_Expression_Constant('foo', 1),
155 new Twig_Node_Expression_Constant('foo', 1),
156 0
157 );
158
159 $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
160 $tests[] = array($node, <<<EOF
161<?php
162
163/* foo.twig */
164class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
165{
166 protected function doGetParent(array \$context)
167 {
168 return \$this->env->resolveTemplate(((true) ? ("foo") : ("foo")));
169 }
170
171 protected function doDisplay(array \$context, array \$blocks = array())
172 {
173 \$this->getParent(\$context)->display(\$context, array_merge(\$this->blocks, \$blocks));
174 }
175
176 public function getTemplateName()
177 {
178 return "foo.twig";
179 }
180
181 public function isTraitable()
182 {
183 return false;
184 }
185
186 public function getDebugInfo()
187 {
188 return array ();
189 }
190}
191EOF
192 , $twig);
193
194 return $tests;
195 }
196}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php b/vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php
new file mode 100644
index 00000000..6fe43a41
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/PrintTest.php
@@ -0,0 +1,41 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_PrintTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Print::__construct
16 */
17 public function testConstructor()
18 {
19 $expr = new Twig_Node_Expression_Constant('foo', 1);
20 $node = new Twig_Node_Print($expr, 1);
21
22 $this->assertEquals($expr, $node->getNode('expr'));
23 }
24
25 /**
26 * @covers Twig_Node_Print::compile
27 * @dataProvider getTests
28 */
29 public function testCompile($node, $source, $environment = null)
30 {
31 parent::testCompile($node, $source, $environment);
32 }
33
34 public function getTests()
35 {
36 $tests = array();
37 $tests[] = array(new Twig_Node_Print(new Twig_Node_Expression_Constant('foo', 1), 1), "// line 1\necho \"foo\";");
38
39 return $tests;
40 }
41}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php
new file mode 100644
index 00000000..db9dbf95
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/SandboxTest.php
@@ -0,0 +1,56 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_SandboxTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Sandbox::__construct
16 */
17 public function testConstructor()
18 {
19 $body = new Twig_Node_Text('foo', 1);
20 $node = new Twig_Node_Sandbox($body, 1);
21
22 $this->assertEquals($body, $node->getNode('body'));
23 }
24
25 /**
26 * @covers Twig_Node_Sandbox::compile
27 * @dataProvider getTests
28 */
29 public function testCompile($node, $source, $environment = null)
30 {
31 parent::testCompile($node, $source, $environment);
32 }
33
34 public function getTests()
35 {
36 $tests = array();
37
38 $body = new Twig_Node_Text('foo', 1);
39 $node = new Twig_Node_Sandbox($body, 1);
40
41 $tests[] = array($node, <<<EOF
42// line 1
43\$sandbox = \$this->env->getExtension('sandbox');
44if (!\$alreadySandboxed = \$sandbox->isSandboxed()) {
45 \$sandbox->enableSandbox();
46}
47echo "foo";
48if (!\$alreadySandboxed) {
49 \$sandbox->disableSandbox();
50}
51EOF
52 );
53
54 return $tests;
55 }
56}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php
new file mode 100644
index 00000000..217e3408
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/SandboxedModuleTest.php
@@ -0,0 +1,173 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_SandboxedModuleTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_SandboxedModule::__construct
16 */
17 public function testConstructor()
18 {
19 $body = new Twig_Node_Text('foo', 1);
20 $parent = new Twig_Node_Expression_Constant('layout.twig', 1);
21 $blocks = new Twig_Node();
22 $macros = new Twig_Node();
23 $traits = new Twig_Node();
24 $filename = 'foo.twig';
25 $node = new Twig_Node_Module($body, $parent, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
26 $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
27
28 $this->assertEquals($body, $node->getNode('body'));
29 $this->assertEquals($blocks, $node->getNode('blocks'));
30 $this->assertEquals($macros, $node->getNode('macros'));
31 $this->assertEquals($parent, $node->getNode('parent'));
32 $this->assertEquals($filename, $node->getAttribute('filename'));
33 }
34
35 /**
36 * @covers Twig_Node_SandboxedModule::compile
37 * @covers Twig_Node_SandboxedModule::compileDisplayBody
38 * @covers Twig_Node_SandboxedModule::compileDisplayFooter
39 * @dataProvider getTests
40 */
41 public function testCompile($node, $source, $environment = null)
42 {
43 parent::testCompile($node, $source, $environment);
44 }
45
46 public function getTests()
47 {
48 $twig = new Twig_Environment(new Twig_Loader_String());
49
50 $tests = array();
51
52 $body = new Twig_Node_Text('foo', 1);
53 $extends = null;
54 $blocks = new Twig_Node();
55 $macros = new Twig_Node();
56 $traits = new Twig_Node();
57 $filename = 'foo.twig';
58
59 $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
60 $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
61
62 $tests[] = array($node, <<<EOF
63<?php
64
65/* foo.twig */
66class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
67{
68 public function __construct(Twig_Environment \$env)
69 {
70 parent::__construct(\$env);
71
72 \$this->parent = false;
73
74 \$this->blocks = array(
75 );
76 }
77
78 protected function doDisplay(array \$context, array \$blocks = array())
79 {
80 \$this->checkSecurity();
81 // line 1
82 echo "foo";
83 }
84
85 protected function checkSecurity()
86 {
87 \$this->env->getExtension('sandbox')->checkSecurity(
88 array('upper'),
89 array('for'),
90 array('cycle')
91 );
92 }
93
94 public function getTemplateName()
95 {
96 return "foo.twig";
97 }
98
99 public function getDebugInfo()
100 {
101 return array ( 20 => 1,);
102 }
103}
104EOF
105 , $twig);
106
107 $body = new Twig_Node();
108 $extends = new Twig_Node_Expression_Constant('layout.twig', 1);
109 $blocks = new Twig_Node();
110 $macros = new Twig_Node();
111 $traits = new Twig_Node();
112 $filename = 'foo.twig';
113
114 $node = new Twig_Node_Module($body, $extends, $blocks, $macros, $traits, new Twig_Node(array()), $filename);
115 $node = new Twig_Node_SandboxedModule($node, array('for'), array('upper'), array('cycle'));
116
117 $tests[] = array($node, <<<EOF
118<?php
119
120/* foo.twig */
121class __TwigTemplate_be925a7b06dda0dfdbd18a1509f7eb34 extends Twig_Template
122{
123 public function __construct(Twig_Environment \$env)
124 {
125 parent::__construct(\$env);
126
127 \$this->parent = \$this->env->loadTemplate("layout.twig");
128
129 \$this->blocks = array(
130 );
131 }
132
133 protected function doGetParent(array \$context)
134 {
135 return "layout.twig";
136 }
137
138 protected function doDisplay(array \$context, array \$blocks = array())
139 {
140 \$this->checkSecurity();
141 \$this->parent->display(\$context, array_merge(\$this->blocks, \$blocks));
142 }
143
144 protected function checkSecurity()
145 {
146 \$this->env->getExtension('sandbox')->checkSecurity(
147 array('upper'),
148 array('for'),
149 array('cycle')
150 );
151 }
152
153 public function getTemplateName()
154 {
155 return "foo.twig";
156 }
157
158 public function isTraitable()
159 {
160 return false;
161 }
162
163 public function getDebugInfo()
164 {
165 return array ();
166 }
167}
168EOF
169 , $twig);
170
171 return $tests;
172 }
173}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php
new file mode 100644
index 00000000..058e02bc
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/SandboxedPrintTest.php
@@ -0,0 +1,45 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_SandboxedPrintTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_SandboxedPrint::__construct
16 */
17 public function testConstructor()
18 {
19 $node = new Twig_Node_SandboxedPrint($expr = new Twig_Node_Expression_Constant('foo', 1), 1);
20
21 $this->assertEquals($expr, $node->getNode('expr'));
22 }
23
24 /**
25 * @covers Twig_Node_SandboxedPrint::compile
26 * @dataProvider getTests
27 */
28 public function testCompile($node, $source, $environment = null)
29 {
30 parent::testCompile($node, $source, $environment);
31 }
32
33 public function getTests()
34 {
35 $tests = array();
36
37 $tests[] = array(new Twig_Node_SandboxedPrint(new Twig_Node_Expression_Constant('foo', 1), 1), <<<EOF
38// line 1
39echo \$this->env->getExtension('sandbox')->ensureToStringAllowed("foo");
40EOF
41 );
42
43 return $tests;
44 }
45}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SetTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SetTest.php
new file mode 100644
index 00000000..d64d671a
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/SetTest.php
@@ -0,0 +1,81 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_SetTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Set::__construct
16 */
17 public function testConstructor()
18 {
19 $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
20 $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 1)), array(), 1);
21 $node = new Twig_Node_Set(false, $names, $values, 1);
22
23 $this->assertEquals($names, $node->getNode('names'));
24 $this->assertEquals($values, $node->getNode('values'));
25 $this->assertEquals(false, $node->getAttribute('capture'));
26 }
27
28 /**
29 * @covers Twig_Node_Set::compile
30 * @dataProvider getTests
31 */
32 public function testCompile($node, $source, $environment = null)
33 {
34 parent::testCompile($node, $source, $environment);
35 }
36
37 public function getTests()
38 {
39 $tests = array();
40
41 $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
42 $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 1)), array(), 1);
43 $node = new Twig_Node_Set(false, $names, $values, 1);
44 $tests[] = array($node, <<<EOF
45// line 1
46\$context["foo"] = "foo";
47EOF
48 );
49
50 $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
51 $values = new Twig_Node(array(new Twig_Node_Print(new Twig_Node_Expression_Constant('foo', 1), 1)), array(), 1);
52 $node = new Twig_Node_Set(true, $names, $values, 1);
53 $tests[] = array($node, <<<EOF
54// line 1
55ob_start();
56echo "foo";
57\$context["foo"] = ('' === \$tmp = ob_get_clean()) ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());
58EOF
59 );
60
61 $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1)), array(), 1);
62 $values = new Twig_Node_Text('foo', 1);
63 $node = new Twig_Node_Set(true, $names, $values, 1);
64 $tests[] = array($node, <<<EOF
65// line 1
66\$context["foo"] = ('' === \$tmp = "foo") ? '' : new Twig_Markup(\$tmp, \$this->env->getCharset());
67EOF
68 );
69
70 $names = new Twig_Node(array(new Twig_Node_Expression_AssignName('foo', 1), new Twig_Node_Expression_AssignName('bar', 1)), array(), 1);
71 $values = new Twig_Node(array(new Twig_Node_Expression_Constant('foo', 1), new Twig_Node_Expression_Name('bar', 1)), array(), 1);
72 $node = new Twig_Node_Set(false, $names, $values, 1);
73 $tests[] = array($node, <<<EOF
74// line 1
75list(\$context["foo"], \$context["bar"]) = array("foo", {$this->getVariableGetter('bar')});
76EOF
77 );
78
79 return $tests;
80 }
81}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php b/vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php
new file mode 100644
index 00000000..6735dc31
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/SpacelessTest.php
@@ -0,0 +1,49 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_SpacelessTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Spaceless::__construct
16 */
17 public function testConstructor()
18 {
19 $body = new Twig_Node(array(new Twig_Node_Text('<div> <div> foo </div> </div>', 1)));
20 $node = new Twig_Node_Spaceless($body, 1);
21
22 $this->assertEquals($body, $node->getNode('body'));
23 }
24
25 /**
26 * @covers Twig_Node_Spaceless::compile
27 * @dataProvider getTests
28 */
29 public function testCompile($node, $source, $environment = null)
30 {
31 parent::testCompile($node, $source, $environment);
32 }
33
34 public function getTests()
35 {
36 $body = new Twig_Node(array(new Twig_Node_Text('<div> <div> foo </div> </div>', 1)));
37 $node = new Twig_Node_Spaceless($body, 1);
38
39 return array(
40 array($node, <<<EOF
41// line 1
42ob_start();
43echo "<div> <div> foo </div> </div>";
44echo trim(preg_replace('/>\s+</', '><', ob_get_clean()));
45EOF
46 ),
47 );
48 }
49}
diff --git a/vendor/twig/twig/test/Twig/Tests/Node/TextTest.php b/vendor/twig/twig/test/Twig/Tests/Node/TextTest.php
new file mode 100644
index 00000000..6f85576e
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/Node/TextTest.php
@@ -0,0 +1,40 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_Node_TextTest extends Twig_Test_NodeTestCase
13{
14 /**
15 * @covers Twig_Node_Text::__construct
16 */
17 public function testConstructor()
18 {
19 $node = new Twig_Node_Text('foo', 1);
20
21 $this->assertEquals('foo', $node->getAttribute('data'));
22 }
23
24 /**
25 * @covers Twig_Node_Text::compile
26 * @dataProvider getTests
27 */
28 public function testCompile($node, $source, $environment = null)
29 {
30 parent::testCompile($node, $source, $environment);
31 }
32
33 public function getTests()
34 {
35 $tests = array();
36 $tests[] = array(new Twig_Node_Text('foo', 1), "// line 1\necho \"foo\";");
37
38 return $tests;
39 }
40}
diff --git a/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php b/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php
new file mode 100644
index 00000000..d35740d5
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/NodeVisitor/OptimizerTest.php
@@ -0,0 +1,114 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Tests_NodeVisitor_OptimizerTest extends PHPUnit_Framework_TestCase
12{
13 public function testRenderBlockOptimizer()
14 {
15 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
16
17 $stream = $env->parse($env->tokenize('{{ block("foo") }}', 'index'));
18
19 $node = $stream->getNode('body')->getNode(0);
20
21 $this->assertEquals('Twig_Node_Expression_BlockReference', get_class($node));
22 $this->assertTrue($node->getAttribute('output'));
23 }
24
25 public function testRenderParentBlockOptimizer()
26 {
27 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
28
29 $stream = $env->parse($env->tokenize('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index'));
30
31 $node = $stream->getNode('blocks')->getNode('content')->getNode(0)->getNode('body');
32
33 $this->assertEquals('Twig_Node_Expression_Parent', get_class($node));
34 $this->assertTrue($node->getAttribute('output'));
35 }
36
37 public function testRenderVariableBlockOptimizer()
38 {
39 if (version_compare(phpversion(), '5.4.0RC1', '>=')) {
40 return;
41 }
42
43 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false, 'autoescape' => false));
44 $stream = $env->parse($env->tokenize('{{ block(name|lower) }}', 'index'));
45
46 $node = $stream->getNode('body')->getNode(0)->getNode(1);
47
48 $this->assertEquals('Twig_Node_Expression_BlockReference', get_class($node));
49 $this->assertTrue($node->getAttribute('output'));
50 }
51
52 /**
53 * @dataProvider getTestsForForOptimizer
54 */
55 public function testForOptimizer($template, $expected)
56 {
57 $env = new Twig_Environment(new Twig_Loader_String(), array('cache' => false));
58
59 $stream = $env->parse($env->tokenize($template, 'index'));
60
61 foreach ($expected as $target => $withLoop) {
62 $this->assertTrue($this->checkForConfiguration($stream, $target, $withLoop), sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : ''));
63 }
64 }
65
66 public function getTestsForForOptimizer()
67 {
68 return array(
69 array('{% for i in foo %}{% endfor %}', array('i' => false)),
70
71 array('{% for i in foo %}{{ loop.index }}{% endfor %}', array('i' => true)),
72
73 array('{% for i in foo %}{% for j in foo %}{% endfor %}{% endfor %}', array('i' => false, 'j' => false)),
74
75 array('{% for i in foo %}{% include "foo" %}{% endfor %}', array('i' => true)),
76
77 array('{% for i in foo %}{% include "foo" only %}{% endfor %}', array('i' => false)),
78
79 array('{% for i in foo %}{% include "foo" with { "foo": "bar" } only %}{% endfor %}', array('i' => false)),
80
81 array('{% for i in foo %}{% include "foo" with { "foo": loop.index } only %}{% endfor %}', array('i' => true)),
82
83 array('{% for i in foo %}{% for j in foo %}{{ loop.index }}{% endfor %}{% endfor %}', array('i' => false, 'j' => true)),
84
85 array('{% for i in foo %}{% for j in foo %}{{ loop.parent.loop.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => true)),
86
87 array('{% for i in foo %}{% set l = loop %}{% for j in foo %}{{ l.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => false)),
88
89 array('{% for i in foo %}{% for j in foo %}{{ foo.parent.loop.index }}{% endfor %}{% endfor %}', array('i' => false, 'j' => false)),
90
91 array('{% for i in foo %}{% for j in foo %}{{ loop["parent"].loop.index }}{% endfor %}{% endfor %}', array('i' => true, 'j' => true)),
92 );
93 }
94
95 public function checkForConfiguration(Twig_NodeInterface $node = null, $target, $withLoop)
96 {
97 if (null === $node) {
98 return;
99 }
100
101 foreach ($node as $n) {
102 if ($n instanceof Twig_Node_For) {
103 if ($target === $n->getNode('value_target')->getAttribute('name')) {
104 return $withLoop == $n->getAttribute('with_loop');
105 }
106 }
107
108 $ret = $this->checkForConfiguration($n, $target, $withLoop);
109 if (null !== $ret) {
110 return $ret;
111 }
112 }
113 }
114}
diff --git a/vendor/twig/twig/test/Twig/Tests/ParserTest.php b/vendor/twig/twig/test/Twig/Tests/ParserTest.php
new file mode 100644
index 00000000..55eb7fb4
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/ParserTest.php
@@ -0,0 +1,180 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Tests_ParserTest extends PHPUnit_Framework_TestCase
12{
13 /**
14 * @expectedException Twig_Error_Syntax
15 */
16 public function testSetMacroThrowsExceptionOnReservedMethods()
17 {
18 $parser = $this->getParser();
19 $parser->setMacro('display', $this->getMock('Twig_Node_Macro', array(), array(), '', null));
20 }
21
22 /**
23 * @expectedException Twig_Error_Syntax
24 * @expectedExceptionMessage Unknown tag name "foo". Did you mean "for" at line 1
25 */
26 public function testUnknownTag()
27 {
28 $stream = new Twig_TokenStream(array(
29 new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', 1),
30 new Twig_Token(Twig_Token::NAME_TYPE, 'foo', 1),
31 new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1),
32 new Twig_Token(Twig_Token::EOF_TYPE, '', 1),
33 ));
34 $parser = new Twig_Parser(new Twig_Environment());
35 $parser->parse($stream);
36 }
37
38 /**
39 * @dataProvider getFilterBodyNodesData
40 */
41 public function testFilterBodyNodes($input, $expected)
42 {
43 $parser = $this->getParser();
44
45 $this->assertEquals($expected, $parser->filterBodyNodes($input));
46 }
47
48 public function getFilterBodyNodesData()
49 {
50 return array(
51 array(
52 new Twig_Node(array(new Twig_Node_Text(' ', 1))),
53 new Twig_Node(array()),
54 ),
55 array(
56 $input = new Twig_Node(array(new Twig_Node_Set(false, new Twig_Node(), new Twig_Node(), 1))),
57 $input,
58 ),
59 array(
60 $input = new Twig_Node(array(new Twig_Node_Set(true, new Twig_Node(), new Twig_Node(array(new Twig_Node(array(new Twig_Node_Text('foo', 1))))), 1))),
61 $input,
62 ),
63 );
64 }
65
66 /**
67 * @dataProvider getFilterBodyNodesDataThrowsException
68 * @expectedException Twig_Error_Syntax
69 */
70 public function testFilterBodyNodesThrowsException($input)
71 {
72 $parser = $this->getParser();
73
74 $parser->filterBodyNodes($input);
75 }
76
77 public function getFilterBodyNodesDataThrowsException()
78 {
79 return array(
80 array(new Twig_Node_Text('foo', 1)),
81 array(new Twig_Node(array(new Twig_Node(array(new Twig_Node_Text('foo', 1)))))),
82 );
83 }
84
85 /**
86 * @expectedException Twig_Error_Syntax
87 * @expectedExceptionMessage A template that extends another one cannot have a body but a byte order mark (BOM) has been detected; it must be removed at line 1.
88 */
89 public function testFilterBodyNodesWithBOM()
90 {
91 $parser = $this->getParser();
92 $parser->filterBodyNodes(new Twig_Node_Text(chr(0xEF).chr(0xBB).chr(0xBF), 1));
93 }
94
95 public function testParseIsReentrant()
96 {
97 $twig = new Twig_Environment(null, array(
98 'autoescape' => false,
99 'optimizations' => 0,
100 ));
101 $twig->addTokenParser(new TestTokenParser());
102
103 $parser = new Twig_Parser($twig);
104
105 $parser->parse(new Twig_TokenStream(array(
106 new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', 1),
107 new Twig_Token(Twig_Token::NAME_TYPE, 'test', 1),
108 new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1),
109 new Twig_Token(Twig_Token::VAR_START_TYPE, '', 1),
110 new Twig_Token(Twig_Token::NAME_TYPE, 'foo', 1),
111 new Twig_Token(Twig_Token::VAR_END_TYPE, '', 1),
112 new Twig_Token(Twig_Token::EOF_TYPE, '', 1),
113 )));
114
115 $this->assertEquals(null, $parser->getParent());
116 }
117
118 // The getVarName() must not depend on the template loaders,
119 // If this test does not throw any exception, that's good.
120 // see https://github.com/symfony/symfony/issues/4218
121 public function testGetVarName()
122 {
123 $twig = new Twig_Environment(null, array(
124 'autoescape' => false,
125 'optimizations' => 0,
126 ));
127
128 $twig->parse($twig->tokenize(<<<EOF
129{% from _self import foo %}
130
131{% macro foo() %}
132 {{ foo }}
133{% endmacro %}
134EOF
135 ));
136 }
137
138 protected function getParser()
139 {
140 $parser = new TestParser(new Twig_Environment());
141 $parser->setParent(new Twig_Node());
142 $parser->stream = $this->getMockBuilder('Twig_TokenStream')->disableOriginalConstructor()->getMock();
143
144 return $parser;
145 }
146}
147
148class TestParser extends Twig_Parser
149{
150 public $stream;
151
152 public function filterBodyNodes(Twig_NodeInterface $node)
153 {
154 return parent::filterBodyNodes($node);
155 }
156}
157
158class TestTokenParser extends Twig_TokenParser
159{
160 public function parse(Twig_Token $token)
161 {
162 // simulate the parsing of another template right in the middle of the parsing of the current template
163 $this->parser->parse(new Twig_TokenStream(array(
164 new Twig_Token(Twig_Token::BLOCK_START_TYPE, '', 1),
165 new Twig_Token(Twig_Token::NAME_TYPE, 'extends', 1),
166 new Twig_Token(Twig_Token::STRING_TYPE, 'base', 1),
167 new Twig_Token(Twig_Token::BLOCK_END_TYPE, '', 1),
168 new Twig_Token(Twig_Token::EOF_TYPE, '', 1),
169 )));
170
171 $this->parser->getStream()->expect(Twig_Token::BLOCK_END_TYPE);
172
173 return new Twig_Node(array());
174 }
175
176 public function getTag()
177 {
178 return 'test';
179 }
180}
diff --git a/vendor/twig/twig/test/Twig/Tests/TemplateTest.php b/vendor/twig/twig/test/Twig/Tests/TemplateTest.php
new file mode 100644
index 00000000..823a9ce9
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/TemplateTest.php
@@ -0,0 +1,626 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11class Twig_Tests_TemplateTest extends PHPUnit_Framework_TestCase
12{
13 /**
14 * @dataProvider getAttributeExceptions
15 */
16 public function testGetAttributeExceptions($template, $message, $useExt)
17 {
18 $name = 'index_'.($useExt ? 1 : 0);
19 $templates = array(
20 $name => $template.$useExt, // appending $useExt makes the template content unique
21 );
22
23 $env = new Twig_Environment(new Twig_Loader_Array($templates), array('strict_variables' => true));
24 if (!$useExt) {
25 $env->addNodeVisitor(new CExtDisablingNodeVisitor());
26 }
27 $template = $env->loadTemplate($name);
28
29 $context = array(
30 'string' => 'foo',
31 'array' => array('foo' => 'foo'),
32 'array_access' => new Twig_TemplateArrayAccessObject(),
33 'magic_exception' => new Twig_TemplateMagicPropertyObjectWithException(),
34 );
35
36 try {
37 $template->render($context);
38 $this->fail('Accessing an invalid attribute should throw an exception.');
39 } catch (Twig_Error_Runtime $e) {
40 $this->assertSame(sprintf($message, $name), $e->getMessage());
41 }
42 }
43
44 public function getAttributeExceptions()
45 {
46 $tests = array(
47 array('{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1', false),
48 array('{{ array["a"] }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
49 array('{{ array_access["a"] }}', 'Key "a" in object (with ArrayAccess) of type "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
50 array('{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1', false),
51 array('{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1', false),
52 array('{{ array.a }}', 'Key "a" for array with keys "foo" does not exist in "%s" at line 1', false),
53 array('{{ attribute(array, -10) }}', 'Key "-10" for array with keys "foo" does not exist in "%s" at line 1', false),
54 array('{{ array_access.a }}', 'Method "a" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
55 array('{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ _self.foo(array_access) }}', 'Method "missing_method" for object "Twig_TemplateArrayAccessObject" does not exist in "%s" at line 1', false),
56 array('{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.', false),
57 );
58
59 if (function_exists('twig_template_get_attributes')) {
60 foreach (array_slice($tests, 0) as $test) {
61 $test[2] = true;
62 $tests[] = $test;
63 }
64 }
65
66 return $tests;
67 }
68
69 /**
70 * @dataProvider getGetAttributeWithSandbox
71 */
72 public function testGetAttributeWithSandbox($object, $item, $allowed, $useExt)
73 {
74 $twig = new Twig_Environment();
75 $policy = new Twig_Sandbox_SecurityPolicy(array(), array(), array(/*method*/), array(/*prop*/), array());
76 $twig->addExtension(new Twig_Extension_Sandbox($policy, !$allowed));
77 $template = new Twig_TemplateTest($twig, $useExt);
78
79 try {
80 $template->getAttribute($object, $item, array(), 'any');
81
82 if (!$allowed) {
83 $this->fail();
84 }
85 } catch (Twig_Sandbox_SecurityError $e) {
86 if ($allowed) {
87 $this->fail();
88 }
89
90 $this->assertContains('is not allowed', $e->getMessage());
91 }
92 }
93
94 public function getGetAttributeWithSandbox()
95 {
96 $tests = array(
97 array(new Twig_TemplatePropertyObject(), 'defined', false, false),
98 array(new Twig_TemplatePropertyObject(), 'defined', true, false),
99 array(new Twig_TemplateMethodObject(), 'defined', false, false),
100 array(new Twig_TemplateMethodObject(), 'defined', true, false),
101 );
102
103 if (function_exists('twig_template_get_attributes')) {
104 foreach (array_slice($tests, 0) as $test) {
105 $test[3] = true;
106 $tests[] = $test;
107 }
108 }
109
110 return $tests;
111 }
112
113 /**
114 * @dataProvider getGetAttributeWithTemplateAsObject
115 */
116 public function testGetAttributeWithTemplateAsObject($useExt)
117 {
118 $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
119 $template1 = new Twig_TemplateTest(new Twig_Environment(), false);
120
121 $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'string'));
122 $this->assertEquals('some_string', $template->getAttribute($template1, 'string'));
123
124 $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'true'));
125 $this->assertEquals('1', $template->getAttribute($template1, 'true'));
126
127 $this->assertInstanceof('Twig_Markup', $template->getAttribute($template1, 'zero'));
128 $this->assertEquals('0', $template->getAttribute($template1, 'zero'));
129
130 $this->assertNotInstanceof('Twig_Markup', $template->getAttribute($template1, 'empty'));
131 $this->assertSame('', $template->getAttribute($template1, 'empty'));
132 }
133
134 public function getGetAttributeWithTemplateAsObject()
135 {
136 $bools = array(
137 array(false),
138 );
139
140 if (function_exists('twig_template_get_attributes')) {
141 $bools[] = array(true);
142 }
143
144 return $bools;
145 }
146
147 /**
148 * @dataProvider getTestsDependingOnExtensionAvailability
149 */
150 public function testGetAttributeOnArrayWithConfusableKey($useExt = false)
151 {
152 $template = new Twig_TemplateTest(
153 new Twig_Environment(),
154 $useExt
155 );
156
157 $array = array('Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros');
158
159 $this->assertSame('Zero', $array[false]);
160 $this->assertSame('One', $array[true]);
161 $this->assertSame('One', $array[1.5]);
162 $this->assertSame('One', $array['1']);
163 $this->assertSame('MinusOne', $array[-1.5]);
164 $this->assertSame('FloatButString', $array['1.5']);
165 $this->assertSame('IntegerButStringWithLeadingZeros', $array['01']);
166 $this->assertSame('EmptyString', $array[null]);
167
168 $this->assertSame('Zero', $template->getAttribute($array, false), 'false is treated as 0 when accessing an array (equals PHP behavior)');
169 $this->assertSame('One', $template->getAttribute($array, true), 'true is treated as 1 when accessing an array (equals PHP behavior)');
170 $this->assertSame('One', $template->getAttribute($array, 1.5), 'float is casted to int when accessing an array (equals PHP behavior)');
171 $this->assertSame('One', $template->getAttribute($array, '1'), '"1" is treated as integer 1 when accessing an array (equals PHP behavior)');
172 $this->assertSame('MinusOne', $template->getAttribute($array, -1.5), 'negative float is casted to int when accessing an array (equals PHP behavior)');
173 $this->assertSame('FloatButString', $template->getAttribute($array, '1.5'), '"1.5" is treated as-is when accessing an array (equals PHP behavior)');
174 $this->assertSame('IntegerButStringWithLeadingZeros', $template->getAttribute($array, '01'), '"01" is treated as-is when accessing an array (equals PHP behavior)');
175 $this->assertSame('EmptyString', $template->getAttribute($array, null), 'null is treated as "" when accessing an array (equals PHP behavior)');
176 }
177
178 public function getTestsDependingOnExtensionAvailability()
179 {
180 if (function_exists('twig_template_get_attributes')) {
181 return array(array(false), array(true));
182 }
183
184 return array(array(false));
185 }
186
187 /**
188 * @dataProvider getGetAttributeTests
189 */
190 public function testGetAttribute($defined, $value, $object, $item, $arguments, $type, $useExt = false)
191 {
192 $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
193
194 $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
195 }
196
197 /**
198 * @dataProvider getGetAttributeTests
199 */
200 public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false, $exceptionMessage = null)
201 {
202 $template = new Twig_TemplateTest(new Twig_Environment(null, array('strict_variables' => true)), $useExt);
203
204 if ($defined) {
205 $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
206 } else {
207 try {
208 $this->assertEquals($value, $template->getAttribute($object, $item, $arguments, $type));
209
210 throw new Exception('Expected Twig_Error_Runtime exception.');
211 } catch (Twig_Error_Runtime $e) {
212 if (null !== $exceptionMessage) {
213 $this->assertSame($exceptionMessage, $e->getMessage());
214 }
215 }
216 }
217 }
218
219 /**
220 * @dataProvider getGetAttributeTests
221 */
222 public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type, $useExt = false)
223 {
224 $template = new Twig_TemplateTest(new Twig_Environment(), $useExt);
225
226 $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true));
227 }
228
229 /**
230 * @dataProvider getGetAttributeTests
231 */
232 public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type, $useExt = false)
233 {
234 $template = new Twig_TemplateTest(new Twig_Environment(null, array('strict_variables' => true)), $useExt);
235
236 $this->assertEquals($defined, $template->getAttribute($object, $item, $arguments, $type, true));
237 }
238
239 public function getGetAttributeTests()
240 {
241 $array = array(
242 'defined' => 'defined',
243 'zero' => 0,
244 'null' => null,
245 '1' => 1,
246 'bar' => true,
247 '09' => '09',
248 '+4' => '+4',
249 );
250
251 $objectArray = new Twig_TemplateArrayAccessObject();
252 $stdObject = (object) $array;
253 $magicPropertyObject = new Twig_TemplateMagicPropertyObject();
254 $propertyObject = new Twig_TemplatePropertyObject();
255 $propertyObject1 = new Twig_TemplatePropertyObjectAndIterator();
256 $propertyObject2 = new Twig_TemplatePropertyObjectAndArrayAccess();
257 $methodObject = new Twig_TemplateMethodObject();
258 $magicMethodObject = new Twig_TemplateMagicMethodObject();
259
260 $anyType = Twig_TemplateInterface::ANY_CALL;
261 $methodType = Twig_TemplateInterface::METHOD_CALL;
262 $arrayType = Twig_TemplateInterface::ARRAY_CALL;
263
264 $basicTests = array(
265 // array(defined, value, property to fetch)
266 array(true, 'defined', 'defined'),
267 array(false, null, 'undefined'),
268 array(false, null, 'protected'),
269 array(true, 0, 'zero'),
270 array(true, 1, 1),
271 array(true, 1, 1.0),
272 array(true, null, 'null'),
273 array(true, true, 'bar'),
274 array(true, '09', '09'),
275 array(true, '+4', '+4'),
276 );
277 $testObjects = array(
278 // array(object, type of fetch)
279 array($array, $arrayType),
280 array($objectArray, $arrayType),
281 array($stdObject, $anyType),
282 array($magicPropertyObject, $anyType),
283 array($methodObject, $methodType),
284 array($methodObject, $anyType),
285 array($propertyObject, $anyType),
286 array($propertyObject1, $anyType),
287 array($propertyObject2, $anyType),
288 );
289
290 $tests = array();
291 foreach ($testObjects as $testObject) {
292 foreach ($basicTests as $test) {
293 // properties cannot be numbers
294 if (($testObject[0] instanceof stdClass || $testObject[0] instanceof Twig_TemplatePropertyObject) && is_numeric($test[2])) {
295 continue;
296 }
297
298 if ('+4' === $test[2] && $methodObject === $testObject[0]) {
299 continue;
300 }
301
302 $tests[] = array($test[0], $test[1], $testObject[0], $test[2], array(), $testObject[1]);
303 }
304 }
305
306 // additional method tests
307 $tests = array_merge($tests, array(
308 array(true, 'defined', $methodObject, 'defined', array(), $methodType),
309 array(true, 'defined', $methodObject, 'DEFINED', array(), $methodType),
310 array(true, 'defined', $methodObject, 'getDefined', array(), $methodType),
311 array(true, 'defined', $methodObject, 'GETDEFINED', array(), $methodType),
312 array(true, 'static', $methodObject, 'static', array(), $methodType),
313 array(true, 'static', $methodObject, 'getStatic', array(), $methodType),
314
315 array(true, '__call_undefined', $magicMethodObject, 'undefined', array(), $methodType),
316 array(true, '__call_UNDEFINED', $magicMethodObject, 'UNDEFINED', array(), $methodType),
317 ));
318
319 // add the same tests for the any type
320 foreach ($tests as $test) {
321 if ($anyType !== $test[5]) {
322 $test[5] = $anyType;
323 $tests[] = $test;
324 }
325 }
326
327 $methodAndPropObject = new Twig_TemplateMethodAndPropObject;
328
329 // additional method tests
330 $tests = array_merge($tests, array(
331 array(true, 'a', $methodAndPropObject, 'a', array(), $anyType),
332 array(true, 'a', $methodAndPropObject, 'a', array(), $methodType),
333 array(false, null, $methodAndPropObject, 'a', array(), $arrayType),
334
335 array(true, 'b_prop', $methodAndPropObject, 'b', array(), $anyType),
336 array(true, 'b', $methodAndPropObject, 'B', array(), $anyType),
337 array(true, 'b', $methodAndPropObject, 'b', array(), $methodType),
338 array(true, 'b', $methodAndPropObject, 'B', array(), $methodType),
339 array(false, null, $methodAndPropObject, 'b', array(), $arrayType),
340
341 array(false, null, $methodAndPropObject, 'c', array(), $anyType),
342 array(false, null, $methodAndPropObject, 'c', array(), $methodType),
343 array(false, null, $methodAndPropObject, 'c', array(), $arrayType),
344
345 ));
346
347 // tests when input is not an array or object
348 $tests = array_merge($tests, array(
349 array(false, null, 42, 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a integer variable ("42")'),
350 array(false, null, "string", 'a', array(), $anyType, false, 'Impossible to access an attribute ("a") on a string variable ("string")'),
351 array(false, null, array(), 'a', array(), $anyType, false, 'Key "a" for array with keys "" does not exist'),
352 ));
353
354 // add twig_template_get_attributes tests
355
356 if (function_exists('twig_template_get_attributes')) {
357 foreach (array_slice($tests, 0) as $test) {
358 $test = array_pad($test, 7, null);
359 $test[6] = true;
360 $tests[] = $test;
361 }
362 }
363
364 return $tests;
365 }
366}
367
368class Twig_TemplateTest extends Twig_Template
369{
370 protected $useExtGetAttribute = false;
371
372 public function __construct(Twig_Environment $env, $useExtGetAttribute = false)
373 {
374 parent::__construct($env);
375 $this->useExtGetAttribute = $useExtGetAttribute;
376 Twig_Template::clearCache();
377 }
378
379 public function getZero()
380 {
381 return 0;
382 }
383
384 public function getEmpty()
385 {
386 return '';
387 }
388
389 public function getString()
390 {
391 return 'some_string';
392 }
393
394 public function getTrue()
395 {
396 return true;
397 }
398
399 public function getTemplateName()
400 {
401 }
402
403 public function getDebugInfo()
404 {
405 return array();
406 }
407
408 protected function doGetParent(array $context)
409 {
410 }
411
412 protected function doDisplay(array $context, array $blocks = array())
413 {
414 }
415
416 public function getAttribute($object, $item, array $arguments = array(), $type = Twig_TemplateInterface::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false)
417 {
418 if ($this->useExtGetAttribute) {
419 return twig_template_get_attributes($this, $object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);
420 } else {
421 return parent::getAttribute($object, $item, $arguments, $type, $isDefinedTest, $ignoreStrictCheck);
422 }
423 }
424}
425
426class Twig_TemplateArrayAccessObject implements ArrayAccess
427{
428 protected $protected = 'protected';
429
430 public $attributes = array(
431 'defined' => 'defined',
432 'zero' => 0,
433 'null' => null,
434 '1' => 1,
435 'bar' => true,
436 '09' => '09',
437 '+4' => '+4',
438 );
439
440 public function offsetExists($name)
441 {
442 return array_key_exists($name, $this->attributes);
443 }
444
445 public function offsetGet($name)
446 {
447 return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
448 }
449
450 public function offsetSet($name, $value)
451 {
452 }
453
454 public function offsetUnset($name)
455 {
456 }
457}
458
459class Twig_TemplateMagicPropertyObject
460{
461 public $defined = 'defined';
462
463 public $attributes = array(
464 'zero' => 0,
465 'null' => null,
466 '1' => 1,
467 'bar' => true,
468 '09' => '09',
469 '+4' => '+4',
470 );
471
472 protected $protected = 'protected';
473
474 public function __isset($name)
475 {
476 return array_key_exists($name, $this->attributes);
477 }
478
479 public function __get($name)
480 {
481 return array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null;
482 }
483}
484
485class Twig_TemplateMagicPropertyObjectWithException
486{
487 public function __isset($key)
488 {
489 throw new Exception("Hey! Don't try to isset me!");
490 }
491}
492
493class Twig_TemplatePropertyObject
494{
495 public $defined = 'defined';
496 public $zero = 0;
497 public $null = null;
498 public $bar = true;
499
500 protected $protected = 'protected';
501}
502
503class Twig_TemplatePropertyObjectAndIterator extends Twig_TemplatePropertyObject implements IteratorAggregate
504{
505 public function getIterator()
506 {
507 return new ArrayIterator(array('foo', 'bar'));
508 }
509}
510
511class Twig_TemplatePropertyObjectAndArrayAccess extends Twig_TemplatePropertyObject implements ArrayAccess
512{
513 private $data = array();
514
515 public function offsetExists($offset)
516 {
517 return array_key_exists($offset, $this->data);
518 }
519
520 public function offsetGet($offset)
521 {
522 return $this->offsetExists($offset) ? $this->data[$offset] : 'n/a';
523 }
524
525 public function offsetSet($offset, $value)
526 {
527 }
528
529 public function offsetUnset($offset)
530 {
531 }
532}
533
534class Twig_TemplateMethodObject
535{
536 public function getDefined()
537 {
538 return 'defined';
539 }
540
541 public function get1()
542 {
543 return 1;
544 }
545
546 public function get09()
547 {
548 return '09';
549 }
550
551 public function getZero()
552 {
553 return 0;
554 }
555
556 public function getNull()
557 {
558 return null;
559 }
560
561 public function isBar()
562 {
563 return true;
564 }
565
566 protected function getProtected()
567 {
568 return 'protected';
569 }
570
571 public static function getStatic()
572 {
573 return 'static';
574 }
575}
576
577class Twig_TemplateMethodAndPropObject
578{
579 private $a = 'a_prop';
580 public function getA()
581 {
582 return 'a';
583 }
584
585 public $b = 'b_prop';
586 public function getB()
587 {
588 return 'b';
589 }
590
591 private $c = 'c_prop';
592 private function getC()
593 {
594 return 'c';
595 }
596}
597
598class Twig_TemplateMagicMethodObject
599{
600 public function __call($method, $arguments)
601 {
602 return '__call_'.$method;
603 }
604}
605
606class CExtDisablingNodeVisitor implements Twig_NodeVisitorInterface
607{
608 public function enterNode(Twig_NodeInterface $node, Twig_Environment $env)
609 {
610 if ($node instanceof Twig_Node_Expression_GetAttr) {
611 $node->setAttribute('disable_c_ext', true);
612 }
613
614 return $node;
615 }
616
617 public function leaveNode(Twig_NodeInterface $node, Twig_Environment $env)
618 {
619 return $node;
620 }
621
622 public function getPriority()
623 {
624 return 0;
625 }
626}
diff --git a/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php b/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php
new file mode 100644
index 00000000..fd4ec633
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/TokenStreamTest.php
@@ -0,0 +1,70 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12class Twig_Tests_TokenStreamTest extends PHPUnit_Framework_TestCase
13{
14 protected static $tokens;
15
16 public function setUp()
17 {
18 self::$tokens = array(
19 new Twig_Token(Twig_Token::TEXT_TYPE, 1, 1),
20 new Twig_Token(Twig_Token::TEXT_TYPE, 2, 1),
21 new Twig_Token(Twig_Token::TEXT_TYPE, 3, 1),
22 new Twig_Token(Twig_Token::TEXT_TYPE, 4, 1),
23 new Twig_Token(Twig_Token::TEXT_TYPE, 5, 1),
24 new Twig_Token(Twig_Token::TEXT_TYPE, 6, 1),
25 new Twig_Token(Twig_Token::TEXT_TYPE, 7, 1),
26 new Twig_Token(Twig_Token::EOF_TYPE, 0, 1),
27 );
28 }
29
30 public function testNext()
31 {
32 $stream = new Twig_TokenStream(self::$tokens);
33 $repr = array();
34 while (!$stream->isEOF()) {
35 $token = $stream->next();
36
37 $repr[] = $token->getValue();
38 }
39 $this->assertEquals('1, 2, 3, 4, 5, 6, 7', implode(', ', $repr), '->next() advances the pointer and returns the current token');
40 }
41
42 /**
43 * @expectedException Twig_Error_Syntax
44 * @expectedMessage Unexpected end of template
45 */
46 public function testEndOfTemplateNext()
47 {
48 $stream = new Twig_TokenStream(array(
49 new Twig_Token(Twig_Token::BLOCK_START_TYPE, 1, 1),
50 ));
51 while (!$stream->isEOF()) {
52 $stream->next();
53 }
54 }
55
56 /**
57 * @expectedException Twig_Error_Syntax
58 * @expectedMessage Unexpected end of template
59 */
60 public function testEndOfTemplateLook()
61 {
62 $stream = new Twig_TokenStream(array(
63 new Twig_Token(Twig_Token::BLOCK_START_TYPE, 1, 1),
64 ));
65 while (!$stream->isEOF()) {
66 $stream->look();
67 $stream->next();
68 }
69 }
70}
diff --git a/vendor/twig/twig/test/Twig/Tests/escapingTest.php b/vendor/twig/twig/test/Twig/Tests/escapingTest.php
new file mode 100644
index 00000000..b41b5f97
--- /dev/null
+++ b/vendor/twig/twig/test/Twig/Tests/escapingTest.php
@@ -0,0 +1,320 @@
1<?php
2
3/**
4 * This class is adapted from code coming from Zend Framework.
5 *
6 * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (http://www.zend.com)
7 * @license http://framework.zend.com/license/new-bsd New BSD License
8 */
9
10class Twig_Test_EscapingTest extends PHPUnit_Framework_TestCase
11{
12 /**
13 * All character encodings supported by htmlspecialchars()
14 */
15 protected $htmlSpecialChars = array(
16 '\'' => '&#039;',
17 '"' => '&quot;',
18 '<' => '&lt;',
19 '>' => '&gt;',
20 '&' => '&amp;'
21 );
22
23 protected $htmlAttrSpecialChars = array(
24 '\'' => '&#x27;',
25 /* Characters beyond ASCII value 255 to unicode escape */
26 'Ā' => '&#x0100;',
27 /* Immune chars excluded */
28 ',' => ',',
29 '.' => '.',
30 '-' => '-',
31 '_' => '_',
32 /* Basic alnums excluded */
33 'a' => 'a',
34 'A' => 'A',
35 'z' => 'z',
36 'Z' => 'Z',
37 '0' => '0',
38 '9' => '9',
39 /* Basic control characters and null */
40 "\r" => '&#x0D;',
41 "\n" => '&#x0A;',
42 "\t" => '&#x09;',
43 "\0" => '&#xFFFD;', // should use Unicode replacement char
44 /* Encode chars as named entities where possible */
45 '<' => '&lt;',
46 '>' => '&gt;',
47 '&' => '&amp;',
48 '"' => '&quot;',
49 /* Encode spaces for quoteless attribute protection */
50 ' ' => '&#x20;',
51 );
52
53 protected $jsSpecialChars = array(
54 /* HTML special chars - escape without exception to hex */
55 '<' => '\\x3C',
56 '>' => '\\x3E',
57 '\'' => '\\x27',
58 '"' => '\\x22',
59 '&' => '\\x26',
60 /* Characters beyond ASCII value 255 to unicode escape */
61 'Ā' => '\\u0100',
62 /* Immune chars excluded */
63 ',' => ',',
64 '.' => '.',
65 '_' => '_',
66 /* Basic alnums excluded */
67 'a' => 'a',
68 'A' => 'A',
69 'z' => 'z',
70 'Z' => 'Z',
71 '0' => '0',
72 '9' => '9',
73 /* Basic control characters and null */
74 "\r" => '\\x0D',
75 "\n" => '\\x0A',
76 "\t" => '\\x09',
77 "\0" => '\\x00',
78 /* Encode spaces for quoteless attribute protection */
79 ' ' => '\\x20',
80 );
81
82 protected $urlSpecialChars = array(
83 /* HTML special chars - escape without exception to percent encoding */
84 '<' => '%3C',
85 '>' => '%3E',
86 '\'' => '%27',
87 '"' => '%22',
88 '&' => '%26',
89 /* Characters beyond ASCII value 255 to hex sequence */
90 'Ā' => '%C4%80',
91 /* Punctuation and unreserved check */
92 ',' => '%2C',
93 '.' => '.',
94 '_' => '_',
95 '-' => '-',
96 ':' => '%3A',
97 ';' => '%3B',
98 '!' => '%21',
99 /* Basic alnums excluded */
100 'a' => 'a',
101 'A' => 'A',
102 'z' => 'z',
103 'Z' => 'Z',
104 '0' => '0',
105 '9' => '9',
106 /* Basic control characters and null */
107 "\r" => '%0D',
108 "\n" => '%0A',
109 "\t" => '%09',
110 "\0" => '%00',
111 /* PHP quirks from the past */
112 ' ' => '%20',
113 '~' => '~',
114 '+' => '%2B',
115 );
116
117 protected $cssSpecialChars = array(
118 /* HTML special chars - escape without exception to hex */
119 '<' => '\\3C ',
120 '>' => '\\3E ',
121 '\'' => '\\27 ',
122 '"' => '\\22 ',
123 '&' => '\\26 ',
124 /* Characters beyond ASCII value 255 to unicode escape */
125 'Ā' => '\\100 ',
126 /* Immune chars excluded */
127 ',' => '\\2C ',
128 '.' => '\\2E ',
129 '_' => '\\5F ',
130 /* Basic alnums excluded */
131 'a' => 'a',
132 'A' => 'A',
133 'z' => 'z',
134 'Z' => 'Z',
135 '0' => '0',
136 '9' => '9',
137 /* Basic control characters and null */
138 "\r" => '\\D ',
139 "\n" => '\\A ',
140 "\t" => '\\9 ',
141 "\0" => '\\0 ',
142 /* Encode spaces for quoteless attribute protection */
143 ' ' => '\\20 ',
144 );
145
146 protected $env;
147
148 public function setUp()
149 {
150 $this->env = new Twig_Environment();
151 }
152
153 public function testHtmlEscapingConvertsSpecialChars()
154 {
155 foreach ($this->htmlSpecialChars as $key => $value) {
156 $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html'), 'Failed to escape: '.$key);
157 }
158 }
159
160 public function testHtmlAttributeEscapingConvertsSpecialChars()
161 {
162 foreach ($this->htmlAttrSpecialChars as $key => $value) {
163 $this->assertEquals($value, twig_escape_filter($this->env, $key, 'html_attr'), 'Failed to escape: '.$key);
164 }
165 }
166
167 public function testJavascriptEscapingConvertsSpecialChars()
168 {
169 foreach ($this->jsSpecialChars as $key => $value) {
170 $this->assertEquals($value, twig_escape_filter($this->env, $key, 'js'), 'Failed to escape: '.$key);
171 }
172 }
173
174 public function testJavascriptEscapingReturnsStringIfZeroLength()
175 {
176 $this->assertEquals('', twig_escape_filter($this->env, '', 'js'));
177 }
178
179 public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits()
180 {
181 $this->assertEquals('123', twig_escape_filter($this->env, '123', 'js'));
182 }
183
184 public function testCssEscapingConvertsSpecialChars()
185 {
186 foreach ($this->cssSpecialChars as $key => $value) {
187 $this->assertEquals($value, twig_escape_filter($this->env, $key, 'css'), 'Failed to escape: '.$key);
188 }
189 }
190
191 public function testCssEscapingReturnsStringIfZeroLength()
192 {
193 $this->assertEquals('', twig_escape_filter($this->env, '', 'css'));
194 }
195
196 public function testCssEscapingReturnsStringIfContainsOnlyDigits()
197 {
198 $this->assertEquals('123', twig_escape_filter($this->env, '123', 'css'));
199 }
200
201 public function testUrlEscapingConvertsSpecialChars()
202 {
203 foreach ($this->urlSpecialChars as $key => $value) {
204 $this->assertEquals($value, twig_escape_filter($this->env, $key, 'url'), 'Failed to escape: '.$key);
205 }
206 }
207
208 /**
209 * Range tests to confirm escaped range of characters is within OWASP recommendation
210 */
211
212 /**
213 * Only testing the first few 2 ranges on this prot. function as that's all these
214 * other range tests require
215 */
216 public function testUnicodeCodepointConversionToUtf8()
217 {
218 $expected = " ~ޙ";
219 $codepoints = array(0x20, 0x7e, 0x799);
220 $result = '';
221 foreach ($codepoints as $value) {
222 $result .= $this->codepointToUtf8($value);
223 }
224 $this->assertEquals($expected, $result);
225 }
226
227 /**
228 * Convert a Unicode Codepoint to a literal UTF-8 character.
229 *
230 * @param int Unicode codepoint in hex notation
231 * @return string UTF-8 literal string
232 */
233 protected function codepointToUtf8($codepoint)
234 {
235 if ($codepoint < 0x80) {
236 return chr($codepoint);
237 }
238 if ($codepoint < 0x800) {
239 return chr($codepoint >> 6 & 0x3f | 0xc0)
240 . chr($codepoint & 0x3f | 0x80);
241 }
242 if ($codepoint < 0x10000) {
243 return chr($codepoint >> 12 & 0x0f | 0xe0)
244 . chr($codepoint >> 6 & 0x3f | 0x80)
245 . chr($codepoint & 0x3f | 0x80);
246 }
247 if ($codepoint < 0x110000) {
248 return chr($codepoint >> 18 & 0x07 | 0xf0)
249 . chr($codepoint >> 12 & 0x3f | 0x80)
250 . chr($codepoint >> 6 & 0x3f | 0x80)
251 . chr($codepoint & 0x3f | 0x80);
252 }
253 throw new Exception('Codepoint requested outside of Unicode range');
254 }
255
256 public function testJavascriptEscapingEscapesOwaspRecommendedRanges()
257 {
258 $immune = array(',', '.', '_'); // Exceptions to escaping ranges
259 for ($chr=0; $chr < 0xFF; $chr++) {
260 if ($chr >= 0x30 && $chr <= 0x39
261 || $chr >= 0x41 && $chr <= 0x5A
262 || $chr >= 0x61 && $chr <= 0x7A) {
263 $literal = $this->codepointToUtf8($chr);
264 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
265 } else {
266 $literal = $this->codepointToUtf8($chr);
267 if (in_array($literal, $immune)) {
268 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'js'));
269 } else {
270 $this->assertNotEquals(
271 $literal,
272 twig_escape_filter($this->env, $literal, 'js'),
273 "$literal should be escaped!");
274 }
275 }
276 }
277 }
278
279 public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges()
280 {
281 $immune = array(',', '.', '-', '_'); // Exceptions to escaping ranges
282 for ($chr=0; $chr < 0xFF; $chr++) {
283 if ($chr >= 0x30 && $chr <= 0x39
284 || $chr >= 0x41 && $chr <= 0x5A
285 || $chr >= 0x61 && $chr <= 0x7A) {
286 $literal = $this->codepointToUtf8($chr);
287 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
288 } else {
289 $literal = $this->codepointToUtf8($chr);
290 if (in_array($literal, $immune)) {
291 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'html_attr'));
292 } else {
293 $this->assertNotEquals(
294 $literal,
295 twig_escape_filter($this->env, $literal, 'html_attr'),
296 "$literal should be escaped!");
297 }
298 }
299 }
300 }
301
302 public function testCssEscapingEscapesOwaspRecommendedRanges()
303 {
304 $immune = array(); // CSS has no exceptions to escaping ranges
305 for ($chr=0; $chr < 0xFF; $chr++) {
306 if ($chr >= 0x30 && $chr <= 0x39
307 || $chr >= 0x41 && $chr <= 0x5A
308 || $chr >= 0x61 && $chr <= 0x7A) {
309 $literal = $this->codepointToUtf8($chr);
310 $this->assertEquals($literal, twig_escape_filter($this->env, $literal, 'css'));
311 } else {
312 $literal = $this->codepointToUtf8($chr);
313 $this->assertNotEquals(
314 $literal,
315 twig_escape_filter($this->env, $literal, 'css'),
316 "$literal should be escaped!");
317 }
318 }
319 }
320}
diff --git a/vendor/twig/twig/test/bootstrap.php b/vendor/twig/twig/test/bootstrap.php
new file mode 100644
index 00000000..aecb976f
--- /dev/null
+++ b/vendor/twig/twig/test/bootstrap.php
@@ -0,0 +1,13 @@
1<?php
2
3/*
4 * This file is part of Twig.
5 *
6 * (c) Fabien Potencier
7 *
8 * For the full copyright and license information, please view the LICENSE
9 * file that was distributed with this source code.
10 */
11
12require_once dirname(__FILE__).'/../lib/Twig/Autoloader.php';
13Twig_Autoloader::register(true);