Initial Drupal 11 with DDEV setup
This commit is contained in:
456
vendor/twig/twig/CHANGELOG
vendored
Normal file
456
vendor/twig/twig/CHANGELOG
vendored
Normal file
@ -0,0 +1,456 @@
|
||||
# 3.21.1 (2025-05-03)
|
||||
|
||||
* Fix ExtensionSet usage of BinaryOperatorExpressionParser
|
||||
|
||||
# 3.21.0 (2025-05-02)
|
||||
|
||||
* Fix wrong array index
|
||||
* Deprecate `Template::loadTemplate()`
|
||||
* Fix testing and expression when it evaluates to an instance of `Markup`
|
||||
* Add `ReturnPrimitiveTypeInterface` (and sub-interfaces for number, boolean, string, and array)
|
||||
* Add `SupportDefinedTestInterface` for expression nodes supporting the `defined` test
|
||||
* Deprecate using the `|` operator in an expression with `+` or `-` without using parentheses to clarify precedence
|
||||
* Deprecate operator precedence outside of the [0, 512] range
|
||||
* Introduce expression parser classes to describe operators and operands provided by extensions
|
||||
instead of arrays (it comes with many deprecations that are documented in
|
||||
the ``deprecated`` documentation chapter)
|
||||
* Deprecate the `Twig\ExpressionParser`, and `Twig\OperatorPrecedenceChange` classes
|
||||
* Add attributes `AsTwigFilter`, `AsTwigFunction`, and `AsTwigTest` to ease extension development
|
||||
|
||||
# 3.20.0 (2025-02-13)
|
||||
|
||||
* Fix support for ignoring syntax errors in an undefined handler in guard
|
||||
* Add configuration for Commonmark
|
||||
* Fix wrong array index
|
||||
* Bump minimum PHP version to 8.1
|
||||
* Add support for registering callbacks for undefined functions, filters or token parsers in the IntegrationTestCase
|
||||
* Use correct line number for `ForElseNode`
|
||||
* Fix timezone conversion on strings
|
||||
|
||||
# 3.19.0 (2025-01-28)
|
||||
|
||||
* Fix a security issue where escaping was missing when using `??`
|
||||
* Deprecate `Token::getType()`, use `Token::test()` instead
|
||||
* Add `Token::toEnglish()`
|
||||
* Add `ForElseNode`
|
||||
* Deprecate `Twig\ExpressionParser::parseOnlyArguments()` and
|
||||
`Twig\ExpressionParser::parseArguments()` (use
|
||||
`Twig\ExpressionParser::parseNamedArguments()` instead)
|
||||
* Fix `constant()` behavior when used with `??`
|
||||
* Add the `invoke` filter
|
||||
* Make `{}` optional for the `types` tag
|
||||
* Add `LastModifiedExtensionInterface` and implementation in `AbstractExtension` to track modification of runtime classes
|
||||
* Ignore static properties when using the dot operator
|
||||
|
||||
# 3.18.0 (2024-12-29)
|
||||
|
||||
* Fix unary operator precedence change
|
||||
* Ignore `SyntaxError` exceptions from undefined handlers when using the `guard` tag
|
||||
* Add a way to stream template rendering (`TemplateWrapper::stream()` and `TemplateWrapper::streamBlock()`)
|
||||
|
||||
# 3.17.1 (2024-12-12)
|
||||
|
||||
* Fix the null coalescing operator when the test returns null
|
||||
* Fix the Elvis operator when used as '? :' instead of '?:'
|
||||
* Support for invoking closures
|
||||
|
||||
# 3.17.0 (2024-12-10)
|
||||
|
||||
* Fix ArrayAccess with objects as keys
|
||||
* Support underscores in number literals
|
||||
* Deprecate `ConditionalExpression` and `NullCoalesceExpression` (use `ConditionalTernary` and `NullCoalesceBinary` instead)
|
||||
|
||||
# 3.16.0 (2024-11-29)
|
||||
|
||||
* Deprecate `InlinePrint`
|
||||
* Fix having macro variables starting with an underscore
|
||||
* Deprecate not passing a `Source` instance to `TokenStream`
|
||||
* Deprecate returning `null` from `TwigFilter::getSafe()` and `TwigFunction::getSafe()`, return `[]` instead
|
||||
|
||||
# 3.15.0 (2024-11-17)
|
||||
|
||||
* [BC BREAK] Add support for accessing class constants with the dot operator;
|
||||
this can be a BC break if you don't use UPPERCASE constant names
|
||||
* Add Spanish inflector support for the `plural` and `singular` filters in the String extension
|
||||
* Deprecate `TempNameExpression` in favor of `LocalVariable`
|
||||
* Deprecate `NameExpression` in favor of `ContextVariable`
|
||||
* Deprecate `AssignNameExpression` in favor of `AssignContextVariable`
|
||||
* Remove `MacroAutoImportNodeVisitor`
|
||||
* Deprecate `MethodCallExpression` in favor of `MacroReferenceExpression`
|
||||
* Fix support for the "is defined" test on `_self.xxx` (auto-imported) macros
|
||||
* Fix support for the "is defined" test on inherited macros
|
||||
* Add named arguments support for the dot operator arguments (`foo.bar(some: arg)`)
|
||||
* Add named arguments support for macros
|
||||
* Add a new `guard` tag that allows to test if some Twig callables are available at compilation time
|
||||
* Allow arrow functions everywhere
|
||||
* Deprecate passing a string or an array to Twig callable arguments accepting arrow functions (pass a `\Closure`)
|
||||
* Add support for triggering deprecations for future operator precedence changes
|
||||
* Deprecate using the `not` unary operator in an expression with ``*``, ``/``, ``//``, or ``%`` without using explicit parentheses to clarify precedence
|
||||
* Deprecate using the `??` binary operator without explicit parentheses
|
||||
* Deprecate using the `~` binary operator in an expression with `+` or `-` without using parentheses to clarify precedence
|
||||
* Deprecate not passing `AbstractExpression` args to most constructor arguments for classes extending `AbstractExpression`
|
||||
* Fix `power` expressions with a negative number in parenthesis (`(-1) ** 2`)
|
||||
* Deprecate instantiating `Node` directly. Use `EmptyNode` or `Nodes` instead.
|
||||
* Add support for inline comments
|
||||
* Add `Profile::getStartTime()` and `Profile::getEndTime()`
|
||||
* Fix "ignore missing" when used on an "embed" tag
|
||||
* Fix the possibility to override an aliased block (via use)
|
||||
* Add template cache hot reload
|
||||
* Allow Twig callable argument names to be free-form (snake-case or camelCase) independently of the PHP callable signature
|
||||
They were automatically converted to snake-cased before
|
||||
* Deprecate the `attribute` function; use the `.` notation and wrap the name with parenthesis instead
|
||||
* Add support for argument unpackaging
|
||||
* Add JSON support for the file extension escaping strategy
|
||||
* Support Markup instances (and any other \Stringable) as dynamic mapping keys
|
||||
* Deprecate the `sandbox` tag
|
||||
* Improve the way one can deprecate a Twig callable (use `deprecation_info` instead of the other callable options)
|
||||
* Add the `enum` function
|
||||
* Add support for logical `xor` operator
|
||||
|
||||
# 3.14.2 (2024-11-07)
|
||||
|
||||
* Fix an infinite recursion in the sandbox code
|
||||
|
||||
# 3.14.1 (2024-11-06)
|
||||
|
||||
* [BC BREAK] Fix a security issue in the sandbox mode allowing an attacker to call attributes on Array-like objects
|
||||
They are now checked via the property policy
|
||||
* Fix a security issue in the sandbox mode allowing an attacker to be able to call `toString()`
|
||||
under some circumstances on an object even if the `__toString()` method is not allowed by the security policy
|
||||
|
||||
# 3.14.0 (2024-09-09)
|
||||
|
||||
* Fix a security issue when an included sandboxed template has been loaded before without the sandbox context
|
||||
* Add the possibility to reset globals via `Environment::resetGlobals()`
|
||||
* Deprecate `Environment::mergeGlobals()`
|
||||
|
||||
# 3.13.0 (2024-09-07)
|
||||
|
||||
* Add the `types` tag (experimental)
|
||||
* Deprecate the `Twig\Test\NodeTestCase::getTests()` data provider, override `provideTests()` instead.
|
||||
* Mark `Twig\Test\NodeTestCase::getEnvironment()` as final, override `createEnvironment()` instead.
|
||||
* Deprecate `Twig\Test\NodeTestCase::getVariableGetter()`, call `createVariableGetter()` instead.
|
||||
* Deprecate `Twig\Test\NodeTestCase::getAttributeGetter()`, call `createAttributeGetter()` instead.
|
||||
* Deprecate not overriding `Twig\Test\IntegrationTestCase::getFixturesDirectory()`, this method will be abstract in 4.0
|
||||
* Marked `Twig\Test\IntegrationTestCase::getTests()` and `getLegacyTests()` as final
|
||||
|
||||
# 3.12.0 (2024-08-29)
|
||||
|
||||
* Deprecate the fact that the `extends` and `use` tags are always allowed in a sandboxed template.
|
||||
This behavior will change in 4.0 where these tags will need to be explicitly allowed like any other tag.
|
||||
* Deprecate the "tag" constructor argument of the "Twig\Node\Node" class as the tag is now automatically set by the Parser when needed
|
||||
* Fix precedence of two-word tests when the first word is a valid test
|
||||
* Deprecate the `spaceless` filter
|
||||
* Deprecate some internal methods from `Parser`: `getBlockStack()`, `hasBlock()`, `getBlock()`, `hasMacro()`, `hasTraits()`, `getParent()`
|
||||
* Deprecate passing `null` to `Twig\Parser::setParent()`
|
||||
* Update `Node::__toString()` to include the node tag if set
|
||||
* Add support for integers in methods of `Twig\Node\Node` that take a Node name
|
||||
* Deprecate not passing a `BodyNode` instance as the body of a `ModuleNode` or `MacroNode` constructor
|
||||
* Deprecate returning "null" from "TokenParserInterface::parse()".
|
||||
* Deprecate `OptimizerNodeVisitor::OPTIMIZE_TEXT_NODES`
|
||||
* Fix performance regression when `use_yield` is `false` (which is the default)
|
||||
* Improve compatibility when `use_yield` is `false` (as extensions still using `echo` will work as is)
|
||||
* Accept colons (`:`) in addition to equals (`=`) to separate argument names and values in named arguments
|
||||
* Add the `html_cva` function (in the HTML extra package)
|
||||
* Add support for named arguments to the `block` and `attribute` functions
|
||||
* Throw a SyntaxError exception at compile time when a Twig callable has not the minimum number of required arguments
|
||||
* Add a `CallableArgumentsExtractor` class
|
||||
* Deprecate passing a name to `FunctionExpression`, `FilterExpression`, and `TestExpression`;
|
||||
pass a `TwigFunction`, `TwigFilter`, or `TestFilter` instead
|
||||
* Deprecate all Twig callable attributes on `FunctionExpression`, `FilterExpression`, and `TestExpression`
|
||||
* Deprecate the `filter` node of `FilterExpression`
|
||||
* Add the notion of Twig callables (functions, filters, and tests)
|
||||
* Bump minimum PHP version to 8.0
|
||||
* Fix integration tests when a test has more than one data/expect section and deprecations
|
||||
* Add the `enum_cases` function
|
||||
|
||||
# 3.11.2 (2024-11-06)
|
||||
|
||||
* [BC BREAK] Fix a security issue in the sandbox mode allowing an attacker to call attributes on Array-like objects
|
||||
They are now checked via the property policy
|
||||
* Fix a security issue in the sandbox mode allowing an attacker to be able to call `toString()`
|
||||
under some circumstances on an object even if the `__toString()` method is not allowed by the security policy
|
||||
|
||||
# 3.11.1 (2024-09-10)
|
||||
|
||||
* Fix a security issue when an included sandboxed template has been loaded before without the sandbox context
|
||||
|
||||
# 3.11.0 (2024-08-08)
|
||||
|
||||
* Deprecate `OptimizerNodeVisitor::OPTIMIZE_RAW_FILTER`
|
||||
* Add `Twig\Cache\ChainCache` and `Twig\Cache\ReadOnlyFilesystemCache`
|
||||
* Add the possibility to deprecate attributes and nodes on `Node`
|
||||
* Add the possibility to add a package and a version to the `deprecated` tag
|
||||
* Add the possibility to add a package for filter/function/test deprecations
|
||||
* Mark `ConstantExpression` as being `@final`
|
||||
* Add the `find` filter
|
||||
* Fix optimizer mode validation in `OptimizerNodeVisitor`
|
||||
* Add the possibility to yield from a generator in `PrintNode`
|
||||
* Add the `shuffle` filter
|
||||
* Add the `singular` and `plural` filters in `StringExtension`
|
||||
* Deprecate the second argument of `Twig\Node\Expression\CallExpression::compileArguments()`
|
||||
* Deprecate `Twig\ExpressionParser\parseHashExpression()` in favor of
|
||||
`Twig\ExpressionParser::parseMappingExpression()`
|
||||
* Deprecate `Twig\ExpressionParser\parseArrayExpression()` in favor of
|
||||
`Twig\ExpressionParser::parseSequenceExpression()`
|
||||
* Add `sequence` and `mapping` tests
|
||||
* Deprecate `Twig\Node\Expression\NameExpression::isSimple()` and
|
||||
`Twig\Node\Expression\NameExpression::isSpecial()`
|
||||
|
||||
# 3.10.3 (2024-05-16)
|
||||
|
||||
* Fix missing ; in generated code
|
||||
|
||||
# 3.10.2 (2024-05-14)
|
||||
|
||||
* Fix support for the deprecated escaper signature
|
||||
|
||||
# 3.10.1 (2024-05-12)
|
||||
|
||||
* Fix BC break on escaper extension
|
||||
* Fix constant return type
|
||||
|
||||
# 3.10.0 (2024-05-11)
|
||||
|
||||
* Make `CoreExtension::formatDate`, `CoreExtension::convertDate`, and
|
||||
`CoreExtension::formatNumber` part of the public API
|
||||
* Add `needs_charset` option for filters and functions
|
||||
* Extract the escaping logic from the `EscaperExtension` class to a new
|
||||
`EscaperRuntime` class.
|
||||
|
||||
The following methods from ``Twig\\Extension\\EscaperExtension`` are
|
||||
deprecated: ``setEscaper()``, ``getEscapers()``, ``setSafeClasses``,
|
||||
``addSafeClasses()``. Use the same methods on the
|
||||
``Twig\\Runtime\\EscaperRuntime`` class instead.
|
||||
* Fix capturing output from extensions that still use echo
|
||||
* Fix a PHP warning in the Lexer on malformed templates
|
||||
* Fix blocks not available under some circumstances
|
||||
* Synchronize source context in templates when setting a Node on a Node
|
||||
|
||||
# 3.9.3 (2024-04-18)
|
||||
|
||||
* Add missing `twig_escape_filter_is_safe` deprecated function
|
||||
* Fix yield usage with CaptureNode
|
||||
* Add missing unwrap call when using a TemplateWrapper instance internally
|
||||
* Ensure Lexer is initialized early on
|
||||
|
||||
# 3.9.2 (2024-04-17)
|
||||
|
||||
* Fix usage of display_end hook
|
||||
|
||||
# 3.9.1 (2024-04-17)
|
||||
|
||||
* Fix missing `$blocks` variable in `CaptureNode`
|
||||
|
||||
# 3.9.0 (2024-04-16)
|
||||
|
||||
* Add support for PHP 8.4
|
||||
* Deprecate AbstractNodeVisitor
|
||||
* Deprecate passing Template to Environment::resolveTemplate(), Environment::load(), and Template::loadTemplate()
|
||||
* Add a new "yield" mode for output generation;
|
||||
Node implementations that use "echo" or "print" should use "yield" instead;
|
||||
all Node implementations should be flagged with `#[YieldReady]` once they've been made ready for "yield";
|
||||
the "use_yield" Environment option can be turned on when all nodes have been made `#[YieldReady]`;
|
||||
"yield" will be the only strategy supported in the next major version
|
||||
* Add return type for Symfony 7 compatibility
|
||||
* Fix premature loop exit in Security Policy lookup of allowed methods/properties
|
||||
* Deprecate all internal extension functions in favor of methods on the extension classes
|
||||
* Mark all extension functions as @internal
|
||||
* Add SourcePolicyInterface to selectively enable the Sandbox based on a template's Source
|
||||
* Throw a proper Twig exception when using cycle on an empty array
|
||||
|
||||
# 3.8.0 (2023-11-21)
|
||||
|
||||
* Catch errors thrown during template rendering
|
||||
* Fix IntlExtension::formatDateTime use of date formatter prototype
|
||||
* Fix premature loop exit in Security Policy lookup of allowed methods/properties
|
||||
* Remove NumberFormatter::TYPE_CURRENCY (deprecated in PHP 8.3)
|
||||
* Restore return type annotations
|
||||
* Allow Symfony 7 packages to be installed
|
||||
* Deprecate `twig_test_iterable` function. Use the native `is_iterable` instead.
|
||||
|
||||
# 3.7.1 (2023-08-28)
|
||||
|
||||
* Fix some phpdocs
|
||||
|
||||
# 3.7.0 (2023-07-26)
|
||||
|
||||
* Add support for the ...spread operator on arrays and hashes
|
||||
|
||||
# 3.6.1 (2023-06-08)
|
||||
|
||||
* Suppress some native return type deprecation messages
|
||||
|
||||
# 3.6.0 (2023-05-03)
|
||||
|
||||
* Allow psr/container 2.0
|
||||
* Add the new PHP 8.0 IntlDateFormatter::RELATIVE_* constants for date formatting
|
||||
* Make the Lexer initialize itself lazily
|
||||
|
||||
# 3.5.1 (2023-02-08)
|
||||
|
||||
* Arrow functions passed to the "reduce" filter now accept the current key as a third argument
|
||||
* Restores the leniency of the matches twig comparison
|
||||
* Fix error messages in sandboxed mode for "has some" and "has every"
|
||||
|
||||
# 3.5.0 (2022-12-27)
|
||||
|
||||
* Make Twig\ExpressionParser non-internal
|
||||
* Add "has some" and "has every" operators
|
||||
* Add Compile::reset()
|
||||
* Throw a better runtime error when the "matches" regexp is not valid
|
||||
* Add "twig *_names" intl functions
|
||||
* Fix optimizing closures callbacks
|
||||
* Add a better exception when getting an undefined constant via `constant`
|
||||
* Fix `if` nodes when outside of a block and with an empty body
|
||||
|
||||
# 3.4.3 (2022-09-28)
|
||||
|
||||
* Fix a security issue on filesystem loader (possibility to load a template outside a configured directory)
|
||||
|
||||
# 3.4.2 (2022-08-12)
|
||||
|
||||
* Allow inherited magic method to still run with calling class
|
||||
* Fix CallExpression::reflectCallable() throwing TypeError
|
||||
* Fix typo in naming (currency_code)
|
||||
|
||||
# 3.4.1 (2022-05-17)
|
||||
|
||||
* Fix optimizing non-public named closures
|
||||
|
||||
# 3.4.0 (2022-05-22)
|
||||
|
||||
* Add support for named closures
|
||||
|
||||
# 3.3.10 (2022-04-06)
|
||||
|
||||
* Enable bytecode invalidation when auto_reload is enabled
|
||||
|
||||
# 3.3.9 (2022-03-25)
|
||||
|
||||
* Fix custom escapers when using multiple Twig environments
|
||||
* Add support for "constant('class', object)"
|
||||
* Do not reuse internally generated variable names during parsing
|
||||
|
||||
# 3.3.8 (2022-02-04)
|
||||
|
||||
* Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter
|
||||
* Fix deprecation notice on `round`
|
||||
* Fix call to deprecated `convertToHtml` method
|
||||
|
||||
# 3.3.7 (2022-01-03)
|
||||
|
||||
* Allow more null support when Twig expects a string (for better 8.1 support)
|
||||
* Only use Commonmark extensions if markdown enabled
|
||||
|
||||
# 3.3.6 (2022-01-03)
|
||||
|
||||
* Only use Commonmark extensions if markdown enabled
|
||||
|
||||
# 3.3.5 (2022-01-03)
|
||||
|
||||
* Allow CommonMark extensions to easily be added
|
||||
* Allow null when Twig expects a string (for better 8.1 support)
|
||||
* Make some performance optimizations
|
||||
* Allow Symfony translation contract v3+
|
||||
|
||||
# 3.3.4 (2021-11-25)
|
||||
|
||||
* Bump minimum supported Symfony component versions
|
||||
* Fix a deprecated message
|
||||
|
||||
# 3.3.3 (2021-09-17)
|
||||
|
||||
* Allow Symfony 6
|
||||
* Improve compatibility with PHP 8.1
|
||||
* Explicitly specify the encoding for mb_ord in JS escaper
|
||||
|
||||
# 3.3.2 (2021-05-16)
|
||||
|
||||
* Revert "Throw a proper exception when a template name is an absolute path (as it has never been supported)"
|
||||
|
||||
# 3.3.1 (2021-05-12)
|
||||
|
||||
* Fix PHP 8.1 compatibility
|
||||
* Throw a proper exception when a template name is an absolute path (as it has never been supported)
|
||||
|
||||
# 3.3.0 (2021-02-08)
|
||||
|
||||
* Fix macro calls in a "cache" tag
|
||||
* Add the slug filter
|
||||
* Allow extra bundle to be compatible with Twig 2
|
||||
|
||||
# 3.2.1 (2021-01-05)
|
||||
|
||||
* Fix extra bundle compat with older versions of Symfony
|
||||
|
||||
# 3.2.0 (2021-01-05)
|
||||
|
||||
* Add the Cache extension in the "extra" repositories: "cache" tag
|
||||
* Add "registerUndefinedTokenParserCallback"
|
||||
* Mark built-in node visitors as @internal
|
||||
* Fix "odd" not working for negative numbers
|
||||
|
||||
# 3.1.1 (2020-10-27)
|
||||
|
||||
* Fix "include(template_from_string())"
|
||||
|
||||
# 3.1.0 (2020-10-21)
|
||||
|
||||
* Fix sandbox support when using "include(template_from_string())"
|
||||
* Make round brackets optional for one argument tests like "same as" or "divisible by"
|
||||
* Add support for ES2015 style object initialisation shortcut { a } is the same as { 'a': a }
|
||||
|
||||
# 3.0.5 (2020-08-05)
|
||||
|
||||
* Fix twig_compare w.r.t. whitespace trimming
|
||||
* Fix sandbox not disabled if syntax error occurs within {% sandbox %} tag
|
||||
* Fix a regression when not using a space before an operator
|
||||
* Restrict callables to closures in filters
|
||||
* Allow trailing commas in argument lists (in calls as well as definitions)
|
||||
|
||||
# 3.0.4 (2020-07-05)
|
||||
|
||||
* Fix comparison operators
|
||||
* Fix options not taken into account when using "Michelf\MarkdownExtra"
|
||||
* Fix "Twig\Extra\Intl\IntlExtension::getCountryName()" to accept "null" as a first argument
|
||||
* Throw exception in case non-Traversable data is passed to "filter"
|
||||
* Fix context optimization on PHP 7.4
|
||||
* Fix PHP 8 compatibility
|
||||
* Fix ambiguous syntax parsing
|
||||
|
||||
# 3.0.3 (2020-02-11)
|
||||
|
||||
* Add a check to ensure that iconv() is defined
|
||||
|
||||
# 3.0.2 (2020-02-11)
|
||||
|
||||
* Avoid exceptions when an intl resource is not found
|
||||
* Fix implementation of case-insensitivity for method names
|
||||
|
||||
# 3.0.1 (2019-12-28)
|
||||
|
||||
* fixed Symfony 5.0 support for the HTML extra extension
|
||||
|
||||
# 3.0.0 (2019-11-15)
|
||||
|
||||
* fixed number formatter in Intl extra extension when using a formatter prototype
|
||||
|
||||
# 3.0.0-BETA1 (2019-11-11)
|
||||
|
||||
* removed the "if" condition support on the "for" tag
|
||||
* made the in, <, >, <=, >=, ==, and != operators more strict when comparing strings and integers/floats
|
||||
* removed the "filter" tag
|
||||
* added type hints everywhere
|
||||
* changed Environment::resolveTemplate() to always return a TemplateWrapper instance
|
||||
* removed Template::__toString()
|
||||
* removed Parser::isReservedMacroName()
|
||||
* removed SanboxedPrintNode
|
||||
* removed Node::setTemplateName()
|
||||
* made classes marked as "@final" final
|
||||
* removed InitRuntimeInterface, ExistsLoaderInterface, and SourceContextLoaderInterface
|
||||
* removed the "spaceless" tag
|
||||
* removed Twig\Environment::getBaseTemplateClass() and Twig\Environment::setBaseTemplateClass()
|
||||
* removed the "base_template_class" option on Twig\Environment
|
||||
* bumped minimum PHP version to 7.2
|
||||
* removed PSR-0 classes
|
||||
27
vendor/twig/twig/LICENSE
vendored
Normal file
27
vendor/twig/twig/LICENSE
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009-present by the Twig Team.
|
||||
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without modification,
|
||||
are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright notice,
|
||||
this list of conditions and the following disclaimer in the documentation
|
||||
and/or other materials provided with the distribution.
|
||||
* Neither the name of Twig nor the names of its contributors
|
||||
may be used to endorse or promote products derived from this software
|
||||
without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
|
||||
CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
|
||||
EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
|
||||
PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
|
||||
LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
|
||||
NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
23
vendor/twig/twig/README.rst
vendored
Normal file
23
vendor/twig/twig/README.rst
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
Twig, the flexible, fast, and secure template language for PHP
|
||||
==============================================================
|
||||
|
||||
Twig is a template language for PHP.
|
||||
|
||||
Twig uses a syntax similar to the Django and Jinja template languages which
|
||||
inspired the Twig runtime environment.
|
||||
|
||||
Sponsors
|
||||
--------
|
||||
|
||||
.. raw:: html
|
||||
|
||||
<a href="https://docs.blackfire.io/introduction?utm_source=twig&utm_medium=github_readme&utm_campaign=logo">
|
||||
<img src="https://static.blackfire.io/assets/intemporals/logo/png/blackfire-io_secondary_horizontal_transparent.png?1" width="255px" alt="Blackfire.io">
|
||||
</a>
|
||||
|
||||
More Information
|
||||
----------------
|
||||
|
||||
Read the `documentation`_ for more information.
|
||||
|
||||
.. _documentation: https://twig.symfony.com/documentation
|
||||
53
vendor/twig/twig/composer.json
vendored
Normal file
53
vendor/twig/twig/composer.json
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
{
|
||||
"name": "twig/twig",
|
||||
"type": "library",
|
||||
"description": "Twig, the flexible, fast, and secure template language for PHP",
|
||||
"keywords": ["templating"],
|
||||
"homepage": "https://twig.symfony.com",
|
||||
"license": "BSD-3-Clause",
|
||||
"minimum-stability": "dev",
|
||||
"authors": [
|
||||
{
|
||||
"name": "Fabien Potencier",
|
||||
"email": "fabien@symfony.com",
|
||||
"homepage": "http://fabien.potencier.org",
|
||||
"role": "Lead Developer"
|
||||
},
|
||||
{
|
||||
"name": "Twig Team",
|
||||
"role": "Contributors"
|
||||
},
|
||||
{
|
||||
"name": "Armin Ronacher",
|
||||
"email": "armin.ronacher@active-4.com",
|
||||
"role": "Project Founder"
|
||||
}
|
||||
],
|
||||
"require": {
|
||||
"php": ">=8.1.0",
|
||||
"symfony/deprecation-contracts": "^2.5|^3",
|
||||
"symfony/polyfill-mbstring": "^1.3",
|
||||
"symfony/polyfill-ctype": "^1.8"
|
||||
},
|
||||
"require-dev": {
|
||||
"symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0",
|
||||
"psr/container": "^1.0|^2.0",
|
||||
"phpstan/phpstan": "^2.0"
|
||||
},
|
||||
"autoload": {
|
||||
"files": [
|
||||
"src/Resources/core.php",
|
||||
"src/Resources/debug.php",
|
||||
"src/Resources/escaper.php",
|
||||
"src/Resources/string_loader.php"
|
||||
],
|
||||
"psr-4" : {
|
||||
"Twig\\" : "src/"
|
||||
}
|
||||
},
|
||||
"autoload-dev": {
|
||||
"psr-4" : {
|
||||
"Twig\\Tests\\" : "tests/"
|
||||
}
|
||||
}
|
||||
}
|
||||
25
vendor/twig/twig/phpstan-baseline.neon
vendored
Normal file
25
vendor/twig/twig/phpstan-baseline.neon
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
parameters:
|
||||
ignoreErrors:
|
||||
- # The method is dynamically generated by the CheckSecurityNode
|
||||
message: '#^Call to an undefined method Twig\\Template\:\:checkSecurity\(\)\.$#'
|
||||
identifier: method.notFound
|
||||
count: 1
|
||||
path: src/Extension/CoreExtension.php
|
||||
|
||||
- # 2 parameters will be required
|
||||
message: '#^Method Twig\\Node\\IncludeNode\:\:addGetTemplate\(\) invoked with 2 parameters, 1 required\.$#'
|
||||
identifier: arguments.count
|
||||
count: 1
|
||||
path: src/Node/IncludeNode.php
|
||||
|
||||
- # int|string will be supported in 4.x
|
||||
message: '#^PHPDoc tag @param for parameter $name with type int|string is not subtype of native type string\.$#'
|
||||
identifier: parameter.phpDocType
|
||||
count: 5
|
||||
path: src/Node/Node.php
|
||||
|
||||
- # Adding 0 to the string representation of a number is valid and what we want here
|
||||
message: '#^Binary operation "\+" between 0 and string results in an error\.$#'
|
||||
identifier: binaryOp.invalid
|
||||
count: 1
|
||||
path: src/Lexer.php
|
||||
9
vendor/twig/twig/phpstan.neon.dist
vendored
Normal file
9
vendor/twig/twig/phpstan.neon.dist
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
includes:
|
||||
- phpstan-baseline.neon
|
||||
|
||||
parameters:
|
||||
level: 3
|
||||
paths:
|
||||
- src
|
||||
excludePaths:
|
||||
- src/Test
|
||||
187
vendor/twig/twig/src/AbstractTwigCallable.php
vendored
Normal file
187
vendor/twig/twig/src/AbstractTwigCallable.php
vendored
Normal file
@ -0,0 +1,187 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractTwigCallable implements TwigCallableInterface
|
||||
{
|
||||
protected $options;
|
||||
|
||||
private $name;
|
||||
private $dynamicName;
|
||||
private $callable;
|
||||
private $arguments;
|
||||
|
||||
public function __construct(string $name, $callable = null, array $options = [])
|
||||
{
|
||||
$this->name = $this->dynamicName = $name;
|
||||
$this->callable = $callable;
|
||||
$this->arguments = [];
|
||||
$this->options = array_merge([
|
||||
'needs_environment' => false,
|
||||
'needs_context' => false,
|
||||
'needs_charset' => false,
|
||||
'is_variadic' => false,
|
||||
'deprecation_info' => null,
|
||||
'deprecated' => false,
|
||||
'deprecating_package' => '',
|
||||
'alternative' => null,
|
||||
], $options);
|
||||
|
||||
if ($this->options['deprecation_info'] && !$this->options['deprecation_info'] instanceof DeprecatedCallableInfo) {
|
||||
throw new \LogicException(\sprintf('The "deprecation_info" option must be an instance of "%s".', DeprecatedCallableInfo::class));
|
||||
}
|
||||
|
||||
if ($this->options['deprecated']) {
|
||||
if ($this->options['deprecation_info']) {
|
||||
throw new \LogicException('When setting the "deprecation_info" option, you need to remove the obsolete deprecated options.');
|
||||
}
|
||||
|
||||
trigger_deprecation('twig/twig', '3.15', 'Using the "deprecated", "deprecating_package", and "alternative" options is deprecated, pass a "deprecation_info" one instead.');
|
||||
|
||||
$this->options['deprecation_info'] = new DeprecatedCallableInfo(
|
||||
$this->options['deprecating_package'],
|
||||
$this->options['deprecated'],
|
||||
null,
|
||||
$this->options['alternative'],
|
||||
);
|
||||
}
|
||||
|
||||
if ($this->options['deprecation_info']) {
|
||||
$this->options['deprecation_info']->setName($name);
|
||||
$this->options['deprecation_info']->setType($this->getType());
|
||||
}
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return \sprintf('%s(%s)', static::class, $this->name);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getDynamicName(): string
|
||||
{
|
||||
return $this->dynamicName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return callable|array{class-string, string}|null
|
||||
*/
|
||||
public function getCallable()
|
||||
{
|
||||
return $this->callable;
|
||||
}
|
||||
|
||||
public function getNodeClass(): string
|
||||
{
|
||||
return $this->options['node_class'];
|
||||
}
|
||||
|
||||
public function needsCharset(): bool
|
||||
{
|
||||
return $this->options['needs_charset'];
|
||||
}
|
||||
|
||||
public function needsEnvironment(): bool
|
||||
{
|
||||
return $this->options['needs_environment'];
|
||||
}
|
||||
|
||||
public function needsContext(): bool
|
||||
{
|
||||
return $this->options['needs_context'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function withDynamicArguments(string $name, string $dynamicName, array $arguments): self
|
||||
{
|
||||
$new = clone $this;
|
||||
$new->name = $name;
|
||||
$new->dynamicName = $dynamicName;
|
||||
$new->arguments = $arguments;
|
||||
|
||||
return $new;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.12, use withDynamicArguments() instead
|
||||
*/
|
||||
public function setArguments(array $arguments): void
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.12', 'The "%s::setArguments()" method is deprecated, use "%s::withDynamicArguments()" instead.', static::class, static::class);
|
||||
|
||||
$this->arguments = $arguments;
|
||||
}
|
||||
|
||||
public function getArguments(): array
|
||||
{
|
||||
return $this->arguments;
|
||||
}
|
||||
|
||||
public function isVariadic(): bool
|
||||
{
|
||||
return $this->options['is_variadic'];
|
||||
}
|
||||
|
||||
public function isDeprecated(): bool
|
||||
{
|
||||
return (bool) $this->options['deprecation_info'];
|
||||
}
|
||||
|
||||
public function triggerDeprecation(?string $file = null, ?int $line = null): void
|
||||
{
|
||||
$this->options['deprecation_info']->triggerDeprecation($file, $line);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.15
|
||||
*/
|
||||
public function getDeprecatingPackage(): string
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class);
|
||||
|
||||
return $this->options['deprecating_package'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.15
|
||||
*/
|
||||
public function getDeprecatedVersion(): string
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class);
|
||||
|
||||
return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated'];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.15
|
||||
*/
|
||||
public function getAlternative(): ?string
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class);
|
||||
|
||||
return $this->options['alternative'];
|
||||
}
|
||||
|
||||
public function getMinimalNumberOfRequiredArguments(): int
|
||||
{
|
||||
return ($this->options['needs_charset'] ? 1 : 0) + ($this->options['needs_environment'] ? 1 : 0) + ($this->options['needs_context'] ? 1 : 0) + \count($this->arguments);
|
||||
}
|
||||
}
|
||||
56
vendor/twig/twig/src/Attribute/AsTwigFilter.php
vendored
Normal file
56
vendor/twig/twig/src/Attribute/AsTwigFilter.php
vendored
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Attribute;
|
||||
|
||||
use Twig\DeprecatedCallableInfo;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
/**
|
||||
* Registers a method as template filter.
|
||||
*
|
||||
* If the first argument of the method has Twig\Environment type-hint, the filter will receive the current environment.
|
||||
* Additional arguments of the method come from the filter call.
|
||||
*
|
||||
* #[AsTwigFilter(name: 'foo')]
|
||||
* function fooFilter(Environment $env, $string, $arg1 = null, ...) { ... }
|
||||
*
|
||||
* {{ 'string'|foo(arg1) }}
|
||||
*
|
||||
* @see TwigFilter
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
final class AsTwigFilter
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $name The name of the filter in Twig.
|
||||
* @param bool|null $needsCharset Whether the filter needs the charset passed as the first argument.
|
||||
* @param bool|null $needsEnvironment Whether the filter needs the environment passed as the first argument, or after the charset.
|
||||
* @param bool|null $needsContext Whether the filter needs the context array passed as the first argument, or after the charset and the environment.
|
||||
* @param string[]|null $isSafe List of formats in which you want the raw output to be printed unescaped.
|
||||
* @param string|array|null $isSafeCallback Function called at compilation time to determine if the filter is safe.
|
||||
* @param string|null $preEscape Some filters may need to work on input that is already escaped or safe
|
||||
* @param string[]|null $preservesSafety Preserves the safety of the value that the filter is applied to.
|
||||
* @param DeprecatedCallableInfo|null $deprecationInfo Information about the deprecation
|
||||
*/
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public ?bool $needsCharset = null,
|
||||
public ?bool $needsEnvironment = null,
|
||||
public ?bool $needsContext = null,
|
||||
public ?array $isSafe = null,
|
||||
public string|array|null $isSafeCallback = null,
|
||||
public ?string $preEscape = null,
|
||||
public ?array $preservesSafety = null,
|
||||
public ?DeprecatedCallableInfo $deprecationInfo = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
52
vendor/twig/twig/src/Attribute/AsTwigFunction.php
vendored
Normal file
52
vendor/twig/twig/src/Attribute/AsTwigFunction.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Attribute;
|
||||
|
||||
use Twig\DeprecatedCallableInfo;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
/**
|
||||
* Registers a method as template function.
|
||||
*
|
||||
* If the first argument of the method has Twig\Environment type-hint, the function will receive the current environment.
|
||||
* Additional arguments of the method come from the function call.
|
||||
*
|
||||
* #[AsTwigFunction(name: 'foo')]
|
||||
* function fooFunction(Environment $env, string $string, $arg1 = null, ...) { ... }
|
||||
*
|
||||
* {{ foo('string', arg1) }}
|
||||
*
|
||||
* @see TwigFunction
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
final class AsTwigFunction
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $name The name of the function in Twig.
|
||||
* @param bool|null $needsCharset Whether the function needs the charset passed as the first argument.
|
||||
* @param bool|null $needsEnvironment Whether the function needs the environment passed as the first argument, or after the charset.
|
||||
* @param bool|null $needsContext Whether the function needs the context array passed as the first argument, or after the charset and the environment.
|
||||
* @param string[]|null $isSafe List of formats in which you want the raw output to be printed unescaped.
|
||||
* @param string|array|null $isSafeCallback Function called at compilation time to determine if the function is safe.
|
||||
* @param DeprecatedCallableInfo|null $deprecationInfo Information about the deprecation
|
||||
*/
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public ?bool $needsCharset = null,
|
||||
public ?bool $needsEnvironment = null,
|
||||
public ?bool $needsContext = null,
|
||||
public ?array $isSafe = null,
|
||||
public string|array|null $isSafeCallback = null,
|
||||
public ?DeprecatedCallableInfo $deprecationInfo = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
48
vendor/twig/twig/src/Attribute/AsTwigTest.php
vendored
Normal file
48
vendor/twig/twig/src/Attribute/AsTwigTest.php
vendored
Normal file
@ -0,0 +1,48 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Attribute;
|
||||
|
||||
use Twig\DeprecatedCallableInfo;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* Registers a method as template test.
|
||||
*
|
||||
* The first argument is the value to test and the other arguments are the
|
||||
* arguments passed to the test in the template.
|
||||
*
|
||||
* #[AsTwigTest(name: 'foo')]
|
||||
* public function fooTest($value, $arg1 = null) { ... }
|
||||
*
|
||||
* {% if value is foo(arg1) %}
|
||||
*
|
||||
* @see TwigTest
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD | \Attribute::IS_REPEATABLE)]
|
||||
final class AsTwigTest
|
||||
{
|
||||
/**
|
||||
* @param non-empty-string $name The name of the test in Twig.
|
||||
* @param bool|null $needsCharset Whether the test needs the charset passed as the first argument.
|
||||
* @param bool|null $needsEnvironment Whether the test needs the environment passed as the first argument, or after the charset.
|
||||
* @param bool|null $needsContext Whether the test needs the context array passed as the first argument, or after the charset and the environment.
|
||||
* @param DeprecatedCallableInfo|null $deprecationInfo Information about the deprecation
|
||||
*/
|
||||
public function __construct(
|
||||
public string $name,
|
||||
public ?bool $needsCharset = null,
|
||||
public ?bool $needsEnvironment = null,
|
||||
public ?bool $needsContext = null,
|
||||
public ?DeprecatedCallableInfo $deprecationInfo = null,
|
||||
) {
|
||||
}
|
||||
}
|
||||
20
vendor/twig/twig/src/Attribute/FirstClassTwigCallableReady.php
vendored
Normal file
20
vendor/twig/twig/src/Attribute/FirstClassTwigCallableReady.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Attribute;
|
||||
|
||||
/**
|
||||
* Marks nodes that are ready to accept a TwigCallable instead of its name.
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_METHOD)]
|
||||
final class FirstClassTwigCallableReady
|
||||
{
|
||||
}
|
||||
20
vendor/twig/twig/src/Attribute/YieldReady.php
vendored
Normal file
20
vendor/twig/twig/src/Attribute/YieldReady.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Attribute;
|
||||
|
||||
/**
|
||||
* Marks nodes that are ready for using "yield" instead of "echo" or "print()" for rendering.
|
||||
*/
|
||||
#[\Attribute(\Attribute::TARGET_CLASS)]
|
||||
final class YieldReady
|
||||
{
|
||||
}
|
||||
46
vendor/twig/twig/src/Cache/CacheInterface.php
vendored
Normal file
46
vendor/twig/twig/src/Cache/CacheInterface.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Cache;
|
||||
|
||||
/**
|
||||
* Interface implemented by cache classes.
|
||||
*
|
||||
* It is highly recommended to always store templates on the filesystem to
|
||||
* benefit from the PHP opcode cache. This interface is mostly useful if you
|
||||
* need to implement a custom strategy for storing templates on the filesystem.
|
||||
*
|
||||
* @author Andrew Tch <andrew@noop.lv>
|
||||
*/
|
||||
interface CacheInterface
|
||||
{
|
||||
/**
|
||||
* Generates a cache key for the given template class name.
|
||||
*/
|
||||
public function generateKey(string $name, string $className): string;
|
||||
|
||||
/**
|
||||
* Writes the compiled template to cache.
|
||||
*
|
||||
* @param string $content The template representation as a PHP class
|
||||
*/
|
||||
public function write(string $key, string $content): void;
|
||||
|
||||
/**
|
||||
* Loads a template from the cache.
|
||||
*/
|
||||
public function load(string $key): void;
|
||||
|
||||
/**
|
||||
* Returns the modification timestamp of a key.
|
||||
*/
|
||||
public function getTimestamp(string $key): int;
|
||||
}
|
||||
88
vendor/twig/twig/src/Cache/ChainCache.php
vendored
Normal file
88
vendor/twig/twig/src/Cache/ChainCache.php
vendored
Normal file
@ -0,0 +1,88 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Cache;
|
||||
|
||||
/**
|
||||
* Chains several caches together.
|
||||
*
|
||||
* Cached items are fetched from the first cache having them in its data store.
|
||||
* They are saved and deleted in all adapters at once.
|
||||
*
|
||||
* @author Quentin Devos <quentin@devos.pm>
|
||||
*/
|
||||
final class ChainCache implements CacheInterface, RemovableCacheInterface
|
||||
{
|
||||
/**
|
||||
* @param iterable<CacheInterface> $caches The ordered list of caches used to store and fetch cached items
|
||||
*/
|
||||
public function __construct(
|
||||
private iterable $caches,
|
||||
) {
|
||||
}
|
||||
|
||||
public function generateKey(string $name, string $className): string
|
||||
{
|
||||
return $className.'#'.$name;
|
||||
}
|
||||
|
||||
public function write(string $key, string $content): void
|
||||
{
|
||||
$splitKey = $this->splitKey($key);
|
||||
|
||||
foreach ($this->caches as $cache) {
|
||||
$cache->write($cache->generateKey(...$splitKey), $content);
|
||||
}
|
||||
}
|
||||
|
||||
public function load(string $key): void
|
||||
{
|
||||
[$name, $className] = $this->splitKey($key);
|
||||
|
||||
foreach ($this->caches as $cache) {
|
||||
$cache->load($cache->generateKey($name, $className));
|
||||
|
||||
if (class_exists($className, false)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getTimestamp(string $key): int
|
||||
{
|
||||
$splitKey = $this->splitKey($key);
|
||||
|
||||
foreach ($this->caches as $cache) {
|
||||
if (0 < $timestamp = $cache->getTimestamp($cache->generateKey(...$splitKey))) {
|
||||
return $timestamp;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function remove(string $name, string $cls): void
|
||||
{
|
||||
foreach ($this->caches as $cache) {
|
||||
if ($cache instanceof RemovableCacheInterface) {
|
||||
$cache->remove($name, $cls);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string[]
|
||||
*/
|
||||
private function splitKey(string $key): array
|
||||
{
|
||||
return array_reverse(explode('#', $key, 2));
|
||||
}
|
||||
}
|
||||
95
vendor/twig/twig/src/Cache/FilesystemCache.php
vendored
Normal file
95
vendor/twig/twig/src/Cache/FilesystemCache.php
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Cache;
|
||||
|
||||
/**
|
||||
* Implements a cache on the filesystem.
|
||||
*
|
||||
* @author Andrew Tch <andrew@noop.lv>
|
||||
*/
|
||||
class FilesystemCache implements CacheInterface, RemovableCacheInterface
|
||||
{
|
||||
public const FORCE_BYTECODE_INVALIDATION = 1;
|
||||
|
||||
private $directory;
|
||||
private $options;
|
||||
|
||||
public function __construct(string $directory, int $options = 0)
|
||||
{
|
||||
$this->directory = rtrim($directory, '\/').'/';
|
||||
$this->options = $options;
|
||||
}
|
||||
|
||||
public function generateKey(string $name, string $className): string
|
||||
{
|
||||
$hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $className);
|
||||
|
||||
return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php';
|
||||
}
|
||||
|
||||
public function load(string $key): void
|
||||
{
|
||||
if (is_file($key)) {
|
||||
@include_once $key;
|
||||
}
|
||||
}
|
||||
|
||||
public function write(string $key, string $content): void
|
||||
{
|
||||
$dir = \dirname($key);
|
||||
if (!is_dir($dir)) {
|
||||
if (false === @mkdir($dir, 0777, true)) {
|
||||
clearstatcache(true, $dir);
|
||||
if (!is_dir($dir)) {
|
||||
throw new \RuntimeException(\sprintf('Unable to create the cache directory (%s).', $dir));
|
||||
}
|
||||
}
|
||||
} elseif (!is_writable($dir)) {
|
||||
throw new \RuntimeException(\sprintf('Unable to write in the cache directory (%s).', $dir));
|
||||
}
|
||||
|
||||
$tmpFile = tempnam($dir, basename($key));
|
||||
if (false !== @file_put_contents($tmpFile, $content) && @rename($tmpFile, $key)) {
|
||||
@chmod($key, 0666 & ~umask());
|
||||
|
||||
if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) {
|
||||
// Compile cached file into bytecode cache
|
||||
if (\function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) {
|
||||
@opcache_invalidate($key, true);
|
||||
} elseif (\function_exists('apc_compile_file')) {
|
||||
apc_compile_file($key);
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
throw new \RuntimeException(\sprintf('Failed to write cache file "%s".', $key));
|
||||
}
|
||||
|
||||
public function remove(string $name, string $cls): void
|
||||
{
|
||||
$key = $this->generateKey($name, $cls);
|
||||
if (!@unlink($key) && file_exists($key)) {
|
||||
throw new \RuntimeException(\sprintf('Failed to delete cache file "%s".', $key));
|
||||
}
|
||||
}
|
||||
|
||||
public function getTimestamp(string $key): int
|
||||
{
|
||||
if (!is_file($key)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
return (int) @filemtime($key);
|
||||
}
|
||||
}
|
||||
42
vendor/twig/twig/src/Cache/NullCache.php
vendored
Normal file
42
vendor/twig/twig/src/Cache/NullCache.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Cache;
|
||||
|
||||
/**
|
||||
* Implements a no-cache strategy.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class NullCache implements CacheInterface, RemovableCacheInterface
|
||||
{
|
||||
public function generateKey(string $name, string $className): string
|
||||
{
|
||||
return '';
|
||||
}
|
||||
|
||||
public function write(string $key, string $content): void
|
||||
{
|
||||
}
|
||||
|
||||
public function load(string $key): void
|
||||
{
|
||||
}
|
||||
|
||||
public function getTimestamp(string $key): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function remove(string $name, string $cls): void
|
||||
{
|
||||
}
|
||||
}
|
||||
25
vendor/twig/twig/src/Cache/ReadOnlyFilesystemCache.php
vendored
Normal file
25
vendor/twig/twig/src/Cache/ReadOnlyFilesystemCache.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Cache;
|
||||
|
||||
/**
|
||||
* Implements a cache on the filesystem that can only be read, not written to.
|
||||
*
|
||||
* @author Quentin Devos <quentin@devos.pm>
|
||||
*/
|
||||
class ReadOnlyFilesystemCache extends FilesystemCache
|
||||
{
|
||||
public function write(string $key, string $content): void
|
||||
{
|
||||
// Do nothing with the content, it's a read-only filesystem.
|
||||
}
|
||||
}
|
||||
20
vendor/twig/twig/src/Cache/RemovableCacheInterface.php
vendored
Normal file
20
vendor/twig/twig/src/Cache/RemovableCacheInterface.php
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Cache;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface RemovableCacheInterface
|
||||
{
|
||||
public function remove(string $name, string $cls): void;
|
||||
}
|
||||
257
vendor/twig/twig/src/Compiler.php
vendored
Normal file
257
vendor/twig/twig/src/Compiler.php
vendored
Normal file
@ -0,0 +1,257 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Compiler
|
||||
{
|
||||
private $lastLine;
|
||||
private $source;
|
||||
private $indentation;
|
||||
private $debugInfo = [];
|
||||
private $sourceOffset;
|
||||
private $sourceLine;
|
||||
private $varNameSalt = 0;
|
||||
private $didUseEcho = false;
|
||||
private $didUseEchoStack = [];
|
||||
|
||||
public function __construct(
|
||||
private Environment $env,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getEnvironment(): Environment
|
||||
{
|
||||
return $this->env;
|
||||
}
|
||||
|
||||
public function getSource(): string
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function reset(int $indentation = 0)
|
||||
{
|
||||
$this->lastLine = null;
|
||||
$this->source = '';
|
||||
$this->debugInfo = [];
|
||||
$this->sourceOffset = 0;
|
||||
// source code starts at 1 (as we then increment it when we encounter new lines)
|
||||
$this->sourceLine = 1;
|
||||
$this->indentation = $indentation;
|
||||
$this->varNameSalt = 0;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function compile(Node $node, int $indentation = 0)
|
||||
{
|
||||
$this->reset($indentation);
|
||||
$this->didUseEchoStack[] = $this->didUseEcho;
|
||||
|
||||
try {
|
||||
$this->didUseEcho = false;
|
||||
$node->compile($this);
|
||||
|
||||
if ($this->didUseEcho) {
|
||||
trigger_deprecation('twig/twig', '3.9', 'Using "%s" is deprecated, use "yield" instead in "%s", then flag the class with #[\Twig\Attribute\YieldReady].', $this->didUseEcho, $node::class);
|
||||
}
|
||||
|
||||
return $this;
|
||||
} finally {
|
||||
$this->didUseEcho = array_pop($this->didUseEchoStack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function subcompile(Node $node, bool $raw = true)
|
||||
{
|
||||
if (!$raw) {
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4);
|
||||
}
|
||||
|
||||
$this->didUseEchoStack[] = $this->didUseEcho;
|
||||
|
||||
try {
|
||||
$this->didUseEcho = false;
|
||||
$node->compile($this);
|
||||
|
||||
if ($this->didUseEcho) {
|
||||
trigger_deprecation('twig/twig', '3.9', 'Using "%s" is deprecated, use "yield" instead in "%s", then flag the class with #[\Twig\Attribute\YieldReady].', $this->didUseEcho, $node::class);
|
||||
}
|
||||
|
||||
return $this;
|
||||
} finally {
|
||||
$this->didUseEcho = array_pop($this->didUseEchoStack);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a raw string to the compiled code.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function raw(string $string)
|
||||
{
|
||||
$this->checkForEcho($string);
|
||||
$this->source .= $string;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes a string to the compiled code by adding indentation.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function write(...$strings)
|
||||
{
|
||||
foreach ($strings as $string) {
|
||||
$this->checkForEcho($string);
|
||||
$this->source .= str_repeat(' ', $this->indentation * 4).$string;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a quoted string to the compiled code.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function string(string $value)
|
||||
{
|
||||
$this->source .= \sprintf('"%s"', addcslashes($value, "\0\t\"\$\\"));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a PHP representation of a given value.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function repr($value)
|
||||
{
|
||||
if (\is_int($value) || \is_float($value)) {
|
||||
if (false !== $locale = setlocale(\LC_NUMERIC, '0')) {
|
||||
setlocale(\LC_NUMERIC, 'C');
|
||||
}
|
||||
|
||||
$this->raw(var_export($value, true));
|
||||
|
||||
if (false !== $locale) {
|
||||
setlocale(\LC_NUMERIC, $locale);
|
||||
}
|
||||
} elseif (null === $value) {
|
||||
$this->raw('null');
|
||||
} elseif (\is_bool($value)) {
|
||||
$this->raw($value ? 'true' : 'false');
|
||||
} elseif (\is_array($value)) {
|
||||
$this->raw('[');
|
||||
$first = true;
|
||||
foreach ($value as $key => $v) {
|
||||
if (!$first) {
|
||||
$this->raw(', ');
|
||||
}
|
||||
$first = false;
|
||||
$this->repr($key);
|
||||
$this->raw(' => ');
|
||||
$this->repr($v);
|
||||
}
|
||||
$this->raw(']');
|
||||
} else {
|
||||
$this->string($value);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function addDebugInfo(Node $node)
|
||||
{
|
||||
if ($node->getTemplateLine() != $this->lastLine) {
|
||||
$this->write(\sprintf("// line %d\n", $node->getTemplateLine()));
|
||||
|
||||
$this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset);
|
||||
$this->sourceOffset = \strlen($this->source);
|
||||
$this->debugInfo[$this->sourceLine] = $node->getTemplateLine();
|
||||
|
||||
$this->lastLine = $node->getTemplateLine();
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDebugInfo(): array
|
||||
{
|
||||
ksort($this->debugInfo);
|
||||
|
||||
return $this->debugInfo;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*/
|
||||
public function indent(int $step = 1)
|
||||
{
|
||||
$this->indentation += $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return $this
|
||||
*
|
||||
* @throws \LogicException When trying to outdent too much so the indentation would become negative
|
||||
*/
|
||||
public function outdent(int $step = 1)
|
||||
{
|
||||
// can't outdent by more steps than the current indentation level
|
||||
if ($this->indentation < $step) {
|
||||
throw new \LogicException('Unable to call outdent() as the indentation would become negative.');
|
||||
}
|
||||
|
||||
$this->indentation -= $step;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getVarName(): string
|
||||
{
|
||||
return \sprintf('_v%d', $this->varNameSalt++);
|
||||
}
|
||||
|
||||
private function checkForEcho(string $string): void
|
||||
{
|
||||
if ($this->didUseEcho) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->didUseEcho = preg_match('/^\s*+(echo|print)\b/', $string, $m) ? $m[1] : false;
|
||||
}
|
||||
}
|
||||
67
vendor/twig/twig/src/DeprecatedCallableInfo.php
vendored
Normal file
67
vendor/twig/twig/src/DeprecatedCallableInfo.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class DeprecatedCallableInfo
|
||||
{
|
||||
private string $type;
|
||||
private string $name;
|
||||
|
||||
public function __construct(
|
||||
private string $package,
|
||||
private string $version,
|
||||
private ?string $altName = null,
|
||||
private ?string $altPackage = null,
|
||||
private ?string $altVersion = null,
|
||||
) {
|
||||
}
|
||||
|
||||
public function setType(string $type): void
|
||||
{
|
||||
$this->type = $type;
|
||||
}
|
||||
|
||||
public function setName(string $name): void
|
||||
{
|
||||
$this->name = $name;
|
||||
}
|
||||
|
||||
public function triggerDeprecation(?string $file = null, ?int $line = null): void
|
||||
{
|
||||
$message = \sprintf('Twig %s "%s" is deprecated', ucfirst($this->type), $this->name);
|
||||
|
||||
if ($this->altName) {
|
||||
$message .= \sprintf('; use "%s"', $this->altName);
|
||||
if ($this->altPackage) {
|
||||
$message .= \sprintf(' from the "%s" package', $this->altPackage);
|
||||
}
|
||||
if ($this->altVersion) {
|
||||
$message .= \sprintf(' (available since version %s)', $this->altVersion);
|
||||
}
|
||||
$message .= ' instead';
|
||||
}
|
||||
|
||||
if ($file) {
|
||||
$message .= \sprintf(' in %s', $file);
|
||||
if ($line) {
|
||||
$message .= \sprintf(' at line %d', $line);
|
||||
}
|
||||
}
|
||||
|
||||
$message .= '.';
|
||||
|
||||
trigger_deprecation($this->package, $this->version, $message);
|
||||
}
|
||||
}
|
||||
945
vendor/twig/twig/src/Environment.php
vendored
Normal file
945
vendor/twig/twig/src/Environment.php
vendored
Normal file
@ -0,0 +1,945 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Cache\CacheInterface;
|
||||
use Twig\Cache\FilesystemCache;
|
||||
use Twig\Cache\NullCache;
|
||||
use Twig\Cache\RemovableCacheInterface;
|
||||
use Twig\Error\Error;
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\ExpressionParsers;
|
||||
use Twig\Extension\CoreExtension;
|
||||
use Twig\Extension\EscaperExtension;
|
||||
use Twig\Extension\ExtensionInterface;
|
||||
use Twig\Extension\OptimizerExtension;
|
||||
use Twig\Extension\YieldNotReadyExtension;
|
||||
use Twig\Loader\ArrayLoader;
|
||||
use Twig\Loader\ChainLoader;
|
||||
use Twig\Loader\LoaderInterface;
|
||||
use Twig\Node\ModuleNode;
|
||||
use Twig\Node\Node;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\Runtime\EscaperRuntime;
|
||||
use Twig\RuntimeLoader\FactoryRuntimeLoader;
|
||||
use Twig\RuntimeLoader\RuntimeLoaderInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
|
||||
/**
|
||||
* Stores the Twig configuration and renders templates.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Environment
|
||||
{
|
||||
public const VERSION = '3.21.1';
|
||||
public const VERSION_ID = 32101;
|
||||
public const MAJOR_VERSION = 3;
|
||||
public const MINOR_VERSION = 21;
|
||||
public const RELEASE_VERSION = 1;
|
||||
public const EXTRA_VERSION = '';
|
||||
|
||||
private $charset;
|
||||
private $loader;
|
||||
private $debug;
|
||||
private $autoReload;
|
||||
private $cache;
|
||||
private $lexer;
|
||||
private $parser;
|
||||
private $compiler;
|
||||
/** @var array<string, mixed> */
|
||||
private $globals = [];
|
||||
private $resolvedGlobals;
|
||||
private $loadedTemplates;
|
||||
private $strictVariables;
|
||||
private $originalCache;
|
||||
private $extensionSet;
|
||||
private $runtimeLoaders = [];
|
||||
private $runtimes = [];
|
||||
private $optionsHash;
|
||||
/** @var bool */
|
||||
private $useYield;
|
||||
private $defaultRuntimeLoader;
|
||||
private array $hotCache = [];
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* Available options:
|
||||
*
|
||||
* * debug: When set to true, it automatically set "auto_reload" to true as
|
||||
* well (default to false).
|
||||
*
|
||||
* * charset: The charset used by the templates (default to UTF-8).
|
||||
*
|
||||
* * cache: An absolute path where to store the compiled templates,
|
||||
* a \Twig\Cache\CacheInterface implementation,
|
||||
* or false to disable compilation cache (default).
|
||||
*
|
||||
* * auto_reload: Whether to reload the template if the original source changed.
|
||||
* If you don't provide the auto_reload option, it will be
|
||||
* determined automatically based on the debug value.
|
||||
*
|
||||
* * strict_variables: Whether to ignore invalid variables in templates
|
||||
* (default to false).
|
||||
*
|
||||
* * autoescape: Whether to enable auto-escaping (default to html):
|
||||
* * false: disable auto-escaping
|
||||
* * html, js: set the autoescaping to one of the supported strategies
|
||||
* * name: set the autoescaping strategy based on the template name extension
|
||||
* * PHP callback: a PHP callback that returns an escaping strategy based on the template "name"
|
||||
*
|
||||
* * optimizations: A flag that indicates which optimizations to apply
|
||||
* (default to -1 which means that all optimizations are enabled;
|
||||
* set it to 0 to disable).
|
||||
*
|
||||
* * use_yield: true: forces templates to exclusively use "yield" instead of "echo" (all extensions must be yield ready)
|
||||
* false (default): allows templates to use a mix of "yield" and "echo" calls to allow for a progressive migration
|
||||
* Switch to "true" when possible as this will be the only supported mode in Twig 4.0
|
||||
*/
|
||||
public function __construct(LoaderInterface $loader, array $options = [])
|
||||
{
|
||||
$this->setLoader($loader);
|
||||
|
||||
$options = array_merge([
|
||||
'debug' => false,
|
||||
'charset' => 'UTF-8',
|
||||
'strict_variables' => false,
|
||||
'autoescape' => 'html',
|
||||
'cache' => false,
|
||||
'auto_reload' => null,
|
||||
'optimizations' => -1,
|
||||
'use_yield' => false,
|
||||
], $options);
|
||||
|
||||
$this->useYield = (bool) $options['use_yield'];
|
||||
$this->debug = (bool) $options['debug'];
|
||||
$this->setCharset($options['charset'] ?? 'UTF-8');
|
||||
$this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload'];
|
||||
$this->strictVariables = (bool) $options['strict_variables'];
|
||||
$this->setCache($options['cache']);
|
||||
$this->extensionSet = new ExtensionSet();
|
||||
$this->defaultRuntimeLoader = new FactoryRuntimeLoader([
|
||||
EscaperRuntime::class => function () { return new EscaperRuntime($this->charset); },
|
||||
]);
|
||||
|
||||
$this->addExtension(new CoreExtension());
|
||||
$escaperExt = new EscaperExtension($options['autoescape']);
|
||||
$escaperExt->setEnvironment($this, false);
|
||||
$this->addExtension($escaperExt);
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
$this->addExtension(new YieldNotReadyExtension($this->useYield));
|
||||
}
|
||||
$this->addExtension(new OptimizerExtension($options['optimizations']));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function useYield(): bool
|
||||
{
|
||||
return $this->useYield;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables debugging mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enableDebug()
|
||||
{
|
||||
$this->debug = true;
|
||||
$this->updateOptionsHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables debugging mode.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disableDebug()
|
||||
{
|
||||
$this->debug = false;
|
||||
$this->updateOptionsHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if debug mode is enabled.
|
||||
*
|
||||
* @return bool true if debug mode is enabled, false otherwise
|
||||
*/
|
||||
public function isDebug()
|
||||
{
|
||||
return $this->debug;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the auto_reload option.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enableAutoReload()
|
||||
{
|
||||
$this->autoReload = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the auto_reload option.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disableAutoReload()
|
||||
{
|
||||
$this->autoReload = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the auto_reload option is enabled.
|
||||
*
|
||||
* @return bool true if auto_reload is enabled, false otherwise
|
||||
*/
|
||||
public function isAutoReload()
|
||||
{
|
||||
return $this->autoReload;
|
||||
}
|
||||
|
||||
/**
|
||||
* Enables the strict_variables option.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function enableStrictVariables()
|
||||
{
|
||||
$this->strictVariables = true;
|
||||
$this->updateOptionsHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Disables the strict_variables option.
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function disableStrictVariables()
|
||||
{
|
||||
$this->strictVariables = false;
|
||||
$this->updateOptionsHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if the strict_variables option is enabled.
|
||||
*
|
||||
* @return bool true if strict_variables is enabled, false otherwise
|
||||
*/
|
||||
public function isStrictVariables()
|
||||
{
|
||||
return $this->strictVariables;
|
||||
}
|
||||
|
||||
public function removeCache(string $name): void
|
||||
{
|
||||
$cls = $this->getTemplateClass($name);
|
||||
$this->hotCache[$name] = $cls.'_'.bin2hex(random_bytes(16));
|
||||
|
||||
if ($this->cache instanceof RemovableCacheInterface) {
|
||||
$this->cache->remove($name, $cls);
|
||||
} else {
|
||||
throw new \LogicException(\sprintf('The "%s" cache class does not support removing template cache as it does not implement the "RemovableCacheInterface" interface.', \get_class($this->cache)));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the current cache implementation.
|
||||
*
|
||||
* @param bool $original Whether to return the original cache option or the real cache instance
|
||||
*
|
||||
* @return CacheInterface|string|false A Twig\Cache\CacheInterface implementation,
|
||||
* an absolute path to the compiled templates,
|
||||
* or false to disable cache
|
||||
*/
|
||||
public function getCache($original = true)
|
||||
{
|
||||
return $original ? $this->originalCache : $this->cache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the current cache implementation.
|
||||
*
|
||||
* @param CacheInterface|string|false $cache A Twig\Cache\CacheInterface implementation,
|
||||
* an absolute path to the compiled templates,
|
||||
* or false to disable cache
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setCache($cache)
|
||||
{
|
||||
if (\is_string($cache)) {
|
||||
$this->originalCache = $cache;
|
||||
$this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0);
|
||||
} elseif (false === $cache) {
|
||||
$this->originalCache = $cache;
|
||||
$this->cache = new NullCache();
|
||||
} elseif ($cache instanceof CacheInterface) {
|
||||
$this->originalCache = $this->cache = $cache;
|
||||
} else {
|
||||
throw new \LogicException('Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the template class associated with the given string.
|
||||
*
|
||||
* The generated template class is based on the following parameters:
|
||||
*
|
||||
* * The cache key for the given template;
|
||||
* * The currently enabled extensions;
|
||||
* * PHP version;
|
||||
* * Twig version;
|
||||
* * Options with what environment was created.
|
||||
*
|
||||
* @param string $name The name for which to calculate the template class name
|
||||
* @param int|null $index The index if it is an embedded template
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getTemplateClass(string $name, ?int $index = null): string
|
||||
{
|
||||
$key = ($this->hotCache[$name] ?? $this->getLoader()->getCacheKey($name)).$this->optionsHash;
|
||||
|
||||
return '__TwigTemplate_'.hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $key).(null === $index ? '' : '___'.$index);
|
||||
}
|
||||
|
||||
/**
|
||||
* Renders a template.
|
||||
*
|
||||
* @param string|TemplateWrapper $name The template name
|
||||
*
|
||||
* @throws LoaderError When the template cannot be found
|
||||
* @throws SyntaxError When an error occurred during compilation
|
||||
* @throws RuntimeError When an error occurred during rendering
|
||||
*/
|
||||
public function render($name, array $context = []): string
|
||||
{
|
||||
return $this->load($name)->render($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Displays a template.
|
||||
*
|
||||
* @param string|TemplateWrapper $name The template name
|
||||
*
|
||||
* @throws LoaderError When the template cannot be found
|
||||
* @throws SyntaxError When an error occurred during compilation
|
||||
* @throws RuntimeError When an error occurred during rendering
|
||||
*/
|
||||
public function display($name, array $context = []): void
|
||||
{
|
||||
$this->load($name)->display($context);
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a template.
|
||||
*
|
||||
* @param string|TemplateWrapper $name The template name
|
||||
*
|
||||
* @throws LoaderError When the template cannot be found
|
||||
* @throws RuntimeError When a previously generated cache is corrupted
|
||||
* @throws SyntaxError When an error occurred during compilation
|
||||
*/
|
||||
public function load($name): TemplateWrapper
|
||||
{
|
||||
if ($name instanceof TemplateWrapper) {
|
||||
return $name;
|
||||
}
|
||||
if ($name instanceof Template) {
|
||||
trigger_deprecation('twig/twig', '3.9', 'Passing a "%s" instance to "%s" is deprecated.', self::class, __METHOD__);
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name));
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a template internal representation.
|
||||
*
|
||||
* This method is for internal use only and should never be called
|
||||
* directly.
|
||||
*
|
||||
* @param string $name The template name
|
||||
* @param int|null $index The index if it is an embedded template
|
||||
*
|
||||
* @throws LoaderError When the template cannot be found
|
||||
* @throws RuntimeError When a previously generated cache is corrupted
|
||||
* @throws SyntaxError When an error occurred during compilation
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function loadTemplate(string $cls, string $name, ?int $index = null): Template
|
||||
{
|
||||
$mainCls = $cls;
|
||||
if (null !== $index) {
|
||||
$cls .= '___'.$index;
|
||||
}
|
||||
|
||||
if (isset($this->loadedTemplates[$cls])) {
|
||||
return $this->loadedTemplates[$cls];
|
||||
}
|
||||
|
||||
if (!class_exists($cls, false)) {
|
||||
$key = $this->cache->generateKey($name, $mainCls);
|
||||
|
||||
if (!$this->isAutoReload() || $this->isTemplateFresh($name, $this->cache->getTimestamp($key))) {
|
||||
$this->cache->load($key);
|
||||
}
|
||||
|
||||
if (!class_exists($cls, false)) {
|
||||
$source = $this->getLoader()->getSourceContext($name);
|
||||
$content = $this->compileSource($source);
|
||||
if (!isset($this->hotCache[$name])) {
|
||||
$this->cache->write($key, $content);
|
||||
$this->cache->load($key);
|
||||
}
|
||||
|
||||
if (!class_exists($mainCls, false)) {
|
||||
/* Last line of defense if either $this->bcWriteCacheFile was used,
|
||||
* $this->cache is implemented as a no-op or we have a race condition
|
||||
* where the cache was cleared between the above calls to write to and load from
|
||||
* the cache.
|
||||
*/
|
||||
eval('?>'.$content);
|
||||
}
|
||||
|
||||
if (!class_exists($cls, false)) {
|
||||
throw new RuntimeError(\sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.', $name, $index), -1, $source);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->extensionSet->initRuntime();
|
||||
|
||||
return $this->loadedTemplates[$cls] = new $cls($this);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a template from source.
|
||||
*
|
||||
* This method should not be used as a generic way to load templates.
|
||||
*
|
||||
* @param string $template The template source
|
||||
* @param string|null $name An optional name of the template to be used in error messages
|
||||
*
|
||||
* @throws LoaderError When the template cannot be found
|
||||
* @throws SyntaxError When an error occurred during compilation
|
||||
*/
|
||||
public function createTemplate(string $template, ?string $name = null): TemplateWrapper
|
||||
{
|
||||
$hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $template, false);
|
||||
if (null !== $name) {
|
||||
$name = \sprintf('%s (string template %s)', $name, $hash);
|
||||
} else {
|
||||
$name = \sprintf('__string_template__%s', $hash);
|
||||
}
|
||||
|
||||
$loader = new ChainLoader([
|
||||
new ArrayLoader([$name => $template]),
|
||||
$current = $this->getLoader(),
|
||||
]);
|
||||
|
||||
$this->setLoader($loader);
|
||||
try {
|
||||
return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name));
|
||||
} finally {
|
||||
$this->setLoader($current);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns true if the template is still fresh.
|
||||
*
|
||||
* Besides checking the loader for freshness information,
|
||||
* this method also checks if the enabled extensions have
|
||||
* not changed.
|
||||
*
|
||||
* @param int $time The last modification time of the cached template
|
||||
*/
|
||||
public function isTemplateFresh(string $name, int $time): bool
|
||||
{
|
||||
return $this->extensionSet->getLastModified() <= $time && $this->getLoader()->isFresh($name, $time);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tries to load a template consecutively from an array.
|
||||
*
|
||||
* Similar to load() but it also accepts instances of \Twig\TemplateWrapper
|
||||
* and an array of templates where each is tried to be loaded.
|
||||
*
|
||||
* @param string|TemplateWrapper|array<string|TemplateWrapper> $names A template or an array of templates to try consecutively
|
||||
*
|
||||
* @throws LoaderError When none of the templates can be found
|
||||
* @throws SyntaxError When an error occurred during compilation
|
||||
*/
|
||||
public function resolveTemplate($names): TemplateWrapper
|
||||
{
|
||||
if (!\is_array($names)) {
|
||||
return $this->load($names);
|
||||
}
|
||||
|
||||
$count = \count($names);
|
||||
foreach ($names as $name) {
|
||||
if ($name instanceof Template) {
|
||||
trigger_deprecation('twig/twig', '3.9', 'Passing a "%s" instance to "%s" is deprecated.', Template::class, __METHOD__);
|
||||
|
||||
return new TemplateWrapper($this, $name);
|
||||
}
|
||||
if ($name instanceof TemplateWrapper) {
|
||||
return $name;
|
||||
}
|
||||
|
||||
if (1 !== $count && !$this->getLoader()->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
return $this->load($name);
|
||||
}
|
||||
|
||||
throw new LoaderError(\sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names)));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setLexer(Lexer $lexer)
|
||||
{
|
||||
$this->lexer = $lexer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SyntaxError When the code is syntactically wrong
|
||||
*/
|
||||
public function tokenize(Source $source): TokenStream
|
||||
{
|
||||
if (null === $this->lexer) {
|
||||
$this->lexer = new Lexer($this);
|
||||
}
|
||||
|
||||
return $this->lexer->tokenize($source);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setParser(Parser $parser)
|
||||
{
|
||||
$this->parser = $parser;
|
||||
}
|
||||
|
||||
/**
|
||||
* Converts a token stream to a node tree.
|
||||
*
|
||||
* @throws SyntaxError When the token stream is syntactically or semantically wrong
|
||||
*/
|
||||
public function parse(TokenStream $stream): ModuleNode
|
||||
{
|
||||
if (null === $this->parser) {
|
||||
$this->parser = new Parser($this);
|
||||
}
|
||||
|
||||
return $this->parser->parse($stream);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setCompiler(Compiler $compiler)
|
||||
{
|
||||
$this->compiler = $compiler;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a node and returns the PHP code.
|
||||
*/
|
||||
public function compile(Node $node): string
|
||||
{
|
||||
if (null === $this->compiler) {
|
||||
$this->compiler = new Compiler($this);
|
||||
}
|
||||
|
||||
return $this->compiler->compile($node)->getSource();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles a template source code.
|
||||
*
|
||||
* @throws SyntaxError When there was an error during tokenizing, parsing or compiling
|
||||
*/
|
||||
public function compileSource(Source $source): string
|
||||
{
|
||||
try {
|
||||
return $this->compile($this->parse($this->tokenize($source)));
|
||||
} catch (Error $e) {
|
||||
$e->setSourceContext($source);
|
||||
throw $e;
|
||||
} catch (\Exception $e) {
|
||||
throw new SyntaxError(\sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setLoader(LoaderInterface $loader)
|
||||
{
|
||||
$this->loader = $loader;
|
||||
}
|
||||
|
||||
public function getLoader(): LoaderInterface
|
||||
{
|
||||
return $this->loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function setCharset(string $charset)
|
||||
{
|
||||
if ('UTF8' === $charset = strtoupper($charset ?: '')) {
|
||||
// iconv on Windows requires "UTF-8" instead of "UTF8"
|
||||
$charset = 'UTF-8';
|
||||
}
|
||||
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
public function getCharset(): string
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
public function hasExtension(string $class): bool
|
||||
{
|
||||
return $this->extensionSet->hasExtension($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addRuntimeLoader(RuntimeLoaderInterface $loader)
|
||||
{
|
||||
$this->runtimeLoaders[] = $loader;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template TExtension of ExtensionInterface
|
||||
*
|
||||
* @param class-string<TExtension> $class
|
||||
*
|
||||
* @return TExtension
|
||||
*/
|
||||
public function getExtension(string $class): ExtensionInterface
|
||||
{
|
||||
return $this->extensionSet->getExtension($class);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the runtime implementation of a Twig element (filter/function/tag/test).
|
||||
*
|
||||
* @template TRuntime of object
|
||||
*
|
||||
* @param class-string<TRuntime> $class A runtime class name
|
||||
*
|
||||
* @return TRuntime The runtime implementation
|
||||
*
|
||||
* @throws RuntimeError When the template cannot be found
|
||||
*/
|
||||
public function getRuntime(string $class)
|
||||
{
|
||||
if (isset($this->runtimes[$class])) {
|
||||
return $this->runtimes[$class];
|
||||
}
|
||||
|
||||
foreach ($this->runtimeLoaders as $loader) {
|
||||
if (null !== $runtime = $loader->load($class)) {
|
||||
return $this->runtimes[$class] = $runtime;
|
||||
}
|
||||
}
|
||||
|
||||
if (null !== $runtime = $this->defaultRuntimeLoader->load($class)) {
|
||||
return $this->runtimes[$class] = $runtime;
|
||||
}
|
||||
|
||||
throw new RuntimeError(\sprintf('Unable to load the "%s" runtime.', $class));
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addExtension(ExtensionInterface $extension)
|
||||
{
|
||||
$this->extensionSet->addExtension($extension);
|
||||
$this->updateOptionsHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ExtensionInterface[] $extensions An array of extensions
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function setExtensions(array $extensions)
|
||||
{
|
||||
$this->extensionSet->setExtensions($extensions);
|
||||
$this->updateOptionsHash();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on)
|
||||
*/
|
||||
public function getExtensions(): array
|
||||
{
|
||||
return $this->extensionSet->getExtensions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addTokenParser(TokenParserInterface $parser)
|
||||
{
|
||||
$this->extensionSet->addTokenParser($parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TokenParserInterface[]
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getTokenParsers(): array
|
||||
{
|
||||
return $this->extensionSet->getTokenParsers();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getTokenParser(string $name): ?TokenParserInterface
|
||||
{
|
||||
return $this->extensionSet->getTokenParser($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): (TokenParserInterface|false) $callable
|
||||
*/
|
||||
public function registerUndefinedTokenParserCallback(callable $callable): void
|
||||
{
|
||||
$this->extensionSet->registerUndefinedTokenParserCallback($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addNodeVisitor(NodeVisitorInterface $visitor)
|
||||
{
|
||||
$this->extensionSet->addNodeVisitor($visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NodeVisitorInterface[]
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return $this->extensionSet->getNodeVisitors();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addFilter(TwigFilter $filter)
|
||||
{
|
||||
$this->extensionSet->addFilter($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getFilter(string $name): ?TwigFilter
|
||||
{
|
||||
return $this->extensionSet->getFilter($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): (TwigFilter|false) $callable
|
||||
*/
|
||||
public function registerUndefinedFilterCallback(callable $callable): void
|
||||
{
|
||||
$this->extensionSet->registerUndefinedFilterCallback($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the registered Filters.
|
||||
*
|
||||
* Be warned that this method cannot return filters defined with registerUndefinedFilterCallback.
|
||||
*
|
||||
* @return TwigFilter[]
|
||||
*
|
||||
* @see registerUndefinedFilterCallback
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getFilters(): array
|
||||
{
|
||||
return $this->extensionSet->getFilters();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addTest(TwigTest $test)
|
||||
{
|
||||
$this->extensionSet->addTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigTest[]
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getTests(): array
|
||||
{
|
||||
return $this->extensionSet->getTests();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getTest(string $name): ?TwigTest
|
||||
{
|
||||
return $this->extensionSet->getTest($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function addFunction(TwigFunction $function)
|
||||
{
|
||||
$this->extensionSet->addFunction($function);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getFunction(string $name): ?TwigFunction
|
||||
{
|
||||
return $this->extensionSet->getFunction($name);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): (TwigFunction|false) $callable
|
||||
*/
|
||||
public function registerUndefinedFunctionCallback(callable $callable): void
|
||||
{
|
||||
$this->extensionSet->registerUndefinedFunctionCallback($callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets registered functions.
|
||||
*
|
||||
* Be warned that this method cannot return functions defined with registerUndefinedFunctionCallback.
|
||||
*
|
||||
* @return TwigFunction[]
|
||||
*
|
||||
* @see registerUndefinedFunctionCallback
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return $this->extensionSet->getFunctions();
|
||||
}
|
||||
|
||||
/**
|
||||
* Registers a Global.
|
||||
*
|
||||
* New globals can be added before compiling or rendering a template;
|
||||
* but after, you can only update existing globals.
|
||||
*
|
||||
* @param mixed $value The global value
|
||||
*
|
||||
* @return void
|
||||
*/
|
||||
public function addGlobal(string $name, $value)
|
||||
{
|
||||
if ($this->extensionSet->isInitialized() && !\array_key_exists($name, $this->getGlobals())) {
|
||||
throw new \LogicException(\sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name));
|
||||
}
|
||||
|
||||
if (null !== $this->resolvedGlobals) {
|
||||
$this->resolvedGlobals[$name] = $value;
|
||||
} else {
|
||||
$this->globals[$name] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getGlobals(): array
|
||||
{
|
||||
if ($this->extensionSet->isInitialized()) {
|
||||
if (null === $this->resolvedGlobals) {
|
||||
$this->resolvedGlobals = array_merge($this->extensionSet->getGlobals(), $this->globals);
|
||||
}
|
||||
|
||||
return $this->resolvedGlobals;
|
||||
}
|
||||
|
||||
return array_merge($this->extensionSet->getGlobals(), $this->globals);
|
||||
}
|
||||
|
||||
public function resetGlobals(): void
|
||||
{
|
||||
$this->resolvedGlobals = null;
|
||||
$this->extensionSet->resetGlobals();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.14
|
||||
*/
|
||||
public function mergeGlobals(array $context): array
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.14', 'The "%s" method is deprecated.', __METHOD__);
|
||||
|
||||
return $context + $this->getGlobals();
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getExpressionParsers(): ExpressionParsers
|
||||
{
|
||||
return $this->extensionSet->getExpressionParsers();
|
||||
}
|
||||
|
||||
private function updateOptionsHash(): void
|
||||
{
|
||||
$this->optionsHash = implode(':', [
|
||||
$this->extensionSet->getSignature(),
|
||||
\PHP_MAJOR_VERSION,
|
||||
\PHP_MINOR_VERSION,
|
||||
self::VERSION,
|
||||
(int) $this->debug,
|
||||
(int) $this->strictVariables,
|
||||
$this->useYield ? '1' : '0',
|
||||
]);
|
||||
}
|
||||
}
|
||||
178
vendor/twig/twig/src/Error/Error.php
vendored
Normal file
178
vendor/twig/twig/src/Error/Error.php
vendored
Normal file
@ -0,0 +1,178 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Error;
|
||||
|
||||
use Twig\Source;
|
||||
use Twig\Template;
|
||||
|
||||
/**
|
||||
* Twig base exception.
|
||||
*
|
||||
* This exception class and its children must only be used when
|
||||
* an error occurs during the loading of a template, when a syntax error
|
||||
* is detected in a template, or when rendering a template. Other
|
||||
* errors must use regular PHP exception classes (like when the template
|
||||
* cache directory is not writable for instance).
|
||||
*
|
||||
* To help debugging template issues, this class tracks the original template
|
||||
* name and line where the error occurred.
|
||||
*
|
||||
* Whenever possible, you must set these information (original template name
|
||||
* and line number) yourself by passing them to the constructor. If some or all
|
||||
* these information are not available from where you throw the exception, then
|
||||
* this class will guess them automatically.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Error extends \Exception
|
||||
{
|
||||
private $lineno;
|
||||
private $rawMessage;
|
||||
private ?Source $source;
|
||||
private string $phpFile;
|
||||
private int $phpLine;
|
||||
|
||||
/**
|
||||
* Constructor.
|
||||
*
|
||||
* By default, automatic guessing is enabled.
|
||||
*
|
||||
* @param string $message The error message
|
||||
* @param int $lineno The template line where the error occurred
|
||||
* @param Source|null $source The source context where the error occurred
|
||||
*/
|
||||
public function __construct(string $message, int $lineno = -1, ?Source $source = null, ?\Throwable $previous = null)
|
||||
{
|
||||
parent::__construct('', 0, $previous);
|
||||
|
||||
$this->phpFile = $this->getFile();
|
||||
$this->phpLine = $this->getLine();
|
||||
$this->lineno = $lineno;
|
||||
$this->source = $source;
|
||||
$this->rawMessage = $message;
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
public function getRawMessage(): string
|
||||
{
|
||||
return $this->rawMessage;
|
||||
}
|
||||
|
||||
public function getTemplateLine(): int
|
||||
{
|
||||
return $this->lineno;
|
||||
}
|
||||
|
||||
public function setTemplateLine(int $lineno): void
|
||||
{
|
||||
$this->lineno = $lineno;
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
public function getSourceContext(): ?Source
|
||||
{
|
||||
return $this->source;
|
||||
}
|
||||
|
||||
public function setSourceContext(?Source $source = null): void
|
||||
{
|
||||
$this->source = $source;
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
public function guess(): void
|
||||
{
|
||||
if ($this->lineno > -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->guessTemplateInfo();
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
public function appendMessage($rawMessage): void
|
||||
{
|
||||
$this->rawMessage .= $rawMessage;
|
||||
$this->updateRepr();
|
||||
}
|
||||
|
||||
private function updateRepr(): void
|
||||
{
|
||||
if ($this->source && $this->source->getPath()) {
|
||||
// we only update the file and the line together
|
||||
$this->file = $this->source->getPath();
|
||||
if ($this->lineno > 0) {
|
||||
$this->line = $this->lineno;
|
||||
} else {
|
||||
$this->line = -1;
|
||||
}
|
||||
}
|
||||
|
||||
$this->message = $this->rawMessage;
|
||||
$last = substr($this->message, -1);
|
||||
if ($punctuation = '.' === $last || '?' === $last ? $last : '') {
|
||||
$this->message = substr($this->message, 0, -1);
|
||||
}
|
||||
if ($this->source && $this->source->getName()) {
|
||||
$this->message .= \sprintf(' in "%s"', $this->source->getName());
|
||||
}
|
||||
if ($this->lineno > 0) {
|
||||
$this->message .= \sprintf(' at line %d', $this->lineno);
|
||||
}
|
||||
if ($punctuation) {
|
||||
$this->message .= $punctuation;
|
||||
}
|
||||
}
|
||||
|
||||
private function guessTemplateInfo(): void
|
||||
{
|
||||
// $this->source is never null here (see guess() usage in Template)
|
||||
|
||||
$this->lineno = 0;
|
||||
$template = null;
|
||||
$backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS | \DEBUG_BACKTRACE_PROVIDE_OBJECT);
|
||||
foreach ($backtrace as $trace) {
|
||||
if (isset($trace['object']) && $trace['object'] instanceof Template && $this->source->getName() === $trace['object']->getTemplateName()) {
|
||||
$template = $trace['object'];
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$r = new \ReflectionObject($template);
|
||||
$file = $r->getFileName();
|
||||
|
||||
$exceptions = [$e = $this];
|
||||
while ($e = $e->getPrevious()) {
|
||||
$exceptions[] = $e;
|
||||
}
|
||||
|
||||
while ($e = array_pop($exceptions)) {
|
||||
$traces = $e->getTrace();
|
||||
array_unshift($traces, ['file' => $e instanceof Error ? $e->phpFile : $e->getFile(), 'line' => $e instanceof Error ? $e->phpLine : $e->getLine()]);
|
||||
while ($trace = array_shift($traces)) {
|
||||
if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) {
|
||||
continue;
|
||||
}
|
||||
|
||||
foreach ($template->getDebugInfo() as $codeLine => $templateLine) {
|
||||
if ($codeLine <= $trace['line']) {
|
||||
// update template line
|
||||
$this->lineno = $templateLine;
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
21
vendor/twig/twig/src/Error/LoaderError.php
vendored
Normal file
21
vendor/twig/twig/src/Error/LoaderError.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Error;
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs during template loading.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class LoaderError extends Error
|
||||
{
|
||||
}
|
||||
22
vendor/twig/twig/src/Error/RuntimeError.php
vendored
Normal file
22
vendor/twig/twig/src/Error/RuntimeError.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Error;
|
||||
|
||||
/**
|
||||
* Exception thrown when an error occurs at runtime.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class RuntimeError extends Error
|
||||
{
|
||||
}
|
||||
46
vendor/twig/twig/src/Error/SyntaxError.php
vendored
Normal file
46
vendor/twig/twig/src/Error/SyntaxError.php
vendored
Normal file
@ -0,0 +1,46 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Error;
|
||||
|
||||
/**
|
||||
* \Exception thrown when a syntax error occurs during lexing or parsing of a template.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class SyntaxError extends Error
|
||||
{
|
||||
/**
|
||||
* Tweaks the error message to include suggestions.
|
||||
*
|
||||
* @param string $name The original name of the item that does not exist
|
||||
* @param array $items An array of possible items
|
||||
*/
|
||||
public function addSuggestions(string $name, array $items): void
|
||||
{
|
||||
$alternatives = [];
|
||||
foreach ($items as $item) {
|
||||
$lev = levenshtein($name, $item);
|
||||
if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) {
|
||||
$alternatives[$item] = $lev;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$alternatives) {
|
||||
return;
|
||||
}
|
||||
|
||||
asort($alternatives);
|
||||
|
||||
$this->appendMessage(\sprintf(' Did you mean "%s"?', implode('", "', array_keys($alternatives))));
|
||||
}
|
||||
}
|
||||
369
vendor/twig/twig/src/ExpressionParser.php
vendored
Normal file
369
vendor/twig/twig/src/ExpressionParser.php
vendored
Normal file
@ -0,0 +1,369 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\Infix\DotExpressionParser;
|
||||
use Twig\ExpressionParser\Infix\FilterExpressionParser;
|
||||
use Twig\ExpressionParser\Infix\SquareBracketExpressionParser;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Unary\NegUnary;
|
||||
use Twig\Node\Expression\Unary\PosUnary;
|
||||
use Twig\Node\Expression\Unary\SpreadUnary;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Node\Node;
|
||||
use Twig\Node\Nodes;
|
||||
|
||||
/**
|
||||
* Parses expressions.
|
||||
*
|
||||
* This parser implements a "Precedence climbing" algorithm.
|
||||
*
|
||||
* @see https://www.engr.mun.ca/~theo/Misc/exp_parsing.htm
|
||||
* @see https://en.wikipedia.org/wiki/Operator-precedence_parser
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
class ExpressionParser
|
||||
{
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public const OPERATOR_LEFT = 1;
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public const OPERATOR_RIGHT = 2;
|
||||
|
||||
public function __construct(
|
||||
private Parser $parser,
|
||||
private Environment $env,
|
||||
) {
|
||||
trigger_deprecation('twig/twig', '3.21', 'Class "%s" is deprecated, use "Parser::parseExpression()" instead.', __CLASS__);
|
||||
}
|
||||
|
||||
public function parseExpression($precedence = 0)
|
||||
{
|
||||
if (\func_num_args() > 1) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'Passing a second argument ($allowArrow) to "%s()" is deprecated.', __METHOD__);
|
||||
}
|
||||
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated, use "Parser::parseExpression()" instead.', __METHOD__);
|
||||
|
||||
return $this->parser->parseExpression((int) $precedence);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parsePrimaryExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
return $this->parseExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parseStringExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
return $this->parseExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.11, use parseExpression() instead
|
||||
*/
|
||||
public function parseArrayExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.11', 'Calling "%s()" is deprecated, use "parseExpression()" instead.', __METHOD__);
|
||||
|
||||
return $this->parseExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parseSequenceExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
return $this->parseExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.11, use parseExpression() instead
|
||||
*/
|
||||
public function parseHashExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.11', 'Calling "%s()" is deprecated, use "parseExpression()" instead.', __METHOD__);
|
||||
|
||||
return $this->parseExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parseMappingExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
return $this->parseExpression();
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parsePostfixExpression($node)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
while (true) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if ($token->test(Token::PUNCTUATION_TYPE)) {
|
||||
if ('.' == $token->getValue() || '[' == $token->getValue()) {
|
||||
$node = $this->parseSubscriptExpression($node);
|
||||
} elseif ('|' == $token->getValue()) {
|
||||
$node = $this->parseFilterExpression($node);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parseSubscriptExpression($node)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
$parsers = new \ReflectionProperty($this->parser, 'parsers');
|
||||
|
||||
if ('.' === $this->parser->getStream()->next()->getValue()) {
|
||||
return $parsers->getValue($this->parser)->getByClass(DotExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken());
|
||||
}
|
||||
|
||||
return $parsers->getValue($this->parser)->getByClass(SquareBracketExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken());
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parseFilterExpression($node)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
$this->parser->getStream()->next();
|
||||
|
||||
return $this->parseFilterExpressionRaw($node);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parseFilterExpressionRaw($node)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
$parsers = new \ReflectionProperty($this->parser, 'parsers');
|
||||
|
||||
$op = $parsers->getValue($this->parser)->getByClass(FilterExpressionParser::class);
|
||||
while (true) {
|
||||
$node = $op->parse($this->parser, $node, $this->parser->getCurrentToken());
|
||||
if (!$this->parser->getStream()->test(Token::OPERATOR_TYPE, '|')) {
|
||||
break;
|
||||
}
|
||||
$this->parser->getStream()->next();
|
||||
}
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parses arguments.
|
||||
*
|
||||
* @return Node
|
||||
*
|
||||
* @throws SyntaxError
|
||||
*
|
||||
* @deprecated since Twig 3.19 Use Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments() instead
|
||||
*/
|
||||
public function parseArguments()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.19', \sprintf('The "%s()" method is deprecated, use "Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments()" instead.', __METHOD__));
|
||||
|
||||
$parsePrimary = new \ReflectionMethod($this->parser, 'parsePrimary');
|
||||
|
||||
$namedArguments = false;
|
||||
$definition = false;
|
||||
if (\func_num_args() > 1) {
|
||||
$definition = func_get_arg(1);
|
||||
}
|
||||
if (\func_num_args() > 0) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'Passing arguments to "%s()" is deprecated.', __METHOD__);
|
||||
$namedArguments = func_get_arg(0);
|
||||
}
|
||||
|
||||
$args = [];
|
||||
$stream = $this->parser->getStream();
|
||||
|
||||
$stream->expect(Token::OPERATOR_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
|
||||
$hasSpread = false;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
if ($args) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
|
||||
|
||||
// if the comma above was a trailing comma, early exit the argument parse loop
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition) {
|
||||
$token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name');
|
||||
$value = new ContextVariable($token->getValue(), $this->parser->getCurrentToken()->getLine());
|
||||
} else {
|
||||
if ($stream->nextIf(Token::SPREAD_TYPE)) {
|
||||
$hasSpread = true;
|
||||
$value = new SpreadUnary($this->parseExpression(), $stream->getCurrent()->getLine());
|
||||
} elseif ($hasSpread) {
|
||||
throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext());
|
||||
} else {
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if ($namedArguments && (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || (!$definition && $token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':')))) {
|
||||
if (!$value instanceof ContextVariable) {
|
||||
throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', $value::class), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
$name = $value->getAttribute('name');
|
||||
|
||||
if ($definition) {
|
||||
$value = $parsePrimary->invoke($this->parser);
|
||||
|
||||
if (!$this->checkConstantExpression($value)) {
|
||||
throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).', $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
} else {
|
||||
$value = $this->parseExpression();
|
||||
}
|
||||
}
|
||||
|
||||
if ($definition) {
|
||||
if (null === $name) {
|
||||
$name = $value->getAttribute('name');
|
||||
$value = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine());
|
||||
$value->setAttribute('is_implicit', true);
|
||||
}
|
||||
$args[$name] = $value;
|
||||
} else {
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
$args[$name] = $value;
|
||||
}
|
||||
}
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
|
||||
return new Nodes($args);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21, use "AbstractTokenParser::parseAssignmentExpression()" instead
|
||||
*/
|
||||
public function parseAssignmentExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated, use "AbstractTokenParser::parseAssignmentExpression()" instead.', __METHOD__);
|
||||
|
||||
$stream = $this->parser->getStream();
|
||||
$targets = [];
|
||||
while (true) {
|
||||
$token = $this->parser->getCurrentToken();
|
||||
if ($stream->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) {
|
||||
// in this context, string operators are variable names
|
||||
$this->parser->getStream()->next();
|
||||
} else {
|
||||
$stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to');
|
||||
}
|
||||
$targets[] = new AssignContextVariable($token->getValue(), $token->getLine());
|
||||
|
||||
if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Nodes($targets);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.21
|
||||
*/
|
||||
public function parseMultitargetExpression()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__);
|
||||
|
||||
$targets = [];
|
||||
while (true) {
|
||||
$targets[] = $this->parseExpression();
|
||||
if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return new Nodes($targets);
|
||||
}
|
||||
|
||||
// checks that the node only contains "constant" elements
|
||||
// to be removed in 4.0
|
||||
private function checkConstantExpression(Node $node): bool
|
||||
{
|
||||
if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression
|
||||
|| $node instanceof NegUnary || $node instanceof PosUnary
|
||||
)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach ($node as $n) {
|
||||
if (!$this->checkConstantExpression($n)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.19 Use Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments() instead
|
||||
*/
|
||||
public function parseOnlyArguments()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.19', \sprintf('The "%s()" method is deprecated, use "Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments()" instead.', __METHOD__));
|
||||
|
||||
return $this->parseArguments();
|
||||
}
|
||||
}
|
||||
30
vendor/twig/twig/src/ExpressionParser/AbstractExpressionParser.php
vendored
Normal file
30
vendor/twig/twig/src/ExpressionParser/AbstractExpressionParser.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
abstract class AbstractExpressionParser implements ExpressionParserInterface
|
||||
{
|
||||
public function __toString(): string
|
||||
{
|
||||
return \sprintf('%s(%s)', ExpressionParserType::getType($this)->value, $this->getName());
|
||||
}
|
||||
|
||||
public function getPrecedenceChange(): ?PrecedenceChange
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getAliases(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
}
|
||||
17
vendor/twig/twig/src/ExpressionParser/ExpressionParserDescriptionInterface.php
vendored
Normal file
17
vendor/twig/twig/src/ExpressionParser/ExpressionParserDescriptionInterface.php
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
interface ExpressionParserDescriptionInterface
|
||||
{
|
||||
public function getDescription(): string;
|
||||
}
|
||||
28
vendor/twig/twig/src/ExpressionParser/ExpressionParserInterface.php
vendored
Normal file
28
vendor/twig/twig/src/ExpressionParser/ExpressionParserInterface.php
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
interface ExpressionParserInterface
|
||||
{
|
||||
public function __toString(): string;
|
||||
|
||||
public function getName(): string;
|
||||
|
||||
public function getPrecedence(): int;
|
||||
|
||||
public function getPrecedenceChange(): ?PrecedenceChange;
|
||||
|
||||
/**
|
||||
* @return array<string>
|
||||
*/
|
||||
public function getAliases(): array;
|
||||
}
|
||||
33
vendor/twig/twig/src/ExpressionParser/ExpressionParserType.php
vendored
Normal file
33
vendor/twig/twig/src/ExpressionParser/ExpressionParserType.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
enum ExpressionParserType: string
|
||||
{
|
||||
case Prefix = 'prefix';
|
||||
case Infix = 'infix';
|
||||
|
||||
public static function getType(object $object): ExpressionParserType
|
||||
{
|
||||
if ($object instanceof PrefixExpressionParserInterface) {
|
||||
return self::Prefix;
|
||||
}
|
||||
if ($object instanceof InfixExpressionParserInterface) {
|
||||
return self::Infix;
|
||||
}
|
||||
|
||||
throw new \InvalidArgumentException(\sprintf('Unsupported expression parser type: %s', $object::class));
|
||||
}
|
||||
}
|
||||
127
vendor/twig/twig/src/ExpressionParser/ExpressionParsers.php
vendored
Normal file
127
vendor/twig/twig/src/ExpressionParser/ExpressionParsers.php
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
/**
|
||||
* @template-implements \IteratorAggregate<ExpressionParserInterface>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class ExpressionParsers implements \IteratorAggregate
|
||||
{
|
||||
/**
|
||||
* @var array<class-string<ExpressionParserInterface>, array<string, ExpressionParserInterface>>
|
||||
*/
|
||||
private array $parsersByName = [];
|
||||
|
||||
/**
|
||||
* @var array<class-string<ExpressionParserInterface>, ExpressionParserInterface>
|
||||
*/
|
||||
private array $parsersByClass = [];
|
||||
|
||||
/**
|
||||
* @var \WeakMap<ExpressionParserInterface, array<ExpressionParserInterface>>|null
|
||||
*/
|
||||
private ?\WeakMap $precedenceChanges = null;
|
||||
|
||||
/**
|
||||
* @param array<ExpressionParserInterface> $parsers
|
||||
*/
|
||||
public function __construct(array $parsers = [])
|
||||
{
|
||||
$this->add($parsers);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param array<ExpressionParserInterface> $parsers
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function add(array $parsers): static
|
||||
{
|
||||
foreach ($parsers as $parser) {
|
||||
if ($parser->getPrecedence() > 512 || $parser->getPrecedence() < 0) {
|
||||
trigger_deprecation('twig/twig', '3.21', 'Precedence for "%s" must be between 0 and 512, got %d.', $parser->getName(), $parser->getPrecedence());
|
||||
// throw new \InvalidArgumentException(\sprintf('Precedence for "%s" must be between 0 and 512, got %d.', $parser->getName(), $parser->getPrecedence()));
|
||||
}
|
||||
$interface = $parser instanceof PrefixExpressionParserInterface ? PrefixExpressionParserInterface::class : InfixExpressionParserInterface::class;
|
||||
$this->parsersByName[$interface][$parser->getName()] = $parser;
|
||||
$this->parsersByClass[$parser::class] = $parser;
|
||||
foreach ($parser->getAliases() as $alias) {
|
||||
$this->parsersByName[$interface][$alias] = $parser;
|
||||
}
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of ExpressionParserInterface
|
||||
*
|
||||
* @param class-string<T> $class
|
||||
*
|
||||
* @return T|null
|
||||
*/
|
||||
public function getByClass(string $class): ?ExpressionParserInterface
|
||||
{
|
||||
return $this->parsersByClass[$class] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @template T of ExpressionParserInterface
|
||||
*
|
||||
* @param class-string<T> $interface
|
||||
*
|
||||
* @return T|null
|
||||
*/
|
||||
public function getByName(string $interface, string $name): ?ExpressionParserInterface
|
||||
{
|
||||
return $this->parsersByName[$interface][$name] ?? null;
|
||||
}
|
||||
|
||||
public function getIterator(): \Traversable
|
||||
{
|
||||
foreach ($this->parsersByName as $parsers) {
|
||||
// we don't yield the keys
|
||||
yield from $parsers;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @return \WeakMap<ExpressionParserInterface, array<ExpressionParserInterface>>
|
||||
*/
|
||||
public function getPrecedenceChanges(): \WeakMap
|
||||
{
|
||||
if (null === $this->precedenceChanges) {
|
||||
$this->precedenceChanges = new \WeakMap();
|
||||
foreach ($this as $ep) {
|
||||
if (!$ep->getPrecedenceChange()) {
|
||||
continue;
|
||||
}
|
||||
$min = min($ep->getPrecedenceChange()->getNewPrecedence(), $ep->getPrecedence());
|
||||
$max = max($ep->getPrecedenceChange()->getNewPrecedence(), $ep->getPrecedence());
|
||||
foreach ($this as $e) {
|
||||
if ($e->getPrecedence() > $min && $e->getPrecedence() < $max) {
|
||||
if (!isset($this->precedenceChanges[$e])) {
|
||||
$this->precedenceChanges[$e] = [];
|
||||
}
|
||||
$this->precedenceChanges[$e][] = $ep;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->precedenceChanges;
|
||||
}
|
||||
}
|
||||
79
vendor/twig/twig/src/ExpressionParser/Infix/ArgumentsTrait.php
vendored
Normal file
79
vendor/twig/twig/src/ExpressionParser/Infix/ArgumentsTrait.php
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\Unary\SpreadUnary;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Node\Expression\Variable\LocalVariable;
|
||||
use Twig\Node\Nodes;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
trait ArgumentsTrait
|
||||
{
|
||||
private function parseCallableArguments(Parser $parser, int $line, bool $parseOpenParenthesis = true): ArrayExpression
|
||||
{
|
||||
$arguments = new ArrayExpression([], $line);
|
||||
foreach ($this->parseNamedArguments($parser, $parseOpenParenthesis) as $k => $n) {
|
||||
$arguments->addElement($n, new LocalVariable($k, $line));
|
||||
}
|
||||
|
||||
return $arguments;
|
||||
}
|
||||
|
||||
private function parseNamedArguments(Parser $parser, bool $parseOpenParenthesis = true): Nodes
|
||||
{
|
||||
$args = [];
|
||||
$stream = $parser->getStream();
|
||||
if ($parseOpenParenthesis) {
|
||||
$stream->expect(Token::OPERATOR_TYPE, '(', 'A list of arguments must begin with an opening parenthesis');
|
||||
}
|
||||
$hasSpread = false;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
if ($args) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma');
|
||||
|
||||
// if the comma above was a trailing comma, early exit the argument parse loop
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ')')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$value = $parser->parseExpression();
|
||||
if ($value instanceof SpreadUnary) {
|
||||
$hasSpread = true;
|
||||
} elseif ($hasSpread) {
|
||||
throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
$name = null;
|
||||
if (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':'))) {
|
||||
if (!$value instanceof ContextVariable) {
|
||||
throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', $value::class), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
$name = $value->getAttribute('name');
|
||||
$value = $parser->parseExpression();
|
||||
}
|
||||
|
||||
if (null === $name) {
|
||||
$args[] = $value;
|
||||
} else {
|
||||
$args[$name] = $value;
|
||||
}
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis');
|
||||
|
||||
return new Nodes($args);
|
||||
}
|
||||
}
|
||||
53
vendor/twig/twig/src/ExpressionParser/Infix/ArrowExpressionParser.php
vendored
Normal file
53
vendor/twig/twig/src/ExpressionParser/Infix/ArrowExpressionParser.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrowFunctionExpression;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ArrowExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
// As the expression of the arrow function is independent from the current precedence, we want a precedence of 0
|
||||
return new ArrowFunctionExpression($parser->parseExpression(), $expr, $token->getLine());
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '=>';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Arrow function (x => expr)';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 250;
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return InfixAssociativity::Left;
|
||||
}
|
||||
}
|
||||
80
vendor/twig/twig/src/ExpressionParser/Infix/BinaryOperatorExpressionParser.php
vendored
Normal file
80
vendor/twig/twig/src/ExpressionParser/Infix/BinaryOperatorExpressionParser.php
vendored
Normal file
@ -0,0 +1,80 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\ExpressionParser\PrecedenceChange;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\Binary\AbstractBinary;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class BinaryOperatorExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
/** @var class-string<AbstractBinary> */
|
||||
private string $nodeClass,
|
||||
private string $name,
|
||||
private int $precedence,
|
||||
private InfixAssociativity $associativity = InfixAssociativity::Left,
|
||||
private ?PrecedenceChange $precedenceChange = null,
|
||||
private ?string $description = null,
|
||||
private array $aliases = [],
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractBinary
|
||||
*/
|
||||
public function parse(Parser $parser, AbstractExpression $left, Token $token): AbstractExpression
|
||||
{
|
||||
$right = $parser->parseExpression(InfixAssociativity::Left === $this->getAssociativity() ? $this->getPrecedence() + 1 : $this->getPrecedence());
|
||||
|
||||
return new ($this->nodeClass)($left, $right, $token->getLine());
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return $this->associativity;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description ?? '';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return $this->precedence;
|
||||
}
|
||||
|
||||
public function getPrecedenceChange(): ?PrecedenceChange
|
||||
{
|
||||
return $this->precedenceChange;
|
||||
}
|
||||
|
||||
public function getAliases(): array
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
}
|
||||
62
vendor/twig/twig/src/ExpressionParser/Infix/ConditionalTernaryExpressionParser.php
vendored
Normal file
62
vendor/twig/twig/src/ExpressionParser/Infix/ConditionalTernaryExpressionParser.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Ternary\ConditionalTernary;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class ConditionalTernaryExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
public function parse(Parser $parser, AbstractExpression $left, Token $token): AbstractExpression
|
||||
{
|
||||
$then = $parser->parseExpression($this->getPrecedence());
|
||||
if ($parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
// Ternary operator (expr ? expr2 : expr3)
|
||||
$else = $parser->parseExpression($this->getPrecedence());
|
||||
} else {
|
||||
// Ternary without else (expr ? expr2)
|
||||
$else = new ConstantExpression('', $token->getLine());
|
||||
}
|
||||
|
||||
return new ConditionalTernary($left, $then, $else, $token->getLine());
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '?';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Conditional operator (a ? b : c)';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return InfixAssociativity::Left;
|
||||
}
|
||||
}
|
||||
99
vendor/twig/twig/src/ExpressionParser/Infix/DotExpressionParser.php
vendored
Normal file
99
vendor/twig/twig/src/ExpressionParser/Infix/DotExpressionParser.php
vendored
Normal file
@ -0,0 +1,99 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\Lexer;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Expression\MacroReferenceExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Expression\Variable\TemplateVariable;
|
||||
use Twig\Parser;
|
||||
use Twig\Template;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class DotExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
use ArgumentsTrait;
|
||||
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$token = $stream->getCurrent();
|
||||
$lineno = $token->getLine();
|
||||
$arguments = new ArrayExpression([], $lineno);
|
||||
$type = Template::ANY_CALL;
|
||||
|
||||
if ($stream->nextIf(Token::OPERATOR_TYPE, '(')) {
|
||||
$attribute = $parser->parseExpression();
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')');
|
||||
} else {
|
||||
$token = $stream->next();
|
||||
if (
|
||||
$token->test(Token::NAME_TYPE)
|
||||
|| $token->test(Token::NUMBER_TYPE)
|
||||
|| ($token->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue()))
|
||||
) {
|
||||
$attribute = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
} else {
|
||||
throw new SyntaxError(\sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), $token->toEnglish()), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
}
|
||||
|
||||
if ($stream->test(Token::OPERATOR_TYPE, '(')) {
|
||||
$type = Template::METHOD_CALL;
|
||||
$arguments = $this->parseCallableArguments($parser, $token->getLine());
|
||||
}
|
||||
|
||||
if (
|
||||
$expr instanceof NameExpression
|
||||
&& (
|
||||
null !== $parser->getImportedSymbol('template', $expr->getAttribute('name'))
|
||||
|| '_self' === $expr->getAttribute('name') && $attribute instanceof ConstantExpression
|
||||
)
|
||||
) {
|
||||
return new MacroReferenceExpression(new TemplateVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$attribute->getAttribute('value'), $arguments, $expr->getTemplateLine());
|
||||
}
|
||||
|
||||
return new GetAttrExpression($expr, $attribute, $arguments, $type, $lineno);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '.';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Get an attribute on a variable';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 512;
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return InfixAssociativity::Left;
|
||||
}
|
||||
}
|
||||
85
vendor/twig/twig/src/ExpressionParser/Infix/FilterExpressionParser.php
vendored
Normal file
85
vendor/twig/twig/src/ExpressionParser/Infix/FilterExpressionParser.php
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Attribute\FirstClassTwigCallableReady;
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\ExpressionParser\PrecedenceChange;
|
||||
use Twig\Node\EmptyNode;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class FilterExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
use ArgumentsTrait;
|
||||
|
||||
private $readyNodes = [];
|
||||
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$token = $stream->expect(Token::NAME_TYPE);
|
||||
$line = $token->getLine();
|
||||
|
||||
if (!$stream->test(Token::OPERATOR_TYPE, '(')) {
|
||||
$arguments = new EmptyNode();
|
||||
} else {
|
||||
$arguments = $this->parseNamedArguments($parser);
|
||||
}
|
||||
|
||||
$filter = $parser->getFilter($token->getValue(), $line);
|
||||
|
||||
$ready = true;
|
||||
if (!isset($this->readyNodes[$class = $filter->getNodeClass()])) {
|
||||
$this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class);
|
||||
}
|
||||
|
||||
if (!$ready = $this->readyNodes[$class]) {
|
||||
trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigFilter" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class);
|
||||
}
|
||||
|
||||
return new $class($expr, $ready ? $filter : new ConstantExpression($filter->getName(), $line), $arguments, $line);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '|';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Twig filter call';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 512;
|
||||
}
|
||||
|
||||
public function getPrecedenceChange(): ?PrecedenceChange
|
||||
{
|
||||
return new PrecedenceChange('twig/twig', '3.21', 300);
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return InfixAssociativity::Left;
|
||||
}
|
||||
}
|
||||
90
vendor/twig/twig/src/ExpressionParser/Infix/FunctionExpressionParser.php
vendored
Normal file
90
vendor/twig/twig/src/ExpressionParser/Infix/FunctionExpressionParser.php
vendored
Normal file
@ -0,0 +1,90 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Attribute\FirstClassTwigCallableReady;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\Node\EmptyNode;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\MacroReferenceExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class FunctionExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
use ArgumentsTrait;
|
||||
|
||||
private $readyNodes = [];
|
||||
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
$line = $token->getLine();
|
||||
if (!$expr instanceof NameExpression) {
|
||||
throw new SyntaxError('Function name must be an identifier.', $line, $parser->getStream()->getSourceContext());
|
||||
}
|
||||
|
||||
$name = $expr->getAttribute('name');
|
||||
|
||||
if (null !== $alias = $parser->getImportedSymbol('function', $name)) {
|
||||
return new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], $this->parseCallableArguments($parser, $line, false), $line);
|
||||
}
|
||||
|
||||
$args = $this->parseNamedArguments($parser, false);
|
||||
|
||||
$function = $parser->getFunction($name, $line);
|
||||
|
||||
if ($function->getParserCallable()) {
|
||||
$fakeNode = new EmptyNode($line);
|
||||
$fakeNode->setSourceContext($parser->getStream()->getSourceContext());
|
||||
|
||||
return ($function->getParserCallable())($parser, $fakeNode, $args, $line);
|
||||
}
|
||||
|
||||
if (!isset($this->readyNodes[$class = $function->getNodeClass()])) {
|
||||
$this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class);
|
||||
}
|
||||
|
||||
if (!$ready = $this->readyNodes[$class]) {
|
||||
trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigFunction" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class);
|
||||
}
|
||||
|
||||
return new $class($ready ? $function : $function->getName(), $args, $line);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '(';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Twig function call';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 512;
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return InfixAssociativity::Left;
|
||||
}
|
||||
}
|
||||
84
vendor/twig/twig/src/ExpressionParser/Infix/IsExpressionParser.php
vendored
Normal file
84
vendor/twig/twig/src/ExpressionParser/Infix/IsExpressionParser.php
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Attribute\FirstClassTwigCallableReady;
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\MacroReferenceExpression;
|
||||
use Twig\Node\Expression\NameExpression;
|
||||
use Twig\Node\Nodes;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
class IsExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
use ArgumentsTrait;
|
||||
|
||||
private $readyNodes = [];
|
||||
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$test = $parser->getTest($token->getLine());
|
||||
|
||||
$arguments = null;
|
||||
if ($stream->test(Token::OPERATOR_TYPE, '(')) {
|
||||
$arguments = $this->parseNamedArguments($parser);
|
||||
} elseif ($test->hasOneMandatoryArgument()) {
|
||||
$arguments = new Nodes([0 => $parser->parseExpression($this->getPrecedence())]);
|
||||
}
|
||||
|
||||
if ('defined' === $test->getName() && $expr instanceof NameExpression && null !== $alias = $parser->getImportedSymbol('function', $expr->getAttribute('name'))) {
|
||||
$expr = new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], new ArrayExpression([], $expr->getTemplateLine()), $expr->getTemplateLine());
|
||||
}
|
||||
|
||||
$ready = $test instanceof TwigTest;
|
||||
if (!isset($this->readyNodes[$class = $test->getNodeClass()])) {
|
||||
$this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class);
|
||||
}
|
||||
|
||||
if (!$ready = $this->readyNodes[$class]) {
|
||||
trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigTest" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class);
|
||||
}
|
||||
|
||||
return new $class($expr, $ready ? $test : $test->getName(), $arguments, $stream->getCurrent()->getLine());
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 100;
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'is';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Twig tests';
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return InfixAssociativity::Left;
|
||||
}
|
||||
}
|
||||
33
vendor/twig/twig/src/ExpressionParser/Infix/IsNotExpressionParser.php
vendored
Normal file
33
vendor/twig/twig/src/ExpressionParser/Infix/IsNotExpressionParser.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\Unary\NotUnary;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class IsNotExpressionParser extends IsExpressionParser
|
||||
{
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
return new NotUnary(parent::parse($parser, $expr, $token), $token->getLine());
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return 'is not';
|
||||
}
|
||||
}
|
||||
91
vendor/twig/twig/src/ExpressionParser/Infix/SquareBracketExpressionParser.php
vendored
Normal file
91
vendor/twig/twig/src/ExpressionParser/Infix/SquareBracketExpressionParser.php
vendored
Normal file
@ -0,0 +1,91 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Infix;
|
||||
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\GetAttrExpression;
|
||||
use Twig\Node\Nodes;
|
||||
use Twig\Parser;
|
||||
use Twig\Template;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class SquareBracketExpressionParser extends AbstractExpressionParser implements InfixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$lineno = $token->getLine();
|
||||
$arguments = new ArrayExpression([], $lineno);
|
||||
|
||||
// slice?
|
||||
$slice = false;
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
$attribute = new ConstantExpression(0, $token->getLine());
|
||||
} else {
|
||||
$attribute = $parser->parseExpression();
|
||||
}
|
||||
|
||||
if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) {
|
||||
$slice = true;
|
||||
}
|
||||
|
||||
if ($slice) {
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
$length = new ConstantExpression(null, $token->getLine());
|
||||
} else {
|
||||
$length = $parser->parseExpression();
|
||||
}
|
||||
|
||||
$filter = $parser->getFilter('slice', $token->getLine());
|
||||
$arguments = new Nodes([$attribute, $length]);
|
||||
$filter = new ($filter->getNodeClass())($expr, $filter, $arguments, $token->getLine());
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']');
|
||||
|
||||
return $filter;
|
||||
}
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']');
|
||||
|
||||
return new GetAttrExpression($expr, $attribute, $arguments, Template::ARRAY_CALL, $lineno);
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '[';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Array access';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 512;
|
||||
}
|
||||
|
||||
public function getAssociativity(): InfixAssociativity
|
||||
{
|
||||
return InfixAssociativity::Left;
|
||||
}
|
||||
}
|
||||
18
vendor/twig/twig/src/ExpressionParser/InfixAssociativity.php
vendored
Normal file
18
vendor/twig/twig/src/ExpressionParser/InfixAssociativity.php
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
enum InfixAssociativity
|
||||
{
|
||||
case Left;
|
||||
case Right;
|
||||
}
|
||||
23
vendor/twig/twig/src/ExpressionParser/InfixExpressionParserInterface.php
vendored
Normal file
23
vendor/twig/twig/src/ExpressionParser/InfixExpressionParserInterface.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
interface InfixExpressionParserInterface extends ExpressionParserInterface
|
||||
{
|
||||
public function parse(Parser $parser, AbstractExpression $left, Token $token): AbstractExpression;
|
||||
|
||||
public function getAssociativity(): InfixAssociativity;
|
||||
}
|
||||
42
vendor/twig/twig/src/ExpressionParser/PrecedenceChange.php
vendored
Normal file
42
vendor/twig/twig/src/ExpressionParser/PrecedenceChange.php
vendored
Normal file
@ -0,0 +1,42 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
/**
|
||||
* Represents a precedence change.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class PrecedenceChange
|
||||
{
|
||||
public function __construct(
|
||||
private string $package,
|
||||
private string $version,
|
||||
private int $newPrecedence,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getPackage(): string
|
||||
{
|
||||
return $this->package;
|
||||
}
|
||||
|
||||
public function getVersion(): string
|
||||
{
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
public function getNewPrecedence(): int
|
||||
{
|
||||
return $this->newPrecedence;
|
||||
}
|
||||
}
|
||||
78
vendor/twig/twig/src/ExpressionParser/Prefix/GroupingExpressionParser.php
vendored
Normal file
78
vendor/twig/twig/src/ExpressionParser/Prefix/GroupingExpressionParser.php
vendored
Normal file
@ -0,0 +1,78 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Prefix;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\PrefixExpressionParserInterface;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ListExpression;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class GroupingExpressionParser extends AbstractExpressionParser implements PrefixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
public function parse(Parser $parser, Token $token): AbstractExpression
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
$expr = $parser->parseExpression($this->getPrecedence());
|
||||
|
||||
if ($stream->nextIf(Token::PUNCTUATION_TYPE, ')')) {
|
||||
if (!$stream->test(Token::OPERATOR_TYPE, '=>')) {
|
||||
return $expr->setExplicitParentheses();
|
||||
}
|
||||
|
||||
return new ListExpression([$expr], $token->getLine());
|
||||
}
|
||||
|
||||
// determine if we are parsing an arrow function arguments
|
||||
if (!$stream->test(Token::PUNCTUATION_TYPE, ',')) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed');
|
||||
}
|
||||
|
||||
$names = [$expr];
|
||||
while (true) {
|
||||
if ($stream->nextIf(Token::PUNCTUATION_TYPE, ')')) {
|
||||
break;
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',');
|
||||
$token = $stream->expect(Token::NAME_TYPE);
|
||||
$names[] = new ContextVariable($token->getValue(), $token->getLine());
|
||||
}
|
||||
|
||||
if (!$stream->test(Token::OPERATOR_TYPE, '=>')) {
|
||||
throw new SyntaxError('A list of variables must be followed by an arrow.', $stream->getCurrent()->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
return new ListExpression($names, $token->getLine());
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return '(';
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'Explicit group expression (a)';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
244
vendor/twig/twig/src/ExpressionParser/Prefix/LiteralExpressionParser.php
vendored
Normal file
244
vendor/twig/twig/src/ExpressionParser/Prefix/LiteralExpressionParser.php
vendored
Normal file
@ -0,0 +1,244 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Prefix;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\PrefixExpressionParserInterface;
|
||||
use Twig\Lexer;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ArrayExpression;
|
||||
use Twig\Node\Expression\Binary\ConcatBinary;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class LiteralExpressionParser extends AbstractExpressionParser implements PrefixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
private string $type = 'literal';
|
||||
|
||||
public function parse(Parser $parser, Token $token): AbstractExpression
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
switch (true) {
|
||||
case $token->test(Token::NAME_TYPE):
|
||||
$stream->next();
|
||||
switch ($token->getValue()) {
|
||||
case 'true':
|
||||
case 'TRUE':
|
||||
$this->type = 'constant';
|
||||
|
||||
return new ConstantExpression(true, $token->getLine());
|
||||
|
||||
case 'false':
|
||||
case 'FALSE':
|
||||
$this->type = 'constant';
|
||||
|
||||
return new ConstantExpression(false, $token->getLine());
|
||||
|
||||
case 'none':
|
||||
case 'NONE':
|
||||
case 'null':
|
||||
case 'NULL':
|
||||
$this->type = 'constant';
|
||||
|
||||
return new ConstantExpression(null, $token->getLine());
|
||||
|
||||
default:
|
||||
$this->type = 'variable';
|
||||
|
||||
return new ContextVariable($token->getValue(), $token->getLine());
|
||||
}
|
||||
|
||||
// no break
|
||||
case $token->test(Token::NUMBER_TYPE):
|
||||
$stream->next();
|
||||
$this->type = 'constant';
|
||||
|
||||
return new ConstantExpression($token->getValue(), $token->getLine());
|
||||
|
||||
case $token->test(Token::STRING_TYPE):
|
||||
case $token->test(Token::INTERPOLATION_START_TYPE):
|
||||
$this->type = 'string';
|
||||
|
||||
return $this->parseStringExpression($parser);
|
||||
|
||||
case $token->test(Token::PUNCTUATION_TYPE):
|
||||
// In 4.0, we should always return the node or throw an error for default
|
||||
if ($node = match ($token->getValue()) {
|
||||
'{' => $this->parseMappingExpression($parser),
|
||||
default => null,
|
||||
}) {
|
||||
return $node;
|
||||
}
|
||||
|
||||
// no break
|
||||
case $token->test(Token::OPERATOR_TYPE):
|
||||
if ('[' === $token->getValue()) {
|
||||
return $this->parseSequenceExpression($parser);
|
||||
}
|
||||
|
||||
if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) {
|
||||
// in this context, string operators are variable names
|
||||
$stream->next();
|
||||
$this->type = 'variable';
|
||||
|
||||
return new ContextVariable($token->getValue(), $token->getLine());
|
||||
}
|
||||
|
||||
if ('=' === $token->getValue() && ('==' === $stream->look(-1)->getValue() || '!=' === $stream->look(-1)->getValue())) {
|
||||
throw new SyntaxError(\sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
// no break
|
||||
default:
|
||||
throw new SyntaxError(\sprintf('Unexpected token "%s" of value "%s".', $token->toEnglish(), $token->getValue()), $token->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->type;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return 'A literal value (boolean, string, number, sequence, mapping, ...)';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
// not used
|
||||
return 0;
|
||||
}
|
||||
|
||||
private function parseStringExpression(Parser $parser)
|
||||
{
|
||||
$stream = $parser->getStream();
|
||||
|
||||
$nodes = [];
|
||||
// a string cannot be followed by another string in a single expression
|
||||
$nextCanBeString = true;
|
||||
while (true) {
|
||||
if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) {
|
||||
$nodes[] = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
$nextCanBeString = false;
|
||||
} elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) {
|
||||
$nodes[] = $parser->parseExpression();
|
||||
$stream->expect(Token::INTERPOLATION_END_TYPE);
|
||||
$nextCanBeString = true;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$expr = array_shift($nodes);
|
||||
foreach ($nodes as $node) {
|
||||
$expr = new ConcatBinary($expr, $node, $node->getTemplateLine());
|
||||
}
|
||||
|
||||
return $expr;
|
||||
}
|
||||
|
||||
private function parseSequenceExpression(Parser $parser)
|
||||
{
|
||||
$this->type = 'sequence';
|
||||
|
||||
$stream = $parser->getStream();
|
||||
$stream->expect(Token::OPERATOR_TYPE, '[', 'A sequence element was expected');
|
||||
|
||||
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A sequence element must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, ']')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
$node->addElement($parser->parseExpression());
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened sequence is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
|
||||
private function parseMappingExpression(Parser $parser)
|
||||
{
|
||||
$this->type = 'mapping';
|
||||
|
||||
$stream = $parser->getStream();
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '{', 'A mapping element was expected');
|
||||
|
||||
$node = new ArrayExpression([], $stream->getCurrent()->getLine());
|
||||
$first = true;
|
||||
while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
||||
if (!$first) {
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ',', 'A mapping value must be followed by a comma');
|
||||
|
||||
// trailing ,?
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, '}')) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
$first = false;
|
||||
|
||||
if ($stream->test(Token::OPERATOR_TYPE, '...')) {
|
||||
$node->addElement($parser->parseExpression());
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
// a mapping key can be:
|
||||
//
|
||||
// * a number -- 12
|
||||
// * a string -- 'a'
|
||||
// * a name, which is equivalent to a string -- a
|
||||
// * an expression, which must be enclosed in parentheses -- (1 + 2)
|
||||
if ($token = $stream->nextIf(Token::NAME_TYPE)) {
|
||||
$key = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
|
||||
// {a} is a shortcut for {a:a}
|
||||
if ($stream->test(Token::PUNCTUATION_TYPE, [',', '}'])) {
|
||||
$value = new ContextVariable($key->getAttribute('value'), $key->getTemplateLine());
|
||||
$node->addElement($value, $key);
|
||||
continue;
|
||||
}
|
||||
} elseif (($token = $stream->nextIf(Token::STRING_TYPE)) || $token = $stream->nextIf(Token::NUMBER_TYPE)) {
|
||||
$key = new ConstantExpression($token->getValue(), $token->getLine());
|
||||
} elseif ($stream->test(Token::OPERATOR_TYPE, '(')) {
|
||||
$key = $parser->parseExpression();
|
||||
} else {
|
||||
$current = $stream->getCurrent();
|
||||
|
||||
throw new SyntaxError(\sprintf('A mapping key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', $current->toEnglish(), $current->getValue()), $current->getLine(), $stream->getSourceContext());
|
||||
}
|
||||
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, ':', 'A mapping key must be followed by a colon (:)');
|
||||
$value = $parser->parseExpression();
|
||||
|
||||
$node->addElement($value, $key);
|
||||
}
|
||||
$stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed');
|
||||
|
||||
return $node;
|
||||
}
|
||||
}
|
||||
71
vendor/twig/twig/src/ExpressionParser/Prefix/UnaryOperatorExpressionParser.php
vendored
Normal file
71
vendor/twig/twig/src/ExpressionParser/Prefix/UnaryOperatorExpressionParser.php
vendored
Normal file
@ -0,0 +1,71 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser\Prefix;
|
||||
|
||||
use Twig\ExpressionParser\AbstractExpressionParser;
|
||||
use Twig\ExpressionParser\ExpressionParserDescriptionInterface;
|
||||
use Twig\ExpressionParser\PrecedenceChange;
|
||||
use Twig\ExpressionParser\PrefixExpressionParserInterface;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\Unary\AbstractUnary;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
final class UnaryOperatorExpressionParser extends AbstractExpressionParser implements PrefixExpressionParserInterface, ExpressionParserDescriptionInterface
|
||||
{
|
||||
public function __construct(
|
||||
/** @var class-string<AbstractUnary> */
|
||||
private string $nodeClass,
|
||||
private string $name,
|
||||
private int $precedence,
|
||||
private ?PrecedenceChange $precedenceChange = null,
|
||||
private ?string $description = null,
|
||||
private array $aliases = [],
|
||||
) {
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AbstractUnary
|
||||
*/
|
||||
public function parse(Parser $parser, Token $token): AbstractExpression
|
||||
{
|
||||
return new ($this->nodeClass)($parser->parseExpression($this->precedence), $token->getLine());
|
||||
}
|
||||
|
||||
public function getName(): string
|
||||
{
|
||||
return $this->name;
|
||||
}
|
||||
|
||||
public function getDescription(): string
|
||||
{
|
||||
return $this->description ?? '';
|
||||
}
|
||||
|
||||
public function getPrecedence(): int
|
||||
{
|
||||
return $this->precedence;
|
||||
}
|
||||
|
||||
public function getPrecedenceChange(): ?PrecedenceChange
|
||||
{
|
||||
return $this->precedenceChange;
|
||||
}
|
||||
|
||||
public function getAliases(): array
|
||||
{
|
||||
return $this->aliases;
|
||||
}
|
||||
}
|
||||
21
vendor/twig/twig/src/ExpressionParser/PrefixExpressionParserInterface.php
vendored
Normal file
21
vendor/twig/twig/src/ExpressionParser/PrefixExpressionParserInterface.php
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\ExpressionParser;
|
||||
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Parser;
|
||||
use Twig\Token;
|
||||
|
||||
interface PrefixExpressionParserInterface extends ExpressionParserInterface
|
||||
{
|
||||
public function parse(Parser $parser, Token $token): AbstractExpression;
|
||||
}
|
||||
67
vendor/twig/twig/src/Extension/AbstractExtension.php
vendored
Normal file
67
vendor/twig/twig/src/Extension/AbstractExtension.php
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
abstract class AbstractExtension implements LastModifiedExtensionInterface
|
||||
{
|
||||
public function getTokenParsers()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getNodeVisitors()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFilters()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getTests()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getFunctions()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getOperators()
|
||||
{
|
||||
return [[], []];
|
||||
}
|
||||
|
||||
public function getExpressionParsers(): array
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getLastModified(): int
|
||||
{
|
||||
$filename = (new \ReflectionClass($this))->getFileName();
|
||||
if (!is_file($filename)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
$lastModified = filemtime($filename);
|
||||
|
||||
// Track modifications of the runtime class if it exists and follows the naming convention
|
||||
if (str_ends_with($filename, 'Extension.php') && is_file($filename = substr($filename, 0, -13).'Runtime.php')) {
|
||||
$lastModified = max($lastModified, filemtime($filename));
|
||||
}
|
||||
|
||||
return $lastModified;
|
||||
}
|
||||
}
|
||||
174
vendor/twig/twig/src/Extension/AttributeExtension.php
vendored
Normal file
174
vendor/twig/twig/src/Extension/AttributeExtension.php
vendored
Normal file
@ -0,0 +1,174 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Attribute\AsTwigFilter;
|
||||
use Twig\Attribute\AsTwigFunction;
|
||||
use Twig\Attribute\AsTwigTest;
|
||||
use Twig\Environment;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* Define Twig filters, functions, and tests with PHP attributes.
|
||||
*
|
||||
* @author Jérôme Tamarelle <jerome@tamarelle.net>
|
||||
*/
|
||||
final class AttributeExtension extends AbstractExtension
|
||||
{
|
||||
private array $filters;
|
||||
private array $functions;
|
||||
private array $tests;
|
||||
|
||||
/**
|
||||
* Use a runtime class using PHP attributes to define filters, functions, and tests.
|
||||
*
|
||||
* @param class-string $class
|
||||
*/
|
||||
public function __construct(private string $class)
|
||||
{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return class-string
|
||||
*/
|
||||
public function getClass(): string
|
||||
{
|
||||
return $this->class;
|
||||
}
|
||||
|
||||
public function getFilters(): array
|
||||
{
|
||||
if (!isset($this->filters)) {
|
||||
$this->initFromAttributes();
|
||||
}
|
||||
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function getFunctions(): array
|
||||
{
|
||||
if (!isset($this->functions)) {
|
||||
$this->initFromAttributes();
|
||||
}
|
||||
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
public function getTests(): array
|
||||
{
|
||||
if (!isset($this->tests)) {
|
||||
$this->initFromAttributes();
|
||||
}
|
||||
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
public function getLastModified(): int
|
||||
{
|
||||
return max(
|
||||
filemtime(__FILE__),
|
||||
is_file($filename = (new \ReflectionClass($this->getClass()))->getFileName()) ? filemtime($filename) : 0,
|
||||
);
|
||||
}
|
||||
|
||||
private function initFromAttributes(): void
|
||||
{
|
||||
$filters = $functions = $tests = [];
|
||||
$reflectionClass = new \ReflectionClass($this->getClass());
|
||||
foreach ($reflectionClass->getMethods() as $method) {
|
||||
foreach ($method->getAttributes(AsTwigFilter::class) as $reflectionAttribute) {
|
||||
/** @var AsTwigFilter $attribute */
|
||||
$attribute = $reflectionAttribute->newInstance();
|
||||
|
||||
$callable = new TwigFilter($attribute->name, [$reflectionClass->name, $method->getName()], [
|
||||
'needs_context' => $attribute->needsContext ?? false,
|
||||
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
|
||||
'needs_charset' => $attribute->needsCharset ?? false,
|
||||
'is_variadic' => $method->isVariadic(),
|
||||
'is_safe' => $attribute->isSafe,
|
||||
'is_safe_callback' => $attribute->isSafeCallback,
|
||||
'pre_escape' => $attribute->preEscape,
|
||||
'preserves_safety' => $attribute->preservesSafety,
|
||||
'deprecation_info' => $attribute->deprecationInfo,
|
||||
]);
|
||||
|
||||
if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) {
|
||||
throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigFilter, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters()));
|
||||
}
|
||||
|
||||
$filters[$attribute->name] = $callable;
|
||||
}
|
||||
|
||||
foreach ($method->getAttributes(AsTwigFunction::class) as $reflectionAttribute) {
|
||||
/** @var AsTwigFunction $attribute */
|
||||
$attribute = $reflectionAttribute->newInstance();
|
||||
|
||||
$callable = new TwigFunction($attribute->name, [$reflectionClass->name, $method->getName()], [
|
||||
'needs_context' => $attribute->needsContext ?? false,
|
||||
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
|
||||
'needs_charset' => $attribute->needsCharset ?? false,
|
||||
'is_variadic' => $method->isVariadic(),
|
||||
'is_safe' => $attribute->isSafe,
|
||||
'is_safe_callback' => $attribute->isSafeCallback,
|
||||
'deprecation_info' => $attribute->deprecationInfo,
|
||||
]);
|
||||
|
||||
if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) {
|
||||
throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigFunction, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters()));
|
||||
}
|
||||
|
||||
$functions[$attribute->name] = $callable;
|
||||
}
|
||||
|
||||
foreach ($method->getAttributes(AsTwigTest::class) as $reflectionAttribute) {
|
||||
|
||||
/** @var AsTwigTest $attribute */
|
||||
$attribute = $reflectionAttribute->newInstance();
|
||||
|
||||
$callable = new TwigTest($attribute->name, [$reflectionClass->name, $method->getName()], [
|
||||
'needs_context' => $attribute->needsContext ?? false,
|
||||
'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method),
|
||||
'needs_charset' => $attribute->needsCharset ?? false,
|
||||
'is_variadic' => $method->isVariadic(),
|
||||
'deprecation_info' => $attribute->deprecationInfo,
|
||||
]);
|
||||
|
||||
if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) {
|
||||
throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigTest, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters()));
|
||||
}
|
||||
|
||||
$tests[$attribute->name] = $callable;
|
||||
}
|
||||
}
|
||||
|
||||
// Assign all at the end to avoid inconsistent state in case of exception
|
||||
$this->filters = array_values($filters);
|
||||
$this->functions = array_values($functions);
|
||||
$this->tests = array_values($tests);
|
||||
}
|
||||
|
||||
/**
|
||||
* Detect if the first argument of the method is the environment.
|
||||
*/
|
||||
private function needsEnvironment(\ReflectionFunctionAbstract $function): bool
|
||||
{
|
||||
if (!$parameters = $function->getParameters()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $parameters[0]->getType() instanceof \ReflectionNamedType
|
||||
&& Environment::class === $parameters[0]->getType()->getName()
|
||||
&& !$parameters[0]->isVariadic();
|
||||
}
|
||||
}
|
||||
2189
vendor/twig/twig/src/Extension/CoreExtension.php
vendored
Normal file
2189
vendor/twig/twig/src/Extension/CoreExtension.php
vendored
Normal file
File diff suppressed because it is too large
Load Diff
62
vendor/twig/twig/src/Extension/DebugExtension.php
vendored
Normal file
62
vendor/twig/twig/src/Extension/DebugExtension.php
vendored
Normal file
@ -0,0 +1,62 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\Template;
|
||||
use Twig\TemplateWrapper;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
final class DebugExtension extends AbstractExtension
|
||||
{
|
||||
public function getFunctions(): array
|
||||
{
|
||||
// dump is safe if var_dump is overridden by xdebug
|
||||
$isDumpOutputHtmlSafe = \extension_loaded('xdebug')
|
||||
// Xdebug overloads var_dump in develop mode when html_errors is enabled
|
||||
&& str_contains(\ini_get('xdebug.mode'), 'develop')
|
||||
&& (false === \ini_get('html_errors') || \ini_get('html_errors'))
|
||||
|| 'cli' === \PHP_SAPI
|
||||
;
|
||||
|
||||
return [
|
||||
new TwigFunction('dump', [self::class, 'dump'], ['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [], 'needs_context' => true, 'needs_environment' => true, 'is_variadic' => true]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public static function dump(Environment $env, $context, ...$vars)
|
||||
{
|
||||
if (!$env->isDebug()) {
|
||||
return;
|
||||
}
|
||||
|
||||
ob_start();
|
||||
|
||||
if (!$vars) {
|
||||
$vars = [];
|
||||
foreach ($context as $key => $value) {
|
||||
if (!$value instanceof Template && !$value instanceof TemplateWrapper) {
|
||||
$vars[$key] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
var_dump($vars);
|
||||
} else {
|
||||
var_dump(...$vars);
|
||||
}
|
||||
|
||||
return ob_get_clean();
|
||||
}
|
||||
}
|
||||
218
vendor/twig/twig/src/Extension/EscaperExtension.php
vendored
Normal file
218
vendor/twig/twig/src/Extension/EscaperExtension.php
vendored
Normal file
@ -0,0 +1,218 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\FileExtensionEscapingStrategy;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
use Twig\Node\Expression\Filter\RawFilter;
|
||||
use Twig\Node\Node;
|
||||
use Twig\NodeVisitor\EscaperNodeVisitor;
|
||||
use Twig\Runtime\EscaperRuntime;
|
||||
use Twig\TokenParser\AutoEscapeTokenParser;
|
||||
use Twig\TwigFilter;
|
||||
|
||||
final class EscaperExtension extends AbstractExtension
|
||||
{
|
||||
private $environment;
|
||||
private $escapers = [];
|
||||
private $escaper;
|
||||
private $defaultStrategy;
|
||||
|
||||
/**
|
||||
* @param string|false|callable $defaultStrategy An escaping strategy
|
||||
*
|
||||
* @see setDefaultStrategy()
|
||||
*/
|
||||
public function __construct($defaultStrategy = 'html')
|
||||
{
|
||||
$this->setDefaultStrategy($defaultStrategy);
|
||||
}
|
||||
|
||||
public function getTokenParsers(): array
|
||||
{
|
||||
return [new AutoEscapeTokenParser()];
|
||||
}
|
||||
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return [new EscaperNodeVisitor()];
|
||||
}
|
||||
|
||||
public function getFilters(): array
|
||||
{
|
||||
return [
|
||||
new TwigFilter('escape', [EscaperRuntime::class, 'escape'], ['is_safe_callback' => [self::class, 'escapeFilterIsSafe']]),
|
||||
new TwigFilter('e', [EscaperRuntime::class, 'escape'], ['is_safe_callback' => [self::class, 'escapeFilterIsSafe']]),
|
||||
new TwigFilter('raw', null, ['is_safe' => ['all'], 'node_class' => RawFilter::class]),
|
||||
];
|
||||
}
|
||||
|
||||
public function getLastModified(): int
|
||||
{
|
||||
return max(
|
||||
parent::getLastModified(),
|
||||
filemtime((new \ReflectionClass(EscaperRuntime::class))->getFileName()),
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated since Twig 3.10
|
||||
*/
|
||||
public function setEnvironment(Environment $environment): void
|
||||
{
|
||||
$triggerDeprecation = \func_num_args() > 1 ? func_get_arg(1) : true;
|
||||
if ($triggerDeprecation) {
|
||||
trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated and not needed if you are using methods from "Twig\Runtime\EscaperRuntime".', __METHOD__);
|
||||
}
|
||||
|
||||
$this->environment = $environment;
|
||||
$this->escaper = $environment->getRuntime(EscaperRuntime::class);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @deprecated since Twig 3.10
|
||||
*/
|
||||
public function setEscaperRuntime(EscaperRuntime $escaper)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated and not needed if you are using methods from "Twig\Runtime\EscaperRuntime".', __METHOD__);
|
||||
|
||||
$this->escaper = $escaper;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the default strategy to use when not defined by the user.
|
||||
*
|
||||
* The strategy can be a valid PHP callback that takes the template
|
||||
* name as an argument and returns the strategy to use.
|
||||
*
|
||||
* @param string|false|callable(string $templateName): string $defaultStrategy An escaping strategy
|
||||
*/
|
||||
public function setDefaultStrategy($defaultStrategy): void
|
||||
{
|
||||
if ('name' === $defaultStrategy) {
|
||||
$defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess'];
|
||||
}
|
||||
|
||||
$this->defaultStrategy = $defaultStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default strategy to use when not defined by the user.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string|false The default strategy to use for the template
|
||||
*/
|
||||
public function getDefaultStrategy(string $name)
|
||||
{
|
||||
// disable string callables to avoid calling a function named html or js,
|
||||
// or any other upcoming escaping strategy
|
||||
if (!\is_string($this->defaultStrategy) && false !== $this->defaultStrategy) {
|
||||
return \call_user_func($this->defaultStrategy, $name);
|
||||
}
|
||||
|
||||
return $this->defaultStrategy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Defines a new escaper to be used via the escape filter.
|
||||
*
|
||||
* @param string $strategy The strategy name that should be used as a strategy in the escape call
|
||||
* @param callable(Environment, string, string): string $callable A valid PHP callable
|
||||
*
|
||||
* @return void
|
||||
*
|
||||
* @deprecated since Twig 3.10
|
||||
*/
|
||||
public function setEscaper($strategy, callable $callable)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::setEscaper()" method instead (be warned that Environment is not passed anymore to the callable).', __METHOD__);
|
||||
|
||||
if (!isset($this->environment)) {
|
||||
throw new \LogicException(\sprintf('You must call "setEnvironment()" before calling "%s()".', __METHOD__));
|
||||
}
|
||||
|
||||
$this->escapers[$strategy] = $callable;
|
||||
$callable = function ($string, $charset) use ($callable) {
|
||||
return $callable($this->environment, $string, $charset);
|
||||
};
|
||||
|
||||
$this->escaper->setEscaper($strategy, $callable);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets all defined escapers.
|
||||
*
|
||||
* @return array<string, callable(Environment, string, string): string> An array of escapers
|
||||
*
|
||||
* @deprecated since Twig 3.10
|
||||
*/
|
||||
public function getEscapers()
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::getEscaper()" method instead.', __METHOD__);
|
||||
|
||||
return $this->escapers;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @deprecated since Twig 3.10
|
||||
*/
|
||||
public function setSafeClasses(array $safeClasses = [])
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::setSafeClasses()" method instead.', __METHOD__);
|
||||
|
||||
if (!isset($this->escaper)) {
|
||||
throw new \LogicException(\sprintf('You must call "setEnvironment()" before calling "%s()".', __METHOD__));
|
||||
}
|
||||
|
||||
$this->escaper->setSafeClasses($safeClasses);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*
|
||||
* @deprecated since Twig 3.10
|
||||
*/
|
||||
public function addSafeClass(string $class, array $strategies)
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::addSafeClass()" method instead.', __METHOD__);
|
||||
|
||||
if (!isset($this->escaper)) {
|
||||
throw new \LogicException(\sprintf('You must call "setEnvironment()" before calling "%s()".', __METHOD__));
|
||||
}
|
||||
|
||||
$this->escaper->addSafeClass($class, $strategies);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*
|
||||
* @return array<string>
|
||||
*/
|
||||
public static function escapeFilterIsSafe(Node $filterArgs)
|
||||
{
|
||||
foreach ($filterArgs as $arg) {
|
||||
if ($arg instanceof ConstantExpression) {
|
||||
return [$arg->getAttribute('value')];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
return ['html'];
|
||||
}
|
||||
}
|
||||
77
vendor/twig/twig/src/Extension/ExtensionInterface.php
vendored
Normal file
77
vendor/twig/twig/src/Extension/ExtensionInterface.php
vendored
Normal file
@ -0,0 +1,77 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\ExpressionParser\ExpressionParserInterface;
|
||||
use Twig\ExpressionParser\PrecedenceChange;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* Interface implemented by extension classes.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @method array<ExpressionParserInterface> getExpressionParsers()
|
||||
*/
|
||||
interface ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* Returns the token parser instances to add to the existing list.
|
||||
*
|
||||
* @return TokenParserInterface[]
|
||||
*/
|
||||
public function getTokenParsers();
|
||||
|
||||
/**
|
||||
* Returns the node visitor instances to add to the existing list.
|
||||
*
|
||||
* @return NodeVisitorInterface[]
|
||||
*/
|
||||
public function getNodeVisitors();
|
||||
|
||||
/**
|
||||
* Returns a list of filters to add to the existing list.
|
||||
*
|
||||
* @return TwigFilter[]
|
||||
*/
|
||||
public function getFilters();
|
||||
|
||||
/**
|
||||
* Returns a list of tests to add to the existing list.
|
||||
*
|
||||
* @return TwigTest[]
|
||||
*/
|
||||
public function getTests();
|
||||
|
||||
/**
|
||||
* Returns a list of functions to add to the existing list.
|
||||
*
|
||||
* @return TwigFunction[]
|
||||
*/
|
||||
public function getFunctions();
|
||||
|
||||
/**
|
||||
* Returns a list of operators to add to the existing list.
|
||||
*
|
||||
* @return array<array>
|
||||
*
|
||||
* @psalm-return array{
|
||||
* array<string, array{precedence: int, precedence_change?: PrecedenceChange, class: class-string<AbstractUnary>}>,
|
||||
* array<string, array{precedence: int, precedence_change?: PrecedenceChange, class?: class-string<AbstractBinary>, associativity: ExpressionParser::OPERATOR_*}>
|
||||
* }
|
||||
*/
|
||||
public function getOperators();
|
||||
}
|
||||
25
vendor/twig/twig/src/Extension/GlobalsInterface.php
vendored
Normal file
25
vendor/twig/twig/src/Extension/GlobalsInterface.php
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
/**
|
||||
* Allows Twig extensions to add globals to the context.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface GlobalsInterface
|
||||
{
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getGlobals(): array;
|
||||
}
|
||||
23
vendor/twig/twig/src/Extension/LastModifiedExtensionInterface.php
vendored
Normal file
23
vendor/twig/twig/src/Extension/LastModifiedExtensionInterface.php
vendored
Normal file
@ -0,0 +1,23 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
interface LastModifiedExtensionInterface extends ExtensionInterface
|
||||
{
|
||||
/**
|
||||
* Returns the last modification time of the extension for cache invalidation.
|
||||
*
|
||||
* This timestamp should be the last time the source code of the extension class
|
||||
* and all its dependencies were modified (including the Runtime class).
|
||||
*/
|
||||
public function getLastModified(): int;
|
||||
}
|
||||
27
vendor/twig/twig/src/Extension/OptimizerExtension.php
vendored
Normal file
27
vendor/twig/twig/src/Extension/OptimizerExtension.php
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\OptimizerNodeVisitor;
|
||||
|
||||
final class OptimizerExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(
|
||||
private int $optimizers = -1,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return [new OptimizerNodeVisitor($this->optimizers)];
|
||||
}
|
||||
}
|
||||
52
vendor/twig/twig/src/Extension/ProfilerExtension.php
vendored
Normal file
52
vendor/twig/twig/src/Extension/ProfilerExtension.php
vendored
Normal file
@ -0,0 +1,52 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Profiler\NodeVisitor\ProfilerNodeVisitor;
|
||||
use Twig\Profiler\Profile;
|
||||
|
||||
class ProfilerExtension extends AbstractExtension
|
||||
{
|
||||
private $actives = [];
|
||||
|
||||
public function __construct(Profile $profile)
|
||||
{
|
||||
$this->actives[] = $profile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function enter(Profile $profile)
|
||||
{
|
||||
$this->actives[0]->addProfile($profile);
|
||||
array_unshift($this->actives, $profile);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function leave(Profile $profile)
|
||||
{
|
||||
$profile->leave();
|
||||
array_shift($this->actives);
|
||||
|
||||
if (1 === \count($this->actives)) {
|
||||
$this->actives[0]->leave();
|
||||
}
|
||||
}
|
||||
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return [new ProfilerNodeVisitor(static::class)];
|
||||
}
|
||||
}
|
||||
19
vendor/twig/twig/src/Extension/RuntimeExtensionInterface.php
vendored
Normal file
19
vendor/twig/twig/src/Extension/RuntimeExtensionInterface.php
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
/**
|
||||
* @author Grégoire Pineau <lyrixx@lyrixx.info>
|
||||
*/
|
||||
interface RuntimeExtensionInterface
|
||||
{
|
||||
}
|
||||
168
vendor/twig/twig/src/Extension/SandboxExtension.php
vendored
Normal file
168
vendor/twig/twig/src/Extension/SandboxExtension.php
vendored
Normal file
@ -0,0 +1,168 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\SandboxNodeVisitor;
|
||||
use Twig\Sandbox\SecurityNotAllowedMethodError;
|
||||
use Twig\Sandbox\SecurityNotAllowedPropertyError;
|
||||
use Twig\Sandbox\SecurityPolicyInterface;
|
||||
use Twig\Sandbox\SourcePolicyInterface;
|
||||
use Twig\Source;
|
||||
use Twig\TokenParser\SandboxTokenParser;
|
||||
|
||||
final class SandboxExtension extends AbstractExtension
|
||||
{
|
||||
private $sandboxedGlobally;
|
||||
private $sandboxed;
|
||||
private $policy;
|
||||
private $sourcePolicy;
|
||||
|
||||
public function __construct(SecurityPolicyInterface $policy, $sandboxed = false, ?SourcePolicyInterface $sourcePolicy = null)
|
||||
{
|
||||
$this->policy = $policy;
|
||||
$this->sandboxedGlobally = $sandboxed;
|
||||
$this->sourcePolicy = $sourcePolicy;
|
||||
}
|
||||
|
||||
public function getTokenParsers(): array
|
||||
{
|
||||
return [new SandboxTokenParser()];
|
||||
}
|
||||
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return [new SandboxNodeVisitor()];
|
||||
}
|
||||
|
||||
public function enableSandbox(): void
|
||||
{
|
||||
$this->sandboxed = true;
|
||||
}
|
||||
|
||||
public function disableSandbox(): void
|
||||
{
|
||||
$this->sandboxed = false;
|
||||
}
|
||||
|
||||
public function isSandboxed(?Source $source = null): bool
|
||||
{
|
||||
return $this->sandboxedGlobally || $this->sandboxed || $this->isSourceSandboxed($source);
|
||||
}
|
||||
|
||||
public function isSandboxedGlobally(): bool
|
||||
{
|
||||
return $this->sandboxedGlobally;
|
||||
}
|
||||
|
||||
private function isSourceSandboxed(?Source $source): bool
|
||||
{
|
||||
if (null === $source || null === $this->sourcePolicy) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return $this->sourcePolicy->enableSandbox($source);
|
||||
}
|
||||
|
||||
public function setSecurityPolicy(SecurityPolicyInterface $policy): void
|
||||
{
|
||||
$this->policy = $policy;
|
||||
}
|
||||
|
||||
public function getSecurityPolicy(): SecurityPolicyInterface
|
||||
{
|
||||
return $this->policy;
|
||||
}
|
||||
|
||||
public function checkSecurity($tags, $filters, $functions, ?Source $source = null): void
|
||||
{
|
||||
if ($this->isSandboxed($source)) {
|
||||
$this->policy->checkSecurity($tags, $filters, $functions);
|
||||
}
|
||||
}
|
||||
|
||||
public function checkMethodAllowed($obj, $method, int $lineno = -1, ?Source $source = null): void
|
||||
{
|
||||
if ($this->isSandboxed($source)) {
|
||||
try {
|
||||
$this->policy->checkMethodAllowed($obj, $method);
|
||||
} catch (SecurityNotAllowedMethodError $e) {
|
||||
$e->setSourceContext($source);
|
||||
$e->setTemplateLine($lineno);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function checkPropertyAllowed($obj, $property, int $lineno = -1, ?Source $source = null): void
|
||||
{
|
||||
if ($this->isSandboxed($source)) {
|
||||
try {
|
||||
$this->policy->checkPropertyAllowed($obj, $property);
|
||||
} catch (SecurityNotAllowedPropertyError $e) {
|
||||
$e->setSourceContext($source);
|
||||
$e->setTemplateLine($lineno);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws SecurityNotAllowedMethodError
|
||||
*/
|
||||
public function ensureToStringAllowed($obj, int $lineno = -1, ?Source $source = null)
|
||||
{
|
||||
if (\is_array($obj)) {
|
||||
$this->ensureToStringAllowedForArray($obj, $lineno, $source);
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
if ($obj instanceof \Stringable && $this->isSandboxed($source)) {
|
||||
try {
|
||||
$this->policy->checkMethodAllowed($obj, '__toString');
|
||||
} catch (SecurityNotAllowedMethodError $e) {
|
||||
$e->setSourceContext($source);
|
||||
$e->setTemplateLine($lineno);
|
||||
|
||||
throw $e;
|
||||
}
|
||||
}
|
||||
|
||||
return $obj;
|
||||
}
|
||||
|
||||
private function ensureToStringAllowedForArray(array $obj, int $lineno, ?Source $source, array &$stack = []): void
|
||||
{
|
||||
foreach ($obj as $k => $v) {
|
||||
if (!$v) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!\is_array($v)) {
|
||||
$this->ensureToStringAllowed($v, $lineno, $source);
|
||||
continue;
|
||||
}
|
||||
|
||||
if ($r = \ReflectionReference::fromArrayElement($obj, $k)) {
|
||||
if (isset($stack[$r->getId()])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$stack[$r->getId()] = true;
|
||||
}
|
||||
|
||||
$this->ensureToStringAllowedForArray($v, $lineno, $source, $stack);
|
||||
}
|
||||
}
|
||||
}
|
||||
100
vendor/twig/twig/src/Extension/StagingExtension.php
vendored
Normal file
100
vendor/twig/twig/src/Extension/StagingExtension.php
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
use Twig\TwigFilter;
|
||||
use Twig\TwigFunction;
|
||||
use Twig\TwigTest;
|
||||
|
||||
/**
|
||||
* Used by \Twig\Environment as a staging area.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class StagingExtension extends AbstractExtension
|
||||
{
|
||||
private $functions = [];
|
||||
private $filters = [];
|
||||
private $visitors = [];
|
||||
private $tokenParsers = [];
|
||||
private $tests = [];
|
||||
|
||||
public function addFunction(TwigFunction $function): void
|
||||
{
|
||||
if (isset($this->functions[$function->getName()])) {
|
||||
throw new \LogicException(\sprintf('Function "%s" is already registered.', $function->getName()));
|
||||
}
|
||||
|
||||
$this->functions[$function->getName()] = $function;
|
||||
}
|
||||
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
public function addFilter(TwigFilter $filter): void
|
||||
{
|
||||
if (isset($this->filters[$filter->getName()])) {
|
||||
throw new \LogicException(\sprintf('Filter "%s" is already registered.', $filter->getName()));
|
||||
}
|
||||
|
||||
$this->filters[$filter->getName()] = $filter;
|
||||
}
|
||||
|
||||
public function getFilters(): array
|
||||
{
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function addNodeVisitor(NodeVisitorInterface $visitor): void
|
||||
{
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return $this->visitors;
|
||||
}
|
||||
|
||||
public function addTokenParser(TokenParserInterface $parser): void
|
||||
{
|
||||
if (isset($this->tokenParsers[$parser->getTag()])) {
|
||||
throw new \LogicException(\sprintf('Tag "%s" is already registered.', $parser->getTag()));
|
||||
}
|
||||
|
||||
$this->tokenParsers[$parser->getTag()] = $parser;
|
||||
}
|
||||
|
||||
public function getTokenParsers(): array
|
||||
{
|
||||
return $this->tokenParsers;
|
||||
}
|
||||
|
||||
public function addTest(TwigTest $test): void
|
||||
{
|
||||
if (isset($this->tests[$test->getName()])) {
|
||||
throw new \LogicException(\sprintf('Test "%s" is already registered.', $test->getName()));
|
||||
}
|
||||
|
||||
$this->tests[$test->getName()] = $test;
|
||||
}
|
||||
|
||||
public function getTests(): array
|
||||
{
|
||||
return $this->tests;
|
||||
}
|
||||
}
|
||||
40
vendor/twig/twig/src/Extension/StringLoaderExtension.php
vendored
Normal file
40
vendor/twig/twig/src/Extension/StringLoaderExtension.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\Environment;
|
||||
use Twig\TemplateWrapper;
|
||||
use Twig\TwigFunction;
|
||||
|
||||
final class StringLoaderExtension extends AbstractExtension
|
||||
{
|
||||
public function getFunctions(): array
|
||||
{
|
||||
return [
|
||||
new TwigFunction('template_from_string', [self::class, 'templateFromString'], ['needs_environment' => true]),
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Loads a template from a string.
|
||||
*
|
||||
* {{ include(template_from_string("Hello {{ name }}")) }}
|
||||
*
|
||||
* @param string|null $name An optional name of the template to be used in error messages
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
public static function templateFromString(Environment $env, string|\Stringable $template, ?string $name = null): TemplateWrapper
|
||||
{
|
||||
return $env->createTemplate((string) $template, $name);
|
||||
}
|
||||
}
|
||||
30
vendor/twig/twig/src/Extension/YieldNotReadyExtension.php
vendored
Normal file
30
vendor/twig/twig/src/Extension/YieldNotReadyExtension.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Extension;
|
||||
|
||||
use Twig\NodeVisitor\YieldNotReadyNodeVisitor;
|
||||
|
||||
/**
|
||||
* @internal to be removed in Twig 4
|
||||
*/
|
||||
final class YieldNotReadyExtension extends AbstractExtension
|
||||
{
|
||||
public function __construct(
|
||||
private bool $useYield,
|
||||
) {
|
||||
}
|
||||
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
return [new YieldNotReadyNodeVisitor($this->useYield)];
|
||||
}
|
||||
}
|
||||
547
vendor/twig/twig/src/ExtensionSet.php
vendored
Normal file
547
vendor/twig/twig/src/ExtensionSet.php
vendored
Normal file
@ -0,0 +1,547 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Error\RuntimeError;
|
||||
use Twig\ExpressionParser\ExpressionParsers;
|
||||
use Twig\ExpressionParser\Infix\BinaryOperatorExpressionParser;
|
||||
use Twig\ExpressionParser\InfixAssociativity;
|
||||
use Twig\ExpressionParser\InfixExpressionParserInterface;
|
||||
use Twig\ExpressionParser\PrecedenceChange;
|
||||
use Twig\ExpressionParser\Prefix\UnaryOperatorExpressionParser;
|
||||
use Twig\Extension\AttributeExtension;
|
||||
use Twig\Extension\ExtensionInterface;
|
||||
use Twig\Extension\GlobalsInterface;
|
||||
use Twig\Extension\LastModifiedExtensionInterface;
|
||||
use Twig\Extension\StagingExtension;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\NodeVisitor\NodeVisitorInterface;
|
||||
use Twig\TokenParser\TokenParserInterface;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class ExtensionSet
|
||||
{
|
||||
private $extensions;
|
||||
private $initialized = false;
|
||||
private $runtimeInitialized = false;
|
||||
private $staging;
|
||||
private $parsers;
|
||||
private $visitors;
|
||||
/** @var array<string, TwigFilter> */
|
||||
private $filters;
|
||||
/** @var array<string, TwigFilter> */
|
||||
private $dynamicFilters;
|
||||
/** @var array<string, TwigTest> */
|
||||
private $tests;
|
||||
/** @var array<string, TwigTest> */
|
||||
private $dynamicTests;
|
||||
/** @var array<string, TwigFunction> */
|
||||
private $functions;
|
||||
/** @var array<string, TwigFunction> */
|
||||
private $dynamicFunctions;
|
||||
private ExpressionParsers $expressionParsers;
|
||||
/** @var array<string, mixed>|null */
|
||||
private $globals;
|
||||
/** @var array<callable(string): (TwigFunction|false)> */
|
||||
private $functionCallbacks = [];
|
||||
/** @var array<callable(string): (TwigFilter|false)> */
|
||||
private $filterCallbacks = [];
|
||||
/** @var array<callable(string): (TokenParserInterface|false)> */
|
||||
private $parserCallbacks = [];
|
||||
private $lastModified = 0;
|
||||
|
||||
public function __construct()
|
||||
{
|
||||
$this->staging = new StagingExtension();
|
||||
}
|
||||
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function initRuntime()
|
||||
{
|
||||
$this->runtimeInitialized = true;
|
||||
}
|
||||
|
||||
public function hasExtension(string $class): bool
|
||||
{
|
||||
return isset($this->extensions[ltrim($class, '\\')]);
|
||||
}
|
||||
|
||||
public function getExtension(string $class): ExtensionInterface
|
||||
{
|
||||
$class = ltrim($class, '\\');
|
||||
|
||||
if (!isset($this->extensions[$class])) {
|
||||
throw new RuntimeError(\sprintf('The "%s" extension is not enabled.', $class));
|
||||
}
|
||||
|
||||
return $this->extensions[$class];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param ExtensionInterface[] $extensions
|
||||
*/
|
||||
public function setExtensions(array $extensions): void
|
||||
{
|
||||
foreach ($extensions as $extension) {
|
||||
$this->addExtension($extension);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ExtensionInterface[]
|
||||
*/
|
||||
public function getExtensions(): array
|
||||
{
|
||||
return $this->extensions;
|
||||
}
|
||||
|
||||
public function getSignature(): string
|
||||
{
|
||||
return json_encode(array_keys($this->extensions));
|
||||
}
|
||||
|
||||
public function isInitialized(): bool
|
||||
{
|
||||
return $this->initialized || $this->runtimeInitialized;
|
||||
}
|
||||
|
||||
public function getLastModified(): int
|
||||
{
|
||||
if (0 !== $this->lastModified) {
|
||||
return $this->lastModified;
|
||||
}
|
||||
|
||||
$lastModified = 0;
|
||||
foreach ($this->extensions as $extension) {
|
||||
if ($extension instanceof LastModifiedExtensionInterface) {
|
||||
$lastModified = max($extension->getLastModified(), $lastModified);
|
||||
} else {
|
||||
$r = new \ReflectionObject($extension);
|
||||
if (is_file($r->getFileName())) {
|
||||
$lastModified = max(filemtime($r->getFileName()), $lastModified);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $this->lastModified = $lastModified;
|
||||
}
|
||||
|
||||
public function addExtension(ExtensionInterface $extension): void
|
||||
{
|
||||
if ($extension instanceof AttributeExtension) {
|
||||
$class = $extension->getClass();
|
||||
} else {
|
||||
$class = $extension::class;
|
||||
}
|
||||
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(\sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class));
|
||||
}
|
||||
|
||||
if (isset($this->extensions[$class])) {
|
||||
throw new \LogicException(\sprintf('Unable to register extension "%s" as it is already registered.', $class));
|
||||
}
|
||||
|
||||
$this->extensions[$class] = $extension;
|
||||
}
|
||||
|
||||
public function addFunction(TwigFunction $function): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(\sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName()));
|
||||
}
|
||||
|
||||
$this->staging->addFunction($function);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFunction[]
|
||||
*/
|
||||
public function getFunctions(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->functions;
|
||||
}
|
||||
|
||||
public function getFunction(string $name): ?TwigFunction
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->functions[$name])) {
|
||||
return $this->functions[$name];
|
||||
}
|
||||
|
||||
foreach ($this->dynamicFunctions as $pattern => $function) {
|
||||
if (preg_match($pattern, $name, $matches)) {
|
||||
array_shift($matches);
|
||||
|
||||
return $function->withDynamicArguments($name, $function->getName(), $matches);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->functionCallbacks as $callback) {
|
||||
if (false !== $function = $callback($name)) {
|
||||
return $function;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): (TwigFunction|false) $callable
|
||||
*/
|
||||
public function registerUndefinedFunctionCallback(callable $callable): void
|
||||
{
|
||||
$this->functionCallbacks[] = $callable;
|
||||
}
|
||||
|
||||
public function addFilter(TwigFilter $filter): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(\sprintf('Unable to add filter "%s" as extensions have already been initialized.', $filter->getName()));
|
||||
}
|
||||
|
||||
$this->staging->addFilter($filter);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigFilter[]
|
||||
*/
|
||||
public function getFilters(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->filters;
|
||||
}
|
||||
|
||||
public function getFilter(string $name): ?TwigFilter
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->filters[$name])) {
|
||||
return $this->filters[$name];
|
||||
}
|
||||
|
||||
foreach ($this->dynamicFilters as $pattern => $filter) {
|
||||
if (preg_match($pattern, $name, $matches)) {
|
||||
array_shift($matches);
|
||||
|
||||
return $filter->withDynamicArguments($name, $filter->getName(), $matches);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($this->filterCallbacks as $callback) {
|
||||
if (false !== $filter = $callback($name)) {
|
||||
return $filter;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): (TwigFilter|false) $callable
|
||||
*/
|
||||
public function registerUndefinedFilterCallback(callable $callable): void
|
||||
{
|
||||
$this->filterCallbacks[] = $callable;
|
||||
}
|
||||
|
||||
public function addNodeVisitor(NodeVisitorInterface $visitor): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.');
|
||||
}
|
||||
|
||||
$this->staging->addNodeVisitor($visitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return NodeVisitorInterface[]
|
||||
*/
|
||||
public function getNodeVisitors(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->visitors;
|
||||
}
|
||||
|
||||
public function addTokenParser(TokenParserInterface $parser): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException('Unable to add a token parser as extensions have already been initialized.');
|
||||
}
|
||||
|
||||
$this->staging->addTokenParser($parser);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TokenParserInterface[]
|
||||
*/
|
||||
public function getTokenParsers(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->parsers;
|
||||
}
|
||||
|
||||
public function getTokenParser(string $name): ?TokenParserInterface
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->parsers[$name])) {
|
||||
return $this->parsers[$name];
|
||||
}
|
||||
|
||||
foreach ($this->parserCallbacks as $callback) {
|
||||
if (false !== $parser = $callback($name)) {
|
||||
return $parser;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param callable(string): (TokenParserInterface|false) $callable
|
||||
*/
|
||||
public function registerUndefinedTokenParserCallback(callable $callable): void
|
||||
{
|
||||
$this->parserCallbacks[] = $callable;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return array<string, mixed>
|
||||
*/
|
||||
public function getGlobals(): array
|
||||
{
|
||||
if (null !== $this->globals) {
|
||||
return $this->globals;
|
||||
}
|
||||
|
||||
$globals = [];
|
||||
foreach ($this->extensions as $extension) {
|
||||
if (!$extension instanceof GlobalsInterface) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$globals = array_merge($globals, $extension->getGlobals());
|
||||
}
|
||||
|
||||
if ($this->initialized) {
|
||||
$this->globals = $globals;
|
||||
}
|
||||
|
||||
return $globals;
|
||||
}
|
||||
|
||||
public function resetGlobals(): void
|
||||
{
|
||||
$this->globals = null;
|
||||
}
|
||||
|
||||
public function addTest(TwigTest $test): void
|
||||
{
|
||||
if ($this->initialized) {
|
||||
throw new \LogicException(\sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName()));
|
||||
}
|
||||
|
||||
$this->staging->addTest($test);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return TwigTest[]
|
||||
*/
|
||||
public function getTests(): array
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->tests;
|
||||
}
|
||||
|
||||
public function getTest(string $name): ?TwigTest
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
if (isset($this->tests[$name])) {
|
||||
return $this->tests[$name];
|
||||
}
|
||||
|
||||
foreach ($this->dynamicTests as $pattern => $test) {
|
||||
if (preg_match($pattern, $name, $matches)) {
|
||||
array_shift($matches);
|
||||
|
||||
return $test->withDynamicArguments($name, $test->getName(), $matches);
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public function getExpressionParsers(): ExpressionParsers
|
||||
{
|
||||
if (!$this->initialized) {
|
||||
$this->initExtensions();
|
||||
}
|
||||
|
||||
return $this->expressionParsers;
|
||||
}
|
||||
|
||||
private function initExtensions(): void
|
||||
{
|
||||
$this->parsers = [];
|
||||
$this->filters = [];
|
||||
$this->functions = [];
|
||||
$this->tests = [];
|
||||
$this->dynamicFilters = [];
|
||||
$this->dynamicFunctions = [];
|
||||
$this->dynamicTests = [];
|
||||
$this->visitors = [];
|
||||
$this->expressionParsers = new ExpressionParsers();
|
||||
|
||||
foreach ($this->extensions as $extension) {
|
||||
$this->initExtension($extension);
|
||||
}
|
||||
$this->initExtension($this->staging);
|
||||
// Done at the end only, so that an exception during initialization does not mark the environment as initialized when catching the exception
|
||||
$this->initialized = true;
|
||||
}
|
||||
|
||||
private function initExtension(ExtensionInterface $extension): void
|
||||
{
|
||||
// filters
|
||||
foreach ($extension->getFilters() as $filter) {
|
||||
$this->filters[$name = $filter->getName()] = $filter;
|
||||
if (str_contains($name, '*')) {
|
||||
$this->dynamicFilters['#^'.str_replace('\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $filter;
|
||||
}
|
||||
}
|
||||
|
||||
// functions
|
||||
foreach ($extension->getFunctions() as $function) {
|
||||
$this->functions[$name = $function->getName()] = $function;
|
||||
if (str_contains($name, '*')) {
|
||||
$this->dynamicFunctions['#^'.str_replace('\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $function;
|
||||
}
|
||||
}
|
||||
|
||||
// tests
|
||||
foreach ($extension->getTests() as $test) {
|
||||
$this->tests[$name = $test->getName()] = $test;
|
||||
if (str_contains($name, '*')) {
|
||||
$this->dynamicTests['#^'.str_replace('\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $test;
|
||||
}
|
||||
}
|
||||
|
||||
// token parsers
|
||||
foreach ($extension->getTokenParsers() as $parser) {
|
||||
if (!$parser instanceof TokenParserInterface) {
|
||||
throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.');
|
||||
}
|
||||
|
||||
$this->parsers[$parser->getTag()] = $parser;
|
||||
}
|
||||
|
||||
// node visitors
|
||||
foreach ($extension->getNodeVisitors() as $visitor) {
|
||||
$this->visitors[] = $visitor;
|
||||
}
|
||||
|
||||
// expression parsers
|
||||
if (method_exists($extension, 'getExpressionParsers')) {
|
||||
$this->expressionParsers->add($extension->getExpressionParsers());
|
||||
}
|
||||
|
||||
$operators = $extension->getOperators();
|
||||
if (!\is_array($operators)) {
|
||||
throw new \InvalidArgumentException(\sprintf('"%s::getOperators()" must return an array with operators, got "%s".', $extension::class, get_debug_type($operators).(\is_resource($operators) ? '' : '#'.$operators)));
|
||||
}
|
||||
|
||||
if (2 !== \count($operators)) {
|
||||
throw new \InvalidArgumentException(\sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', $extension::class, \count($operators)));
|
||||
}
|
||||
|
||||
$expressionParsers = [];
|
||||
foreach ($operators[0] as $operator => $op) {
|
||||
$expressionParsers[] = new UnaryOperatorExpressionParser($op['class'], $operator, $op['precedence'], $op['precedence_change'] ?? null, '', $op['aliases'] ?? []);
|
||||
}
|
||||
foreach ($operators[1] as $operator => $op) {
|
||||
$op['associativity'] = match ($op['associativity']) {
|
||||
1 => InfixAssociativity::Left,
|
||||
2 => InfixAssociativity::Right,
|
||||
default => throw new \InvalidArgumentException(\sprintf('Invalid associativity "%s" for operator "%s".', $op['associativity'], $operator)),
|
||||
};
|
||||
|
||||
if (isset($op['callable'])) {
|
||||
$expressionParsers[] = $this->convertInfixExpressionParser($op['class'], $operator, $op['precedence'], $op['associativity'], $op['precedence_change'] ?? null, $op['aliases'] ?? [], $op['callable']);
|
||||
} else {
|
||||
$expressionParsers[] = new BinaryOperatorExpressionParser($op['class'], $operator, $op['precedence'], $op['associativity'], $op['precedence_change'] ?? null, '', $op['aliases'] ?? []);
|
||||
}
|
||||
}
|
||||
|
||||
if (\count($expressionParsers)) {
|
||||
trigger_deprecation('twig/twig', '3.21', \sprintf('Extension "%s" uses the old signature for "getOperators()", please implement "getExpressionParsers()" instead.', $extension::class));
|
||||
|
||||
$this->expressionParsers->add($expressionParsers);
|
||||
}
|
||||
}
|
||||
|
||||
private function convertInfixExpressionParser(string $nodeClass, string $operator, int $precedence, InfixAssociativity $associativity, ?PrecedenceChange $precedenceChange, array $aliases, callable $callable): InfixExpressionParserInterface
|
||||
{
|
||||
trigger_deprecation('twig/twig', '3.21', \sprintf('Using a non-ExpressionParserInterface object to define the "%s" binary operator is deprecated.', $operator));
|
||||
|
||||
return new class($nodeClass, $operator, $precedence, $associativity, $precedenceChange, $aliases, $callable) extends BinaryOperatorExpressionParser {
|
||||
public function __construct(
|
||||
string $nodeClass,
|
||||
string $operator,
|
||||
int $precedence,
|
||||
InfixAssociativity $associativity = InfixAssociativity::Left,
|
||||
?PrecedenceChange $precedenceChange = null,
|
||||
array $aliases = [],
|
||||
private $callable = null,
|
||||
) {
|
||||
parent::__construct($nodeClass, $operator, $precedence, $associativity, $precedenceChange, $aliases);
|
||||
}
|
||||
|
||||
public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression
|
||||
{
|
||||
return ($this->callable)($parser, $expr);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
61
vendor/twig/twig/src/FileExtensionEscapingStrategy.php
vendored
Normal file
61
vendor/twig/twig/src/FileExtensionEscapingStrategy.php
vendored
Normal file
@ -0,0 +1,61 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
/**
|
||||
* Default autoescaping strategy based on file names.
|
||||
*
|
||||
* This strategy sets the HTML as the default autoescaping strategy,
|
||||
* but changes it based on the template name.
|
||||
*
|
||||
* Note that there is no runtime performance impact as the
|
||||
* default autoescaping strategy is set at compilation time.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FileExtensionEscapingStrategy
|
||||
{
|
||||
/**
|
||||
* Guesses the best autoescaping strategy based on the file name.
|
||||
*
|
||||
* @param string $name The template name
|
||||
*
|
||||
* @return string|false The escaping strategy name to use or false to disable
|
||||
*/
|
||||
public static function guess(string $name)
|
||||
{
|
||||
if (\in_array(substr($name, -1), ['/', '\\'], true)) {
|
||||
return 'html'; // return html for directories
|
||||
}
|
||||
|
||||
if (str_ends_with($name, '.twig')) {
|
||||
$name = substr($name, 0, -5);
|
||||
}
|
||||
|
||||
$extension = pathinfo($name, \PATHINFO_EXTENSION);
|
||||
|
||||
switch ($extension) {
|
||||
case 'js':
|
||||
case 'json':
|
||||
return 'js';
|
||||
|
||||
case 'css':
|
||||
return 'css';
|
||||
|
||||
case 'txt':
|
||||
return false;
|
||||
|
||||
default:
|
||||
return 'html';
|
||||
}
|
||||
}
|
||||
}
|
||||
591
vendor/twig/twig/src/Lexer.php
vendored
Normal file
591
vendor/twig/twig/src/Lexer.php
vendored
Normal file
@ -0,0 +1,591 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
use Twig\Error\SyntaxError;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Lexer
|
||||
{
|
||||
private $isInitialized = false;
|
||||
|
||||
private $tokens;
|
||||
private $code;
|
||||
private $cursor;
|
||||
private $lineno;
|
||||
private $end;
|
||||
private $state;
|
||||
private $states;
|
||||
private $brackets;
|
||||
private $env;
|
||||
private $source;
|
||||
private $options;
|
||||
private $regexes;
|
||||
private $position;
|
||||
private $positions;
|
||||
private $currentVarBlockLine;
|
||||
private array $openingBrackets = ['{', '(', '['];
|
||||
private array $closingBrackets = ['}', ')', ']'];
|
||||
|
||||
public const STATE_DATA = 0;
|
||||
public const STATE_BLOCK = 1;
|
||||
public const STATE_VAR = 2;
|
||||
public const STATE_STRING = 3;
|
||||
public const STATE_INTERPOLATION = 4;
|
||||
|
||||
public const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A';
|
||||
public const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As';
|
||||
|
||||
public const REGEX_NUMBER = '/(?(DEFINE)
|
||||
(?<LNUM>[0-9]+(_[0-9]+)*) # Integers (with underscores) 123_456
|
||||
(?<FRAC>\.(?&LNUM)) # Fractional part .456
|
||||
(?<EXPONENT>[eE][+-]?(?&LNUM)) # Exponent part E+10
|
||||
(?<DNUM>(?&LNUM)(?:(?&FRAC))?) # Decimal number 123_456.456
|
||||
)(?:(?&DNUM)(?:(?&EXPONENT))?) # 123_456.456E+10
|
||||
/Ax';
|
||||
|
||||
public const REGEX_DQ_STRING_DELIM = '/"/A';
|
||||
public const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As';
|
||||
public const REGEX_INLINE_COMMENT = '/#[^\n]*/A';
|
||||
public const PUNCTUATION = '()[]{}?:.,|';
|
||||
|
||||
private const SPECIAL_CHARS = [
|
||||
'f' => "\f",
|
||||
'n' => "\n",
|
||||
'r' => "\r",
|
||||
't' => "\t",
|
||||
'v' => "\v",
|
||||
];
|
||||
|
||||
public function __construct(Environment $env, array $options = [])
|
||||
{
|
||||
$this->env = $env;
|
||||
|
||||
$this->options = array_merge([
|
||||
'tag_comment' => ['{#', '#}'],
|
||||
'tag_block' => ['{%', '%}'],
|
||||
'tag_variable' => ['{{', '}}'],
|
||||
'whitespace_trim' => '-',
|
||||
'whitespace_line_trim' => '~',
|
||||
'whitespace_line_chars' => ' \t\0\x0B',
|
||||
'interpolation' => ['#{', '}'],
|
||||
], $options);
|
||||
}
|
||||
|
||||
private function initialize(): void
|
||||
{
|
||||
if ($this->isInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
// when PHP 7.3 is the min version, we will be able to remove the '#' part in preg_quote as it's part of the default
|
||||
$this->regexes = [
|
||||
// }}
|
||||
'lex_var' => '{
|
||||
\s*
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_variable'][1], '#').'\s*'. // -}}\s*
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_variable'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~}}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_variable'][1], '#'). // }}
|
||||
')
|
||||
}Ax',
|
||||
|
||||
// %}
|
||||
'lex_block' => '{
|
||||
\s*
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*\n?'. // -%}\s*\n?
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_block'][1], '#').'\n?'. // %}\n?
|
||||
')
|
||||
}Ax',
|
||||
|
||||
// {% endverbatim %}
|
||||
'lex_raw_data' => '{'.
|
||||
preg_quote($this->options['tag_block'][0], '#'). // {%
|
||||
'('.
|
||||
$this->options['whitespace_trim']. // -
|
||||
'|'.
|
||||
$this->options['whitespace_line_trim']. // ~
|
||||
')?\s*endverbatim\s*'.
|
||||
'(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_block'][1], '#'). // %}
|
||||
')
|
||||
}sx',
|
||||
|
||||
'operator' => $this->getOperatorRegex(),
|
||||
|
||||
// #}
|
||||
'lex_comment' => '{
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n?
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~#}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_comment'][1], '#').'\n?'. // #}\n?
|
||||
')
|
||||
}sx',
|
||||
|
||||
// verbatim %}
|
||||
'lex_block_raw' => '{
|
||||
\s*verbatim\s*
|
||||
(?:'.
|
||||
preg_quote($this->options['whitespace_trim'].$this->options['tag_block'][1], '#').'\s*'. // -%}\s*
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'].$this->options['tag_block'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~%}[ \t\0\x0B]*
|
||||
'|'.
|
||||
preg_quote($this->options['tag_block'][1], '#'). // %}
|
||||
')
|
||||
}Asx',
|
||||
|
||||
'lex_block_line' => '{\s*line\s+(\d+)\s*'.preg_quote($this->options['tag_block'][1], '#').'}As',
|
||||
|
||||
// {{ or {% or {#
|
||||
'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'], '#'). // -
|
||||
'|'.
|
||||
preg_quote($this->options['whitespace_line_trim'], '#'). // ~
|
||||
')?
|
||||
}sx',
|
||||
'interpolation_start' => '{'.preg_quote($this->options['interpolation'][0], '#').'\s*}A',
|
||||
'interpolation_end' => '{\s*'.preg_quote($this->options['interpolation'][1], '#').'}A',
|
||||
];
|
||||
|
||||
$this->isInitialized = true;
|
||||
}
|
||||
|
||||
public function tokenize(Source $source): TokenStream
|
||||
{
|
||||
$this->initialize();
|
||||
|
||||
$this->source = $source;
|
||||
$this->code = str_replace(["\r\n", "\r"], "\n", $source->getCode());
|
||||
$this->cursor = 0;
|
||||
$this->lineno = 1;
|
||||
$this->end = \strlen($this->code);
|
||||
$this->tokens = [];
|
||||
$this->state = self::STATE_DATA;
|
||||
$this->states = [];
|
||||
$this->brackets = [];
|
||||
$this->position = -1;
|
||||
|
||||
// find all token starts in one go
|
||||
preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, \PREG_OFFSET_CAPTURE);
|
||||
$this->positions = $matches;
|
||||
|
||||
while ($this->cursor < $this->end) {
|
||||
// dispatch to the lexing functions depending
|
||||
// on the current state
|
||||
switch ($this->state) {
|
||||
case self::STATE_DATA:
|
||||
$this->lexData();
|
||||
break;
|
||||
|
||||
case self::STATE_BLOCK:
|
||||
$this->lexBlock();
|
||||
break;
|
||||
|
||||
case self::STATE_VAR:
|
||||
$this->lexVar();
|
||||
break;
|
||||
|
||||
case self::STATE_STRING:
|
||||
$this->lexString();
|
||||
break;
|
||||
|
||||
case self::STATE_INTERPOLATION:
|
||||
$this->lexInterpolation();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::EOF_TYPE);
|
||||
|
||||
if ($this->brackets) {
|
||||
[$expect, $lineno] = array_pop($this->brackets);
|
||||
throw new SyntaxError(\sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
|
||||
}
|
||||
|
||||
return new TokenStream($this->tokens, $this->source);
|
||||
}
|
||||
|
||||
private function lexData(): void
|
||||
{
|
||||
// if no matches are left we return the rest of the template as simple text token
|
||||
if ($this->position == \count($this->positions[0]) - 1) {
|
||||
$this->pushToken(Token::TEXT_TYPE, substr($this->code, $this->cursor));
|
||||
$this->cursor = $this->end;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// Find the first token after the current cursor
|
||||
$position = $this->positions[0][++$this->position];
|
||||
while ($position[1] < $this->cursor) {
|
||||
if ($this->position == \count($this->positions[0]) - 1) {
|
||||
return;
|
||||
}
|
||||
$position = $this->positions[0][++$this->position];
|
||||
}
|
||||
|
||||
// push the template text first
|
||||
$text = $textContent = substr($this->code, $this->cursor, $position[1] - $this->cursor);
|
||||
|
||||
// trim?
|
||||
if (isset($this->positions[2][$this->position][0])) {
|
||||
if ($this->options['whitespace_trim'] === $this->positions[2][$this->position][0]) {
|
||||
// whitespace_trim detected ({%-, {{- or {#-)
|
||||
$text = rtrim($text);
|
||||
} elseif ($this->options['whitespace_line_trim'] === $this->positions[2][$this->position][0]) {
|
||||
// whitespace_line_trim detected ({%~, {{~ or {#~)
|
||||
// don't trim \r and \n
|
||||
$text = rtrim($text, " \t\0\x0B");
|
||||
}
|
||||
}
|
||||
$this->pushToken(Token::TEXT_TYPE, $text);
|
||||
$this->moveCursor($textContent.$position[0]);
|
||||
|
||||
switch ($this->positions[1][$this->position][0]) {
|
||||
case $this->options['tag_comment'][0]:
|
||||
$this->lexComment();
|
||||
break;
|
||||
|
||||
case $this->options['tag_block'][0]:
|
||||
// raw data?
|
||||
if (preg_match($this->regexes['lex_block_raw'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lexRawData();
|
||||
// {% line \d+ %}
|
||||
} elseif (preg_match($this->regexes['lex_block_line'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
$this->lineno = (int) $match[1];
|
||||
} else {
|
||||
$this->pushToken(Token::BLOCK_START_TYPE);
|
||||
$this->pushState(self::STATE_BLOCK);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
}
|
||||
break;
|
||||
|
||||
case $this->options['tag_variable'][0]:
|
||||
$this->pushToken(Token::VAR_START_TYPE);
|
||||
$this->pushState(self::STATE_VAR);
|
||||
$this->currentVarBlockLine = $this->lineno;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private function lexBlock(): void
|
||||
{
|
||||
if (!$this->brackets && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::BLOCK_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
private function lexVar(): void
|
||||
{
|
||||
if (!$this->brackets && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::VAR_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
private function lexExpression(): void
|
||||
{
|
||||
// whitespace
|
||||
if (preg_match('/\s+/A', $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
|
||||
if ($this->cursor >= $this->end) {
|
||||
throw new SyntaxError(\sprintf('Unclosed "%s".', self::STATE_BLOCK === $this->state ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source);
|
||||
}
|
||||
}
|
||||
|
||||
// operators
|
||||
if (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) {
|
||||
$operator = preg_replace('/\s+/', ' ', $match[0]);
|
||||
if (\in_array($operator, $this->openingBrackets, true)) {
|
||||
$this->checkBrackets($operator);
|
||||
}
|
||||
$this->pushToken(Token::OPERATOR_TYPE, $operator);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// names
|
||||
elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::NAME_TYPE, $match[0]);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// numbers
|
||||
elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::NUMBER_TYPE, 0 + str_replace('_', '', $match[0]));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// punctuation
|
||||
elseif (str_contains(self::PUNCTUATION, $this->code[$this->cursor])) {
|
||||
$this->checkBrackets($this->code[$this->cursor]);
|
||||
$this->pushToken(Token::PUNCTUATION_TYPE, $this->code[$this->cursor]);
|
||||
++$this->cursor;
|
||||
}
|
||||
// strings
|
||||
elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->pushToken(Token::STRING_TYPE, $this->stripcslashes(substr($match[0], 1, -1), substr($match[0], 0, 1)));
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// opening double quoted string
|
||||
elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->brackets[] = ['"', $this->lineno];
|
||||
$this->pushState(self::STATE_STRING);
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// inline comment
|
||||
elseif (preg_match(self::REGEX_INLINE_COMMENT, $this->code, $match, 0, $this->cursor)) {
|
||||
$this->moveCursor($match[0]);
|
||||
}
|
||||
// unlexable
|
||||
else {
|
||||
throw new SyntaxError(\sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
|
||||
}
|
||||
}
|
||||
|
||||
private function stripcslashes(string $str, string $quoteType): string
|
||||
{
|
||||
$result = '';
|
||||
$length = \strlen($str);
|
||||
|
||||
$i = 0;
|
||||
while ($i < $length) {
|
||||
if (false === $pos = strpos($str, '\\', $i)) {
|
||||
$result .= substr($str, $i);
|
||||
break;
|
||||
}
|
||||
|
||||
$result .= substr($str, $i, $pos - $i);
|
||||
$i = $pos + 1;
|
||||
|
||||
if ($i >= $length) {
|
||||
$result .= '\\';
|
||||
break;
|
||||
}
|
||||
|
||||
$nextChar = $str[$i];
|
||||
|
||||
if (isset(self::SPECIAL_CHARS[$nextChar])) {
|
||||
$result .= self::SPECIAL_CHARS[$nextChar];
|
||||
} elseif ('\\' === $nextChar) {
|
||||
$result .= $nextChar;
|
||||
} elseif ("'" === $nextChar || '"' === $nextChar) {
|
||||
if ($nextChar !== $quoteType) {
|
||||
trigger_deprecation('twig/twig', '3.12', 'Character "%s" should not be escaped; the "\" character is ignored in Twig 3 but will not be in Twig 4. Please remove the extra "\" character at position %d in "%s" at line %d.', $nextChar, $i + 1, $this->source->getName(), $this->lineno);
|
||||
}
|
||||
$result .= $nextChar;
|
||||
} elseif ('#' === $nextChar && $i + 1 < $length && '{' === $str[$i + 1]) {
|
||||
$result .= '#{';
|
||||
++$i;
|
||||
} elseif ('x' === $nextChar && $i + 1 < $length && ctype_xdigit($str[$i + 1])) {
|
||||
$hex = $str[++$i];
|
||||
if ($i + 1 < $length && ctype_xdigit($str[$i + 1])) {
|
||||
$hex .= $str[++$i];
|
||||
}
|
||||
$result .= \chr(hexdec($hex));
|
||||
} elseif (ctype_digit($nextChar) && $nextChar < '8') {
|
||||
$octal = $nextChar;
|
||||
while ($i + 1 < $length && ctype_digit($str[$i + 1]) && $str[$i + 1] < '8' && \strlen($octal) < 3) {
|
||||
$octal .= $str[++$i];
|
||||
}
|
||||
$result .= \chr(octdec($octal));
|
||||
} else {
|
||||
trigger_deprecation('twig/twig', '3.12', 'Character "%s" should not be escaped; the "\" character is ignored in Twig 3 but will not be in Twig 4. Please remove the extra "\" character at position %d in "%s" at line %d.', $nextChar, $i + 1, $this->source->getName(), $this->lineno);
|
||||
$result .= $nextChar;
|
||||
}
|
||||
|
||||
++$i;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function lexRawData(): void
|
||||
{
|
||||
if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new SyntaxError('Unexpected end of file: Unclosed "verbatim" block.', $this->lineno, $this->source);
|
||||
}
|
||||
|
||||
$text = substr($this->code, $this->cursor, $match[0][1] - $this->cursor);
|
||||
$this->moveCursor($text.$match[0][0]);
|
||||
|
||||
// trim?
|
||||
if (isset($match[1][0])) {
|
||||
if ($this->options['whitespace_trim'] === $match[1][0]) {
|
||||
// whitespace_trim detected ({%-, {{- or {#-)
|
||||
$text = rtrim($text);
|
||||
} else {
|
||||
// whitespace_line_trim detected ({%~, {{~ or {#~)
|
||||
// don't trim \r and \n
|
||||
$text = rtrim($text, " \t\0\x0B");
|
||||
}
|
||||
}
|
||||
|
||||
$this->pushToken(Token::TEXT_TYPE, $text);
|
||||
}
|
||||
|
||||
private function lexComment(): void
|
||||
{
|
||||
if (!preg_match($this->regexes['lex_comment'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) {
|
||||
throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source);
|
||||
}
|
||||
|
||||
$this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]);
|
||||
}
|
||||
|
||||
private function lexString(): void
|
||||
{
|
||||
if (preg_match($this->regexes['interpolation_start'], $this->code, $match, 0, $this->cursor)) {
|
||||
$this->brackets[] = [$this->options['interpolation'][0], $this->lineno];
|
||||
$this->pushToken(Token::INTERPOLATION_START_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->pushState(self::STATE_INTERPOLATION);
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && '' !== $match[0]) {
|
||||
$this->pushToken(Token::STRING_TYPE, $this->stripcslashes($match[0], '"'));
|
||||
$this->moveCursor($match[0]);
|
||||
} elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) {
|
||||
[$expect, $lineno] = array_pop($this->brackets);
|
||||
if ('"' != $this->code[$this->cursor]) {
|
||||
throw new SyntaxError(\sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
|
||||
}
|
||||
|
||||
$this->popState();
|
||||
++$this->cursor;
|
||||
} else {
|
||||
// unlexable
|
||||
throw new SyntaxError(\sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source);
|
||||
}
|
||||
}
|
||||
|
||||
private function lexInterpolation(): void
|
||||
{
|
||||
$bracket = end($this->brackets);
|
||||
if ($this->options['interpolation'][0] === $bracket[0] && preg_match($this->regexes['interpolation_end'], $this->code, $match, 0, $this->cursor)) {
|
||||
array_pop($this->brackets);
|
||||
$this->pushToken(Token::INTERPOLATION_END_TYPE);
|
||||
$this->moveCursor($match[0]);
|
||||
$this->popState();
|
||||
} else {
|
||||
$this->lexExpression();
|
||||
}
|
||||
}
|
||||
|
||||
private function pushToken($type, $value = ''): void
|
||||
{
|
||||
// do not push empty text tokens
|
||||
if (Token::TEXT_TYPE === $type && '' === $value) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->tokens[] = new Token($type, $value, $this->lineno);
|
||||
}
|
||||
|
||||
private function moveCursor($text): void
|
||||
{
|
||||
$this->cursor += \strlen($text);
|
||||
$this->lineno += substr_count($text, "\n");
|
||||
}
|
||||
|
||||
private function getOperatorRegex(): string
|
||||
{
|
||||
$expressionParsers = ['='];
|
||||
foreach ($this->env->getExpressionParsers() as $expressionParser) {
|
||||
$expressionParsers = array_merge($expressionParsers, [$expressionParser->getName()], $expressionParser->getAliases());
|
||||
}
|
||||
|
||||
$expressionParsers = array_combine($expressionParsers, array_map('strlen', $expressionParsers));
|
||||
arsort($expressionParsers);
|
||||
|
||||
$regex = [];
|
||||
foreach ($expressionParsers as $expressionParser => $length) {
|
||||
// an operator that ends with a character must be followed by
|
||||
// a whitespace, a parenthesis, an opening map [ or sequence {
|
||||
$r = preg_quote($expressionParser, '/');
|
||||
if (ctype_alpha($expressionParser[$length - 1])) {
|
||||
$r .= '(?=[\s()\[{])';
|
||||
}
|
||||
|
||||
// an operator that begins with a character must not have a dot or pipe before
|
||||
if (ctype_alpha($expressionParser[0])) {
|
||||
$r = '(?<![\.\|])'.$r;
|
||||
}
|
||||
|
||||
// an operator with a space can be any amount of whitespaces
|
||||
$r = preg_replace('/\s+/', '\s+', $r);
|
||||
|
||||
$regex[] = $r;
|
||||
}
|
||||
|
||||
return '/'.implode('|', $regex).'/A';
|
||||
}
|
||||
|
||||
private function pushState($state): void
|
||||
{
|
||||
$this->states[] = $this->state;
|
||||
$this->state = $state;
|
||||
}
|
||||
|
||||
private function popState(): void
|
||||
{
|
||||
if (0 === \count($this->states)) {
|
||||
throw new \LogicException('Cannot pop state without a previous state.');
|
||||
}
|
||||
|
||||
$this->state = array_pop($this->states);
|
||||
}
|
||||
|
||||
private function checkBrackets(string $code): void
|
||||
{
|
||||
// opening bracket
|
||||
if (\in_array($code, $this->openingBrackets, true)) {
|
||||
$this->brackets[] = [$code, $this->lineno];
|
||||
} elseif (\in_array($code, $this->closingBrackets, true)) {
|
||||
// closing bracket
|
||||
if (!$this->brackets) {
|
||||
throw new SyntaxError(\sprintf('Unexpected "%s".', $code), $this->lineno, $this->source);
|
||||
}
|
||||
|
||||
[$expect, $lineno] = array_pop($this->brackets);
|
||||
if ($code !== str_replace($this->openingBrackets, $this->closingBrackets, $expect)) {
|
||||
throw new SyntaxError(\sprintf('Unclosed "%s".', $expect), $lineno, $this->source);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
75
vendor/twig/twig/src/Loader/ArrayLoader.php
vendored
Normal file
75
vendor/twig/twig/src/Loader/ArrayLoader.php
vendored
Normal file
@ -0,0 +1,75 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Loads a template from an array.
|
||||
*
|
||||
* When using this loader with a cache mechanism, you should know that a new cache
|
||||
* key is generated each time a template content "changes" (the cache key being the
|
||||
* source code of the template). If you don't want to see your cache grows out of
|
||||
* control, you need to take care of clearing the old cache file by yourself.
|
||||
*
|
||||
* This loader should only be used for unit testing.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class ArrayLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @param array $templates An array of templates (keys are the names, and values are the source code)
|
||||
*/
|
||||
public function __construct(
|
||||
private array $templates = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public function setTemplate(string $name, string $template): void
|
||||
{
|
||||
$this->templates[$name] = $template;
|
||||
}
|
||||
|
||||
public function getSourceContext(string $name): Source
|
||||
{
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(\sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return new Source($this->templates[$name], $name);
|
||||
}
|
||||
|
||||
public function exists(string $name): bool
|
||||
{
|
||||
return isset($this->templates[$name]);
|
||||
}
|
||||
|
||||
public function getCacheKey(string $name): string
|
||||
{
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(\sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return $name.':'.$this->templates[$name];
|
||||
}
|
||||
|
||||
public function isFresh(string $name, int $time): bool
|
||||
{
|
||||
if (!isset($this->templates[$name])) {
|
||||
throw new LoaderError(\sprintf('Template "%s" is not defined.', $name));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
132
vendor/twig/twig/src/Loader/ChainLoader.php
vendored
Normal file
132
vendor/twig/twig/src/Loader/ChainLoader.php
vendored
Normal file
@ -0,0 +1,132 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Loads templates from other loaders.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
final class ChainLoader implements LoaderInterface
|
||||
{
|
||||
/**
|
||||
* @var array<string, bool>
|
||||
*/
|
||||
private $hasSourceCache = [];
|
||||
|
||||
/**
|
||||
* @param iterable<LoaderInterface> $loaders
|
||||
*/
|
||||
public function __construct(
|
||||
private iterable $loaders = [],
|
||||
) {
|
||||
}
|
||||
|
||||
public function addLoader(LoaderInterface $loader): void
|
||||
{
|
||||
$current = $this->loaders;
|
||||
|
||||
$this->loaders = (static function () use ($current, $loader): \Generator {
|
||||
yield from $current;
|
||||
yield $loader;
|
||||
})();
|
||||
|
||||
$this->hasSourceCache = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @return LoaderInterface[]
|
||||
*/
|
||||
public function getLoaders(): array
|
||||
{
|
||||
if (!\is_array($this->loaders)) {
|
||||
$this->loaders = iterator_to_array($this->loaders, false);
|
||||
}
|
||||
|
||||
return $this->loaders;
|
||||
}
|
||||
|
||||
public function getSourceContext(string $name): Source
|
||||
{
|
||||
$exceptions = [];
|
||||
|
||||
foreach ($this->getLoaders() as $loader) {
|
||||
if (!$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->getSourceContext($name);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = $e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(\sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
|
||||
public function exists(string $name): bool
|
||||
{
|
||||
if (isset($this->hasSourceCache[$name])) {
|
||||
return $this->hasSourceCache[$name];
|
||||
}
|
||||
|
||||
foreach ($this->getLoaders() as $loader) {
|
||||
if ($loader->exists($name)) {
|
||||
return $this->hasSourceCache[$name] = true;
|
||||
}
|
||||
}
|
||||
|
||||
return $this->hasSourceCache[$name] = false;
|
||||
}
|
||||
|
||||
public function getCacheKey(string $name): string
|
||||
{
|
||||
$exceptions = [];
|
||||
|
||||
foreach ($this->getLoaders() as $loader) {
|
||||
if (!$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->getCacheKey($name);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = $loader::class.': '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(\sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
|
||||
public function isFresh(string $name, int $time): bool
|
||||
{
|
||||
$exceptions = [];
|
||||
|
||||
foreach ($this->getLoaders() as $loader) {
|
||||
if (!$loader->exists($name)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
return $loader->isFresh($name, $time);
|
||||
} catch (LoaderError $e) {
|
||||
$exceptions[] = $loader::class.': '.$e->getMessage();
|
||||
}
|
||||
}
|
||||
|
||||
throw new LoaderError(\sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : ''));
|
||||
}
|
||||
}
|
||||
290
vendor/twig/twig/src/Loader/FilesystemLoader.php
vendored
Normal file
290
vendor/twig/twig/src/Loader/FilesystemLoader.php
vendored
Normal file
@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Loads template from the filesystem.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class FilesystemLoader implements LoaderInterface
|
||||
{
|
||||
/** Identifier of the main namespace. */
|
||||
public const MAIN_NAMESPACE = '__main__';
|
||||
|
||||
/**
|
||||
* @var array<string, list<string>>
|
||||
*/
|
||||
protected $paths = [];
|
||||
protected $cache = [];
|
||||
protected $errorCache = [];
|
||||
|
||||
private $rootPath;
|
||||
|
||||
/**
|
||||
* @param string|string[] $paths A path or an array of paths where to look for templates
|
||||
* @param string|null $rootPath The root path common to all relative paths (null for getcwd())
|
||||
*/
|
||||
public function __construct($paths = [], ?string $rootPath = null)
|
||||
{
|
||||
$this->rootPath = ($rootPath ?? getcwd()).\DIRECTORY_SEPARATOR;
|
||||
if (null !== $rootPath && false !== ($realPath = realpath($rootPath))) {
|
||||
$this->rootPath = $realPath.\DIRECTORY_SEPARATOR;
|
||||
}
|
||||
|
||||
if ($paths) {
|
||||
$this->setPaths($paths);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the paths to the templates.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getPaths(string $namespace = self::MAIN_NAMESPACE): array
|
||||
{
|
||||
return $this->paths[$namespace] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the path namespaces.
|
||||
*
|
||||
* The main namespace is always defined.
|
||||
*
|
||||
* @return list<string>
|
||||
*/
|
||||
public function getNamespaces(): array
|
||||
{
|
||||
return array_keys($this->paths);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string|string[] $paths A path or an array of paths where to look for templates
|
||||
*/
|
||||
public function setPaths($paths, string $namespace = self::MAIN_NAMESPACE): void
|
||||
{
|
||||
if (!\is_array($paths)) {
|
||||
$paths = [$paths];
|
||||
}
|
||||
|
||||
$this->paths[$namespace] = [];
|
||||
foreach ($paths as $path) {
|
||||
$this->addPath($path, $namespace);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws LoaderError
|
||||
*/
|
||||
public function addPath(string $path, string $namespace = self::MAIN_NAMESPACE): void
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = $this->errorCache = [];
|
||||
|
||||
$checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
|
||||
if (!is_dir($checkPath)) {
|
||||
throw new LoaderError(\sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
|
||||
}
|
||||
|
||||
$this->paths[$namespace][] = rtrim($path, '/\\');
|
||||
}
|
||||
|
||||
/**
|
||||
* @throws LoaderError
|
||||
*/
|
||||
public function prependPath(string $path, string $namespace = self::MAIN_NAMESPACE): void
|
||||
{
|
||||
// invalidate the cache
|
||||
$this->cache = $this->errorCache = [];
|
||||
|
||||
$checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path;
|
||||
if (!is_dir($checkPath)) {
|
||||
throw new LoaderError(\sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath));
|
||||
}
|
||||
|
||||
$path = rtrim($path, '/\\');
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
$this->paths[$namespace][] = $path;
|
||||
} else {
|
||||
array_unshift($this->paths[$namespace], $path);
|
||||
}
|
||||
}
|
||||
|
||||
public function getSourceContext(string $name): Source
|
||||
{
|
||||
if (null === $path = $this->findTemplate($name)) {
|
||||
return new Source('', $name, '');
|
||||
}
|
||||
|
||||
return new Source(file_get_contents($path), $name, $path);
|
||||
}
|
||||
|
||||
public function getCacheKey(string $name): string
|
||||
{
|
||||
if (null === $path = $this->findTemplate($name)) {
|
||||
return '';
|
||||
}
|
||||
$len = \strlen($this->rootPath);
|
||||
if (0 === strncmp($this->rootPath, $path, $len)) {
|
||||
return substr($path, $len);
|
||||
}
|
||||
|
||||
return $path;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $name)
|
||||
{
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return null !== $this->findTemplate($name, false);
|
||||
}
|
||||
|
||||
public function isFresh(string $name, int $time): bool
|
||||
{
|
||||
// false support to be removed in 3.0
|
||||
if (null === $path = $this->findTemplate($name)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return filemtime($path) < $time;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return string|null
|
||||
*/
|
||||
protected function findTemplate(string $name, bool $throw = true)
|
||||
{
|
||||
$name = $this->normalizeName($name);
|
||||
|
||||
if (isset($this->cache[$name])) {
|
||||
return $this->cache[$name];
|
||||
}
|
||||
|
||||
if (isset($this->errorCache[$name])) {
|
||||
if (!$throw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new LoaderError($this->errorCache[$name]);
|
||||
}
|
||||
|
||||
try {
|
||||
[$namespace, $shortname] = $this->parseName($name);
|
||||
|
||||
$this->validateName($shortname);
|
||||
} catch (LoaderError $e) {
|
||||
if (!$throw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw $e;
|
||||
}
|
||||
|
||||
if (!isset($this->paths[$namespace])) {
|
||||
$this->errorCache[$name] = \sprintf('There are no registered paths for namespace "%s".', $namespace);
|
||||
|
||||
if (!$throw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new LoaderError($this->errorCache[$name]);
|
||||
}
|
||||
|
||||
foreach ($this->paths[$namespace] as $path) {
|
||||
if (!$this->isAbsolutePath($path)) {
|
||||
$path = $this->rootPath.$path;
|
||||
}
|
||||
|
||||
if (is_file($path.'/'.$shortname)) {
|
||||
if (false !== $realpath = realpath($path.'/'.$shortname)) {
|
||||
return $this->cache[$name] = $realpath;
|
||||
}
|
||||
|
||||
return $this->cache[$name] = $path.'/'.$shortname;
|
||||
}
|
||||
}
|
||||
|
||||
$this->errorCache[$name] = \sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace]));
|
||||
|
||||
if (!$throw) {
|
||||
return null;
|
||||
}
|
||||
|
||||
throw new LoaderError($this->errorCache[$name]);
|
||||
}
|
||||
|
||||
private function normalizeName(string $name): string
|
||||
{
|
||||
return preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name));
|
||||
}
|
||||
|
||||
private function parseName(string $name, string $default = self::MAIN_NAMESPACE): array
|
||||
{
|
||||
if (isset($name[0]) && '@' == $name[0]) {
|
||||
if (false === $pos = strpos($name, '/')) {
|
||||
throw new LoaderError(\sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name));
|
||||
}
|
||||
|
||||
$namespace = substr($name, 1, $pos - 1);
|
||||
$shortname = substr($name, $pos + 1);
|
||||
|
||||
return [$namespace, $shortname];
|
||||
}
|
||||
|
||||
return [$default, $name];
|
||||
}
|
||||
|
||||
private function validateName(string $name): void
|
||||
{
|
||||
if (str_contains($name, "\0")) {
|
||||
throw new LoaderError('A template name cannot contain NUL bytes.');
|
||||
}
|
||||
|
||||
$name = ltrim($name, '/');
|
||||
$parts = explode('/', $name);
|
||||
$level = 0;
|
||||
foreach ($parts as $part) {
|
||||
if ('..' === $part) {
|
||||
--$level;
|
||||
} elseif ('.' !== $part) {
|
||||
++$level;
|
||||
}
|
||||
|
||||
if ($level < 0) {
|
||||
throw new LoaderError(\sprintf('Looks like you try to load a template outside configured directories (%s).', $name));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function isAbsolutePath(string $file): bool
|
||||
{
|
||||
return strspn($file, '/\\', 0, 1)
|
||||
|| (\strlen($file) > 3 && ctype_alpha($file[0])
|
||||
&& ':' === $file[1]
|
||||
&& strspn($file, '/\\', 2, 1)
|
||||
)
|
||||
|| null !== parse_url($file, \PHP_URL_SCHEME)
|
||||
;
|
||||
}
|
||||
}
|
||||
49
vendor/twig/twig/src/Loader/LoaderInterface.php
vendored
Normal file
49
vendor/twig/twig/src/Loader/LoaderInterface.php
vendored
Normal file
@ -0,0 +1,49 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Loader;
|
||||
|
||||
use Twig\Error\LoaderError;
|
||||
use Twig\Source;
|
||||
|
||||
/**
|
||||
* Interface all loaders must implement.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
interface LoaderInterface
|
||||
{
|
||||
/**
|
||||
* Returns the source context for a given template logical name.
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function getSourceContext(string $name): Source;
|
||||
|
||||
/**
|
||||
* Gets the cache key to use for the cache for a given template name.
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function getCacheKey(string $name): string;
|
||||
|
||||
/**
|
||||
* @param int $time Timestamp of the last modification time of the cached template
|
||||
*
|
||||
* @throws LoaderError When $name is not found
|
||||
*/
|
||||
public function isFresh(string $name, int $time): bool;
|
||||
|
||||
/**
|
||||
* @return bool
|
||||
*/
|
||||
public function exists(string $name);
|
||||
}
|
||||
57
vendor/twig/twig/src/Markup.php
vendored
Normal file
57
vendor/twig/twig/src/Markup.php
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig;
|
||||
|
||||
/**
|
||||
* Marks a content as safe.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class Markup implements \Countable, \JsonSerializable, \Stringable
|
||||
{
|
||||
private $content;
|
||||
private ?string $charset;
|
||||
|
||||
public function __construct($content, $charset)
|
||||
{
|
||||
$this->content = (string) $content;
|
||||
$this->charset = $charset;
|
||||
}
|
||||
|
||||
public function __toString(): string
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
|
||||
public function getCharset(): string
|
||||
{
|
||||
return $this->charset;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return int
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function count()
|
||||
{
|
||||
return mb_strlen($this->content, $this->charset);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return mixed
|
||||
*/
|
||||
#[\ReturnTypeWillChange]
|
||||
public function jsonSerialize()
|
||||
{
|
||||
return $this->content;
|
||||
}
|
||||
}
|
||||
40
vendor/twig/twig/src/Node/AutoEscapeNode.php
vendored
Normal file
40
vendor/twig/twig/src/Node/AutoEscapeNode.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents an autoescape node.
|
||||
*
|
||||
* The value is the escaping strategy (can be html, js, ...)
|
||||
*
|
||||
* The true value is equivalent to html.
|
||||
*
|
||||
* If autoescaping is disabled, then the value is false.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class AutoEscapeNode extends Node
|
||||
{
|
||||
public function __construct($value, Node $body, int $lineno)
|
||||
{
|
||||
parent::__construct(['body' => $body], ['value' => $value], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler->subcompile($this->getNode('body'));
|
||||
}
|
||||
}
|
||||
50
vendor/twig/twig/src/Node/BlockNode.php
vendored
Normal file
50
vendor/twig/twig/src/Node/BlockNode.php
vendored
Normal file
@ -0,0 +1,50 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents a block node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class BlockNode extends Node
|
||||
{
|
||||
public function __construct(string $name, Node $body, int $lineno)
|
||||
{
|
||||
parent::__construct(['body' => $body], ['name' => $name], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write("/**\n")
|
||||
->write(" * @return iterable<null|scalar|\Stringable>\n")
|
||||
->write(" */\n")
|
||||
->write(\sprintf("public function block_%s(array \$context, array \$blocks = []): iterable\n", $this->getAttribute('name')), "{\n")
|
||||
->indent()
|
||||
->write("\$macros = \$this->macros;\n")
|
||||
;
|
||||
|
||||
$compiler
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("yield from [];\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
38
vendor/twig/twig/src/Node/BlockReferenceNode.php
vendored
Normal file
38
vendor/twig/twig/src/Node/BlockReferenceNode.php
vendored
Normal file
@ -0,0 +1,38 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents a block call node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class BlockReferenceNode extends Node implements NodeOutputInterface
|
||||
{
|
||||
public function __construct(string $name, int $lineno)
|
||||
{
|
||||
parent::__construct([], ['name' => $name], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write(\sprintf("yield from \$this->unwrap()->yieldBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name')))
|
||||
;
|
||||
}
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/BodyNode.php
vendored
Normal file
24
vendor/twig/twig/src/Node/BodyNode.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
|
||||
/**
|
||||
* Represents a body node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class BodyNode extends Node
|
||||
{
|
||||
}
|
||||
57
vendor/twig/twig/src/Node/CaptureNode.php
vendored
Normal file
57
vendor/twig/twig/src/Node/CaptureNode.php
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* Represents a node for which we need to capture the output.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class CaptureNode extends Node
|
||||
{
|
||||
public function __construct(Node $body, int $lineno)
|
||||
{
|
||||
parent::__construct(['body' => $body], ['raw' => false], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$useYield = $compiler->getEnvironment()->useYield();
|
||||
|
||||
if (!$this->getAttribute('raw')) {
|
||||
$compiler->raw("('' === \$tmp = ");
|
||||
}
|
||||
$compiler
|
||||
->raw($useYield ? "implode('', iterator_to_array(" : '\\Twig\\Extension\\CoreExtension::captureOutput(')
|
||||
->raw("(function () use (&\$context, \$macros, \$blocks) {\n")
|
||||
->indent()
|
||||
->subcompile($this->getNode('body'))
|
||||
->write("yield from [];\n")
|
||||
->outdent()
|
||||
->write('})()')
|
||||
;
|
||||
if ($useYield) {
|
||||
$compiler->raw(', false))');
|
||||
} else {
|
||||
$compiler->raw(')');
|
||||
}
|
||||
if (!$this->getAttribute('raw')) {
|
||||
$compiler->raw(") ? '' : new Markup(\$tmp, \$this->env->getCharset());");
|
||||
} else {
|
||||
$compiler->raw(';');
|
||||
}
|
||||
}
|
||||
}
|
||||
33
vendor/twig/twig/src/Node/CheckSecurityCallNode.php
vendored
Normal file
33
vendor/twig/twig/src/Node/CheckSecurityCallNode.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class CheckSecurityCallNode extends Node
|
||||
{
|
||||
/**
|
||||
* @return void
|
||||
*/
|
||||
public function compile(Compiler $compiler)
|
||||
{
|
||||
$compiler
|
||||
->write("\$this->sandbox = \$this->extensions[SandboxExtension::class];\n")
|
||||
->write("\$this->checkSecurity();\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
85
vendor/twig/twig/src/Node/CheckSecurityNode.php
vendored
Normal file
85
vendor/twig/twig/src/Node/CheckSecurityNode.php
vendored
Normal file
@ -0,0 +1,85 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
|
||||
/**
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class CheckSecurityNode extends Node
|
||||
{
|
||||
private $usedFilters;
|
||||
private $usedTags;
|
||||
private $usedFunctions;
|
||||
|
||||
/**
|
||||
* @param array<string, int> $usedFilters
|
||||
* @param array<string, int> $usedTags
|
||||
* @param array<string, int> $usedFunctions
|
||||
*/
|
||||
public function __construct(array $usedFilters, array $usedTags, array $usedFunctions)
|
||||
{
|
||||
$this->usedFilters = $usedFilters;
|
||||
$this->usedTags = $usedTags;
|
||||
$this->usedFunctions = $usedFunctions;
|
||||
|
||||
parent::__construct();
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->write("\n")
|
||||
->write("public function checkSecurity()\n")
|
||||
->write("{\n")
|
||||
->indent()
|
||||
->write('static $tags = ')->repr(array_filter($this->usedTags))->raw(";\n")
|
||||
->write('static $filters = ')->repr(array_filter($this->usedFilters))->raw(";\n")
|
||||
->write('static $functions = ')->repr(array_filter($this->usedFunctions))->raw(";\n\n")
|
||||
->write("try {\n")
|
||||
->indent()
|
||||
->write("\$this->sandbox->checkSecurity(\n")
|
||||
->indent()
|
||||
->write(!$this->usedTags ? "[],\n" : "['".implode("', '", array_keys($this->usedTags))."'],\n")
|
||||
->write(!$this->usedFilters ? "[],\n" : "['".implode("', '", array_keys($this->usedFilters))."'],\n")
|
||||
->write(!$this->usedFunctions ? "[],\n" : "['".implode("', '", array_keys($this->usedFunctions))."'],\n")
|
||||
->write("\$this->source\n")
|
||||
->outdent()
|
||||
->write(");\n")
|
||||
->outdent()
|
||||
->write("} catch (SecurityError \$e) {\n")
|
||||
->indent()
|
||||
->write("\$e->setSourceContext(\$this->source);\n\n")
|
||||
->write("if (\$e instanceof SecurityNotAllowedTagError && isset(\$tags[\$e->getTagName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$tags[\$e->getTagName()]);\n")
|
||||
->outdent()
|
||||
->write("} elseif (\$e instanceof SecurityNotAllowedFilterError && isset(\$filters[\$e->getFilterName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$filters[\$e->getFilterName()]);\n")
|
||||
->outdent()
|
||||
->write("} elseif (\$e instanceof SecurityNotAllowedFunctionError && isset(\$functions[\$e->getFunctionName()])) {\n")
|
||||
->indent()
|
||||
->write("\$e->setTemplateLine(\$functions[\$e->getFunctionName()]);\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->write("throw \$e;\n")
|
||||
->outdent()
|
||||
->write("}\n\n")
|
||||
->outdent()
|
||||
->write("}\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
47
vendor/twig/twig/src/Node/CheckToStringNode.php
vendored
Normal file
47
vendor/twig/twig/src/Node/CheckToStringNode.php
vendored
Normal file
@ -0,0 +1,47 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
|
||||
/**
|
||||
* Checks if casting an expression to __toString() is allowed by the sandbox.
|
||||
*
|
||||
* For instance, when there is a simple Print statement, like {{ article }},
|
||||
* and if the sandbox is enabled, we need to check that the __toString()
|
||||
* method is allowed if 'article' is an object. The same goes for {{ article|upper }}
|
||||
* or {{ random(article) }}
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class CheckToStringNode extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $expr)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $expr->getTemplateLine());
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$expr = $this->getNode('expr');
|
||||
$compiler
|
||||
->raw('$this->sandbox->ensureToStringAllowed(')
|
||||
->subcompile($expr)
|
||||
->raw(', ')
|
||||
->repr($expr->getTemplateLine())
|
||||
->raw(', $this->source)')
|
||||
;
|
||||
}
|
||||
}
|
||||
73
vendor/twig/twig/src/Node/DeprecatedNode.php
vendored
Normal file
73
vendor/twig/twig/src/Node/DeprecatedNode.php
vendored
Normal file
@ -0,0 +1,73 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
|
||||
/**
|
||||
* Represents a deprecated node.
|
||||
*
|
||||
* @author Yonel Ceruto <yonelceruto@gmail.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class DeprecatedNode extends Node
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, int $lineno)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler->addDebugInfo($this);
|
||||
|
||||
$expr = $this->getNode('expr');
|
||||
|
||||
if (!$expr instanceof ConstantExpression) {
|
||||
$varName = $compiler->getVarName();
|
||||
$compiler
|
||||
->write(\sprintf('$%s = ', $varName))
|
||||
->subcompile($expr)
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->write('trigger_deprecation(');
|
||||
if ($this->hasNode('package')) {
|
||||
$compiler->subcompile($this->getNode('package'));
|
||||
} else {
|
||||
$compiler->raw("''");
|
||||
}
|
||||
$compiler->raw(', ');
|
||||
if ($this->hasNode('version')) {
|
||||
$compiler->subcompile($this->getNode('version'));
|
||||
} else {
|
||||
$compiler->raw("''");
|
||||
}
|
||||
$compiler->raw(', ');
|
||||
|
||||
if ($expr instanceof ConstantExpression) {
|
||||
$compiler->subcompile($expr);
|
||||
} else {
|
||||
$compiler->write(\sprintf('$%s', $varName));
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('.')
|
||||
->string(\sprintf(' in "%s" at line %d.', $this->getTemplateName(), $this->getTemplateLine()))
|
||||
->raw(");\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
40
vendor/twig/twig/src/Node/DoNode.php
vendored
Normal file
40
vendor/twig/twig/src/Node/DoNode.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
|
||||
/**
|
||||
* Represents a do node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class DoNode extends Node
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, int $lineno)
|
||||
{
|
||||
parent::__construct(['expr' => $expr], [], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->write('')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw(";\n")
|
||||
;
|
||||
}
|
||||
}
|
||||
54
vendor/twig/twig/src/Node/EmbedNode.php
vendored
Normal file
54
vendor/twig/twig/src/Node/EmbedNode.php
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\ConstantExpression;
|
||||
|
||||
/**
|
||||
* Represents an embed node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
class EmbedNode extends IncludeNode
|
||||
{
|
||||
// we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module)
|
||||
public function __construct(string $name, int $index, ?AbstractExpression $variables, bool $only, bool $ignoreMissing, int $lineno)
|
||||
{
|
||||
parent::__construct(new ConstantExpression('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno);
|
||||
|
||||
$this->setAttribute('name', $name);
|
||||
$this->setAttribute('index', $index);
|
||||
}
|
||||
|
||||
protected function addGetTemplate(Compiler $compiler, string $template = ''): void
|
||||
{
|
||||
$compiler
|
||||
->raw('$this->load(')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(', ')
|
||||
->repr($this->getTemplateLine())
|
||||
->raw(', ')
|
||||
->string($this->getAttribute('index'))
|
||||
->raw(')')
|
||||
;
|
||||
if ($this->getAttribute('ignore_missing')) {
|
||||
$compiler
|
||||
->raw(";\n")
|
||||
->write(\sprintf("\$%s->getParent(\$context);\n", $template))
|
||||
;
|
||||
}
|
||||
}
|
||||
}
|
||||
33
vendor/twig/twig/src/Node/EmptyNode.php
vendored
Normal file
33
vendor/twig/twig/src/Node/EmptyNode.php
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node;
|
||||
|
||||
use Twig\Attribute\YieldReady;
|
||||
|
||||
/**
|
||||
* Represents an empty node.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
#[YieldReady]
|
||||
final class EmptyNode extends Node
|
||||
{
|
||||
public function __construct(int $lineno = 0)
|
||||
{
|
||||
parent::__construct([], [], $lineno);
|
||||
}
|
||||
|
||||
public function setNode(string $name, Node $node): void
|
||||
{
|
||||
throw new \LogicException('EmptyNode cannot have children.');
|
||||
}
|
||||
}
|
||||
43
vendor/twig/twig/src/Node/Expression/AbstractExpression.php
vendored
Normal file
43
vendor/twig/twig/src/Node/Expression/AbstractExpression.php
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Abstract class for all nodes that represents an expression.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
abstract class AbstractExpression extends Node
|
||||
{
|
||||
public function isGenerator(): bool
|
||||
{
|
||||
return $this->hasAttribute('is_generator') && $this->getAttribute('is_generator');
|
||||
}
|
||||
|
||||
/**
|
||||
* @return static
|
||||
*/
|
||||
public function setExplicitParentheses(): self
|
||||
{
|
||||
$this->setAttribute('with_parentheses', true);
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasExplicitParentheses(): bool
|
||||
{
|
||||
return $this->hasAttribute('with_parentheses') && $this->getAttribute('with_parentheses');
|
||||
}
|
||||
}
|
||||
112
vendor/twig/twig/src/Node/Expression/ArrayExpression.php
vendored
Normal file
112
vendor/twig/twig/src/Node/Expression/ArrayExpression.php
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\Unary\SpreadUnary;
|
||||
use Twig\Node\Expression\Unary\StringCastUnary;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
|
||||
class ArrayExpression extends AbstractExpression implements SupportDefinedTestInterface, ReturnArrayInterface
|
||||
{
|
||||
use SupportDefinedTestTrait;
|
||||
|
||||
private $index;
|
||||
|
||||
public function __construct(array $elements, int $lineno)
|
||||
{
|
||||
parent::__construct($elements, [], $lineno);
|
||||
|
||||
$this->index = -1;
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
if ($pair['key'] instanceof ConstantExpression && ctype_digit((string) $pair['key']->getAttribute('value')) && $pair['key']->getAttribute('value') > $this->index) {
|
||||
$this->index = $pair['key']->getAttribute('value');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getKeyValuePairs(): array
|
||||
{
|
||||
$pairs = [];
|
||||
foreach (array_chunk($this->nodes, 2) as $pair) {
|
||||
$pairs[] = [
|
||||
'key' => $pair[0],
|
||||
'value' => $pair[1],
|
||||
];
|
||||
}
|
||||
|
||||
return $pairs;
|
||||
}
|
||||
|
||||
public function hasElement(AbstractExpression $key): bool
|
||||
{
|
||||
foreach ($this->getKeyValuePairs() as $pair) {
|
||||
// we compare the string representation of the keys
|
||||
// to avoid comparing the line numbers which are not relevant here.
|
||||
if ((string) $key === (string) $pair['key']) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function addElement(AbstractExpression $value, ?AbstractExpression $key = null): void
|
||||
{
|
||||
if (null === $key) {
|
||||
$key = new ConstantExpression(++$this->index, $value->getTemplateLine());
|
||||
}
|
||||
|
||||
array_push($this->nodes, $key, $value);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
if ($this->definedTest) {
|
||||
$compiler->repr(true);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler->raw('[');
|
||||
$isSequence = true;
|
||||
foreach ($this->getKeyValuePairs() as $i => $pair) {
|
||||
if (0 !== $i) {
|
||||
$compiler->raw(', ');
|
||||
}
|
||||
|
||||
$key = null;
|
||||
if ($pair['key'] instanceof ContextVariable) {
|
||||
$pair['key'] = new StringCastUnary($pair['key'], $pair['key']->getTemplateLine());
|
||||
} elseif ($pair['key'] instanceof TempNameExpression) {
|
||||
$key = $pair['key']->getAttribute('name');
|
||||
$pair['key'] = new ConstantExpression($key, $pair['key']->getTemplateLine());
|
||||
} elseif ($pair['key'] instanceof ConstantExpression) {
|
||||
$key = $pair['key']->getAttribute('value');
|
||||
}
|
||||
|
||||
if ($key !== $i) {
|
||||
$isSequence = false;
|
||||
}
|
||||
|
||||
if (!$isSequence && !$pair['value'] instanceof SpreadUnary) {
|
||||
$compiler
|
||||
->subcompile($pair['key'])
|
||||
->raw(' => ')
|
||||
;
|
||||
}
|
||||
|
||||
$compiler->subcompile($pair['value']);
|
||||
}
|
||||
$compiler->raw(']');
|
||||
}
|
||||
}
|
||||
63
vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php
vendored
Normal file
63
vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php
vendored
Normal file
@ -0,0 +1,63 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
use Twig\Node\Node;
|
||||
|
||||
/**
|
||||
* Represents an arrow function.
|
||||
*
|
||||
* @author Fabien Potencier <fabien@symfony.com>
|
||||
*/
|
||||
class ArrowFunctionExpression extends AbstractExpression
|
||||
{
|
||||
public function __construct(AbstractExpression $expr, Node $names, $lineno)
|
||||
{
|
||||
if (!$names instanceof ListExpression && !$names instanceof ContextVariable) {
|
||||
throw new SyntaxError('The arrow function argument must be a list of variables or a single variable.', $names->getTemplateLine(), $names->getSourceContext());
|
||||
}
|
||||
|
||||
if ($names instanceof ContextVariable) {
|
||||
$names = new ListExpression([new AssignContextVariable($names->getAttribute('name'), $names->getTemplateLine())], $lineno);
|
||||
}
|
||||
|
||||
parent::__construct(['expr' => $expr, 'names' => $names], [], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->addDebugInfo($this)
|
||||
->raw('function (')
|
||||
->subcompile($this->getNode('names'))
|
||||
->raw(') use ($context, $macros) { ')
|
||||
;
|
||||
foreach ($this->getNode('names') as $name) {
|
||||
$compiler
|
||||
->raw('$context["')
|
||||
->raw($name->getAttribute('name'))
|
||||
->raw('"] = $__')
|
||||
->raw($name->getAttribute('name'))
|
||||
->raw('__; ')
|
||||
;
|
||||
}
|
||||
$compiler
|
||||
->raw('return ')
|
||||
->subcompile($this->getNode('expr'))
|
||||
->raw('; }')
|
||||
;
|
||||
}
|
||||
}
|
||||
44
vendor/twig/twig/src/Node/Expression/AssignNameExpression.php
vendored
Normal file
44
vendor/twig/twig/src/Node/Expression/AssignNameExpression.php
vendored
Normal file
@ -0,0 +1,44 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Error\SyntaxError;
|
||||
use Twig\Node\Expression\Variable\AssignContextVariable;
|
||||
use Twig\Node\Expression\Variable\ContextVariable;
|
||||
|
||||
class AssignNameExpression extends ContextVariable
|
||||
{
|
||||
public function __construct(string $name, int $lineno)
|
||||
{
|
||||
if (self::class === static::class) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'The "%s" class is deprecated, use "%s" instead.', self::class, AssignContextVariable::class);
|
||||
}
|
||||
|
||||
// All names supported by ExpressionParser::parsePrimaryExpression() should be excluded
|
||||
if (\in_array(strtolower($name), ['true', 'false', 'none', 'null'], true)) {
|
||||
throw new SyntaxError(\sprintf('You cannot assign a value to "%s".', $name), $lineno);
|
||||
}
|
||||
|
||||
parent::__construct($name, $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->raw('$context[')
|
||||
->string($this->getAttribute('name'))
|
||||
->raw(']')
|
||||
;
|
||||
}
|
||||
}
|
||||
53
vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php
vendored
Normal file
53
vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php
vendored
Normal file
@ -0,0 +1,53 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Node;
|
||||
|
||||
abstract class AbstractBinary extends AbstractExpression implements BinaryInterface
|
||||
{
|
||||
/**
|
||||
* @param AbstractExpression $left
|
||||
* @param AbstractExpression $right
|
||||
*/
|
||||
public function __construct(Node $left, Node $right, int $lineno)
|
||||
{
|
||||
if (!$left instanceof AbstractExpression) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "left" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $left::class);
|
||||
}
|
||||
if (!$right instanceof AbstractExpression) {
|
||||
trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "right" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $right::class);
|
||||
}
|
||||
|
||||
parent::__construct(['left' => $left, 'right' => $right], [], $lineno);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->raw('(')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(' ')
|
||||
;
|
||||
$this->operator($compiler);
|
||||
$compiler
|
||||
->raw(' ')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(')')
|
||||
;
|
||||
}
|
||||
|
||||
abstract public function operator(Compiler $compiler): Compiler;
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php
vendored
Normal file
24
vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnNumberInterface;
|
||||
|
||||
class AddBinary extends AbstractBinary implements ReturnNumberInterface
|
||||
{
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('+');
|
||||
}
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php
vendored
Normal file
24
vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnBoolInterface;
|
||||
|
||||
class AndBinary extends AbstractBinary implements ReturnBoolInterface
|
||||
{
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('&&');
|
||||
}
|
||||
}
|
||||
22
vendor/twig/twig/src/Node/Expression/Binary/BinaryInterface.php
vendored
Normal file
22
vendor/twig/twig/src/Node/Expression/Binary/BinaryInterface.php
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
interface BinaryInterface
|
||||
{
|
||||
public function __construct(AbstractExpression $left, AbstractExpression $right, int $lineno);
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php
vendored
Normal file
24
vendor/twig/twig/src/Node/Expression/Binary/BitwiseAndBinary.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnNumberInterface;
|
||||
|
||||
class BitwiseAndBinary extends AbstractBinary implements ReturnNumberInterface
|
||||
{
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('&');
|
||||
}
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php
vendored
Normal file
24
vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnNumberInterface;
|
||||
|
||||
class BitwiseOrBinary extends AbstractBinary implements ReturnNumberInterface
|
||||
{
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('|');
|
||||
}
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php
vendored
Normal file
24
vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnNumberInterface;
|
||||
|
||||
class BitwiseXorBinary extends AbstractBinary implements ReturnNumberInterface
|
||||
{
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('^');
|
||||
}
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php
vendored
Normal file
24
vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnStringInterface;
|
||||
|
||||
class ConcatBinary extends AbstractBinary implements ReturnStringInterface
|
||||
{
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('.');
|
||||
}
|
||||
}
|
||||
24
vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php
vendored
Normal file
24
vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
* (c) Armin Ronacher
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnNumberInterface;
|
||||
|
||||
class DivBinary extends AbstractBinary implements ReturnNumberInterface
|
||||
{
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('/');
|
||||
}
|
||||
}
|
||||
55
vendor/twig/twig/src/Node/Expression/Binary/ElvisBinary.php
vendored
Normal file
55
vendor/twig/twig/src/Node/Expression/Binary/ElvisBinary.php
vendored
Normal file
@ -0,0 +1,55 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\AbstractExpression;
|
||||
use Twig\Node\Expression\OperatorEscapeInterface;
|
||||
use Twig\Node\Node;
|
||||
|
||||
final class ElvisBinary extends AbstractBinary implements OperatorEscapeInterface
|
||||
{
|
||||
/**
|
||||
* @param AbstractExpression $left
|
||||
* @param AbstractExpression $right
|
||||
*/
|
||||
public function __construct(Node $left, Node $right, int $lineno)
|
||||
{
|
||||
parent::__construct($left, $right, $lineno);
|
||||
|
||||
$this->setNode('test', clone $left);
|
||||
$left->setAttribute('always_defined', true);
|
||||
}
|
||||
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler
|
||||
->raw('((')
|
||||
->subcompile($this->getNode('test'))
|
||||
->raw(') ? (')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(') : (')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw('))')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('?:');
|
||||
}
|
||||
|
||||
public function getOperandNamesToEscape(): array
|
||||
{
|
||||
return ['left', 'right'];
|
||||
}
|
||||
}
|
||||
36
vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php
vendored
Normal file
36
vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnBoolInterface;
|
||||
|
||||
class EndsWithBinary extends AbstractBinary implements ReturnBoolInterface
|
||||
{
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$left = $compiler->getVarName();
|
||||
$right = $compiler->getVarName();
|
||||
$compiler
|
||||
->raw(\sprintf('(is_string($%s = ', $left))
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(\sprintf(') && is_string($%s = ', $right))
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw(\sprintf(') && str_ends_with($%1$s, $%2$s))', $left, $right))
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('');
|
||||
}
|
||||
}
|
||||
40
vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php
vendored
Normal file
40
vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php
vendored
Normal file
@ -0,0 +1,40 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnBoolInterface;
|
||||
|
||||
class EqualBinary extends AbstractBinary implements ReturnBoolInterface
|
||||
{
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
if (\PHP_VERSION_ID >= 80000) {
|
||||
parent::compile($compiler);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
$compiler
|
||||
->raw('(0 === CoreExtension::compare(')
|
||||
->subcompile($this->getNode('left'))
|
||||
->raw(', ')
|
||||
->subcompile($this->getNode('right'))
|
||||
->raw('))')
|
||||
;
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('==');
|
||||
}
|
||||
}
|
||||
30
vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php
vendored
Normal file
30
vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php
vendored
Normal file
@ -0,0 +1,30 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
* This file is part of Twig.
|
||||
*
|
||||
* (c) Fabien Potencier
|
||||
*
|
||||
* For the full copyright and license information, please view the LICENSE
|
||||
* file that was distributed with this source code.
|
||||
*/
|
||||
|
||||
namespace Twig\Node\Expression\Binary;
|
||||
|
||||
use Twig\Compiler;
|
||||
use Twig\Node\Expression\ReturnNumberInterface;
|
||||
|
||||
class FloorDivBinary extends AbstractBinary implements ReturnNumberInterface
|
||||
{
|
||||
public function compile(Compiler $compiler): void
|
||||
{
|
||||
$compiler->raw('(int) floor(');
|
||||
parent::compile($compiler);
|
||||
$compiler->raw(')');
|
||||
}
|
||||
|
||||
public function operator(Compiler $compiler): Compiler
|
||||
{
|
||||
return $compiler->raw('/');
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user