diff --git a/composer.lock b/composer.lock index 513da5531..0a6f574a8 100644 --- a/composer.lock +++ b/composer.lock @@ -1352,24 +1352,23 @@ }, { "name": "twig/twig", - "version": "v3.11.0", + "version": "v3.12.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d" + "reference": "4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", - "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea", + "reference": "4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22", "symfony/polyfill-php81": "^1.29" }, "require-dev": { @@ -1416,7 +1415,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.11.0" + "source": "https://github.com/twigphp/Twig/tree/v3.12.0" }, "funding": [ { @@ -1428,20 +1427,20 @@ "type": "tidelift" } ], - "time": "2024-08-08T16:15:16+00:00" + "time": "2024-08-29T09:51:12+00:00" }, { "name": "zoujingli/wechat-developer", - "version": "v1.2.54", + "version": "v1.2.59", "source": { "type": "git", "url": "https://github.com/zoujingli/WeChatDeveloper.git", - "reference": "812aae37ffc5b6038b03163796f6eed97bb79730" + "reference": "d422e5dc1768033dd6849f7e23180ee7da02d8ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zoujingli/WeChatDeveloper/zipball/812aae37ffc5b6038b03163796f6eed97bb79730", - "reference": "812aae37ffc5b6038b03163796f6eed97bb79730", + "url": "https://api.github.com/repos/zoujingli/WeChatDeveloper/zipball/d422e5dc1768033dd6849f7e23180ee7da02d8ee", + "reference": "d422e5dc1768033dd6849f7e23180ee7da02d8ee", "shasum": "" }, "require": { @@ -1457,6 +1456,9 @@ }, "type": "library", "autoload": { + "files": [ + "helper.php" + ], "psr-4": { "WePay\\": "WePay", "AliPay\\": "AliPay", @@ -1490,9 +1492,9 @@ ], "support": { "issues": "https://github.com/zoujingli/WeChatDeveloper/issues", - "source": "https://github.com/zoujingli/WeChatDeveloper/tree/v1.2.54" + "source": "https://github.com/zoujingli/WeChatDeveloper/tree/v1.2.59" }, - "time": "2024-01-16T13:13:59+00:00" + "time": "2024-08-18T14:32:01+00:00" } ], "packages-dev": [], diff --git a/upload/system/storage/vendor/composer/autoload_files.php b/upload/system/storage/vendor/composer/autoload_files.php index 95c41dc18..673c5913a 100644 --- a/upload/system/storage/vendor/composer/autoload_files.php +++ b/upload/system/storage/vendor/composer/autoload_files.php @@ -8,14 +8,15 @@ return array( '7b11c4dc42b3b3023073cb14e519683c' => $vendorDir . '/ralouphie/getallheaders/src/getallheaders.php', '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', - 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => $vendorDir . '/guzzlehttp/guzzle/src/functions_include.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', + 'a4a119a56e50fbb293281d9a48007e0e' => $vendorDir . '/symfony/polyfill-php80/bootstrap.php', '662a729f963d39afe703c9d9b7ab4a8c' => $vendorDir . '/symfony/polyfill-php83/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => $vendorDir . '/symfony/polyfill-php81/bootstrap.php', '89efb1254ef2d1c5d80096acd12c4098' => $vendorDir . '/twig/twig/src/Resources/core.php', 'ffecb95d45175fd40f75be8a23b34f90' => $vendorDir . '/twig/twig/src/Resources/debug.php', 'c7baa00073ee9c61edf148c51917cfb4' => $vendorDir . '/twig/twig/src/Resources/escaper.php', 'f844ccf1d25df8663951193c3fc307c8' => $vendorDir . '/twig/twig/src/Resources/string_loader.php', + 'c8e8d17e7ab157f630af324c6fb87d41' => $vendorDir . '/zoujingli/wechat-developer/helper.php', ); diff --git a/upload/system/storage/vendor/composer/autoload_static.php b/upload/system/storage/vendor/composer/autoload_static.php index 9beecd570..29d688215 100644 --- a/upload/system/storage/vendor/composer/autoload_static.php +++ b/upload/system/storage/vendor/composer/autoload_static.php @@ -9,16 +9,17 @@ class ComposerStaticInit723df5bcd9366cc0d09a11e95c1b3994 public static $files = array ( '7b11c4dc42b3b3023073cb14e519683c' => __DIR__ . '/..' . '/ralouphie/getallheaders/src/getallheaders.php', '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', - 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '37a3dc5111fe8f707ab4c132ef1dbc62' => __DIR__ . '/..' . '/guzzlehttp/guzzle/src/functions_include.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', + 'a4a119a56e50fbb293281d9a48007e0e' => __DIR__ . '/..' . '/symfony/polyfill-php80/bootstrap.php', '662a729f963d39afe703c9d9b7ab4a8c' => __DIR__ . '/..' . '/symfony/polyfill-php83/bootstrap.php', '23c18046f52bef3eea034657bafda50f' => __DIR__ . '/..' . '/symfony/polyfill-php81/bootstrap.php', '89efb1254ef2d1c5d80096acd12c4098' => __DIR__ . '/..' . '/twig/twig/src/Resources/core.php', 'ffecb95d45175fd40f75be8a23b34f90' => __DIR__ . '/..' . '/twig/twig/src/Resources/debug.php', 'c7baa00073ee9c61edf148c51917cfb4' => __DIR__ . '/..' . '/twig/twig/src/Resources/escaper.php', 'f844ccf1d25df8663951193c3fc307c8' => __DIR__ . '/..' . '/twig/twig/src/Resources/string_loader.php', + 'c8e8d17e7ab157f630af324c6fb87d41' => __DIR__ . '/..' . '/zoujingli/wechat-developer/helper.php', ); public static $prefixLengthsPsr4 = array ( diff --git a/upload/system/storage/vendor/composer/installed.json b/upload/system/storage/vendor/composer/installed.json index c17a7a824..7451426f4 100644 --- a/upload/system/storage/vendor/composer/installed.json +++ b/upload/system/storage/vendor/composer/installed.json @@ -1400,34 +1400,33 @@ }, { "name": "twig/twig", - "version": "v3.11.0", - "version_normalized": "3.11.0.0", + "version": "v3.12.0", + "version_normalized": "3.12.0.0", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d" + "reference": "4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", - "reference": "e80fb8ebba85c7341a97a9ebf825d7fd4b77708d", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea", + "reference": "4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea", "shasum": "" }, "require": { - "php": ">=7.2.5", + "php": ">=8.0.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3", - "symfony/polyfill-php80": "^1.22", "symfony/polyfill-php81": "^1.29" }, "require-dev": { "psr/container": "^1.0|^2.0", "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, - "time": "2024-08-08T16:15:16+00:00", + "time": "2024-08-29T09:51:12+00:00", "type": "library", - "installation-source": "dist", + "installation-source": "source", "autoload": { "files": [ "src/Resources/core.php", @@ -1467,7 +1466,7 @@ ], "support": { "issues": "https://github.com/twigphp/Twig/issues", - "source": "https://github.com/twigphp/Twig/tree/v3.11.0" + "source": "https://github.com/twigphp/Twig/tree/v3.12.0" }, "funding": [ { @@ -1483,17 +1482,17 @@ }, { "name": "zoujingli/wechat-developer", - "version": "v1.2.54", - "version_normalized": "1.2.54.0", + "version": "v1.2.59", + "version_normalized": "1.2.59.0", "source": { "type": "git", "url": "https://github.com/zoujingli/WeChatDeveloper.git", - "reference": "812aae37ffc5b6038b03163796f6eed97bb79730" + "reference": "d422e5dc1768033dd6849f7e23180ee7da02d8ee" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/zoujingli/WeChatDeveloper/zipball/812aae37ffc5b6038b03163796f6eed97bb79730", - "reference": "812aae37ffc5b6038b03163796f6eed97bb79730", + "url": "https://api.github.com/repos/zoujingli/WeChatDeveloper/zipball/d422e5dc1768033dd6849f7e23180ee7da02d8ee", + "reference": "d422e5dc1768033dd6849f7e23180ee7da02d8ee", "shasum": "" }, "require": { @@ -1507,10 +1506,13 @@ "ext-xml": "*", "php": ">=5.4" }, - "time": "2024-01-16T13:13:59+00:00", + "time": "2024-08-18T14:32:01+00:00", "type": "library", - "installation-source": "dist", + "installation-source": "source", "autoload": { + "files": [ + "helper.php" + ], "psr-4": { "WePay\\": "WePay", "AliPay\\": "AliPay", @@ -1544,7 +1546,7 @@ ], "support": { "issues": "https://github.com/zoujingli/WeChatDeveloper/issues", - "source": "https://github.com/zoujingli/WeChatDeveloper/tree/v1.2.54" + "source": "https://github.com/zoujingli/WeChatDeveloper/tree/v1.2.59" }, "install-path": "../zoujingli/wechat-developer" } diff --git a/upload/system/storage/vendor/composer/installed.php b/upload/system/storage/vendor/composer/installed.php index 9038f820d..6e4be943f 100644 --- a/upload/system/storage/vendor/composer/installed.php +++ b/upload/system/storage/vendor/composer/installed.php @@ -3,7 +3,7 @@ 'name' => 'opencart/opencart-3', 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '5930e7357189d9fe45ecea0498fbd7f199902bec', + 'reference' => '7142bae06b051db7aba9450f86dea75c041af710', 'type' => 'project', 'install_path' => __DIR__ . '/../../../../../', 'aliases' => array(), @@ -58,7 +58,7 @@ 'opencart/opencart-3' => array( 'pretty_version' => 'dev-main', 'version' => 'dev-main', - 'reference' => '5930e7357189d9fe45ecea0498fbd7f199902bec', + 'reference' => '7142bae06b051db7aba9450f86dea75c041af710', 'type' => 'project', 'install_path' => __DIR__ . '/../../../../../', 'aliases' => array(), @@ -200,18 +200,18 @@ 'dev_requirement' => false, ), 'twig/twig' => array( - 'pretty_version' => 'v3.11.0', - 'version' => '3.11.0.0', - 'reference' => 'e80fb8ebba85c7341a97a9ebf825d7fd4b77708d', + 'pretty_version' => 'v3.12.0', + 'version' => '3.12.0.0', + 'reference' => '4d19472d4ac1838e0b1f0e029ce1fa4040eb34ea', 'type' => 'library', 'install_path' => __DIR__ . '/../twig/twig', 'aliases' => array(), 'dev_requirement' => false, ), 'zoujingli/wechat-developer' => array( - 'pretty_version' => 'v1.2.54', - 'version' => '1.2.54.0', - 'reference' => '812aae37ffc5b6038b03163796f6eed97bb79730', + 'pretty_version' => 'v1.2.59', + 'version' => '1.2.59.0', + 'reference' => 'd422e5dc1768033dd6849f7e23180ee7da02d8ee', 'type' => 'library', 'install_path' => __DIR__ . '/../zoujingli/wechat-developer', 'aliases' => array(), diff --git a/upload/system/storage/vendor/twig/twig/.editorconfig b/upload/system/storage/vendor/twig/twig/.editorconfig new file mode 100644 index 000000000..270f1d1b7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/.editorconfig @@ -0,0 +1,18 @@ +; top-most EditorConfig file +root = true + +; Unix-style newlines +[*] +end_of_line = LF + +[*.php] +indent_style = space +indent_size = 4 + +[*.test] +indent_style = space +indent_size = 4 + +[*.rst] +indent_style = space +indent_size = 4 diff --git a/upload/system/storage/vendor/twig/twig/.gitattributes b/upload/system/storage/vendor/twig/twig/.gitattributes new file mode 100644 index 000000000..86b9ef413 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/.gitattributes @@ -0,0 +1,9 @@ +/.github/ export-ignore +/doc/ export-ignore +/extra/ export-ignore +/tests/ export-ignore +/.editorconfig export-ignore +/.gitattributes export-ignore +/.gitignore export-ignore +/.php-cs-fixer.dist.php export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/.github/dependabot.yml b/upload/system/storage/vendor/twig/twig/.github/dependabot.yml new file mode 100644 index 000000000..5ace4600a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/.github/dependabot.yml @@ -0,0 +1,6 @@ +version: 2 +updates: + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "weekly" diff --git a/upload/system/storage/vendor/twig/twig/.github/workflows/ci.yml b/upload/system/storage/vendor/twig/twig/.github/workflows/ci.yml new file mode 100644 index 000000000..3de8aa10c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/.github/workflows/ci.yml @@ -0,0 +1,158 @@ +name: "CI" + +on: + pull_request: + push: + branches: + - '3.x' + +env: + SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE: 1 + +permissions: + contents: read + +jobs: + tests: + name: "PHP ${{ matrix.php-version }}" + + runs-on: 'ubuntu-latest' + + strategy: + matrix: + php-version: + - '8.0' + - '8.1' + - '8.2' + - '8.3' + - '8.4' + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Install PHP with extensions" + uses: shivammathur/setup-php@v2 + with: + coverage: "none" + php-version: ${{ matrix.php-version }} + ini-values: memory_limit=-1 + + - name: "Add PHPUnit matcher" + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - run: composer install + + - name: "Switch use_yield to true on PHP ${{ matrix.php-version }}" + if: "matrix.php-version == '8.2'" + run: | + sed -i -e "s/'use_yield' => false/'use_yield' => true/" src/Environment.php + + - name: "Install PHPUnit" + run: vendor/bin/simple-phpunit install + + - name: "PHPUnit version" + run: vendor/bin/simple-phpunit --version + + - name: "Run tests" + run: vendor/bin/simple-phpunit + + extension-tests: + needs: + - 'tests' + + name: "${{ matrix.extension }} PHP ${{ matrix.php-version }}" + + runs-on: 'ubuntu-latest' + + continue-on-error: true + + strategy: + matrix: + php-version: + - '8.0' + - '8.1' + - '8.2' + - '8.3' + - '8.4' + extension: + - 'cache-extra' + - 'cssinliner-extra' + - 'html-extra' + - 'inky-extra' + - 'intl-extra' + - 'markdown-extra' + - 'string-extra' + - 'twig-extra-bundle' + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Install PHP with extensions" + uses: shivammathur/setup-php@v2 + with: + coverage: "none" + php-version: ${{ matrix.php-version }} + ini-values: memory_limit=-1 + + - name: "Add PHPUnit matcher" + run: echo "::add-matcher::${{ runner.tool_cache }}/phpunit.json" + + - name: "Composer install Twig" + run: composer install + + - name: "Install PHPUnit" + run: vendor/bin/simple-phpunit install + + - name: "PHPUnit version" + run: vendor/bin/simple-phpunit --version + + - name: "Prevent installing symfony/translation-contract 3.0" + if: "matrix.extension == 'twig-extra-bundle'" + working-directory: extra/${{ matrix.extension }} + run: "composer require --no-update 'symfony/translation-contracts:^1.1|^2.0'" + + - name: "Composer install ${{ matrix.extension }}" + working-directory: extra/${{ matrix.extension }} + run: composer install + + - name: "Switch use_yield to true" + if: "matrix.php-version == '8.2'" + run: | + sed -i -e "s/'use_yield' => false/'use_yield' => true/" extra/${{ matrix.extension }}/vendor/twig/twig/src/Environment.php + + - name: "Run tests for ${{ matrix.extension }}" + working-directory: extra/${{ matrix.extension }} + run: ../../vendor/bin/simple-phpunit + + integration-tests: + needs: + - 'tests' + + name: "Integration tests with PHP ${{ matrix.php-version }}" + + runs-on: 'ubuntu-latest' + + continue-on-error: true + + strategy: + matrix: + php-version: + - '8.2' + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Install PHP with extensions" + uses: shivammathur/setup-php@v2 + with: + coverage: "none" + extensions: "gd, pdo_sqlite, uuid" + php-version: ${{ matrix.php-version }} + ini-values: memory_limit=-1 + tools: composer:v2 + + - run: bash ./tests/drupal_test.sh + shell: "bash" diff --git a/upload/system/storage/vendor/twig/twig/.github/workflows/documentation.yml b/upload/system/storage/vendor/twig/twig/.github/workflows/documentation.yml new file mode 100644 index 000000000..8fe7c868c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/.github/workflows/documentation.yml @@ -0,0 +1,64 @@ +name: "Documentation" + +on: + pull_request: + push: + branches: + - '2.x' + - '3.x' + +permissions: + contents: read + +jobs: + build: + name: "Build" + + runs-on: ubuntu-latest + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Set-up PHP" + uses: shivammathur/setup-php@v2 + with: + php-version: 8.2 + coverage: none + tools: "composer:v2" + + - name: Get composer cache directory + id: composercache + working-directory: doc/_build + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v4 + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: "Install dependencies" + working-directory: doc/_build + run: composer install --prefer-dist --no-progress + + - name: "Build the docs" + working-directory: doc/_build + run: php build.php --disable-cache + + doctor-rst: + name: "DOCtor-RST" + + runs-on: ubuntu-latest + + steps: + - name: "Checkout code" + uses: actions/checkout@v4 + + - name: "Run DOCtor-RST" + uses: docker://oskarstark/doctor-rst + with: + args: --short + env: + DOCS_DIR: 'doc/' diff --git a/upload/system/storage/vendor/twig/twig/.gitignore b/upload/system/storage/vendor/twig/twig/.gitignore new file mode 100644 index 000000000..b197246ba --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/.gitignore @@ -0,0 +1,6 @@ +/doc/_build/vendor +/doc/_build/output +/composer.lock +/phpunit.xml +/vendor +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/.php-cs-fixer.dist.php b/upload/system/storage/vendor/twig/twig/.php-cs-fixer.dist.php new file mode 100644 index 000000000..5c3a731a1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/.php-cs-fixer.dist.php @@ -0,0 +1,21 @@ +setRules([ + '@Symfony' => true, + '@Symfony:risky' => true, + '@PHPUnit75Migration:risky' => true, + 'php_unit_dedicate_assert' => ['target' => '5.6'], + 'array_syntax' => ['syntax' => 'short'], + 'php_unit_fqcn_annotation' => true, + 'no_unreachable_default_argument_value' => false, + 'braces' => ['allow_single_line_closure' => true], + 'heredoc_to_nowdoc' => false, + 'ordered_imports' => true, + 'phpdoc_types_order' => ['null_adjustment' => 'always_last', 'sort_algorithm' => 'none'], + // TODO: Remove once the "compiler_optimized" set includes "sprintf" + 'native_function_invocation' => ['include' => ['@compiler_optimized', 'sprintf'], 'scope' => 'all'], + ]) + ->setRiskyAllowed(true) + ->setFinder((new PhpCsFixer\Finder())->in(__DIR__)) +; diff --git a/upload/system/storage/vendor/twig/twig/CHANGELOG b/upload/system/storage/vendor/twig/twig/CHANGELOG index dd776e083..ff6951e6d 100644 --- a/upload/system/storage/vendor/twig/twig/CHANGELOG +++ b/upload/system/storage/vendor/twig/twig/CHANGELOG @@ -1,5 +1,36 @@ +# 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.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 diff --git a/upload/system/storage/vendor/twig/twig/composer.json b/upload/system/storage/vendor/twig/twig/composer.json index 26cb4972e..e0c3e6c6c 100644 --- a/upload/system/storage/vendor/twig/twig/composer.json +++ b/upload/system/storage/vendor/twig/twig/composer.json @@ -24,8 +24,7 @@ } ], "require": { - "php": ">=7.2.5", - "symfony/polyfill-php80": "^1.22", + "php": ">=8.0.2", "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "^1.3", "symfony/polyfill-ctype": "^1.8", diff --git a/upload/system/storage/vendor/twig/twig/doc/.doctor-rst.yaml b/upload/system/storage/vendor/twig/twig/doc/.doctor-rst.yaml new file mode 100644 index 000000000..bfb404263 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/.doctor-rst.yaml @@ -0,0 +1,54 @@ +rules: + american_english: ~ + avoid_repetetive_words: ~ + blank_line_after_directive: ~ + blank_line_before_directive: ~ + composer_dev_option_not_at_the_end: ~ + correct_code_block_directive_based_on_the_content: ~ + deprecated_directive_should_have_version: ~ + ensure_order_of_code_blocks_in_configuration_block: ~ + extension_xlf_instead_of_xliff: ~ + indention: ~ + lowercase_as_in_use_statements: ~ + max_blank_lines: + max: 2 + no_blank_line_after_filepath_in_php_code_block: ~ + no_blank_line_after_filepath_in_twig_code_block: ~ + no_blank_line_after_filepath_in_xml_code_block: ~ + no_blank_line_after_filepath_in_yaml_code_block: ~ + no_composer_req: ~ + no_explicit_use_of_code_block_php: ~ + no_inheritdoc: ~ + no_namespace_after_use_statements: ~ + no_php_open_tag_in_code_block_php_directive: ~ + no_space_before_self_xml_closing_tag: ~ + ordered_use_statements: ~ + php_prefix_before_bin_console: ~ + replace_code_block_types: ~ + replacement: ~ + short_array_syntax: ~ + typo: ~ + unused_links: ~ + use_deprecated_directive_instead_of_versionadded: ~ + use_https_xsd_urls: ~ + valid_inline_highlighted_namespaces: ~ + valid_use_statements: ~ + versionadded_directive_should_have_version: ~ + yaml_instead_of_yml_suffix: ~ + yarn_dev_option_at_the_end: ~ + + versionadded_directive_major_version: + major_version: 3 + + versionadded_directive_min_version: + min_version: '3.0' + + deprecated_directive_major_version: + major_version: 3 + + deprecated_directive_min_version: + min_version: '3.0' + +whitelist: + lines: + - 'I like Twig.
' diff --git a/upload/system/storage/vendor/twig/twig/doc/_build/build.php b/upload/system/storage/vendor/twig/twig/doc/_build/build.php new file mode 100644 index 000000000..2b183b186 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/_build/build.php @@ -0,0 +1,56 @@ +#!/usr/bin/env php +register('build-docs') + ->addOption('disable-cache', null, InputOption::VALUE_NONE, 'Use this option to force a full regeneration of all doc contents') + ->setCode(function (InputInterface $input, OutputInterface $output) { + $io = new SymfonyStyle($input, $output); + $io->text('Building all Twig docs...'); + + $outputDir = __DIR__.'/output'; + $buildConfig = (new BuildConfig()) + ->setContentDir(__DIR__.'/..') + ->setOutputDir($outputDir) + ->setImagesDir(__DIR__.'/output/_images') + ->setImagesPublicPrefix('_images') + ->setTheme('rtd') + ; + + $buildConfig->setExcludedPaths(['vendor/']); + $buildConfig->disableJsonFileGeneration(); + $buildConfig->disableBuildCache(); + + $result = (new DocBuilder())->build($buildConfig); + + if ($result->isSuccessful()) { + // fix assets URLs to make them absolute (otherwise, they don't work in subdirectories) + foreach (glob($outputDir.'/**/*.html') as $htmlFilePath) { + $htmlContents = file_get_contents($htmlFilePath); + file_put_contents($htmlFilePath, str_replace('href="assets/', 'href="/assets/', $htmlContents)); + } + + $io->success(\sprintf('The Twig docs were successfully built at %s', realpath($outputDir))); + } else { + $io->error(\sprintf("There were some errors while building the docs:\n\n%s\n", $result->getErrorTrace())); + $io->newLine(); + $io->comment('Tip: you can add the -v, -vv or -vvv flags to this command to get debug information.'); + + return 1; + } + + return 0; + }) + ->getApplication() + ->setDefaultCommand('build-docs', true) + ->run(); diff --git a/upload/system/storage/vendor/twig/twig/doc/_build/composer.json b/upload/system/storage/vendor/twig/twig/doc/_build/composer.json new file mode 100644 index 000000000..d9b1e5d41 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/_build/composer.json @@ -0,0 +1,19 @@ +{ + "minimum-stability": "dev", + "prefer-stable": true, + "config": { + "platform": { + "php": "8.1.0" + }, + "preferred-install": { + "*": "dist" + }, + "sort-packages": true + }, + "require": { + "php": ">=8.1", + "symfony/console": "^5.4", + "symfony/process": "^5.4", + "symfony-tools/docs-builder": "^0.18" + } +} diff --git a/upload/system/storage/vendor/twig/twig/doc/_build/composer.lock b/upload/system/storage/vendor/twig/twig/doc/_build/composer.lock new file mode 100644 index 000000000..765fc3e72 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/_build/composer.lock @@ -0,0 +1,1962 @@ +{ + "_readme": [ + "This file locks the dependencies of your project to a known state", + "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", + "This file is @generated automatically" + ], + "content-hash": "b71f84fed3bf9d4dddbc68f14f71b9b4", + "packages": [ + { + "name": "doctrine/event-manager", + "version": "1.1.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/event-manager.git", + "reference": "eb2ecf80e3093e8f3c2769ac838e27d8ede8e683" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/event-manager/zipball/eb2ecf80e3093e8f3c2769ac838e27d8ede8e683", + "reference": "eb2ecf80e3093e8f3c2769ac838e27d8ede8e683", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/common": "<2.9" + }, + "require-dev": { + "doctrine/coding-standard": "^9", + "phpstan/phpstan": "~1.4.10 || ^1.5.4", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "vimeo/psalm": "^4.22" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\": "lib/Doctrine/Common" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + }, + { + "name": "Marco Pivetta", + "email": "ocramius@gmail.com" + } + ], + "description": "The Doctrine Event Manager is a simple PHP event system that was built to be used with the various Doctrine projects.", + "homepage": "https://www.doctrine-project.org/projects/event-manager.html", + "keywords": [ + "event", + "event dispatcher", + "event manager", + "event system", + "events" + ], + "support": { + "issues": "https://github.com/doctrine/event-manager/issues", + "source": "https://github.com/doctrine/event-manager/tree/1.1.2" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Fevent-manager", + "type": "tidelift" + } + ], + "time": "2022-07-27T22:18:11+00:00" + }, + { + "name": "doctrine/rst-parser", + "version": "0.5.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/rst-parser.git", + "reference": "3b914d5eb8f6a91afc7462ea7794b0e05b884a35" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/rst-parser/zipball/3b914d5eb8f6a91afc7462ea7794b0e05b884a35", + "reference": "3b914d5eb8f6a91afc7462ea7794b0e05b884a35", + "shasum": "" + }, + "require": { + "doctrine/event-manager": "^1.0", + "php": "^7.2 || ^8.0", + "symfony/filesystem": "^4.1 || ^5.0 || ^6.0", + "symfony/finder": "^4.1 || ^5.0 || ^6.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/string": "^5.3 || ^6.0", + "symfony/translation-contracts": "^1.1 || ^2.0", + "twig/twig": "^2.9 || ^3.3" + }, + "require-dev": { + "doctrine/coding-standard": "^8.0", + "gajus/dindent": "^2.0.2", + "phpstan/phpstan": "^0.12", + "phpstan/phpstan-deprecation-rules": "^0.12", + "phpstan/phpstan-phpunit": "^0.12", + "phpstan/phpstan-strict-rules": "^0.12", + "phpunit/phpunit": "^7.5 || ^8.0 || ^9.0", + "symfony/css-selector": "4.4 || ^5.2 || ^6.0", + "symfony/dom-crawler": "4.4 || ^5.2 || ^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\RST\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Jonathan H. Wage", + "email": "jonwage@gmail.com", + "homepage": "https://jwage.com" + } + ], + "description": "PHP library to parse reStructuredText documents and generate HTML or LaTeX documents.", + "homepage": "https://github.com/doctrine/rst-parser", + "keywords": [ + "html", + "latex", + "markup", + "parser", + "reStructuredText", + "rst" + ], + "support": { + "issues": "https://github.com/doctrine/rst-parser/issues", + "source": "https://github.com/doctrine/rst-parser/tree/0.5.2" + }, + "time": "2022-03-22T13:52:20+00:00" + }, + { + "name": "masterminds/html5", + "version": "2.7.5", + "source": { + "type": "git", + "url": "https://github.com/Masterminds/html5-php.git", + "reference": "f640ac1bdddff06ea333a920c95bbad8872429ab" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Masterminds/html5-php/zipball/f640ac1bdddff06ea333a920c95bbad8872429ab", + "reference": "f640ac1bdddff06ea333a920c95bbad8872429ab", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-dom": "*", + "ext-libxml": "*", + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^4.8.35 || ^5.7.21 || ^6 || ^7" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.7-dev" + } + }, + "autoload": { + "psr-4": { + "Masterminds\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Matt Butcher", + "email": "technosophos@gmail.com" + }, + { + "name": "Matt Farina", + "email": "matt@mattfarina.com" + }, + { + "name": "Asmir Mustafic", + "email": "goetas@gmail.com" + } + ], + "description": "An HTML5 parser and serializer.", + "homepage": "http://masterminds.github.io/html5-php", + "keywords": [ + "HTML5", + "dom", + "html", + "parser", + "querypath", + "serializer", + "xml" + ], + "support": { + "issues": "https://github.com/Masterminds/html5-php/issues", + "source": "https://github.com/Masterminds/html5-php/tree/2.7.5" + }, + "time": "2021-07-01T14:25:37+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/log", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/ef29f6d262798707a9edd554e2b82517ef3a9376", + "reference": "ef29f6d262798707a9edd554e2b82517ef3a9376", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/2.0.0" + }, + "time": "2021-07-14T16:41:46+00:00" + }, + { + "name": "scrivo/highlight.php", + "version": "v9.18.1.9", + "source": { + "type": "git", + "url": "https://github.com/scrivo/highlight.php.git", + "reference": "d45585504777e6194a91dffc7270ca39833787f8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/scrivo/highlight.php/zipball/d45585504777e6194a91dffc7270ca39833787f8", + "reference": "d45585504777e6194a91dffc7270ca39833787f8", + "shasum": "" + }, + "require": { + "ext-json": "*", + "php": ">=5.4" + }, + "require-dev": { + "phpunit/phpunit": "^4.8|^5.7", + "sabberworm/php-css-parser": "^8.3", + "symfony/finder": "^2.8|^3.4", + "symfony/var-dumper": "^2.8|^3.4" + }, + "suggest": { + "ext-mbstring": "Allows highlighting code with unicode characters and supports language with unicode keywords" + }, + "type": "library", + "autoload": { + "files": [ + "HighlightUtilities/functions.php" + ], + "psr-0": { + "Highlight\\": "", + "HighlightUtilities\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Geert Bergman", + "homepage": "http://www.scrivo.org/", + "role": "Project Author" + }, + { + "name": "Vladimir Jimenez", + "homepage": "https://allejo.io", + "role": "Maintainer" + }, + { + "name": "Martin Folkers", + "homepage": "https://twobrain.io", + "role": "Contributor" + } + ], + "description": "Server side syntax highlighter that supports 185 languages. It's a PHP port of highlight.js", + "keywords": [ + "code", + "highlight", + "highlight.js", + "highlight.php", + "syntax" + ], + "support": { + "issues": "https://github.com/scrivo/highlight.php/issues", + "source": "https://github.com/scrivo/highlight.php" + }, + "funding": [ + { + "url": "https://github.com/allejo", + "type": "github" + } + ], + "time": "2021-12-03T06:45:28+00:00" + }, + { + "name": "symfony-tools/docs-builder", + "version": "v0.18.9", + "source": { + "type": "git", + "url": "https://github.com/symfony-tools/docs-builder.git", + "reference": "1bc91f91887b115d78e7d2c8879c19af515b36ae" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony-tools/docs-builder/zipball/1bc91f91887b115d78e7d2c8879c19af515b36ae", + "reference": "1bc91f91887b115d78e7d2c8879c19af515b36ae", + "shasum": "" + }, + "require": { + "doctrine/rst-parser": "^0.5", + "ext-curl": "*", + "ext-json": "*", + "php": ">=7.4", + "scrivo/highlight.php": "^9.12.0", + "symfony/console": "^5.2 || ^6.0", + "symfony/css-selector": "^5.2 || ^6.0", + "symfony/dom-crawler": "^5.2 || ^6.0", + "symfony/filesystem": "^5.2 || ^6.0", + "symfony/finder": "^5.2 || ^6.0", + "symfony/http-client": "^5.2 || ^6.0", + "twig/twig": "^2.14 || ^3.3" + }, + "require-dev": { + "gajus/dindent": "^2.0", + "symfony/phpunit-bridge": "^5.2 || ^6.0", + "symfony/process": "^5.2 || ^6.0" + }, + "bin": [ + "bin/docs-builder" + ], + "type": "project", + "autoload": { + "psr-4": { + "SymfonyDocsBuilder\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The build system for Symfony's documentation", + "support": { + "issues": "https://github.com/symfony-tools/docs-builder/issues", + "source": "https://github.com/symfony-tools/docs-builder/tree/v0.18.9" + }, + "time": "2022-03-22T14:32:49+00:00" + }, + { + "name": "symfony/console", + "version": "v5.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "535846c7ee6bc4dd027ca0d93220601456734b10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/535846c7ee6bc4dd027ca0d93220601456734b10", + "reference": "535846c7ee6bc4dd027ca0d93220601456734b10", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/deprecation-contracts": "^2.1|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/polyfill-php73": "^1.9", + "symfony/polyfill-php80": "^1.16", + "symfony/service-contracts": "^1.1|^2|^3", + "symfony/string": "^5.1|^6.0" + }, + "conflict": { + "psr/log": ">=3", + "symfony/dependency-injection": "<4.4", + "symfony/dotenv": "<5.1", + "symfony/event-dispatcher": "<4.4", + "symfony/lock": "<4.4", + "symfony/process": "<4.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0" + }, + "require-dev": { + "psr/log": "^1|^2", + "symfony/config": "^4.4|^5.0|^6.0", + "symfony/dependency-injection": "^4.4|^5.0|^6.0", + "symfony/event-dispatcher": "^4.4|^5.0|^6.0", + "symfony/lock": "^4.4|^5.0|^6.0", + "symfony/process": "^4.4|^5.0|^6.0", + "symfony/var-dumper": "^4.4|^5.0|^6.0" + }, + "suggest": { + "psr/log": "For using the console logger", + "symfony/event-dispatcher": "", + "symfony/lock": "", + "symfony/process": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command line", + "console", + "terminal" + ], + "support": { + "source": "https://github.com/symfony/console/tree/v5.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-22T10:42:43+00:00" + }, + { + "name": "symfony/css-selector", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/css-selector.git", + "reference": "0dd5e36b80e1de97f8f74ed7023ac2b837a36443" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/css-selector/zipball/0dd5e36b80e1de97f8f74ed7023ac2b837a36443", + "reference": "0dd5e36b80e1de97f8f74ed7023ac2b837a36443", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\CssSelector\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Jean-François Simon", + "email": "jeanfrancois.simon@sensiolabs.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Converts CSS selectors to XPath expressions", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/css-selector/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T17:24:16+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "reference": "07f1b9cc2ffee6aaafcf4b710fbc38ff736bd918", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-02-25T11:15:52+00:00" + }, + { + "name": "symfony/dom-crawler", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/dom-crawler.git", + "reference": "58eb9858d8752ac3586fffb37421441fc18f793c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/dom-crawler/zipball/58eb9858d8752ac3586fffb37421441fc18f793c", + "reference": "58eb9858d8752ac3586fffb37421441fc18f793c", + "shasum": "" + }, + "require": { + "masterminds/html5": "^2.6", + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.0" + }, + "require-dev": { + "symfony/css-selector": "^5.4|^6.0" + }, + "suggest": { + "symfony/css-selector": "" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\DomCrawler\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Eases DOM navigation for HTML and XML documents", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/dom-crawler/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T17:24:16+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "c780e677cddda78417fa5187a7c6cd2f21110db9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/c780e677cddda78417fa5187a7c6cd2f21110db9", + "reference": "c780e677cddda78417fa5187a7c6cd2f21110db9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/filesystem/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-20T14:45:06+00:00" + }, + { + "name": "symfony/finder", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "reference": "39696bff2c2970b3779a5cac7bf9f0b88fc2b709", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "symfony/filesystem": "^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/finder/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-29T07:42:06+00:00" + }, + { + "name": "symfony/http-client", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client.git", + "reference": "1ef59920a9cedf223e8564ae8ad7608cbe799b4d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client/zipball/1ef59920a9cedf223e8564ae8ad7608cbe799b4d", + "reference": "1ef59920a9cedf223e8564ae8ad7608cbe799b4d", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/log": "^1|^2|^3", + "symfony/http-client-contracts": "^3", + "symfony/service-contracts": "^1.0|^2|^3" + }, + "provide": { + "php-http/async-client-implementation": "*", + "php-http/client-implementation": "*", + "psr/http-client-implementation": "1.0", + "symfony/http-client-implementation": "3.0" + }, + "require-dev": { + "amphp/amp": "^2.5", + "amphp/http-client": "^4.2.1", + "amphp/http-tunnel": "^1.0", + "amphp/socket": "^1.1", + "guzzlehttp/promises": "^1.4", + "nyholm/psr7": "^1.0", + "php-http/httplug": "^1.0|^2.0", + "psr/http-client": "^1.0", + "symfony/dependency-injection": "^5.4|^6.0", + "symfony/http-kernel": "^5.4|^6.0", + "symfony/process": "^5.4|^6.0", + "symfony/stopwatch": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides powerful methods to fetch HTTP resources synchronously or asynchronously", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/http-client/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-28T13:40:41+00:00" + }, + { + "name": "symfony/http-client-contracts", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/http-client-contracts.git", + "reference": "fd038f08c623ab5d22b26e9ba35afe8c79071800" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/http-client-contracts/zipball/fd038f08c623ab5d22b26e9ba35afe8c79071800", + "reference": "fd038f08c623ab5d22b26e9ba35afe8c79071800", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "suggest": { + "symfony/http-client-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\HttpClient\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to HTTP clients", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/http-client-contracts/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-04-22T07:30:54+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "reference": "6fd1b9a79f6e3cf65f9e679b23af304cd9e010d4", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], + "support": { + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "433d05519ce6990bf3530fba6957499d327395c2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/433d05519ce6990bf3530fba6957499d327395c2", + "reference": "433d05519ce6990bf3530fba6957499d327395c2", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's grapheme_* functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/219aa369ceff116e673852dce47c3a41794c14bd", + "reference": "219aa369ceff116e673852dce47c3a41794c14bd", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "suggest": { + "ext-intl": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for intl's Normalizer class and related functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "reference": "9344f9cb97f3b19424af1a21a3b0e75b0a7d8d7e", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill for the Mbstring extension", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php73", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php73.git", + "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php73/zipball/e440d35fa0286f77fb45b79a03fedbeda9307e85", + "reference": "e440d35fa0286f77fb45b79a03fedbeda9307e85", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php73\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 7.3+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php73/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-24T11:49:31+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.26.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "reference": "cfa0ae98841b9e461207c13ab093d76b0fa7bace", + "shasum": "" + }, + "require": { + "php": ">=7.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "1.26-dev" + }, + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" + } + }, + "autoload": { + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "polyfill", + "portable", + "shim" + ], + "support": { + "source": "https://github.com/symfony/polyfill-php80/tree/v1.26.0" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-10T07:21:04+00:00" + }, + { + "name": "symfony/process", + "version": "v5.4.11", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/6e75fe6874cbc7e4773d049616ab450eff537bf1", + "reference": "6e75fe6874cbc7e4773d049616ab450eff537bf1", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-php80": "^1.16" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Process\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Executes commands in sub-processes", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/process/tree/v5.4.11" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T16:58:25+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/925e713fe8fcacf6bc05e936edd8dd5441a21239", + "reference": "925e713fe8fcacf6bc05e936edd8dd5441a21239", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "psr/container": "^2.0" + }, + "conflict": { + "ext-psr": "<1.1|>=2" + }, + "suggest": { + "symfony/service-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.1-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/service-contracts/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-05-30T19:18:58+00:00" + }, + { + "name": "symfony/string", + "version": "v6.1.3", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "f35241f45c30bcd9046af2bb200a7086f70e1d6b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/f35241f45c30bcd9046af2bb200a7086f70e1d6b", + "reference": "f35241f45c30bcd9046af2bb200a7086f70e1d6b", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.0" + }, + "require-dev": { + "symfony/error-handler": "^5.4|^6.0", + "symfony/http-client": "^5.4|^6.0", + "symfony/translation-contracts": "^2.0|^3.0", + "symfony/var-exporter": "^5.4|^6.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", + "homepage": "https://symfony.com", + "keywords": [ + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" + ], + "support": { + "source": "https://github.com/symfony/string/tree/v6.1.3" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-07-27T15:50:51+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v2.5.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/136b19dd05cdf0709db6537d058bcab6dd6e2dbe", + "reference": "136b19dd05cdf0709db6537d058bcab6dd6e2dbe", + "shasum": "" + }, + "require": { + "php": ">=7.2.5" + }, + "suggest": { + "symfony/translation-implementation": "" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.5-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v2.5.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2022-06-27T16:58:25+00:00" + }, + { + "name": "twig/twig", + "version": "v3.4.2", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077", + "reference": "e07cdd3d430cd7e453c31b36eb5ad6c0c5e43077", + "shasum": "" + }, + "require": { + "php": ">=7.2.5", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "psr/container": "^1.0", + "symfony/phpunit-bridge": "^4.4.9|^5.0.9|^6.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.4-dev" + } + }, + "autoload": { + "psr-4": { + "Twig\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "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" + } + ], + "description": "Twig, the flexible, fast, and secure template language for PHP", + "homepage": "https://twig.symfony.com", + "keywords": [ + "templating" + ], + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.4.2" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "time": "2022-08-12T06:47:24+00:00" + } + ], + "packages-dev": [], + "aliases": [], + "minimum-stability": "dev", + "stability-flags": [], + "prefer-stable": true, + "prefer-lowest": false, + "platform": { + "php": ">=8.1" + }, + "platform-dev": [], + "platform-overrides": { + "php": "8.1.0" + }, + "plugin-api-version": "2.3.0" +} diff --git a/upload/system/storage/vendor/twig/twig/doc/advanced.rst b/upload/system/storage/vendor/twig/twig/doc/advanced.rst new file mode 100644 index 000000000..b3eaa1a2e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/advanced.rst @@ -0,0 +1,936 @@ +Extending Twig +============== + +Twig can be extended in many ways; you can add extra tags, filters, tests, +operators, global variables, and functions. You can even extend the parser +itself with node visitors. + +.. note:: + + The first section of this chapter describes how to extend Twig. If you want + to reuse your changes in different projects or if you want to share them + with others, you should then create an extension as described in the + following section. + +.. caution:: + + When extending Twig without creating an extension, Twig won't be able to + recompile your templates when the PHP code is updated. To see your changes + in real-time, either disable template caching or package your code into an + extension (see the next section of this chapter). + +Before extending Twig, you must understand the differences between all the +different possible extension points and when to use them. + +First, remember that Twig has two main language constructs: + +* ``{{ }}``: used to print the result of an expression evaluation; + +* ``{% %}``: used to execute statements. + +To understand why Twig exposes so many extension points, let's see how to +implement a *Lorem ipsum* generator (it needs to know the number of words to +generate). + +You can use a ``lipsum`` *tag*: + +.. code-block:: twig + + {% lipsum 40 %} + +That works, but using a tag for ``lipsum`` is not a good idea for at least +three main reasons: + +* ``lipsum`` is not a language construct; +* The tag outputs something; +* The tag is not flexible as you cannot use it in an expression: + + .. code-block:: twig + + {{ 'some text' ~ {% lipsum 40 %} ~ 'some more text' }} + +In fact, you rarely need to create tags; and that's good news because tags are +the most complex extension point. + +Now, let's use a ``lipsum`` *filter*: + +.. code-block:: twig + + {{ 40|lipsum }} + +Again, it works. But a filter should transform the passed value to something +else. Here, we use the value to indicate the number of words to generate (so, +``40`` is an argument of the filter, not the value we want to transform). + +Next, let's use a ``lipsum`` *function*: + +.. code-block:: twig + + {{ lipsum(40) }} + +Here we go. For this specific example, the creation of a function is the +extension point to use. And you can use it anywhere an expression is accepted: + +.. code-block:: twig + + {{ 'some text' ~ lipsum(40) ~ 'some more text' }} + + {% set lipsum = lipsum(40) %} + +Lastly, you can also use a *global* object with a method able to generate lorem +ipsum text: + +.. code-block:: twig + + {{ text.lipsum(40) }} + +As a rule of thumb, use functions for frequently used features and global +objects for everything else. + +Keep in mind the following when you want to extend Twig: + +========== ========================== ========== ========================= +What? Implementation difficulty? How often? When? +========== ========================== ========== ========================= +*macro* simple frequent Content generation +*global* simple frequent Helper object +*function* simple frequent Content generation +*filter* simple frequent Value transformation +*tag* complex rare DSL language construct +*test* simple rare Boolean decision +*operator* simple rare Values transformation +========== ========================== ========== ========================= + +Globals +------- + +A global variable is like any other template variable, except that it's +available in all templates and macros:: + + $twig = new \Twig\Environment($loader); + $twig->addGlobal('text', new Text()); + +You can then use the ``text`` variable anywhere in a template: + +.. code-block:: twig + + {{ text.lipsum(40) }} + +Filters +------- + +Creating a filter consists of associating a name with a PHP callable:: + + // an anonymous function + $filter = new \Twig\TwigFilter('rot13', function ($string) { + return str_rot13($string); + }); + + // or a simple PHP function + $filter = new \Twig\TwigFilter('rot13', 'str_rot13'); + + // or a class static method + $filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']); + $filter = new \Twig\TwigFilter('rot13', 'SomeClass::rot13Filter'); + + // or a class method + $filter = new \Twig\TwigFilter('rot13', [$this, 'rot13Filter']); + // the one below needs a runtime implementation (see below for more information) + $filter = new \Twig\TwigFilter('rot13', ['SomeClass', 'rot13Filter']); + +The first argument passed to the ``\Twig\TwigFilter`` constructor is the name of the +filter you will use in templates and the second one is the PHP callable to +associate with it. + +Then, add the filter to the Twig environment:: + + $twig = new \Twig\Environment($loader); + $twig->addFilter($filter); + +And here is how to use it in a template: + +.. code-block:: twig + + {{ 'Twig'|rot13 }} + + {# will output Gjvt #} + +When called by Twig, the PHP callable receives the left side of the filter +(before the pipe ``|``) as the first argument and the extra arguments passed +to the filter (within parentheses ``()``) as extra arguments. + +For instance, the following code: + +.. code-block:: twig + + {{ 'TWIG'|lower }} + {{ now|date('d/m/Y') }} + +is compiled to something like the following:: + + + + +The ``\Twig\TwigFilter`` class takes an array of options as its last argument:: + + $filter = new \Twig\TwigFilter('rot13', 'str_rot13', $options); + +Charset-aware Filters +~~~~~~~~~~~~~~~~~~~~~ + +If you want to access the default charset in your filter, set the +``needs_charset`` option to ``true``; Twig will pass the default charset as the +first argument to the filter call:: + + $filter = new \Twig\TwigFilter('rot13', function (string $charset, $string) { + return str_rot13($string); + }, ['needs_charset' => true]); + +Environment-aware Filters +~~~~~~~~~~~~~~~~~~~~~~~~~ + +If you want to access the current environment instance in your filter, set the +``needs_environment`` option to ``true``; Twig will pass the current +environment as the first argument to the filter call:: + + $filter = new \Twig\TwigFilter('rot13', function (\Twig\Environment $env, $string) { + // get the current charset for instance + $charset = $env->getCharset(); + + return str_rot13($string); + }, ['needs_environment' => true]); + +Context-aware Filters +~~~~~~~~~~~~~~~~~~~~~ + +If you want to access the current context in your filter, set the +``needs_context`` option to ``true``; Twig will pass the current context as +the first argument to the filter call (or the second one if +``needs_environment`` is also set to ``true``):: + + $filter = new \Twig\TwigFilter('rot13', function ($context, $string) { + // ... + }, ['needs_context' => true]); + + $filter = new \Twig\TwigFilter('rot13', function (\Twig\Environment $env, $context, $string) { + // ... + }, ['needs_context' => true, 'needs_environment' => true]); + +Automatic Escaping +~~~~~~~~~~~~~~~~~~ + +If automatic escaping is enabled, the output of the filter may be escaped +before printing. If your filter acts as an escaper (or explicitly outputs HTML +or JavaScript code), you will want the raw output to be printed. In such a +case, set the ``is_safe`` option:: + + $filter = new \Twig\TwigFilter('nl2br', 'nl2br', ['is_safe' => ['html']]); + +Some filters may need to work on input that is already escaped or safe, for +example when adding (safe) HTML tags to originally unsafe output. In such a +case, set the ``pre_escape`` option to escape the input data before it is run +through your filter:: + + $filter = new \Twig\TwigFilter('somefilter', 'somefilter', ['pre_escape' => 'html', 'is_safe' => ['html']]); + +Variadic Filters +~~~~~~~~~~~~~~~~ + +When a filter should accept an arbitrary number of arguments, set the +``is_variadic`` option to ``true``; Twig will pass the extra arguments as the +last argument to the filter call as an array:: + + $filter = new \Twig\TwigFilter('thumbnail', function ($file, array $options = []) { + // ... + }, ['is_variadic' => true]); + +Be warned that :ref:`named arguments ` passed to a variadic +filter cannot be checked for validity as they will automatically end up in the +option array. + +Dynamic Filters +~~~~~~~~~~~~~~~ + +A filter name containing the special ``*`` character is a dynamic filter and +the ``*`` part will match any string:: + + $filter = new \Twig\TwigFilter('*_path', function ($name, $arguments) { + // ... + }); + +The following filters are matched by the above defined dynamic filter: + +* ``product_path`` +* ``category_path`` + +A dynamic filter can define more than one dynamic parts:: + + $filter = new \Twig\TwigFilter('*_path_*', function ($name, $suffix, $arguments) { + // ... + }); + +The filter receives all dynamic part values before the normal filter arguments, +but after the environment and the context. For instance, a call to +``'foo'|a_path_b()`` will result in the following arguments to be passed to the +filter: ``('a', 'b', 'foo')``. + +Deprecated Filters +~~~~~~~~~~~~~~~~~~ + +You can mark a filter as being deprecated by setting the ``deprecated`` option +to ``true``. You can also give an alternative filter that replaces the +deprecated one when that makes sense:: + + $filter = new \Twig\TwigFilter('obsolete', function () { + // ... + }, ['deprecated' => true, 'alternative' => 'new_one']); + +.. versionadded:: 3.11 + + The ``deprecating_package`` option was added in Twig 3.11. + +You can also set the ``deprecating_package`` option to specify the package that +is deprecating the filter, and ``deprecated`` can be set to the package version +when the filter was deprecated:: + + $filter = new \Twig\TwigFilter('obsolete', function () { + // ... + }, ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar']); + +When a filter is deprecated, Twig emits a deprecation notice when compiling a +template using it. See :ref:`deprecation-notices` for more information. + +Functions +--------- + +Functions are defined in the exact same way as filters, but you need to create +an instance of ``\Twig\TwigFunction``:: + + $twig = new \Twig\Environment($loader); + $function = new \Twig\TwigFunction('function_name', function () { + // ... + }); + $twig->addFunction($function); + +Functions support the same features as filters, except for the ``pre_escape`` +and ``preserves_safety`` options. + +Tests +----- + +Tests are defined in the exact same way as filters and functions, but you need +to create an instance of ``\Twig\TwigTest``:: + + $twig = new \Twig\Environment($loader); + $test = new \Twig\TwigTest('test_name', function () { + // ... + }); + $twig->addTest($test); + +Tests allow you to create custom application specific logic for evaluating +boolean conditions. As a simple example, let's create a Twig test that checks if +objects are 'red':: + + $twig = new \Twig\Environment($loader); + $test = new \Twig\TwigTest('red', function ($value) { + if (isset($value->color) && $value->color == 'red') { + return true; + } + if (isset($value->paint) && $value->paint == 'red') { + return true; + } + return false; + }); + $twig->addTest($test); + +Test functions must always return ``true``/``false``. + +When creating tests you can use the ``node_class`` option to provide custom test +compilation. This is useful if your test can be compiled into PHP primitives. +This is used by many of the tests built into Twig:: + + namespace App; + + use Twig\Environment; + use Twig\Node\Expression\TestExpression; + use Twig\TwigTest; + + $twig = new Environment($loader); + $test = new TwigTest( + 'odd', + null, + ['node_class' => OddTestExpression::class]); + $twig->addTest($test); + + class OddTestExpression extends TestExpression + { + public function compile(\Twig\Compiler $compiler) + { + $compiler + ->raw('(') + ->subcompile($this->getNode('node')) + ->raw(' % 2 != 0') + ->raw(')') + ; + } + } + +The above example shows how you can create tests that use a node class. The node +class has access to one sub-node called ``node``. This sub-node contains the +value that is being tested. When the ``odd`` filter is used in code such as: + +.. code-block:: twig + + {% if my_value is odd %} + +The ``node`` sub-node will contain an expression of ``my_value``. Node-based +tests also have access to the ``arguments`` node. This node will contain the +various other arguments that have been provided to your test. + +If you want to pass a variable number of positional or named arguments to the +test, set the ``is_variadic`` option to ``true``. Tests support dynamic +names (see dynamic filters for the syntax). + +Tags +---- + +One of the most exciting features of a template engine like Twig is the +possibility to define new **language constructs**. This is also the most complex +feature as you need to understand how Twig's internals work. + +Most of the time though, a tag is not needed: + +* If your tag generates some output, use a **function** instead. + +* If your tag modifies some content and returns it, use a **filter** instead. + + For instance, if you want to create a tag that converts a Markdown formatted + text to HTML, create a ``markdown`` filter instead: + + .. code-block:: twig + + {{ '**markdown** text'|markdown }} + + If you want use this filter on large amounts of text, wrap it with the + :doc:`apply ` tag: + + .. code-block:: twig + + {% apply markdown %} + Title + ===== + + Much better than creating a tag as you can **compose** filters. + {% endapply %} + +* If your tag does not output anything, but only exists because of a side + effect, create a **function** that returns nothing and call it via the + :doc:`do ` tag. + + For instance, if you want to create a tag that logs text, create a ``log`` + function instead and call it via the :doc:`do ` tag: + + .. code-block:: twig + + {% do log('Log some things') %} + +If you still want to create a tag for a new language construct, great! + +Let's create a ``set`` tag that allows the definition of simple variables from +within a template. The tag can be used like follows: + +.. code-block:: twig + + {% set name = "value" %} + + {{ name }} + + {# should output value #} + +.. note:: + + The ``set`` tag is part of the Core extension and as such is always + available. The built-in version is slightly more powerful and supports + multiple assignments by default. + +Three steps are needed to define a new tag: + +* Defining a Token Parser class (responsible for parsing the template code); + +* Defining a Node class (responsible for converting the parsed code to PHP); + +* Registering the tag. + +Registering a new tag +~~~~~~~~~~~~~~~~~~~~~ + +Add a tag by calling the ``addTokenParser`` method on the ``\Twig\Environment`` +instance:: + + $twig = new \Twig\Environment($loader); + $twig->addTokenParser(new CustomSetTokenParser()); + +Defining a Token Parser +~~~~~~~~~~~~~~~~~~~~~~~ + +Now, let's see the actual code of this class:: + + class CustomSetTokenParser extends \Twig\TokenParser\AbstractTokenParser + { + public function parse(\Twig\Token $token) + { + $parser = $this->parser; + $stream = $parser->getStream(); + + $name = $stream->expect(\Twig\Token::NAME_TYPE)->getValue(); + $stream->expect(\Twig\Token::OPERATOR_TYPE, '='); + $value = $parser->getExpressionParser()->parseExpression(); + $stream->expect(\Twig\Token::BLOCK_END_TYPE); + + return new CustomSetNode($name, $value, $token->getLine()); + } + + public function getTag() + { + return 'set'; + } + } + +The ``getTag()`` method must return the tag we want to parse, here ``set``. + +The ``parse()`` method is invoked whenever the parser encounters a ``set`` +tag. It should return a ``\Twig\Node\Node`` instance that represents the node (the +``CustomSetNode`` calls creating is explained in the next section). + +The parsing process is simplified thanks to a bunch of methods you can call +from the token stream (``$this->parser->getStream()``): + +* ``getCurrent()``: Gets the current token in the stream. + +* ``next()``: Moves to the next token in the stream, *but returns the old one*. + +* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether + the current token is of a particular type or value (or both). The value may be an + array of several possible values. + +* ``expect($type[, $value[, $message]])``: If the current token isn't of the given + type/value a syntax error is thrown. Otherwise, if the type and value are correct, + the token is returned and the stream moves to the next token. + +* ``look()``: Looks at the next token without consuming it. + +Parsing expressions is done by calling the ``parseExpression()`` like we did for +the ``set`` tag. + +.. tip:: + + Reading the existing ``TokenParser`` classes is the best way to learn all + the nitty-gritty details of the parsing process. + +Defining a Node +~~~~~~~~~~~~~~~ + +The ``CustomSetNode`` class itself is quite short:: + + class CustomSetNode extends \Twig\Node\Node + { + public function __construct($name, \Twig\Node\Expression\AbstractExpression $value, $line) + { + parent::__construct(['value' => $value], ['name' => $name], $line); + } + + public function compile(\Twig\Compiler $compiler) + { + $compiler + ->addDebugInfo($this) + ->write('$context[\''.$this->getAttribute('name').'\'] = ') + ->subcompile($this->getNode('value')) + ->raw(";\n") + ; + } + } + +The compiler implements a fluid interface and provides methods that help the +developer generate beautiful and readable PHP code: + +* ``subcompile()``: Compiles a node. + +* ``raw()``: Writes the given string as is. + +* ``write()``: Writes the given string by adding indentation at the beginning + of each line. + +* ``string()``: Writes a quoted string. + +* ``repr()``: Writes a PHP representation of a given value (see + ``\Twig\Node\ForNode`` for a usage example). + +* ``addDebugInfo()``: Adds the line of the original template file related to + the current node as a comment. + +* ``indent()``: Indents the generated code (see ``\Twig\Node\BlockNode`` for a + usage example). + +* ``outdent()``: Outdents the generated code (see ``\Twig\Node\BlockNode`` for a + usage example). + +.. _creating_extensions: + +Creating an Extension +--------------------- + +The main motivation for writing an extension is to move often used code into a +reusable class like adding support for internationalization. An extension can +define tags, filters, tests, operators, functions, and node visitors. + +Most of the time, it is useful to create a single extension for your project, +to host all the specific tags and filters you want to add to Twig. + +.. tip:: + + When packaging your code into an extension, Twig is smart enough to + recompile your templates whenever you make a change to it (when + ``auto_reload`` is enabled). + +An extension is a class that implements the following interface:: + + interface \Twig\Extension\ExtensionInterface + { + /** + * Returns the token parser instances to add to the existing list. + * + * @return \Twig\TokenParser\TokenParserInterface[] + */ + public function getTokenParsers(); + + /** + * Returns the node visitor instances to add to the existing list. + * + * @return \Twig\NodeVisitor\NodeVisitorInterface[] + */ + public function getNodeVisitors(); + + /** + * Returns a list of filters to add to the existing list. + * + * @return \Twig\TwigFilter[] + */ + public function getFilters(); + + /** + * Returns a list of tests to add to the existing list. + * + * @return \Twig\TwigTest[] + */ + public function getTests(); + + /** + * Returns a list of functions to add to the existing list. + * + * @return \Twig\TwigFunction[] + */ + public function getFunctions(); + + /** + * Returns a list of operators to add to the existing list. + * + * @return array First array of unary operators, second array of binary operators + */ + public function getOperators(); + } + +To keep your extension class clean and lean, inherit from the built-in +``\Twig\Extension\AbstractExtension`` class instead of implementing the interface as it provides +empty implementations for all methods:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + } + +This extension does nothing for now. We will customize it in the next sections. + +You can save your extension anywhere on the filesystem, as all extensions must +be registered explicitly to be available in your templates. + +You can register an extension by using the ``addExtension()`` method on your +main ``Environment`` object:: + + $twig = new \Twig\Environment($loader); + $twig->addExtension(new CustomTwigExtension()); + +.. tip:: + + The Twig core extensions are great examples of how extensions work. + +Globals +~~~~~~~ + +Global variables can be registered in an extension via the ``getGlobals()`` +method:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface + { + public function getGlobals(): array + { + return [ + 'text' => new Text(), + ]; + } + + // ... + } + +Functions +~~~~~~~~~ + +Functions can be registered in an extension via the ``getFunctions()`` +method:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + public function getFunctions() + { + return [ + new \Twig\TwigFunction('lipsum', 'generate_lipsum'), + ]; + } + + // ... + } + +Filters +~~~~~~~ + +To add a filter to an extension, you need to override the ``getFilters()`` +method. This method must return an array of filters to add to the Twig +environment:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + public function getFilters() + { + return [ + new \Twig\TwigFilter('rot13', 'str_rot13'), + ]; + } + + // ... + } + +Tags +~~~~ + +Adding a tag in an extension can be done by overriding the +``getTokenParsers()`` method. This method must return an array of tags to add +to the Twig environment:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + public function getTokenParsers() + { + return [new CustomSetTokenParser()]; + } + + // ... + } + +In the above code, we have added a single new tag, defined by the +``CustomSetTokenParser`` class. The ``CustomSetTokenParser`` class is +responsible for parsing the tag and compiling it to PHP. + +Operators +~~~~~~~~~ + +The ``getOperators()`` methods lets you add new operators. Here is how to add +the ``!``, ``||``, and ``&&`` operators:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + public function getOperators() + { + return [ + [ + '!' => ['precedence' => 50, 'class' => \Twig\Node\Expression\Unary\NotUnary::class], + ], + [ + '||' => ['precedence' => 10, 'class' => \Twig\Node\Expression\Binary\OrBinary::class, 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT], + '&&' => ['precedence' => 15, 'class' => \Twig\Node\Expression\Binary\AndBinary::class, 'associativity' => \Twig\ExpressionParser::OPERATOR_LEFT], + ], + ]; + } + + // ... + } + +Tests +~~~~~ + +The ``getTests()`` method lets you add new test functions:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + public function getTests() + { + return [ + new \Twig\TwigTest('even', 'twig_test_even'), + ]; + } + + // ... + } + +Definition vs Runtime +~~~~~~~~~~~~~~~~~~~~~ + +Twig filters, functions, and tests runtime implementations can be defined as +any valid PHP callable: + +* **functions/static methods**: Simple to implement and fast (used by all Twig + core extensions); but it is hard for the runtime to depend on external + objects; + +* **closures**: Simple to implement; + +* **object methods**: More flexible and required if your runtime code depends + on external objects. + +The simplest way to use methods is to define them on the extension itself:: + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + private $rot13Provider; + + public function __construct($rot13Provider) + { + $this->rot13Provider = $rot13Provider; + } + + public function getFunctions() + { + return [ + new \Twig\TwigFunction('rot13', [$this, 'rot13']), + ]; + } + + public function rot13($value) + { + return $this->rot13Provider->rot13($value); + } + } + +This is very convenient but not recommended as it makes template compilation +depend on runtime dependencies even if they are not needed (think for instance +as a dependency that connects to a database engine). + +You can decouple the extension definitions from their runtime implementations by +registering a ``\Twig\RuntimeLoader\RuntimeLoaderInterface`` instance on the +environment that knows how to instantiate such runtime classes (runtime classes +must be autoload-able):: + + class RuntimeLoader implements \Twig\RuntimeLoader\RuntimeLoaderInterface + { + public function load($class) + { + // implement the logic to create an instance of $class + // and inject its dependencies + // most of the time, it means using your dependency injection container + if ('CustomRuntimeExtension' === $class) { + return new $class(new Rot13Provider()); + } else { + // ... + } + } + } + + $twig->addRuntimeLoader(new RuntimeLoader()); + +.. note:: + + Twig comes with a PSR-11 compatible runtime loader + (``\Twig\RuntimeLoader\ContainerRuntimeLoader``). + +It is now possible to move the runtime logic to a new +``CustomRuntimeExtension`` class and use it directly in the extension:: + + class CustomRuntimeExtension + { + private $rot13Provider; + + public function __construct($rot13Provider) + { + $this->rot13Provider = $rot13Provider; + } + + public function rot13($value) + { + return $this->rot13Provider->rot13($value); + } + } + + class CustomTwigExtension extends \Twig\Extension\AbstractExtension + { + public function getFunctions() + { + return [ + new \Twig\TwigFunction('rot13', ['CustomRuntimeExtension', 'rot13']), + // or + new \Twig\TwigFunction('rot13', 'CustomRuntimeExtension::rot13'), + ]; + } + } + +Testing an Extension +-------------------- + +Functional Tests +~~~~~~~~~~~~~~~~ + +You can create functional tests for extensions by creating the following file +structure in your test directory:: + + Fixtures/ + filters/ + foo.test + bar.test + functions/ + foo.test + bar.test + tags/ + foo.test + bar.test + IntegrationTest.php + +The ``IntegrationTest.php`` file should look like this:: + + namespace Project\Tests; + + use Twig\Test\IntegrationTestCase; + + class IntegrationTest extends IntegrationTestCase + { + public function getExtensions() + { + return [ + new CustomTwigExtension1(), + new CustomTwigExtension2(), + ]; + } + + public function getFixturesDir() + { + return __DIR__.'/Fixtures/'; + } + } + +Fixtures examples can be found within the Twig repository +`tests/Twig/Fixtures`_ directory. + +Node Tests +~~~~~~~~~~ + +Testing the node visitors can be complex, so extend your test cases from +``\Twig\Test\NodeTestCase``. Examples can be found in the Twig repository +`tests/Twig/Node`_ directory. + +.. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/3.x/tests/Fixtures +.. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/3.x/tests/Node diff --git a/upload/system/storage/vendor/twig/twig/doc/api.rst b/upload/system/storage/vendor/twig/twig/doc/api.rst new file mode 100644 index 000000000..09c553175 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/api.rst @@ -0,0 +1,585 @@ +Twig for Developers +=================== + +This chapter describes the API to Twig and not the template language. It will +be most useful as reference to those implementing the template interface to +the application and not those who are creating Twig templates. + +Basics +------ + +Twig uses a central object called the **environment** (of class +``\Twig\Environment``). Instances of this class are used to store the +configuration and extensions, and are used to load templates. + +Most applications create one ``\Twig\Environment`` object on application +initialization and use that to load templates. In some cases, it might be useful +to have multiple environments side by side, with different configurations. + +The typical way to configure Twig to load templates for an application looks +roughly like this:: + + require_once '/path/to/vendor/autoload.php'; + + $loader = new \Twig\Loader\FilesystemLoader('/path/to/templates'); + $twig = new \Twig\Environment($loader, [ + 'cache' => '/path/to/compilation_cache', + ]); + +This creates a template environment with a default configuration and a loader +that looks up templates in the ``/path/to/templates/`` directory. Different +loaders are available and you can also write your own if you want to load +templates from a database or other resources. + +.. note:: + + Notice that the second argument of the environment is an array of options. + The ``cache`` option is a compilation cache directory, where Twig caches + the compiled templates to avoid the parsing phase for sub-sequent + requests. It is very different from the cache you might want to add for + the evaluated templates. For such a need, you can use any available PHP + cache library. + +Rendering Templates +------------------- + +To load a template from a Twig environment, call the ``load()`` method which +returns a ``\Twig\TemplateWrapper`` instance:: + + $template = $twig->load('index.html'); + +To render the template with some variables, call the ``render()`` method:: + + echo $template->render(['the' => 'variables', 'go' => 'here']); + +.. note:: + + The ``display()`` method is a shortcut to output the rendered template. + +You can also load and render the template in one fell swoop:: + + echo $twig->render('index.html', ['the' => 'variables', 'go' => 'here']); + +If a template defines blocks, they can be rendered individually via the +``renderBlock()`` call:: + + echo $template->renderBlock('block_name', ['the' => 'variables', 'go' => 'here']); + +.. _environment_options: + +Environment Options +------------------- + +When creating a new ``\Twig\Environment`` instance, you can pass an array of +options as the constructor second argument:: + + $twig = new \Twig\Environment($loader, ['debug' => true]); + +The following options are available: + +* ``debug`` *boolean* + + When set to ``true``, the generated templates have a + ``__toString()`` method that you can use to display the generated nodes + (default to ``false``). + +* ``charset`` *string* (defaults to ``utf-8``) + + The charset used by the templates. + +* ``cache`` *string* or ``false`` + + An absolute path where to store the compiled templates, or + ``false`` to disable caching (which is the default). + +* ``auto_reload`` *boolean* + + When developing with Twig, it's useful to recompile the + template whenever the source code changes. If you don't provide a value for + the ``auto_reload`` option, it will be determined automatically based on the + ``debug`` value. + +.. _environment_options_strict_variables: + +* ``strict_variables`` *boolean* + + If set to ``false``, Twig will silently ignore invalid + variables (variables and or attributes/methods that do not exist) and + replace them with a ``null`` value. When set to ``true``, Twig throws an + exception instead (default to ``false``). + +* ``autoescape`` *string* + + Sets the default auto-escaping strategy (``name``, ``html``, ``js``, ``css``, + ``url``, ``html_attr``, or a PHP callback that takes the template "filename" + and returns the escaping strategy to use -- the callback cannot be a function + name to avoid collision with built-in escaping strategies); set it to + ``false`` to disable auto-escaping. The ``name`` escaping strategy determines + the escaping strategy to use for a template based on the template filename + extension (this strategy does not incur any overhead at runtime as + auto-escaping is done at compilation time.) + +* ``optimizations`` *integer* + + A flag that indicates which optimizations to apply + (default to ``-1`` -- all optimizations are enabled; set it to ``0`` to + disable). + +Loaders +------- + +Loaders are responsible for loading templates from a resource such as the file +system. + +Compilation Cache +~~~~~~~~~~~~~~~~~ + +All template loaders can cache the compiled templates on the filesystem for +future reuse. It speeds up Twig a lot as templates are only compiled once. + +Built-in Loaders +~~~~~~~~~~~~~~~~ + +Here is a list of the built-in loaders: + +``\Twig\Loader\FilesystemLoader`` +................................. + +``\Twig\Loader\FilesystemLoader`` loads templates from the file system. This loader +can find templates in folders on the file system and is the preferred way to +load them:: + + $loader = new \Twig\Loader\FilesystemLoader($templateDir); + +It can also look for templates in an array of directories:: + + $loader = new \Twig\Loader\FilesystemLoader([$templateDir1, $templateDir2]); + +With such a configuration, Twig will first look for templates in +``$templateDir1`` and if they do not exist, it will fallback to look for them +in the ``$templateDir2``. + +You can add or prepend paths via the ``addPath()`` and ``prependPath()`` +methods:: + + $loader->addPath($templateDir3); + $loader->prependPath($templateDir4); + +The filesystem loader also supports namespaced templates. This allows to group +your templates under different namespaces which have their own template paths. + +When using the ``setPaths()``, ``addPath()``, and ``prependPath()`` methods, +specify the namespace as the second argument (when not specified, these +methods act on the "main" namespace):: + + $loader->addPath($templateDir, 'admin'); + +Namespaced templates can be accessed via the special +``@namespace_name/template_path`` notation:: + + $twig->render('@admin/index.html', []); + +``\Twig\Loader\FilesystemLoader`` supports absolute and relative paths. Using relative +paths is preferred as it makes the cache keys independent of the project root +directory (for instance, it allows warming the cache from a build server where +the directory might be different from the one used on production servers):: + + $loader = new \Twig\Loader\FilesystemLoader('templates', getcwd().'/..'); + +.. note:: + + When not passing the root path as a second argument, Twig uses ``getcwd()`` + for relative paths. + +``\Twig\Loader\ArrayLoader`` +............................ + +``\Twig\Loader\ArrayLoader`` loads a template from a PHP array. It is passed an +array of strings bound to template names:: + + $loader = new \Twig\Loader\ArrayLoader([ + 'index.html' => 'Hello {{ name }}!', + ]); + $twig = new \Twig\Environment($loader); + + echo $twig->render('index.html', ['name' => 'Fabien']); + +This loader is very useful for unit testing. It can also be used for small +projects where storing all templates in a single PHP file might make sense. + +.. tip:: + + When using the ``Array`` 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. + +``\Twig\Loader\ChainLoader`` +............................ + +``\Twig\Loader\ChainLoader`` delegates the loading of templates to other loaders:: + + $loader1 = new \Twig\Loader\ArrayLoader([ + 'base.html' => '{% block content %}{% endblock %}', + ]); + $loader2 = new \Twig\Loader\ArrayLoader([ + 'index.html' => '{% extends "base.html" %}{% block content %}Hello {{ name }}{% endblock %}', + 'base.html' => 'Will never be loaded', + ]); + + $loader = new \Twig\Loader\ChainLoader([$loader1, $loader2]); + + $twig = new \Twig\Environment($loader); + +When looking for a template, Twig tries each loader in turn and returns as soon +as the template is found. When rendering the ``index.html`` template from the +above example, Twig will load it with ``$loader2`` but the ``base.html`` +template will be loaded from ``$loader1``. + +.. note:: + + You can also add loaders via the ``addLoader()`` method. + +Create your own Loader +~~~~~~~~~~~~~~~~~~~~~~ + +All loaders implement the ``\Twig\Loader\LoaderInterface``:: + + interface \Twig\Loader\LoaderInterface + { + /** + * Returns the source context for a given template logical name. + * + * @param string $name The template logical name + * + * @return \Twig\Source + * + * @throws \Twig\Error\LoaderError When $name is not found + */ + public function getSourceContext($name); + + /** + * Gets the cache key to use for the cache for a given template name. + * + * @param string $name The name of the template to load + * + * @return string The cache key + * + * @throws \Twig\Error\LoaderError When $name is not found + */ + public function getCacheKey($name); + + /** + * Returns true if the template is still fresh. + * + * @param string $name The template name + * @param timestamp $time The last modification time of the cached template + * + * @return bool true if the template is fresh, false otherwise + * + * @throws \Twig\Error\LoaderError When $name is not found + */ + public function isFresh($name, $time); + + /** + * Check if we have the source code of a template, given its name. + * + * @param string $name The name of the template to check if we can load + * + * @return bool If the template source code is handled by this loader or not + */ + public function exists($name); + } + +The ``isFresh()`` method must return ``true`` if the current cached template +is still fresh, given the last modification time, or ``false`` otherwise. + +The ``getSourceContext()`` method must return an instance of ``\Twig\Source``. + +Using Extensions +---------------- + +Twig extensions are packages that add new features to Twig. Register an +extension via the ``addExtension()`` method:: + + $twig->addExtension(new \Twig\Extension\SandboxExtension()); + +Twig comes bundled with the following extensions: + +* *Twig\Extension\CoreExtension*: Defines all the core features of Twig. + +* *Twig\Extension\DebugExtension*: Defines the ``dump`` function to help debug + template variables. + +* *Twig\Extension\EscaperExtension*: Adds automatic output-escaping and the + possibility to escape/unescape blocks of code. + +* *Twig\Extension\SandboxExtension*: Adds a sandbox mode to the default Twig + environment, making it safe to evaluate untrusted code. + +* *Twig\Extension\ProfilerExtension*: Enables the built-in Twig profiler. + +* *Twig\Extension\OptimizerExtension*: Optimizes the node tree before + compilation. + +* *Twig\Extension\StringLoaderExtension*: Defines the ``template_from_string`` + function to allow loading templates from string in a template. + +The Core, Escaper, and Optimizer extensions are registered by default. + +Built-in Extensions +------------------- + +This section describes the features added by the built-in extensions. + +.. tip:: + + Read the chapter about :doc:`extending Twig ` to learn how to + create your own extensions. + +Core Extension +~~~~~~~~~~~~~~ + +The ``core`` extension defines all the core features of Twig: + +* :doc:`Tags `; +* :doc:`Filters `; +* :doc:`Functions `; +* :doc:`Tests `. + +Escaper Extension +~~~~~~~~~~~~~~~~~ + +The ``escaper`` extension adds automatic output escaping to Twig. It defines a +tag, ``autoescape``, and a filter, ``raw``. + +When creating the escaper extension, you can switch on or off the global +output escaping strategy:: + + $escaper = new \Twig\Extension\EscaperExtension('html'); + $twig->addExtension($escaper); + +If set to ``html``, all variables in templates are escaped (using the ``html`` +escaping strategy), except those using the ``raw`` filter: + +.. code-block:: twig + + {{ article.to_html|raw }} + +You can also change the escaping mode locally by using the ``autoescape`` tag: + +.. code-block:: twig + + {% autoescape 'html' %} + {{ var }} + {{ var|raw }} {# var won't be escaped #} + {{ var|escape }} {# var won't be double-escaped #} + {% endautoescape %} + +.. warning:: + + The ``autoescape`` tag has no effect on included files. + +The escaping rules are implemented as follows: + +* Literals (integers, booleans, arrays, ...) used in the template directly as + variables or filter arguments are never automatically escaped: + + .. code-block:: html+twig + + {{ "Twig
" }} {# won't be escaped #} + + {% set text = "Twig
" %} + {{ text }} {# will be escaped #} + +* Expressions which the result is a literal or a variable marked safe + are never automatically escaped: + + .. code-block:: html+twig + + {{ foo ? "Twig
" : "
Twig" }} {# won't be escaped #} + + {% set text = "Twig
" %} + {{ true ? text : "
Twig" }} {# will be escaped #} + {{ false ? text : "
Twig" }} {# won't be escaped #} + + {% set text = "Twig
" %} + {{ foo ? text|raw : "
Twig" }} {# won't be escaped #} + +* Objects with a ``__toString`` method are converted to strings and + escaped. You can mark some classes and/or interfaces as being safe for some + strategies via ``EscaperExtension::addSafeClass()``: + + .. code-block:: twig + + // mark object of class Foo as safe for the HTML strategy + $escaper->addSafeClass('Foo', ['html']); + + // mark object of interface Foo as safe for the HTML strategy + $escaper->addSafeClass('FooInterface', ['html']); + + // mark object of class Foo as safe for the HTML and JS strategies + $escaper->addSafeClass('Foo', ['html', 'js']); + + // mark object of class Foo as safe for all strategies + $escaper->addSafeClass('Foo', ['all']); + +* Escaping is applied before printing, after any other filter is applied: + + .. code-block:: twig + + {{ var|upper }} {# is equivalent to {{ var|upper|escape }} #} + +* The ``raw`` filter should only be used at the end of the filter chain: + + .. code-block:: twig + + {{ var|raw|upper }} {# will be escaped #} + + {{ var|upper|raw }} {# won't be escaped #} + +* Automatic escaping is not applied if the last filter in the chain is marked + safe for the current context (e.g. ``html`` or ``js``). ``escape`` and + ``escape('html')`` are marked safe for HTML, ``escape('js')`` is marked + safe for JavaScript, ``raw`` is marked safe for everything. + + .. code-block:: twig + + {% autoescape 'js' %} + {{ var|escape('html') }} {# will be escaped for HTML and JavaScript #} + {{ var }} {# will be escaped for JavaScript #} + {{ var|escape('js') }} {# won't be double-escaped #} + {% endautoescape %} + +.. note:: + + Note that autoescaping has some limitations as escaping is applied on + expressions after evaluation. For instance, when working with + concatenation, ``{{ foo|raw ~ bar }}`` won't give the expected result as + escaping is applied on the result of the concatenation, not on the + individual variables (so, the ``raw`` filter won't have any effect here). + +Sandbox Extension +~~~~~~~~~~~~~~~~~ + +The ``sandbox`` extension can be used to evaluate untrusted code. Access to +unsafe attributes and methods is prohibited. The sandbox security is managed +by a policy instance. By default, Twig comes with one policy class: +``\Twig\Sandbox\SecurityPolicy``. This class allows you to white-list some +tags, filters, functions, properties, and methods:: + + $tags = ['if']; + $filters = ['upper']; + $methods = [ + 'Article' => ['getTitle', 'getBody'], + ]; + $properties = [ + 'Article' => ['title', 'body'], + ]; + $functions = ['range']; + $policy = new \Twig\Sandbox\SecurityPolicy($tags, $filters, $methods, $properties, $functions); + +With the previous configuration, the security policy will only allow usage of +the ``if`` tag, and the ``upper`` filter. Moreover, the templates will only be +able to call the ``getTitle()`` and ``getBody()`` methods on ``Article`` +objects, and the ``title`` and ``body`` public properties. Everything else +won't be allowed and will generate a ``\Twig\Sandbox\SecurityError`` exception. + +.. caution:: + + The ``extends`` and ``use`` tags are always allowed in a sandboxed + template. That behavior will change in 4.0 where these tags will need to be + explicitly allowed like any other tag. + +The policy object is the first argument of the sandbox constructor:: + + $sandbox = new \Twig\Extension\SandboxExtension($policy); + $twig->addExtension($sandbox); + +By default, the sandbox mode is disabled and should be enabled when including +untrusted template code by using the ``sandbox`` tag: + +.. code-block:: twig + + {% sandbox %} + {% include 'user.html' %} + {% endsandbox %} + +You can sandbox all templates by passing ``true`` as the second argument of +the extension constructor:: + + $sandbox = new \Twig\Extension\SandboxExtension($policy, true); + +Profiler Extension +~~~~~~~~~~~~~~~~~~ + +The ``profiler`` extension enables a profiler for Twig templates; it should +only be used on your development machines as it adds some overhead:: + + $profile = new \Twig\Profiler\Profile(); + $twig->addExtension(new \Twig\Extension\ProfilerExtension($profile)); + + $dumper = new \Twig\Profiler\Dumper\TextDumper(); + echo $dumper->dump($profile); + +A profile contains information about time and memory consumption for template, +block, and macro executions. + +You can also dump the data in a `Blackfire.io `_ +compatible format:: + + $dumper = new \Twig\Profiler\Dumper\BlackfireDumper(); + file_put_contents('/path/to/profile.prof', $dumper->dump($profile)); + +Upload the profile to visualize it (create a `free account +`_ +first): + +.. code-block:: sh + + blackfire --slot=7 upload /path/to/profile.prof + +Optimizer Extension +~~~~~~~~~~~~~~~~~~~ + +The ``optimizer`` extension optimizes the node tree before compilation:: + + $twig->addExtension(new \Twig\Extension\OptimizerExtension()); + +By default, all optimizations are turned on. You can select the ones you want +to enable by passing them to the constructor:: + + $optimizer = new \Twig\Extension\OptimizerExtension(\Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_FOR); + + $twig->addExtension($optimizer); + +Twig supports the following optimizations: + +* ``\Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_ALL``, enables all optimizations + (this is the default value). + +* ``\Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_NONE``, disables all optimizations. + This reduces the compilation time, but it can increase the execution time + and the consumed memory. + +* ``\Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_FOR``, optimizes the ``for`` tag by + removing the ``loop`` variable creation whenever possible. + +Exceptions +---------- + +Twig can throw exceptions: + +* ``\Twig\Error\Error``: The base exception for all errors. + +* ``\Twig\Error\SyntaxError``: Thrown to tell the user that there is a problem with + the template syntax. + +* ``\Twig\Error\RuntimeError``: Thrown when an error occurs at runtime (when a filter + does not exist for instance). + +* ``\Twig\Error\LoaderError``: Thrown when an error occurs during template loading. + +* ``\Twig\Sandbox\SecurityError``: Thrown when an unallowed tag, filter, or + method is called in a sandboxed template. diff --git a/upload/system/storage/vendor/twig/twig/doc/coding_standards.rst b/upload/system/storage/vendor/twig/twig/doc/coding_standards.rst new file mode 100644 index 000000000..46be8eca6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/coding_standards.rst @@ -0,0 +1,113 @@ +Coding Standards +================ + +.. note:: + + The `Twig CS fixer tool `_ + uses the coding standards described in this document to automatically fix + your templates. + +When writing Twig templates, we recommend you to follow these official coding +standards: + +* Put exactly one space after the start of a delimiter (``{{``, ``{%``, + and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``): + + .. code-block:: twig + + {{ foo }} + {# comment #} + {% if foo %}{% endif %} + + When using the whitespace control character, do not put any spaces between + it and the delimiter: + + .. code-block:: twig + + {{- foo -}} + {#- comment -#} + {%- if foo -%}{%- endif -%} + +* Put exactly one space before and after the following operators: + comparison operators (``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``), math + operators (``+``, ``-``, ``/``, ``*``, ``%``, ``//``, ``**``), logic + operators (``not``, ``and``, ``or``), ``~``, ``is``, ``in``, and the ternary + operator (``?:``): + + .. code-block:: twig + + {{ 1 + 2 }} + {{ foo ~ bar }} + {{ true ? true : false }} + +* Put exactly one space after the ``:`` sign in mappings and ``,`` in sequences + and mappings: + + .. code-block:: twig + + {{ [1, 2, 3] }} + {{ {'foo': 'bar'} }} + +* Do not put any spaces after an opening parenthesis and before a closing + parenthesis in expressions: + + .. code-block:: twig + + {{ 1 + (2 * 3) }} + +* Do not put any spaces before and after string delimiters: + + .. code-block:: twig + + {{ 'foo' }} + {{ "foo" }} + +* Do not put any spaces before and after the following operators: ``|``, + ``.``, ``..``, ``[]``: + + .. code-block:: twig + + {{ foo|upper|lower }} + {{ user.name }} + {{ user[name] }} + {% for i in 1..12 %}{% endfor %} + +* Do not put any spaces before and after the parenthesis used for filter and + function calls: + + .. code-block:: twig + + {{ foo|default('foo') }} + {{ range(1..10) }} + +* Do not put any spaces before and after the opening and the closing of + sequences and mappings: + + .. code-block:: twig + + {{ [1, 2, 3] }} + {{ {'foo': 'bar'} }} + +* Use lower cased and underscored variable names: + + .. code-block:: twig + + {% set foo = 'foo' %} + {% set foo_bar = 'foo' %} + +* Indent your code inside tags (use the same indentation as the one used for + the target language of the rendered template): + + .. code-block:: twig + + {% block foo %} + {% if true %} + true + {% endif %} + {% endblock %} + +* Use ``:`` instead of ``=`` to separate argument names and values: + + .. code-block:: twig + + {{ data|convert_encoding(from: 'iso-2022-jp', to: 'UTF-8') }} diff --git a/upload/system/storage/vendor/twig/twig/doc/deprecated.rst b/upload/system/storage/vendor/twig/twig/doc/deprecated.rst new file mode 100644 index 000000000..affdd2ba9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/deprecated.rst @@ -0,0 +1,202 @@ +Deprecated Features +=================== + +This document lists deprecated features in Twig 3.x. Deprecated features are +kept for backward compatibility and removed in the next major release (a +feature that was deprecated in Twig 3.x is removed in Twig 4.0). + +Functions +--------- + + * The ``twig_test_iterable`` function is deprecated; use the native PHP + ``is_iterable`` function instead. + +Extensions +---------- + +* All functions defined in Twig extensions are marked as internal as of Twig + 3.9.0, and will be removed in Twig 4.0. They have been replaced by internal + methods on their respective extension classes. + + If you were using the ``twig_escape_filter()`` function in your code, use + ``$env->getRuntime(EscaperRuntime::class)->escape()`` instead. + +* The following methods from ``Twig\Extension\EscaperExtension`` are + deprecated: ``setEscaper()``, ``getEscapers()``, ``setSafeClasses``, + ``addSafeClasses()``. Use the same methods on the + ``Twig\Runtime\EscaperRuntime`` class instead: + + Before: + ``$twig->getExtension(EscaperExtension::class)->METHOD();`` + + After: + ``$twig->getRuntime(EscaperRuntime::class)->METHOD();`` + +Nodes +----- + +* The "tag" constructor parameter of the ``Twig\Node\Node`` class is deprecated + as of Twig 3.12 as the tag is now automatically set by the Parser when + needed. + +* Passing a second argument to "ExpressionParser::parseFilterExpressionRaw()" + is deprecated as of Twig 3.12. + +* The following ``Twig\Node\Node`` methods will take a string or an integer + (instead of just a string) in Twig 4.0 for their "name" argument: + ``getNode()``, ``hasNode()``, ``setNode()``, ``removeNode()``, and + ``deprecateNode()``. + +* Not passing a ``BodyNode`` instance as the body of a ``ModuleNode`` or + ``MacroNode`` constructor is deprecated as of Twig 3.12. + +* Returning ``null`` from ``TokenParserInterface::parse()`` is deprecated as of + Twig 3.12 (as forbidden by the interface). + +* The second argument of the + ``Twig\Node\Expression\CallExpression::compileArguments()`` method is + deprecated. + +* The ``Twig\Node\Expression\NameExpression::isSimple()`` and + ``Twig\Node\Expression\NameExpression::isSpecial()`` methods are deprecated as + of Twig 3.11 and will be removed in Twig 4.0. + +* The ``filter`` node of ``Twig\Node\Expression\FilterExpression`` is + deprecated as of Twig 3.12 and will be removed in 4.0. Use the ``filter`` + attribute instead to get the filter: + + Before: + ``$node->getNode('filter')->getAttribute('value')`` + + After: + ``$node->getAttribute('twig_callable')->getName()`` + +* Passing a name to ``Twig\Node\Expression\FunctionExpression``, + ``Twig\Node\Expression\FilterExpression``, and + ``Twig\Node\Expression\TestExpression`` is deprecated as of Twig 3.12. + As of Twig 4.0, you need to pass a ``TwigFunction``, ``TwigFilter``, or + ``TestFilter`` instead. + + Let's take a ``FunctionExpression`` as an example. + + If you have a node that extends ``FunctionExpression`` and if you don't + override the constructor, you don't need to do anything. But if you override + the constructor, then you need to change the type hint of the name and mark + the constructor with the ``Twig\Attribute\FirstClassTwigCallableReady`` attribute. + + Before:: + + class NotReadyFunctionExpression extends FunctionExpression + { + public function __construct(string $function, Node $arguments, int $lineno) + { + parent::__construct($function, $arguments, $lineno); + } + } + + class NotReadyFilterExpression extends FilterExpression + { + public function __construct(Node $node, ConstantExpression $filter, Node $arguments, int $lineno) + { + parent::__construct($node, $filter, $arguments, $lineno); + } + } + + class NotReadyTestExpression extends TestExpression + { + public function __construct(Node $node, string $test, ?Node $arguments, int $lineno) + { + parent::__construct($node, $test, $arguments, $lineno); + } + } + + After:: + + class ReadyFunctionExpression extends FunctionExpression + { + #[FirstClassTwigCallableReady] + public function __construct(TwigFunction|string $function, Node $arguments, int $lineno) + { + parent::__construct($function, $arguments, $lineno); + } + } + + class ReadyFilterExpression extends FilterExpression + { + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigFilter|ConstantExpression $filter, Node $arguments, int $lineno) + { + parent::__construct($node, $filter, $arguments, $lineno); + } + } + + class ReadyTestExpression extends TestExpression + { + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigTest|string $test, ?Node $arguments, int $lineno) + { + parent::__construct($node, $test, $arguments, $lineno); + } + } + +* The following ``Twig\Node\Expression\FunctionExpression`` attributes are + deprecated as of Twig 3.12: ``needs_charset``, ``needs_environment``, + ``needs_context``, ``arguments``, ``callable``, ``is_variadic``, + and ``dynamic_name``. + +* The following ``Twig\Node\Expression\FilterExpression`` attributes are + deprecated as of Twig 3.12: ``needs_charset``, ``needs_environment``, + ``needs_context``, ``arguments``, ``callable``, ``is_variadic``, + and ``dynamic_name``. + +* The following ``Twig\Node\Expression\TestExpression`` attributes are + deprecated as of Twig 3.12: ``arguments``, ``callable``, ``is_variadic``, + and ``dynamic_name``. + +Node Visitors +------------- + +* The ``Twig\NodeVisitor\AbstractNodeVisitor`` class is deprecated, implement the + ``Twig\NodeVisitor\NodeVisitorInterface`` interface instead. + +* The ``Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_RAW_FILTER`` and the + ``Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_TEXT_NODES`` options are + deprecated as of Twig 3.12 and will be removed in Twig 4.0; they don't do + anything anymore. + +Parser +------ + +* The following methods from ``Twig\Parser`` are deprecated as of Twig 3.12: + ``getBlockStack()``, ``hasBlock()``, ``getBlock()``, ``hasMacro()``, + ``hasTraits()``, ``getParent()``. + +* The ``Twig\ExpressionParser::parseHashExpression()`` method is deprecated, use + ``Twig\ExpressionParser::parseMappingExpression()`` instead. + +* The ``Twig\ExpressionParser::parseArrayExpression()`` method is deprecated, use + ``Twig\ExpressionParser::parseSequenceExpression()`` instead. + +* Passing ``null`` to ``Twig\Parser::setParent()`` is deprecated as of Twig + 3.12. + +Templates +--------- + +* Passing ``Twig\Template`` instances to Twig public API is deprecated (like + in ``Environment::resolveTemplate()``, ``Environment::load()``, and + ``Template::loadTemplate()``); pass instances of ``Twig\TemplateWrapper`` + instead. + +Filters +------- + +* The ``spaceless`` filter is deprecated as of Twig 3.12 and will be removed in + Twig 4.0. + +Sandbox +------- + +* Having the ``extends`` and ``use`` tags allowed by default in a sandbox is + deprecated as of Twig 3.12. You will need to explicitly allow them if needed + in 4.0. diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/abs.rst b/upload/system/storage/vendor/twig/twig/doc/filters/abs.rst new file mode 100644 index 000000000..fea117536 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/abs.rst @@ -0,0 +1,18 @@ +``abs`` +======= + +The ``abs`` filter returns the absolute value. + +.. code-block:: twig + + {# number = -5 #} + + {{ number|abs }} + + {# outputs 5 #} + +.. note:: + + Internally, Twig uses the PHP `abs`_ function. + +.. _`abs`: https://www.php.net/abs diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/batch.rst b/upload/system/storage/vendor/twig/twig/doc/filters/batch.rst new file mode 100644 index 000000000..adb2948c6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/batch.rst @@ -0,0 +1,77 @@ +``batch`` +========= + +The ``batch`` filter "batches" items by returning a list of lists with the +given number of items. A second parameter can be provided and used to fill in +missing items: + +.. code-block:: html+twig + + {% set items = ['a', 'b', 'c', 'd'] %} + + + {% for row in items|batch(3, 'No item') %} + + {% for index, column in row %} + + {% endfor %} + + {% endfor %} +
{{ index }} - {{ column }}
+ +The above example will be rendered as: + +.. code-block:: html+twig + + + + + + + + + + + + +
0 - a1 - b2 - c
3 - d4 - No item5 - No item
+ +If you choose to set the third parameter ``preserve_keys`` to ``false``, the keys will be reset in each loop. + +.. code-block:: html+twig + + {% set items = ['a', 'b', 'c', 'd'] %} + + + {% for row in items|batch(3, 'No item', false) %} + + {% for index, column in row %} + + {% endfor %} + + {% endfor %} +
{{ index }} - {{ column }}
+ +The above example will be rendered as: + +.. code-block:: html+twig + + + + + + + + + + + + +
0 - a1 - b2 - c
0 - d1 - No item2 - No item
+ +Arguments +--------- + +* ``size``: The size of the batch; fractional numbers will be rounded up +* ``fill``: Used to fill in missing items +* ``preserve_keys``: Whether to preserve keys or not (defaults to ``true``) diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/capitalize.rst b/upload/system/storage/vendor/twig/twig/doc/filters/capitalize.rst new file mode 100644 index 000000000..2353658bf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/capitalize.rst @@ -0,0 +1,11 @@ +``capitalize`` +============== + +The ``capitalize`` filter capitalizes a value. The first character will be +uppercase, all others lowercase: + +.. code-block:: twig + + {{ 'my first car'|capitalize }} + + {# outputs 'My first car' #} diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/column.rst b/upload/system/storage/vendor/twig/twig/doc/filters/column.rst new file mode 100644 index 000000000..981b8608a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/column.rst @@ -0,0 +1,24 @@ +``column`` +========== + +The ``column`` filter returns the values from a single column in the input +array. + +.. code-block:: twig + + {% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %} + + {% set fruits = items|column('fruit') %} + + {# fruits now contains ['apple', 'orange'] #} + +.. note:: + + Internally, Twig uses the PHP `array_column`_ function. + +Arguments +--------- + +* ``name``: The column name to extract + +.. _`array_column`: https://www.php.net/array_column diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/convert_encoding.rst b/upload/system/storage/vendor/twig/twig/doc/filters/convert_encoding.rst new file mode 100644 index 000000000..98645494e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/convert_encoding.rst @@ -0,0 +1,22 @@ +``convert_encoding`` +==================== + +The ``convert_encoding`` filter converts a string from one encoding to +another. The first argument is the expected output charset and the second one +is the input charset: + +.. code-block:: twig + + {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }} + +.. note:: + + This filter relies on the `iconv`_ extension. + +Arguments +--------- + +* ``to``: The output charset +* ``from``: The input charset + +.. _`iconv`: https://www.php.net/iconv diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/country_name.rst b/upload/system/storage/vendor/twig/twig/doc/filters/country_name.rst new file mode 100644 index 000000000..434b0bda7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/country_name.rst @@ -0,0 +1,44 @@ +``country_name`` +================ + +The ``country_name`` filter returns the country name given its ISO-3166 +two-letter code: + +.. code-block:: twig + + {# France #} + {{ 'FR'|country_name }} + +By default, the filter uses the current locale. You can pass it explicitly: + +.. code-block:: twig + + {# États-Unis #} + {{ 'US'|country_name('fr') }} + +.. note:: + + The ``country_name`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``locale``: The locale diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/currency_name.rst b/upload/system/storage/vendor/twig/twig/doc/filters/currency_name.rst new file mode 100644 index 000000000..a35c49998 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/currency_name.rst @@ -0,0 +1,47 @@ +``currency_name`` +================= + +The ``currency_name`` filter returns the currency name given its three-letter +code: + +.. code-block:: twig + + {# Euro #} + {{ 'EUR'|currency_name }} + + {# Japanese Yen #} + {{ 'JPY'|currency_name }} + +By default, the filter uses the current locale. You can pass it explicitly: + +.. code-block:: twig + + {# yen japonais #} + {{ 'JPY'|currency_name('fr_FR') }} + +.. note:: + + The ``currency_name`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``locale``: The locale diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/currency_symbol.rst b/upload/system/storage/vendor/twig/twig/doc/filters/currency_symbol.rst new file mode 100644 index 000000000..84a048ed5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/currency_symbol.rst @@ -0,0 +1,47 @@ +``currency_symbol`` +=================== + +The ``currency_symbol`` filter returns the currency symbol given its three-letter +code: + +.. code-block:: twig + + {# € #} + {{ 'EUR'|currency_symbol }} + + {# ¥ #} + {{ 'JPY'|currency_symbol }} + +By default, the filter uses the current locale. You can pass it explicitly: + +.. code-block:: twig + + {# ¥ #} + {{ 'JPY'|currency_symbol('fr') }} + +.. note:: + + The ``currency_symbol`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``locale``: The locale diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/data_uri.rst b/upload/system/storage/vendor/twig/twig/doc/filters/data_uri.rst new file mode 100644 index 000000000..a68deb0f2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/data_uri.rst @@ -0,0 +1,55 @@ +``data_uri`` +============ + +The ``data_uri`` filter generates a URL using the data scheme as defined in +`RFC 2397`_: + +.. code-block:: html+twig + + {{ image_data|data_uri }} + + {{ source('path_to_image')|data_uri }} + + {# force the mime type, disable the guessing of the mime type #} + {{ image_data|data_uri(mime: "image/svg") }} + + {# also works with plain text #} + {{ 'foobar'|data_uri(mime: "text/html") }} + + {# add some extra parameters #} + {{ 'foobar'|data_uri(mime: "text/html", parameters: {charset: "ascii"}) }} + +.. note:: + + The ``data_uri`` filter is part of the ``HtmlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/html-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Html\HtmlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new HtmlExtension()); + +.. note:: + + The filter does not perform any length validation on purpose (limit depends + on the usage context), validation should be done before calling this filter. + +Arguments +--------- + +* ``mime``: The mime type +* ``parameters``: A mapping of parameters + +.. _RFC 2397: https://tools.ietf.org/html/rfc2397 diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/date.rst b/upload/system/storage/vendor/twig/twig/doc/filters/date.rst new file mode 100644 index 000000000..7ac9b8750 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/date.rst @@ -0,0 +1,79 @@ +``date`` +======== + +The ``date`` filter formats a date to a given format: + +.. code-block:: twig + + {{ post.published_at|date("m/d/Y") }} + +The format specifier is the same as supported by `date`_, +except when the filtered data is of type `DateInterval`_, when the format must conform to +`DateInterval::format`_ instead. + +The ``date`` filter accepts strings (it must be in a format supported by the +`strtotime`_ function), `DateTime`_ instances, or `DateInterval`_ instances. For +instance, to display the current date, filter the word "now": + +.. code-block:: twig + + {{ "now"|date("m/d/Y") }} + +To escape words and characters in the date format use ``\\`` in front of each +character: + +.. code-block:: twig + + {{ post.published_at|date("F jS \\a\\t g:ia") }} + +If the value passed to the ``date`` filter is ``null``, it will return the +current date by default. If an empty string is desired instead of the current +date, use a ternary operator: + +.. code-block:: twig + + {{ post.published_at is empty ? "" : post.published_at|date("m/d/Y") }} + +If no format is provided, Twig will use the default one: ``F j, Y H:i``. This +default can be changed by calling the ``setDateFormat()`` method on the +``core`` extension instance. The first argument is the default format for +dates and the second one is the default format for date intervals:: + + $twig = new \Twig\Environment($loader); + $twig->getExtension(\Twig\Extension\CoreExtension::class)->setDateFormat('d/m/Y', '%d days'); + +Timezone +-------- + +By default, the date is displayed by applying the default timezone (the one +specified in php.ini or declared in Twig -- see below), but you can override +it by explicitly specifying a supported `timezone`_: + +.. code-block:: twig + + {{ post.published_at|date("m/d/Y", "Europe/Paris") }} + +If the date is already a DateTime object, and if you want to keep its current +timezone, pass ``false`` as the timezone value: + +.. code-block:: twig + + {{ post.published_at|date("m/d/Y", false) }} + +The default timezone can also be set globally by calling ``setTimezone()``:: + + $twig = new \Twig\Environment($loader); + $twig->getExtension(\Twig\Extension\CoreExtension::class)->setTimezone('Europe/Paris'); + +Arguments +--------- + +* ``format``: The date format (default format is ``F j, Y H:i``, which will render as ``January 11, 2024 15:17``) +* ``timezone``: The date timezone + +.. _`strtotime`: https://www.php.net/strtotime +.. _`DateTime`: https://www.php.net/DateTime +.. _`DateInterval`: https://www.php.net/DateInterval +.. _`date`: https://www.php.net/date +.. _`DateInterval::format`: https://www.php.net/DateInterval.format +.. _`timezone`: https://www.php.net/manual/en/timezones.php diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/date_modify.rst b/upload/system/storage/vendor/twig/twig/doc/filters/date_modify.rst new file mode 100644 index 000000000..e091391d1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/date_modify.rst @@ -0,0 +1,20 @@ +``date_modify`` +=============== + +The ``date_modify`` filter modifies a date with a given modifier string: + +.. code-block:: twig + + {{ post.published_at|date_modify("+1 day")|date("m/d/Y") }} + +The ``date_modify`` filter accepts strings (it must be in a format supported +by the `strtotime`_ function) or `DateTime`_ instances. You can combine +it with the :doc:`date` filter for formatting. + +Arguments +--------- + +* ``modifier``: The modifier + +.. _`strtotime`: https://www.php.net/strtotime +.. _`DateTime`: https://www.php.net/DateTime diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/default.rst b/upload/system/storage/vendor/twig/twig/doc/filters/default.rst new file mode 100644 index 000000000..2376fe7a6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/default.rst @@ -0,0 +1,42 @@ +``default`` +=========== + +The ``default`` filter returns the passed default value if the value is +undefined or empty, otherwise the value of the variable: + +.. code-block:: twig + + {{ var|default('var is not defined') }} + + {{ var.foo|default('foo item on var is not defined') }} + + {{ var['foo']|default('foo item on var is not defined') }} + + {{ ''|default('passed var is empty') }} + +When using the ``default`` filter on an expression that uses variables in some +method calls, be sure to use the ``default`` filter whenever a variable can be +undefined: + +.. code-block:: twig + + {{ var.method(foo|default('foo'))|default('foo') }} + +Using the ``default`` filter on a boolean variable might trigger unexpected behavior, as +``false`` is treated as an empty value. Consider using ``??`` instead: + +.. code-block:: twig + + {% set foo = false %} + {{ foo|default(true) }} {# true #} + {{ foo ?? true }} {# false #} + +.. note:: + + Read the documentation for the :doc:`defined<../tests/defined>` and + :doc:`empty<../tests/empty>` tests to learn more about their semantics. + +Arguments +--------- + +* ``default``: The default value diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/escape.rst b/upload/system/storage/vendor/twig/twig/doc/filters/escape.rst new file mode 100644 index 000000000..70d18eb57 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/escape.rst @@ -0,0 +1,136 @@ +``escape`` +========== + +The ``escape`` filter escapes a string using strategies that depend on the +context. + +By default, it uses the HTML escaping strategy: + +.. code-block:: html+twig + +

+ {{ user.username|escape }} +

+ +For convenience, the ``e`` filter is defined as an alias: + +.. code-block:: html+twig + +

+ {{ user.username|e }} +

+ +The ``escape`` filter can also be used in other contexts than HTML thanks to +an optional argument which defines the escaping strategy to use: + +.. code-block:: twig + + {{ user.username|e }} + {# is equivalent to #} + {{ user.username|e('html') }} + +And here is how to escape variables included in JavaScript code: + +.. code-block:: twig + + {{ user.username|escape('js') }} + {{ user.username|e('js') }} + +The ``escape`` filter supports the following escaping strategies for HTML +documents: + +* ``html``: escapes a string for the **HTML body** context. + +* ``js``: escapes a string for the **JavaScript** context. + +* ``css``: escapes a string for the **CSS** context. CSS escaping can be + applied to any string being inserted into CSS and escapes everything except + alphanumerics. + +* ``url``: escapes a string for the **URI or parameter** contexts. This should + not be used to escape an entire URI; only a subcomponent being inserted. + +* ``html_attr``: escapes a string for the **HTML attribute** context. + +Note that doing contextual escaping in HTML documents is hard and choosing the +right escaping strategy depends on a lot of factors. Please, read related +documentation like `the OWASP prevention cheat sheet +`_ +to learn more about this topic. + +.. note:: + + Internally, ``escape`` uses the PHP native `htmlspecialchars`_ function + for the HTML escaping strategy. + +.. caution:: + + When using automatic escaping, Twig tries to not double-escape a variable + when the automatic escaping strategy is the same as the one applied by the + escape filter; but that does not work when using a variable as the + escaping strategy: + + .. code-block:: twig + + {% set strategy = 'html' %} + + {% autoescape 'html' %} + {{ var|escape('html') }} {# won't be double-escaped #} + {{ var|escape(strategy) }} {# will be double-escaped #} + {% endautoescape %} + + When using a variable as the escaping strategy, you should disable + automatic escaping: + + .. code-block:: twig + + {% set strategy = 'html' %} + + {% autoescape 'html' %} + {{ var|escape(strategy)|raw }} {# won't be double-escaped #} + {% endautoescape %} + +Custom Escapers +--------------- + +.. versionadded:: 3.10 + + The ``EscaperRuntime`` class has been added in 3.10. On previous versions, + you can define custom escapers by calling the ``setEscaper()`` method on + the escaper extension instance. The first argument is the escaper strategy + (to be used in the ``escape`` call) and the second one must be a valid PHP + callable:: + + use Twig\Extension\EscaperExtension; + + $twig = new \Twig\Environment($loader); + $twig->getExtension(EscaperExtension::class)->setEscaper('csv', 'csv_escaper'); + + When called by Twig, the callable receives the Twig environment instance, + the string to escape, and the charset. + +You can define custom escapers by calling the ``setEscaper()`` method on the +escaper runtime instance. It accepts two arguments: the strategy name and a PHP +callable that accepts a string to escape and the charset:: + + use Twig\Runtime\EscaperRuntime; + + $twig = new \Twig\Environment($loader); + $escaper = fn ($string, $charset) => $string; + $twig->getRuntime(EscaperRuntime::class)->setEscaper('identity', $escaper); + + # Usage in a template: + # {{ 'foo'|escape('identity') }} + +.. note:: + + Built-in escapers cannot be overridden mainly because they should be + considered as the final implementation and also for better performance. + +Arguments +--------- + +* ``strategy``: The escaping strategy +* ``charset``: The string charset + +.. _`htmlspecialchars`: https://www.php.net/htmlspecialchars diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/filter.rst b/upload/system/storage/vendor/twig/twig/doc/filters/filter.rst new file mode 100644 index 000000000..257421f66 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/filter.rst @@ -0,0 +1,55 @@ +``filter`` +========== + +The ``filter`` filter filters elements of a sequence or a mapping using an arrow +function. The arrow function receives the value of the sequence or mapping: + +.. code-block:: twig + + {% set sizes = [34, 36, 38, 40, 42] %} + + {{ sizes|filter(v => v > 38)|join(', ') }} + {# output 40, 42 #} + +Combined with the ``for`` tag, it allows to filter the items to iterate over: + +.. code-block:: twig + + {% for v in sizes|filter(v => v > 38) -%} + {{ v }} + {% endfor %} + {# output 40 42 #} + +It also works with mappings: + +.. code-block:: twig + + {% set sizes = { + xs: 34, + s: 36, + m: 38, + l: 40, + xl: 42, + } %} + + {% for k, v in sizes|filter(v => v > 38) -%} + {{ k }} = {{ v }} + {% endfor %} + {# output l = 40 xl = 42 #} + +The arrow function also receives the key as a second argument: + +.. code-block:: twig + + {% for k, v in sizes|filter((v, k) => v > 38 and k != "xl") -%} + {{ k }} = {{ v }} + {% endfor %} + {# output l = 40 #} + +Note that the arrow function has access to the current context. + +Arguments +--------- + +* ``array``: The sequence or mapping +* ``arrow``: The arrow function diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/find.rst b/upload/system/storage/vendor/twig/twig/doc/filters/find.rst new file mode 100644 index 000000000..f11b68e36 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/find.rst @@ -0,0 +1,57 @@ +``find`` +======== + +.. versionadded:: 3.11 + + The ``find`` filter was added in Twig 3.11. + +The ``find`` filter returns the first element of a sequence matching an arrow +function. The arrow function receives the value of the sequence: + +.. code-block:: twig + + {% set sizes = [34, 36, 38, 40, 42] %} + + {{ sizes|find(v => v > 38) }} + {# output 40 #} + +It also works with mappings: + +.. code-block:: twig + + {% set sizes = { + xxs: 32, + xs: 34, + s: 36, + m: 38, + l: 40, + xl: 42, + } %} + + {{ sizes|find(v => v > 38) }} + + {# output 40 #} + +The arrow function also receives the key as a second argument: + +.. code-block:: twig + + {{ sizes|find((v, k) => 's' not in k) }} + + {# output 38 #} + +Note that the arrow function has access to the current context: + +.. code-block:: twig + + {% set my_size = 39 %} + + {{ sizes|find(v => v >= my_size) }} + + {# output 40 #} + +Arguments +--------- + +* ``array``: The sequence or mapping +* ``arrow``: The arrow function diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/first.rst b/upload/system/storage/vendor/twig/twig/doc/filters/first.rst new file mode 100644 index 000000000..0d9ba9c49 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/first.rst @@ -0,0 +1,22 @@ +``first`` +========= + +The ``first`` filter returns the first "element" of a sequence, a mapping, or +a string: + +.. code-block:: twig + + {{ [1, 2, 3, 4]|first }} + {# outputs 1 #} + + {{ {a: 1, b: 2, c: 3, d: 4}|first }} + {# outputs 1 #} + + {{ '1234'|first }} + {# outputs 1 #} + +.. note:: + + It also works with objects implementing the `Traversable`_ interface. + +.. _`Traversable`: https://www.php.net/manual/en/class.traversable.php diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/format.rst b/upload/system/storage/vendor/twig/twig/doc/filters/format.rst new file mode 100644 index 000000000..68551a3dd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/format.rst @@ -0,0 +1,18 @@ +``format`` +========== + +The ``format`` filter formats a given string by replacing the placeholders +(placeholders follows the `sprintf`_ notation): + +.. code-block:: twig + + {{ "I like %s and %s."|format(foo, "bar") }} + + {# outputs I like foo and bar + if the foo parameter equals to the foo string. #} + +.. seealso:: + + :doc:`replace` + +.. _`sprintf`: https://www.php.net/sprintf diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/format_currency.rst b/upload/system/storage/vendor/twig/twig/doc/filters/format_currency.rst new file mode 100644 index 000000000..c4c364a00 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/format_currency.rst @@ -0,0 +1,77 @@ +``format_currency`` +=================== + +The ``format_currency`` filter formats a number as a currency: + +.. code-block:: twig + + {# €1,000,000.00 #} + {{ '1000000'|format_currency('EUR') }} + +You can pass attributes to tweak the output: + +.. code-block:: twig + + {# €12.34 #} + {{ '12.345'|format_currency('EUR', {rounding_mode: 'floor'}) }} + + {# €1,000,000.0000 #} + {{ '1000000'|format_currency('EUR', {fraction_digit: 4}) }} + +The list of supported options: + +* ``grouping_used``; +* ``decimal_always_shown``; +* ``max_integer_digit``; +* ``min_integer_digit``; +* ``integer_digit``; +* ``max_fraction_digit``; +* ``min_fraction_digit``; +* ``fraction_digit``; +* ``multiplier``; +* ``grouping_size``; +* ``rounding_mode``; +* ``rounding_increment``; +* ``format_width``; +* ``padding_position``; +* ``secondary_grouping_size``; +* ``significant_digits_used``; +* ``min_significant_digits_used``; +* ``max_significant_digits_used``; +* ``lenient_parse``. + +By default, the filter uses the current locale. You can pass it explicitly: + +.. code-block:: twig + + {# 1.000.000,00 € #} + {{ '1000000'|format_currency('EUR', locale: 'de') }} + +.. note:: + + The ``format_currency`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``currency``: The currency +* ``attrs``: A map of attributes +* ``locale``: The locale diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/format_date.rst b/upload/system/storage/vendor/twig/twig/doc/filters/format_date.rst new file mode 100644 index 000000000..cd6beba9f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/format_date.rst @@ -0,0 +1,36 @@ +``format_date`` +=============== + +The ``format_date`` filter formats a date. It behaves in the exact same way as +the :doc:`format_datetime` filter, but without the time. + +.. note:: + + The ``format_date`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``locale``: The locale +* ``dateFormat``: The date format +* ``pattern``: A date time pattern +* ``timezone``: The date timezone +* ``calendar``: The calendar ("gregorian" by default) diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/format_datetime.rst b/upload/system/storage/vendor/twig/twig/doc/filters/format_datetime.rst new file mode 100644 index 000000000..a47d49731 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/format_datetime.rst @@ -0,0 +1,108 @@ +``format_datetime`` +=================== + +The ``format_datetime`` filter formats a date time: + +.. code-block:: twig + + {# Aug 7, 2019, 11:39:12 PM #} + {{ '2019-08-07 23:39:12'|format_datetime() }} + +Format +------ + +You can tweak the output for the date part and the time part: + +.. code-block:: twig + + {# 23:39 #} + {{ '2019-08-07 23:39:12'|format_datetime('none', 'short', locale: 'fr') }} + + {# 07/08/2019 #} + {{ '2019-08-07 23:39:12'|format_datetime('short', 'none', locale: 'fr') }} + + {# mercredi 7 août 2019 23:39:12 UTC #} + {{ '2019-08-07 23:39:12'|format_datetime('full', 'full', locale: 'fr') }} + +Supported values are: ``none``, ``short``, ``medium``, ``long``, and ``full``. + +.. versionadded:: 3.6 + + ``relative_short``, ``relative_medium``, ``relative_long``, and ``relative_full`` are also supported when running on + PHP 8.0 and superior or when using a polyfill that define the ``IntlDateFormatter::RELATIVE_*`` constants and + associated behavior. + +For greater flexibility, you can even define your own pattern +(see the `ICU user guide`_ for supported patterns). + +.. code-block:: twig + + {# 11 oclock PM, GMT #} + {{ '2019-08-07 23:39:12'|format_datetime(pattern: "hh 'oclock' a, zzzz") }} + +Locale +------ + +By default, the filter uses the current locale. You can pass it explicitly: + +.. code-block:: twig + + {# 7 août 2019 23:39:12 #} + {{ '2019-08-07 23:39:12'|format_datetime(locale: 'fr') }} + +Timezone +-------- + +By default, the date is displayed by applying the default timezone (the one +specified in php.ini or declared in Twig -- see below), but you can override +it by explicitly specifying a timezone: + +.. code-block:: twig + + {{ datetime|format_datetime(locale: 'en', timezone: 'Pacific/Midway') }} + +If the date is already a DateTime object, and if you want to keep its current +timezone, pass ``false`` as the timezone value: + +.. code-block:: twig + + {{ datetime|format_datetime(locale: 'en', timezone: false) }} + +The default timezone can also be set globally by calling ``setTimezone()``:: + + $twig = new \Twig\Environment($loader); + $twig->getExtension(\Twig\Extension\CoreExtension::class)->setTimezone('Europe/Paris'); + +.. note:: + + The ``format_datetime`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``locale``: The locale +* ``dateFormat``: The date format +* ``timeFormat``: The time format +* ``pattern``: A date time pattern +* ``timezone``: The date timezone name +* ``calendar``: The calendar ("gregorian" by default) + +.. _ICU user guide: https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/format_number.rst b/upload/system/storage/vendor/twig/twig/doc/filters/format_number.rst new file mode 100644 index 000000000..900fa5ea9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/format_number.rst @@ -0,0 +1,117 @@ +``format_number`` +================= + +The ``format_number`` filter formats a number: + +.. code-block:: twig + + {{ '12.345'|format_number }} + +You can pass attributes to tweak the output: + +.. code-block:: twig + + {# 12.34 #} + {{ '12.345'|format_number({rounding_mode: 'floor'}) }} + + {# 1000000.0000 #} + {{ '1000000'|format_number({fraction_digit: 4}) }} + +The list of supported options: + +* ``grouping_used``; +* ``decimal_always_shown``; +* ``max_integer_digit``; +* ``min_integer_digit``; +* ``integer_digit``; +* ``max_fraction_digit``; +* ``min_fraction_digit``; +* ``fraction_digit``; +* ``multiplier``; +* ``grouping_size``; +* ``rounding_mode``; +* ``rounding_increment``; +* ``format_width``; +* ``padding_position``; +* ``secondary_grouping_size``; +* ``significant_digits_used``; +* ``min_significant_digits_used``; +* ``max_significant_digits_used``; +* ``lenient_parse``. + +Besides plain numbers, the filter can also format numbers in various styles: + +.. code-block:: twig + + {# 1,234% #} + {{ '12.345'|format_number(style: 'percent') }} + + {# twelve point three four five #} + {{ '12.345'|format_number(style: 'spellout') }} + + {# 12 sec. #} + {{ '12'|format_duration_number }} + +The list of supported styles: + +* ``decimal``; +* ``currency``; +* ``percent``; +* ``scientific``; +* ``spellout``; +* ``ordinal``; +* ``duration``. + +As a shortcut, you can use the ``format_*_number`` filters by replacing ``*`` +with a style: + +.. code-block:: twig + + {# 1,234% #} + {{ '12.345'|format_percent_number }} + + {# twelve point three four five #} + {{ '12.345'|format_spellout_number }} + +You can pass attributes to tweak the output: + +.. code-block:: twig + + {# 12.3% #} + {{ '0.12345'|format_percent_number({rounding_mode: 'floor', fraction_digit: 1}) }} + +By default, the filter uses the current locale. You can pass it explicitly: + +.. code-block:: twig + + {# 12,345 #} + {{ '12.345'|format_number(locale: 'fr') }} + +.. note:: + + The ``format_number`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``locale``: The locale +* ``attrs``: A map of attributes +* ``style``: The style of the number output diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/format_time.rst b/upload/system/storage/vendor/twig/twig/doc/filters/format_time.rst new file mode 100644 index 000000000..1e213e616 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/format_time.rst @@ -0,0 +1,36 @@ +``format_time`` +=============== + +The ``format_time`` filter formats a time. It behaves in the exact same way as +the :doc:`format_datetime` filter, but without the date. + +.. note:: + + The ``format_time`` filter is part of the ``IntlExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/intl-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Intl\IntlExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new IntlExtension()); + +Arguments +--------- + +* ``locale``: The locale +* ``timeFormat``: The time format +* ``pattern``: A date time pattern +* ``timezone``: The date timezone +* ``calendar``: The calendar ("gregorian" by default) diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/html_to_markdown.rst b/upload/system/storage/vendor/twig/twig/doc/filters/html_to_markdown.rst new file mode 100644 index 000000000..58568a445 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/html_to_markdown.rst @@ -0,0 +1,77 @@ +``html_to_markdown`` +==================== + +The ``html_to_markdown`` filter converts a block of HTML to Markdown: + +.. code-block:: html+twig + + {% apply html_to_markdown %} + +

Hello!

+ + {% endapply %} + +You can also use the filter on an entire template which you ``include``: + +.. code-block:: twig + + {{ include('some_template.html.twig')|html_to_markdown }} + +.. note:: + + The ``html_to_markdown`` filter is part of the ``MarkdownExtension`` which + is not installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/markdown-extra + + On Symfony projects, you can automatically enable it by installing the + ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Or add the extension explicitly on the Twig environment:: + + use Twig\Extra\Markdown\MarkdownExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new MarkdownExtension()); + + If you are not using Symfony, you must also register the extension runtime:: + + use Twig\Extra\Markdown\DefaultMarkdown; + use Twig\Extra\Markdown\MarkdownRuntime; + use Twig\RuntimeLoader\RuntimeLoaderInterface; + + $twig->addRuntimeLoader(new class implements RuntimeLoaderInterface { + public function load($class) { + if (MarkdownRuntime::class === $class) { + return new MarkdownRuntime(new DefaultMarkdown()); + } + } + }); + +``html_to_markdown`` is just a frontend; the actual conversion is done by one of +the following compatible libraries, from which you can choose: + +* `league/html-to-markdown`_ +* `michelf/php-markdown`_ +* `erusev/parsedown`_ + +Depending on the library, you can also add some options by passing them as an argument +to the filter. Example for ``league/html-to-markdown``: + +.. code-block:: html+twig + + {% apply html_to_markdown({hard_break: false}) %} + +

Hello!

+ + {% endapply %} + +.. _league/html-to-markdown: https://github.com/thephpleague/html-to-markdown +.. _michelf/php-markdown: https://github.com/michelf/php-markdown +.. _erusev/parsedown: https://github.com/erusev/parsedown diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/index.rst b/upload/system/storage/vendor/twig/twig/doc/filters/index.rst new file mode 100644 index 000000000..7d2bde1b1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/index.rst @@ -0,0 +1,63 @@ +Filters +======= + +.. toctree:: + :maxdepth: 1 + + abs + batch + capitalize + column + convert_encoding + country_name + currency_name + currency_symbol + data_uri + date + date_modify + default + escape + filter + first + format + format_currency + format_date + format_datetime + format_number + format_time + html_to_markdown + inline_css + inky_to_html + join + json_encode + keys + language_name + last + length + locale_name + lower + map + markdown_to_html + merge + nl2br + number_format + plural + raw + reduce + replace + reverse + round + shuffle + singular + slice + slug + sort + spaceless + split + striptags + timezone_name + title + trim + u + upper + url_encode diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/inky_to_html.rst b/upload/system/storage/vendor/twig/twig/doc/filters/inky_to_html.rst new file mode 100644 index 000000000..563baba36 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/inky_to_html.rst @@ -0,0 +1,42 @@ +``inky_to_html`` +================ + +The ``inky_to_html`` filter processes an `inky email template +`_: + +.. code-block:: html+twig + + {% apply inky_to_html %} + + + + + {% endapply %} + +You can also use the filter on an included file: + +.. code-block:: twig + + {{ include('some_template.inky.twig')|inky_to_html }} + +.. note:: + + The ``inky_to_html`` filter is part of the ``InkyExtension`` which is not + installed by default. Install it first: + + .. code-block:: bash + + $ composer require twig/inky-extra + + Then, on Symfony projects, install the ``twig/extra-bundle``: + + .. code-block:: bash + + $ composer require twig/extra-bundle + + Otherwise, add the extension explicitly on the Twig environment:: + + use Twig\Extra\Inky\InkyExtension; + + $twig = new \Twig\Environment(...); + $twig->addExtension(new InkyExtension()); diff --git a/upload/system/storage/vendor/twig/twig/doc/filters/inline_css.rst b/upload/system/storage/vendor/twig/twig/doc/filters/inline_css.rst new file mode 100644 index 000000000..44b142626 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/filters/inline_css.rst @@ -0,0 +1,66 @@ +``inline_css`` +============== + +The ``inline_css`` filter inline CSS styles in HTML documents: + +.. code-block:: html+twig + + {% apply inline_css %} + + + + + +

Hello CSS!

+ + + {% endapply %} + +You can also add some stylesheets by passing them as arguments to the filter: + +.. code-block:: html+twig + + {% apply inline_css(source("some_styles.css"), source("another.css")) %} + + +

Hello CSS!

+ + + {% endapply %} + +Styles loaded via the filter override the styles defined in the `` + {% endblock %} + {% block content %} +

Index

+

+ Welcome on my awesome homepage. +

+ {% endblock %} + +The ``extends`` tag is the key here. It tells the template engine that this +template "extends" another template. When the template system evaluates this +template, first it locates the parent. The extends tag should be the first tag +in the template. + +Note that since the child template doesn't define the ``footer`` block, the +value from the parent template is used instead. + +You can't define multiple ``block`` tags with the same name in the same +template. This limitation exists because a block tag works in "both" +directions. That is, a block tag doesn't just provide a hole to fill - it also +defines the content that fills the hole in the *parent*. If there were two +similarly-named ``block`` tags in a template, that template's parent wouldn't +know which one of the blocks' content to use. + +If you want to print a block multiple times you can however use the +``block`` function: + +.. code-block:: html+twig + + {% block title %}{% endblock %} +

{{ block('title') }}

+ {% block body %}{% endblock %} + +Parent Blocks +------------- + +It's possible to render the contents of the parent block by using the +:doc:`parent<../functions/parent>` function. This gives back the results of +the parent block: + +.. code-block:: html+twig + + {% block sidebar %} +

Table Of Contents

+ ... + {{ parent() }} + {% endblock %} + +Named Block End-Tags +-------------------- + +Twig allows you to put the name of the block after the end tag for better +readability (the name after the ``endblock`` word must match the block name): + +.. code-block:: twig + + {% block sidebar %} + {% block inner_sidebar %} + ... + {% endblock inner_sidebar %} + {% endblock sidebar %} + +Block Nesting and Scope +----------------------- + +Blocks can be nested for more complex layouts. Per default, blocks have access +to variables from outer scopes: + +.. code-block:: html+twig + + {% for item in seq %} +
  • {% block loop_item %}{{ item }}{% endblock %}
  • + {% endfor %} + +Block Shortcuts +--------------- + +For blocks with little content, it's possible to use a shortcut syntax. The +following constructs do the same thing: + +.. code-block:: twig + + {% block title %} + {{ page_title|title }} + {% endblock %} + +.. code-block:: twig + + {% block title page_title|title %} + +Dynamic Inheritance +------------------- + +Twig supports dynamic inheritance by using a variable as the base template: + +.. code-block:: twig + + {% extends some_var %} + +If the variable evaluates to a ``\Twig\Template`` or a ``\Twig\TemplateWrapper`` +instance, Twig will use it as the parent template:: + + // {% extends layout %} + + $layout = $twig->load('some_layout_template.twig'); + + $twig->display('template.twig', ['layout' => $layout]); + +You can also provide a list of templates that are checked for existence. The +first template that exists will be used as a parent: + +.. code-block:: twig + + {% extends ['layout.html', 'base_layout.html'] %} + +Conditional Inheritance +----------------------- + +As the template name for the parent can be any valid Twig expression, it's +possible to make the inheritance mechanism conditional: + +.. code-block:: twig + + {% extends standalone ? "minimum.html" : "base.html" %} + +In this example, the template will extend the "minimum.html" layout template +if the ``standalone`` variable evaluates to ``true``, and "base.html" +otherwise. + +How do blocks work? +------------------- + +A block provides a way to change how a certain part of a template is rendered +but it does not interfere in any way with the logic around it. + +Let's take the following example to illustrate how a block works and more +importantly, how it does not work: + +.. code-block:: html+twig + + {# base.twig #} + {% for post in posts %} + {% block post %} +

    {{ post.title }}

    +

    {{ post.body }}

    + {% endblock %} + {% endfor %} + +If you render this template, the result would be exactly the same with or +without the ``block`` tag. The ``block`` inside the ``for`` loop is just a way +to make it overridable by a child template: + +.. code-block:: html+twig + + {# child.twig #} + {% extends "base.twig" %} + + {% block post %} +
    +
    {{ post.title }}
    +
    {{ post.text }}
    +
    + {% endblock %} + +Now, when rendering the child template, the loop is going to use the block +defined in the child template instead of the one defined in the base one; the +executed template is then equivalent to the following one: + +.. code-block:: html+twig + + {% for post in posts %} +
    +
    {{ post.title }}
    +
    {{ post.text }}
    +
    + {% endfor %} + +Let's take another example: a block included within an ``if`` statement: + +.. code-block:: html+twig + + {% if posts is empty %} + {% block head %} + {{ parent() }} + + + {% endblock head %} + {% endif %} + +Contrary to what you might think, this template does not define a block +conditionally; it just makes overridable by a child template the output of +what will be rendered when the condition is ``true``. + +If you want the output to be displayed conditionally, use the following +instead: + +.. code-block:: html+twig + + {% block head %} + {{ parent() }} + + {% if posts is empty %} + + {% endif %} + {% endblock head %} + +.. seealso:: + + :doc:`block<../functions/block>`, :doc:`block<../tags/block>`, :doc:`parent<../functions/parent>`, :doc:`use<../tags/use>` diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/flush.rst b/upload/system/storage/vendor/twig/twig/doc/tags/flush.rst new file mode 100644 index 000000000..03d2a3677 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/flush.rst @@ -0,0 +1,14 @@ +``flush`` +========= + +The ``flush`` tag tells Twig to flush the output buffer: + +.. code-block:: twig + + {% flush %} + +.. note:: + + Internally, Twig uses the PHP `flush`_ function. + +.. _`flush`: https://www.php.net/flush diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/for.rst b/upload/system/storage/vendor/twig/twig/doc/tags/for.rst new file mode 100644 index 000000000..656d9c07b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/for.rst @@ -0,0 +1,141 @@ +``for`` +======= + +Loop over each item in a sequence or a mapping. For example, to display a list +of users provided in a variable called ``users``: + +.. code-block:: html+twig + +

    Members

    +
      + {% for user in users %} +
    • {{ user.username|e }}
    • + {% endfor %} +
    + +.. note:: + + A sequence or a mapping can be either an array or an object implementing + the ``Traversable`` interface. + +If you do need to iterate over a sequence of numbers, you can use the ``..`` +operator: + +.. code-block:: twig + + {% for i in 0..10 %} + * {{ i }} + {% endfor %} + +The above snippet of code would print all numbers from 0 to 10. + +It can be also useful with letters: + +.. code-block:: twig + + {% for letter in 'a'..'z' %} + * {{ letter }} + {% endfor %} + +The ``..`` operator can take any expression at both sides: + +.. code-block:: twig + + {% for letter in 'a'|upper..'z'|upper %} + * {{ letter }} + {% endfor %} + +.. tip: + + If you need a step different from 1, you can use the ``range`` function + instead. + +The ``loop`` variable +--------------------- + +Inside of a ``for`` loop block you can access some special variables: + +===================== ============================================================= +Variable Description +===================== ============================================================= +``loop.index`` The current iteration of the loop. (1 indexed) +``loop.index0`` The current iteration of the loop. (0 indexed) +``loop.revindex`` The number of iterations from the end of the loop (1 indexed) +``loop.revindex0`` The number of iterations from the end of the loop (0 indexed) +``loop.first`` True if first iteration +``loop.last`` True if last iteration +``loop.length`` The number of items in the sequence +``loop.parent`` The parent context +===================== ============================================================= + +.. code-block:: twig + + {% for user in users %} + {{ loop.index }} - {{ user.username }} + {% endfor %} + +.. note:: + + The ``loop.length``, ``loop.revindex``, ``loop.revindex0``, and + ``loop.last`` variables are only available for PHP arrays, or objects that + implement the ``Countable`` interface. + +The ``else`` Clause +------------------- + +If no iteration took place because the sequence was empty, you can render a +replacement block by using ``else``: + +.. code-block:: html+twig + +
      + {% for user in users %} +
    • {{ user.username|e }}
    • + {% else %} +
    • no user found
    • + {% endfor %} +
    + +Iterating over Keys +------------------- + +By default, a loop iterates over the values of the sequence. You can iterate +on keys by using the ``keys`` filter: + +.. code-block:: html+twig + +

    Members

    +
      + {% for key in users|keys %} +
    • {{ key }}
    • + {% endfor %} +
    + +Iterating over Keys and Values +------------------------------ + +You can also access both keys and values: + +.. code-block:: html+twig + +

    Members

    +
      + {% for key, user in users %} +
    • {{ key }}: {{ user.username|e }}
    • + {% endfor %} +
    + +Iterating over a Subset +----------------------- + +You might want to iterate over a subset of values. This can be achieved using +the :doc:`slice <../filters/slice>` filter: + +.. code-block:: html+twig + +

    Top Ten Members

    +
      + {% for user in users|slice(0, 10) %} +
    • {{ user.username|e }}
    • + {% endfor %} +
    diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/from.rst b/upload/system/storage/vendor/twig/twig/doc/tags/from.rst new file mode 100644 index 000000000..96c439aa7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/from.rst @@ -0,0 +1,6 @@ +``from`` +======== + +The ``from`` tag imports :doc:`macro<../tags/macro>` names into the current +namespace. The tag is documented in detail in the documentation for the +:doc:`macro<../tags/macro>` tag. diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/if.rst b/upload/system/storage/vendor/twig/twig/doc/tags/if.rst new file mode 100644 index 000000000..8a29af5da --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/if.rst @@ -0,0 +1,80 @@ +``if`` +====== + +The ``if`` statement in Twig is comparable with the if statements of PHP. + +In the simplest form you can use it to test if an expression evaluates to +``true``: + +.. code-block:: html+twig + + {% if online == false %} +

    Our website is in maintenance mode. Please, come back later.

    + {% endif %} + +You can also test if a sequence or a mapping is not empty: + +.. code-block:: html+twig + + {% if users %} +
      + {% for user in users %} +
    • {{ user.username|e }}
    • + {% endfor %} +
    + {% endif %} + +.. note:: + + If you want to test if the variable is defined, use ``if users is defined`` instead. + +You can also use ``not`` to check for values that evaluate to ``false``: + +.. code-block:: html+twig + + {% if not user.subscribed %} +

    You are not subscribed to our mailing list.

    + {% endif %} + +For multiple conditions, ``and`` and ``or`` can be used: + +.. code-block:: html+twig + + {% if temperature > 18 and temperature < 27 %} +

    It's a nice day for a walk in the park.

    + {% endif %} + +For multiple branches ``elseif`` and ``else`` can be used like in PHP. You can +use more complex ``expressions`` there too: + +.. code-block:: twig + + {% if product.stock > 10 %} + Available + {% elseif product.stock > 0 %} + Only {{ product.stock }} left! + {% else %} + Sold-out! + {% endif %} + +.. note:: + + The rules to determine if an expression is ``true`` or ``false`` are the + same as in PHP; here are the edge cases rules: + + ====================== ==================== + Value Boolean evaluation + ====================== ==================== + empty string false + numeric zero false + NAN (Not A Number) true + INF (Infinity) true + whitespace-only string true + string "0" or '0' false + empty sequence false + empty mapping false + null false + non-empty sequence true + non-empty mapping true + object true + ====================== ==================== diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/import.rst b/upload/system/storage/vendor/twig/twig/doc/tags/import.rst new file mode 100644 index 000000000..f217479fb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/import.rst @@ -0,0 +1,6 @@ +``import`` +========== + +The ``import`` tag imports :doc:`macro<../tags/macro>` names in a local +variable. The tag is documented in detail in the documentation for the +:doc:`macro<../tags/macro>` tag. diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/include.rst b/upload/system/storage/vendor/twig/twig/doc/tags/include.rst new file mode 100644 index 000000000..93fb0371b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/include.rst @@ -0,0 +1,110 @@ +``include`` +=========== + +The ``include`` statement includes a template and outputs the rendered content +of that file: + +.. code-block:: twig + + {% include 'header.html' %} + Body + {% include 'footer.html' %} + +.. note:: + + It is recommended to use the :doc:`include<../functions/include>` function + instead as it provides the same features with a bit more flexibility: + + * The ``include`` function is semantically more "correct" (including a + template outputs its rendered contents in the current scope; a tag should + not display anything); + + * The ``include`` function is more "composable": + + .. code-block:: twig + + {# Store a rendered template in a variable #} + {% set content %} + {% include 'template.html' %} + {% endset %} + {# vs #} + {% set content = include('template.html') %} + + {# Apply filter on a rendered template #} + {% apply upper %} + {% include 'template.html' %} + {% endapply %} + {# vs #} + {{ include('template.html')|upper }} + + * The ``include`` function does not impose any specific order for + arguments thanks to :ref:`named arguments `. + +Included templates have access to the variables of the active context. + +If you are using the filesystem loader, the templates are looked for in the +paths defined by it. + +You can add additional variables by passing them after the ``with`` keyword: + +.. code-block:: twig + + {# template.html will have access to the variables from the current context and the additional ones provided #} + {% include 'template.html' with {'foo': 'bar'} %} + + {% set vars = {'foo': 'bar'} %} + {% include 'template.html' with vars %} + +You can disable access to the context by appending the ``only`` keyword: + +.. code-block:: twig + + {# only the foo variable will be accessible #} + {% include 'template.html' with {'foo': 'bar'} only %} + +.. code-block:: twig + + {# no variables will be accessible #} + {% include 'template.html' only %} + +.. tip:: + + When including a template created by an end user, you should consider + sandboxing it. More information in the :doc:`Twig for Developers<../api>` + chapter and in the :doc:`sandbox<../tags/sandbox>` tag documentation. + +The template name can be any valid Twig expression: + +.. code-block:: twig + + {% include some_var %} + {% include ajax ? 'ajax.html' : 'not_ajax.html' %} + +And if the expression evaluates to a ``\Twig\Template`` or a +``\Twig\TemplateWrapper`` instance, Twig will use it directly:: + + // {% include template %} + + $template = $twig->load('some_template.twig'); + + $twig->display('template.twig', ['template' => $template]); + +You can mark an include with ``ignore missing`` in which case Twig will ignore +the statement if the template to be included does not exist. It has to be +placed just after the template name. Here some valid examples: + +.. code-block:: twig + + {% include 'sidebar.html' ignore missing %} + {% include 'sidebar.html' ignore missing with {'foo': 'bar'} %} + {% include 'sidebar.html' ignore missing only %} + +You can also provide a list of templates that are checked for existence before +inclusion. The first template that exists will be included: + +.. code-block:: twig + + {% include ['page_detailed.html', 'page.html'] %} + +If ``ignore missing`` is given, it will fall back to rendering nothing if none +of the templates exist, otherwise it will throw an exception. diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/index.rst b/upload/system/storage/vendor/twig/twig/doc/tags/index.rst new file mode 100644 index 000000000..b3c104080 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/index.rst @@ -0,0 +1,26 @@ +Tags +==== + +.. toctree:: + :maxdepth: 1 + + apply + autoescape + block + cache + deprecated + do + embed + extends + flush + for + from + if + import + include + macro + sandbox + set + use + verbatim + with diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/macro.rst b/upload/system/storage/vendor/twig/twig/doc/tags/macro.rst new file mode 100644 index 000000000..d1d0641c0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/macro.rst @@ -0,0 +1,148 @@ +``macro`` +========= + +Macros are comparable with functions in regular programming languages. They +are useful to reuse template fragments to not repeat yourself. + +Macros are defined in regular templates. + +Imagine having a generic helper template that define how to render HTML forms +via macros (called ``forms.twig``): + +.. code-block:: html+twig + + {% macro input(name, value, type = "text", size = 20) %} + + {% endmacro %} + + {% macro textarea(name, value, rows = 10, cols = 40) %} + + {% endmacro %} + +Each macro argument can have a default value (here ``text`` is the default value +for ``type`` if not provided in the call). + +Macros differ from native PHP functions in a few ways: + +* Arguments of a macro are always optional. + +* If extra positional arguments are passed to a macro, they end up in the + special ``varargs`` variable as a list of values. + +But as with PHP functions, macros don't have access to the current template +variables. + +.. tip:: + + You can pass the whole context as an argument by using the special + ``_context`` variable. + +Importing Macros +---------------- + +There are two ways to import macros. You can import the complete template +containing the macros into a local variable (via the ``import`` tag) or only +import specific macros from the template (via the ``from`` tag). + +To import all macros from a template into a local variable, use the ``import`` +tag: + +.. code-block:: twig + + {% import "forms.twig" as forms %} + +The above ``import`` call imports the ``forms.twig`` file (which can contain +only macros, or a template and some macros), and import the macros as items of +the ``forms`` local variable. + +The macros can then be called at will in the *current* template: + +.. code-block:: html+twig + +

    {{ forms.input('username') }}

    +

    {{ forms.input('password', null, 'password') }}

    + +Alternatively you can import names from the template into the current namespace +via the ``from`` tag: + +.. code-block:: html+twig + + {% from 'forms.twig' import input as input_field, textarea %} + +

    {{ input_field('password', '', 'password') }}

    +

    {{ textarea('comment') }}

    + +.. caution:: + + As macros imported via ``from`` are called like functions, be careful that + they shadow existing functions: + + .. code-block:: twig + + {% from 'forms.twig' import input as include %} + + {# include refers to the macro and not to the built-in "include" function #} + {{ include() }} + +.. tip:: + + When macro usages and definitions are in the same template, you don't need to + import the macros as they are automatically available under the special + ``_self`` variable: + + .. code-block:: html+twig + +

    {{ _self.input('password', '', 'password') }}

    + + {% macro input(name, value, type = "text", size = 20) %} + + {% endmacro %} + +Macros Scoping +-------------- + +The scoping rules are the same whether you imported macros via ``import`` or +``from``. + +Imported macros are always **local** to the current template. It means that +macros are available in all blocks and other macros defined in the current +template, but they are not available in included templates or child templates; +you need to explicitly re-import macros in each template. + +Imported macros are not available in the body of ``embed`` tags, you need +to explicitly re-import macros inside the tag. + +When calling ``import`` or ``from`` from a ``block`` tag, the imported macros +are only defined in the current block and they shadow macros defined at the +template level with the same names. + +Checking if a Macro is defined +------------------------------ + +You can check if a macro is defined via the ``defined`` test: + +.. code-block:: twig + + {% import "macros.twig" as macros %} + + {% from "macros.twig" import hello %} + + {% if macros.hello is defined -%} + OK + {% endif %} + + {% if hello is defined -%} + OK + {% endif %} + +Named Macro End-Tags +-------------------- + +Twig allows you to put the name of the macro after the end tag for better +readability (the name after the ``endmacro`` word must match the macro name): + +.. code-block:: twig + + {% macro input() %} + ... + {% endmacro input %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/sandbox.rst b/upload/system/storage/vendor/twig/twig/doc/tags/sandbox.rst new file mode 100644 index 000000000..b331fdb8e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/sandbox.rst @@ -0,0 +1,30 @@ +``sandbox`` +=========== + +The ``sandbox`` tag can be used to enable the sandboxing mode for an included +template, when sandboxing is not enabled globally for the Twig environment: + +.. code-block:: twig + + {% sandbox %} + {% include 'user.html' %} + {% endsandbox %} + +.. warning:: + + The ``sandbox`` tag is only available when the sandbox extension is + enabled (see the :doc:`Twig for Developers<../api>` chapter). + +.. note:: + + The ``sandbox`` tag can only be used to sandbox an include tag and it + cannot be used to sandbox a section of a template. The following example + won't work: + + .. code-block:: twig + + {% sandbox %} + {% for i in 1..2 %} + {{ i }} + {% endfor %} + {% endsandbox %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/set.rst b/upload/system/storage/vendor/twig/twig/doc/tags/set.rst new file mode 100644 index 000000000..7a3a784f5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/set.rst @@ -0,0 +1,78 @@ +``set`` +======= + +Inside code blocks you can also assign values to variables. Assignments use +the ``set`` tag and can have multiple targets. + +Here is how you can assign the ``bar`` value to the ``foo`` variable: + +.. code-block:: twig + + {% set foo = 'bar' %} + +After the ``set`` call, the ``foo`` variable is available in the template like +any other ones: + +.. code-block:: twig + + {# displays bar #} + {{ foo }} + +The assigned value can be any valid :ref:`Twig expression +`: + +.. code-block:: twig + + {% set foo = [1, 2] %} + {% set foo = {'foo': 'bar'} %} + {% set foo = 'foo' ~ 'bar' %} + +Several variables can be assigned in one block: + +.. code-block:: twig + + {% set foo, bar = 'foo', 'bar' %} + + {# is equivalent to #} + + {% set foo = 'foo' %} + {% set bar = 'bar' %} + +The ``set`` tag can also be used to 'capture' chunks of text: + +.. code-block:: html+twig + + {% set foo %} + + {% endset %} + +.. caution:: + + If you enable automatic output escaping, Twig will only consider the + content to be safe when capturing chunks of text. + +.. note:: + + Note that loops are scoped in Twig; therefore a variable declared inside a + ``for`` loop is not accessible outside the loop itself: + + .. code-block:: twig + + {% for item in list %} + {% set foo = item %} + {% endfor %} + + {# foo is NOT available #} + + If you want to access the variable, just declare it before the loop: + + .. code-block:: twig + + {% set foo = "" %} + {% for item in list %} + {% set foo = item %} + {% endfor %} + + {# foo is available #} diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/use.rst b/upload/system/storage/vendor/twig/twig/doc/tags/use.rst new file mode 100644 index 000000000..2aca6a01f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/use.rst @@ -0,0 +1,117 @@ +``use`` +======= + +.. note:: + + Horizontal reuse is an advanced Twig feature that is hardly ever needed in + regular templates. It is mainly used by projects that need to make + template blocks reusable without using inheritance. + +Template inheritance is one of the most powerful features of Twig but it is +limited to single inheritance; a template can only extend one other template. +This limitation makes template inheritance simple to understand and easy to +debug: + +.. code-block:: twig + + {% extends "base.html" %} + + {% block title %}{% endblock %} + {% block content %}{% endblock %} + +Horizontal reuse is a way to achieve the same goal as multiple inheritance, +but without the associated complexity: + +.. code-block:: twig + + {% extends "base.html" %} + + {% use "blocks.html" %} + + {% block title %}{% endblock %} + {% block content %}{% endblock %} + +The ``use`` statement tells Twig to import the blocks defined in +``blocks.html`` into the current template (it's like macros, but for blocks): + +.. code-block:: twig + + {# blocks.html #} + + {% block sidebar %}{% endblock %} + +In this example, the ``use`` statement imports the ``sidebar`` block into the +main template. The code is mostly equivalent to the following one (the +imported blocks are not outputted automatically): + +.. code-block:: twig + + {% extends "base.html" %} + + {% block sidebar %}{% endblock %} + {% block title %}{% endblock %} + {% block content %}{% endblock %} + +.. note:: + + The ``use`` tag only imports a template if it does not extend another + template, if it does not define macros, and if the body is empty. But it + can *use* other templates. + +.. note:: + + Because ``use`` statements are resolved independently of the context + passed to the template, the template reference cannot be an expression. + +The main template can also override any imported block. If the template +already defines the ``sidebar`` block, then the one defined in ``blocks.html`` +is ignored. To avoid name conflicts, you can rename imported blocks: + +.. code-block:: twig + + {% extends "base.html" %} + + {% use "blocks.html" with sidebar as base_sidebar, title as base_title %} + + {% block sidebar %}{% endblock %} + {% block title %}{% endblock %} + {% block content %}{% endblock %} + +The ``parent()`` function automatically determines the correct inheritance +tree, so it can be used when overriding a block defined in an imported +template: + +.. code-block:: twig + + {% extends "base.html" %} + + {% use "blocks.html" %} + + {% block sidebar %} + {{ parent() }} + {% endblock %} + + {% block title %}{% endblock %} + {% block content %}{% endblock %} + +In this example, ``parent()`` will correctly call the ``sidebar`` block from +the ``blocks.html`` template. + +.. tip:: + + Renaming allows you to simulate inheritance by calling the "parent" block: + + .. code-block:: twig + + {% extends "base.html" %} + + {% use "blocks.html" with sidebar as parent_sidebar %} + + {% block sidebar %} + {{ block('parent_sidebar') }} + {% endblock %} + +.. note:: + + You can use as many ``use`` statements as you want in any given template. + If two imported templates define the same block, the latest one wins. diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/verbatim.rst b/upload/system/storage/vendor/twig/twig/doc/tags/verbatim.rst new file mode 100644 index 000000000..3d7115a68 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/verbatim.rst @@ -0,0 +1,16 @@ +``verbatim`` +============ + +The ``verbatim`` tag marks sections as being raw text that should not be +parsed. For example to put Twig syntax as example into a template you can use +this snippet: + +.. code-block:: html+twig + + {% verbatim %} +
      + {% for item in seq %} +
    • {{ item }}
    • + {% endfor %} +
    + {% endverbatim %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tags/with.rst b/upload/system/storage/vendor/twig/twig/doc/tags/with.rst new file mode 100644 index 000000000..268bb373d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tags/with.rst @@ -0,0 +1,41 @@ +``with`` +======== + +Use the ``with`` tag to create a new inner scope. Variables set within this +scope are not visible outside of the scope: + +.. code-block:: twig + + {% with %} + {% set foo = 42 %} + {{ foo }} {# foo is 42 here #} + {% endwith %} + foo is not visible here any longer + +Instead of defining variables at the beginning of the scope, you can pass a +mapping of variables you want to define in the ``with`` tag; the previous +example is equivalent to the following one: + +.. code-block:: twig + + {% with {foo: 42} %} + {{ foo }} {# foo is 42 here #} + {% endwith %} + foo is not visible here any longer + + {# it works with any expression that resolves to a mapping #} + {% set vars = {foo: 42} %} + {% with vars %} + ... + {% endwith %} + +By default, the inner scope has access to the outer scope context; you can +disable this behavior by appending the ``only`` keyword: + +.. code-block:: twig + + {% set bar = 'bar' %} + {% with {foo: 42} only %} + {# only foo is defined #} + {# bar is not defined #} + {% endwith %} diff --git a/upload/system/storage/vendor/twig/twig/doc/templates.rst b/upload/system/storage/vendor/twig/twig/doc/templates.rst new file mode 100644 index 000000000..3cd8f9b90 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/templates.rst @@ -0,0 +1,965 @@ +Twig for Template Designers +=========================== + +This document describes the syntax and semantics of the template engine and +will be most useful as reference to those creating Twig templates. + +Synopsis +-------- + +A template is a regular text file. It can generate any text-based format (HTML, +XML, CSV, LaTeX, etc.). It doesn't have a specific extension, ``.html`` or +``.xml`` are just fine. + +A template contains **variables** or **expressions**, which get replaced with +values when the template is evaluated, and **tags**, which control the +template's logic. + +Below is a minimal template that illustrates a few basics. We will cover further +details later on: + +.. code-block:: html+twig + + + + + My Webpage + + + + +

    My Webpage

    + {{ a_variable }} + + + +There are two kinds of delimiters: ``{% ... %}`` and ``{{ ... }}``. The first +one is used to execute statements such as for-loops, the latter outputs the +result of an expression. + +IDEs Integration +---------------- + +Many IDEs support syntax highlighting and auto-completion for Twig: + +* *Textmate* via the `Twig bundle`_ +* *Vim* via the `vim-twig plugin`_ +* *Netbeans* (native as of 7.2) +* *PhpStorm* (native as of 2.1) +* *Eclipse* via the `Twig plugin`_ +* *Sublime Text* via the `Twig bundle`_ +* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects) +* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_ +* *Coda 2* via the `other Twig syntax mode`_ +* *Komodo* and *Komodo Edit* via the Twig highlight/syntax check mode +* *Notepad++* via the `Notepad++ Twig Highlighter`_ +* *Emacs* via `web-mode.el`_ +* *Atom* via the `PHP-twig for atom`_ +* *Visual Studio Code* via the `Twig pack`_, `Modern Twig`_ or `Twiggy`_ + +You might also be interested in: + +* `TwigFiddle`_: an online service that allows you to execute Twig templates + from a browser; it supports all versions of Twig + +* `Twig Language Server`_: provides some language features like syntax + highlighting, diagnostics, auto complete, ... + +Variables +--------- + +The application passes variables to the templates for manipulation in the +template. Variables may have attributes or elements you can access, too. The +visual representation of a variable depends heavily on the application providing +it. + +Use a dot (``.``) to access attributes of a variable (methods or properties of a +PHP object, or items of a PHP array): + +.. code-block:: twig + + {{ foo.bar }} + +.. note:: + + It's important to know that the curly braces are *not* part of the + variable but the print statement. When accessing variables inside tags, + don't put the braces around them. + +If a variable or attribute does not exist, the behavior depends on the +``strict_variables`` option value (see :ref:`environment options +`): + +* When ``false``, it returns ``null``; +* When ``true``, it throws an exception. + +.. sidebar:: Implementation + + For convenience's sake ``foo.bar`` does the following things on the PHP + layer: + + * check if ``foo`` is a sequence or a mapping and ``bar`` a valid element; + * if not, and if ``foo`` is an object, check that ``bar`` is a valid property; + * if not, and if ``foo`` is an object, check that ``bar`` is a valid method + (even if ``bar`` is the constructor - use ``__construct()`` instead); + * if not, and if ``foo`` is an object, check that ``getBar`` is a valid method; + * if not, and if ``foo`` is an object, check that ``isBar`` is a valid method; + * if not, and if ``foo`` is an object, check that ``hasBar`` is a valid method; + * if not, and if ``strict_variables`` is ``false``, return ``null``; + * if not, throw an exception. + + Twig also supports a specific syntax for accessing items on PHP arrays, + ``foo['bar']``: + + * check if ``foo`` is a sequence or a mapping and ``bar`` a valid element; + * if not, and if ``strict_variables`` is ``false``, return ``null``; + * if not, throw an exception. + +.. note:: + + If you want to access a dynamic attribute of a variable, use the + :doc:`attribute` function instead. + + The ``attribute`` function is also useful when the attribute contains + special characters (like ``-`` that would be interpreted as the minus + operator): + + .. code-block:: twig + + {# equivalent to the non-working foo.data-foo #} + {{ attribute(foo, 'data-foo') }} + +Global Variables +~~~~~~~~~~~~~~~~ + +The following variables are always available in templates: + +* ``_self``: references the current template name; +* ``_context``: references the current context; +* ``_charset``: references the current charset. + +Setting Variables +~~~~~~~~~~~~~~~~~ + +You can assign values to variables inside code blocks. Assignments use the +:doc:`set` tag: + +.. code-block:: twig + + {% set foo = 'foo' %} + {% set foo = [1, 2] %} + {% set foo = {'foo': 'bar'} %} + +Filters +------- + +Variables can be modified by **filters**. Filters are separated from the +variable by a pipe symbol (``|``). Multiple filters can be chained. The output +of one filter is applied to the next. + +The following example removes all HTML tags from the ``name`` and title-cases +it: + +.. code-block:: twig + + {{ name|striptags|title }} + +Filters that accept arguments have parentheses around the arguments. This +example joins the elements of a list by commas: + +.. code-block:: twig + + {{ list|join(', ') }} + +To apply a filter on a section of code, wrap it with the +:doc:`apply` tag: + +.. code-block:: twig + + {% apply upper %} + This text becomes uppercase + {% endapply %} + +Go to the :doc:`filters` page to learn more about built-in +filters. + +Functions +--------- + +Functions can be called to generate content. Functions are called by their +name followed by parentheses (``()``) and may have arguments. + +For instance, the ``range`` function returns a list containing an arithmetic +progression of integers: + +.. code-block:: twig + + {% for i in range(0, 3) %} + {{ i }}, + {% endfor %} + +Go to the :doc:`functions` page to learn more about the +built-in functions. + +.. _named-arguments: + +Named Arguments +--------------- + +Named arguments are supported in functions, filters, and tests. + +.. versionadded:: 3.12 + + Twig supports both ``=`` and ``:`` as separators between argument names and + values, but support for ``:`` was introduced in Twig 3.12. + +.. code-block:: twig + + {% for i in range(low: 1, high: 10, step: 2) %} + {{ i }}, + {% endfor %} + +Using named arguments makes your templates more explicit about the meaning of +the values you pass as arguments: + +.. code-block:: twig + + {{ data|convert_encoding('UTF-8', 'iso-2022-jp') }} + + {# versus #} + + {{ data|convert_encoding(from: 'iso-2022-jp', to: 'UTF-8') }} + +Named arguments also allow you to skip some arguments for which you don't want +to change the default value: + +.. code-block:: twig + + {# the first argument is the date format, which defaults to the global date format if null is passed #} + {{ "now"|date(null, "Europe/Paris") }} + + {# or skip the format value by using a named argument for the time zone #} + {{ "now"|date(timezone: "Europe/Paris") }} + +You can also use both positional and named arguments in one call, in which +case positional arguments must always come before named arguments: + +.. code-block:: twig + + {{ "now"|date('d/m/Y H:i', timezone: "Europe/Paris") }} + +.. tip:: + + Each function, filter, and test documentation page has a section where the + names of all supported arguments are listed. + +Control Structure +----------------- + +A control structure refers to all those things that control the flow of a +program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as +well as things like blocks. Control structures appear inside ``{% ... %}`` +blocks. + +For example, to display a list of users provided in a variable called +``users``, use the :doc:`for` tag: + +.. code-block:: html+twig + +

    Members

    +
      + {% for user in users %} +
    • {{ user.username|e }}
    • + {% endfor %} +
    + +The :doc:`if` tag can be used to test an expression: + +.. code-block:: html+twig + + {% if users|length > 0 %} +
      + {% for user in users %} +
    • {{ user.username|e }}
    • + {% endfor %} +
    + {% endif %} + +Go to the :doc:`tags` page to learn more about the built-in tags. + +Comments +-------- + +To comment-out part of a line in a template, use the comment syntax ``{# ... +#}``. This is useful for debugging or to add information for other template +designers or yourself: + +.. code-block:: twig + + {# note: disabled template because we no longer use this + {% for user in users %} + ... + {% endfor %} + #} + +Including other Templates +------------------------- + +The :doc:`include` function is useful to include a template +and return the rendered content of that template into the current one: + +.. code-block:: twig + + {{ include('sidebar.html') }} + +By default, included templates have access to the same context as the template +which includes them. This means that any variable defined in the main template +will be available in the included template too: + +.. code-block:: twig + + {% for box in boxes %} + {{ include('render_box.html') }} + {% endfor %} + +The included template ``render_box.html`` is able to access the ``box`` variable. + +The name of the template depends on the template loader. For instance, the +``\Twig\Loader\FilesystemLoader`` allows you to access other templates by giving the +filename. You can access templates in subdirectories with a slash: + +.. code-block:: twig + + {{ include('sections/articles/sidebar.html') }} + +This behavior depends on the application embedding Twig. + +Template Inheritance +-------------------- + +The most powerful part of Twig is template inheritance. Template inheritance +allows you to build a base "skeleton" template that contains all the common +elements of your site and defines **blocks** that child templates can +override. + +It's easier to understand the concept by starting with an example. + +Let's define a base template, ``base.html``, which defines an HTML skeleton +document that might be used for a two-column page: + +.. code-block:: html+twig + + + + + {% block head %} + + {% block title %}{% endblock %} - My Webpage + {% endblock %} + + +
    {% block content %}{% endblock %}
    + + + + +In this example, the :doc:`block` tags define four blocks that +child templates can fill in. All the ``block`` tag does is to tell the +template engine that a child template may override those portions of the +template. + +A child template might look like this: + +.. code-block:: html+twig + + {% extends "base.html" %} + + {% block title %}Index{% endblock %} + {% block head %} + {{ parent() }} + + {% endblock %} + {% block content %} +

    Index

    +

    + Welcome to my awesome homepage. +

    + {% endblock %} + +The :doc:`extends` tag is the key here. It tells the template +engine that this template "extends" another template. When the template system +evaluates this template, first it locates the parent. The extends tag should +be the first tag in the template. + +Note that since the child template doesn't define the ``footer`` block, the +value from the parent template is used instead. + +It's possible to render the contents of the parent block by using the +:doc:`parent` function. This gives back the results of the +parent block: + +.. code-block:: html+twig + + {% block sidebar %} +

    Table Of Contents

    + ... + {{ parent() }} + {% endblock %} + +.. tip:: + + The documentation page for the :doc:`extends` tag describes + more advanced features like block nesting, scope, dynamic inheritance, and + conditional inheritance. + +.. note:: + + Twig also supports multiple inheritance via "horizontal reuse" with the help + of the :doc:`use` tag. + +HTML Escaping +------------- + +When generating HTML from templates, there's always a risk that a variable +will include characters that affect the resulting HTML. There are two +approaches: manually escaping each variable or automatically escaping +everything by default. + +Twig supports both, automatic escaping is enabled by default. + +The automatic escaping strategy can be configured via the +:ref:`autoescape` option and defaults to ``html``. + +Working with Manual Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +If manual escaping is enabled, it is **your** responsibility to escape variables +if needed. What to escape? Any variable that comes from an untrusted source. + +Escaping works by using the :doc:`escape` or ``e`` filter: + +.. code-block:: twig + + {{ user.username|e }} + +By default, the ``escape`` filter uses the ``html`` strategy, but depending on +the escaping context, you might want to explicitly use another strategy: + +.. code-block:: twig + + {{ user.username|e('js') }} + {{ user.username|e('css') }} + {{ user.username|e('url') }} + {{ user.username|e('html_attr') }} + +Working with Automatic Escaping +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Whether automatic escaping is enabled or not, you can mark a section of a +template to be escaped or not by using the :doc:`autoescape` +tag: + +.. code-block:: twig + + {% autoescape %} + Everything will be automatically escaped in this block (using the HTML strategy) + {% endautoescape %} + +By default, auto-escaping uses the ``html`` escaping strategy. If you output +variables in other contexts, you need to explicitly escape them with the +appropriate escaping strategy: + +.. code-block:: twig + + {% autoescape 'js' %} + Everything will be automatically escaped in this block (using the JS strategy) + {% endautoescape %} + +Escaping +-------- + +It is sometimes desirable or even necessary to have Twig ignore parts it would +otherwise handle as variables or blocks. For example if the default syntax is +used and you want to use ``{{`` as raw string in the template and not start a +variable you have to use a trick. + +The easiest way is to output the variable delimiter (``{{``) by using a variable +expression: + +.. code-block:: twig + + {{ '{{' }} + +For bigger sections it makes sense to mark a block +:doc:`verbatim`. + +Macros +------ + +Macros are comparable with functions in regular programming languages. They are +useful to reuse HTML fragments to not repeat yourself. They are described in the +:doc:`macro` tag documentation. + +.. _twig-expressions: + +Expressions +----------- + +Twig allows expressions everywhere. + +Literals +~~~~~~~~ + +The simplest form of expressions are literals. Literals are representations +for PHP types such as strings, numbers, and arrays. The following literals +exist: + +* ``"Hello World"``: Everything between two double or single quotes is a + string. They are useful whenever you need a string in the template (for + example as arguments to function calls, filters or just to extend or include + a template). + + Note that certain characters require escaping: + * ``\f``: Form feed + * ``\n``: New line + * ``\r``: Carriage return + * ``\t``: Horizontal tab + * ``\v``: Vertical tab + * ``\x``: Hexadecimal escape sequence + * ``\0`` to ``\377``: Octal escape sequences representing characters + * ``\``: Backslash + + When using single-quoted strings, the single quote character (``'``) needs to be escaped with a backslash (``\'``). + When using double-quoted strings, the double quote character (``"``) needs to be escaped with a backslash (``\"``). + + For example, a single quoted string can contain a delimiter if it is preceded by a + backslash (``\``) -- like in ``'It\'s good'``. If the string contains a + backslash (e.g. ``'c:\Program Files'``) escape it by doubling it + (e.g. ``'c:\\Program Files'``). + +* ``42`` / ``42.23``: Integers and floating point numbers are created by + writing the number down. If a dot is present the number is a float, + otherwise an integer. + +* ``["foo", "bar"]``: Sequences are defined by a sequence of expressions + separated by a comma (``,``) and wrapped with squared brackets (``[]``). + +* ``{"foo": "bar"}``: Mappings are defined by a list of keys and values + separated by a comma (``,``) and wrapped with curly braces (``{}``): + + .. code-block:: twig + + {# keys as string #} + {'foo': 'foo', 'bar': 'bar'} + + {# keys as names (equivalent to the previous mapping) #} + {foo: 'foo', bar: 'bar'} + + {# keys as integer #} + {2: 'foo', 4: 'bar'} + + {# keys can be omitted if it is the same as the variable name #} + {foo} + {# is equivalent to the following #} + {'foo': foo} + + {# keys as expressions (the expression must be enclosed into parentheses) #} + {% set foo = 'foo' %} + {(foo): 'foo', (1 + 1): 'bar', (foo ~ 'b'): 'baz'} + +* ``true`` / ``false``: ``true`` represents the true value, ``false`` + represents the false value. + +* ``null``: ``null`` represents no specific value. This is the value returned + when a variable does not exist. ``none`` is an alias for ``null``. + +Sequences and mappings can be nested: + +.. code-block:: twig + + {% set foo = [1, {"foo": "bar"}] %} + +.. tip:: + + Using double-quoted or single-quoted strings has no impact on performance + but :ref:`string interpolation ` is only + supported in double-quoted strings. + +.. _templates-string-interpolation: + +String Interpolation +~~~~~~~~~~~~~~~~~~~~ + +String interpolation (``#{expression}``) allows any valid expression to appear +within a *double-quoted string*. The result of evaluating that expression is +inserted into the string: + +.. code-block:: twig + + {{ "foo #{bar} baz" }} + {{ "foo #{1 + 2} baz" }} + +.. tip:: + + String interpolations can be ignored by escaping them with a backslash + (``\``): + + .. code-block:: twig + + {# outputs foo #{1 + 2} baz #} + {{ "foo \#{1 + 2} baz" }} + +Math +~~~~ + +Twig allows you to do math in templates; the following operators are supported: + +* ``+``: Adds two numbers together (the operands are casted to numbers). ``{{ + 1 + 1 }}`` is ``2``. + +* ``-``: Subtracts the second number from the first one. ``{{ 3 - 2 }}`` is + ``1``. + +* ``/``: Divides two numbers. The returned value will be a floating point + number. ``{{ 1 / 2 }}`` is ``{{ 0.5 }}``. + +* ``%``: Calculates the remainder of an integer division. ``{{ 11 % 7 }}`` is + ``4``. + +* ``//``: Divides two numbers and returns the floored integer result. ``{{ 20 + // 7 }}`` is ``2``, ``{{ -20 // 7 }}`` is ``-3`` (this is just syntactic + sugar for the :doc:`round` filter). + +* ``*``: Multiplies the left operand with the right one. ``{{ 2 * 2 }}`` would + return ``4``. + +* ``**``: Raises the left operand to the power of the right operand. ``{{ 2 ** + 3 }}`` would return ``8``. + +.. _template_logic: + +Logic +~~~~~ + +You can combine multiple expressions with the following operators: + +* ``and``: Returns true if the left and the right operands are both true. + +* ``or``: Returns true if the left or the right operand is true. + +* ``not``: Negates a statement. + +* ``(expr)``: Groups an expression. + +.. note:: + + Twig also supports bitwise operators (``b-and``, ``b-xor``, and ``b-or``). + +.. note:: + + Operators are case sensitive. + +Comparisons +~~~~~~~~~~~ + +The following comparison operators are supported in any expression: ``==``, +``!=``, ``<``, ``>``, ``>=``, and ``<=``. + +Check if a string ``starts with`` or ``ends with`` another string: + +.. code-block:: twig + + {% if 'Fabien' starts with 'F' %} + {% endif %} + + {% if 'Fabien' ends with 'n' %} + {% endif %} + +Check that a string contains another string via the containment operator (see +next section). + +.. note:: + + For complex string comparisons, the ``matches`` operator allows you to use + `regular expressions`_: + + .. code-block:: twig + + {% if phone matches '/^[\\d\\.]+$/' %} + {% endif %} + +Check that a sequence or a mapping ``has every`` or ``has some`` of its +elements return ``true`` using an arrow function. The arrow function receives +the value of the sequence or mapping: + +.. code-block:: twig + + {% set sizes = [34, 36, 38, 40, 42] %} + + {% set hasOnlyOver38 = sizes has every v => v > 38 %} + {# hasOnlyOver38 is false #} + + {% set hasOver38 = sizes has some v => v > 38 %} + {# hasOver38 is true #} + +Containment Operator +~~~~~~~~~~~~~~~~~~~~ + +The ``in`` operator performs containment test. It returns ``true`` if the left +operand is contained in the right: + +.. code-block:: twig + + {# returns true #} + + {{ 1 in [1, 2, 3] }} + + {{ 'cd' in 'abcde' }} + +.. tip:: + + You can use this filter to perform a containment test on strings, + sequences, mappings, or objects implementing the ``Traversable`` interface. + +To perform a negative test, use the ``not in`` operator: + +.. code-block:: twig + + {% if 1 not in [1, 2, 3] %} + + {# is equivalent to #} + {% if not (1 in [1, 2, 3]) %} + +Test Operator +~~~~~~~~~~~~~ + +The ``is`` operator performs tests. Tests can be used to test a variable against +a common expression. The right operand is name of the test: + +.. code-block:: twig + + {# find out if a variable is odd #} + + {{ name is odd }} + +Tests can accept arguments too: + +.. code-block:: twig + + {% if post.status is constant('Post::PUBLISHED') %} + +Tests can be negated by using the ``is not`` operator: + +.. code-block:: twig + + {% if post.status is not constant('Post::PUBLISHED') %} + + {# is equivalent to #} + {% if not (post.status is constant('Post::PUBLISHED')) %} + +Go to the :doc:`tests` page to learn more about the built-in +tests. + +Other Operators +~~~~~~~~~~~~~~~ + +The following operators don't fit into any of the other categories: + +* ``|``: Applies a filter. + +* ``..``: Creates a sequence based on the operand before and after the operator + (this is syntactic sugar for the :doc:`range` function): + + .. code-block:: twig + + {% for i in 1..5 %}{{ i }}{% endfor %} + + {# is equivalent to #} + {% for i in range(1, 5) %}{{ i }}{% endfor %} + + Note that you must use parentheses when combining it with the filter operator + due to the :ref:`operator precedence rules `: + + .. code-block:: twig + + (1..5)|join(', ') + +* ``~``: Converts all operands into strings and concatenates them. ``{{ "Hello + " ~ name ~ "!" }}`` would return (assuming ``name`` is ``'John'``) ``Hello + John!``. + +* ``.``, ``[]``: Gets an attribute of a variable. + +* ``?:``: The ternary operator: + + .. code-block:: twig + + {{ foo ? 'yes' : 'no' }} + {{ foo ?: 'no' }} is the same as {{ foo ? foo : 'no' }} + {{ foo ? 'yes' }} is the same as {{ foo ? 'yes' : '' }} + +* ``??``: The null-coalescing operator: + + .. code-block:: twig + + {# returns the value of foo if it is defined and not null, 'no' otherwise #} + {{ foo ?? 'no' }} + +* ``...``: The spread operator can be used to expand sequences or mappings (it + cannot be used to expand the arguments of a function call): + + .. code-block:: twig + + {% set numbers = [1, 2, ...moreNumbers] %} + {% set ratings = {'foo': 10, 'bar': 5, ...moreRatings} %} + +Operators +~~~~~~~~~ + +Twig uses operators to perform various operations within templates. +Understanding the precedence of these operators is crucial for writing correct +and efficient Twig templates. + +The operator precedence rules are as follows, with the lowest-precedence +operators listed first: + +============================= =================================== ===================================================== +Operator Score of precedence Description +============================= =================================== ===================================================== +``?:`` 0 Ternary operator, conditional statement +``or`` 10 Logical OR operation between two boolean expressions +``and`` 15 Logical AND operation between two boolean expressions +``b-or`` 16 Bitwise OR operation on integers +``b-xor`` 17 Bitwise XOR operation on integers +``b-and`` 18 Bitwise AND operation on integers +``==``, ``!=``, ``<=>``, 20 Comparison operators +``<``, ``>``, ``>=``, +``<=``, ``not in``, ``in``, +``matches``, ``starts with``, +``ends with``, ``has some``, +``has every`` +``..`` 25 Range of values +``+``, ``-`` 30 Addition and subtraction on numbers +``~`` 40 String concatenation +``not`` 50 Negates a statement +``*``, ``/``, ``//``, ``%`` 60 Arithmetic operations on numbers +``is``, ``is not`` 100 Tests +``**`` 200 Raises a number to the power of another +``??`` 300 Default value when a variable is null +``+``, ``-`` 500 Unary operations on numbers +``|``,``[]``,``.`` - Filters, sequence, mapping, and attribute access +============================= =================================== ===================================================== + +Without using any parentheses, the operator precedence rules are used to +determine how to convert the code to PHP: + +.. code-block:: twig + + {{ 6 b-and 2 or 6 b-and 16 }} + + {# it is converted to the following PHP code: (6 & 2) || (6 & 16) #} + +Change the default precedence by explicitly grouping expressions with parentheses: + +.. code-block:: twig + + {% set greeting = 'Hello ' %} + {% set name = 'Fabien' %} + + {{ greeting ~ name|lower }} {# Hello fabien #} + + {# use parenthesis to change precedence #} + {{ (greeting ~ name)|lower }} {# hello fabien #} + +.. _templates-whitespace-control: + +Whitespace Control +------------------ + +The first newline after a template tag is removed automatically (like in PHP). +Whitespace is not further modified by the template engine, so each whitespace +(spaces, tabs, newlines etc.) is returned unchanged. + +You can also control whitespace on a per tag level. By using the whitespace +control modifiers on your tags, you can trim leading and or trailing whitespace. + +Twig supports two modifiers: + +* *Whitespace trimming* via the ``-`` modifier: Removes all whitespace + (including newlines); + +* *Line whitespace trimming* via the ``~`` modifier: Removes all whitespace + (excluding newlines). Using this modifier on the right disables the default + removal of the first newline inherited from PHP. + +The modifiers can be used on either side of the tags like in ``{%-`` or ``-%}`` +and they consume all whitespace for that side of the tag. It is possible to use +the modifiers on one side of a tag or on both sides: + +.. code-block:: html+twig + + {% set value = 'no spaces' %} + {#- No leading/trailing whitespace -#} + {%- if true -%} + {{- value -}} + {%- endif -%} + {# output 'no spaces' #} + +
  • + {{ value }}
  • + {# outputs '
  • \n no spaces
  • ' #} + +
  • + {{- value }}
  • + {# outputs '
  • no spaces
  • ' #} + +
  • + {{~ value }}
  • + {# outputs '
  • \nno spaces
  • ' #} + +.. tip:: + + In addition to the whitespace modifiers, Twig also has a ``spaceless`` filter + that removes whitespace **between HTML tags**: + + .. code-block:: html+twig + + {% apply spaceless %} +
    + foo bar +
    + {% endapply %} + + {# output will be
    foo bar
    #} + +Extensions +---------- + +Twig can be extended. If you want to create your own extensions, read the +:ref:`Creating an Extension ` chapter. + +.. _`Twig bundle`: https://github.com/uhnomoli/PHP-Twig.tmbundle +.. _`vim-twig plugin`: https://github.com/lumiliet/vim-twig +.. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin +.. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language +.. _`Twig syntax mode`: https://github.com/bobthecow/Twig-HTML.mode +.. _`other Twig syntax mode`: https://github.com/muxx/Twig-HTML.mode +.. _`Notepad++ Twig Highlighter`: https://github.com/Banane9/notepadplusplus-twig +.. _`web-mode.el`: https://web-mode.org/ +.. _`regular expressions`: https://www.php.net/manual/en/pcre.pattern.php +.. _`PHP-twig for atom`: https://github.com/reesef/php-twig +.. _`TwigFiddle`: https://twigfiddle.com/ +.. _`Twig pack`: https://marketplace.visualstudio.com/items?itemName=bajdzis.vscode-twig-pack +.. _`Modern Twig`: https://marketplace.visualstudio.com/items?itemName=Stanislav.vscode-twig +.. _`Twig Language Server`: https://github.com/kaermorchen/twig-language-server/tree/master/packages/language-server +.. _`Twiggy`: https://marketplace.visualstudio.com/items?itemName=moetelo.twiggy diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/constant.rst b/upload/system/storage/vendor/twig/twig/doc/tests/constant.rst new file mode 100644 index 000000000..448c238bc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/constant.rst @@ -0,0 +1,19 @@ +``constant`` +============ + +``constant`` checks if a variable has the exact same value as a constant. You +can use either global constants or class constants: + +.. code-block:: twig + + {% if post.status is constant('Post::PUBLISHED') %} + the status attribute is exactly the same as Post::PUBLISHED + {% endif %} + +You can test constants from object instances as well: + +.. code-block:: twig + + {% if post.status is constant('PUBLISHED', post) %} + the status attribute is exactly the same as Post::PUBLISHED + {% endif %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/defined.rst b/upload/system/storage/vendor/twig/twig/doc/tests/defined.rst new file mode 100644 index 000000000..234a28988 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/defined.rst @@ -0,0 +1,30 @@ +``defined`` +=========== + +``defined`` checks if a variable is defined in the current context. This is very +useful if you use the ``strict_variables`` option: + +.. code-block:: twig + + {# defined works with variable names #} + {% if foo is defined %} + ... + {% endif %} + + {# and attributes on variables names #} + {% if foo.bar is defined %} + ... + {% endif %} + + {% if foo['bar'] is defined %} + ... + {% endif %} + +When using the ``defined`` test on an expression that uses variables in some +method calls, be sure that they are all defined first: + +.. code-block:: twig + + {% if var is defined and foo.method(var) is defined %} + ... + {% endif %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/divisibleby.rst b/upload/system/storage/vendor/twig/twig/doc/tests/divisibleby.rst new file mode 100644 index 000000000..8032d349a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/divisibleby.rst @@ -0,0 +1,10 @@ +``divisible by`` +================ + +``divisible by`` checks if a variable is divisible by a number: + +.. code-block:: twig + + {% if loop.index is divisible by(3) %} + ... + {% endif %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/empty.rst b/upload/system/storage/vendor/twig/twig/doc/tests/empty.rst new file mode 100644 index 000000000..3abdb8bbd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/empty.rst @@ -0,0 +1,18 @@ +``empty`` +========= + +``empty`` checks if a variable is an empty string, an empty sequence, an empty +mapping, exactly ``false``, or exactly ``null``. + +For objects that implement the ``Countable`` interface, ``empty`` will check the +return value of the ``count()`` method. + +For objects that implement the ``__toString()`` magic method (and not ``Countable``), +it will check if an empty string is returned. + +.. code-block:: twig + + {% if foo is empty %} + ... + {% endif %} + diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/even.rst b/upload/system/storage/vendor/twig/twig/doc/tests/even.rst new file mode 100644 index 000000000..2de0de2f3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/even.rst @@ -0,0 +1,12 @@ +``even`` +======== + +``even`` returns ``true`` if the given number is even: + +.. code-block:: twig + + {{ var is even }} + +.. seealso:: + + :doc:`odd<../tests/odd>` diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/index.rst b/upload/system/storage/vendor/twig/twig/doc/tests/index.rst new file mode 100644 index 000000000..c63208ee7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/index.rst @@ -0,0 +1,15 @@ +Tests +===== + +.. toctree:: + :maxdepth: 1 + + constant + defined + divisibleby + empty + even + iterable + null + odd + sameas diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/iterable.rst b/upload/system/storage/vendor/twig/twig/doc/tests/iterable.rst new file mode 100644 index 000000000..8c83efcd8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/iterable.rst @@ -0,0 +1,16 @@ +``iterable`` +============ + +``iterable`` checks if a variable is an array or a traversable object: + +.. code-block:: twig + + {# evaluates to true if the users variable is iterable #} + {% if users is iterable %} + {% for user in users %} + Hello {{ user }}! + {% endfor %} + {% else %} + {# users is probably a string #} + Hello {{ users }}! + {% endif %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/mapping.rst b/upload/system/storage/vendor/twig/twig/doc/tests/mapping.rst new file mode 100644 index 000000000..ab90cd6c1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/mapping.rst @@ -0,0 +1,14 @@ +``mapping`` +=========== + +``mapping`` checks if a variable is a mapping: + +.. code-block:: twig + + {% set users = {alice: "Alice Dupond", bob: "Bob Smith"} %} + {# evaluates to true if the users variable is a mapping #} + {% if users is mapping %} + {% for key, user in users %} + {{ key }}: {{ user }}; + {% endfor %} + {% endif %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/null.rst b/upload/system/storage/vendor/twig/twig/doc/tests/null.rst new file mode 100644 index 000000000..9ed93f6bb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/null.rst @@ -0,0 +1,12 @@ +``null`` +======== + +``null`` returns ``true`` if the variable is ``null``: + +.. code-block:: twig + + {{ var is null }} + +.. note:: + + ``none`` is an alias for ``null``. diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/odd.rst b/upload/system/storage/vendor/twig/twig/doc/tests/odd.rst new file mode 100644 index 000000000..27fe7e4dc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/odd.rst @@ -0,0 +1,12 @@ +``odd`` +======= + +``odd`` returns ``true`` if the given number is odd: + +.. code-block:: twig + + {{ var is odd }} + +.. seealso:: + + :doc:`even<../tests/even>` diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/sameas.rst b/upload/system/storage/vendor/twig/twig/doc/tests/sameas.rst new file mode 100644 index 000000000..c09297114 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/sameas.rst @@ -0,0 +1,11 @@ +``same as`` +=========== + +``same as`` checks if a variable is the same as another variable. +This is equivalent to ``===`` in PHP: + +.. code-block:: twig + + {% if foo.attribute is same as(false) %} + the foo attribute really is the 'false' PHP value + {% endif %} diff --git a/upload/system/storage/vendor/twig/twig/doc/tests/sequence.rst b/upload/system/storage/vendor/twig/twig/doc/tests/sequence.rst new file mode 100644 index 000000000..0ae47a387 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/doc/tests/sequence.rst @@ -0,0 +1,14 @@ +``sequence`` +============ + +``sequence`` checks if a variable is a sequence: + +.. code-block:: twig + + {% set users = ["Alice", "Bob"] %} + {# evaluates to true if the users variable is a sequence #} + {% if users is sequence %} + {% for user in users %} + Hello {{ user }}! + {% endfor %} + {% endif %} diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/cache-extra/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/.gitignore b/upload/system/storage/vendor/twig/twig/extra/cache-extra/.gitignore new file mode 100644 index 000000000..76367ee5b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/CacheExtension.php b/upload/system/storage/vendor/twig/twig/extra/cache-extra/CacheExtension.php new file mode 100644 index 000000000..5cf849dc6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/CacheExtension.php @@ -0,0 +1,25 @@ +cache = $cache; + } + + public function getCache(): CacheInterface + { + return $this->cache; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/LICENSE b/upload/system/storage/vendor/twig/twig/extra/cache-extra/LICENSE new file mode 100644 index 000000000..99c6bdf35 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2021-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/Node/CacheNode.php b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Node/CacheNode.php new file mode 100644 index 000000000..7308b6bc9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Node/CacheNode.php @@ -0,0 +1,71 @@ +setAttribute('raw', true); + + $nodes = ['key' => $key, 'body' => $body]; + if (null !== $ttl) { + $nodes['ttl'] = $ttl; + } + if (null !== $tags) { + $nodes['tags'] = $tags; + } + + parent::__construct($nodes, [], $lineno); + } + + public function compile(Compiler $compiler): void + { + $compiler + ->addDebugInfo($this) + ->raw('$this->env->getRuntime(\'Twig\Extra\Cache\CacheRuntime\')->getCache()->get(') + ->subcompile($this->getNode('key')) + ->raw(", function (\Symfony\Contracts\Cache\ItemInterface \$item) use (\$context, \$macros, \$blocks) {\n") + ->indent() + ; + + if ($this->hasNode('tags')) { + $compiler + ->write('$item->tag(') + ->subcompile($this->getNode('tags')) + ->raw(");\n") + ; + } + + if ($this->hasNode('ttl')) { + $compiler + ->write('$item->expiresAfter(') + ->subcompile($this->getNode('ttl')) + ->raw(");\n") + ; + } + + $compiler + ->write('return ') + ->subcompile($this->getNode('body')) + ->raw(";\n") + ->outdent() + ->write("})\n") + ; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/README.md b/upload/system/storage/vendor/twig/twig/extra/cache-extra/README.md new file mode 100644 index 000000000..9db5195cc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/README.md @@ -0,0 +1,9 @@ +Cache Extension +=============== + +This package is a Twig extension that provides integration with the Symfony +Cache component. + +It provides a single [`cache`][1] tag that allows to cache template fragments. + +[1]: https://twig.symfony.com/cache diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache.test b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache.test new file mode 100644 index 000000000..734f106cb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache.test @@ -0,0 +1,22 @@ +--TEST-- +"cache" tag +--TEMPLATE-- +{% set foo = "bar" %} +{% set value1 %} + {% cache "test;v1" ttl(3) %} + {% set foo = "bar1" %} + {{ random(1, 1000000) }} + {% endcache %} +{% endset %} +{% set value2 %} + {% cache "test;v1" ttl(3) %} + {{ random(1, 1000000) }} + {% endcache %} +{% endset %} +{{ value1 == value2 ? 'OK' : 'KO' }} +{{ foo == "bar" ? 'OK' : 'KO' }} +--DATA-- +return [] +--EXPECT-- +OK +OK diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache_complex.test b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache_complex.test new file mode 100644 index 000000000..206865088 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache_complex.test @@ -0,0 +1,15 @@ +--TEST-- +"cache" tag +--TEMPLATE-- +{% cache 'test_%s_%s'|format(10, 10000) ttl(36000) %} + {% set content %} + ok + {% endset %} + {% apply upper %} + {{ content }} + {% endapply %} +{% endcache %} +--DATA-- +return [] +--EXPECT-- +OK diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache_with_blocks.test b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache_with_blocks.test new file mode 100644 index 000000000..79721d7fc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/cache_with_blocks.test @@ -0,0 +1,15 @@ +--TEST-- +"cache" tag +--TEMPLATE-- +{% extends "layout.twig" %} +{% block bar %} + {% cache "foo" %} + {%- block content %}FOO{% endblock %} + {% endcache %} +{% endblock %} +--TEMPLATE(layout.twig)-- +{% block content %}{% endblock %} +--DATA-- +return [] +--EXPECT-- +FOO diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/macro.test b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/macro.test new file mode 100644 index 000000000..4a8afb5cc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/Fixtures/macro.test @@ -0,0 +1,22 @@ +--TEST-- +macro call inside "cache" tag +--TEMPLATE-- +{% macro macro_out(bar) %}{{ bar }}{% endmacro %} +1 +{% cache "testmacro1" ttl(3) %} + {%~ macro macro_in(bar) %}{{ bar }}{% endmacro %} + 2 + {{ _self.macro_out(3) }} + {{ _self.macro_in(4) }} +{% endcache %} +5 +{{ _self.macro_in(6) }} +--DATA-- +return [] +--EXPECT-- +1 + 2 + 3 + 4 +5 +6 diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/FunctionalTest.php b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/FunctionalTest.php new file mode 100644 index 000000000..0ae24436e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/FunctionalTest.php @@ -0,0 +1,91 @@ +createEnvironment(['index' => '{% cache "city;v1" %}{{- city -}}{% endcache %}'], $cache); + + $this->assertSame('Paris', $twig->render('index', ['city' => 'Paris'])); + $value = $cache->get('city;v1', function () { throw new \RuntimeException('Key should be in the cache'); }); + $this->assertSame('Paris', $value); + } + + public function testTtlNoArgs() + { + $twig = $this->createEnvironment(['index' => '{% cache "ttl_no_args" ttl() %}{% endcache %}']); + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('The "ttl" modifier takes exactly one argument (0 given) in "index" at line 1.'); + $twig->render('index'); + } + + public function testTtlTooManyArgs() + { + $twig = $this->createEnvironment(['index' => '{% cache "ttl_too_many_args" ttl(0, 1) %}{% endcache %}']); + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('The "ttl" modifier takes exactly one argument (2 given) in "index" at line 1.'); + $twig->render('index'); + } + + public function testTagsNoArgs() + { + $twig = $this->createEnvironment(['index' => '{% cache "tags_no_args" tags() %}{% endcache %}']); + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('The "tags" modifier takes exactly one argument (0 given) in "index" at line 1.'); + $twig->render('index'); + } + + public function testTagsTooManyArgs() + { + $twig = $this->createEnvironment(['index' => '{% cache "tags_too_many_args" tags(["foo"], 1) %}{% endcache %}']); + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('The "tags" modifier takes exactly one argument (2 given) in "index" at line 1.'); + $twig->render('index'); + } + + private function createEnvironment(array $templates, ?ArrayAdapter $cache = null): Environment + { + $twig = new Environment(new ArrayLoader($templates)); + $cache = $cache ?? new ArrayAdapter(); + $twig->addExtension(new CacheExtension()); + $twig->addRuntimeLoader(new class($cache) implements RuntimeLoaderInterface { + private $cache; + + public function __construct(CacheInterface $cache) + { + $this->cache = $cache; + } + + public function load($class) + { + if (CacheRuntime::class === $class) { + return new CacheRuntime($this->cache); + } + } + }); + + return $twig; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/IntegrationTest.php new file mode 100644 index 000000000..8e216aaa5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/Tests/IntegrationTest.php @@ -0,0 +1,47 @@ +parser->getStream(); + $expressionParser = $this->parser->getExpressionParser(); + $key = $expressionParser->parseExpression(); + + $ttl = null; + $tags = null; + while ($stream->test(Token::NAME_TYPE)) { + $k = $stream->getCurrent()->getValue(); + $stream->next(); + $args = $expressionParser->parseArguments(); + + switch ($k) { + case 'ttl': + if (1 !== \count($args)) { + throw new SyntaxError(\sprintf('The "ttl" modifier takes exactly one argument (%d given).', \count($args)), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } + $ttl = $args->getNode('0'); + break; + case 'tags': + if (1 !== \count($args)) { + throw new SyntaxError(\sprintf('The "tags" modifier takes exactly one argument (%d given).', \count($args)), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } + $tags = $args->getNode('0'); + break; + default: + throw new SyntaxError(\sprintf('Unknown "%s" configuration.', $k), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } + } + + $stream->expect(Token::BLOCK_END_TYPE); + $body = $this->parser->subparse([$this, 'decideCacheEnd'], true); + $stream->expect(Token::BLOCK_END_TYPE); + + $body = new CacheNode($key, $ttl, $tags, $body, $token->getLine()); + + return new PrintNode(new RawFilter($body), $token->getLine()); + } + + public function decideCacheEnd(Token $token): bool + { + return $token->test('endcache'); + } + + public function getTag(): string + { + return 'cache'; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/composer.json b/upload/system/storage/vendor/twig/twig/extra/cache-extra/composer.json new file mode 100644 index 000000000..74e377e95 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/composer.json @@ -0,0 +1,31 @@ +{ + "name": "twig/cache-extra", + "type": "library", + "description": "A Twig extension for Symfony Cache", + "keywords": ["twig", "html", "cache"], + "homepage": "https://twig.symfony.com", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/cache": "^5.4|^6.4|^7.0", + "twig/twig": "^3.12" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "autoload": { + "psr-4" : { "Twig\\Extra\\Cache\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cache-extra/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/cache-extra/phpunit.xml.dist new file mode 100644 index 000000000..0cfdd7296 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cache-extra/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/.gitignore b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/.gitignore new file mode 100644 index 000000000..76367ee5b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/CssInlinerExtension.php b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/CssInlinerExtension.php new file mode 100644 index 000000000..2ceb2e085 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/CssInlinerExtension.php @@ -0,0 +1,39 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\CssInliner; + +use TijsVerkoyen\CssToInlineStyles\CssToInlineStyles; +use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; + +class CssInlinerExtension extends AbstractExtension +{ + public function getFilters() + { + return [ + new TwigFilter('inline_css', [self::class, 'inlineCss'], ['is_safe' => ['all']]), + ]; + } + + /** + * @internal + */ + public static function inlineCss(string $body, string ...$css): string + { + static $inliner; + if (null === $inliner) { + $inliner = new CssToInlineStyles(); + } + + return $inliner->convert($body, implode("\n", $css)); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/LICENSE b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/LICENSE new file mode 100644 index 000000000..f37c76b59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/README.md b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/README.md new file mode 100644 index 000000000..089a189a0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/README.md @@ -0,0 +1,8 @@ +Twig CssInliner Extension +========================= + +This package is a Twig extension that provides the following: + + * [`inline_css`][1] filter: inlines CSS styles in HTML documents. + +[1]: https://twig.symfony.com/inline_css diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Resources/functions.php b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Resources/functions.php new file mode 100644 index 000000000..60305e231 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Resources/functions.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\CssInliner; + +/** + * @internal + * + * @deprecated since Twig 3.9.0 + */ +function twig_inline_css(string $body, string ...$css): string +{ + trigger_deprecation('twig/cssinliner-extra', '3.9.0', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CssInlinerExtension::inlineCss($body, ...$css); +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Tests/Fixtures/inline_css.test b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Tests/Fixtures/inline_css.test new file mode 100644 index 000000000..7142b9e71 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Tests/Fixtures/inline_css.test @@ -0,0 +1,50 @@ +--TEST-- +"inline_css" filter +--TEMPLATE-- +{% apply inline_css %} +

    Great!

    +{% endapply %} + + +{% apply inline_css(source('css')) %} +

    Great!

    +{% endapply %} + + +{% apply inline_css(source('css'), source('more_css')) %} +

    Great!

    +{% endapply %} + + +{% apply inline_css(source('css') ~ source('more_css')) %} +

    Great!

    +{% endapply %} + + +{{ include('html')|inline_css(source('css') ~ source('more_css')) }} +--TEMPLATE(html)-- +

    Great!

    +--TEMPLATE(css)-- +p { color: red } +--TEMPLATE(more_css)-- +p { color: blue } +--DATA-- +return [] +--EXPECT-- + + + +

    Great!

    + + + +

    Great!

    + + +

    Great!

    + + +

    Great!

    + + +

    Great!

    diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Tests/IntegrationTest.php new file mode 100644 index 000000000..5ab6ec9b4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/Tests/IntegrationTest.php @@ -0,0 +1,30 @@ +assertSame(CssInlinerExtension::inlineCss('

    body

    ', 'p { color: red }'), twig_inline_css('

    body

    ', 'p { color: red }')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/composer.json b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/composer.json new file mode 100644 index 000000000..66b4ace78 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/composer.json @@ -0,0 +1,33 @@ +{ + "name": "twig/cssinliner-extra", + "type": "library", + "description": "A Twig extension to allow inlining CSS", + "keywords": ["twig", "css", "inlining"], + "homepage": "https://twig.symfony.com", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", + "tijsverkoyen/css-to-inline-styles": "^2.0", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "autoload": { + "files": [ "Resources/functions.php" ], + "psr-4" : { "Twig\\Extra\\CssInliner\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/phpunit.xml.dist new file mode 100644 index 000000000..de3fc99ea --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/cssinliner-extra/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/html-extra/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/.gitignore b/upload/system/storage/vendor/twig/twig/extra/html-extra/.gitignore new file mode 100644 index 000000000..76367ee5b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Cva.php b/upload/system/storage/vendor/twig/twig/extra/html-extra/Cva.php new file mode 100644 index 000000000..a77d7dfdf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Cva.php @@ -0,0 +1,129 @@ + + */ +final class Cva +{ + /** + * @var list + */ + private array $base; + + /** + * @param string|list $base The base classes to apply to the component + */ + public function __construct( + string|array $base = [], + /** + * The variants to apply based on recipes. + * + * Format: [variantCategory => [variantName => classes]] + * + * Example: + * 'colors' => [ + * 'primary' => 'bleu-8000', + * 'danger' => 'red-800 text-bold', + * ], + * 'size' => [...], + * + * @var array>> + */ + private array $variants = [], + + /** + * The compound variants to apply based on recipes. + * + * Format: [variantsCategory => ['variantName', 'variantName'], class: classes] + * + * Example: + * [ + * 'colors' => ['primary'], + * 'size' => ['small'], + * 'class' => 'text-red-500', + * ], + * [ + * 'size' => ['large'], + * 'class' => 'font-weight-500', + * ] + * + * @var array>> + */ + private array $compoundVariants = [], + + /** + * The default variants to apply if specific recipes aren't provided. + * + * Format: [variantCategory => variantName] + * + * Example: + * 'colors' => 'primary', + * + * @var array + */ + private array $defaultVariants = [], + ) { + $this->base = (array) $base; + } + + public function apply(array $recipes, ?string ...$additionalClasses): string + { + $classes = $this->base; + + // Resolve recipes against variants + foreach ($recipes as $recipeName => $recipeValue) { + if (\is_bool($recipeValue)) { + $recipeValue = $recipeValue ? 'true' : 'false'; + } + $recipeClasses = $this->variants[$recipeName][$recipeValue] ?? []; + $classes = [...$classes, ...(array) $recipeClasses]; + } + + // Resolve compound variants + foreach ($this->compoundVariants as $compound) { + $compoundClasses = $this->resolveCompoundVariant($compound, $recipes) ?? []; + $classes = [...$classes, ...$compoundClasses]; + } + + // Apply default variants if specific recipes aren't provided + foreach ($this->defaultVariants as $defaultVariantName => $defaultVariantValue) { + if (!isset($recipes[$defaultVariantName])) { + $variantClasses = $this->variants[$defaultVariantName][$defaultVariantValue] ?? []; + $classes = [...$classes, ...(array) $variantClasses]; + } + } + $classes = [...$classes, ...array_values($additionalClasses)]; + + $classes = implode(' ', array_filter($classes, 'is_string')); + $classes = preg_split('#\s+#', $classes, -1, \PREG_SPLIT_NO_EMPTY) ?: []; + + return implode(' ', array_unique($classes)); + } + + private function resolveCompoundVariant(array $compound, array $recipes): array + { + foreach ($compound as $compoundName => $compoundValues) { + if ('class' === $compoundName) { + continue; + } + if (!isset($recipes[$compoundName]) || !\in_array($recipes[$compoundName], (array) $compoundValues)) { + return []; + } + } + + return (array) ($compound['class'] ?? []); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/HtmlExtension.php b/upload/system/storage/vendor/twig/twig/extra/html-extra/HtmlExtension.php new file mode 100644 index 000000000..e1766ad45 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/HtmlExtension.php @@ -0,0 +1,127 @@ +mimeTypes = $mimeTypes; + } + + public function getFilters(): array + { + return [ + new TwigFilter('data_uri', [$this, 'dataUri']), + ]; + } + + public function getFunctions(): array + { + return [ + new TwigFunction('html_classes', [self::class, 'htmlClasses']), + new TwigFunction('html_cva', [self::class, 'htmlCva']), + ]; + } + + /** + * Creates a data URI (RFC 2397). + * + * Length validation is not performed on purpose, validation should + * be done before calling this filter. + * + * @return string The generated data URI + * + * @internal + */ + public function dataUri(string $data, ?string $mime = null, array $parameters = []): string + { + $repr = 'data:'; + + if (null === $mime) { + if (null === $this->mimeTypes) { + $this->mimeTypes = new MimeTypes(); + } + + $tmp = tempnam(sys_get_temp_dir(), 'mime'); + file_put_contents($tmp, $data); + try { + if (null === $mime = $this->mimeTypes->guessMimeType($tmp)) { + $mime = 'text/plain'; + } + } finally { + @unlink($tmp); + } + } + $repr .= $mime; + + foreach ($parameters as $key => $value) { + $repr .= ';'.$key.'='.rawurlencode($value); + } + + if (0 === strpos($mime, 'text/')) { + $repr .= ','.rawurlencode($data); + } else { + $repr .= ';base64,'.base64_encode($data); + } + + return $repr; + } + + /** + * @internal + */ + public static function htmlClasses(...$args): string + { + $classes = []; + foreach ($args as $i => $arg) { + if (\is_string($arg)) { + $classes[] = $arg; + } elseif (\is_array($arg)) { + foreach ($arg as $class => $condition) { + if (!\is_string($class)) { + throw new RuntimeError(\sprintf('The html_classes function argument %d (key %d) should be a string, got "%s".', $i, $class, \gettype($class))); + } + if (!$condition) { + continue; + } + $classes[] = $class; + } + } else { + throw new RuntimeError(\sprintf('The html_classes function argument %d should be either a string or an array, got "%s".', $i, \gettype($arg))); + } + } + + return implode(' ', array_unique(array_filter($classes, static function ($v) { return '' !== $v; }))); + } + + /** + * @param string|list $base + * @param array> $variants + * @param array>> $compoundVariants + * @param array $defaultVariant + * + * @internal + */ + public static function htmlCva(array|string $base = [], array $variants = [], array $compoundVariants = [], array $defaultVariant = []): Cva + { + return new Cva($base, $variants, $compoundVariants, $defaultVariant); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/LICENSE b/upload/system/storage/vendor/twig/twig/extra/html-extra/LICENSE new file mode 100644 index 000000000..f37c76b59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/README.md b/upload/system/storage/vendor/twig/twig/extra/html-extra/README.md new file mode 100644 index 000000000..9cd51fe28 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/README.md @@ -0,0 +1,16 @@ +Twig HTML Extension +=================== + +This package is a Twig extension that provides the following: + + * [`data_uri`][1] filter: generates a URL using the data scheme as defined in + RFC 2397; + + * [`html_classes`][2] function: returns a string by conditionally joining class + names together. + + * [`html_cva`][3] function: returns a `Cva` object to handle class variants. + +[1]: https://twig.symfony.com/data_uri +[2]: https://twig.symfony.com/html_classes +[3]: https://twig.symfony.com/html_cva diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Resources/functions.php b/upload/system/storage/vendor/twig/twig/extra/html-extra/Resources/functions.php new file mode 100644 index 000000000..ca18af1d3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Resources/functions.php @@ -0,0 +1,24 @@ +assertEquals($expected, $recipeClass->apply($recipes)); + } + + public function testApply() + { + $recipe = new Cva('font-semibold border rounded', [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], [ + [ + 'colors' => ['primary'], + 'sizes' => ['sm'], + 'class' => 'text-red-500', + ], + ]); + + $this->assertEquals('font-semibold border rounded text-primary text-sm text-red-500', $recipe->apply(['colors' => 'primary', 'sizes' => 'sm'])); + } + + public function testApplyWithNullString() + { + $recipe = new Cva('font-semibold border rounded', [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], [ + [ + 'colors' => ['primary'], + 'sizes' => ['sm'], + 'class' => 'text-red-500', + ], + ]); + + $this->assertEquals('font-semibold border rounded text-primary text-sm text-red-500 flex justify-center', $recipe->apply(['colors' => 'primary', 'sizes' => 'sm'], 'flex', null, 'justify-center')); + } + + public static function recipeProvider(): iterable + { + yield 'base null' => [ + ['variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ]], + ['colors' => 'primary', 'sizes' => 'sm'], + 'text-primary text-sm', + ]; + + yield 'base empty' => [ + [ + 'base' => '', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ]], + ['colors' => 'primary', 'sizes' => 'sm'], + 'text-primary text-sm', + ]; + + yield 'base array' => [ + [ + 'base' => ['font-semibold', 'border', 'rounded'], + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ]], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm', + ]; + + yield 'no recipes match' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + ], + ['colors' => 'red', 'sizes' => 'test'], + 'font-semibold border rounded', + ]; + + yield 'simple variants' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm', + ]; + + yield 'simple variants as array' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => ['text-primary', 'uppercase'], + 'secondary' => ['text-secondary', 'uppercase'], + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary uppercase text-sm', + ]; + + yield 'simple variants with custom' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + ], + ['colors' => 'secondary', 'sizes' => 'md'], + 'font-semibold border rounded text-secondary text-md', + ]; + + yield 'compound variants' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => 'primary', + 'sizes' => ['sm'], + 'class' => 'text-red-100', + ], + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm text-red-100', + ]; + + yield 'compound variants with true' => [ + [ + 'base' => 'button', + 'variants' => [ + 'colors' => [ + 'blue' => 'btn-blue', + 'red' => 'btn-red', + ], + 'disabled' => [ + 'true' => 'disabled', + ], + ], + 'compounds' => [ + [ + 'colors' => 'blue', + 'disabled' => ['true'], + 'class' => 'font-bold', + ], + ], + ], + ['colors' => 'blue', 'disabled' => 'true'], + 'button btn-blue disabled font-bold', + ]; + + yield 'compound variants as array' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => ['primary'], + 'sizes' => ['sm'], + 'class' => ['text-red-900', 'bold'], + ], + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm text-red-900 bold', + ]; + + yield 'multiple compound variants' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => ['primary'], + 'sizes' => ['sm'], + 'class' => 'text-red-300', + ], + [ + 'colors' => ['primary'], + 'sizes' => ['md'], + 'class' => 'text-blue-300', + ], + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm text-red-300', + ]; + + yield 'compound with multiple variants' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => ['primary', 'secondary'], + 'sizes' => ['sm'], + 'class' => 'text-red-800', + ], + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm text-red-800', + ]; + + yield 'compound doesn\'t match' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => ['danger', 'secondary'], + 'sizes' => ['sm'], + 'class' => 'text-red-500', + ], + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm', + ]; + + yield 'default variables' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + 'rounded' => [ + 'sm' => 'rounded-sm', + 'md' => 'rounded-md', + 'lg' => 'rounded-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => ['danger', 'secondary'], + 'sizes' => 'sm', + 'class' => 'text-red-500', + ], + ], + 'defaultVariants' => [ + 'colors' => 'primary', + 'sizes' => 'sm', + 'rounded' => 'md', + ], + ], + ['colors' => 'primary', 'sizes' => 'sm'], + 'font-semibold border rounded text-primary text-sm rounded-md', + ]; + + yield 'default variables all overwrite' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + 'rounded' => [ + 'sm' => 'rounded-sm', + 'md' => 'rounded-md', + 'lg' => 'rounded-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => ['danger', 'secondary'], + 'sizes' => ['sm'], + 'class' => 'text-red-500', + ], + ], + 'defaultVariants' => [ + 'colors' => 'primary', + 'sizes' => 'sm', + 'rounded' => 'md', + ], + ], + ['colors' => 'primary', 'sizes' => 'sm', 'rounded' => 'lg'], + 'font-semibold border rounded text-primary text-sm rounded-lg', + ]; + + yield 'default variables without matching variants' => [ + [ + 'base' => 'font-semibold border rounded', + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'md' => 'text-md', + 'lg' => 'text-lg', + ], + 'rounded' => [ + 'sm' => 'rounded-sm', + 'md' => 'rounded-md', + 'lg' => 'rounded-lg', + ], + ], + 'compounds' => [ + [ + 'colors' => ['danger', 'secondary'], + 'sizes' => ['sm'], + 'class' => 'text-red-500', + ], + ], + 'defaultVariants' => [ + 'colors' => 'primary', + 'sizes' => 'sm', + 'rounded' => 'md', + ], + ], + [], + 'font-semibold border rounded text-primary text-sm rounded-md', + ]; + + yield 'default variables with boolean' => [ + [ + 'base' => 'button', + 'variants' => [ + 'colors' => [ + 'blue' => 'btn-blue', + 'red' => 'btn-red', + ], + 'disabled' => [ + 'true' => 'disabled', + 'false' => 'opacity-100', + ], + ], + 'defaultVariants' => [ + 'colors' => 'blue', + 'disabled' => 'false', + ], + ], + [], + 'button btn-blue opacity-100', + ]; + + yield 'boolean string variants true / true' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'true' => 'disable', + ], + ], + ], + ['colors' => 'primary', 'disabled' => true], + 'text-primary disable', + ]; + + yield 'boolean string variants true / false' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'true' => 'disable', + ], + ], + ], + ['colors' => 'primary', 'disabled' => false], + 'text-primary', + ]; + + yield 'boolean string variants false / true' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'false' => 'disable', + ], + ], + ], + ['colors' => 'primary', 'disabled' => true], + 'text-primary', + ]; + + yield 'boolean string variants false / false' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'false' => 'disable', + ], + ], + ], + ['colors' => 'primary', 'disabled' => false], + 'text-primary disable', + ]; + + yield 'boolean string variants missing' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'true' => 'disable', + ], + ], + ], + ['colors' => 'primary'], + 'text-primary', + ]; + + yield 'boolean list variants true' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'true' => ['disable', 'opacity-50'], + ], + ], + ], + ['colors' => 'primary', 'disabled' => true], + 'text-primary disable opacity-50', + ]; + + yield 'boolean list variants false' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'true' => ['disable', 'opacity-50'], + ], + ], + ], + ['colors' => 'primary', 'disabled' => false], + 'text-primary', + ]; + + yield 'boolean list variants missing' => [ + [ + 'variants' => [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary', + ], + 'disabled' => [ + 'true' => ['disable', 'opacity-50'], + ], + ], + ], + ['colors' => 'primary'], + 'text-primary', + ]; + } + + /** + * @dataProvider provideAdditionalClassesCases + */ + public function testAdditionalClasses(string|array $base, array|string $additionals, string $expected) + { + $cva = new Cva($base); + if (!$additionals) { + $this->assertEquals($expected, $cva->apply([])); + } else { + $this->assertEquals($expected, $cva->apply([], ...(array) $additionals)); + } + } + + public static function provideAdditionalClassesCases(): iterable + { + yield 'additionals_are_optional' => [ + '', + 'foo', + 'foo', + ]; + + yield 'additional_are_used' => [ + '', + 'foo', + 'foo', + ]; + + yield 'additionals_are_used' => [ + '', + ['foo', 'bar'], + 'foo bar', + ]; + + yield 'additionals_preserve_order' => [ + ['foo'], + ['bar', 'foo'], + 'foo bar', + ]; + + yield 'additional_are_deduplicated' => [ + '', + ['bar', 'bar'], + 'bar', + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/data_uri.test b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/data_uri.test new file mode 100644 index 000000000..070e713e0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/data_uri.test @@ -0,0 +1,14 @@ +--TEST-- +"data_uri" filter +--TEMPLATE-- +{{ 'foobar#'|data_uri(parameters={charset: "utf-8", foo: "$bar"}) }} +{{ 'foobar'|data_uri(mime="text/html", parameters={charset: "ascii"}) }} + +--DATA-- +return [ + 'sf_logo' => base64_decode('iVBORw0KGgoAAAANSUhEUgAAAG8AAAAlCAQAAADmD3/hAAAE70lEQVRoBd3BAUTdiQMH8C+PR0TEOCIiRsQ4xvgTY4zHGDHGOCIeMY7HccQYESPG8YhjRETEY4wYcYwYMUbEI0bEiEc8Pv/f+9X+3brtrduM9f98ks9QVbNk07b3eGPbit/dyOXnujVHPq2tYSCXlXGrvuSdGZVcPuYcu5hXRnKZqHrq33jnei4LVS/8Wx03czn4w0Xt2rbnxHvj+fGpu5hlE2YtqLqjo+etofzYjDn2ZcfuGrSD3RQsOdHMj82Ki5hJPNazkYI/neiayI/LNRfRSgw41NP1h6YzrXySW37X1HAtX0XVkre6buTrWfNPx7Y0vdJ14r2RRM3nTOQcVRs4tm3b7XwV81h3XzVfy7CO81pGVdwxbcyKnsUUPNKza8N5CznHLB7lm9i2r5JvYdp5W6qJZT2H7nmoaywFL/RcTaz72E7OsYKBfBNtr/JtPHVeLQW7PlhwLSUHeu4lXjrvSj5iGbfzPxrmVVNS8ZuZxIIbRv1h25/GEzUbWuZVDWhqOvJOU9N4YkjDqpbHxlIy54Gqhk3rGiqJOY9VU1LRUI+XzhtPwawz91IwbNu2Z0Yt+qepfMR1XR0NAylp4H5K7uKXRNsre15Zd6xtTkfLX1hX0bLt2JFtm35y3b5jmzYcOnIvBS2HXti36jXWErOop6SG2dhzXi0lT3wwmoJxs+Y07fmUX3KOKbvYN5OCEV2tlKzoGEy0sZiCeT23UrCJ0RS0baWgqu3YVApG7Toykmhh23AKNjFhWMfzlCzrGI73znuRkqpXevZS8rt+HuYfDJh3hDWVxAsdA4kBHaspaDs2mILr+CslDUyloG0rBffxJKfcw2KihVpKZnA/sa5rOFFxaD2J9868s+w95lIyoedZSlb108gnGfYSs4kHuJe4i9spaHubkkk8SUkdtRS0baXgEWo5ZRCbiRaGUnIH9cRd/JK4jbtJvHPmRuIBuqZTcoCHKXmjn3o+wxWsJgZ0rCRWHKikoG0nJZNYSEkdtRS0baXgEe7klGFsJlrklBrqiapDrUTToYEkXjsznpjW814lMegYN1IwpKufO/kMA1hLwTNHhnUspaRtJyWTWEhJHbUUtG2l4D4e55QaFhItckoN9RQ0dVxxqJkeq84sJFb1tFPwUM9QCm7pbyKfYREPU3ALy7iekradlExiISV11FLQtpWCqtcOTaZg2GtHRhItckoN9RTcwDKm0uNXfzfvmqcWTSRmdPA2JUv6OVTJR9Ts2LDiAFuqKajYx9uc0vY6JZNYSEkdtRS0baXkmj1H1j3zzpHpFLTIKTXUU7KHdk6Y9LFDK55oeuvESkra+lnPOa5Z8pdtTQ9UcsoS5nPKglpKRjRNpGTKIwMpWHA3pwyZt2HToqspmfMwp0x6aiQlj/A4H3ijn3oKpvQ3nQvxRNdYvivzuJoPPNTP1RQ818+BgVyASUdW810Zd+h5zhh04HPaKZjS32/5IiNe6to3lu/GsE1d70zk79T93Z663xzqaSSGtPWzazBfZFTTr37Kd+QnTQ2j+ZiKl87cTME8DgyqWNNP16386Fyx54OJRNWmjuuq1vTXyGVg0pETGwb97IlJN+3o71kuC1MOnDiw4Zm2L/lTNZeHUTsuqquRy8agp4592Rs3czkZs6KftplcbkbMea7rY/ueuq2S/w+qfvYfs2ZN+9lYLp3/AoptWBePkE2wAAAAAElFTkSuQmCC'), +] +--EXPECT-- +data:text/plain;charset=utf-8;foo=%24bar,foobar%23 +data:text/html;charset=ascii,%3Cb%3Efoobar%3C%2Fb%3E + diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes.test b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes.test new file mode 100644 index 000000000..65ecaba6a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes.test @@ -0,0 +1,12 @@ +--TEST-- +"html_classes" function +--TEMPLATE-- +{{ html_classes('a', {'b': true, 'c': false}, 'd', false ? 'e', true ? 'f', '0') }} +{% set class_a = 'a' %} +{% set class_b = 'b' %} +{{ html_classes(class_a, {(class_b): true})}} +--DATA-- +return [] +--EXPECT-- +a b d f 0 +a b diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes_with_unsupported_arg.test b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes_with_unsupported_arg.test new file mode 100644 index 000000000..85faed624 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes_with_unsupported_arg.test @@ -0,0 +1,8 @@ +--TEST-- +"html_classes" function +--TEMPLATE-- +{{ html_classes(true) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: The html_classes function argument 0 should be either a string or an array, got "boolean" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes_with_unsupported_key.test b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes_with_unsupported_key.test new file mode 100644 index 000000000..b74748c58 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_classes_with_unsupported_key.test @@ -0,0 +1,8 @@ +--TEST-- +"html_classes" function +--TEMPLATE-- +{{ html_classes(['foo']) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: The html_classes function argument 0 (key 0) should be a string, got "integer" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_cva.test b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_cva.test new file mode 100644 index 000000000..71e16036a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_cva.test @@ -0,0 +1,37 @@ +--TEST-- +"html_cva" function +--TEMPLATE-- +{% set alert = html_cva( + ['alert'], + { + color: { + blue: 'alert-blue', + red: 'alert-red', + green: 'alert-green', + yellow: 'alert-yellow', + }, + size: { + sm: 'alert-sm', + md: 'alert-md', + lg: 'alert-lg', + }, + rounded: { + sm: 'rounded-sm', + md: 'rounded-md', + lg: 'rounded-lg', + } + }, + [{ + color: ['red'], + size: ['lg'], + class: 'font-semibold' + }], + { + rounded: 'md' + } +) %} +{{ alert.apply({color: 'blue', size: 'sm'}) }} +--DATA-- +return [] +--EXPECT-- +alert alert-blue alert-sm rounded-md diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_cva_pass_to_template.test b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_cva_pass_to_template.test new file mode 100644 index 000000000..49b842fac --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/Fixtures/html_cva_pass_to_template.test @@ -0,0 +1,19 @@ +--TEST-- +pass Cva object to template +--TEMPLATE-- +{{ alert.apply({colors: 'primary', sizes: 'sm'}) }} +--DATA-- +return [ + 'alert' => new Twig\Extra\Html\Cva('font-semibold border rounded', [ + 'colors' => [ + 'primary' => 'text-primary', + 'secondary' => 'text-secondary' + ], + 'sizes' => [ + 'sm' => 'text-sm', + 'lg' => 'text-lg' + ] + ]) +]; +--EXPECT-- +font-semibold border rounded text-primary text-sm diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/IntegrationTest.php new file mode 100644 index 000000000..8f464c152 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/Tests/IntegrationTest.php @@ -0,0 +1,30 @@ +assertSame(HtmlExtension::htmlClasses(['charset' => 'utf-8']), twig_html_classes(['charset' => 'utf-8'])); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/composer.json b/upload/system/storage/vendor/twig/twig/extra/html-extra/composer.json new file mode 100644 index 000000000..a5b65d00b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/composer.json @@ -0,0 +1,33 @@ +{ + "name": "twig/html-extra", + "type": "library", + "description": "A Twig extension for HTML", + "keywords": ["twig", "html"], + "homepage": "https://twig.symfony.com", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/mime": "^5.4|^6.4|^7.0", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "autoload": { + "files": [ "Resources/functions.php" ], + "psr-4" : { "Twig\\Extra\\Html\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/html-extra/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/html-extra/phpunit.xml.dist new file mode 100644 index 000000000..1b088ff26 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/html-extra/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/inky-extra/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/.gitignore b/upload/system/storage/vendor/twig/twig/extra/inky-extra/.gitignore new file mode 100644 index 000000000..76367ee5b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/InkyExtension.php b/upload/system/storage/vendor/twig/twig/extra/inky-extra/InkyExtension.php new file mode 100644 index 000000000..374cb7efb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/InkyExtension.php @@ -0,0 +1,34 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\Inky; + +use Pinky; +use Twig\Extension\AbstractExtension; +use Twig\TwigFilter; + +class InkyExtension extends AbstractExtension +{ + public function getFilters() + { + return [ + new TwigFilter('inky_to_html', [self::class, 'inky'], ['is_safe' => ['html']]), + ]; + } + + /** + * @internal + */ + public static function inky(string $body): string + { + return false === ($html = Pinky\transformString($body)->saveHTML()) ? '' : $html; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/LICENSE b/upload/system/storage/vendor/twig/twig/extra/inky-extra/LICENSE new file mode 100644 index 000000000..f37c76b59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/README.md b/upload/system/storage/vendor/twig/twig/extra/inky-extra/README.md new file mode 100644 index 000000000..33ccaeda3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/README.md @@ -0,0 +1,8 @@ +Twig Inky Extension +=================== + +This package is a Twig extension that provides the following: + + * [`inky_to_html`][1] filter: processes an [inky email template](https://github.com/zurb/inky). + +[1]: https://twig.symfony.com/inky_to_html diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/Resources/functions.php b/upload/system/storage/vendor/twig/twig/extra/inky-extra/Resources/functions.php new file mode 100644 index 000000000..0fa4111de --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/Resources/functions.php @@ -0,0 +1,24 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\Inky; + +/** + * @internal + * + * @deprecated since Twig 3.9.0 + */ +function twig_inky(string $body): string +{ + trigger_deprecation('twig/inky-extra', '3.9.0', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return InkyExtension::inky($body); +} diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/Tests/Fixtures/inky.test b/upload/system/storage/vendor/twig/twig/extra/inky-extra/Tests/Fixtures/inky.test new file mode 100644 index 000000000..1dce3a4aa --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/Tests/Fixtures/inky.test @@ -0,0 +1,16 @@ +--TEST-- +"inky_to_html" filter +--TEMPLATE-- +{% apply inky_to_html %} + {{ var }} +{%- endapply %} + +{{ include("inky")|inky_to_html }} +--TEMPLATE(inky)-- +{{ var }} +--DATA-- +return ['var' => 'value
    '] +--EXPECT-- +
    value<br />
    + +
    value<br />
    diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/inky-extra/Tests/IntegrationTest.php new file mode 100644 index 000000000..317d36476 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/Tests/IntegrationTest.php @@ -0,0 +1,30 @@ +assertSame(InkyExtension::inky('

    Foo

    '), twig_inky('

    Foo

    ')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/composer.json b/upload/system/storage/vendor/twig/twig/extra/inky-extra/composer.json new file mode 100644 index 000000000..77d83e2ff --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/composer.json @@ -0,0 +1,33 @@ +{ + "name": "twig/inky-extra", + "type": "library", + "description": "A Twig extension for the inky email templating engine", + "keywords": ["twig", "inky", "email", "emails"], + "homepage": "https://twig.symfony.com", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", + "lorenzo/pinky": "^1.0.5", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "autoload": { + "files": [ "Resources/functions.php" ], + "psr-4" : { "Twig\\Extra\\Inky\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/inky-extra/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/inky-extra/phpunit.xml.dist new file mode 100644 index 000000000..7f759dd3d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/inky-extra/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/intl-extra/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/.gitignore b/upload/system/storage/vendor/twig/twig/extra/intl-extra/.gitignore new file mode 100644 index 000000000..76367ee5b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/IntlExtension.php b/upload/system/storage/vendor/twig/twig/extra/intl-extra/IntlExtension.php new file mode 100644 index 000000000..7278db214 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/IntlExtension.php @@ -0,0 +1,525 @@ + \IntlDateFormatter::NONE, + 'short' => \IntlDateFormatter::SHORT, + 'medium' => \IntlDateFormatter::MEDIUM, + 'long' => \IntlDateFormatter::LONG, + 'full' => \IntlDateFormatter::FULL, + ]; + + // Assuming that each `RELATIVE_*` constant are defined when one of them is. + if (\defined('IntlDateFormatter::RELATIVE_FULL')) { + $formats = array_merge($formats, [ + 'relative_short' => \IntlDateFormatter::RELATIVE_SHORT, + 'relative_medium' => \IntlDateFormatter::RELATIVE_MEDIUM, + 'relative_long' => \IntlDateFormatter::RELATIVE_LONG, + 'relative_full' => \IntlDateFormatter::RELATIVE_FULL, + ]); + } + + return $formats; + } + + private const TIME_FORMATS = [ + 'none' => \IntlDateFormatter::NONE, + 'short' => \IntlDateFormatter::SHORT, + 'medium' => \IntlDateFormatter::MEDIUM, + 'long' => \IntlDateFormatter::LONG, + 'full' => \IntlDateFormatter::FULL, + ]; + private const NUMBER_TYPES = [ + 'default' => \NumberFormatter::TYPE_DEFAULT, + 'int32' => \NumberFormatter::TYPE_INT32, + 'int64' => \NumberFormatter::TYPE_INT64, + 'double' => \NumberFormatter::TYPE_DOUBLE, + ]; + private const NUMBER_STYLES = [ + 'decimal' => \NumberFormatter::DECIMAL, + 'currency' => \NumberFormatter::CURRENCY, + 'percent' => \NumberFormatter::PERCENT, + 'scientific' => \NumberFormatter::SCIENTIFIC, + 'spellout' => \NumberFormatter::SPELLOUT, + 'ordinal' => \NumberFormatter::ORDINAL, + 'duration' => \NumberFormatter::DURATION, + ]; + private const NUMBER_ATTRIBUTES = [ + 'grouping_used' => \NumberFormatter::GROUPING_USED, + 'decimal_always_shown' => \NumberFormatter::DECIMAL_ALWAYS_SHOWN, + 'max_integer_digit' => \NumberFormatter::MAX_INTEGER_DIGITS, + 'min_integer_digit' => \NumberFormatter::MIN_INTEGER_DIGITS, + 'integer_digit' => \NumberFormatter::INTEGER_DIGITS, + 'max_fraction_digit' => \NumberFormatter::MAX_FRACTION_DIGITS, + 'min_fraction_digit' => \NumberFormatter::MIN_FRACTION_DIGITS, + 'fraction_digit' => \NumberFormatter::FRACTION_DIGITS, + 'multiplier' => \NumberFormatter::MULTIPLIER, + 'grouping_size' => \NumberFormatter::GROUPING_SIZE, + 'rounding_mode' => \NumberFormatter::ROUNDING_MODE, + 'rounding_increment' => \NumberFormatter::ROUNDING_INCREMENT, + 'format_width' => \NumberFormatter::FORMAT_WIDTH, + 'padding_position' => \NumberFormatter::PADDING_POSITION, + 'secondary_grouping_size' => \NumberFormatter::SECONDARY_GROUPING_SIZE, + 'significant_digits_used' => \NumberFormatter::SIGNIFICANT_DIGITS_USED, + 'min_significant_digits_used' => \NumberFormatter::MIN_SIGNIFICANT_DIGITS, + 'max_significant_digits_used' => \NumberFormatter::MAX_SIGNIFICANT_DIGITS, + 'lenient_parse' => \NumberFormatter::LENIENT_PARSE, + ]; + private const NUMBER_ROUNDING_ATTRIBUTES = [ + 'ceiling' => \NumberFormatter::ROUND_CEILING, + 'floor' => \NumberFormatter::ROUND_FLOOR, + 'down' => \NumberFormatter::ROUND_DOWN, + 'up' => \NumberFormatter::ROUND_UP, + 'halfeven' => \NumberFormatter::ROUND_HALFEVEN, + 'halfdown' => \NumberFormatter::ROUND_HALFDOWN, + 'halfup' => \NumberFormatter::ROUND_HALFUP, + ]; + private const NUMBER_PADDING_ATTRIBUTES = [ + 'before_prefix' => \NumberFormatter::PAD_BEFORE_PREFIX, + 'after_prefix' => \NumberFormatter::PAD_AFTER_PREFIX, + 'before_suffix' => \NumberFormatter::PAD_BEFORE_SUFFIX, + 'after_suffix' => \NumberFormatter::PAD_AFTER_SUFFIX, + ]; + private const NUMBER_TEXT_ATTRIBUTES = [ + 'positive_prefix' => \NumberFormatter::POSITIVE_PREFIX, + 'positive_suffix' => \NumberFormatter::POSITIVE_SUFFIX, + 'negative_prefix' => \NumberFormatter::NEGATIVE_PREFIX, + 'negative_suffix' => \NumberFormatter::NEGATIVE_SUFFIX, + 'padding_character' => \NumberFormatter::PADDING_CHARACTER, + 'currency_code' => \NumberFormatter::CURRENCY_CODE, + 'default_ruleset' => \NumberFormatter::DEFAULT_RULESET, + 'public_rulesets' => \NumberFormatter::PUBLIC_RULESETS, + ]; + private const NUMBER_SYMBOLS = [ + 'decimal_separator' => \NumberFormatter::DECIMAL_SEPARATOR_SYMBOL, + 'grouping_separator' => \NumberFormatter::GROUPING_SEPARATOR_SYMBOL, + 'pattern_separator' => \NumberFormatter::PATTERN_SEPARATOR_SYMBOL, + 'percent' => \NumberFormatter::PERCENT_SYMBOL, + 'zero_digit' => \NumberFormatter::ZERO_DIGIT_SYMBOL, + 'digit' => \NumberFormatter::DIGIT_SYMBOL, + 'minus_sign' => \NumberFormatter::MINUS_SIGN_SYMBOL, + 'plus_sign' => \NumberFormatter::PLUS_SIGN_SYMBOL, + 'currency' => \NumberFormatter::CURRENCY_SYMBOL, + 'intl_currency' => \NumberFormatter::INTL_CURRENCY_SYMBOL, + 'monetary_separator' => \NumberFormatter::MONETARY_SEPARATOR_SYMBOL, + 'exponential' => \NumberFormatter::EXPONENTIAL_SYMBOL, + 'permill' => \NumberFormatter::PERMILL_SYMBOL, + 'pad_escape' => \NumberFormatter::PAD_ESCAPE_SYMBOL, + 'infinity' => \NumberFormatter::INFINITY_SYMBOL, + 'nan' => \NumberFormatter::NAN_SYMBOL, + 'significant_digit' => \NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL, + 'monetary_grouping_separator' => \NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL, + ]; + + private $dateFormatters = []; + private $numberFormatters = []; + private $dateFormatterPrototype; + private $numberFormatterPrototype; + + public function __construct(?\IntlDateFormatter $dateFormatterPrototype = null, ?\NumberFormatter $numberFormatterPrototype = null) + { + $this->dateFormatterPrototype = $dateFormatterPrototype; + $this->numberFormatterPrototype = $numberFormatterPrototype; + } + + public function getFilters() + { + return [ + // internationalized names + new TwigFilter('country_name', [$this, 'getCountryName']), + new TwigFilter('currency_name', [$this, 'getCurrencyName']), + new TwigFilter('currency_symbol', [$this, 'getCurrencySymbol']), + new TwigFilter('language_name', [$this, 'getLanguageName']), + new TwigFilter('locale_name', [$this, 'getLocaleName']), + new TwigFilter('timezone_name', [$this, 'getTimezoneName']), + + // localized formatters + new TwigFilter('format_currency', [$this, 'formatCurrency']), + new TwigFilter('format_number', [$this, 'formatNumber']), + new TwigFilter('format_*_number', [$this, 'formatNumberStyle']), + new TwigFilter('format_datetime', [$this, 'formatDateTime'], ['needs_environment' => true]), + new TwigFilter('format_date', [$this, 'formatDate'], ['needs_environment' => true]), + new TwigFilter('format_time', [$this, 'formatTime'], ['needs_environment' => true]), + ]; + } + + public function getFunctions() + { + return [ + // internationalized names + new TwigFunction('country_timezones', [$this, 'getCountryTimezones']), + new TwigFunction('language_names', [$this, 'getLanguageNames']), + new TwigFunction('script_names', [$this, 'getScriptNames']), + new TwigFunction('country_names', [$this, 'getCountryNames']), + new TwigFunction('locale_names', [$this, 'getLocaleNames']), + new TwigFunction('currency_names', [$this, 'getCurrencyNames']), + new TwigFunction('timezone_names', [$this, 'getTimezoneNames']), + ]; + } + + public function getCountryName(?string $country, ?string $locale = null): string + { + if (null === $country) { + return ''; + } + + try { + return Countries::getName($country, $locale); + } catch (MissingResourceException $exception) { + return $country; + } + } + + public function getCurrencyName(?string $currency, ?string $locale = null): string + { + if (null === $currency) { + return ''; + } + + try { + return Currencies::getName($currency, $locale); + } catch (MissingResourceException $exception) { + return $currency; + } + } + + public function getCurrencySymbol(?string $currency, ?string $locale = null): string + { + if (null === $currency) { + return ''; + } + + try { + return Currencies::getSymbol($currency, $locale); + } catch (MissingResourceException $exception) { + return $currency; + } + } + + public function getLanguageName(?string $language, ?string $locale = null): string + { + if (null === $language) { + return ''; + } + + try { + return Languages::getName($language, $locale); + } catch (MissingResourceException $exception) { + return $language; + } + } + + public function getLocaleName(?string $data, ?string $locale = null): string + { + if (null === $data) { + return ''; + } + + try { + return Locales::getName($data, $locale); + } catch (MissingResourceException $exception) { + return $data; + } + } + + public function getTimezoneName(?string $timezone, ?string $locale = null): string + { + if (null === $timezone) { + return ''; + } + + try { + return Timezones::getName($timezone, $locale); + } catch (MissingResourceException $exception) { + return $timezone; + } + } + + public function getCountryTimezones(string $country): array + { + try { + return Timezones::forCountryCode($country); + } catch (MissingResourceException $exception) { + return []; + } + } + + public function getLanguageNames(?string $locale = null): array + { + try { + return Languages::getNames($locale); + } catch (MissingResourceException $exception) { + return []; + } + } + + public function getScriptNames(?string $locale = null): array + { + try { + return Scripts::getNames($locale); + } catch (MissingResourceException $exception) { + return []; + } + } + + public function getCountryNames(?string $locale = null): array + { + try { + return Countries::getNames($locale); + } catch (MissingResourceException $exception) { + return []; + } + } + + public function getLocaleNames(?string $locale = null): array + { + try { + return Locales::getNames($locale); + } catch (MissingResourceException $exception) { + return []; + } + } + + public function getCurrencyNames(?string $locale = null): array + { + try { + return Currencies::getNames($locale); + } catch (MissingResourceException $exception) { + return []; + } + } + + public function getTimezoneNames(?string $locale = null): array + { + try { + return Timezones::getNames($locale); + } catch (MissingResourceException $exception) { + return []; + } + } + + public function formatCurrency($amount, string $currency, array $attrs = [], ?string $locale = null): string + { + $formatter = $this->createNumberFormatter($locale, 'currency', $attrs); + + if (false === $ret = $formatter->formatCurrency($amount, $currency)) { + throw new RuntimeError('Unable to format the given number as a currency.'); + } + + return $ret; + } + + public function formatNumber($number, array $attrs = [], string $style = 'decimal', string $type = 'default', ?string $locale = null): string + { + if (!isset(self::NUMBER_TYPES[$type])) { + throw new RuntimeError(\sprintf('The type "%s" does not exist, known types are: "%s".', $type, implode('", "', array_keys(self::NUMBER_TYPES)))); + } + + $formatter = $this->createNumberFormatter($locale, $style, $attrs); + + if (false === $ret = $formatter->format($number, self::NUMBER_TYPES[$type])) { + throw new RuntimeError('Unable to format the given number.'); + } + + return $ret; + } + + public function formatNumberStyle(string $style, $number, array $attrs = [], string $type = 'default', ?string $locale = null): string + { + return $this->formatNumber($number, $attrs, $style, $type, $locale); + } + + /** + * @param \DateTimeInterface|string|null $date A date or null to use the current time + * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged + */ + public function formatDateTime(Environment $env, $date, ?string $dateFormat = 'medium', ?string $timeFormat = 'medium', string $pattern = '', $timezone = null, string $calendar = 'gregorian', ?string $locale = null): string + { + $date = $env->getExtension(CoreExtension::class)->convertDate($date, $timezone); + + $formatterTimezone = $timezone; + if (null === $formatterTimezone || false === $formatterTimezone) { + $formatterTimezone = $date->getTimezone(); + } elseif (\is_string($formatterTimezone)) { + $formatterTimezone = new \DateTimeZone($timezone); + } + $formatter = $this->createDateFormatter($locale, $dateFormat, $timeFormat, $pattern, $formatterTimezone, $calendar); + + if (false === $ret = $formatter->format($date)) { + throw new RuntimeError('Unable to format the given date.'); + } + + return $ret; + } + + /** + * @param \DateTimeInterface|string|null $date A date or null to use the current time + * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged + */ + public function formatDate(Environment $env, $date, ?string $dateFormat = 'medium', string $pattern = '', $timezone = null, string $calendar = 'gregorian', ?string $locale = null): string + { + return $this->formatDateTime($env, $date, $dateFormat, 'none', $pattern, $timezone, $calendar, $locale); + } + + /** + * @param \DateTimeInterface|string|null $date A date or null to use the current time + * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged + */ + public function formatTime(Environment $env, $date, ?string $timeFormat = 'medium', string $pattern = '', $timezone = null, string $calendar = 'gregorian', ?string $locale = null): string + { + return $this->formatDateTime($env, $date, 'none', $timeFormat, $pattern, $timezone, $calendar, $locale); + } + + private function createDateFormatter(?string $locale, ?string $dateFormat, ?string $timeFormat, string $pattern, ?\DateTimeZone $timezone, string $calendar): \IntlDateFormatter + { + $dateFormats = self::availableDateFormats(); + + if (null !== $dateFormat && !isset($dateFormats[$dateFormat])) { + throw new RuntimeError(\sprintf('The date format "%s" does not exist, known formats are: "%s".', $dateFormat, implode('", "', array_keys($dateFormats)))); + } + + if (null !== $timeFormat && !isset(self::TIME_FORMATS[$timeFormat])) { + throw new RuntimeError(\sprintf('The time format "%s" does not exist, known formats are: "%s".', $timeFormat, implode('", "', array_keys(self::TIME_FORMATS)))); + } + + if (null === $locale) { + if ($this->dateFormatterPrototype) { + $locale = $this->dateFormatterPrototype->getLocale(); + } + $locale = $locale ?: \Locale::getDefault(); + } + + $calendar = 'gregorian' === $calendar ? \IntlDateFormatter::GREGORIAN : \IntlDateFormatter::TRADITIONAL; + + $dateFormatValue = $dateFormats[$dateFormat] ?? null; + $timeFormatValue = self::TIME_FORMATS[$timeFormat] ?? null; + + if ($this->dateFormatterPrototype) { + $dateFormatValue = $dateFormatValue ?: $this->dateFormatterPrototype->getDateType(); + $timeFormatValue = $timeFormatValue ?: $this->dateFormatterPrototype->getTimeType(); + $timezone = $timezone ?: $this->dateFormatterPrototype->getTimeZone()->toDateTimeZone(); + $calendar = $calendar ?: $this->dateFormatterPrototype->getCalendar(); + $pattern = $pattern ?: $this->dateFormatterPrototype->getPattern(); + } + + $timezoneName = $timezone ? $timezone->getName() : '(none)'; + + $hash = $locale.'|'.$dateFormatValue.'|'.$timeFormatValue.'|'.$timezoneName.'|'.$calendar.'|'.$pattern; + + if (!isset($this->dateFormatters[$hash])) { + $this->dateFormatters[$hash] = new \IntlDateFormatter($locale, $dateFormatValue, $timeFormatValue, $timezone, $calendar, $pattern); + } + + return $this->dateFormatters[$hash]; + } + + private function createNumberFormatter(?string $locale, string $style, array $attrs = []): \NumberFormatter + { + if (!isset(self::NUMBER_STYLES[$style])) { + throw new RuntimeError(\sprintf('The style "%s" does not exist, known styles are: "%s".', $style, implode('", "', array_keys(self::NUMBER_STYLES)))); + } + + if (null === $locale) { + $locale = \Locale::getDefault(); + } + + // textAttrs and symbols can only be set on the prototype as there is probably no + // use case for setting it on each call. + $textAttrs = []; + $symbols = []; + if ($this->numberFormatterPrototype) { + foreach (self::NUMBER_ATTRIBUTES as $name => $const) { + if (!isset($attrs[$name])) { + $value = $this->numberFormatterPrototype->getAttribute($const); + if ('rounding_mode' === $name) { + $value = array_flip(self::NUMBER_ROUNDING_ATTRIBUTES)[$value]; + } elseif ('padding_position' === $name) { + $value = array_flip(self::NUMBER_PADDING_ATTRIBUTES)[$value]; + } + $attrs[$name] = $value; + } + } + + foreach (self::NUMBER_TEXT_ATTRIBUTES as $name => $const) { + $textAttrs[$name] = $this->numberFormatterPrototype->getTextAttribute($const); + } + + foreach (self::NUMBER_SYMBOLS as $name => $const) { + $symbols[$name] = $this->numberFormatterPrototype->getSymbol($const); + } + } + + ksort($attrs); + $hash = $locale.'|'.$style.'|'.json_encode($attrs).'|'.json_encode($textAttrs).'|'.json_encode($symbols); + + if (!isset($this->numberFormatters[$hash])) { + $this->numberFormatters[$hash] = new \NumberFormatter($locale, self::NUMBER_STYLES[$style]); + } + + foreach ($attrs as $name => $value) { + if (!isset(self::NUMBER_ATTRIBUTES[$name])) { + throw new RuntimeError(\sprintf('The number formatter attribute "%s" does not exist, known attributes are: "%s".', $name, implode('", "', array_keys(self::NUMBER_ATTRIBUTES)))); + } + + if ('rounding_mode' === $name) { + if (!isset(self::NUMBER_ROUNDING_ATTRIBUTES[$value])) { + throw new RuntimeError(\sprintf('The number formatter rounding mode "%s" does not exist, known modes are: "%s".', $value, implode('", "', array_keys(self::NUMBER_ROUNDING_ATTRIBUTES)))); + } + + $value = self::NUMBER_ROUNDING_ATTRIBUTES[$value]; + } elseif ('padding_position' === $name) { + if (!isset(self::NUMBER_PADDING_ATTRIBUTES[$value])) { + throw new RuntimeError(\sprintf('The number formatter padding position "%s" does not exist, known positions are: "%s".', $value, implode('", "', array_keys(self::NUMBER_PADDING_ATTRIBUTES)))); + } + + $value = self::NUMBER_PADDING_ATTRIBUTES[$value]; + } + + $this->numberFormatters[$hash]->setAttribute(self::NUMBER_ATTRIBUTES[$name], $value); + } + + foreach ($textAttrs as $name => $value) { + $this->numberFormatters[$hash]->setTextAttribute(self::NUMBER_TEXT_ATTRIBUTES[$name], $value); + } + + foreach ($symbols as $name => $value) { + $this->numberFormatters[$hash]->setSymbol(self::NUMBER_SYMBOLS[$name], $value); + } + + return $this->numberFormatters[$hash]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/LICENSE b/upload/system/storage/vendor/twig/twig/extra/intl-extra/LICENSE new file mode 100644 index 000000000..f37c76b59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/README.md b/upload/system/storage/vendor/twig/twig/extra/intl-extra/README.md new file mode 100644 index 000000000..ec169878b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/README.md @@ -0,0 +1,30 @@ +Twig Intl Extension +=================== + +This package is a Twig extension that provides the following: + + * [`country_name`][1] filter: returns the country name given its two-letter/five-letter code; + * [`currency_name`][2] filter: returns the currency name given its three-letter code; + * [`currency_symbol`][3] filter: returns the currency symbol given its three-letter code; + * [`language_name`][4] filter: returns the language name given its two-letter/five-letter code; + * [`locale_name`][5] filter: returns the language name given its two-letter/five-letter code; + * [`timezone_name`][6] filter: returns the timezone name given its identifier; + * [`country_timezones`][7] filter: returns the timezone identifiers of the given country code; + * [`format_currency`][8] filter: formats a number as a currency; + * [`format_number`][9] filter: formats a number; + * [`format_datetime`][10] filter: formats a date time; + * [`format_date`][11] filter: formats a date; + * [`format_time`][12] filter: formats a time. + +[1]: https://twig.symfony.com/country_name +[2]: https://twig.symfony.com/currency_name +[3]: https://twig.symfony.com/currency_symbol +[4]: https://twig.symfony.com/language_name +[5]: https://twig.symfony.com/locale_name +[6]: https://twig.symfony.com/timezone_name +[7]: https://twig.symfony.com/country_timezones +[8]: https://twig.symfony.com/format_currency +[9]: https://twig.symfony.com/format_number +[10]: https://twig.symfony.com/format_datetime +[11]: https://twig.symfony.com/format_date +[12]: https://twig.symfony.com/format_time diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_name.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_name.test new file mode 100644 index 000000000..b368d5170 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_name.test @@ -0,0 +1,18 @@ +--TEST-- +"country_name" filter +--TEMPLATE-- +{{ 'UNKNOWN'|country_name }} +{{ null|country_name }} +{{ 'FR'|country_name }} +{{ 'US'|country_name }} +{{ 'US'|country_name('fr') }} +{{ 'CH'|country_name('fr_CA') }} +--DATA-- +return []; +--EXPECT-- +UNKNOWN + +France +United States +États-Unis +Suisse diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_names.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_names.test new file mode 100644 index 000000000..f3cb07974 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_names.test @@ -0,0 +1,16 @@ +--TEST-- +"country_names" function +--TEMPLATE-- +{{ country_names('UNKNOWN')|length }} +{{ country_names()|length }} +{{ country_names('fr')|length }} +{{ country_names()['BE'] }} +{{ country_names('fr')['BE'] }} +--DATA-- +return []; +--EXPECT-- +0 +249 +249 +Belgium +Belgique diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_timezones.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_timezones.test new file mode 100644 index 000000000..3c81440c1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/country_timezones.test @@ -0,0 +1,12 @@ +--TEST-- +"country_timezones" function +--TEMPLATE-- +{{ country_timezones('UNKNOWN')|length }} +{{ country_timezones('FR')|join(', ') }} +{{ country_timezones('US')|join(', ') }} +--DATA-- +return []; +--EXPECT-- +0 +Europe/Paris +America/Adak, America/Anchorage, America/Boise, America/Chicago, America/Denver, America/Detroit, America/Indiana/Knox, America/Indiana/Marengo, America/Indiana/Petersburg, America/Indiana/Tell_City, America/Indiana/Vevay, America/Indiana/Vincennes, America/Indiana/Winamac, America/Indianapolis, America/Juneau, America/Kentucky/Monticello, America/Los_Angeles, America/Louisville, America/Menominee, America/Metlakatla, America/New_York, America/Nome, America/North_Dakota/Beulah, America/North_Dakota/Center, America/North_Dakota/New_Salem, America/Phoenix, America/Sitka, America/Yakutat, Pacific/Honolulu diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_name.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_name.test new file mode 100644 index 000000000..6f88fdce3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_name.test @@ -0,0 +1,18 @@ +--TEST-- +"currency_name" filter +--TEMPLATE-- +{{ 'UNKNOWN'|currency_name }} +{{ null|currency_name }} +{{ 'EUR'|currency_name }} +{{ 'JPY'|currency_name }} +{{ 'EUR'|currency_name('fr') }} +{{ 'JPY'|currency_name('fr_FR') }} +--DATA-- +return []; +--EXPECT-- +UNKNOWN + +Euro +Japanese Yen +euro +yen japonais diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_names.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_names.test new file mode 100644 index 000000000..bc2c54d02 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_names.test @@ -0,0 +1,16 @@ +--TEST-- +"currency_names" function +--TEMPLATE-- +{{ currency_names('UNKNOWN')|length }} +{{ currency_names()|length }} +{{ currency_names('fr')|length }} +{{ currency_names()['USD'] }} +{{ currency_names('fr')['USD'] }} +--DATA-- +return []; +--EXPECT-- +0 +292 +292 +US Dollar +dollar des États-Unis diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_symbol.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_symbol.test new file mode 100644 index 000000000..6503a4262 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/currency_symbol.test @@ -0,0 +1,14 @@ +--TEST-- +"currency_symbol" filter +--TEMPLATE-- +{{ 'UNKNOWN'|currency_symbol }} +{{ null|currency_symbol }} +{{ 'EUR'|currency_symbol }} +{{ 'JPY'|currency_symbol }} +--DATA-- +return []; +--EXPECT-- +UNKNOWN + +€ +¥ diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_currency.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_currency.test new file mode 100644 index 000000000..0d96958a4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_currency.test @@ -0,0 +1,16 @@ +--TEST-- +"format_currency" filter +--TEMPLATE-- +{{ '1000000'|format_currency('EUR') }} +{{ '1000000'|format_currency('EUR', locale='de') }} +{{ '1000000'|format_currency('EUR', {fraction_digit: 2}) }} +{{ '12.345'|format_currency('EUR', {rounding_mode: 'floor'}) }} +{{ '125000'|format_currency('YEN') }} +--DATA-- +return []; +--EXPECT-- +€1,000,000.00 +1.000.000,00 € +€1,000,000.00 +€12.34 +YEN 125,000.00 diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_date.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_date.test new file mode 100644 index 000000000..75844e1e4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_date.test @@ -0,0 +1,26 @@ +--TEST-- +"format_date" filter +--TEMPLATE-- +{{ '2019-08-07 23:39:12'|format_datetime() }} +{{ '2019-08-07 23:39:12'|format_datetime(locale='fr') }} +{{ '2019-08-07 23:39:12'|format_datetime('none', 'short', locale='fr') }} +{{ '2019-08-07 23:39:12'|format_datetime('short', 'none', locale='fr') }} +{{ '2019-08-07 23:39:12'|format_datetime('full', 'full', locale='fr') }} +{{ '2019-08-07 23:39:12'|format_datetime(pattern="hh 'oclock' a, zzzz") }} + +{{ '2019-08-07 23:39:12'|format_date }} +{{ '2019-08-07 23:39:12'|format_date(locale='fr') }} +{{ '2019-08-07 23:39:12'|format_time }} +--DATA-- +return []; +--EXPECT-- +Aug 7, 2019, 11:39:12 PM +7 août 2019, 23:39:12 +23:39 +07/08/2019 +mercredi 7 août 2019 à 23:39:12 temps universel coordonné +11 oclock PM, Coordinated Universal Time + +Aug 7, 2019 +7 août 2019 +11:39:12 PM diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_date_php8.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_date_php8.test new file mode 100644 index 000000000..67e0e6f4d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_date_php8.test @@ -0,0 +1,12 @@ +--TEST-- +"format_date" filter +--CONDITION-- +PHP_VERSION_ID >= 80000 +--TEMPLATE-- +{{ 'today 23:39:12'|format_datetime('relative_short', 'none', locale='fr') }} +{{ 'today 23:39:12'|format_datetime('relative_full', 'full', locale='fr') }} +--DATA-- +return []; +--EXPECT-- +aujourd’hui +aujourd’hui à 23:39:12 temps universel coordonné diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_number.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_number.test new file mode 100644 index 000000000..d52e132de --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/format_number.test @@ -0,0 +1,28 @@ +--TEST-- +"format_number" filter +--TEMPLATE-- +{{ '12.345'|format_number }} +{{ '12.345'|format_number(locale='fr') }} +{{ '12.345'|format_number(style='percent') }} +{{ '12.345'|format_number(style='spellout') }} +{{ '12.345'|format_percent_number }} +{{ '12.345'|format_spellout_number }} +{{ '80.345'|format_spellout_number(locale='fr_FR') }} +{{ '80.345'|format_spellout_number(locale='fr_CH') }} +{{ '12'|format_duration_number }} +{{ '0.12'|format_percent_number({fraction_digit: 1}) }} +{{ '0.12345'|format_percent_number({rounding_mode: 'ceiling'}) }} +--DATA-- +return []; +--EXPECT-- +12.345 +12,345 +1,234% +twelve point three four five +1,234% +twelve point three four five +quatre-vingts virgule trois quatre cinq +huitante virgule trois quatre cinq +12 sec. +12.0% +13% diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/language_name.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/language_name.test new file mode 100644 index 000000000..040aa5a67 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/language_name.test @@ -0,0 +1,20 @@ +--TEST-- +"language_name" filter +--TEMPLATE-- +{{ 'UNKNOWN'|language_name }} +{{ null|language_name }} +{{ 'de'|language_name }} +{{ 'fr'|language_name }} +{{ 'de'|language_name('fr') }} +{{ 'fr'|language_name('fr_FR') }} +{{ 'fr_CA'|language_name('fr_FR') }} +--DATA-- +return []; +--EXPECT-- +UNKNOWN + +German +French +allemand +français +français canadien diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/language_names.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/language_names.test new file mode 100644 index 000000000..ce5a95f49 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/language_names.test @@ -0,0 +1,16 @@ +--TEST-- +"language_names" function +--TEMPLATE-- +{{ language_names('UNKNOWN')|length }} +{{ language_names()|length > 600 ? 'ok' : 'ko' }} +{{ language_names('fr')|length > 600 ? 'ok' : 'ko' }} +{{ language_names()['fr'] }} +{{ language_names('fr')['fr'] }} +--DATA-- +return []; +--EXPECT-- +0 +ok +ok +French +français diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/locale_name.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/locale_name.test new file mode 100644 index 000000000..222d9fb39 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/locale_name.test @@ -0,0 +1,20 @@ +--TEST-- +"locale_name" filter +--TEMPLATE-- +{{ 'UNKNOWN'|locale_name }} +{{ null|locale_name }} +{{ 'de'|locale_name }} +{{ 'fr'|locale_name }} +{{ 'de'|locale_name('fr') }} +{{ 'fr'|locale_name('fr_FR') }} +{{ 'fr_CA'|locale_name('fr_FR') }} +--DATA-- +return []; +--EXPECT-- +UNKNOWN + +German +French +allemand +français +français (Canada) diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/locale_names.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/locale_names.test new file mode 100644 index 000000000..202e43aae --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/locale_names.test @@ -0,0 +1,16 @@ +--TEST-- +"locale_names" function +--TEMPLATE-- +{{ locale_names('UNKNOWN')|length }} +{{ locale_names()|length > 600 ? 'ok' : 'ko' }} +{{ locale_names('fr')|length > 600 ? 'ok' : 'ko' }} +{{ locale_names()['fr'] }} +{{ locale_names('fr')['fr'] }} +--DATA-- +return []; +--EXPECT-- +0 +ok +ok +French +français diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/script_names.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/script_names.test new file mode 100644 index 000000000..c65daf550 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/script_names.test @@ -0,0 +1,16 @@ +--TEST-- +"script_names" function +--TEMPLATE-- +{{ script_names('UNKNOWN')|length }} +{{ script_names()|length }} +{{ script_names('fr')|length }} +{{ script_names()['Marc'] }} +{{ script_names('fr')['Marc'] }} +--DATA-- +return []; +--EXPECT-- +0 +201 +201 +Marchen +Marchen diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/timezone_name.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/timezone_name.test new file mode 100644 index 000000000..d22204a75 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/timezone_name.test @@ -0,0 +1,16 @@ +--TEST-- +"timezone_name" filter +--TEMPLATE-- +{{ 'UNKNOWN'|timezone_name }} +{{ null|timezone_name }} +{{ 'Europe/Paris'|timezone_name }} +{{ 'America/Los_Angeles'|timezone_name }} +{{ 'America/Los_Angeles'|timezone_name('fr') }} +--DATA-- +return []; +--EXPECT-- +UNKNOWN + +Central European Time (Paris) +Pacific Time (Los Angeles) +heure du Pacifique nord-américain (Los Angeles) diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/timezone_names.test b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/timezone_names.test new file mode 100644 index 000000000..c8ee51c6b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/Fixtures/timezone_names.test @@ -0,0 +1,16 @@ +--TEST-- +"timezone_names" function +--TEMPLATE-- +{{ timezone_names('UNKNOWN')|length }} +{{ timezone_names()|length > 400 ? 'ok' : 'ko' }} +{{ timezone_names('fr')|length > 400 ? 'ok' : 'ko' }} +{{ timezone_names()['Europe/Paris'] }} +{{ timezone_names('fr')['Europe/Paris'] }} +--DATA-- +return []; +--EXPECT-- +0 +ok +ok +Central European Time (Paris) +heure d’Europe centrale (Paris) diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/IntegrationTest.php new file mode 100644 index 000000000..7b191bacd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/Tests/IntegrationTest.php @@ -0,0 +1,30 @@ +assertSame('12.346', $ext->formatNumber('12.3456')); + $this->assertStringStartsWith( + 'Feb 20, 2020, 1:37:00', + $ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00')) + ); + } + + public function testFormatterWithoutProtoFallsBackToCoreExtensionTimezone() + { + $ext = new IntlExtension(); + $env = new Environment(new ArrayLoader()); + // EET is always +2 without changes for daylight saving time + // so it has a fixed difference to UTC + $env->getExtension(CoreExtension::class)->setTimezone('EET'); + + $this->assertStringStartsWith( + 'Feb 20, 2020, 3:37:00', + $ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00', new \DateTimeZone('UTC'))) + ); + } + + public function testFormatterWithoutProtoSkipTimezoneConverter() + { + $ext = new IntlExtension(); + $env = new Environment(new ArrayLoader()); + // EET is always +2 without changes for daylight saving time + // so it has a fixed difference to UTC + $env->getExtension(CoreExtension::class)->setTimezone('EET'); + + $this->assertStringStartsWith( + 'Feb 20, 2020, 1:37:00', + $ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00', new \DateTimeZone('UTC')), 'medium', 'medium', '', false) + ); + } + + public function testFormatterProto() + { + $dateFormatterProto = new \IntlDateFormatter('fr', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, new \DateTimeZone('Europe/Paris')); + $numberFormatterProto = new \NumberFormatter('fr', \NumberFormatter::DECIMAL); + $numberFormatterProto->setTextAttribute(\NumberFormatter::POSITIVE_PREFIX, '++'); + $numberFormatterProto->setAttribute(\NumberFormatter::FRACTION_DIGITS, 1); + $ext = new IntlExtension($dateFormatterProto, $numberFormatterProto); + $env = new Environment(new ArrayLoader()); + + $this->assertSame('++12,3', $ext->formatNumber('12.3456')); + $this->assertContains( + $ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00', new \DateTimeZone('Europe/Paris'))), + [ + 'jeudi 20 février 2020 à 13:37:00 heure normale d’Europe centrale', + 'jeudi 20 février 2020 à 13:37:00 temps universel coordonné', + ] + ); + } + + public function testFormatterOverridenProto() + { + $dateFormatterProto = new \IntlDateFormatter('fr', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, new \DateTimeZone('Europe/Paris')); + $numberFormatterProto = new \NumberFormatter('fr', \NumberFormatter::DECIMAL); + $numberFormatterProto->setTextAttribute(\NumberFormatter::POSITIVE_PREFIX, '++'); + $numberFormatterProto->setAttribute(\NumberFormatter::FRACTION_DIGITS, 1); + $ext = new IntlExtension($dateFormatterProto, $numberFormatterProto); + $env = new Environment(new ArrayLoader()); + + $this->assertSame( + 'twelve point three', + $ext->formatNumber('12.3456', [], 'spellout', 'default', 'en_US') + ); + $this->assertSame( + '2020-02-20 13:37:00', + $ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00'), 'short', 'short', 'yyyy-MM-dd HH:mm:ss', 'UTC', 'gregorian', 'en_US') + ); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/composer.json b/upload/system/storage/vendor/twig/twig/extra/intl-extra/composer.json new file mode 100644 index 000000000..54b3f8956 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/composer.json @@ -0,0 +1,31 @@ +{ + "name": "twig/intl-extra", + "type": "library", + "description": "A Twig extension for Intl", + "keywords": ["twig", "intl"], + "homepage": "https://twig.symfony.com", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "require": { + "php": ">=8.0.2", + "twig/twig": "^3.10", + "symfony/intl": "^5.4|^6.4|^7.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "autoload": { + "psr-4" : { "Twig\\Extra\\Intl\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/intl-extra/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/intl-extra/phpunit.xml.dist new file mode 100644 index 000000000..d33987d16 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/intl-extra/phpunit.xml.dist @@ -0,0 +1,21 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/.gitignore b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/.gitignore new file mode 100644 index 000000000..76367ee5b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/DefaultMarkdown.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/DefaultMarkdown.php new file mode 100644 index 000000000..6650a661a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/DefaultMarkdown.php @@ -0,0 +1,39 @@ +converter = new LeagueMarkdown(); + } elseif (class_exists(MarkdownExtra::class)) { + $this->converter = new MichelfMarkdown(); + } elseif (class_exists(Parsedown::class)) { + $this->converter = new ErusevMarkdown(); + } else { + throw new \LogicException('You cannot use the "markdown_to_html" filter as no Markdown library is available; try running "composer require league/commonmark".'); + } + } + + public function convert(string $body): string + { + return $this->converter->convert($body); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/ErusevMarkdown.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/ErusevMarkdown.php new file mode 100644 index 000000000..47b030893 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/ErusevMarkdown.php @@ -0,0 +1,29 @@ +converter = $converter ?: new Parsedown(); + } + + public function convert(string $body): string + { + return $this->converter->text($body); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/LICENSE b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/LICENSE new file mode 100644 index 000000000..f37c76b59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/LeagueMarkdown.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/LeagueMarkdown.php new file mode 100644 index 000000000..be2667702 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/LeagueMarkdown.php @@ -0,0 +1,35 @@ +converter = $converter ?: new CommonMarkConverter(); + $this->legacySupport = !method_exists($this->converter, 'convert'); + } + + public function convert(string $body): string + { + if ($this->legacySupport) { + return $this->converter->convertToHtml($body); + } + + return $this->converter->convert($body); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MarkdownExtension.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MarkdownExtension.php new file mode 100644 index 000000000..6a2450095 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MarkdownExtension.php @@ -0,0 +1,51 @@ + ['all']]), + new TwigFilter('html_to_markdown', [self::class, 'htmlToMarkdown'], ['is_safe' => ['all']]), + ]; + } + + /** + * @internal + */ + public static function htmlToMarkdown(string $body, array $options = []): string + { + static $converters; + + if (!class_exists(HtmlConverter::class)) { + throw new \LogicException('You cannot use the "html_to_markdown" filter as league/html-to-markdown is not installed; try running "composer require league/html-to-markdown".'); + } + + $options += [ + 'hard_break' => true, + 'strip_tags' => true, + 'remove_nodes' => 'head style', + ]; + + if (!isset($converters[$key = serialize($options)])) { + $converters[$key] = new HtmlConverter($options); + } + + return $converters[$key]->convert($body); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MarkdownInterface.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MarkdownInterface.php new file mode 100644 index 000000000..10406ea90 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MarkdownInterface.php @@ -0,0 +1,17 @@ +converter = $converter; + } + + public function convert(string $body): string + { + // remove indentation + if ($white = substr($body, 0, strspn($body, " \t\r\n\0\x0B"))) { + $body = preg_replace("{^$white}m", '', $body); + } + + return $this->converter->convert($body); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MichelfMarkdown.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MichelfMarkdown.php new file mode 100644 index 000000000..0acc3a3a4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/MichelfMarkdown.php @@ -0,0 +1,34 @@ +hard_wrap = true; + } + + $this->converter = $converter; + } + + public function convert(string $body): string + { + return $this->converter->transform($body); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/README.md b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/README.md new file mode 100644 index 000000000..6cdd06bdd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/README.md @@ -0,0 +1,11 @@ +Twig Markdown Extension +======================= + +This package is a Twig extension that provides the following: + + * [`markdown_to_html`][1] filter: generates HTML from a Markdown block; + + * [`html_to_markdown`][2] filter: generates Markdown from an HTML block. + +[1]: https://twig.symfony.com/markdown_to_html +[2]: https://twig.symfony.com/html_to_markdown diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Resources/functions.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Resources/functions.php new file mode 100644 index 000000000..cf498364d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Resources/functions.php @@ -0,0 +1,24 @@ + +

    Hello

    +

    Great!

    + +{% endapply %} + + +{% apply html_to_markdown({hard_break: false}) %} + Great
    Break +{% endapply %} + + +{{ include('html')|html_to_markdown }} +--TEMPLATE(html)-- + +

    Hello

    +

    Great!

    + +--DATA-- +return [] +--EXPECT-- +Hello +===== + +**Great!** + +Great +Break + +Hello +===== + +**Great!** diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Tests/FunctionalTest.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Tests/FunctionalTest.php new file mode 100644 index 000000000..0d9b73a59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Tests/FunctionalTest.php @@ -0,0 +1,86 @@ + $template, + 'html' => <<addExtension(new MarkdownExtension()); + $twig->addRuntimeLoader(new class($class) implements RuntimeLoaderInterface { + private $class; + + public function __construct(string $class) + { + $this->class = $class; + } + + public function load($c) + { + if (MarkdownRuntime::class === $c) { + return new $c(new $this->class()); + } + } + }); + $this->assertMatchesRegularExpression('{'.$expected.'}m', trim($twig->render('index'))); + } + } + + public function getMarkdownTests() + { + return [ + [<<Hello\n+

    Great!

    "], + [<<Hello\n+

    Great!

    "], + ["{{ include('html')|markdown_to_html }}", "

    Hello

    \n+

    Great!

    "], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Tests/IntegrationTest.php new file mode 100644 index 000000000..7474ec769 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/Tests/IntegrationTest.php @@ -0,0 +1,30 @@ +assertSame(MarkdownExtension::htmlToMarkdown('

    foo

    '), html_to_markdown('

    foo

    ')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/composer.json b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/composer.json new file mode 100644 index 000000000..8dfe2fa84 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/composer.json @@ -0,0 +1,36 @@ +{ + "name": "twig/markdown-extra", + "type": "library", + "description": "A Twig extension for Markdown", + "keywords": ["twig", "html", "markdown"], + "homepage": "https://twig.symfony.com", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/deprecation-contracts": "^2.5|^3", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0", + "erusev/parsedown": "^1.7", + "league/commonmark": "^1.0|^2.0", + "league/html-to-markdown": "^4.8|^5.0", + "michelf/php-markdown": "^1.8|^2.0" + }, + "autoload": { + "files": [ "Resources/functions.php" ], + "psr-4" : { "Twig\\Extra\\Markdown\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/markdown-extra/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/phpunit.xml.dist new file mode 100644 index 000000000..a40846ed4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/markdown-extra/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/string-extra/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/.gitignore b/upload/system/storage/vendor/twig/twig/extra/string-extra/.gitignore new file mode 100644 index 000000000..76367ee5b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/.gitignore @@ -0,0 +1,4 @@ +vendor/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/LICENSE b/upload/system/storage/vendor/twig/twig/extra/string-extra/LICENSE new file mode 100644 index 000000000..f37c76b59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/README.md b/upload/system/storage/vendor/twig/twig/extra/string-extra/README.md new file mode 100644 index 000000000..c5a8ca4b9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/README.md @@ -0,0 +1,21 @@ +String Extension +================ + +This package is a Twig extension that provides integration with the Symfony +String component. It provides the following filters: + + * [`u`][1]: Wraps a text in a `UnicodeString` object to give access to +[methods of the class][2]. + + * [`slug`][3]: Wraps the [`AsciiSlugger`][4]'s `slug` method. + + * [`singular`][5] and [`plural`][6]: Wraps the [`Inflector`][7] `singularize` + and `pluralize` methods. + +[1]: https://twig.symfony.com/u +[2]: https://symfony.com/doc/current/components/string.html +[3]: https://twig.symfony.com/slug +[4]: https://symfony.com/doc/current/components/string.html#slugger +[5]: https://twig.symfony.com/singular +[6]: https://twig.symfony.com/plural +[7]: https://symfony.com/doc/current/components/string.html#inflector diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/StringExtension.php b/upload/system/storage/vendor/twig/twig/extra/string-extra/StringExtension.php new file mode 100644 index 000000000..e0abb845f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/StringExtension.php @@ -0,0 +1,90 @@ +slugger = $slugger ?: new AsciiSlugger(); + } + + public function getFilters() + { + return [ + new TwigFilter('u', [$this, 'createUnicodeString']), + new TwigFilter('slug', [$this, 'createSlug']), + new TwigFilter('plural', [$this, 'plural']), + new TwigFilter('singular', [$this, 'singular']), + ]; + } + + public function createUnicodeString(?string $text): UnicodeString + { + return new UnicodeString($text ?? ''); + } + + public function createSlug(string $string, string $separator = '-', ?string $locale = null): AbstractUnicodeString + { + return $this->slugger->slug($string, $separator, $locale); + } + + /** + * @return array|string + */ + public function plural(string $value, string $locale = 'en', bool $all = false) + { + if ($all) { + return $this->getInflector($locale)->pluralize($value); + } + + return $this->getInflector($locale)->pluralize($value)[0]; + } + + /** + * @return array|string + */ + public function singular(string $value, string $locale = 'en', bool $all = false) + { + if ($all) { + return $this->getInflector($locale)->singularize($value); + } + + return $this->getInflector($locale)->singularize($value)[0]; + } + + private function getInflector(string $locale): InflectorInterface + { + switch ($locale) { + case 'fr': + return $this->frenchInflector ?? $this->frenchInflector = new FrenchInflector(); + case 'en': + return $this->englishInflector ?? $this->englishInflector = new EnglishInflector(); + default: + throw new \InvalidArgumentException(\sprintf('Locale "%s" is not supported.', $locale)); + } + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/plural-invalid-language.test b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/plural-invalid-language.test new file mode 100644 index 000000000..9e3851daa --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/plural-invalid-language.test @@ -0,0 +1,10 @@ +--TEST-- +"plural" filter +--TEMPLATE-- +{{ 'partition'|plural('it') }} + +--DATA-- +return [] + +--EXCEPTION-- +Twig\Error\RuntimeError: An exception has been thrown during the rendering of a template ("Locale "it" is not supported.") in "index.twig" at line 2. \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/plural.test b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/plural.test new file mode 100644 index 000000000..b561e2ad0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/plural.test @@ -0,0 +1,15 @@ +--TEST-- +"plural" filter +--TEMPLATE-- +{{ 'partition'|plural('fr') }} +{{ 'partition'|plural('fr', all=true)|join(',') }} +{{ 'person'|plural('fr') }} +{{ 'person'|plural('en', all=true)|join(',') }} + +--DATA-- +return [] +--EXPECT-- +partitions +partitions +persons +persons,people diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/singular-invalid-language.test b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/singular-invalid-language.test new file mode 100644 index 000000000..deaa6fbff --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/singular-invalid-language.test @@ -0,0 +1,10 @@ +--TEST-- +"singular" filter +--TEMPLATE-- +{{ 'partitions'|singular('it') }} + +--DATA-- +return [] + +--EXCEPTION-- +Twig\Error\RuntimeError: An exception has been thrown during the rendering of a template ("Locale "it" is not supported.") in "index.twig" at line 2. \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/singular.test b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/singular.test new file mode 100644 index 000000000..01e03db66 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/singular.test @@ -0,0 +1,19 @@ +--TEST-- +"singular" filter +--TEMPLATE-- +{{ 'partitions'|singular('fr') }} +{{ 'partitions'|singular('fr', all=true)|join(',') }} +{{ 'persons'|singular('fr') }} +{{ 'persons'|singular('en', all=true)|join(',') }} +{{ 'people'|singular('en') }} +{{ 'people'|singular('en', all=true)|join(',') }} + +--DATA-- +return [] +--EXPECT-- +partition +partition +person +person +person +person diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/slug.test b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/slug.test new file mode 100644 index 000000000..b3bc13de5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/slug.test @@ -0,0 +1,13 @@ +--TEST-- +"slug" filter +--TEMPLATE-- +{{ 'Wôrķšƥáçè ~~sèťtïñğš~~'|slug }} + +{{ 'Wôrķšƥáçè ~~sèťtïñğš~~'|slug('/') }} + +--DATA-- +return [] +--EXPECT-- +Workspace-settings + +Workspace/settings diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/string.test b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/string.test new file mode 100644 index 000000000..e2ba62896 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/Fixtures/string.test @@ -0,0 +1,27 @@ +--TEST-- +"u" filter +--TEMPLATE-- +{{ 'Symfony String + Twig = <3'|u|raw }} + +{{ 'Symfony String + Twig = <3'|u.wordwrap(5).upper|raw }} + +{{ 'SymfonyStringWithTwig'|u.snake }} +{{ 'symfony_string with twig'|u.camel.title }} + +{{ 'Lorem ipsum'|u.truncate(8, '...') }} + +--DATA-- +return [] +--EXPECT-- +Symfony String + Twig = <3 + +SYMFONY +STRING ++ +TWIG += <3 + +symfony_string_with_twig +SymfonyStringWithTwig + +Lorem... diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/IntegrationTest.php new file mode 100644 index 000000000..032c9a9d9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/Tests/IntegrationTest.php @@ -0,0 +1,30 @@ +=8.0.2", + "symfony/string": "^5.4|^6.4|^7.0", + "symfony/translation-contracts": "^1.1|^2|^3", + "twig/twig": "^3.0" + }, + "require-dev": { + "symfony/phpunit-bridge": "^6.4|^7.0" + }, + "autoload": { + "psr-4" : { "Twig\\Extra\\String\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/string-extra/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/string-extra/phpunit.xml.dist new file mode 100644 index 000000000..aa15cb642 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/string-extra/phpunit.xml.dist @@ -0,0 +1,20 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/.gitattributes b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/.gitattributes new file mode 100644 index 000000000..aa02dc651 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/.gitattributes @@ -0,0 +1,2 @@ +/Tests export-ignore +/phpunit.xml.dist export-ignore diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/.gitignore b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/.gitignore new file mode 100644 index 000000000..0e559c535 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/.gitignore @@ -0,0 +1,5 @@ +vendor/ +var/ +composer.lock +phpunit.xml +.phpunit.result.cache diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/Compiler/MissingExtensionSuggestorPass.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/Compiler/MissingExtensionSuggestorPass.php new file mode 100644 index 000000000..245e5bfd1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/Compiler/MissingExtensionSuggestorPass.php @@ -0,0 +1,37 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Twig\Extra\TwigExtraBundle\DependencyInjection\Compiler; + +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface; +use Symfony\Component\DependencyInjection\ContainerBuilder; +use Symfony\Component\DependencyInjection\Reference; +use Twig\Environment; + +class MissingExtensionSuggestorPass implements CompilerPassInterface +{ + /** @return void */ + public function process(ContainerBuilder $container) + { + if ($container->getParameter('kernel.debug')) { + $twigDefinition = $container->getDefinition('twig'); + $twigDefinition + ->addMethodCall('registerUndefinedFilterCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestFilter']]) + ->addMethodCall('registerUndefinedFunctionCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestFunction']]) + ; + + // this method was added in Twig 3.2 + if (method_exists(Environment::class, 'registerUndefinedTokenParserCallback')) { + $twigDefinition->addMethodCall('registerUndefinedTokenParserCallback', [[new Reference('twig.missing_extension_suggestor'), 'suggestTag']]); + } + } + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/Configuration.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/Configuration.php new file mode 100644 index 000000000..24c5fd4d3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/Configuration.php @@ -0,0 +1,37 @@ +getRootNode(); + + foreach (Extensions::getClasses() as $name => $class) { + $rootNode + ->children() + ->arrayNode($name) + ->{class_exists($class) ? 'canBeDisabled' : 'canBeEnabled'}() + ->end() + ->end() + ; + } + + return $treeBuilder; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/TwigExtraExtension.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/TwigExtraExtension.php new file mode 100644 index 000000000..501927e21 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/DependencyInjection/TwigExtraExtension.php @@ -0,0 +1,47 @@ + + */ +class TwigExtraExtension extends Extension +{ + /** @return void */ + public function load(array $configs, ContainerBuilder $container) + { + $loader = new PhpFileLoader($container, new FileLocator(\dirname(__DIR__).'/Resources/config')); + $configuration = $this->getConfiguration($configs, $container); + $config = $this->processConfiguration($configuration, $configs); + + if ($container->getParameter('kernel.debug')) { + $loader->load('suggestor.php'); + } + + foreach (array_keys(Extensions::getClasses()) as $extension) { + if ($this->isConfigEnabled($container, $config[$extension])) { + $loader->load($extension.'.php'); + + if ('markdown' === $extension && class_exists(CommonMarkConverter::class)) { + $loader->load('markdown_league.php'); + } + } + } + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Extensions.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Extensions.php new file mode 100644 index 000000000..e542604e1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Extensions.php @@ -0,0 +1,131 @@ + [ + 'name' => 'cache', + 'class' => CacheExtension::class, + 'class_name' => 'CacheExtension', + 'package' => 'twig/cache-extra', + 'filters' => [], + 'functions' => [], + 'tags' => ['cache'], + ], + 'html' => [ + 'name' => 'html', + 'class' => HtmlExtension::class, + 'class_name' => 'HtmlExtension', + 'package' => 'twig/html-extra', + 'filters' => ['data_uri'], + 'functions' => ['html_classes'], + 'tags' => [], + ], + 'markdown' => [ + 'name' => 'markdown', + 'class' => MarkdownExtension::class, + 'class_name' => 'MarkdownExtension', + 'package' => 'twig/markdown-extra', + 'filters' => ['html_to_markdown', 'markdown_to_html'], + 'functions' => [], + 'tags' => [], + ], + 'intl' => [ + 'name' => 'intl', + 'class' => IntlExtension::class, + 'class_name' => 'IntlExtension', + 'package' => 'twig/intl-extra', + 'filters' => ['country_name', 'currency_name', 'currency_symbol', 'language_name', 'locale_name', 'timezone_name', + 'format_currency', 'format_number', 'format_decimal_number', 'format_currency_number', + 'format_percent_number', 'format_scientific_number', 'format_spellout_number', 'format_ordinal_number', + 'format_duration_number', 'format_date', 'format_datetime', 'format_time', + ], + 'functions' => ['country_timezones'], + 'tags' => [], + ], + 'cssinliner' => [ + 'name' => 'cssinliner', + 'class' => CssInlinerExtension::class, + 'class_name' => 'CssInlinerExtension', + 'package' => 'twig/cssinliner-extra', + 'filters' => ['inline_css'], + 'functions' => [], + 'tags' => [], + ], + 'inky' => [ + 'name' => 'inky', + 'class' => InkyExtension::class, + 'class_name' => 'InkyExtension', + 'package' => 'twig/inky-extra', + 'filters' => ['inky_to_html'], + 'functions' => [], + 'tags' => [], + ], + 'string' => [ + 'name' => 'string', + 'class' => StringExtension::class, + 'class_name' => 'StringExtension', + 'package' => 'twig/string-extra', + 'filters' => ['u'], + 'functions' => [], + 'tags' => [], + ], + ]; + + public static function getClasses(): array + { + return array_column(self::EXTENSIONS, 'class', 'name'); + } + + public static function getFilter(string $name): array + { + foreach (self::EXTENSIONS as $extension) { + if (\in_array($name, $extension['filters'])) { + return [$extension['class_name'], $extension['package']]; + } + } + + return []; + } + + public static function getFunction(string $name): array + { + foreach (self::EXTENSIONS as $extension) { + if (\in_array($name, $extension['functions'])) { + return [$extension['class_name'], $extension['package']]; + } + } + + return []; + } + + public static function getTag(string $name): array + { + foreach (self::EXTENSIONS as $extension) { + if (\in_array($name, $extension['tags'])) { + return [$extension['class_name'], $extension['package']]; + } + } + + return []; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/LICENSE b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/LICENSE new file mode 100644 index 000000000..f37c76b59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2019-present Fabien Potencier + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is furnished +to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/LeagueCommonMarkConverterFactory.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/LeagueCommonMarkConverterFactory.php new file mode 100644 index 000000000..a2b90a25f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/LeagueCommonMarkConverterFactory.php @@ -0,0 +1,42 @@ +extensions = $extensions; + } + + public function __invoke(): CommonMarkConverter + { + $converter = new CommonMarkConverter(); + + foreach ($this->extensions as $extension) { + $converter->getEnvironment()->addExtension($extension); + } + + return $converter; + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/MissingExtensionSuggestor.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/MissingExtensionSuggestor.php new file mode 100644 index 000000000..0f8e1d5fd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/MissingExtensionSuggestor.php @@ -0,0 +1,44 @@ +services() + ->set('twig.extension.cache', CacheExtension::class) + ->tag('twig.extension') + + ->set('twig.runtime.cache', CacheRuntime::class) + ->args([ + $service('twig.cache'), + ]) + ->tag('twig.runtime') + + ->set('twig.cache', TagAwareAdapter::class) + ->args([ + $service('.twig.cache.inner'), + ]) + + ->set('.twig.cache.inner') + ->parent('cache.app') + ->tag('cache.pool', ['name' => 'twig.cache']) + + ->alias(TagAwareCacheInterface::class.' $twigCache', 'twig.cache') + ->alias(CacheInterface::class.' $twigCache', '.twig.cache.inner') + ->alias(CacheItemPoolInterface::class.' $twigCache', '.twig.cache.inner') + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/cssinliner.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/cssinliner.php new file mode 100644 index 000000000..7e3adc3e4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/cssinliner.php @@ -0,0 +1,21 @@ +services() + ->set('twig.extension.cssinliner', CssInlinerExtension::class) + ->tag('twig.extension') + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/html.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/html.php new file mode 100644 index 000000000..072485c1d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/html.php @@ -0,0 +1,21 @@ +services() + ->set('twig.extension.html', HtmlExtension::class) + ->tag('twig.extension') + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/inky.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/inky.php new file mode 100644 index 000000000..026e2f228 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/inky.php @@ -0,0 +1,21 @@ +services() + ->set('twig.extension.inky', InkyExtension::class) + ->tag('twig.extension') + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/intl.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/intl.php new file mode 100644 index 000000000..563e2dde7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/intl.php @@ -0,0 +1,21 @@ +services() + ->set('twig.extension.intl', IntlExtension::class) + ->tag('twig.extension') + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/markdown.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/markdown.php new file mode 100644 index 000000000..665542a53 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/markdown.php @@ -0,0 +1,32 @@ +services() + ->set('twig.extension.markdown', MarkdownExtension::class) + ->tag('twig.extension') + + ->set('twig.runtime.markdown', MarkdownRuntime::class) + ->args([ + $service('twig.markdown.default'), + ]) + ->tag('twig.runtime') + + ->set('twig.markdown.default', DefaultMarkdown::class) + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/markdown_league.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/markdown_league.php new file mode 100644 index 000000000..266f59776 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/markdown_league.php @@ -0,0 +1,31 @@ +services() + ->set('twig.markdown.league_common_mark_converter_factory', LeagueCommonMarkConverterFactory::class) + ->args([new TaggedIteratorArgument('twig.markdown.league_extension')]) + + ->set('twig.markdown.league_common_mark_converter', CommonMarkConverter::class) + ->factory($service('twig.markdown.league_common_mark_converter_factory')) + + ->set('twig.markdown.default', LeagueMarkdown::class) + ->args([$service('twig.markdown.league_common_mark_converter')]) + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/string.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/string.php new file mode 100644 index 000000000..3ec633416 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/string.php @@ -0,0 +1,21 @@ +services() + ->set('twig.extension.string', StringExtension::class) + ->tag('twig.extension') + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/suggestor.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/suggestor.php new file mode 100644 index 000000000..cb93df3b6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Resources/config/suggestor.php @@ -0,0 +1,20 @@ +services() + ->set('twig.missing_extension_suggestor', MissingExtensionSuggestor::class) + ; +}; diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/DependencyInjection/TwigExtraExtensionTest.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/DependencyInjection/TwigExtraExtensionTest.php new file mode 100644 index 000000000..355b794d2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/DependencyInjection/TwigExtraExtensionTest.php @@ -0,0 +1,46 @@ + false, + ])); + $container->registerExtension(new TwigExtraExtension()); + $container->loadFromExtension('twig_extra'); + $container->getCompilerPassConfig()->setOptimizationPasses([]); + $container->getCompilerPassConfig()->setRemovingPasses([]); + $container->getCompilerPassConfig()->setAfterRemovingPasses([]); + $container->compile(); + + foreach (Extensions::getClasses() as $name => $class) { + $this->assertEquals($class, $container->getDefinition('twig.extension.'.$name)->getClass()); + } + + $this->assertSame(LeagueMarkdown::class, $container->getDefinition('twig.markdown.default')->getClass()); + + $commonmarkConverterFactory = $container->getDefinition('twig.markdown.league_common_mark_converter')->getFactory(); + + $this->assertSame('twig.markdown.league_common_mark_converter_factory', (string) $commonmarkConverterFactory[0]); + $this->assertSame('__invoke', $commonmarkConverterFactory[1]); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/Fixture/Kernel.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/Fixture/Kernel.php new file mode 100644 index 000000000..faad85c18 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/Fixture/Kernel.php @@ -0,0 +1,54 @@ + 'S3CRET', + 'test' => true, + 'router' => ['utf8' => true], + 'http_method_override' => false, + 'php_errors' => [ + 'log' => true, + ], + ]; + + // the "handle_all_throwables" option was introduced in FrameworkBundle 6.2 (and so was the NotificationAssertionsTrait) + if (trait_exists(NotificationAssertionsTrait::class)) { + $config['handle_all_throwables'] = true; + } + + $c->loadFromExtension('framework', $config); + $c->loadFromExtension('twig', [ + 'default_path' => __DIR__.'/views', + ]); + + $c->register(StrikethroughExtension::class)->addTag('twig.markdown.league_extension'); + } + + protected function configureRoutes($routes): void + { + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/Fixture/views/markdown_to_html.html.twig b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/Fixture/views/markdown_to_html.html.twig new file mode 100644 index 000000000..76ece57dd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/Fixture/views/markdown_to_html.html.twig @@ -0,0 +1,3 @@ +{% apply markdown_to_html %} +# Hello ~~World~~ +{% endapply %} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/IntegrationTest.php new file mode 100644 index 000000000..04cbbeed5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/Tests/IntegrationTest.php @@ -0,0 +1,18 @@ +get('twig')->render('markdown_to_html.html.twig'); + + $this->assertStringContainsString('

    Hello World

    ', $rendered); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/TwigExtraBundle.php b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/TwigExtraBundle.php new file mode 100644 index 000000000..a9c8f734b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/TwigExtraBundle.php @@ -0,0 +1,27 @@ +addCompilerPass(new MissingExtensionSuggestorPass()); + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/composer.json b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/composer.json new file mode 100644 index 000000000..15e365a38 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/composer.json @@ -0,0 +1,40 @@ +{ + "name": "twig/extra-bundle", + "type": "symfony-bundle", + "description": "A Symfony bundle for extra Twig extensions", + "keywords": ["twig", "extra", "bundle"], + "homepage": "https://twig.symfony.com", + "license": "MIT", + "minimum-stability": "dev", + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com", + "homepage": "http://fabien.potencier.org", + "role": "Lead Developer" + } + ], + "require": { + "php": ">=8.0.2", + "symfony/framework-bundle": "^5.4|^6.4|^7.0", + "symfony/twig-bundle": "^5.4|^6.4|^7.0", + "twig/twig": "^3.0" + }, + "require-dev": { + "league/commonmark": "^1.0|^2.0", + "symfony/phpunit-bridge": "^6.4|^7.0", + "twig/cache-extra": "^3.0", + "twig/cssinliner-extra": "^3.0", + "twig/html-extra": "^3.0", + "twig/inky-extra": "^3.0", + "twig/intl-extra": "^3.0", + "twig/markdown-extra": "^3.0", + "twig/string-extra": "^3.0" + }, + "autoload": { + "psr-4" : { "Twig\\Extra\\TwigExtraBundle\\" : "" }, + "exclude-from-classmap": [ + "/Tests/" + ] + } +} diff --git a/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/phpunit.xml.dist new file mode 100644 index 000000000..c8d88d89c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/extra/twig-extra-bundle/phpunit.xml.dist @@ -0,0 +1,22 @@ + + + + + ./ + + + ./Tests + ./vendor + + + + + + + + + + ./Tests/ + + + diff --git a/upload/system/storage/vendor/twig/twig/phpunit.xml.dist b/upload/system/storage/vendor/twig/twig/phpunit.xml.dist new file mode 100644 index 000000000..24d5bd9ea --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/phpunit.xml.dist @@ -0,0 +1,16 @@ + + + + + + ./tests/ + + + + + + + + + + diff --git a/upload/system/storage/vendor/twig/twig/src/AbstractTwigCallable.php b/upload/system/storage/vendor/twig/twig/src/AbstractTwigCallable.php new file mode 100644 index 000000000..f67184300 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/src/AbstractTwigCallable.php @@ -0,0 +1,136 @@ + + */ +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, + 'deprecated' => false, + 'deprecating_package' => '', + 'alternative' => null, + ], $options); + } + + 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; + } + + 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']; + } + + 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['deprecated']; + } + + public function getDeprecatingPackage(): string + { + return $this->options['deprecating_package']; + } + + public function getDeprecatedVersion(): string + { + return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated']; + } + + public function getAlternative(): ?string + { + 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); + } +} diff --git a/upload/system/storage/vendor/twig/twig/src/Attribute/FirstClassTwigCallableReady.php b/upload/system/storage/vendor/twig/twig/src/Attribute/FirstClassTwigCallableReady.php new file mode 100644 index 000000000..ffd8cffc8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/src/Attribute/FirstClassTwigCallableReady.php @@ -0,0 +1,20 @@ +name) { - if (\is_string($this->name) || (\is_object($this->name) && method_exists($this->name, '__toString'))) { + if (\is_string($this->name) || $this->name instanceof \Stringable) { $name = \sprintf('"%s"', $this->name); } else { $name = json_encode($this->name); diff --git a/upload/system/storage/vendor/twig/twig/src/ExpressionParser.php b/upload/system/storage/vendor/twig/twig/src/ExpressionParser.php index 844844048..7ddbb9370 100644 --- a/upload/system/storage/vendor/twig/twig/src/ExpressionParser.php +++ b/upload/system/storage/vendor/twig/twig/src/ExpressionParser.php @@ -12,6 +12,7 @@ namespace Twig; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Error\SyntaxError; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ArrayExpression; @@ -19,13 +20,11 @@ use Twig\Node\Expression\AssignNameExpression; use Twig\Node\Expression\Binary\AbstractBinary; use Twig\Node\Expression\Binary\ConcatBinary; -use Twig\Node\Expression\BlockReferenceExpression; use Twig\Node\Expression\ConditionalExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\GetAttrExpression; use Twig\Node\Expression\MethodCallExpression; use Twig\Node\Expression\NameExpression; -use Twig\Node\Expression\ParentExpression; use Twig\Node\Expression\TestExpression; use Twig\Node\Expression\Unary\AbstractUnary; use Twig\Node\Expression\Unary\NegUnary; @@ -54,6 +53,7 @@ class ExpressionParser private $unaryOperators; /** @var array, associativity: self::OPERATOR_*}> */ private $binaryOperators; + private $readyNodes = []; public function __construct(Parser $parser, Environment $env) { @@ -105,52 +105,52 @@ private function parseArrow() $stream = $this->parser->getStream(); // short array syntax (one argument, no parentheses)? - if ($stream->look(1)->test(/* Token::ARROW_TYPE */ 12)) { + if ($stream->look(1)->test(Token::ARROW_TYPE)) { $line = $stream->getCurrent()->getLine(); - $token = $stream->expect(/* Token::NAME_TYPE */ 5); + $token = $stream->expect(Token::NAME_TYPE); $names = [new AssignNameExpression($token->getValue(), $token->getLine())]; - $stream->expect(/* Token::ARROW_TYPE */ 12); + $stream->expect(Token::ARROW_TYPE); return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line); } // first, determine if we are parsing an arrow function by finding => (long form) $i = 0; - if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { + if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, '(')) { return null; } ++$i; while (true) { // variable name ++$i; - if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ',')) { break; } ++$i; } - if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) { + if (!$stream->look($i)->test(Token::PUNCTUATION_TYPE, ')')) { return null; } ++$i; - if (!$stream->look($i)->test(/* Token::ARROW_TYPE */ 12)) { + if (!$stream->look($i)->test(Token::ARROW_TYPE)) { return null; } // yes, let's parse it properly - $token = $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '('); + $token = $stream->expect(Token::PUNCTUATION_TYPE, '('); $line = $token->getLine(); $names = []; while (true) { - $token = $stream->expect(/* Token::NAME_TYPE */ 5); + $token = $stream->expect(Token::NAME_TYPE); $names[] = new AssignNameExpression($token->getValue(), $token->getLine()); - if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')'); - $stream->expect(/* Token::ARROW_TYPE */ 12); + $stream->expect(Token::PUNCTUATION_TYPE, ')'); + $stream->expect(Token::ARROW_TYPE); return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line); } @@ -166,10 +166,10 @@ private function getPrimary(): AbstractExpression $class = $operator['class']; return $this->parsePostfixExpression(new $class($expr, $token->getLine())); - } elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { + } elseif ($token->test(Token::PUNCTUATION_TYPE, '(')) { $this->parser->getStream()->next(); $expr = $this->parseExpression(); - $this->parser->getStream()->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'An opened parenthesis is not properly closed'); + $this->parser->getStream()->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); return $this->parsePostfixExpression($expr); } @@ -179,10 +179,10 @@ private function getPrimary(): AbstractExpression private function parseConditionalExpression($expr): AbstractExpression { - while ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, '?')) { - if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { + while ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, '?')) { + if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) { $expr2 = $this->parseExpression(); - if ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { + if ($this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) { // Ternary operator (expr ? expr2 : expr3) $expr3 = $this->parseExpression(); } else { @@ -203,19 +203,19 @@ private function parseConditionalExpression($expr): AbstractExpression private function isUnary(Token $token): bool { - return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->unaryOperators[$token->getValue()]); + return $token->test(Token::OPERATOR_TYPE) && isset($this->unaryOperators[$token->getValue()]); } private function isBinary(Token $token): bool { - return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->binaryOperators[$token->getValue()]); + return $token->test(Token::OPERATOR_TYPE) && isset($this->binaryOperators[$token->getValue()]); } public function parsePrimaryExpression() { $token = $this->parser->getCurrentToken(); switch ($token->getType()) { - case /* Token::NAME_TYPE */ 5: + case Token::NAME_TYPE: $this->parser->getStream()->next(); switch ($token->getValue()) { case 'true': @@ -244,17 +244,17 @@ public function parsePrimaryExpression() } break; - case /* Token::NUMBER_TYPE */ 6: + case Token::NUMBER_TYPE: $this->parser->getStream()->next(); $node = new ConstantExpression($token->getValue(), $token->getLine()); break; - case /* Token::STRING_TYPE */ 7: - case /* Token::INTERPOLATION_START_TYPE */ 10: + case Token::STRING_TYPE: + case Token::INTERPOLATION_START_TYPE: $node = $this->parseStringExpression(); break; - case /* Token::OPERATOR_TYPE */ 8: + case Token::OPERATOR_TYPE: if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) { // in this context, string operators are variable names $this->parser->getStream()->next(); @@ -277,11 +277,11 @@ public function parsePrimaryExpression() // no break default: - if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) { + if ($token->test(Token::PUNCTUATION_TYPE, '[')) { $node = $this->parseSequenceExpression(); - } elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) { + } elseif ($token->test(Token::PUNCTUATION_TYPE, '{')) { $node = $this->parseMappingExpression(); - } elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->look(-1)->getValue())) { + } elseif ($token->test(Token::OPERATOR_TYPE, '=') && ('==' === $this->parser->getStream()->look(-1)->getValue() || '!=' === $this->parser->getStream()->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(), $this->parser->getStream()->getSourceContext()); } else { throw new SyntaxError(\sprintf('Unexpected token "%s" of value "%s".', Token::typeToEnglish($token->getType()), $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); @@ -299,12 +299,12 @@ public function parseStringExpression() // a string cannot be followed by another string in a single expression $nextCanBeString = true; while (true) { - if ($nextCanBeString && $token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) { + if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) { $nodes[] = new ConstantExpression($token->getValue(), $token->getLine()); $nextCanBeString = false; - } elseif ($stream->nextIf(/* Token::INTERPOLATION_START_TYPE */ 10)) { + } elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) { $nodes[] = $this->parseExpression(); - $stream->expect(/* Token::INTERPOLATION_END_TYPE */ 11); + $stream->expect(Token::INTERPOLATION_END_TYPE); $nextCanBeString = true; } else { break; @@ -332,22 +332,22 @@ public function parseArrayExpression() public function parseSequenceExpression() { $stream = $this->parser->getStream(); - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'A sequence element was expected'); + $stream->expect(Token::PUNCTUATION_TYPE, '[', 'A sequence element was expected'); $node = new ArrayExpression([], $stream->getCurrent()->getLine()); $first = true; - while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { + while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) { if (!$first) { - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A sequence element must be followed by a comma'); + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'A sequence element must be followed by a comma'); // trailing ,? - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { + if ($stream->test(Token::PUNCTUATION_TYPE, ']')) { break; } } $first = false; - if ($stream->test(/* Token::SPREAD_TYPE */ 13)) { + if ($stream->test(Token::SPREAD_TYPE)) { $stream->next(); $expr = $this->parseExpression(); $expr->setAttribute('spread', true); @@ -356,7 +356,7 @@ public function parseSequenceExpression() $node->addElement($this->parseExpression()); } } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened sequence is not properly closed'); + $stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened sequence is not properly closed'); return $node; } @@ -374,22 +374,22 @@ public function parseHashExpression() public function parseMappingExpression() { $stream = $this->parser->getStream(); - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '{', 'A mapping element was expected'); + $stream->expect(Token::PUNCTUATION_TYPE, '{', 'A mapping element was expected'); $node = new ArrayExpression([], $stream->getCurrent()->getLine()); $first = true; - while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) { + while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) { if (!$first) { - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A mapping value must be followed by a comma'); + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'A mapping value must be followed by a comma'); // trailing ,? - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) { + if ($stream->test(Token::PUNCTUATION_TYPE, '}')) { break; } } $first = false; - if ($stream->test(/* Token::SPREAD_TYPE */ 13)) { + if ($stream->test(Token::SPREAD_TYPE)) { $stream->next(); $value = $this->parseExpression(); $value->setAttribute('spread', true); @@ -403,7 +403,7 @@ public function parseMappingExpression() // * 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 */ 5)) { + if ($token = $stream->nextIf(Token::NAME_TYPE)) { $key = new ConstantExpression($token->getValue(), $token->getLine()); // {a} is a shortcut for {a:a} @@ -412,9 +412,9 @@ public function parseMappingExpression() $node->addElement($value, $key); continue; } - } elseif (($token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) || $token = $stream->nextIf(/* Token::NUMBER_TYPE */ 6)) { + } elseif (($token = $stream->nextIf(Token::STRING_TYPE)) || $token = $stream->nextIf(Token::NUMBER_TYPE)) { $key = new ConstantExpression($token->getValue(), $token->getLine()); - } elseif ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { + } elseif ($stream->test(Token::PUNCTUATION_TYPE, '(')) { $key = $this->parseExpression(); } else { $current = $stream->getCurrent(); @@ -422,12 +422,12 @@ public function parseMappingExpression() 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".', Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext()); } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ':', 'A mapping key must be followed by a colon (:)'); + $stream->expect(Token::PUNCTUATION_TYPE, ':', 'A mapping key must be followed by a colon (:)'); $value = $this->parseExpression(); $node->addElement($value, $key); } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '}', 'An opened mapping is not properly closed'); + $stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed'); return $node; } @@ -436,7 +436,7 @@ public function parsePostfixExpression($node) { while (true) { $token = $this->parser->getCurrentToken(); - if (/* Token::PUNCTUATION_TYPE */ 9 == $token->getType()) { + if (Token::PUNCTUATION_TYPE == $token->getType()) { if ('.' == $token->getValue() || '[' == $token->getValue()) { $node = $this->parseSubscriptExpression($node); } elseif ('|' == $token->getValue()) { @@ -454,50 +454,37 @@ public function parsePostfixExpression($node) public function getFunctionNode($name, $line) { - switch ($name) { - case 'parent': - $this->parseArguments(); - if (!\count($this->parser->getBlockStack())) { - throw new SyntaxError('Calling "parent" outside a block is forbidden.', $line, $this->parser->getStream()->getSourceContext()); - } + if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) { + $arguments = new ArrayExpression([], $line); + foreach ($this->parseArguments() as $n) { + $arguments->addElement($n); + } - if (!$this->parser->getParent() && !$this->parser->hasTraits()) { - throw new SyntaxError('Calling "parent" on a template that does not extend nor "use" another template is forbidden.', $line, $this->parser->getStream()->getSourceContext()); - } + $node = new MethodCallExpression($alias['node'], $alias['name'], $arguments, $line); + $node->setAttribute('safe', true); - return new ParentExpression($this->parser->peekBlockStack(), $line); - case 'block': - $args = $this->parseArguments(); - if (\count($args) < 1) { - throw new SyntaxError('The "block" function takes one argument (the block name).', $line, $this->parser->getStream()->getSourceContext()); - } + return $node; + } - return new BlockReferenceExpression($args->getNode('0'), \count($args) > 1 ? $args->getNode('1') : null, $line); - case 'attribute': - $args = $this->parseArguments(); - if (\count($args) < 2) { - throw new SyntaxError('The "attribute" function takes at least two arguments (the variable and the attributes).', $line, $this->parser->getStream()->getSourceContext()); - } + $args = $this->parseArguments(true); + $function = $this->getFunction($name, $line); - return new GetAttrExpression($args->getNode('0'), $args->getNode('1'), \count($args) > 2 ? $args->getNode('2') : null, Template::ANY_CALL, $line); - default: - if (null !== $alias = $this->parser->getImportedSymbol('function', $name)) { - $arguments = new ArrayExpression([], $line); - foreach ($this->parseArguments() as $n) { - $arguments->addElement($n); - } + if ($function->getParserCallable()) { + $fakeNode = new Node(lineno: $line); + $fakeNode->setSourceContext($this->parser->getStream()->getSourceContext()); - $node = new MethodCallExpression($alias['node'], $alias['name'], $arguments, $line); - $node->setAttribute('safe', true); - - return $node; - } + return ($function->getParserCallable())($this->parser, $fakeNode, $args, $line); + } - $args = $this->parseArguments(true); - $class = $this->getFunctionNodeClass($name, $line); + if (!isset($this->readyNodes[$class = $function->getNodeClass()])) { + $this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class); + } - return new $class($name, $args, $line); + 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 parseSubscriptExpression($node) @@ -510,15 +497,15 @@ public function parseSubscriptExpression($node) if ('.' == $token->getValue()) { $token = $stream->next(); if ( - /* Token::NAME_TYPE */ 5 == $token->getType() + Token::NAME_TYPE == $token->getType() || - /* Token::NUMBER_TYPE */ 6 == $token->getType() + Token::NUMBER_TYPE == $token->getType() || - (/* Token::OPERATOR_TYPE */ 8 == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue())) + (Token::OPERATOR_TYPE == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue())) ) { $arg = new ConstantExpression($token->getValue(), $lineno); - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { + if ($stream->test(Token::PUNCTUATION_TYPE, '(')) { $type = Template::METHOD_CALL; foreach ($this->parseArguments() as $n) { $arguments->addElement($n); @@ -541,34 +528,34 @@ public function parseSubscriptExpression($node) // slice? $slice = false; - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ':')) { + if ($stream->test(Token::PUNCTUATION_TYPE, ':')) { $slice = true; $arg = new ConstantExpression(0, $token->getLine()); } else { $arg = $this->parseExpression(); } - if ($stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { + if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) { $slice = true; } if ($slice) { - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { + if ($stream->test(Token::PUNCTUATION_TYPE, ']')) { $length = new ConstantExpression(null, $token->getLine()); } else { $length = $this->parseExpression(); } - $class = $this->getFilterNodeClass('slice', $token->getLine()); + $filter = $this->getFilter('slice', $token->getLine()); $arguments = new Node([$arg, $length]); - $filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine()); + $filter = new ($filter->getNodeClass())($node, $filter, $arguments, $token->getLine()); - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']'); + $stream->expect(Token::PUNCTUATION_TYPE, ']'); return $filter; } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']'); + $stream->expect(Token::PUNCTUATION_TYPE, ']'); } return new GetAttrExpression($node, $arg, $arguments, $type, $lineno); @@ -581,23 +568,35 @@ public function parseFilterExpression($node) return $this->parseFilterExpressionRaw($node); } - public function parseFilterExpressionRaw($node, $tag = null) + public function parseFilterExpressionRaw($node) { + if (func_num_args() > 1) { + trigger_deprecation('twig/twig', '3.12', 'Passing a second argument to "%s()" is deprecated.', __METHOD__); + } + while (true) { - $token = $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5); + $token = $this->parser->getStream()->expect(Token::NAME_TYPE); - $name = new ConstantExpression($token->getValue(), $token->getLine()); - if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { + if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '(')) { $arguments = new Node(); } else { $arguments = $this->parseArguments(true, false, true); } - $class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine()); + $filter = $this->getFilter($token->getValue(), $token->getLine()); + + $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); + } - $node = new $class($node, $name, $arguments, $token->getLine(), $tag); + $node = new $class($node, $ready ? $filter : new ConstantExpression($filter->getName(), $token->getLine()), $arguments, $token->getLine()); - if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '|')) { + if (!$this->parser->getStream()->test(Token::PUNCTUATION_TYPE, '|')) { break; } @@ -611,7 +610,7 @@ public function parseFilterExpressionRaw($node, $tag = null) * Parses arguments. * * @param bool $namedArguments Whether to allow named arguments or not - * @param bool $definition Whether we are parsing arguments for a function definition + * @param bool $definition Whether we are parsing arguments for a function (or macro) definition * * @return Node * @@ -622,26 +621,26 @@ public function parseArguments($namedArguments = false, $definition = false, $al $args = []; $stream = $this->parser->getStream(); - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(', 'A list of arguments must begin with an opening parenthesis'); - while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) { + $stream->expect(Token::PUNCTUATION_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); + while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) { if (!empty($args)) { - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'Arguments must be separated by a comma'); + $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 */ 9, ')')) { + if ($stream->test(Token::PUNCTUATION_TYPE, ')')) { break; } } if ($definition) { - $token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'An argument must be a name'); + $token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name'); $value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine()); } else { $value = $this->parseExpression(0, $allowArrow); } $name = null; - if ($namedArguments && $token = $stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) { + if ($namedArguments && (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':')))) { if (!$value instanceof NameExpression) { throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext()); } @@ -662,6 +661,7 @@ public function parseArguments($namedArguments = false, $definition = false, $al if (null === $name) { $name = $value->getAttribute('name'); $value = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine()); + $value->setAttribute('is_implicit', true); } $args[$name] = $value; } else { @@ -672,7 +672,7 @@ public function parseArguments($namedArguments = false, $definition = false, $al } } } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'A list of arguments must be closed by a parenthesis'); + $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); return new Node($args); } @@ -683,11 +683,11 @@ public function parseAssignmentExpression() $targets = []; while (true) { $token = $this->parser->getCurrentToken(); - if ($stream->test(/* Token::OPERATOR_TYPE */ 8) && preg_match(Lexer::REGEX_NAME, $token->getValue())) { + 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 */ 5, null, 'Only variables can be assigned to'); + $stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to'); } $value = $token->getValue(); if (\in_array(strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'), ['true', 'false', 'none', 'null'])) { @@ -695,7 +695,7 @@ public function parseAssignmentExpression() } $targets[] = new AssignNameExpression($value, $token->getLine()); - if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } } @@ -708,7 +708,7 @@ public function parseMultitargetExpression() $targets = []; while (true) { $targets[] = $this->parseExpression(); - if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } } @@ -724,52 +724,55 @@ private function parseNotTestExpression(Node $node): NotUnary private function parseTestExpression(Node $node): TestExpression { $stream = $this->parser->getStream(); - [$name, $test] = $this->getTest($node->getTemplateLine()); + $test = $this->getTest($node->getTemplateLine()); - $class = $this->getTestNodeClass($test); $arguments = null; - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { + if ($stream->test(Token::PUNCTUATION_TYPE, '(')) { $arguments = $this->parseArguments(true); } elseif ($test->hasOneMandatoryArgument()) { $arguments = new Node([0 => $this->parsePrimaryExpression()]); } - if ('defined' === $name && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) { + if ('defined' === $test->getName() && $node instanceof NameExpression && null !== $alias = $this->parser->getImportedSymbol('function', $node->getAttribute('name'))) { $node = new MethodCallExpression($alias['node'], $alias['name'], new ArrayExpression([], $node->getTemplateLine()), $node->getTemplateLine()); $node->setAttribute('safe', true); } - return new $class($node, $name, $arguments, $this->parser->getCurrentToken()->getLine()); + $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($node, $ready ? $test : $test->getName(), $arguments, $this->parser->getCurrentToken()->getLine()); } - private function getTest(int $line): array + private function getTest(int $line): TwigTest { $stream = $this->parser->getStream(); - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + $name = $stream->expect(Token::NAME_TYPE)->getValue(); - if ($test = $this->env->getTest($name)) { - return [$name, $test]; - } - - if ($stream->test(/* Token::NAME_TYPE */ 5)) { + if ($stream->test(Token::NAME_TYPE)) { // try 2-words tests $name = $name.' '.$this->parser->getCurrentToken()->getValue(); if ($test = $this->env->getTest($name)) { $stream->next(); - - return [$name, $test]; } + } else { + $test = $this->env->getTest($name); } - $e = new SyntaxError(\sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()); - $e->addSuggestions($name, array_keys($this->env->getTests())); + if (!$test) { + $e = new SyntaxError(\sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()); + $e->addSuggestions($name, array_keys($this->env->getTests())); - throw $e; - } + throw $e; + } - private function getTestNodeClass(TwigTest $test): string - { if ($test->isDeprecated()) { $stream = $this->parser->getStream(); $message = \sprintf('Twig Test "%s" is deprecated', $test->getName()); @@ -783,10 +786,10 @@ private function getTestNodeClass(TwigTest $test): string trigger_deprecation($test->getDeprecatingPackage(), $test->getDeprecatedVersion(), $message); } - return $test->getNodeClass(); + return $test; } - private function getFunctionNodeClass(string $name, int $line): string + private function getFunction(string $name, int $line): TwigFunction { if (!$function = $this->env->getFunction($name)) { $e = new SyntaxError(\sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext()); @@ -806,10 +809,10 @@ private function getFunctionNodeClass(string $name, int $line): string trigger_deprecation($function->getDeprecatingPackage(), $function->getDeprecatedVersion(), $message); } - return $function->getNodeClass(); + return $function; } - private function getFilterNodeClass(string $name, int $line): string + private function getFilter(string $name, int $line): TwigFilter { if (!$filter = $this->env->getFilter($name)) { $e = new SyntaxError(\sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext()); @@ -829,7 +832,7 @@ private function getFilterNodeClass(string $name, int $line): string trigger_deprecation($filter->getDeprecatingPackage(), $filter->getDeprecatedVersion(), $message); } - return $filter->getNodeClass(); + return $filter; } // checks that the node only contains "constant" elements diff --git a/upload/system/storage/vendor/twig/twig/src/Extension/CoreExtension.php b/upload/system/storage/vendor/twig/twig/src/Extension/CoreExtension.php index 5ac80884a..fdb11dc7a 100644 --- a/upload/system/storage/vendor/twig/twig/src/Extension/CoreExtension.php +++ b/upload/system/storage/vendor/twig/twig/src/Extension/CoreExtension.php @@ -14,8 +14,10 @@ use Twig\Environment; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; +use Twig\Error\SyntaxError; use Twig\ExpressionParser; use Twig\Markup; +use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\Binary\AddBinary; use Twig\Node\Expression\Binary\AndBinary; use Twig\Node\Expression\Binary\BitwiseAndBinary; @@ -44,8 +46,12 @@ use Twig\Node\Expression\Binary\SpaceshipBinary; use Twig\Node\Expression\Binary\StartsWithBinary; use Twig\Node\Expression\Binary\SubBinary; +use Twig\Node\Expression\BlockReferenceExpression; use Twig\Node\Expression\Filter\DefaultFilter; +use Twig\Node\Expression\FunctionNode\EnumCasesFunction; +use Twig\Node\Expression\GetAttrExpression; use Twig\Node\Expression\NullCoalesceExpression; +use Twig\Node\Expression\ParentExpression; use Twig\Node\Expression\Test\ConstantTest; use Twig\Node\Expression\Test\DefinedTest; use Twig\Node\Expression\Test\DivisiblebyTest; @@ -56,7 +62,9 @@ use Twig\Node\Expression\Unary\NegUnary; use Twig\Node\Expression\Unary\NotUnary; use Twig\Node\Expression\Unary\PosUnary; +use Twig\Node\Node; use Twig\NodeVisitor\MacroAutoImportNodeVisitor; +use Twig\Parser; use Twig\Source; use Twig\Template; use Twig\TemplateWrapper; @@ -79,6 +87,7 @@ use Twig\TwigFilter; use Twig\TwigFunction; use Twig\TwigTest; +use Twig\Util\CallableArgumentsExtractor; final class CoreExtension extends AbstractExtension { @@ -206,7 +215,7 @@ public function getFilters(): array new TwigFilter('striptags', [self::class, 'striptags']), new TwigFilter('trim', [self::class, 'trim']), new TwigFilter('nl2br', [self::class, 'nl2br'], ['pre_escape' => 'html', 'is_safe' => ['html']]), - new TwigFilter('spaceless', [self::class, 'spaceless'], ['is_safe' => ['html']]), + new TwigFilter('spaceless', [self::class, 'spaceless'], ['is_safe' => ['html'], 'deprecated' => '3.12', 'deprecating_package' => 'twig/twig']), // array helpers new TwigFilter('join', [self::class, 'join']), @@ -237,6 +246,9 @@ public function getFilters(): array public function getFunctions(): array { return [ + new TwigFunction('parent', null, ['parser_callable' => [self::class, 'parseParentFunction']]), + new TwigFunction('block', null, ['parser_callable' => [self::class, 'parseBlockFunction']]), + new TwigFunction('attribute', null, ['parser_callable' => [self::class, 'parseAttributeFunction']]), new TwigFunction('max', 'max'), new TwigFunction('min', 'min'), new TwigFunction('range', 'range'), @@ -246,6 +258,7 @@ public function getFunctions(): array new TwigFunction('date', [$this, 'convertDate']), new TwigFunction('include', [self::class, 'include'], ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['all']]), new TwigFunction('source', [self::class, 'source'], ['needs_environment' => true, 'is_safe' => ['all']]), + new TwigFunction('enum_cases', [self::class, 'enumCases'], ['node_class' => EnumCasesFunction::class]), ]; } @@ -804,9 +817,6 @@ public static function split(string $charset, $value, $delimiter, $limit = null) return $r; } - // The '_default' filter is used internally to avoid using the ternary operator - // which costs a lot for big contexts (before PHP 5.4). So, on average, - // a function call is cheaper. /** * @internal */ @@ -1181,7 +1191,7 @@ public static function length(string $charset, $thing): int return iterator_count($thing); } - if (method_exists($thing, '__toString')) { + if ($thing instanceof \Stringable) { return mb_strlen((string) $thing, $charset); } @@ -1269,6 +1279,12 @@ public static function callMacro(Template $template, string $method, array $args } /** + * @template TSequence + * + * @param TSequence $seq + * + * @return ($seq is iterable ? TSequence : array{}) + * * @internal */ public static function ensureTraversable($seq) @@ -1318,7 +1334,7 @@ public static function testEmpty($value): bool return !iterator_count($value); } - if (\is_object($value) && method_exists($value, '__toString')) { + if ($value instanceof \Stringable) { return '' === (string) $value; } @@ -1449,28 +1465,54 @@ public static function source(Environment $env, $name, $ignoreMissing = false): } } + /** + * Returns the list of cases of the enum. + * + * @template T of \UnitEnum + * + * @param class-string $enum + * + * @return list + * + * @internal + */ + public static function enumCases(string $enum): array + { + if (!enum_exists($enum)) { + throw new RuntimeError(\sprintf('Enum "%s" does not exist.', $enum)); + } + + return $enum::cases(); + } + /** * Provides the ability to get constants from instances as well as class/global constants. * - * @param string $constant The name of the constant - * @param object|null $object The object to get the constant from + * @param string $constant The name of the constant + * @param object|null $object The object to get the constant from + * @param bool $checkDefined Whether to check if the constant is defined or not * * @return mixed Class constants can return many types like scalars, arrays, and * objects depending on the PHP version (\BackedEnum, \UnitEnum, etc.) + * When $checkDefined is true, returns true when the constant is defined, false otherwise * * @internal */ - public static function constant($constant, $object = null) + public static function constant($constant, $object = null, bool $checkDefined = false) { if (null !== $object) { if ('class' === $constant) { - return \get_class($object); + return $checkDefined ? true : \get_class($object); } $constant = \get_class($object).'::'.$constant; } if (!\defined($constant)) { + if ($checkDefined) { + return false; + } + if ('::class' === strtolower(substr($constant, -7))) { throw new RuntimeError(\sprintf('You cannot use the Twig function "constant()" to access "%s". You could provide an object and call constant("class", $object) or use the class name directly as a string.', $constant)); } @@ -1478,28 +1520,7 @@ public static function constant($constant, $object = null) throw new RuntimeError(\sprintf('Constant "%s" is undefined.', $constant)); } - return \constant($constant); - } - - /** - * Checks if a constant exists. - * - * @param string $constant The name of the constant - * @param object|null $object The object to get the constant from - * - * @internal - */ - public static function constantIsDefined($constant, $object = null): bool - { - if (null !== $object) { - if ('class' === $constant) { - return true; - } - - $constant = \get_class($object).'::'.$constant; - } - - return \defined($constant); + return $checkDefined ? true : \constant($constant); } /** @@ -1550,10 +1571,10 @@ public static function batch($items, $size, $fill = null, $preserveKeys = true): * * @internal */ - public static function getAttribute(Environment $env, Source $source, $object, $item, array $arguments = [], $type = /* Template::ANY_CALL */ 'any', $isDefinedTest = false, $ignoreStrictCheck = false, $sandboxed = false, int $lineno = -1) + public static function getAttribute(Environment $env, Source $source, $object, $item, array $arguments = [], $type = Template::ANY_CALL, $isDefinedTest = false, $ignoreStrictCheck = false, $sandboxed = false, int $lineno = -1) { // array - if (/* Template::METHOD_CALL */ 'method' !== $type) { + if (Template::METHOD_CALL !== $type) { $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item; if (((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object))) @@ -1566,7 +1587,7 @@ public static function getAttribute(Environment $env, Source $source, $object, $ return $object[$arrayItem]; } - if (/* Template::ARRAY_CALL */ 'array' === $type || !\is_object($object)) { + if (Template::ARRAY_CALL === $type || !\is_object($object)) { if ($isDefinedTest) { return false; } @@ -1585,7 +1606,7 @@ public static function getAttribute(Environment $env, Source $source, $object, $ } else { $message = \sprintf('Key "%s" for sequence/mapping with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object))); } - } elseif (/* Template::ARRAY_CALL */ 'array' === $type) { + } elseif (Template::ARRAY_CALL === $type) { if (null === $object) { $message = \sprintf('Impossible to access a key ("%s") on a null variable.', $item); } else { @@ -1626,7 +1647,7 @@ public static function getAttribute(Environment $env, Source $source, $object, $ } // object property - if (/* Template::METHOD_CALL */ 'method' !== $type) { + if (Template::METHOD_CALL !== $type) { if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) { if ($isDefinedTest) { return true; @@ -1873,29 +1894,59 @@ public static function checkArrowInSandbox(Environment $env, $arrow, $thing, $ty */ public static function captureOutput(iterable $body): string { - $output = ''; $level = ob_get_level(); ob_start(); try { foreach ($body as $data) { - if (ob_get_length()) { - $output .= ob_get_clean(); - ob_start(); - } - - $output .= $data; - } - - if (ob_get_length()) { - $output .= ob_get_clean(); + echo $data; } - } finally { + } catch (\Throwable $e) { while (ob_get_level() > $level) { ob_end_clean(); } + + throw $e; + } + + return ob_get_clean(); + } + + /** + * @internal + */ + public static function parseParentFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression + { + if (!$blockName = $parser->peekBlockStack()) { + throw new SyntaxError('Calling the "parent" function outside of a block is forbidden.', $line, $parser->getStream()->getSourceContext()); } - return $output; + if (!$parser->hasInheritance()) { + throw new SyntaxError('Calling the "parent" function on a template that does not call "extends" or "use" is forbidden.', $line, $parser->getStream()->getSourceContext()); + } + + return new ParentExpression($blockName, $line); + } + + /** + * @internal + */ + public static function parseBlockFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression + { + $fakeFunction = new TwigFunction('block', fn ($name, $template = null) => null); + $args = (new CallableArgumentsExtractor($fakeNode, $fakeFunction))->extractArguments($args); + + return new BlockReferenceExpression($args[0], $args[1] ?? null, $line); + } + + /** + * @internal + */ + public static function parseAttributeFunction(Parser $parser, Node $fakeNode, $args, int $line): AbstractExpression + { + $fakeFunction = new TwigFunction('attribute', fn ($variable, $attribute, $arguments = null) => null); + $args = (new CallableArgumentsExtractor($fakeNode, $fakeFunction))->extractArguments($args); + + return new GetAttrExpression($args[0], $args[1], $args[2] ?? null, Template::ANY_CALL, $line); } } diff --git a/upload/system/storage/vendor/twig/twig/src/Extension/EscaperExtension.php b/upload/system/storage/vendor/twig/twig/src/Extension/EscaperExtension.php index d8e9b6e48..52531c436 100644 --- a/upload/system/storage/vendor/twig/twig/src/Extension/EscaperExtension.php +++ b/upload/system/storage/vendor/twig/twig/src/Extension/EscaperExtension.php @@ -60,8 +60,9 @@ public function getFilters(): array /** * @deprecated since Twig 3.10 */ - public function setEnvironment(Environment $environment, bool $triggerDeprecation = true): void + 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__); } diff --git a/upload/system/storage/vendor/twig/twig/src/Extension/SandboxExtension.php b/upload/system/storage/vendor/twig/twig/src/Extension/SandboxExtension.php index 921df287a..4e96760f7 100644 --- a/upload/system/storage/vendor/twig/twig/src/Extension/SandboxExtension.php +++ b/upload/system/storage/vendor/twig/twig/src/Extension/SandboxExtension.php @@ -119,7 +119,7 @@ public function checkPropertyAllowed($obj, $property, int $lineno = -1, ?Source public function ensureToStringAllowed($obj, int $lineno = -1, ?Source $source = null) { - if ($this->isSandboxed($source) && \is_object($obj) && method_exists($obj, '__toString')) { + if ($this->isSandboxed($source) && $obj instanceof \Stringable) { try { $this->policy->checkMethodAllowed($obj, '__toString'); } catch (SecurityNotAllowedMethodError $e) { diff --git a/upload/system/storage/vendor/twig/twig/src/Extension/StringLoaderExtension.php b/upload/system/storage/vendor/twig/twig/src/Extension/StringLoaderExtension.php index 12f5c30aa..698d181f1 100644 --- a/upload/system/storage/vendor/twig/twig/src/Extension/StringLoaderExtension.php +++ b/upload/system/storage/vendor/twig/twig/src/Extension/StringLoaderExtension.php @@ -29,12 +29,11 @@ public function getFunctions(): array * * {{ include(template_from_string("Hello {{ name }}")) }} * - * @param string $template A template as a string or object implementing __toString() - * @param string|null $name An optional name of the template to be used in error messages + * @param string|null $name An optional name of the template to be used in error messages * * @internal */ - public static function templateFromString(Environment $env, $template, ?string $name = null): TemplateWrapper + public static function templateFromString(Environment $env, string|\Stringable $template, ?string $name = null): TemplateWrapper { return $env->createTemplate((string) $template, $name); } diff --git a/upload/system/storage/vendor/twig/twig/src/ExtensionSet.php b/upload/system/storage/vendor/twig/twig/src/ExtensionSet.php index 8b59a13e1..32377b0fc 100644 --- a/upload/system/storage/vendor/twig/twig/src/ExtensionSet.php +++ b/upload/system/storage/vendor/twig/twig/src/ExtensionSet.php @@ -36,10 +36,16 @@ final class ExtensionSet private $visitors; /** @var array */ private $filters; + /** @var array */ + private $dynamicFilters; /** @var array */ private $tests; + /** @var array */ + private $dynamicTests; /** @var array */ private $functions; + /** @var array */ + private $dynamicFunctions; /** @var array}> */ private $unaryOperators; /** @var array, associativity: ExpressionParser::OPERATOR_*}> */ @@ -167,14 +173,11 @@ public function getFunction(string $name): ?TwigFunction return $this->functions[$name]; } - foreach ($this->functions as $pattern => $function) { - $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); - - if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) { + foreach ($this->dynamicFunctions as $pattern => $function) { + if (preg_match($pattern, $name, $matches)) { array_shift($matches); - $function->setArguments($matches); - return $function; + return $function->withDynamicArguments($name, $function->getName(), $matches); } } @@ -223,14 +226,11 @@ public function getFilter(string $name): ?TwigFilter return $this->filters[$name]; } - foreach ($this->filters as $pattern => $filter) { - $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); - - if ($count && preg_match('#^'.$pattern.'$#', $name, $matches)) { + foreach ($this->dynamicFilters as $pattern => $filter) { + if (preg_match($pattern, $name, $matches)) { array_shift($matches); - $filter->setArguments($matches); - return $filter; + return $filter->withDynamicArguments($name, $filter->getName(), $matches); } } @@ -375,16 +375,11 @@ public function getTest(string $name): ?TwigTest return $this->tests[$name]; } - foreach ($this->tests as $pattern => $test) { - $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); - - if ($count) { - if (preg_match('#^'.$pattern.'$#', $name, $matches)) { - array_shift($matches); - $test->setArguments($matches); + foreach ($this->dynamicTests as $pattern => $test) { + if (preg_match($pattern, $name, $matches)) { + array_shift($matches); - return $test; - } + return $test->withDynamicArguments($name, $test->getName(), $matches); } } @@ -421,6 +416,9 @@ private function initExtensions(): void $this->filters = []; $this->functions = []; $this->tests = []; + $this->dynamicFilters = []; + $this->dynamicFunctions = []; + $this->dynamicTests = []; $this->visitors = []; $this->unaryOperators = []; $this->binaryOperators = []; @@ -437,17 +435,26 @@ private function initExtension(ExtensionInterface $extension): void { // filters foreach ($extension->getFilters() as $filter) { - $this->filters[$filter->getName()] = $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[$function->getName()] = $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[$test->getName()] = $test; + $this->tests[$name = $test->getName()] = $test; + if (str_contains($name, '*')) { + $this->dynamicTests['#^'.str_replace('\\*', '(.*?)', preg_quote($name, '#')).'$#'] = $test; + } } // token parsers diff --git a/upload/system/storage/vendor/twig/twig/src/Lexer.php b/upload/system/storage/vendor/twig/twig/src/Lexer.php index 8973fbbc8..28feaa2c1 100644 --- a/upload/system/storage/vendor/twig/twig/src/Lexer.php +++ b/upload/system/storage/vendor/twig/twig/src/Lexer.php @@ -50,6 +50,14 @@ class Lexer public const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As'; 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; @@ -207,7 +215,7 @@ public function tokenize(Source $source): TokenStream } } - $this->pushToken(/* Token::EOF_TYPE */ -1); + $this->pushToken(Token::EOF_TYPE); if (!empty($this->brackets)) { [$expect, $lineno] = array_pop($this->brackets); @@ -221,7 +229,7 @@ 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 */ 0, substr($this->code, $this->cursor)); + $this->pushToken(Token::TEXT_TYPE, substr($this->code, $this->cursor)); $this->cursor = $this->end; return; @@ -250,7 +258,7 @@ private function lexData(): void $text = rtrim($text, " \t\0\x0B"); } } - $this->pushToken(/* Token::TEXT_TYPE */ 0, $text); + $this->pushToken(Token::TEXT_TYPE, $text); $this->moveCursor($textContent.$position[0]); switch ($this->positions[1][$this->position][0]) { @@ -268,14 +276,14 @@ private function lexData(): void $this->moveCursor($match[0]); $this->lineno = (int) $match[1]; } else { - $this->pushToken(/* Token::BLOCK_START_TYPE */ 1); + $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 */ 2); + $this->pushToken(Token::VAR_START_TYPE); $this->pushState(self::STATE_VAR); $this->currentVarBlockLine = $this->lineno; break; @@ -285,7 +293,7 @@ private function lexData(): void private function lexBlock(): void { if (empty($this->brackets) && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) { - $this->pushToken(/* Token::BLOCK_END_TYPE */ 3); + $this->pushToken(Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->popState(); } else { @@ -296,7 +304,7 @@ private function lexBlock(): void private function lexVar(): void { if (empty($this->brackets) && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) { - $this->pushToken(/* Token::VAR_END_TYPE */ 4); + $this->pushToken(Token::VAR_END_TYPE); $this->moveCursor($match[0]); $this->popState(); } else { @@ -327,12 +335,12 @@ private function lexExpression(): void } // operators elseif (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) { - $this->pushToken(/* Token::OPERATOR_TYPE */ 8, preg_replace('/\s+/', ' ', $match[0])); + $this->pushToken(Token::OPERATOR_TYPE, preg_replace('/\s+/', ' ', $match[0])); $this->moveCursor($match[0]); } // names elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) { - $this->pushToken(/* Token::NAME_TYPE */ 5, $match[0]); + $this->pushToken(Token::NAME_TYPE, $match[0]); $this->moveCursor($match[0]); } // numbers @@ -341,7 +349,7 @@ private function lexExpression(): void if (ctype_digit($match[0]) && $number <= \PHP_INT_MAX) { $number = (int) $match[0]; // integers lower than the maximum } - $this->pushToken(/* Token::NUMBER_TYPE */ 6, $number); + $this->pushToken(Token::NUMBER_TYPE, $number); $this->moveCursor($match[0]); } // punctuation @@ -362,12 +370,12 @@ private function lexExpression(): void } } - $this->pushToken(/* Token::PUNCTUATION_TYPE */ 9, $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 */ 7, stripcslashes(substr($match[0], 1, -1))); + $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 @@ -382,6 +390,63 @@ private function lexExpression(): void } } + 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" at position %d should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.', $nextChar, $i + 1); + } + $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" at position %d should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.', $nextChar, $i + 1); + $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)) { @@ -403,7 +468,7 @@ private function lexRawData(): void } } - $this->pushToken(/* Token::TEXT_TYPE */ 0, $text); + $this->pushToken(Token::TEXT_TYPE, $text); } private function lexComment(): void @@ -419,11 +484,11 @@ 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 */ 10); + $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 */ 7, stripcslashes($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); @@ -444,7 +509,7 @@ 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 */ 11); + $this->pushToken(Token::INTERPOLATION_END_TYPE); $this->moveCursor($match[0]); $this->popState(); } else { @@ -455,7 +520,7 @@ private function lexInterpolation(): void private function pushToken($type, $value = ''): void { // do not push empty text tokens - if (/* Token::TEXT_TYPE */ 0 === $type && '' === $value) { + if (Token::TEXT_TYPE === $type && '' === $value) { return; } diff --git a/upload/system/storage/vendor/twig/twig/src/Loader/ChainLoader.php b/upload/system/storage/vendor/twig/twig/src/Loader/ChainLoader.php index 163c029f8..90f798db3 100644 --- a/upload/system/storage/vendor/twig/twig/src/Loader/ChainLoader.php +++ b/upload/system/storage/vendor/twig/twig/src/Loader/ChainLoader.php @@ -21,22 +21,33 @@ */ final class ChainLoader implements LoaderInterface { + /** + * @var \Traversable|LoaderInterface[] + */ + private $loaders; + + /** + * @var array + */ private $hasSourceCache = []; - private $loaders = []; /** - * @param LoaderInterface[] $loaders + * @param iterable $loaders */ - public function __construct(array $loaders = []) + public function __construct(iterable $loaders = []) { - foreach ($loaders as $loader) { - $this->addLoader($loader); - } + $this->loaders = $loaders; } public function addLoader(LoaderInterface $loader): void { - $this->loaders[] = $loader; + $current = $this->loaders; + + $this->loaders = (static function () use ($current, $loader): \Generator { + yield from $current; + yield $loader; + })(); + $this->hasSourceCache = []; } @@ -45,13 +56,18 @@ public function addLoader(LoaderInterface $loader): void */ 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->loaders as $loader) { + + foreach ($this->getLoaders() as $loader) { if (!$loader->exists($name)) { continue; } @@ -72,7 +88,7 @@ public function exists(string $name): bool return $this->hasSourceCache[$name]; } - foreach ($this->loaders as $loader) { + foreach ($this->getLoaders() as $loader) { if ($loader->exists($name)) { return $this->hasSourceCache[$name] = true; } @@ -84,7 +100,8 @@ public function exists(string $name): bool public function getCacheKey(string $name): string { $exceptions = []; - foreach ($this->loaders as $loader) { + + foreach ($this->getLoaders() as $loader) { if (!$loader->exists($name)) { continue; } @@ -102,7 +119,8 @@ public function getCacheKey(string $name): string public function isFresh(string $name, int $time): bool { $exceptions = []; - foreach ($this->loaders as $loader) { + + foreach ($this->getLoaders() as $loader) { if (!$loader->exists($name)) { continue; } diff --git a/upload/system/storage/vendor/twig/twig/src/Markup.php b/upload/system/storage/vendor/twig/twig/src/Markup.php index 1788acc4f..4e83c9184 100644 --- a/upload/system/storage/vendor/twig/twig/src/Markup.php +++ b/upload/system/storage/vendor/twig/twig/src/Markup.php @@ -16,7 +16,7 @@ * * @author Fabien Potencier */ -class Markup implements \Countable, \JsonSerializable +class Markup implements \Countable, \JsonSerializable, \Stringable { private $content; private $charset; diff --git a/upload/system/storage/vendor/twig/twig/src/Node/AutoEscapeNode.php b/upload/system/storage/vendor/twig/twig/src/Node/AutoEscapeNode.php index f9bc17e07..ee806396e 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/AutoEscapeNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/AutoEscapeNode.php @@ -28,9 +28,9 @@ #[YieldReady] class AutoEscapeNode extends Node { - public function __construct($value, Node $body, int $lineno, string $tag = 'autoescape') + public function __construct($value, Node $body, int $lineno) { - parent::__construct(['body' => $body], ['value' => $value], $lineno, $tag); + parent::__construct(['body' => $body], ['value' => $value], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/BlockNode.php b/upload/system/storage/vendor/twig/twig/src/Node/BlockNode.php index 15973a343..d2cfc3bd8 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/BlockNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/BlockNode.php @@ -23,9 +23,9 @@ #[YieldReady] class BlockNode extends Node { - public function __construct(string $name, Node $body, int $lineno, ?string $tag = null) + public function __construct(string $name, Node $body, int $lineno) { - parent::__construct(['body' => $body], ['name' => $name], $lineno, $tag); + parent::__construct(['body' => $body], ['name' => $name], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/BlockReferenceNode.php b/upload/system/storage/vendor/twig/twig/src/Node/BlockReferenceNode.php index 23c73eabe..7c313a04c 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/BlockReferenceNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/BlockReferenceNode.php @@ -23,9 +23,9 @@ #[YieldReady] class BlockReferenceNode extends Node implements NodeOutputInterface { - public function __construct(string $name, int $lineno, ?string $tag = null) + public function __construct(string $name, int $lineno) { - parent::__construct([], ['name' => $name], $lineno, $tag); + parent::__construct([], ['name' => $name], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/CaptureNode.php b/upload/system/storage/vendor/twig/twig/src/Node/CaptureNode.php index b1cb357f5..0162113c1 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/CaptureNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/CaptureNode.php @@ -22,9 +22,9 @@ #[YieldReady] class CaptureNode extends Node { - public function __construct(Node $body, int $lineno, ?string $tag = null) + public function __construct(Node $body, int $lineno) { - parent::__construct(['body' => $body], ['raw' => false], $lineno, $tag); + parent::__construct(['body' => $body], ['raw' => false], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/CheckToStringNode.php b/upload/system/storage/vendor/twig/twig/src/Node/CheckToStringNode.php index 81fb92404..937240c1d 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/CheckToStringNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/CheckToStringNode.php @@ -30,7 +30,7 @@ class CheckToStringNode extends AbstractExpression { public function __construct(AbstractExpression $expr) { - parent::__construct(['expr' => $expr], [], $expr->getTemplateLine(), $expr->getNodeTag()); + parent::__construct(['expr' => $expr], [], $expr->getTemplateLine()); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/DeprecatedNode.php b/upload/system/storage/vendor/twig/twig/src/Node/DeprecatedNode.php index afeb8332e..0772adfc3 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/DeprecatedNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/DeprecatedNode.php @@ -24,9 +24,9 @@ #[YieldReady] class DeprecatedNode extends Node { - public function __construct(AbstractExpression $expr, int $lineno, ?string $tag = null) + public function __construct(AbstractExpression $expr, int $lineno) { - parent::__construct(['expr' => $expr], [], $lineno, $tag); + parent::__construct(['expr' => $expr], [], $lineno); } public function compile(Compiler $compiler): void @@ -65,7 +65,7 @@ public function compile(Compiler $compiler): void } $compiler - ->raw(".") + ->raw('.') ->string(\sprintf(' in "%s" at line %d.', $this->getTemplateName(), $this->getTemplateLine())) ->raw(");\n") ; diff --git a/upload/system/storage/vendor/twig/twig/src/Node/DoNode.php b/upload/system/storage/vendor/twig/twig/src/Node/DoNode.php index 445016ab2..1593fd050 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/DoNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/DoNode.php @@ -23,9 +23,9 @@ #[YieldReady] class DoNode extends Node { - public function __construct(AbstractExpression $expr, int $lineno, ?string $tag = null) + public function __construct(AbstractExpression $expr, int $lineno) { - parent::__construct(['expr' => $expr], [], $lineno, $tag); + parent::__construct(['expr' => $expr], [], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/EmbedNode.php b/upload/system/storage/vendor/twig/twig/src/Node/EmbedNode.php index 545509462..4cd3b38f2 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/EmbedNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/EmbedNode.php @@ -25,9 +25,9 @@ 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, ?string $tag = null) + 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, $tag); + parent::__construct(new ConstantExpression('not_used', $lineno), $variables, $only, $ignoreMissing, $lineno); $this->setAttribute('name', $name); $this->setAttribute('index', $index); diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php index eaad03c9c..2bae4edd7 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php @@ -21,9 +21,9 @@ */ class ArrowFunctionExpression extends AbstractExpression { - public function __construct(AbstractExpression $expr, Node $names, $lineno, $tag = null) + public function __construct(AbstractExpression $expr, Node $names, $lineno) { - parent::__construct(['expr' => $expr, 'names' => $names], [], $lineno, $tag); + parent::__construct(['expr' => $expr, 'names' => $names], [], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php index 629382233..acd231e15 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php @@ -22,14 +22,14 @@ */ class BlockReferenceExpression extends AbstractExpression { - public function __construct(Node $name, ?Node $template, int $lineno, ?string $tag = null) + public function __construct(Node $name, ?Node $template, int $lineno) { $nodes = ['name' => $name]; if (null !== $template) { $nodes['template'] = $template; } - parent::__construct($nodes, ['is_defined_test' => false, 'output' => false], $lineno, $tag); + parent::__construct($nodes, ['is_defined_test' => false, 'output' => false], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/CallExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/CallExpression.php index cd81df47a..6fc6f66e0 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/CallExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/CallExpression.php @@ -15,6 +15,11 @@ use Twig\Error\SyntaxError; use Twig\Extension\ExtensionInterface; use Twig\Node\Node; +use Twig\TwigCallableInterface; +use Twig\TwigFilter; +use Twig\TwigFunction; +use Twig\TwigTest; +use Twig\Util\CallableArgumentsExtractor; use Twig\Util\ReflectionCallable; abstract class CallExpression extends AbstractExpression @@ -23,12 +28,13 @@ abstract class CallExpression extends AbstractExpression protected function compileCallable(Compiler $compiler) { - $callable = $this->getAttribute('callable'); + $twigCallable = $this->getTwigCallable(); + $callable = $twigCallable->getCallable(); if (\is_string($callable) && !str_contains($callable, '::')) { $compiler->raw($callable); } else { - $rc = $this->reflectCallable($callable); + $rc = $this->reflectCallable($twigCallable); $r = $rc->getReflector(); $callable = $rc->getCallable(); @@ -51,7 +57,7 @@ protected function compileCallable(Compiler $compiler) $compiler->raw(\sprintf('->%s', $callable[1])); } else { - $compiler->raw(\sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $this->getAttribute('name'))); + $compiler->raw(\sprintf('$this->env->get%s(\'%s\')->getCallable()', ucfirst($this->getAttribute('type')), $twigCallable->getDynamicName())); } } @@ -68,12 +74,14 @@ protected function compileArguments(Compiler $compiler, $isArray = false): void $first = true; - if ($this->hasAttribute('needs_charset') && $this->getAttribute('needs_charset')) { + $twigCallable = $this->getAttribute('twig_callable'); + + if ($twigCallable->needsCharset()) { $compiler->raw('$this->env->getCharset()'); $first = false; } - if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { + if ($twigCallable->needsEnvironment()) { if (!$first) { $compiler->raw(', '); } @@ -81,7 +89,7 @@ protected function compileArguments(Compiler $compiler, $isArray = false): void $first = false; } - if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { + if ($twigCallable->needsContext()) { if (!$first) { $compiler->raw(', '); } @@ -89,14 +97,12 @@ protected function compileArguments(Compiler $compiler, $isArray = false): void $first = false; } - if ($this->hasAttribute('arguments')) { - foreach ($this->getAttribute('arguments') as $argument) { - if (!$first) { - $compiler->raw(', '); - } - $compiler->string($argument); - $first = false; + foreach ($twigCallable->getArguments() as $argument) { + if (!$first) { + $compiler->raw(', '); } + $compiler->string($argument); + $first = false; } if ($this->hasNode('node')) { @@ -108,8 +114,7 @@ protected function compileArguments(Compiler $compiler, $isArray = false): void } if ($this->hasNode('arguments')) { - $callable = $this->getAttribute('callable'); - $arguments = $this->getArguments($callable, $this->getNode('arguments')); + $arguments = (new CallableArgumentsExtractor($this, $this->getTwigCallable()))->extractArguments($this->getNode('arguments')); foreach ($arguments as $node) { if (!$first) { $compiler->raw(', '); @@ -122,8 +127,13 @@ protected function compileArguments(Compiler $compiler, $isArray = false): void $compiler->raw($isArray ? ']' : ')'); } + /** + * @deprecated since 3.12, use Twig\Util\CallableArgumentsExtractor::getArguments() instead + */ protected function getArguments($callable, $arguments) { + trigger_deprecation('twig/twig', '3.12', 'The "%s()" method is deprecated, use Twig\Util\CallableArgumentsExtractor::getArguments() instead.', __METHOD__); + $callType = $this->getAttribute('type'); $callName = $this->getAttribute('name'); @@ -140,7 +150,7 @@ protected function getArguments($callable, $arguments) $parameters[$name] = $node; } - $isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic'); + $isVariadic = $this->getAttribute('twig_callable')->isVariadic(); if (!$named && !$isVariadic) { return $parameters; } @@ -247,14 +257,21 @@ protected function getArguments($callable, $arguments) return $arguments; } + /** + * @deprecated since 3.12 + */ protected function normalizeName(string $name): string { + trigger_deprecation('twig/twig', '3.12', 'The "%s()" method is deprecated.', __METHOD__); + return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], $name)); } + // To be removed in 4.0 private function getCallableParameters($callable, bool $isVariadic): array { - $rc = $this->reflectCallable($callable); + $twigCallable = $this->getAttribute('twig_callable'); + $rc = $this->reflectCallable($twigCallable); $r = $rc->getReflector(); $callableName = $rc->getName(); @@ -262,20 +279,19 @@ private function getCallableParameters($callable, bool $isVariadic): array if ($this->hasNode('node')) { array_shift($parameters); } - if ($this->hasAttribute('needs_charset') && $this->getAttribute('needs_charset')) { + if ($twigCallable->needsCharset()) { array_shift($parameters); } - if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { + if ($twigCallable->needsEnvironment()) { array_shift($parameters); } - if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { + if ($twigCallable->needsContext()) { array_shift($parameters); } - if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) { - foreach ($this->getAttribute('arguments') as $argument) { - array_shift($parameters); - } + foreach ($twigCallable->getArguments() as $argument) { + array_shift($parameters); } + $isPhpVariadic = false; if ($isVariadic) { $argument = end($parameters); @@ -286,19 +302,61 @@ private function getCallableParameters($callable, bool $isVariadic): array array_pop($parameters); $isPhpVariadic = true; } else { - throw new \LogicException(\sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $this->getAttribute('name'))); + throw new \LogicException(\sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $callableName, $this->getAttribute('type'), $twigCallable->getName())); } } return [$parameters, $isPhpVariadic]; } - private function reflectCallable($callable): ReflectionCallable + private function reflectCallable(TwigCallableInterface $callable): ReflectionCallable { if (!$this->reflector) { - $this->reflector = new ReflectionCallable($callable, $this->getAttribute('type'), $this->getAttribute('name')); + $this->reflector = new ReflectionCallable($callable); } return $this->reflector; } + + /** + * Overrides the Twig callable based on attributes (as potentially, attributes changed between the creation and the compilation of the node). + * + * To be removed in 4.0 and replace by $this->getAttribute('twig_callable'). + */ + private function getTwigCallable(): TwigCallableInterface + { + $current = $this->getAttribute('twig_callable'); + + $this->setAttribute('twig_callable', match ($this->getAttribute('type')) { + 'test' => (new TwigTest( + $this->getAttribute('name'), + $this->hasAttribute('callable') ? $this->getAttribute('callable') : $current->getCallable(), + [ + 'is_variadic' => $this->hasAttribute('is_variadic') ? $this->getAttribute('is_variadic') : $current->isVariadic(), + ], + ))->withDynamicArguments($this->getAttribute('name'), $this->hasAttribute('dynamic_name') ? $this->getAttribute('dynamic_name') : $current->getDynamicName(), $this->hasAttribute('arguments') ?: $current->getArguments()), + 'function' => (new TwigFunction( + $this->hasAttribute('name') ? $this->getAttribute('name') : $current->getName(), + $this->hasAttribute('callable') ? $this->getAttribute('callable') : $current->getCallable(), + [ + 'needs_environment' => $this->hasAttribute('needs_environment') ? $this->getAttribute('needs_environment') : $current->needsEnvironment(), + 'needs_context' => $this->hasAttribute('needs_context') ? $this->getAttribute('needs_context') : $current->needsContext(), + 'needs_charset' => $this->hasAttribute('needs_charset') ? $this->getAttribute('needs_charset') : $current->needsCharset(), + 'is_variadic' => $this->hasAttribute('is_variadic') ? $this->getAttribute('is_variadic') : $current->isVariadic(), + ], + ))->withDynamicArguments($this->getAttribute('name'), $this->hasAttribute('dynamic_name') ? $this->getAttribute('dynamic_name') : $current->getDynamicName(), $this->hasAttribute('arguments') ?: $current->getArguments()), + 'filter' => (new TwigFilter( + $this->getAttribute('name'), + $this->hasAttribute('callable') ? $this->getAttribute('callable') : $current->getCallable(), + [ + 'needs_environment' => $this->hasAttribute('needs_environment') ? $this->getAttribute('needs_environment') : $current->needsEnvironment(), + 'needs_context' => $this->hasAttribute('needs_context') ? $this->getAttribute('needs_context') : $current->needsContext(), + 'needs_charset' => $this->hasAttribute('needs_charset') ? $this->getAttribute('needs_charset') : $current->needsCharset(), + 'is_variadic' => $this->hasAttribute('is_variadic') ? $this->getAttribute('is_variadic') : $current->isVariadic(), + ], + ))->withDynamicArguments($this->getAttribute('name'), $this->hasAttribute('dynamic_name') ? $this->getAttribute('dynamic_name') : $current->getDynamicName(), $this->hasAttribute('arguments') ?: $current->getArguments()), + }); + + return $this->getAttribute('twig_callable'); + } } diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php index 7eb0ea770..75b6d18c2 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php @@ -11,7 +11,9 @@ namespace Twig\Node\Expression\Filter; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; +use Twig\Extension\CoreExtension; use Twig\Node\Expression\ConditionalExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; @@ -19,6 +21,8 @@ use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\Test\DefinedTest; use Twig\Node\Node; +use Twig\TwigFilter; +use Twig\TwigTest; /** * Returns the value or the default value when it is undefined or empty. @@ -29,12 +33,19 @@ */ class DefaultFilter extends FilterExpression { - public function __construct(Node $node, ConstantExpression $filterName, Node $arguments, int $lineno, ?string $tag = null) + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigFilter|ConstantExpression $filter, Node $arguments, int $lineno) { - $default = new FilterExpression($node, new ConstantExpression('default', $node->getTemplateLine()), $arguments, $node->getTemplateLine()); + if ($filter instanceof TwigFilter) { + $name = $filter->getName(); + $default = new FilterExpression($node, $filter, $arguments, $node->getTemplateLine()); + } else { + $name = $filter->getAttribute('value'); + $default = new FilterExpression($node, new TwigFilter('default', [CoreExtension::class, 'default']), $arguments, $node->getTemplateLine()); + } - if ('default' === $filterName->getAttribute('value') && ($node instanceof NameExpression || $node instanceof GetAttrExpression)) { - $test = new DefinedTest(clone $node, 'defined', new Node(), $node->getTemplateLine()); + if ('default' === $name && ($node instanceof NameExpression || $node instanceof GetAttrExpression)) { + $test = new DefinedTest(clone $node, new TwigTest('defined'), new Node(), $node->getTemplateLine()); $false = \count($arguments) ? $arguments->getNode('0') : new ConstantExpression('', $node->getTemplateLine()); $node = new ConditionalExpression($test, $default, $false, $node->getTemplateLine()); @@ -42,7 +53,7 @@ public function __construct(Node $node, ConstantExpression $filterName, Node $ar $node = $default; } - parent::__construct($node, $filterName, $arguments, $lineno, $tag); + parent::__construct($node, $filter, $arguments, $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/RawFilter.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/RawFilter.php index 584942306..e115ab194 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/RawFilter.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/Filter/RawFilter.php @@ -11,26 +11,22 @@ namespace Twig\Node\Expression\Filter; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Node; +use Twig\TwigFilter; /** * @author Fabien Potencier */ class RawFilter extends FilterExpression { - public function __construct(Node $node, ?ConstantExpression $filterName = null, ?Node $arguments = null, int $lineno = 0, ?string $tag = null) + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigFilter|ConstantExpression|null $filter = null, ?Node $arguments = null, int $lineno = 0) { - if (null === $filterName) { - $filterName = new ConstantExpression('raw', $node->getTemplateLine()); - } - if (null === $arguments) { - $arguments = new Node(); - } - - parent::__construct($node, $filterName, $arguments, $lineno ?: $node->getTemplateLine(), $tag ?: $node->getNodeTag()); + parent::__construct($node, $filter ?: new TwigFilter('raw', null, ['is_safe' => ['all']]), $arguments ?: new Node(), $lineno ?: $node->getTemplateLine()); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/FilterExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/FilterExpression.php index 251870ae5..efc91193e 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/FilterExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/FilterExpression.php @@ -12,22 +12,49 @@ namespace Twig\Node\Expression; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; +use Twig\Node\NameDeprecation; use Twig\Node\Node; +use Twig\TwigFilter; class FilterExpression extends CallExpression { - public function __construct(Node $node, ConstantExpression $filterName, Node $arguments, int $lineno, ?string $tag = null) + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigFilter|ConstantExpression $filter, Node $arguments, int $lineno) { - parent::__construct(['node' => $node, 'filter' => $filterName, 'arguments' => $arguments], ['name' => $filterName->getAttribute('value'), 'type' => 'filter'], $lineno, $tag); + if ($filter instanceof TwigFilter) { + $name = $filter->getName(); + $filterName = new ConstantExpression($name, $lineno); + } else { + $name = $filter->getAttribute('value'); + $filterName = $filter; + trigger_deprecation('twig/twig', '3.12', 'Not passing an instance of "TwigFilter" when creating a "%s" filter of type "%s" is deprecated.', $name, static::class); + } + + parent::__construct(['node' => $node, 'filter' => $filterName, 'arguments' => $arguments], ['name' => $name, 'type' => 'filter'], $lineno); + + if ($filter instanceof TwigFilter) { + $this->setAttribute('twig_callable', $filter); + } + + $this->deprecateNode('filter', new NameDeprecation('twig/twig', '3.12')); + + $this->deprecateAttribute('needs_charset', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('needs_environment', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('needs_context', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('arguments', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('callable', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('is_variadic', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('dynamic_name', new NameDeprecation('twig/twig', '3.12')); } public function compile(Compiler $compiler): void { - $name = $this->getNode('filter')->getAttribute('value'); + $name = $this->getNode('filter', false)->getAttribute('value'); if ($name !== $this->getAttribute('name')) { trigger_deprecation('twig/twig', '3.11', 'Changing the value of a "filter" node in a NodeVisitor class is not supported anymore.'); - $this->setAttribute('name', $name); + $this->removeAttribute('twig_callable'); } if ('raw' === $name) { trigger_deprecation('twig/twig', '3.11', 'Creating the "raw" filter via "FilterExpression" is deprecated; use "RawFilter" instead.'); @@ -36,14 +63,10 @@ public function compile(Compiler $compiler): void return; } - $filter = $compiler->getEnvironment()->getFilter($name); - - $this->setAttribute('needs_charset', $filter->needsCharset()); - $this->setAttribute('needs_environment', $filter->needsEnvironment()); - $this->setAttribute('needs_context', $filter->needsContext()); - $this->setAttribute('arguments', $filter->getArguments()); - $this->setAttribute('callable', $filter->getCallable()); - $this->setAttribute('is_variadic', $filter->isVariadic()); + + if (!$this->hasAttribute('twig_callable')) { + $this->setAttribute('twig_callable', $compiler->getEnvironment()->getFilter($name)); + } $this->compileCallable($compiler); } diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/FunctionExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/FunctionExpression.php index ef99c401a..6215c6abf 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/FunctionExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/FunctionExpression.php @@ -11,32 +11,57 @@ namespace Twig\Node\Expression; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; -use Twig\Extension\CoreExtension; +use Twig\Node\NameDeprecation; use Twig\Node\Node; +use Twig\TwigFunction; class FunctionExpression extends CallExpression { - public function __construct(string $name, Node $arguments, int $lineno) + #[FirstClassTwigCallableReady] + public function __construct(TwigFunction|string $function, Node $arguments, int $lineno) { + if ($function instanceof TwigFunction) { + $name = $function->getName(); + } else { + $name = $function; + trigger_deprecation('twig/twig', '3.12', 'Not passing an instance of "TwigFunction" when creating a "%s" function of type "%s" is deprecated.', $name, static::class); + } + parent::__construct(['arguments' => $arguments], ['name' => $name, 'type' => 'function', 'is_defined_test' => false], $lineno); + + if ($function instanceof TwigFunction) { + $this->setAttribute('twig_callable', $function); + } + + $this->deprecateAttribute('needs_charset', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('needs_environment', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('needs_context', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('arguments', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('callable', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('is_variadic', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('dynamic_name', new NameDeprecation('twig/twig', '3.12')); } public function compile(Compiler $compiler) { $name = $this->getAttribute('name'); - $function = $compiler->getEnvironment()->getFunction($name); + if ($this->hasAttribute('twig_callable')) { + $name = $this->getAttribute('twig_callable')->getName(); + if ($name !== $this->getAttribute('name')) { + trigger_deprecation('twig/twig', '3.12', 'Changing the value of a "function" node in a NodeVisitor class is not supported anymore.'); + $this->removeAttribute('twig_callable'); + } + } + + if (!$this->hasAttribute('twig_callable')) { + $this->setAttribute('twig_callable', $compiler->getEnvironment()->getFunction($name)); + } - $this->setAttribute('needs_charset', $function->needsCharset()); - $this->setAttribute('needs_environment', $function->needsEnvironment()); - $this->setAttribute('needs_context', $function->needsContext()); - $this->setAttribute('arguments', $function->getArguments()); - $callable = $function->getCallable(); if ('constant' === $name && $this->getAttribute('is_defined_test')) { - $callable = [CoreExtension::class, 'constantIsDefined']; + $this->getNode('arguments')->setNode('checkDefined', new ConstantExpression(true, $this->getTemplateLine())); } - $this->setAttribute('callable', $callable); - $this->setAttribute('is_variadic', $function->isVariadic()); $this->compileCallable($compiler); } diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumCasesFunction.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumCasesFunction.php new file mode 100644 index 000000000..7e5c25ff4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumCasesFunction.php @@ -0,0 +1,41 @@ +getNode('arguments'); + if ($arguments->hasNode('enum')) { + $firstArgument = $arguments->getNode('enum'); + } elseif ($arguments->hasNode('0')) { + $firstArgument = $arguments->getNode('0'); + } else { + $firstArgument = null; + } + + if (!$firstArgument instanceof ConstantExpression || 1 !== \count($arguments)) { + parent::compile($compiler); + + return; + } + + $value = $firstArgument->getAttribute('value'); + + if (!\is_string($value)) { + throw new SyntaxError('The first argument of the "enum_cases" function must be a string.', $this->getTemplateLine(), $this->getSourceContext()); + } + + if (!enum_exists($value)) { + throw new SyntaxError(\sprintf('The first argument of the "enum_cases" function must be the name of an enum, "%s" given.', $value), $this->getTemplateLine(), $this->getSourceContext()); + } + + $compiler->raw(\sprintf('%s::cases()', $value)); + } +} diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php index a72bc4fc6..98630f7f0 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php @@ -17,17 +17,18 @@ use Twig\Node\Expression\Test\NullTest; use Twig\Node\Expression\Unary\NotUnary; use Twig\Node\Node; +use Twig\TwigTest; class NullCoalesceExpression extends ConditionalExpression { public function __construct(Node $left, Node $right, int $lineno) { - $test = new DefinedTest(clone $left, 'defined', new Node(), $left->getTemplateLine()); + $test = new DefinedTest(clone $left, new TwigTest('defined'), new Node(), $left->getTemplateLine()); // for "block()", we don't need the null test as the return value is always a string if (!$left instanceof BlockReferenceExpression) { $test = new AndBinary( $test, - new NotUnary(new NullTest($left, 'null', new Node(), $left->getTemplateLine()), $left->getTemplateLine()), + new NotUnary(new NullTest($left, new TwigTest('null'), new Node(), $left->getTemplateLine()), $left->getTemplateLine()), $left->getTemplateLine() ); } diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/ParentExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/ParentExpression.php index 59d833ac9..22fe38f6a 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/ParentExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/ParentExpression.php @@ -21,9 +21,9 @@ */ class ParentExpression extends AbstractExpression { - public function __construct(string $name, int $lineno, ?string $tag = null) + public function __construct(string $name, int $lineno) { - parent::__construct([], ['output' => false, 'name' => $name], $lineno, $tag); + parent::__construct([], ['output' => false, 'name' => $name], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php index 3953bbbe2..24d3ee82c 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php @@ -11,6 +11,7 @@ namespace Twig\Node\Expression\Test; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; use Twig\Error\SyntaxError; use Twig\Node\Expression\ArrayExpression; @@ -22,6 +23,7 @@ use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\TestExpression; use Twig\Node\Node; +use Twig\TwigTest; /** * Checks if a variable is defined in the current context. @@ -35,7 +37,8 @@ */ class DefinedTest extends TestExpression { - public function __construct(Node $node, string $name, ?Node $arguments, int $lineno) + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigTest|string $name, ?Node $arguments, int $lineno) { if ($node instanceof NameExpression) { $node->setAttribute('is_defined_test', true); @@ -54,6 +57,10 @@ public function __construct(Node $node, string $name, ?Node $arguments, int $lin throw new SyntaxError('The "defined" test only works with simple variables.', $lineno); } + if (\is_string($name) && 'defined' !== $name) { + trigger_deprecation('twig/twig', '3.12', 'Creating a "DefinedTest" instance with a test name that is not "defined" is deprecated.'); + } + parent::__construct($node, $name, $arguments, $lineno); } diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Expression/TestExpression.php b/upload/system/storage/vendor/twig/twig/src/Node/Expression/TestExpression.php index 29c5a522c..080d85aaa 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Expression/TestExpression.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Expression/TestExpression.php @@ -11,28 +11,55 @@ namespace Twig\Node\Expression; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; +use Twig\Node\NameDeprecation; use Twig\Node\Node; +use Twig\TwigTest; class TestExpression extends CallExpression { - public function __construct(Node $node, string $name, ?Node $arguments, int $lineno) + #[FirstClassTwigCallableReady] + public function __construct(Node $node, string|TwigTest $test, ?Node $arguments, int $lineno) { $nodes = ['node' => $node]; if (null !== $arguments) { $nodes['arguments'] = $arguments; } + if ($test instanceof TwigTest) { + $name = $test->getName(); + } else { + $name = $test; + trigger_deprecation('twig/twig', '3.12', 'Not passing an instance of "TwigTest" when creating a "%s" test of type "%s" is deprecated.', $name, static::class); + } + parent::__construct($nodes, ['name' => $name, 'type' => 'test'], $lineno); + + if ($test instanceof TwigTest) { + $this->setAttribute('twig_callable', $test); + } + + $this->deprecateAttribute('arguments', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('callable', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('is_variadic', new NameDeprecation('twig/twig', '3.12')); + $this->deprecateAttribute('dynamic_name', new NameDeprecation('twig/twig', '3.12')); } public function compile(Compiler $compiler): void { - $test = $compiler->getEnvironment()->getTest($this->getAttribute('name')); + $name = $this->getAttribute('name'); + if ($this->hasAttribute('twig_callable')) { + $name = $this->getAttribute('twig_callable')->getName(); + if ($name !== $this->getAttribute('name')) { + trigger_deprecation('twig/twig', '3.12', 'Changing the value of a "test" node in a NodeVisitor class is not supported anymore.'); + $this->removeAttribute('twig_callable'); + } + } - $this->setAttribute('arguments', $test->getArguments()); - $this->setAttribute('callable', $test->getCallable()); - $this->setAttribute('is_variadic', $test->isVariadic()); + if (!$this->hasAttribute('twig_callable')) { + $this->setAttribute('twig_callable', $compiler->getEnvironment()->getTest($this->getAttribute('name'))); + } $this->compileCallable($compiler); } diff --git a/upload/system/storage/vendor/twig/twig/src/Node/FlushNode.php b/upload/system/storage/vendor/twig/twig/src/Node/FlushNode.php index 8a3dde6fc..ff3bd1cf1 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/FlushNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/FlushNode.php @@ -22,16 +22,19 @@ #[YieldReady] class FlushNode extends Node { - public function __construct(int $lineno, string $tag) + public function __construct(int $lineno) { - parent::__construct([], [], $lineno, $tag); + parent::__construct([], [], $lineno); } public function compile(Compiler $compiler): void { - $compiler - ->addDebugInfo($this) - ->write("flush();\n") - ; + $compiler->addDebugInfo($this); + + if ($compiler->getEnvironment()->useYield()) { + $compiler->write("yield '';\n"); + } + + $compiler->write("flush();\n"); } } diff --git a/upload/system/storage/vendor/twig/twig/src/Node/ForLoopNode.php b/upload/system/storage/vendor/twig/twig/src/Node/ForLoopNode.php index 503687c2b..3e044bbb0 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/ForLoopNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/ForLoopNode.php @@ -22,9 +22,9 @@ #[YieldReady] class ForLoopNode extends Node { - public function __construct(int $lineno, ?string $tag = null) + public function __construct(int $lineno) { - parent::__construct([], ['with_loop' => false, 'ifexpr' => false, 'else' => false], $lineno, $tag); + parent::__construct([], ['with_loop' => false, 'ifexpr' => false, 'else' => false], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/ForNode.php b/upload/system/storage/vendor/twig/twig/src/Node/ForNode.php index 5222cf9bf..122063105 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/ForNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/ForNode.php @@ -27,16 +27,16 @@ class ForNode extends Node { private $loop; - public function __construct(AssignNameExpression $keyTarget, AssignNameExpression $valueTarget, AbstractExpression $seq, ?Node $ifexpr, Node $body, ?Node $else, int $lineno, ?string $tag = null) + public function __construct(AssignNameExpression $keyTarget, AssignNameExpression $valueTarget, AbstractExpression $seq, ?Node $ifexpr, Node $body, ?Node $else, int $lineno) { - $body = new Node([$body, $this->loop = new ForLoopNode($lineno, $tag)]); + $body = new Node([$body, $this->loop = new ForLoopNode($lineno)]); $nodes = ['key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body]; if (null !== $else) { $nodes['else'] = $else; } - parent::__construct($nodes, ['with_loop' => true], $lineno, $tag); + parent::__construct($nodes, ['with_loop' => true], $lineno); } public function compile(Compiler $compiler): void @@ -101,7 +101,11 @@ public function compile(Compiler $compiler): void $compiler->write("\$_parent = \$context['_parent'];\n"); // remove some "private" loop variables (needed for nested loops) - $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\'], $context[\'loop\']);'."\n"); + $compiler->write('unset($context[\'_seq\'], $context[\'_iterated\'], $context[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\']'); + if ($this->getAttribute('with_loop')) { + $compiler->raw(', $context[\'loop\']'); + } + $compiler->raw(");\n"); // keep the values set in the inner context for variables defined in the outer context $compiler->write("\$context = array_intersect_key(\$context, \$_parent) + \$_parent;\n"); diff --git a/upload/system/storage/vendor/twig/twig/src/Node/IfNode.php b/upload/system/storage/vendor/twig/twig/src/Node/IfNode.php index 1b883305a..2af48fa81 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/IfNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/IfNode.php @@ -23,14 +23,14 @@ #[YieldReady] class IfNode extends Node { - public function __construct(Node $tests, ?Node $else, int $lineno, ?string $tag = null) + public function __construct(Node $tests, ?Node $else, int $lineno) { $nodes = ['tests' => $tests]; if (null !== $else) { $nodes['else'] = $else; } - parent::__construct($nodes, [], $lineno, $tag); + parent::__construct($nodes, [], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/ImportNode.php b/upload/system/storage/vendor/twig/twig/src/Node/ImportNode.php index db47bfe61..31acbe9d1 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/ImportNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/ImportNode.php @@ -24,9 +24,19 @@ #[YieldReady] class ImportNode extends Node { - public function __construct(AbstractExpression $expr, AbstractExpression $var, int $lineno, ?string $tag = null, bool $global = true) + /** + * @param bool $global + */ + public function __construct(AbstractExpression $expr, AbstractExpression $var, int $lineno, $global = true) { - parent::__construct(['expr' => $expr, 'var' => $var], ['global' => $global], $lineno, $tag); + if (null === $global || is_string($global)) { + trigger_deprecation('twig/twig', '3.12', 'Passing a tag to %s() is deprecated.', __METHOD__); + $global = func_num_args() > 4 ? func_get_arg(4) : true; + } elseif (!is_bool($global)) { + throw new \TypeError(\sprintf('Argument 4 passed to "%s()" must be a boolean, "%s" given.', __METHOD__, get_debug_type($global))); + } + + parent::__construct(['expr' => $expr, 'var' => $var], ['global' => $global], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/IncludeNode.php b/upload/system/storage/vendor/twig/twig/src/Node/IncludeNode.php index 7073fa4ac..1c18292c5 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/IncludeNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/IncludeNode.php @@ -24,14 +24,14 @@ #[YieldReady] class IncludeNode extends Node implements NodeOutputInterface { - public function __construct(AbstractExpression $expr, ?AbstractExpression $variables, bool $only, bool $ignoreMissing, int $lineno, ?string $tag = null) + public function __construct(AbstractExpression $expr, ?AbstractExpression $variables, bool $only, bool $ignoreMissing, int $lineno) { $nodes = ['expr' => $expr]; if (null !== $variables) { $nodes['variables'] = $variables; } - parent::__construct($nodes, ['only' => $only, 'ignore_missing' => $ignoreMissing], $lineno, $tag); + parent::__construct($nodes, ['only' => $only, 'ignore_missing' => $ignoreMissing], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/MacroNode.php b/upload/system/storage/vendor/twig/twig/src/Node/MacroNode.php index a6048de9b..d54b8ac72 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/MacroNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/MacroNode.php @@ -25,15 +25,22 @@ class MacroNode extends Node { public const VARARGS_NAME = 'varargs'; - public function __construct(string $name, Node $body, Node $arguments, int $lineno, ?string $tag = null) + /** + * @param BodyNode $body + */ + public function __construct(string $name, Node $body, Node $arguments, int $lineno) { + if (!$body instanceof BodyNode) { + trigger_deprecation('twig/twig', '3.12', \sprintf('Not passing a "%s" instance as the "body" argument of the "%s" constructor is deprecated.', BodyNode::class, static::class)); + } + foreach ($arguments as $argumentName => $argument) { if (self::VARARGS_NAME === $argumentName) { throw new SyntaxError(\sprintf('The argument "%s" in macro "%s" cannot be defined because the variable "%s" is reserved for arbitrary arguments.', self::VARARGS_NAME, $name, self::VARARGS_NAME), $argument->getTemplateLine(), $argument->getSourceContext()); } } - parent::__construct(['body' => $body, 'arguments' => $arguments], ['name' => $name], $lineno, $tag); + parent::__construct(['body' => $body, 'arguments' => $arguments], ['name' => $name], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/ModuleNode.php b/upload/system/storage/vendor/twig/twig/src/Node/ModuleNode.php index fb85cd895..deb05a16f 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/ModuleNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/ModuleNode.php @@ -21,17 +21,24 @@ /** * Represents a module node. * - * Consider this class as being final. If you need to customize the behavior of - * the generated class, consider adding nodes to the following nodes: display_start, - * display_end, constructor_start, constructor_end, and class_end. + * If you need to customize the behavior of the generated class, add nodes to + * the following nodes: display_start, display_end, constructor_start, + * constructor_end, and class_end. * * @author Fabien Potencier */ #[YieldReady] final class ModuleNode extends Node { + /** + * @param BodyNode $body + */ public function __construct(Node $body, ?AbstractExpression $parent, Node $blocks, Node $macros, Node $traits, $embeddedTemplates, Source $source) { + if (!$body instanceof BodyNode) { + trigger_deprecation('twig/twig', '3.12', \sprintf('Not passing a "%s" instance as the "body" argument of the "%s" constructor is deprecated.', BodyNode::class, static::class)); + } + $nodes = [ 'body' => $body, 'blocks' => $blocks, @@ -108,7 +115,7 @@ protected function compileGetParent(Compiler $compiler) $parent = $this->getNode('parent'); $compiler - ->write("protected function doGetParent(array \$context)\n", "{\n") + ->write("protected function doGetParent(array \$context): bool|string|Template|TemplateWrapper\n", "{\n") ->indent() ->addDebugInfo($parent) ->write('return ') @@ -153,7 +160,9 @@ protected function compileClassHeader(Compiler $compiler) ->write("use Twig\Sandbox\SecurityNotAllowedFilterError;\n") ->write("use Twig\Sandbox\SecurityNotAllowedFunctionError;\n") ->write("use Twig\Source;\n") - ->write("use Twig\Template;\n\n") + ->write("use Twig\Template;\n") + ->write("use Twig\TemplateWrapper;\n") + ->write("\n") ; } $compiler @@ -163,8 +172,11 @@ protected function compileClassHeader(Compiler $compiler) ->raw(" extends Template\n") ->write("{\n") ->indent() - ->write("private \$source;\n") - ->write("private \$macros = [];\n\n") + ->write("private Source \$source;\n") + ->write("/**\n") + ->write(" * @var array\n") + ->write(" */\n") + ->write("private array \$macros = [];\n\n") ; } @@ -370,7 +382,7 @@ protected function compileGetTemplateName(Compiler $compiler) ->write("/**\n") ->write(" * @codeCoverageIgnore\n") ->write(" */\n") - ->write("public function getTemplateName()\n", "{\n") + ->write("public function getTemplateName(): string\n", "{\n") ->indent() ->write('return ') ->repr($this->getSourceContext()->getName()) @@ -427,7 +439,7 @@ protected function compileIsTraitable(Compiler $compiler) ->write("/**\n") ->write(" * @codeCoverageIgnore\n") ->write(" */\n") - ->write("public function isTraitable()\n", "{\n") + ->write("public function isTraitable(): bool\n", "{\n") ->indent() ->write("return false;\n") ->outdent() @@ -441,7 +453,7 @@ protected function compileDebugInfo(Compiler $compiler) ->write("/**\n") ->write(" * @codeCoverageIgnore\n") ->write(" */\n") - ->write("public function getDebugInfo()\n", "{\n") + ->write("public function getDebugInfo(): array\n", "{\n") ->indent() ->write(\sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true)))) ->outdent() @@ -452,7 +464,7 @@ protected function compileDebugInfo(Compiler $compiler) protected function compileGetSourceContext(Compiler $compiler) { $compiler - ->write("public function getSourceContext()\n", "{\n") + ->write("public function getSourceContext(): Source\n", "{\n") ->indent() ->write('return new Source(') ->string($compiler->getEnvironment()->isDebug() ? $this->getSourceContext()->getCode() : '') diff --git a/upload/system/storage/vendor/twig/twig/src/Node/Node.php b/upload/system/storage/vendor/twig/twig/src/Node/Node.php index 5ef661f5e..38ebdfa8c 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/Node.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/Node.php @@ -24,6 +24,9 @@ #[YieldReady] class Node implements \Countable, \IteratorAggregate { + /** + * @var array + */ protected $nodes; protected $attributes; protected $lineno; @@ -36,12 +39,11 @@ class Node implements \Countable, \IteratorAggregate private $attributeNameDeprecations = []; /** - * @param array $nodes An array of named nodes - * @param array $attributes An array of attributes (should not be nodes) - * @param int $lineno The line number - * @param string $tag The tag name associated with the Node + * @param array $nodes An array of named nodes + * @param array $attributes An array of attributes (should not be nodes) + * @param int $lineno The line number */ - public function __construct(array $nodes = [], array $attributes = [], int $lineno = 0, ?string $tag = null) + public function __construct(array $nodes = [], array $attributes = [], int $lineno = 0) { foreach ($nodes as $name => $node) { if (!$node instanceof self) { @@ -51,35 +53,50 @@ public function __construct(array $nodes = [], array $attributes = [], int $line $this->nodes = $nodes; $this->attributes = $attributes; $this->lineno = $lineno; - $this->tag = $tag; + + if (func_num_args() > 3) { + trigger_deprecation('twig/twig', '3.12', sprintf('The "tag" constructor argument of the "%s" class is deprecated and ignored (check which TokenParser class set it to "%s"), the tag is now automatically set by the Parser when needed.', static::class, func_get_arg(3) ?: 'null')); + } } public function __toString() { + $repr = static::class; + + if ($this->tag) { + $repr .= \sprintf("\n tag: %s", $this->tag); + } + $attributes = []; foreach ($this->attributes as $name => $value) { - $attributes[] = \sprintf('%s: %s', $name, \is_callable($value) ? '\Closure' : str_replace("\n", '', var_export($value, true))); + if (\is_callable($value)) { + $v = '\Closure'; + } elseif ($value instanceof \Stringable) { + $v = (string) $value; + } else { + $v = str_replace("\n", '', var_export($value, true)); + } + $attributes[] = \sprintf('%s: %s', $name, $v); } - $repr = [static::class.'('.implode(', ', $attributes)]; + if ($attributes) { + $repr .= \sprintf("\n attributes:\n %s", implode("\n ", $attributes)); + } if (\count($this->nodes)) { + $repr .= "\n nodes:"; foreach ($this->nodes as $name => $node) { - $len = \strlen($name) + 4; + $len = \strlen($name) + 6; $noderepr = []; foreach (explode("\n", (string) $node) as $line) { $noderepr[] = str_repeat(' ', $len).$line; } - $repr[] = \sprintf(' %s: %s', $name, ltrim(implode("\n", $noderepr))); + $repr .= \sprintf("\n %s: %s", $name, ltrim(implode("\n", $noderepr))); } - - $repr[] = ')'; - } else { - $repr[0] .= ')'; } - return implode("\n", $repr); + return $repr; } /** @@ -102,6 +119,18 @@ public function getNodeTag(): ?string return $this->tag; } + /** + * @internal + */ + public function setNodeTag(string $tag): void + { + if ($this->tag) { + throw new \LogicException('The tag of a node can only be set once.'); + } + + $this->tag = $tag; + } + public function hasAttribute(string $name): bool { return \array_key_exists($name, $this->attributes); @@ -151,11 +180,17 @@ public function removeAttribute(string $name): void unset($this->attributes[$name]); } + /** + * @param string|int $name + */ public function hasNode(string $name): bool { return isset($this->nodes[$name]); } + /** + * @param string|int $name + */ public function getNode(string $name): self { if (!isset($this->nodes[$name])) { @@ -175,6 +210,9 @@ public function getNode(string $name): self return $this->nodes[$name]; } + /** + * @param string|int $name + */ public function setNode(string $name, self $node): void { $triggerDeprecation = \func_num_args() > 2 ? func_get_arg(2) : true; @@ -193,11 +231,17 @@ public function setNode(string $name, self $node): void $this->nodes[$name] = $node; } + /** + * @param string|int $name + */ public function removeNode(string $name): void { unset($this->nodes[$name]); } + /** + * @param string|int $name + */ public function deprecateNode(string $name, NameDeprecation $dep): void { $this->nodeNameDeprecations[$name] = $dep; diff --git a/upload/system/storage/vendor/twig/twig/src/Node/PrintNode.php b/upload/system/storage/vendor/twig/twig/src/Node/PrintNode.php index da442d852..e3c23bbfa 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/PrintNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/PrintNode.php @@ -24,9 +24,9 @@ #[YieldReady] class PrintNode extends Node implements NodeOutputInterface { - public function __construct(AbstractExpression $expr, int $lineno, ?string $tag = null) + public function __construct(AbstractExpression $expr, int $lineno) { - parent::__construct(['expr' => $expr], [], $lineno, $tag); + parent::__construct(['expr' => $expr], [], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/SandboxNode.php b/upload/system/storage/vendor/twig/twig/src/Node/SandboxNode.php index 80aecbdba..d51cea44b 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/SandboxNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/SandboxNode.php @@ -22,9 +22,9 @@ #[YieldReady] class SandboxNode extends Node { - public function __construct(Node $body, int $lineno, ?string $tag = null) + public function __construct(Node $body, int $lineno) { - parent::__construct(['body' => $body], [], $lineno, $tag); + parent::__construct(['body' => $body], [], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/SetNode.php b/upload/system/storage/vendor/twig/twig/src/Node/SetNode.php index 0900f1542..67725104e 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/SetNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/SetNode.php @@ -23,7 +23,7 @@ #[YieldReady] class SetNode extends Node implements NodeCaptureInterface { - public function __construct(bool $capture, Node $names, Node $values, int $lineno, ?string $tag = null) + public function __construct(bool $capture, Node $names, Node $values, int $lineno) { /* * Optimizes the node when capture is used for a large block of text. @@ -41,7 +41,7 @@ public function __construct(bool $capture, Node $names, Node $values, int $linen } } - parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => $safe], $lineno, $tag); + parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => $safe], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/Node/WithNode.php b/upload/system/storage/vendor/twig/twig/src/Node/WithNode.php index a7b7e70d9..f9104948b 100644 --- a/upload/system/storage/vendor/twig/twig/src/Node/WithNode.php +++ b/upload/system/storage/vendor/twig/twig/src/Node/WithNode.php @@ -22,14 +22,14 @@ #[YieldReady] class WithNode extends Node { - public function __construct(Node $body, ?Node $variables, bool $only, int $lineno, ?string $tag = null) + public function __construct(Node $body, ?Node $variables, bool $only, int $lineno) { $nodes = ['body' => $body]; if (null !== $variables) { $nodes['variables'] = $variables; } - parent::__construct($nodes, ['only' => $only], $lineno, $tag); + parent::__construct($nodes, ['only' => $only], $lineno); } public function compile(Compiler $compiler): void diff --git a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php index 91e2ea893..32f49ab1e 100644 --- a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php +++ b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php @@ -126,7 +126,7 @@ private function escapeInlinePrintNode(InlinePrint $node, Environment $env, stri return $node; } - return new InlinePrint($this->getEscaperFilter($type, $expression), $node->getTemplateLine()); + return new InlinePrint($this->getEscaperFilter($env, $type, $expression), $node->getTemplateLine()); } private function escapePrintNode(PrintNode $node, Environment $env, string $type): Node @@ -139,14 +139,19 @@ private function escapePrintNode(PrintNode $node, Environment $env, string $type $class = \get_class($node); - return new $class($this->getEscaperFilter($type, $expression), $node->getTemplateLine()); + return new $class($this->getEscaperFilter($env, $type, $expression), $node->getTemplateLine()); } private function preEscapeFilterNode(FilterExpression $filter, Environment $env): FilterExpression { - $name = $filter->getNode('filter')->getAttribute('value'); + if ($filter->hasAttribute('twig_callable')) { + $type = $filter->getAttribute('twig_callable')->getPreEscape(); + } else { + // legacy + $name = $filter->getNode('filter', false)->getAttribute('value'); + $type = $env->getFilter($name)->getPreEscape(); + } - $type = $env->getFilter($name)->getPreEscape(); if (null === $type) { return $filter; } @@ -156,7 +161,7 @@ private function preEscapeFilterNode(FilterExpression $filter, Environment $env) return $filter; } - $filter->setNode('node', $this->getEscaperFilter($type, $node)); + $filter->setNode('node', $this->getEscaperFilter($env, $type, $node)); return $filter; } @@ -188,13 +193,13 @@ private function needEscaping() return $this->defaultStrategy ?: false; } - private function getEscaperFilter(string $type, Node $node): FilterExpression + private function getEscaperFilter(Environment $env, string $type, Node $node): FilterExpression { $line = $node->getTemplateLine(); - $name = new ConstantExpression('escape', $line); + $filter = $env->getFilter('escape'); $args = new Node([new ConstantExpression($type, $line), new ConstantExpression(null, $line), new ConstantExpression(true, $line)]); - return new FilterExpression($node, $name, $args, $line); + return new FilterExpression($node, $filter, $args, $line); } public function getPriority(): int diff --git a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php index d6a7781ba..01d5a997f 100644 --- a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php +++ b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php @@ -46,7 +46,7 @@ public function leaveNode(Node $node, Environment $env): Node if ($node instanceof ModuleNode) { $this->inAModule = false; if ($this->hasMacroCalls) { - $node->getNode('constructor_end')->setNode('_auto_macro_import', new ImportNode(new NameExpression('_self', 0), new AssignNameExpression('_self', 0), 0, 'import', true)); + $node->getNode('constructor_end')->setNode('_auto_macro_import', new ImportNode(new NameExpression('_self', 0), new AssignNameExpression('_self', 0), 0, true)); } } elseif ($this->inAModule) { if ( diff --git a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php index 55f5d6eb9..0d2dc02d5 100644 --- a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php +++ b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php @@ -62,6 +62,10 @@ public function __construct(int $optimizers = -1) trigger_deprecation('twig/twig', '3.11', 'The "Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_RAW_FILTER" option is deprecated and does nothing.'); } + if (-1 !== $optimizers && self::OPTIMIZE_TEXT_NODES === (self::OPTIMIZE_TEXT_NODES & $optimizers)) { + trigger_deprecation('twig/twig', '3.12', 'The "Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_TEXT_NODES" option is deprecated and does nothing.'); + } + $this->optimizers = $optimizers; } @@ -82,42 +86,6 @@ public function leaveNode(Node $node, Environment $env): ?Node $node = $this->optimizePrintNode($node); - if (self::OPTIMIZE_TEXT_NODES === (self::OPTIMIZE_TEXT_NODES & $this->optimizers)) { - $node = $this->mergeTextNodeCalls($node); - } - - return $node; - } - - private function mergeTextNodeCalls(Node $node): Node - { - $text = ''; - $names = []; - foreach ($node as $k => $n) { - if (!$n instanceof TextNode) { - return $node; - } - - $text .= $n->getAttribute('data'); - $names[] = $k; - } - - if (!$text) { - return $node; - } - - if (Node::class === \get_class($node)) { - return new TextNode($text, $node->getTemplateLine()); - } - - foreach ($names as $i => $name) { - if (0 === $i) { - $node->setNode($name, new TextNode($text, $node->getTemplateLine())); - } else { - $node->removeNode($name); - } - } - return $node; } diff --git a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php index 6df046e1c..07672164e 100644 --- a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php +++ b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php @@ -96,10 +96,15 @@ public function leaveNode(Node $node, Environment $env): ?Node $this->setSafe($node, $safe); } elseif ($node instanceof FilterExpression) { // filter expression is safe when the filter is safe - $name = $node->getNode('filter')->getAttribute('value'); - $args = $node->getNode('arguments'); - if ($filter = $env->getFilter($name)) { - $safe = $filter->getSafe($args); + if ($node->hasAttribute('twig_callable')) { + $filter = $node->getAttribute('twig_callable'); + } else { + // legacy + $filter = $env->getFilter($node->getAttribute('name')); + } + + if ($filter) { + $safe = $filter->getSafe($node->getNode('arguments')); if (null === $safe) { $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); } @@ -109,10 +114,15 @@ public function leaveNode(Node $node, Environment $env): ?Node } } elseif ($node instanceof FunctionExpression) { // function expression is safe when the function is safe - $name = $node->getAttribute('name'); - $args = $node->getNode('arguments'); - if ($function = $env->getFunction($name)) { - $this->setSafe($node, $function->getSafe($args)); + if ($node->hasAttribute('twig_callable')) { + $function = $node->getAttribute('twig_callable'); + } else { + // legacy + $function = $env->getFunction($node->getAttribute('name')); + } + + if ($function) { + $this->setSafe($node, $function->getSafe($node->getNode('arguments'))); } else { $this->setSafe($node, []); } diff --git a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php index 68020885e..37e184a3e 100644 --- a/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php +++ b/upload/system/storage/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php @@ -58,8 +58,8 @@ public function enterNode(Node $node, Environment $env): Node } // look for filters - if ($node instanceof FilterExpression && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) { - $this->filters[$node->getNode('filter')->getAttribute('value')] = $node->getTemplateLine(); + if ($node instanceof FilterExpression && !isset($this->filters[$node->getAttribute('name')])) { + $this->filters[$node->getAttribute('name')] = $node->getTemplateLine(); } // look for functions diff --git a/upload/system/storage/vendor/twig/twig/src/Parser.php b/upload/system/storage/vendor/twig/twig/src/Parser.php index 7cfc2058c..8923d4db6 100644 --- a/upload/system/storage/vendor/twig/twig/src/Parser.php +++ b/upload/system/storage/vendor/twig/twig/src/Parser.php @@ -92,7 +92,7 @@ public function parse(TokenStream $stream, $test = null, bool $dropNeedle = fals } if (!$e->getTemplateLine()) { - $e->setTemplateLine($this->stream->getCurrent()->getLine()); + $e->setTemplateLine($this->getCurrentToken()->getLine()); } throw $e; @@ -121,23 +121,23 @@ public function subparse($test, bool $dropNeedle = false): Node $rv = []; while (!$this->stream->isEOF()) { switch ($this->getCurrentToken()->getType()) { - case /* Token::TEXT_TYPE */ 0: + case Token::TEXT_TYPE: $token = $this->stream->next(); $rv[] = new TextNode($token->getValue(), $token->getLine()); break; - case /* Token::VAR_START_TYPE */ 2: + case Token::VAR_START_TYPE: $token = $this->stream->next(); $expr = $this->expressionParser->parseExpression(); - $this->stream->expect(/* Token::VAR_END_TYPE */ 4); + $this->stream->expect(Token::VAR_END_TYPE); $rv[] = new PrintNode($expr, $token->getLine()); break; - case /* Token::BLOCK_START_TYPE */ 1: + case Token::BLOCK_START_TYPE: $this->stream->next(); $token = $this->getCurrentToken(); - if (/* Token::NAME_TYPE */ 5 !== $token->getType()) { + if (Token::NAME_TYPE !== $token->getType()) { throw new SyntaxError('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext()); } @@ -157,7 +157,7 @@ public function subparse($test, bool $dropNeedle = false): Node if (null !== $test) { $e = new SyntaxError(\sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); - $callable = (new ReflectionCallable($test))->getCallable(); + $callable = (new ReflectionCallable(new TwigTest('decision', $test)))->getCallable(); if (\is_array($callable) && $callable[0] instanceof TokenParserInterface) { $e->appendMessage(\sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $callable[0]->getTag(), $lineno)); } @@ -173,13 +173,16 @@ public function subparse($test, bool $dropNeedle = false): Node $subparser->setParser($this); $node = $subparser->parse($token); - if (null !== $node) { + if (!$node) { + trigger_deprecation('twig/twig', '3.12', 'Returning "null" from "%s" is deprecated and forbidden by "TokenParserInterface".', $subparser::class); + } else { + $node->setNodeTag($subparser->getTag()); $rv[] = $node; } break; default: - throw new SyntaxError('Lexer or parser ended up in unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext()); + throw new SyntaxError('The lexer or the parser ended up in an unsupported state.', $this->getCurrentToken()->getLine(), $this->stream->getSourceContext()); } } @@ -192,6 +195,8 @@ public function subparse($test, bool $dropNeedle = false): Node public function getBlockStack(): array { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->blockStack; } @@ -212,21 +217,31 @@ public function pushBlockStack($name): void public function hasBlock(string $name): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return isset($this->blocks[$name]); } public function getBlock(string $name): Node { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->blocks[$name]; } public function setBlock(string $name, BlockNode $value): void { + if (isset($this->blocks[$name])) { + throw new SyntaxError(\sprintf("The block '%s' has already been defined line %d.", $name, $this->blocks[$name]->getTemplateLine()), $this->getCurrentToken()->getLine(), $this->blocks[$name]->getSourceContext()); + } + $this->blocks[$name] = new BodyNode([$value], [], $value->getTemplateLine()); } public function hasMacro(string $name): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return isset($this->macros[$name]); } @@ -242,6 +257,8 @@ public function addTrait($trait): void public function hasTraits(): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return \count($this->traits) > 0; } @@ -285,11 +302,26 @@ public function getExpressionParser(): ExpressionParser public function getParent(): ?Node { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->parent; } + public function hasInheritance() + { + return $this->parent || 0 < \count($this->traits); + } + public function setParent(?Node $parent): void { + if (null === $parent) { + trigger_deprecation('twig/twig', '3.12', 'Passing "null" to "%s()" is deprecated.', __METHOD__); + } + + if (null !== $this->parent) { + throw new SyntaxError('Multiple extends tags are forbidden.', $parent->getTemplateLine(), $parent->getSourceContext()); + } + $this->parent = $parent; } diff --git a/upload/system/storage/vendor/twig/twig/src/Resources/core.php b/upload/system/storage/vendor/twig/twig/src/Resources/core.php index e5372cda4..6e2fcfb07 100644 --- a/upload/system/storage/vendor/twig/twig/src/Resources/core.php +++ b/upload/system/storage/vendor/twig/twig/src/Resources/core.php @@ -441,7 +441,7 @@ function twig_constant_is_defined($constant, $object = null) { trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); - return CoreExtension::constantIsDefined($constant, $object); + return CoreExtension::constant($constant, $object, true); } /** diff --git a/upload/system/storage/vendor/twig/twig/src/Runtime/EscaperRuntime.php b/upload/system/storage/vendor/twig/twig/src/Runtime/EscaperRuntime.php index b1dac9640..5388b0d41 100644 --- a/upload/system/storage/vendor/twig/twig/src/Runtime/EscaperRuntime.php +++ b/upload/system/storage/vendor/twig/twig/src/Runtime/EscaperRuntime.php @@ -93,7 +93,7 @@ public function escape($string, string $strategy = 'html', ?string $charset = nu } if (!\is_string($string)) { - if (\is_object($string) && method_exists($string, '__toString')) { + if ($string instanceof \Stringable) { if ($autoescape) { $c = \get_class($string); if (!isset($this->safeClasses[$c])) { diff --git a/upload/system/storage/vendor/twig/twig/src/Sandbox/SecurityPolicy.php b/upload/system/storage/vendor/twig/twig/src/Sandbox/SecurityPolicy.php index 417d38a8d..988e37216 100644 --- a/upload/system/storage/vendor/twig/twig/src/Sandbox/SecurityPolicy.php +++ b/upload/system/storage/vendor/twig/twig/src/Sandbox/SecurityPolicy.php @@ -68,7 +68,13 @@ public function checkSecurity($tags, $filters, $functions): void { foreach ($tags as $tag) { if (!\in_array($tag, $this->allowedTags)) { - throw new SecurityNotAllowedTagError(\sprintf('Tag "%s" is not allowed.', $tag), $tag); + if ('extends' === $tag) { + trigger_deprecation('twig/twig', '3.12', 'The "extends" tag is always allowed in sandboxes, but won\'t be in 4.0, please enable it explicitly in your sandbox policy if needed.'); + } elseif ('use' === $tag) { + trigger_deprecation('twig/twig', '3.12', 'The "use" tag is always allowed in sandboxes, but won\'t be in 4.0, please enable it explicitly in your sandbox policy if needed.'); + } else { + throw new SecurityNotAllowedTagError(\sprintf('Tag "%s" is not allowed.', $tag), $tag); + } } } diff --git a/upload/system/storage/vendor/twig/twig/src/Template.php b/upload/system/storage/vendor/twig/twig/src/Template.php index 04c530cc9..e0cbb94e2 100644 --- a/upload/system/storage/vendor/twig/twig/src/Template.php +++ b/upload/system/storage/vendor/twig/twig/src/Template.php @@ -52,24 +52,20 @@ public function __construct(Environment $env) /** * Returns the template name. - * - * @return string The template name */ - abstract public function getTemplateName(); + abstract public function getTemplateName(): string; /** * Returns debug information about the template. * - * @return array Debug information + * @return array Debug information */ - abstract public function getDebugInfo(); + abstract public function getDebugInfo(): array; /** * Returns information about the original template source code. - * - * @return Source */ - abstract public function getSourceContext(); + abstract public function getSourceContext(): Source; /** * Returns the parent template. @@ -107,12 +103,12 @@ public function getParent(array $context) return $this->parents[$parent]; } - protected function doGetParent(array $context) + protected function doGetParent(array $context): bool|string|self|TemplateWrapper { return false; } - public function isTraitable() + public function isTraitable(): bool { return true; } @@ -166,6 +162,17 @@ public function displayBlock($name, array $context, array $blocks = [], $useBloc */ public function renderParentBlock($name, array $context, array $blocks = []) { + if (!$this->useYield) { + if ($this->env->isDebug()) { + ob_start(); + } else { + ob_start(function () { return ''; }); + } + $this->displayParentBlock($name, $context, $blocks); + + return ob_get_clean(); + } + $content = ''; foreach ($this->yieldParentBlock($name, $context, $blocks) as $data) { $content .= $data; @@ -189,6 +196,26 @@ public function renderParentBlock($name, array $context, array $blocks = []) */ public function renderBlock($name, array $context, array $blocks = [], $useBlocks = true) { + if (!$this->useYield) { + $level = ob_get_level(); + if ($this->env->isDebug()) { + ob_start(); + } else { + ob_start(function () { return ''; }); + } + try { + $this->displayBlock($name, $context, $blocks, $useBlocks); + } catch (\Throwable $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } + + return ob_get_clean(); + } + $content = ''; foreach ($this->yieldBlock($name, $context, $blocks, $useBlocks) as $data) { $content .= $data; @@ -331,6 +358,26 @@ public function display(array $context, array $blocks = []): void public function render(array $context): string { + if (!$this->useYield) { + $level = ob_get_level(); + if ($this->env->isDebug()) { + ob_start(); + } else { + ob_start(function () { return ''; }); + } + try { + $this->display($context); + } catch (\Throwable $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } + + return ob_get_clean(); + } + $content = ''; foreach ($this->yield($context) as $data) { $content .= $data; @@ -348,27 +395,7 @@ public function yield(array $context, array $blocks = []): iterable $blocks = array_merge($this->blocks, $blocks); try { - if ($this->useYield) { - yield from $this->doDisplay($context, $blocks); - - return; - } - - $level = ob_get_level(); - ob_start(); - - foreach ($this->doDisplay($context, $blocks) as $data) { - if (ob_get_length()) { - $data = ob_get_clean().$data; - ob_start(); - } - - yield $data; - } - - if (ob_get_length()) { - yield ob_get_clean(); - } + yield from $this->doDisplay($context, $blocks); } catch (Error $e) { if (!$e->getSourceContext()) { $e->setSourceContext($this->getSourceContext()); @@ -386,12 +413,6 @@ public function yield(array $context, array $blocks = []): iterable $e->guess(); throw $e; - } finally { - if (!$this->useYield) { - while (ob_get_level() > $level) { - ob_end_clean(); - } - } } } @@ -418,27 +439,7 @@ public function yieldBlock($name, array $context, array $blocks = [], $useBlocks if (null !== $template) { try { - if ($this->useYield) { - yield from $template->$block($context, $blocks); - - return; - } - - $level = ob_get_level(); - ob_start(); - - foreach ($template->$block($context, $blocks) as $data) { - if (ob_get_length()) { - $data = ob_get_clean().$data; - ob_start(); - } - - yield $data; - } - - if (ob_get_length()) { - yield ob_get_clean(); - } + yield from $template->$block($context, $blocks); } catch (Error $e) { if (!$e->getSourceContext()) { $e->setSourceContext($template->getSourceContext()); @@ -456,12 +457,6 @@ public function yieldBlock($name, array $context, array $blocks = [], $useBlocks $e->guess(); throw $e; - } finally { - if (!$this->useYield) { - while (ob_get_level() > $level) { - ob_end_clean(); - } - } } } elseif ($parent = $this->getParent($context)) { yield from $parent->unwrap()->yieldBlock($name, $context, array_merge($this->blocks, $blocks), false, $templateContext ?? $this); diff --git a/upload/system/storage/vendor/twig/twig/src/Test/IntegrationTestCase.php b/upload/system/storage/vendor/twig/twig/src/Test/IntegrationTestCase.php index 5519dd0a9..88b3349c4 100644 --- a/upload/system/storage/vendor/twig/twig/src/Test/IntegrationTestCase.php +++ b/upload/system/storage/vendor/twig/twig/src/Test/IntegrationTestCase.php @@ -156,13 +156,16 @@ protected function doIntegrationTest($file, $message, $condition, $templates, $e } } - $loader = new ArrayLoader($templates); - foreach ($outputs as $i => $match) { $config = array_merge([ 'cache' => false, 'strict_variables' => true, ], $match[2] ? eval($match[2].';') : []); + // make sure that template are always compiled even if they are the same (useful when testing with more than one data/expect sections) + foreach ($templates as $j => $template) { + $templates[$j] = $template.str_repeat(' ', $i); + } + $loader = new ArrayLoader($templates); $twig = new Environment($loader, $config); $twig->addGlobal('global', 'global'); foreach ($this->getRuntimeLoaders() as $runtimeLoader) { diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php index 4dbf30406..0a6c1afb5 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php @@ -36,16 +36,16 @@ public function parse(Token $token): Node $ref = new TempNameExpression($name, $lineno); $ref->setAttribute('always_defined', true); - $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag()); + $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref); $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideApplyEnd'], true); $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); return new Node([ - new SetNode(true, $ref, $body, $lineno, $this->getTag()), - new PrintNode($filter, $lineno, $this->getTag()), - ]); + new SetNode(true, $ref, $body, $lineno), + new PrintNode($filter, $lineno), + ], [], $lineno); } public function decideApplyEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php index b674bea4a..b50b29e65 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php @@ -29,7 +29,7 @@ public function parse(Token $token): Node $lineno = $token->getLine(); $stream = $this->parser->getStream(); - if ($stream->test(/* Token::BLOCK_END_TYPE */ 3)) { + if ($stream->test(Token::BLOCK_END_TYPE)) { $value = 'html'; } else { $expr = $this->parser->getExpressionParser()->parseExpression(); @@ -39,11 +39,11 @@ public function parse(Token $token): Node $value = $expr->getAttribute('value'); } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideBlockEnd'], true); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); - return new AutoEscapeNode($value, $body, $lineno, $this->getTag()); + return new AutoEscapeNode($value, $body, $lineno); } public function decideBlockEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/BlockTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/BlockTokenParser.php index c654d31f9..81d675db0 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/BlockTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/BlockTokenParser.php @@ -35,17 +35,14 @@ public function parse(Token $token): Node { $lineno = $token->getLine(); $stream = $this->parser->getStream(); - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); - if ($this->parser->hasBlock($name)) { - throw new SyntaxError(\sprintf("The block '%s' has already been defined line %d.", $name, $this->parser->getBlock($name)->getTemplateLine()), $stream->getCurrent()->getLine(), $stream->getSourceContext()); - } + $name = $stream->expect(Token::NAME_TYPE)->getValue(); $this->parser->setBlock($name, $block = new BlockNode($name, new Node([]), $lineno)); $this->parser->pushLocalScope(); $this->parser->pushBlockStack($name); - if ($stream->nextIf(/* Token::BLOCK_END_TYPE */ 3)) { + if ($stream->nextIf(Token::BLOCK_END_TYPE)) { $body = $this->parser->subparse([$this, 'decideBlockEnd'], true); - if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) { + if ($token = $stream->nextIf(Token::NAME_TYPE)) { $value = $token->getValue(); if ($value != $name) { @@ -57,13 +54,13 @@ public function parse(Token $token): Node new PrintNode($this->parser->getExpressionParser()->parseExpression(), $lineno), ]); } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $block->setNode('body', $body); $this->parser->popBlockStack(); $this->parser->popLocalScope(); - return new BlockReferenceNode($name, $lineno, $this->getTag()); + return new BlockReferenceNode($name, $lineno); } public function decideBlockEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php index c17c4aadc..164ef26ee 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php @@ -35,7 +35,7 @@ public function parse(Token $token): Node $stream = $this->parser->getStream(); $expressionParser = $this->parser->getExpressionParser(); $expr = $expressionParser->parseExpression(); - $node = new DeprecatedNode($expr, $token->getLine(), $this->getTag()); + $node = new DeprecatedNode($expr, $token->getLine()); while ($stream->test(Token::NAME_TYPE)) { $k = $stream->getCurrent()->getValue(); diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/DoTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/DoTokenParser.php index 32c8f12ff..8afd48559 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/DoTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/DoTokenParser.php @@ -26,9 +26,9 @@ public function parse(Token $token): Node { $expr = $this->parser->getExpressionParser()->parseExpression(); - $this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3); + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); - return new DoNode($expr, $token->getLine(), $this->getTag()); + return new DoNode($expr, $token->getLine()); } public function getTag(): string diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php index adf683cc1..7bf3233e2 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php @@ -32,19 +32,19 @@ public function parse(Token $token): Node [$variables, $only, $ignoreMissing] = $this->parseArguments(); - $parentToken = $fakeParentToken = new Token(/* Token::STRING_TYPE */ 7, '__parent__', $token->getLine()); + $parentToken = $fakeParentToken = new Token(Token::STRING_TYPE, '__parent__', $token->getLine()); if ($parent instanceof ConstantExpression) { - $parentToken = new Token(/* Token::STRING_TYPE */ 7, $parent->getAttribute('value'), $token->getLine()); + $parentToken = new Token(Token::STRING_TYPE, $parent->getAttribute('value'), $token->getLine()); } elseif ($parent instanceof NameExpression) { - $parentToken = new Token(/* Token::NAME_TYPE */ 5, $parent->getAttribute('name'), $token->getLine()); + $parentToken = new Token(Token::NAME_TYPE, $parent->getAttribute('name'), $token->getLine()); } // inject a fake parent to make the parent() function work $stream->injectTokens([ - new Token(/* Token::BLOCK_START_TYPE */ 1, '', $token->getLine()), - new Token(/* Token::NAME_TYPE */ 5, 'extends', $token->getLine()), + new Token(Token::BLOCK_START_TYPE, '', $token->getLine()), + new Token(Token::NAME_TYPE, 'extends', $token->getLine()), $parentToken, - new Token(/* Token::BLOCK_END_TYPE */ 3, '', $token->getLine()), + new Token(Token::BLOCK_END_TYPE, '', $token->getLine()), ]); $module = $this->parser->parse($stream, [$this, 'decideBlockEnd'], true); @@ -56,9 +56,9 @@ public function parse(Token $token): Node $this->parser->embedTemplate($module); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); - return new EmbedNode($module->getTemplateName(), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); + return new EmbedNode($module->getTemplateName(), $module->getAttribute('index'), $variables, $only, $ignoreMissing, $token->getLine()); } public function decideBlockEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php index 0ca46dd29..86ddfdfba 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php @@ -35,14 +35,11 @@ public function parse(Token $token): Node throw new SyntaxError('Cannot use "extend" in a macro.', $token->getLine(), $stream->getSourceContext()); } - if (null !== $this->parser->getParent()) { - throw new SyntaxError('Multiple extends tags are forbidden.', $token->getLine(), $stream->getSourceContext()); - } $this->parser->setParent($this->parser->getExpressionParser()->parseExpression()); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); - return new Node(); + return new Node([], [], $token->getLine()); } public function getTag(): string diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/FlushTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/FlushTokenParser.php index 02c74aa13..0d2388745 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/FlushTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/FlushTokenParser.php @@ -26,9 +26,9 @@ final class FlushTokenParser extends AbstractTokenParser { public function parse(Token $token): Node { - $this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3); + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); - return new FlushNode($token->getLine(), $this->getTag()); + return new FlushNode($token->getLine()); } public function getTag(): string diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/ForTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/ForTokenParser.php index 1af6da8fd..cf655f842 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/ForTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/ForTokenParser.php @@ -35,18 +35,18 @@ public function parse(Token $token): Node $lineno = $token->getLine(); $stream = $this->parser->getStream(); $targets = $this->parser->getExpressionParser()->parseAssignmentExpression(); - $stream->expect(/* Token::OPERATOR_TYPE */ 8, 'in'); + $stream->expect(Token::OPERATOR_TYPE, 'in'); $seq = $this->parser->getExpressionParser()->parseExpression(); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideForFork']); if ('else' == $stream->next()->getValue()) { - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $else = $this->parser->subparse([$this, 'decideForEnd'], true); } else { $else = null; } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); if (\count($targets) > 1) { $keyTarget = $targets->getNode('0'); @@ -58,7 +58,7 @@ public function parse(Token $token): Node } $valueTarget = new AssignNameExpression($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine()); - return new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, $lineno, $this->getTag()); + return new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, $lineno); } public function decideForFork(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/FromTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/FromTokenParser.php index 31b6cde41..2ccff5fbe 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/FromTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/FromTokenParser.php @@ -29,28 +29,28 @@ public function parse(Token $token): Node { $macro = $this->parser->getExpressionParser()->parseExpression(); $stream = $this->parser->getStream(); - $stream->expect(/* Token::NAME_TYPE */ 5, 'import'); + $stream->expect(Token::NAME_TYPE, 'import'); $targets = []; while (true) { - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + $name = $stream->expect(Token::NAME_TYPE)->getValue(); $alias = $name; if ($stream->nextIf('as')) { - $alias = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + $alias = $stream->expect(Token::NAME_TYPE)->getValue(); } $targets[$name] = $alias; - if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $var = new AssignNameExpression($this->parser->getVarName(), $token->getLine()); - $node = new ImportNode($macro, $var, $token->getLine(), $this->getTag(), $this->parser->isMainScope()); + $node = new ImportNode($macro, $var, $token->getLine(), $this->parser->isMainScope()); foreach ($targets as $name => $alias) { $this->parser->addImportedSymbol('function', $alias, 'macro_'.$name, $var); diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/IfTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/IfTokenParser.php index 569ccfaf1..4ea6f3df9 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/IfTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/IfTokenParser.php @@ -37,7 +37,7 @@ public function parse(Token $token): Node $lineno = $token->getLine(); $expr = $this->parser->getExpressionParser()->parseExpression(); $stream = $this->parser->getStream(); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideIfFork']); $tests = [$expr, $body]; $else = null; @@ -46,13 +46,13 @@ public function parse(Token $token): Node while (!$end) { switch ($stream->next()->getValue()) { case 'else': - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $else = $this->parser->subparse([$this, 'decideIfEnd']); break; case 'elseif': $expr = $this->parser->getExpressionParser()->parseExpression(); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideIfFork']); $tests[] = $expr; $tests[] = $body; @@ -67,9 +67,9 @@ public function parse(Token $token): Node } } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); - return new IfNode(new Node($tests), $else, $lineno, $this->getTag()); + return new IfNode(new Node($tests), $else, $lineno); } public function decideIfFork(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/ImportTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/ImportTokenParser.php index 44cb4dad7..f20f35ab3 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/ImportTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/ImportTokenParser.php @@ -28,13 +28,13 @@ final class ImportTokenParser extends AbstractTokenParser public function parse(Token $token): Node { $macro = $this->parser->getExpressionParser()->parseExpression(); - $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5, 'as'); - $var = new AssignNameExpression($this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5)->getValue(), $token->getLine()); - $this->parser->getStream()->expect(/* Token::BLOCK_END_TYPE */ 3); + $this->parser->getStream()->expect(Token::NAME_TYPE, 'as'); + $var = new AssignNameExpression($this->parser->getStream()->expect(Token::NAME_TYPE)->getValue(), $token->getLine()); + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); $this->parser->addImportedSymbol('template', $var->getAttribute('name')); - return new ImportNode($macro, $var, $token->getLine(), $this->getTag(), $this->parser->isMainScope()); + return new ImportNode($macro, $var, $token->getLine(), $this->parser->isMainScope()); } public function getTag(): string diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php index fda5bfd8c..466f2288c 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php @@ -33,7 +33,7 @@ public function parse(Token $token): Node [$variables, $only, $ignoreMissing] = $this->parseArguments(); - return new IncludeNode($expr, $variables, $only, $ignoreMissing, $token->getLine(), $this->getTag()); + return new IncludeNode($expr, $variables, $only, $ignoreMissing, $token->getLine()); } protected function parseArguments() @@ -41,23 +41,23 @@ protected function parseArguments() $stream = $this->parser->getStream(); $ignoreMissing = false; - if ($stream->nextIf(/* Token::NAME_TYPE */ 5, 'ignore')) { - $stream->expect(/* Token::NAME_TYPE */ 5, 'missing'); + if ($stream->nextIf(Token::NAME_TYPE, 'ignore')) { + $stream->expect(Token::NAME_TYPE, 'missing'); $ignoreMissing = true; } $variables = null; - if ($stream->nextIf(/* Token::NAME_TYPE */ 5, 'with')) { + if ($stream->nextIf(Token::NAME_TYPE, 'with')) { $variables = $this->parser->getExpressionParser()->parseExpression(); } $only = false; - if ($stream->nextIf(/* Token::NAME_TYPE */ 5, 'only')) { + if ($stream->nextIf(Token::NAME_TYPE, 'only')) { $only = true; } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); return [$variables, $only, $ignoreMissing]; } diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/MacroTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/MacroTokenParser.php index 1f0e3e97f..c7762075c 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/MacroTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/MacroTokenParser.php @@ -32,14 +32,14 @@ public function parse(Token $token): Node { $lineno = $token->getLine(); $stream = $this->parser->getStream(); - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + $name = $stream->expect(Token::NAME_TYPE)->getValue(); $arguments = $this->parser->getExpressionParser()->parseArguments(true, true); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $this->parser->pushLocalScope(); $body = $this->parser->subparse([$this, 'decideBlockEnd'], true); - if ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) { + if ($token = $stream->nextIf(Token::NAME_TYPE)) { $value = $token->getValue(); if ($value != $name) { @@ -47,11 +47,11 @@ public function parse(Token $token): Node } } $this->parser->popLocalScope(); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); - $this->parser->setMacro($name, new MacroNode($name, new BodyNode([$body]), $arguments, $lineno, $this->getTag())); + $this->parser->setMacro($name, new MacroNode($name, new BodyNode([$body]), $arguments, $lineno)); - return new Node(); + return new Node([], [], $lineno); } public function decideBlockEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php index c919556ec..70869fbc5 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php @@ -34,9 +34,9 @@ final class SandboxTokenParser extends AbstractTokenParser public function parse(Token $token): Node { $stream = $this->parser->getStream(); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideBlockEnd'], true); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); // in a sandbox tag, only include tags are allowed if (!$body instanceof IncludeNode) { @@ -51,7 +51,7 @@ public function parse(Token $token): Node } } - return new SandboxNode($body, $token->getLine(), $this->getTag()); + return new SandboxNode($body, $token->getLine()); } public function decideBlockEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/SetTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/SetTokenParser.php index 2fbdfe090..bb43907bd 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/SetTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/SetTokenParser.php @@ -37,10 +37,10 @@ public function parse(Token $token): Node $names = $this->parser->getExpressionParser()->parseAssignmentExpression(); $capture = false; - if ($stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) { + if ($stream->nextIf(Token::OPERATOR_TYPE, '=')) { $values = $this->parser->getExpressionParser()->parseMultitargetExpression(); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); if (\count($names) !== \count($values)) { throw new SyntaxError('When using set, you must have the same number of variables and assignments.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); @@ -52,13 +52,13 @@ public function parse(Token $token): Node throw new SyntaxError('When using set with a block, you cannot have a multi-target.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $values = $this->parser->subparse([$this, 'decideBlockEnd'], true); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); } - return new SetNode($capture, $names, $values, $lineno, $this->getTag()); + return new SetNode($capture, $names, $values, $lineno); } public function decideBlockEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/UseTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/UseTokenParser.php index 3cdbb98ad..1b96b4047 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/UseTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/UseTokenParser.php @@ -44,26 +44,26 @@ public function parse(Token $token): Node $targets = []; if ($stream->nextIf('with')) { while (true) { - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + $name = $stream->expect(Token::NAME_TYPE)->getValue(); $alias = $name; if ($stream->nextIf('as')) { - $alias = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + $alias = $stream->expect(Token::NAME_TYPE)->getValue(); } $targets[$name] = new ConstantExpression($alias, -1); - if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } } } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $this->parser->addTrait(new Node(['template' => $template, 'targets' => new Node($targets)])); - return new Node(); + return new Node([], [], $token->getLine()); } public function getTag(): string diff --git a/upload/system/storage/vendor/twig/twig/src/TokenParser/WithTokenParser.php b/upload/system/storage/vendor/twig/twig/src/TokenParser/WithTokenParser.php index 7d8cbe261..8ce4f02b2 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenParser/WithTokenParser.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenParser/WithTokenParser.php @@ -30,18 +30,18 @@ public function parse(Token $token): Node $variables = null; $only = false; - if (!$stream->test(/* Token::BLOCK_END_TYPE */ 3)) { + if (!$stream->test(Token::BLOCK_END_TYPE)) { $variables = $this->parser->getExpressionParser()->parseExpression(); - $only = (bool) $stream->nextIf(/* Token::NAME_TYPE */ 5, 'only'); + $only = (bool) $stream->nextIf(Token::NAME_TYPE, 'only'); } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideWithEnd'], true); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); - return new WithNode($body, $variables, $only, $token->getLine(), $this->getTag()); + return new WithNode($body, $variables, $only, $token->getLine()); } public function decideWithEnd(Token $token): bool diff --git a/upload/system/storage/vendor/twig/twig/src/TokenStream.php b/upload/system/storage/vendor/twig/twig/src/TokenStream.php index 9921f788d..32357f931 100644 --- a/upload/system/storage/vendor/twig/twig/src/TokenStream.php +++ b/upload/system/storage/vendor/twig/twig/src/TokenStream.php @@ -110,7 +110,7 @@ public function test($primary, $secondary = null): bool */ public function isEOF(): bool { - return /* Token::EOF_TYPE */ -1 === $this->tokens[$this->current]->getType(); + return Token::EOF_TYPE === $this->tokens[$this->current]->getType(); } public function getCurrent(): Token diff --git a/upload/system/storage/vendor/twig/twig/src/TwigCallableInterface.php b/upload/system/storage/vendor/twig/twig/src/TwigCallableInterface.php new file mode 100644 index 000000000..2a8ff6116 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/src/TwigCallableInterface.php @@ -0,0 +1,53 @@ + + */ +interface TwigCallableInterface extends \Stringable +{ + public function getName(): string; + + public function getType(): string; + + public function getDynamicName(): string; + + /** + * @return callable|array{class-string, string}|null + */ + public function getCallable(); + + public function getNodeClass(): string; + + public function needsCharset(): bool; + + public function needsEnvironment(): bool; + + public function needsContext(): bool; + + public function withDynamicArguments(string $name, string $dynamicName, array $arguments): self; + + public function getArguments(): array; + + public function isVariadic(): bool; + + public function isDeprecated(): bool; + + public function getDeprecatingPackage(): string; + + public function getDeprecatedVersion(): string; + + public function getAlternative(): ?string; + + public function getMinimalNumberOfRequiredArguments(): int; +} diff --git a/upload/system/storage/vendor/twig/twig/src/TwigFilter.php b/upload/system/storage/vendor/twig/twig/src/TwigFilter.php index 2b80df939..70b1f8f3f 100644 --- a/upload/system/storage/vendor/twig/twig/src/TwigFilter.php +++ b/upload/system/storage/vendor/twig/twig/src/TwigFilter.php @@ -21,79 +21,27 @@ * * @see https://twig.symfony.com/doc/templates.html#filters */ -final class TwigFilter +final class TwigFilter extends AbstractTwigCallable { - private $name; - private $callable; - private $options; - private $arguments = []; - /** * @param callable|array{class-string, string}|null $callable A callable implementing the filter. If null, you need to overwrite the "node_class" option to customize compilation. */ public function __construct(string $name, $callable = null, array $options = []) { - $this->name = $name; - $this->callable = $callable; + parent::__construct($name, $callable, $options); + $this->options = array_merge([ - 'needs_environment' => false, - 'needs_context' => false, - 'needs_charset' => false, - 'is_variadic' => false, 'is_safe' => null, 'is_safe_callback' => null, 'pre_escape' => null, 'preserves_safety' => null, 'node_class' => FilterExpression::class, - 'deprecated' => false, - 'deprecating_package' => '', - 'alternative' => null, - ], $options); - } - - public function getName(): string - { - return $this->name; - } - - /** - * Returns the callable to execute for this filter. - * - * @return callable|array{class-string, string}|null - */ - public function getCallable() - { - return $this->callable; - } - - public function getNodeClass(): string - { - return $this->options['node_class']; - } - - public function setArguments(array $arguments): void - { - $this->arguments = $arguments; - } - - public function getArguments(): array - { - return $this->arguments; + ], $this->options); } - public function needsCharset(): bool + public function getType(): string { - return $this->options['needs_charset']; - } - - public function needsEnvironment(): bool - { - return $this->options['needs_environment']; - } - - public function needsContext(): bool - { - return $this->options['needs_context']; + return 'filter'; } public function getSafe(Node $filterArgs): ?array @@ -119,28 +67,8 @@ public function getPreEscape(): ?string return $this->options['pre_escape']; } - public function isVariadic(): bool - { - return $this->options['is_variadic']; - } - - public function isDeprecated(): bool - { - return (bool) $this->options['deprecated']; - } - - public function getDeprecatingPackage(): string - { - return $this->options['deprecating_package']; - } - - public function getDeprecatedVersion(): string - { - return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated']; - } - - public function getAlternative(): ?string + public function getMinimalNumberOfRequiredArguments(): int { - return $this->options['alternative']; + return parent::getMinimalNumberOfRequiredArguments() + 1; } } diff --git a/upload/system/storage/vendor/twig/twig/src/TwigFunction.php b/upload/system/storage/vendor/twig/twig/src/TwigFunction.php index bfee7eb87..4a10df95e 100644 --- a/upload/system/storage/vendor/twig/twig/src/TwigFunction.php +++ b/upload/system/storage/vendor/twig/twig/src/TwigFunction.php @@ -21,77 +21,31 @@ * * @see https://twig.symfony.com/doc/templates.html#functions */ -final class TwigFunction +final class TwigFunction extends AbstractTwigCallable { - private $name; - private $callable; - private $options; - private $arguments = []; - /** * @param callable|array{class-string, string}|null $callable A callable implementing the function. If null, you need to overwrite the "node_class" option to customize compilation. */ public function __construct(string $name, $callable = null, array $options = []) { - $this->name = $name; - $this->callable = $callable; + parent::__construct($name, $callable, $options); + $this->options = array_merge([ - 'needs_environment' => false, - 'needs_context' => false, - 'needs_charset' => false, - 'is_variadic' => false, 'is_safe' => null, 'is_safe_callback' => null, 'node_class' => FunctionExpression::class, - 'deprecated' => false, - 'deprecating_package' => '', - 'alternative' => null, - ], $options); - } - - public function getName(): string - { - return $this->name; - } - - /** - * Returns the callable to execute for this function. - * - * @return callable|array{class-string, string}|null - */ - public function getCallable() - { - return $this->callable; - } - - public function getNodeClass(): string - { - return $this->options['node_class']; - } - - public function setArguments(array $arguments): void - { - $this->arguments = $arguments; - } - - public function getArguments(): array - { - return $this->arguments; + 'parser_callable' => null, + ], $this->options); } - public function needsCharset(): bool + public function getType(): string { - return $this->options['needs_charset']; + return 'function'; } - public function needsEnvironment(): bool + public function getParserCallable(): ?callable { - return $this->options['needs_environment']; - } - - public function needsContext(): bool - { - return $this->options['needs_context']; + return $this->options['parser_callable']; } public function getSafe(Node $functionArgs): ?array @@ -106,29 +60,4 @@ public function getSafe(Node $functionArgs): ?array return []; } - - public function isVariadic(): bool - { - return (bool) $this->options['is_variadic']; - } - - public function isDeprecated(): bool - { - return (bool) $this->options['deprecated']; - } - - public function getDeprecatingPackage(): string - { - return $this->options['deprecating_package']; - } - - public function getDeprecatedVersion(): string - { - return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated']; - } - - public function getAlternative(): ?string - { - return $this->options['alternative']; - } } diff --git a/upload/system/storage/vendor/twig/twig/src/TwigTest.php b/upload/system/storage/vendor/twig/twig/src/TwigTest.php index 0b43a2849..5e58ad8b0 100644 --- a/upload/system/storage/vendor/twig/twig/src/TwigTest.php +++ b/upload/system/storage/vendor/twig/twig/src/TwigTest.php @@ -20,87 +20,48 @@ * * @see https://twig.symfony.com/doc/templates.html#test-operator */ -final class TwigTest +final class TwigTest extends AbstractTwigCallable { - private $name; - private $callable; - private $options; - private $arguments = []; - /** * @param callable|array{class-string, string}|null $callable A callable implementing the test. If null, you need to overwrite the "node_class" option to customize compilation. */ public function __construct(string $name, $callable = null, array $options = []) { - $this->name = $name; - $this->callable = $callable; + parent::__construct($name, $callable, $options); + $this->options = array_merge([ - 'is_variadic' => false, 'node_class' => TestExpression::class, - 'deprecated' => false, - 'deprecating_package' => '', - 'alternative' => null, 'one_mandatory_argument' => false, - ], $options); - } - - public function getName(): string - { - return $this->name; - } - - /** - * Returns the callable to execute for this test. - * - * @return callable|array{class-string, string}|null - */ - public function getCallable() - { - return $this->callable; + ], $this->options); } - public function getNodeClass(): string + public function getType(): string { - return $this->options['node_class']; + return 'test'; } - public function setArguments(array $arguments): void + public function needsCharset(): bool { - $this->arguments = $arguments; + return false; } - public function getArguments(): array + public function needsEnvironment(): bool { - return $this->arguments; + return false; } - public function isVariadic(): bool + public function needsContext(): bool { - return (bool) $this->options['is_variadic']; + return false; } - public function isDeprecated(): bool - { - return (bool) $this->options['deprecated']; - } - - public function getDeprecatingPackage(): string - { - return $this->options['deprecating_package']; - } - - public function getDeprecatedVersion(): string - { - return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated']; - } - - public function getAlternative(): ?string + public function hasOneMandatoryArgument(): bool { - return $this->options['alternative']; + return (bool) $this->options['one_mandatory_argument']; } - public function hasOneMandatoryArgument(): bool + public function getMinimalNumberOfRequiredArguments(): int { - return (bool) $this->options['one_mandatory_argument']; + return parent::getMinimalNumberOfRequiredArguments() + 1; } } diff --git a/upload/system/storage/vendor/twig/twig/src/Util/CallableArgumentsExtractor.php b/upload/system/storage/vendor/twig/twig/src/Util/CallableArgumentsExtractor.php new file mode 100644 index 000000000..8811ca9c8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/src/Util/CallableArgumentsExtractor.php @@ -0,0 +1,204 @@ + + * + * @internal + */ +final class CallableArgumentsExtractor +{ + private ReflectionCallable $rc; + + public function __construct( + private Node $node, + private TwigCallableInterface $twigCallable, + ) { + $this->rc = new ReflectionCallable($twigCallable); + } + + /** + * @return array + */ + public function extractArguments(Node $arguments): array + { + $extractedArguments = []; + $named = false; + foreach ($arguments as $name => $node) { + if (!\is_int($name)) { + $named = true; + $name = $this->normalizeName($name); + } elseif ($named) { + throw new SyntaxError(\sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); + } + + $extractedArguments[$name] = $node; + } + + if (!$named && !$this->twigCallable->isVariadic()) { + $min = $this->twigCallable->getMinimalNumberOfRequiredArguments(); + if (\count($extractedArguments) < $this->rc->getReflector()->getNumberOfRequiredParameters() - $min) { + throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $this->rc->getReflector()->getParameters()[$min + \count($extractedArguments)]->getName(), $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); + } + + return $extractedArguments; + } + + if (!$callable = $this->twigCallable->getCallable()) { + if ($named) { + throw new SyntaxError(\sprintf('Named arguments are not supported for %s "%s".', $this->twigCallable->getType(), $this->twigCallable->getName())); + } + + throw new SyntaxError(\sprintf('Arbitrary positional arguments are not supported for %s "%s".', $this->twigCallable->getType(), $this->twigCallable->getName())); + } + + [$callableParameters, $isPhpVariadic] = $this->getCallableParameters(); + $arguments = []; + $names = []; + $missingArguments = []; + $optionalArguments = []; + $pos = 0; + foreach ($callableParameters as $callableParameter) { + $name = $this->normalizeName($callableParameter->name); + if (\PHP_VERSION_ID >= 80000 && 'range' === $callable) { + if ('start' === $name) { + $name = 'low'; + } elseif ('end' === $name) { + $name = 'high'; + } + } + + $names[] = $name; + + if (\array_key_exists($name, $extractedArguments)) { + if (\array_key_exists($pos, $extractedArguments)) { + throw new SyntaxError(\sprintf('Argument "%s" is defined twice for %s "%s".', $name, $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); + } + + if (\count($missingArguments)) { + throw new SyntaxError(\sprintf( + 'Argument "%s" could not be assigned for %s "%s(%s)" because it is mapped to an internal PHP function which cannot determine default value for optional argument%s "%s".', + $name, $this->twigCallable->getType(), $this->twigCallable->getName(), implode(', ', $names), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments) + ), $this->node->getTemplateLine(), $this->node->getSourceContext()); + } + + $arguments = array_merge($arguments, $optionalArguments); + $arguments[] = $extractedArguments[$name]; + unset($extractedArguments[$name]); + $optionalArguments = []; + } elseif (\array_key_exists($pos, $extractedArguments)) { + $arguments = array_merge($arguments, $optionalArguments); + $arguments[] = $extractedArguments[$pos]; + unset($extractedArguments[$pos]); + $optionalArguments = []; + ++$pos; + } elseif ($callableParameter->isDefaultValueAvailable()) { + $optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), $this->node->getTemplateLine()); + } elseif ($callableParameter->isOptional()) { + if (!$extractedArguments) { + break; + } + + $missingArguments[] = $name; + } else { + throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $name, $this->twigCallable->getType(), $this->twigCallable->getName()), $this->node->getTemplateLine(), $this->node->getSourceContext()); + } + } + + if ($this->twigCallable->isVariadic()) { + $arbitraryArguments = $isPhpVariadic ? new VariadicExpression([], $this->node->getTemplateLine()) : new ArrayExpression([], $this->node->getTemplateLine()); + foreach ($extractedArguments as $key => $value) { + if (\is_int($key)) { + $arbitraryArguments->addElement($value); + } else { + $arbitraryArguments->addElement($value, new ConstantExpression($key, $this->node->getTemplateLine())); + } + unset($extractedArguments[$key]); + } + + if ($arbitraryArguments->count()) { + $arguments = array_merge($arguments, $optionalArguments); + $arguments[] = $arbitraryArguments; + } + } + + if ($extractedArguments) { + $unknownArgument = null; + foreach ($extractedArguments as $extractedArgument) { + if ($extractedArgument instanceof Node) { + $unknownArgument = $extractedArgument; + break; + } + } + + throw new SyntaxError( + \sprintf( + 'Unknown argument%s "%s" for %s "%s(%s)".', + \count($extractedArguments) > 1 ? 's' : '', implode('", "', array_keys($extractedArguments)), $this->twigCallable->getType(), $this->twigCallable->getName(), implode(', ', $names) + ), + $unknownArgument ? $unknownArgument->getTemplateLine() : $this->node->getTemplateLine(), + $unknownArgument ? $unknownArgument->getSourceContext() : $this->node->getSourceContext() + ); + } + + return $arguments; + } + + private function normalizeName(string $name): string + { + return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z\d])([A-Z])/'], ['\\1_\\2', '\\1_\\2'], $name)); + } + + private function getCallableParameters(): array + { + $parameters = $this->rc->getReflector()->getParameters(); + if ($this->node->hasNode('node')) { + array_shift($parameters); + } + if ($this->twigCallable->needsCharset()) { + array_shift($parameters); + } + if ($this->twigCallable->needsEnvironment()) { + array_shift($parameters); + } + if ($this->twigCallable->needsContext()) { + array_shift($parameters); + } + foreach ($this->twigCallable->getArguments() as $argument) { + array_shift($parameters); + } + + $isPhpVariadic = false; + if ($this->twigCallable->isVariadic()) { + $argument = end($parameters); + $isArray = $argument && $argument->hasType() && $argument->getType() instanceof \ReflectionNamedType && 'array' === $argument->getType()->getName(); + if ($isArray && $argument->isDefaultValueAvailable() && [] === $argument->getDefaultValue()) { + array_pop($parameters); + } elseif ($argument && $argument->isVariadic()) { + array_pop($parameters); + $isPhpVariadic = true; + } else { + throw new SyntaxError(\sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $this->rc->getName(), $this->twigCallable->getType(), $this->twigCallable->getName())); + } + } + + return [$parameters, $isPhpVariadic]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/src/Util/ReflectionCallable.php b/upload/system/storage/vendor/twig/twig/src/Util/ReflectionCallable.php index 54384e14b..9b183f14d 100644 --- a/upload/system/storage/vendor/twig/twig/src/Util/ReflectionCallable.php +++ b/upload/system/storage/vendor/twig/twig/src/Util/ReflectionCallable.php @@ -11,6 +11,8 @@ namespace Twig\Util; +use Twig\TwigCallableInterface; + /** * @author Fabien Potencier * @@ -19,11 +21,13 @@ final class ReflectionCallable { private $reflector; - private $callable = null; + private $callable; private $name; - public function __construct($callable, string $debugType = 'unknown', string $debugName = 'unknown') - { + public function __construct( + private TwigCallableInterface $twigCallable, + ) { + $callable = $twigCallable->getCallable(); if (\is_string($callable) && false !== $pos = strpos($callable, '::')) { $callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)]; } @@ -40,7 +44,7 @@ public function __construct($callable, string $debugType = 'unknown', string $de try { $closure = \Closure::fromCallable($callable); } catch (\TypeError $e) { - throw new \LogicException(\sprintf('Callback for %s "%s" is not callable in the current scope.', $debugType, $debugName), 0, $e); + throw new \LogicException(\sprintf('Callback for %s "%s" is not callable in the current scope.', $twigCallable->getType(), $twigCallable->getName()), 0, $e); } $this->reflector = $r = new \ReflectionFunction($closure); diff --git a/upload/system/storage/vendor/twig/twig/tests/Cache/ChainTest.php b/upload/system/storage/vendor/twig/twig/tests/Cache/ChainTest.php new file mode 100644 index 000000000..3120ab188 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Cache/ChainTest.php @@ -0,0 +1,229 @@ +className = '__Twig_Tests_Cache_ChainTest_Template_'.$nonce; + $this->directory = sys_get_temp_dir().'/twig-test'; + $this->cache = new ChainCache([ + new FilesystemCache($this->directory.'/A'), + new FilesystemCache($this->directory.'/B'), + ]); + $this->key = $this->cache->generateKey('_test_', $this->className); + } + + protected function tearDown(): void + { + if (file_exists($this->directory)) { + FilesystemHelper::removeDir($this->directory); + } + } + + public function testLoadInA() + { + $cache = new FilesystemCache($this->directory.'/A'); + $key = $cache->generateKey('_test_', $this->className); + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + $this->assertFalse(class_exists($this->className, false)); + + $content = $this->generateSource(); + file_put_contents($key, $content); + + $this->cache->load($this->key); + + $this->assertTrue(class_exists($this->className, false)); + } + + public function testLoadInB() + { + $cache = new FilesystemCache($this->directory.'/B'); + $key = $cache->generateKey('_test_', $this->className); + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + $this->assertFalse(class_exists($this->className, false)); + + $content = $this->generateSource(); + file_put_contents($key, $content); + + $this->cache->load($this->key); + + $this->assertTrue(class_exists($this->className, false)); + } + + public function testLoadInBoth() + { + $cache = new FilesystemCache($this->directory.'/A'); + $key = $cache->generateKey('_test_', $this->className); + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + $this->assertFalse(class_exists($this->className, false)); + + $content = $this->generateSource(); + file_put_contents($key, $content); + + $cache = new FilesystemCache($this->directory.'/B'); + $key = $cache->generateKey('_test_', $this->className); + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + $this->assertFalse(class_exists($this->className, false)); + + $content = $this->generateSource(); + file_put_contents($key, $content); + + $this->cache->load($this->key); + + $this->assertTrue(class_exists($this->className, false)); + } + + public function testLoadMissing() + { + $this->assertFalse(class_exists($this->className, false)); + + $this->cache->load($this->key); + + $this->assertFalse(class_exists($this->className, false)); + } + + public function testWrite() + { + $content = $this->generateSource(); + + $cacheA = new FilesystemCache($this->directory.'/A'); + $keyA = $cacheA->generateKey('_test_', $this->className); + + $this->assertFileDoesNotExist($keyA); + $this->assertFileDoesNotExist($this->directory.'/A'); + + $cacheB = new FilesystemCache($this->directory.'/B'); + $keyB = $cacheB->generateKey('_test_', $this->className); + + $this->assertFileDoesNotExist($keyB); + $this->assertFileDoesNotExist($this->directory.'/B'); + + $this->cache->write($this->key, $content); + + $this->assertFileExists($this->directory.'/A'); + $this->assertFileExists($keyA); + $this->assertSame(file_get_contents($keyA), $content); + + $this->assertFileExists($this->directory.'/B'); + $this->assertFileExists($keyB); + $this->assertSame(file_get_contents($keyB), $content); + } + + public function testGetTimestampInA() + { + $cache = new FilesystemCache($this->directory.'/A'); + $key = $cache->generateKey('_test_', $this->className); + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + + // Create the file with a specific modification time. + touch($key, 1234567890); + + $this->assertSame(1234567890, $this->cache->getTimestamp($this->key)); + } + + public function testGetTimestampInB() + { + $cache = new FilesystemCache($this->directory.'/B'); + $key = $cache->generateKey('_test_', $this->className); + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + + // Create the file with a specific modification time. + touch($key, 1234567890); + + $this->assertSame(1234567890, $this->cache->getTimestamp($this->key)); + } + + public function testGetTimestampInBoth() + { + $cacheA = new FilesystemCache($this->directory.'/A'); + $keyA = $cacheA->generateKey('_test_', $this->className); + + $dir = \dirname($keyA); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + + // Create the file with a specific modification time. + touch($keyA, 1234567890); + + $cacheB = new FilesystemCache($this->directory.'/B'); + $keyB = $cacheB->generateKey('_test_', $this->className); + + $dir = \dirname($keyB); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + + // Create the file with a specific modification time. + touch($keyB, 1234567891); + + $this->assertSame(1234567890, $this->cache->getTimestamp($this->key)); + } + + public function testGetTimestampMissingFile() + { + $this->assertSame(0, $this->cache->getTimestamp($this->key)); + } + + /** + * @dataProvider provideInput + */ + public function testGenerateKey($expected, $input) + { + $cache = new ChainCache([]); + $this->assertSame($expected, $cache->generateKey($input, static::class)); + } + + public static function provideInput() + { + return [ + ['Twig\Tests\Cache\ChainTest#_test_', '_test_'], + ['Twig\Tests\Cache\ChainTest#_test#with#hashtag_', '_test#with#hashtag_'], + ]; + } + + private function generateSource() + { + return strtr(' $this->className, + ]); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Cache/FilesystemTest.php b/upload/system/storage/vendor/twig/twig/tests/Cache/FilesystemTest.php new file mode 100644 index 000000000..574631404 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Cache/FilesystemTest.php @@ -0,0 +1,194 @@ +className = '__Twig_Tests_Cache_FilesystemTest_Template_'.$nonce; + $this->directory = sys_get_temp_dir().'/twig-test'; + $this->cache = new FilesystemCache($this->directory); + } + + protected function tearDown(): void + { + if (file_exists($this->directory)) { + FilesystemHelper::removeDir($this->directory); + } + } + + public function testLoad() + { + $key = $this->directory.'/cache/cachefile.php'; + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + $this->assertFalse(class_exists($this->className, false)); + + $content = $this->generateSource(); + file_put_contents($key, $content); + + $this->cache->load($key); + + $this->assertTrue(class_exists($this->className, false)); + } + + public function testLoadMissing() + { + $key = $this->directory.'/cache/cachefile.php'; + + $this->assertFalse(class_exists($this->className, false)); + + $this->cache->load($key); + + $this->assertFalse(class_exists($this->className, false)); + } + + public function testWrite() + { + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileDoesNotExist($key); + $this->assertFileDoesNotExist($this->directory); + + $this->cache->write($key, $content); + + $this->assertFileExists($this->directory); + $this->assertFileExists($key); + $this->assertSame(file_get_contents($key), $content); + } + + public function testWriteFailMkdir() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Unable to create the cache directory'); + + if (\defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Read-only directories not possible on Windows.'); + } + + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileDoesNotExist($key); + + // Create read-only root directory. + @mkdir($this->directory, 0555, true); + $this->assertDirectoryExists($this->directory); + + $this->cache->write($key, $content); + } + + public function testWriteFailDirWritable() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Unable to write in the cache directory'); + + if (\defined('PHP_WINDOWS_VERSION_BUILD')) { + $this->markTestSkipped('Read-only directories not possible on Windows.'); + } + + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileDoesNotExist($key); + + // Create root directory. + @mkdir($this->directory, 0777, true); + // Create read-only subdirectory. + @mkdir($this->directory.'/cache', 0555); + $this->assertDirectoryExists($this->directory.'/cache'); + + $this->cache->write($key, $content); + } + + public function testWriteFailWriteFile() + { + $this->expectException(\RuntimeException::class); + $this->expectExceptionMessage('Failed to write cache file'); + + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileDoesNotExist($key); + + // Create a directory in the place of the cache file. + @mkdir($key, 0777, true); + $this->assertDirectoryExists($key); + + $this->cache->write($key, $content); + } + + public function testGetTimestamp() + { + $key = $this->directory.'/cache/cachefile.php'; + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + + // Create the file with a specific modification time. + touch($key, 1234567890); + + $this->assertSame(1234567890, $this->cache->getTimestamp($key)); + } + + public function testGetTimestampMissingFile() + { + $key = $this->directory.'/cache/cachefile.php'; + $this->assertSame(0, $this->cache->getTimestamp($key)); + } + + /** + * Test file cache is tolerant towards trailing (back)slashes on the configured cache directory. + * + * @dataProvider provideDirectories + */ + public function testGenerateKey($expected, $input) + { + $cache = new FilesystemCache($input); + $this->assertMatchesRegularExpression($expected, $cache->generateKey('_test_', static::class)); + } + + public function provideDirectories() + { + $pattern = '#a/b/[a-zA-Z0-9]+/[a-zA-Z0-9]+.php$#'; + + return [ + [$pattern, 'a/b'], + [$pattern, 'a/b/'], + [$pattern, 'a/b\\'], + [$pattern, 'a/b\\/'], + [$pattern, 'a/b\\//'], + ['#/'.substr($pattern, 1), '/a/b'], + ]; + } + + private function generateSource() + { + return strtr(' $this->className, + ]); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Cache/ReadOnlyFilesystemTest.php b/upload/system/storage/vendor/twig/twig/tests/Cache/ReadOnlyFilesystemTest.php new file mode 100644 index 000000000..34bc14f88 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Cache/ReadOnlyFilesystemTest.php @@ -0,0 +1,132 @@ +className = '__Twig_Tests_Cache_ReadOnlyFilesystemTest_Template_'.$nonce; + $this->directory = sys_get_temp_dir().'/twig-test'; + $this->cache = new ReadOnlyFilesystemCache($this->directory); + } + + protected function tearDown(): void + { + if (file_exists($this->directory)) { + FilesystemHelper::removeDir($this->directory); + } + } + + public function testLoad() + { + $key = $this->directory.'/cache/ro-cachefile.php'; + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + $this->assertFalse(class_exists($this->className, false)); + + $content = $this->generateSource(); + file_put_contents($key, $content); + + $this->cache->load($key); + + $this->assertTrue(class_exists($this->className, false)); + } + + public function testLoadMissing() + { + $key = $this->directory.'/cache/cachefile.php'; + + $this->assertFalse(class_exists($this->className, false)); + + $this->cache->load($key); + + $this->assertFalse(class_exists($this->className, false)); + } + + public function testWrite() + { + $key = $this->directory.'/cache/cachefile.php'; + $content = $this->generateSource(); + + $this->assertFileDoesNotExist($key); + $this->assertFileDoesNotExist($this->directory); + + $this->cache->write($key, $content); + + $this->assertFileDoesNotExist($this->directory); + $this->assertFileDoesNotExist($key); + } + + public function testGetTimestamp() + { + $key = $this->directory.'/cache/cachefile.php'; + + $dir = \dirname($key); + @mkdir($dir, 0777, true); + $this->assertDirectoryExists($dir); + + // Create the file with a specific modification time. + touch($key, 1234567890); + + $this->assertSame(1234567890, $this->cache->getTimestamp($key)); + } + + public function testGetTimestampMissingFile() + { + $key = $this->directory.'/cache/cachefile.php'; + $this->assertSame(0, $this->cache->getTimestamp($key)); + } + + /** + * Test file cache is tolerant towards trailing (back)slashes on the configured cache directory. + * + * @dataProvider provideDirectories + */ + public function testGenerateKey($expected, $input) + { + $cache = new ReadOnlyFilesystemCache($input); + $this->assertMatchesRegularExpression($expected, $cache->generateKey('_test_', static::class)); + } + + public static function provideDirectories() + { + $pattern = '#a/b/[a-zA-Z0-9]+/[a-zA-Z0-9]+.php$#'; + + return [ + [$pattern, 'a/b'], + [$pattern, 'a/b/'], + [$pattern, 'a/b\\'], + [$pattern, 'a/b\\/'], + [$pattern, 'a/b\\//'], + ['#/'.substr($pattern, 1), '/a/b'], + ]; + } + + private function generateSource() + { + return strtr(' $this->className, + ]); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/CompilerTest.php b/upload/system/storage/vendor/twig/twig/tests/CompilerTest.php new file mode 100644 index 000000000..051edfeb2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/CompilerTest.php @@ -0,0 +1,40 @@ +markTestSkipped('Your platform does not support locales.'); + } + + $required_locales = ['fr_FR.UTF-8', 'fr_FR.UTF8', 'fr_FR.utf-8', 'fr_FR.utf8', 'French_France.1252']; + if (false === setlocale(\LC_NUMERIC, $required_locales)) { + $this->markTestSkipped('Could not set any of required locales: '.implode(', ', $required_locales)); + } + + $this->assertEquals('1.2', $compiler->repr(1.2)->getSource()); + $this->assertStringContainsString('fr', strtolower(setlocale(\LC_NUMERIC, '0'))); + + setlocale(\LC_NUMERIC, $locale); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/ContainerRuntimeLoaderTest.php b/upload/system/storage/vendor/twig/twig/tests/ContainerRuntimeLoaderTest.php new file mode 100644 index 000000000..cadd6826f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/ContainerRuntimeLoaderTest.php @@ -0,0 +1,39 @@ +createMock(ContainerInterface::class); + $container->expects($this->once())->method('has')->with('stdClass')->willReturn(true); + $container->expects($this->once())->method('get')->with('stdClass')->willReturn(new \stdClass()); + + $loader = new ContainerRuntimeLoader($container); + + $this->assertInstanceOf('stdClass', $loader->load('stdClass')); + } + + public function testLoadUnknownRuntimeReturnsNull() + { + $container = $this->createMock(ContainerInterface::class); + $container->expects($this->once())->method('has')->with('Foo'); + $container->expects($this->never())->method('get'); + + $this->assertNull((new ContainerRuntimeLoader($container))->load('Foo')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/CustomExtensionTest.php b/upload/system/storage/vendor/twig/twig/tests/CustomExtensionTest.php new file mode 100644 index 000000000..a2ac0dbed --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/CustomExtensionTest.php @@ -0,0 +1,80 @@ +expectException(\InvalidArgumentException::class); + $this->expectExceptionMessage($expectedExceptionMessage); + + $env = new Environment(new ArrayLoader()); + $env->addExtension($extension); + $env->getUnaryOperators(); + } + + public function provideInvalidExtensions() + { + return [ + [new InvalidOperatorExtension([1, 2, 3]), '"Twig\Tests\InvalidOperatorExtension::getOperators()" must return an array of 2 elements, got 3.'], + ]; + } +} + +class InvalidOperatorExtension implements ExtensionInterface +{ + private $operators; + + public function __construct($operators) + { + $this->operators = $operators; + } + + public function getTokenParsers(): array + { + return []; + } + + public function getNodeVisitors(): array + { + return []; + } + + public function getFilters(): array + { + return []; + } + + public function getTests(): array + { + return []; + } + + public function getFunctions(): array + { + return []; + } + + public function getOperators(): array + { + return $this->operators; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/DummyBackedEnum.php b/upload/system/storage/vendor/twig/twig/tests/DummyBackedEnum.php new file mode 100644 index 000000000..c9b07d208 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/DummyBackedEnum.php @@ -0,0 +1,9 @@ +assertEquals(Environment::EXTRA_VERSION, $exploded[1] ?? ''); + + $version = $exploded[0]; + $exploded = explode('.', $version); + $this->assertEquals(Environment::MAJOR_VERSION, $exploded[0]); + $this->assertEquals(Environment::MINOR_VERSION, $exploded[1]); + $this->assertEquals(Environment::RELEASE_VERSION, $exploded[2]); + + $this->assertEquals(Environment::VERSION_ID, \sprintf('%s0%s0%s', $exploded[0], $exploded[1], $exploded[2])); + } + + public function testAutoescapeOption() + { + $loader = new ArrayLoader([ + 'html' => '{{ foo }} {{ foo }}', + 'js' => '{{ bar }} {{ bar }}', + ]); + + $twig = new Environment($loader, [ + 'debug' => true, + 'cache' => false, + 'autoescape' => [$this, 'escapingStrategyCallback'], + ]); + + $this->assertEquals('foo<br/ > foo<br/ >', $twig->render('html', ['foo' => 'foo
    '])); + $this->assertEquals('foo\u003Cbr\/\u0020\u003E foo\u003Cbr\/\u0020\u003E', $twig->render('js', ['bar' => 'foo
    '])); + } + + public function escapingStrategyCallback($name) + { + return $name; + } + + public function testGlobals() + { + $loader = $this->createMock(LoaderInterface::class); + $loader->expects($this->any())->method('getSourceContext')->willReturn(new Source('', '')); + + // globals can be added after calling getGlobals + $twig = new Environment($loader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->addGlobal('foo', 'bar'); + $globals = $twig->getGlobals(); + $this->assertEquals('bar', $globals['foo']); + + // globals can be modified after a template has been loaded + $twig = new Environment($loader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->load('index'); + $twig->addGlobal('foo', 'bar'); + $globals = $twig->getGlobals(); + $this->assertEquals('bar', $globals['foo']); + + // globals can be modified after extensions init + $twig = new Environment($loader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->getFunctions(); + $twig->addGlobal('foo', 'bar'); + $globals = $twig->getGlobals(); + $this->assertEquals('bar', $globals['foo']); + + // globals can be modified after extensions and a template has been loaded + $arrayLoader = new ArrayLoader(['index' => '{{foo}}']); + $twig = new Environment($arrayLoader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->getFunctions(); + $twig->load('index'); + $twig->addGlobal('foo', 'bar'); + $globals = $twig->getGlobals(); + $this->assertEquals('bar', $globals['foo']); + + $twig = new Environment($arrayLoader); + $twig->getGlobals(); + $twig->addGlobal('foo', 'bar'); + $template = $twig->load('index'); + $this->assertEquals('bar', $template->render([])); + + // globals cannot be added after a template has been loaded + $twig = new Environment($loader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->load('index'); + try { + $twig->addGlobal('bar', 'bar'); + $this->fail(); + } catch (\LogicException $e) { + $this->assertArrayNotHasKey('bar', $twig->getGlobals()); + } + + // globals cannot be added after extensions init + $twig = new Environment($loader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->getFunctions(); + try { + $twig->addGlobal('bar', 'bar'); + $this->fail(); + } catch (\LogicException $e) { + $this->assertArrayNotHasKey('bar', $twig->getGlobals()); + } + + // globals cannot be added after extensions and a template has been loaded + $twig = new Environment($loader); + $twig->addGlobal('foo', 'foo'); + $twig->getGlobals(); + $twig->getFunctions(); + $twig->load('index'); + try { + $twig->addGlobal('bar', 'bar'); + $this->fail(); + } catch (\LogicException $e) { + $this->assertArrayNotHasKey('bar', $twig->getGlobals()); + } + + // test adding globals after a template has been loaded without call to getGlobals + $twig = new Environment($loader); + $twig->load('index'); + try { + $twig->addGlobal('bar', 'bar'); + $this->fail(); + } catch (\LogicException $e) { + $this->assertArrayNotHasKey('bar', $twig->getGlobals()); + } + } + + public function testExtensionsAreNotInitializedWhenRenderingACompiledTemplate() + { + $cache = new FilesystemCache($dir = sys_get_temp_dir().'/twig'); + $options = ['cache' => $cache, 'auto_reload' => false, 'debug' => false]; + + // force compilation + $twig = new Environment($loader = new ArrayLoader(['index' => '{{ foo }}']), $options); + + $key = $cache->generateKey('index', $twig->getTemplateClass('index')); + $cache->write($key, $twig->compileSource(new Source('{{ foo }}', 'index'))); + + // check that extensions won't be initialized when rendering a template that is already in the cache + $twig = $this + ->getMockBuilder(Environment::class) + ->setConstructorArgs([$loader, $options]) + ->setMethods(['initExtensions']) + ->getMock() + ; + + $twig->expects($this->never())->method('initExtensions'); + + // render template + $output = $twig->render('index', ['foo' => 'bar']); + $this->assertEquals('bar', $output); + + FilesystemHelper::removeDir($dir); + } + + public function testAutoReloadCacheMiss() + { + $templateName = __FUNCTION__; + $templateContent = __FUNCTION__; + + $cache = $this->createMock(CacheInterface::class); + $loader = $this->getMockLoader($templateName, $templateContent); + $twig = new Environment($loader, ['cache' => $cache, 'auto_reload' => true, 'debug' => false]); + + // Cache miss: getTimestamp returns 0 and as a result the load() is + // skipped. + $cache->expects($this->once()) + ->method('generateKey') + ->willReturn('key'); + $cache->expects($this->once()) + ->method('getTimestamp') + ->willReturn(0); + $loader->expects($this->never()) + ->method('isFresh'); + $cache->expects($this->once()) + ->method('write'); + $cache->expects($this->once()) + ->method('load'); + + $twig->load($templateName); + } + + public function testAutoReloadCacheHit() + { + $templateName = __FUNCTION__; + $templateContent = __FUNCTION__; + + $cache = $this->createMock(CacheInterface::class); + $loader = $this->getMockLoader($templateName, $templateContent); + $twig = new Environment($loader, ['cache' => $cache, 'auto_reload' => true, 'debug' => false]); + + $now = time(); + + // Cache hit: getTimestamp returns something > extension timestamps and + // the loader returns true for isFresh(). + $cache->expects($this->once()) + ->method('generateKey') + ->willReturn('key'); + $cache->expects($this->once()) + ->method('getTimestamp') + ->willReturn($now); + $loader->expects($this->once()) + ->method('isFresh') + ->willReturn(true); + $cache->expects($this->atLeastOnce()) + ->method('load'); + + $twig->load($templateName); + } + + public function testAutoReloadOutdatedCacheHit() + { + $templateName = __FUNCTION__; + $templateContent = __FUNCTION__; + + $cache = $this->createMock(CacheInterface::class); + $loader = $this->getMockLoader($templateName, $templateContent); + $twig = new Environment($loader, ['cache' => $cache, 'auto_reload' => true, 'debug' => false]); + + $now = time(); + + $cache->expects($this->once()) + ->method('generateKey') + ->willReturn('key'); + $cache->expects($this->once()) + ->method('getTimestamp') + ->willReturn($now); + $loader->expects($this->once()) + ->method('isFresh') + ->willReturn(false); + $cache->expects($this->once()) + ->method('write'); + $cache->expects($this->once()) + ->method('load'); + + $twig->load($templateName); + } + + public function testHasGetExtensionByClassName() + { + $twig = new Environment(new ArrayLoader()); + $twig->addExtension($ext = new EnvironmentTest_Extension()); + $this->assertSame($ext, $twig->getExtension(EnvironmentTest_Extension::class)); + $this->assertSame($ext, $twig->getExtension(EnvironmentTest_Extension::class)); + } + + public function testAddExtension() + { + $twig = new Environment(new ArrayLoader()); + $twig->addExtension(new EnvironmentTest_Extension()); + + $this->assertArrayHasKey('test', $twig->getTokenParsers()); + $this->assertArrayHasKey('foo_filter', $twig->getFilters()); + $this->assertArrayHasKey('foo_function', $twig->getFunctions()); + $this->assertArrayHasKey('foo_test', $twig->getTests()); + $this->assertArrayHasKey('foo_unary', $twig->getUnaryOperators()); + $this->assertArrayHasKey('foo_binary', $twig->getBinaryOperators()); + $this->assertArrayHasKey('foo_global', $twig->getGlobals()); + $visitors = $twig->getNodeVisitors(); + $found = false; + foreach ($visitors as $visitor) { + if ($visitor instanceof EnvironmentTest_NodeVisitor) { + $found = true; + } + } + $this->assertTrue($found); + } + + public function testAddMockExtension() + { + $extension = $this->createMock(ExtensionInterface::class); + $loader = new ArrayLoader(['page' => 'hey']); + + $twig = new Environment($loader); + $twig->addExtension($extension); + + $this->assertInstanceOf(ExtensionInterface::class, $twig->getExtension(\get_class($extension))); + $this->assertTrue($twig->isTemplateFresh('page', time())); + } + + public function testOverrideExtension() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Unable to register extension "Twig\Tests\EnvironmentTest_Extension" as it is already registered.'); + + $twig = new Environment(new ArrayLoader()); + + $twig->addExtension(new EnvironmentTest_Extension()); + $twig->addExtension(new EnvironmentTest_Extension()); + } + + public function testAddRuntimeLoader() + { + $runtimeLoader = $this->createMock(RuntimeLoaderInterface::class); + $runtimeLoader->expects($this->any())->method('load')->willReturn(new EnvironmentTest_Runtime()); + + $loader = new ArrayLoader([ + 'func_array' => '{{ from_runtime_array("foo") }}', + 'func_array_default' => '{{ from_runtime_array() }}', + 'func_array_named_args' => '{{ from_runtime_array(name="foo") }}', + 'func_string' => '{{ from_runtime_string("foo") }}', + 'func_string_default' => '{{ from_runtime_string() }}', + 'func_string_named_args' => '{{ from_runtime_string(name="foo") }}', + ]); + + $twig = new Environment($loader, ['autoescape' => false]); + $twig->addExtension(new EnvironmentTest_ExtensionWithoutRuntime()); + $twig->addRuntimeLoader($runtimeLoader); + + $this->assertEquals('foo', $twig->render('func_array')); + $this->assertEquals('bar', $twig->render('func_array_default')); + $this->assertEquals('foo', $twig->render('func_array_named_args')); + $this->assertEquals('foo', $twig->render('func_string')); + $this->assertEquals('bar', $twig->render('func_string_default')); + $this->assertEquals('foo', $twig->render('func_string_named_args')); + } + + public function testFailLoadTemplate() + { + $this->expectException(RuntimeError::class); + $this->expectExceptionMessage('Failed to load Twig template "testFailLoadTemplate.twig", index "112233": cache might be corrupted in "testFailLoadTemplate.twig".'); + + $template = 'testFailLoadTemplate.twig'; + $twig = new Environment(new ArrayLoader([$template => false])); + $twig->loadTemplate($twig->getTemplateClass($template), $template, 112233); + } + + public function testUndefinedFunctionCallback() + { + $twig = new Environment(new ArrayLoader()); + $twig->registerUndefinedFunctionCallback(function (string $name) { + if ('dynamic' === $name) { + return new TwigFunction('dynamic', function () { return 'dynamic'; }); + } + + return false; + }); + + $this->assertNull($twig->getFunction('does_not_exist')); + $this->assertInstanceOf(TwigFunction::class, $function = $twig->getFunction('dynamic')); + $this->assertSame('dynamic', $function->getName()); + } + + public function testUndefinedFilterCallback() + { + $twig = new Environment(new ArrayLoader()); + $twig->registerUndefinedFilterCallback(function (string $name) { + if ('dynamic' === $name) { + return new TwigFilter('dynamic', function () { return 'dynamic'; }); + } + + return false; + }); + + $this->assertNull($twig->getFilter('does_not_exist')); + $this->assertInstanceOf(TwigFilter::class, $filter = $twig->getFilter('dynamic')); + $this->assertSame('dynamic', $filter->getName()); + } + + public function testUndefinedTokenParserCallback() + { + $twig = new Environment(new ArrayLoader()); + $twig->registerUndefinedTokenParserCallback(function (string $name) { + if ('dynamic' === $name) { + $parser = $this->createMock(TokenParserInterface::class); + $parser->expects($this->once())->method('getTag')->willReturn('dynamic'); + + return $parser; + } + + return false; + }); + + $this->assertNull($twig->getTokenParser('does_not_exist')); + $this->assertInstanceOf(TokenParserInterface::class, $parser = $twig->getTokenParser('dynamic')); + $this->assertSame('dynamic', $parser->getTag()); + } + + /** + * @group legacy + * + * @requires PHP 8 + */ + public function testLegacyEchoingNode() + { + $loader = new ArrayLoader(['echo_bar' => 'A{% set v %}B{% test %}C{% endset %}D{% test %}E{{ v }}F{% set w %}{% test %}{% endset %}G{{ w }}H']); + + $twig = new Environment($loader); + $twig->addExtension(new EnvironmentTest_Extension()); + + if ($twig->useYield()) { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('An exception has been thrown during the compilation of a template ("You cannot enable the "use_yield" option of Twig as node "Twig\Tests\EnvironmentTest_LegacyEchoingNode" is not marked as ready for it; please make it ready and then flag it with the #[YieldReady] attribute.") in "echo_bar".'); + } else { + $this->expectDeprecation(<<<'EOF' +Since twig/twig 3.9: Twig node "Twig\Tests\EnvironmentTest_LegacyEchoingNode" is not marked as ready for using "yield" instead of "echo"; please make it ready and then flag it with the #[YieldReady] attribute. + Since twig/twig 3.9: Using "echo" is deprecated, use "yield" instead in "Twig\Tests\EnvironmentTest_LegacyEchoingNode", then flag the class with #[YieldReady]. +EOF + ); + } + + $this->assertSame('ADbarEBbarCFGbarH', $twig->render('echo_bar')); + } + + protected function getMockLoader($templateName, $templateContent) + { + $loader = $this->createMock(LoaderInterface::class); + $loader->expects($this->any()) + ->method('getSourceContext') + ->with($templateName) + ->willReturn(new Source($templateContent, $templateName)); + $loader->expects($this->any()) + ->method('getCacheKey') + ->with($templateName) + ->willReturn($templateName); + + return $loader; + } +} + +class EnvironmentTest_Extension_WithGlobals extends AbstractExtension +{ + public function getGlobals() + { + return [ + 'foo_global' => 'foo_global', + ]; + } +} + +class EnvironmentTest_Extension extends AbstractExtension implements GlobalsInterface +{ + public function getTokenParsers(): array + { + return [ + new EnvironmentTest_TokenParser(), + ]; + } + + public function getNodeVisitors(): array + { + return [ + new EnvironmentTest_NodeVisitor(), + ]; + } + + public function getFilters(): array + { + return [ + new TwigFilter('foo_filter'), + ]; + } + + public function getTests(): array + { + return [ + new TwigTest('foo_test'), + ]; + } + + public function getFunctions(): array + { + return [ + new TwigFunction('foo_function'), + ]; + } + + public function getOperators(): array + { + return [ + ['foo_unary' => []], + ['foo_binary' => []], + ]; + } + + public function getGlobals(): array + { + return [ + 'foo_global' => 'foo_global', + ]; + } +} + +class EnvironmentTest_TokenParser extends AbstractTokenParser +{ + public function parse(Token $token): Node + { + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); + + return new EnvironmentTest_LegacyEchoingNode([], [], 1); + } + + public function getTag(): string + { + return 'test'; + } +} + +class EnvironmentTest_NodeVisitor implements NodeVisitorInterface +{ + public function enterNode(Node $node, Environment $env): Node + { + return $node; + } + + public function leaveNode(Node $node, Environment $env): ?Node + { + return $node; + } + + public function getPriority(): int + { + return 0; + } +} + +class EnvironmentTest_ExtensionWithoutRuntime extends AbstractExtension +{ + public function getFunctions(): array + { + return [ + new TwigFunction('from_runtime_array', ['Twig\Tests\EnvironmentTest_Runtime', 'fromRuntime']), + new TwigFunction('from_runtime_string', 'Twig\Tests\EnvironmentTest_Runtime::fromRuntime'), + ]; + } +} + +class EnvironmentTest_Runtime +{ + public function fromRuntime($name = 'bar') + { + return $name; + } +} + +class EnvironmentTest_LegacyEchoingNode extends Node +{ + public function compile($compiler) + { + $compiler + ->addDebugInfo($this) + ->write('echo "bar";') + ; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/ErrorTest.php b/upload/system/storage/vendor/twig/twig/tests/ErrorTest.php new file mode 100644 index 000000000..423a1a58d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/ErrorTest.php @@ -0,0 +1,293 @@ +setSourceContext(new Source('', new \SplFileInfo(__FILE__))); + + $this->assertStringContainsString('tests'.\DIRECTORY_SEPARATOR.'ErrorTest.php', $error->getMessage()); + } + + public function testTwigExceptionGuessWithMissingVarAndArrayLoader() + { + $loader = new ArrayLoader([ + 'base.html' => '{% block content %}{% endblock %}', + 'index.html' => << true, 'debug' => true, 'cache' => false]); + + $template = $twig->load('index.html'); + try { + $template->render([]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals('Variable "foo" does not exist in "index.html" at line 3.', $e->getMessage()); + $this->assertEquals(3, $e->getTemplateLine()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + } + } + + public function testTwigExceptionGuessWithExceptionAndArrayLoader() + { + $loader = new ArrayLoader([ + 'base.html' => '{% block content %}{% endblock %}', + 'index.html' => << true, 'debug' => true, 'cache' => false]); + + $template = $twig->load('index.html'); + try { + $template->render(['foo' => new ErrorTest_Foo()]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...") in "index.html" at line 3.', $e->getMessage()); + $this->assertEquals(3, $e->getTemplateLine()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + } + } + + public function testTwigExceptionGuessWithMissingVarAndFilesystemLoader() + { + $loader = new FilesystemLoader(__DIR__.'/Fixtures/errors'); + $twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]); + + $template = $twig->load('index.html'); + try { + $template->render([]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals('Variable "foo" does not exist.', $e->getMessage()); + $this->assertEquals(3, $e->getTemplateLine()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + $this->assertEquals(3, $e->getLine()); + $this->assertEquals(strtr(__DIR__.'/Fixtures/errors/index.html', '/', \DIRECTORY_SEPARATOR), $e->getFile()); + } + } + + public function testTwigExceptionGuessWithExceptionAndFilesystemLoader() + { + $loader = new FilesystemLoader(__DIR__.'/Fixtures/errors'); + $twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]); + + $template = $twig->load('index.html'); + try { + $template->render(['foo' => new ErrorTest_Foo()]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals('An exception has been thrown during the rendering of a template ("Runtime error...").', $e->getMessage()); + $this->assertEquals(3, $e->getTemplateLine()); + $this->assertEquals('index.html', $e->getSourceContext()->getName()); + $this->assertEquals(3, $e->getLine()); + $this->assertEquals(strtr(__DIR__.'/Fixtures/errors/index.html', '/', \DIRECTORY_SEPARATOR), $e->getFile()); + } + } + + /** + * @dataProvider getErroredTemplates + */ + public function testTwigExceptionAddsFileAndLine($templates, $name, $line) + { + $loader = new ArrayLoader($templates); + $twig = new Environment($loader, ['strict_variables' => true, 'debug' => true, 'cache' => false]); + + $template = $twig->load('index'); + + try { + $template->render([]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals(\sprintf('Variable "foo" does not exist in "%s" at line %d.', $name, $line), $e->getMessage()); + $this->assertEquals($line, $e->getTemplateLine()); + $this->assertEquals($name, $e->getSourceContext()->getName()); + } + + try { + $template->render(['foo' => new ErrorTest_Foo()]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals(\sprintf('An exception has been thrown during the rendering of a template ("Runtime error...") in "%s" at line %d.', $name, $line), $e->getMessage()); + $this->assertEquals($line, $e->getTemplateLine()); + $this->assertEquals($name, $e->getSourceContext()->getName()); + } + } + + public function testTwigArrayFilterThrowsRuntimeExceptions() + { + $loader = new ArrayLoader([ + 'filter-null.html' => << x > 3) %} + This list contains {{n}}. +{% endfor %} +EOHTML + ]); + + $twig = new Environment($loader, ['debug' => true, 'cache' => false]); + + $template = $twig->load('filter-null.html'); + $out = $template->render(['variable' => [1, 2, 3, 4]]); + $this->assertEquals('This list contains 4.', trim($out)); + + try { + $template->render(['variable' => null]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals(2, $e->getTemplateLine()); + $this->assertEquals('filter-null.html', $e->getSourceContext()->getName()); + } + } + + public function testTwigArrayMapThrowsRuntimeExceptions() + { + $loader = new ArrayLoader([ + 'map-null.html' => << x * 3) %} + {{- n -}} +{% endfor %} +EOHTML + ]); + + $twig = new Environment($loader, ['debug' => true, 'cache' => false]); + + $template = $twig->load('map-null.html'); + $out = $template->render(['variable' => [1, 2, 3, 4]]); + $this->assertEquals('36912', trim($out)); + + try { + $template->render(['variable' => null]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals(2, $e->getTemplateLine()); + $this->assertEquals('map-null.html', $e->getSourceContext()->getName()); + } + } + + public function testTwigArrayReduceThrowsRuntimeExceptions() + { + $loader = new ArrayLoader([ + 'reduce-null.html' => << carry + x) }} +EOHTML + ]); + + $twig = new Environment($loader, ['debug' => true, 'cache' => false]); + + $template = $twig->load('reduce-null.html'); + $out = $template->render(['variable' => [1, 2, 3, 4]]); + $this->assertEquals('10', trim($out)); + + try { + $template->render(['variable' => null]); + + $this->fail(); + } catch (RuntimeError $e) { + $this->assertEquals(2, $e->getTemplateLine()); + $this->assertEquals('reduce-null.html', $e->getSourceContext()->getName()); + } + } + + public function getErroredTemplates() + { + return [ + // error occurs in a template + [ + [ + 'index' => "\n\n{{ foo.bar }}\n\n\n{{ 'foo' }}", + ], + 'index', 3, + ], + + // error occurs in an included template + [ + [ + 'index' => "{% include 'partial' %}", + 'partial' => '{{ foo.bar }}', + ], + 'partial', 1, + ], + + // error occurs in a parent block when called via parent() + [ + [ + 'index' => "{% extends 'base' %} + {% block content %} + {{ parent() }} + {% endblock %}", + 'base' => '{% block content %}{{ foo.bar }}{% endblock %}', + ], + 'base', 1, + ], + + // error occurs in a block from the child + [ + [ + 'index' => "{% extends 'base' %} + {% block content %} + {{ foo.bar }} + {% endblock %} + {% block foo %} + {{ foo.bar }} + {% endblock %}", + 'base' => '{% block content %}{% endblock %}', + ], + 'index', 3, + ], + ]; + } +} + +class ErrorTest_Foo +{ + public function bar() + { + throw new \Exception('Runtime error...'); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/ExpressionParserTest.php b/upload/system/storage/vendor/twig/twig/tests/ExpressionParserTest.php new file mode 100644 index 000000000..3f28cca1e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/ExpressionParserTest.php @@ -0,0 +1,659 @@ +expectException(SyntaxError::class); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + $parser->parse($env->tokenize(new Source($template, 'index'))); + } + + public function getFailingTestsForAssignment() + { + return [ + ['{% set false = "foo" %}'], + ['{% set FALSE = "foo" %}'], + ['{% set true = "foo" %}'], + ['{% set TRUE = "foo" %}'], + ['{% set none = "foo" %}'], + ['{% set NONE = "foo" %}'], + ['{% set null = "foo" %}'], + ['{% set NULL = "foo" %}'], + ['{% set 3 = "foo" %}'], + ['{% set 1 + 2 = "foo" %}'], + ['{% set "bar" = "foo" %}'], + ['{% set %}{% endset %}'], + ]; + } + + /** + * @dataProvider getTestsForSequence + */ + public function testSequenceExpression($template, $expected) + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $stream = $env->tokenize($source = new Source($template, '')); + $parser = new Parser($env); + $expected->setSourceContext($source); + + $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode('0')->getNode('expr')); + } + + /** + * @dataProvider getFailingTestsForSequence + */ + public function testSequenceSyntaxError($template) + { + $this->expectException(SyntaxError::class); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + $parser->parse($env->tokenize(new Source($template, 'index'))); + } + + public function getFailingTestsForSequence() + { + return [ + ['{{ [1, "a": "b"] }}'], + ['{{ {"a": "b", 2} }}'], + ['{{ {"a"} }}'], + ]; + } + + public function getTestsForSequence() + { + return [ + // simple sequence + ['{{ [1, 2] }}', new ArrayExpression([ + new ConstantExpression(0, 1), + new ConstantExpression(1, 1), + + new ConstantExpression(1, 1), + new ConstantExpression(2, 1), + ], 1), + ], + + // sequence with trailing , + ['{{ [1, 2, ] }}', new ArrayExpression([ + new ConstantExpression(0, 1), + new ConstantExpression(1, 1), + + new ConstantExpression(1, 1), + new ConstantExpression(2, 1), + ], 1), + ], + + // simple mapping + ['{{ {"a": "b", "b": "c"} }}', new ArrayExpression([ + new ConstantExpression('a', 1), + new ConstantExpression('b', 1), + + new ConstantExpression('b', 1), + new ConstantExpression('c', 1), + ], 1), + ], + + // mapping with trailing , + ['{{ {"a": "b", "b": "c", } }}', new ArrayExpression([ + new ConstantExpression('a', 1), + new ConstantExpression('b', 1), + + new ConstantExpression('b', 1), + new ConstantExpression('c', 1), + ], 1), + ], + + // mapping in a sequence + ['{{ [1, {"a": "b", "b": "c"}] }}', new ArrayExpression([ + new ConstantExpression(0, 1), + new ConstantExpression(1, 1), + + new ConstantExpression(1, 1), + new ArrayExpression([ + new ConstantExpression('a', 1), + new ConstantExpression('b', 1), + + new ConstantExpression('b', 1), + new ConstantExpression('c', 1), + ], 1), + ], 1), + ], + + // sequence in a mapping + ['{{ {"a": [1, 2], "b": "c"} }}', new ArrayExpression([ + new ConstantExpression('a', 1), + new ArrayExpression([ + new ConstantExpression(0, 1), + new ConstantExpression(1, 1), + + new ConstantExpression(1, 1), + new ConstantExpression(2, 1), + ], 1), + new ConstantExpression('b', 1), + new ConstantExpression('c', 1), + ], 1), + ], + ['{{ {a, b} }}', new ArrayExpression([ + new ConstantExpression('a', 1), + new NameExpression('a', 1), + new ConstantExpression('b', 1), + new NameExpression('b', 1), + ], 1)], + + // sequence with spread operator + ['{{ [1, 2, ...foo] }}', + new ArrayExpression([ + new ConstantExpression(0, 1), + new ConstantExpression(1, 1), + + new ConstantExpression(1, 1), + new ConstantExpression(2, 1), + + new ConstantExpression(2, 1), + $this->createNameExpression('foo', ['spread' => true]), + ], 1)], + + // mapping with spread operator + ['{{ {"a": "b", "b": "c", ...otherLetters} }}', + new ArrayExpression([ + new ConstantExpression('a', 1), + new ConstantExpression('b', 1), + + new ConstantExpression('b', 1), + new ConstantExpression('c', 1), + + new ConstantExpression(0, 1), + $this->createNameExpression('otherLetters', ['spread' => true]), + ], 1)], + ]; + } + + public function testStringExpressionDoesNotConcatenateTwoConsecutiveStrings() + { + $this->expectException(SyntaxError::class); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false, 'optimizations' => 0]); + $stream = $env->tokenize(new Source('{{ "a" "b" }}', 'index')); + $parser = new Parser($env); + + $parser->parse($stream); + } + + /** + * @dataProvider getTestsForString + */ + public function testStringExpression($template, $expected) + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false, 'optimizations' => 0]); + $stream = $env->tokenize($source = new Source($template, '')); + $parser = new Parser($env); + $expected->setSourceContext($source); + + $this->assertEquals($expected, $parser->parse($stream)->getNode('body')->getNode('0')->getNode('expr')); + } + + public function getTestsForString() + { + return [ + [ + '{{ "foo #{bar}" }}', new ConcatBinary( + new ConstantExpression('foo ', 1), + new NameExpression('bar', 1), + 1 + ), + ], + [ + '{{ "foo #{bar} baz" }}', new ConcatBinary( + new ConcatBinary( + new ConstantExpression('foo ', 1), + new NameExpression('bar', 1), + 1 + ), + new ConstantExpression(' baz', 1), + 1 + ), + ], + + [ + '{{ "foo #{"foo #{bar} baz"} baz" }}', new ConcatBinary( + new ConcatBinary( + new ConstantExpression('foo ', 1), + new ConcatBinary( + new ConcatBinary( + new ConstantExpression('foo ', 1), + new NameExpression('bar', 1), + 1 + ), + new ConstantExpression(' baz', 1), + 1 + ), + 1 + ), + new ConstantExpression(' baz', 1), + 1 + ), + ], + ]; + } + + public function testAttributeCallDoesNotSupportNamedArguments() + { + $this->expectException(SyntaxError::class); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ foo.bar(name="Foo") }}', 'index'))); + } + + public function testMacroCallDoesNotSupportNamedArguments() + { + $this->expectException(SyntaxError::class); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{% from _self import foo %}{% macro foo() %}{% endmacro %}{{ foo(name="Foo") }}', 'index'))); + } + + public function testMacroDefinitionDoesNotSupportNonNameVariableName() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('An argument must be a name. Unexpected token "string" of value "a" ("name" expected) in "index" at line 1.'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{% macro foo("a") %}{% endmacro %}', 'index'))); + } + + /** + * @dataProvider getMacroDefinitionDoesNotSupportNonConstantDefaultValues + */ + public function testMacroDefinitionDoesNotSupportNonConstantDefaultValues($template) + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping) in "index" at line 1'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source($template, 'index'))); + } + + public function getMacroDefinitionDoesNotSupportNonConstantDefaultValues() + { + return [ + ['{% macro foo(name = "a #{foo} a") %}{% endmacro %}'], + ['{% macro foo(name = [["b", "a #{foo} a"]]) %}{% endmacro %}'], + ]; + } + + /** + * @dataProvider getMacroDefinitionSupportsConstantDefaultValues + */ + public function testMacroDefinitionSupportsConstantDefaultValues($template) + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source($template, 'index'))); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function getMacroDefinitionSupportsConstantDefaultValues() + { + return [ + ['{% macro foo(name = "aa") %}{% endmacro %}'], + ['{% macro foo(name = 12) %}{% endmacro %}'], + ['{% macro foo(name = true) %}{% endmacro %}'], + ['{% macro foo(name = ["a"]) %}{% endmacro %}'], + ['{% macro foo(name = [["a"]]) %}{% endmacro %}'], + ['{% macro foo(name = {a: "a"}) %}{% endmacro %}'], + ['{% macro foo(name = {a: {b: "a"}}) %}{% endmacro %}'], + ]; + } + + public function testUnknownFunction() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "cycl" function. Did you mean "cycle" in "index" at line 1?'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ cycl() }}', 'index'))); + } + + public function testUnknownFunctionWithoutSuggestions() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "foobar" function in "index" at line 1.'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ foobar() }}', 'index'))); + } + + public function testUnknownFilter() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "lowe" filter. Did you mean "lower" in "index" at line 1?'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1|lowe }}', 'index'))); + } + + public function testUnknownFilterWithoutSuggestions() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "foobar" filter in "index" at line 1.'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1|foobar }}', 'index'))); + } + + public function testUnknownTest() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "nul" test. Did you mean "null" in "index" at line 1'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + $stream = $env->tokenize(new Source('{{ 1 is nul }}', 'index')); + $parser->parse($stream); + } + + public function testUnknownTestWithoutSuggestions() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "foobar" test in "index" at line 1.'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1 is foobar }}', 'index'))); + } + + public function testCompiledCodeForDynamicTest() + { + $env = new Environment(new ArrayLoader(['index' => '{{ "a" is foo_foo_bar_bar }}']), ['cache' => false, 'autoescape' => false]); + $env->addExtension(new class() extends AbstractExtension { + public function getTests() + { + return [ + new TwigTest('*_foo_*_bar', function ($foo, $bar, $a) {}), + ]; + } + }); + + $this->assertStringContainsString('$this->env->getTest(\'*_foo_*_bar\')->getCallable()("foo", "bar", "a")', $env->compile($env->parse($env->tokenize(new Source($env->getLoader()->getSourceContext('index')->getCode(), 'index'))))); + } + + public function testCompiledCodeForDynamicFunction() + { + $env = new Environment(new ArrayLoader(['index' => '{{ foo_foo_bar_bar("a") }}']), ['cache' => false, 'autoescape' => false]); + $env->addExtension(new class() extends AbstractExtension { + public function getFunctions() + { + return [ + new TwigFunction('*_foo_*_bar', function ($foo, $bar, $a) {}), + ]; + } + }); + + $this->assertStringContainsString('$this->env->getFunction(\'*_foo_*_bar\')->getCallable()("foo", "bar", "a")', $env->compile($env->parse($env->tokenize(new Source($env->getLoader()->getSourceContext('index')->getCode(), 'index'))))); + } + + public function testCompiledCodeForDynamicFilter() + { + $env = new Environment(new ArrayLoader(['index' => '{{ "a"|foo_foo_bar_bar }}']), ['cache' => false, 'autoescape' => false]); + $env->addExtension(new class() extends AbstractExtension { + public function getFilters() + { + return [ + new TwigFilter('*_foo_*_bar', function ($foo, $bar, $a) {}), + ]; + } + }); + + $this->assertStringContainsString('$this->env->getFilter(\'*_foo_*_bar\')->getCallable()("foo", "bar", "a")', $env->compile($env->parse($env->tokenize(new Source($env->getLoader()->getSourceContext('index')->getCode(), 'index'))))); + } + + public function testNotReadyFunctionWithNoConstructor() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addFunction(new TwigFunction('foo', 'foo', ['node_class' => NotReadyFunctionExpressionWithNoConstructor::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ foo() }}', 'index'))); + $this->doesNotPerformAssertions(); + } + + public function testNotReadyFilterWithNoConstructor() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addFilter(new TwigFilter('foo', 'foo', ['node_class' => NotReadyFilterExpressionWithNoConstructor::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1|foo }}', 'index'))); + $this->doesNotPerformAssertions(); + } + + public function testNotReadyTestWithNoConstructor() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addTest(new TwigTest('foo', 'foo', ['node_class' => NotReadyTestExpressionWithNoConstructor::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1 is foo }}', 'index'))); + $this->doesNotPerformAssertions(); + } + + /** + * @group legacy + */ + public function testNotReadyFunction() + { + $this->expectDeprecation('Since twig/twig 3.12: Twig node "Twig\Tests\NotReadyFunctionExpression" 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.'); + $this->expectDeprecation('Since twig/twig 3.12: Not passing an instance of "TwigFunction" when creating a "foo" function of type "Twig\Tests\NotReadyFunctionExpression" is deprecated.'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addFunction(new TwigFunction('foo', 'foo', ['node_class' => NotReadyFunctionExpression::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ foo() }}', 'index'))); + } + + /** + * @group legacy + */ + public function testNotReadyFilter() + { + $this->expectDeprecation('Since twig/twig 3.12: Twig node "Twig\Tests\NotReadyFilterExpression" 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.'); + $this->expectDeprecation('Since twig/twig 3.12: Not passing an instance of "TwigFilter" when creating a "foo" filter of type "Twig\Tests\NotReadyFilterExpression" is deprecated.'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addFilter(new TwigFilter('foo', 'foo', ['node_class' => NotReadyFilterExpression::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1|foo }}', 'index'))); + } + + /** + * @group legacy + */ + public function testNotReadyTest() + { + $this->expectDeprecation('Since twig/twig 3.12: Twig node "Twig\Tests\NotReadyTestExpression" 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.'); + $this->expectDeprecation('Since twig/twig 3.12: Not passing an instance of "TwigTest" when creating a "foo" test of type "Twig\Tests\NotReadyTestExpression" is deprecated.'); + + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addTest(new TwigTest('foo', 'foo', ['node_class' => NotReadyTestExpression::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1 is foo }}', 'index'))); + } + + public function testReadyFunction() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addFunction(new TwigFunction('foo', 'foo', ['node_class' => ReadyFunctionExpression::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ foo() }}', 'index'))); + $this->doesNotPerformAssertions(); + } + + public function testReadyFilter() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addFilter(new TwigFilter('foo', 'foo', ['node_class' => ReadyFilterExpression::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1|foo }}', 'index'))); + $this->doesNotPerformAssertions(); + } + + public function testReadyTest() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addTest(new TwigTest('foo', 'foo', ['node_class' => ReadyTestExpression::class])); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1 is foo }}', 'index'))); + $this->doesNotPerformAssertions(); + } + + public function testTwoWordTestPrecedence() + { + // a "empty element" test must have precedence over "empty" + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + $env->addTest(new TwigTest('empty element', 'foo')); + $parser = new Parser($env); + + $parser->parse($env->tokenize(new Source('{{ 1 is empty element }}', 'index'))); + $this->doesNotPerformAssertions(); + } + + private function createNameExpression(string $name, array $attributes) + { + $expression = new NameExpression($name, 1); + foreach ($attributes as $key => $value) { + $expression->setAttribute($key, $value); + } + + return $expression; + } +} + +class NotReadyFunctionExpression extends FunctionExpression +{ + public function __construct(string $function, Node $arguments, int $lineno) + { + parent::__construct($function, $arguments, $lineno); + } +} + +class NotReadyFilterExpression extends FilterExpression +{ + public function __construct(Node $node, ConstantExpression $filter, Node $arguments, int $lineno) + { + parent::__construct($node, $filter, $arguments, $lineno); + } +} + +class NotReadyTestExpression extends TestExpression +{ + public function __construct(Node $node, string $test, ?Node $arguments, int $lineno) + { + parent::__construct($node, $test, $arguments, $lineno); + } +} + +class NotReadyFunctionExpressionWithNoConstructor extends FunctionExpression +{ +} + +class NotReadyFilterExpressionWithNoConstructor extends FilterExpression +{ +} + +class NotReadyTestExpressionWithNoConstructor extends TestExpression +{ +} + +class ReadyFunctionExpression extends FunctionExpression +{ + #[FirstClassTwigCallableReady] + public function __construct(TwigFunction|string $function, Node $arguments, int $lineno) + { + parent::__construct($function, $arguments, $lineno); + } +} + +class ReadyFilterExpression extends FilterExpression +{ + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigFilter|ConstantExpression $filter, Node $arguments, int $lineno) + { + parent::__construct($node, $filter, $arguments, $lineno); + } +} + +class ReadyTestExpression extends TestExpression +{ + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigTest|string $test, ?Node $arguments, int $lineno) + { + parent::__construct($node, $test, $arguments, $lineno); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Extension/CoreTest.php b/upload/system/storage/vendor/twig/twig/tests/Extension/CoreTest.php new file mode 100644 index 000000000..314586281 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Extension/CoreTest.php @@ -0,0 +1,398 @@ +assertTrue(\in_array(CoreExtension::random('UTF-8', $value1, $value2), $expectedInArray, true)); // assertContains() would not consider the type + } + } + + public function getRandomFunctionTestData() + { + return [ + 'array' => [ + ['apple', 'orange', 'citrus'], + ['apple', 'orange', 'citrus'], + ], + 'Traversable' => [ + ['apple', 'orange', 'citrus'], + new \ArrayObject(['apple', 'orange', 'citrus']), + ], + 'unicode string' => [ + ['Ä', '€', 'é'], + 'Ä€é', + ], + 'numeric but string' => [ + ['1', '2', '3'], + '123', + ], + 'integer' => [ + range(0, 5, 1), + 5, + ], + 'float' => [ + range(0, 5, 1), + 5.9, + ], + 'negative' => [ + [0, -1, -2], + -2, + ], + 'min max int' => [ + range(50, 100), + 50, + 100, + ], + 'min max float' => [ + range(-10, 10), + -9.5, + 9.5, + ], + 'min null' => [ + range(0, 100), + null, + 100, + ], + ]; + } + + public function testRandomFunctionWithoutParameter() + { + $max = mt_getrandmax(); + + for ($i = 0; $i < 100; ++$i) { + $val = CoreExtension::random('UTF-8'); + $this->assertTrue(\is_int($val) && $val >= 0 && $val <= $max); + } + } + + public function testRandomFunctionReturnsAsIs() + { + $this->assertSame('', CoreExtension::random('UTF-8', '')); + + $instance = new \stdClass(); + $this->assertSame($instance, CoreExtension::random('UTF-8', $instance)); + } + + public function testRandomFunctionOfEmptyArrayThrowsException() + { + $this->expectException(RuntimeError::class); + CoreExtension::random('UTF-8', []); + } + + public function testRandomFunctionOnNonUTF8String() + { + $text = iconv('UTF-8', 'ISO-8859-1', 'Äé'); + for ($i = 0; $i < 30; ++$i) { + $rand = CoreExtension::random('ISO-8859-1', $text); + $this->assertTrue(\in_array(iconv('ISO-8859-1', 'UTF-8', $rand), ['Ä', 'é'], true)); + } + } + + public function testReverseFilterOnNonUTF8String() + { + $input = iconv('UTF-8', 'ISO-8859-1', 'Äé'); + $output = iconv('ISO-8859-1', 'UTF-8', CoreExtension::reverse('ISO-8859-1', $input)); + + $this->assertEquals($output, 'éÄ'); + } + + /** + * @dataProvider provideTwigFirstCases + */ + public function testTwigFirst($expected, $input) + { + $this->assertSame($expected, CoreExtension::first('UTF-8', $input)); + } + + public function provideTwigFirstCases() + { + $i = [1 => 'a', 2 => 'b', 3 => 'c']; + + return [ + ['a', 'abc'], + [1, [1, 2, 3]], + ['', null], + ['', ''], + ['a', new CoreTestIterator($i, array_keys($i), true, 3)], + ]; + } + + /** + * @dataProvider provideTwigLastCases + */ + public function testTwigLast($expected, $input) + { + $this->assertSame($expected, CoreExtension::last('UTF-8', $input)); + } + + public function provideTwigLastCases() + { + $i = [1 => 'a', 2 => 'b', 3 => 'c']; + + return [ + ['c', 'abc'], + [3, [1, 2, 3]], + ['', null], + ['', ''], + ['c', new CoreTestIterator($i, array_keys($i), true)], + ]; + } + + /** + * @dataProvider provideArrayKeyCases + */ + public function testArrayKeysFilter(array $expected, $input) + { + $this->assertSame($expected, CoreExtension::keys($input)); + } + + public function provideArrayKeyCases() + { + $array = ['a' => 'a1', 'b' => 'b1', 'c' => 'c1']; + $keys = array_keys($array); + + return [ + [$keys, $array], + [$keys, new CoreTestIterator($array, $keys)], + [$keys, new CoreTestIteratorAggregate($array, $keys)], + [$keys, new CoreTestIteratorAggregateAggregate($array, $keys)], + [[], null], + [['a'], new \SimpleXMLElement('')], + ]; + } + + /** + * @dataProvider provideInFilterCases + */ + public function testInFilter($expected, $value, $compare) + { + $this->assertSame($expected, CoreExtension::inFilter($value, $compare)); + } + + public function provideInFilterCases() + { + $array = [1, 2, 'a' => 3, 5, 6, 7]; + $keys = array_keys($array); + + return [ + [true, 1, $array], + [true, '3', $array], + [true, '3', 'abc3def'], + [true, 1, new CoreTestIterator($array, $keys, true, 1)], + [true, '3', new CoreTestIterator($array, $keys, true, 3)], + [true, '3', new CoreTestIteratorAggregateAggregate($array, $keys, true, 3)], + [false, 4, $array], + [false, 4, new CoreTestIterator($array, $keys, true)], + [false, 4, new CoreTestIteratorAggregateAggregate($array, $keys, true)], + [false, 1, 1], + [true, 'b', new \SimpleXMLElement('b')], + ]; + } + + /** + * @dataProvider provideSliceFilterCases + */ + public function testSliceFilter($expected, $input, $start, $length = null, $preserveKeys = false) + { + $this->assertSame($expected, CoreExtension::slice('UTF-8', $input, $start, $length, $preserveKeys)); + } + + public function provideSliceFilterCases() + { + $i = ['a' => 1, 'b' => 2, 'c' => 3, 'd' => 4]; + $keys = array_keys($i); + + return [ + [['a' => 1], $i, 0, 1, true], + [['a' => 1], $i, 0, 1, false], + [['b' => 2, 'c' => 3], $i, 1, 2], + [[1], [1, 2, 3, 4], 0, 1], + [[2, 3], [1, 2, 3, 4], 1, 2], + [[2, 3], new CoreTestIterator($i, $keys, true), 1, 2], + [['c' => 3, 'd' => 4], new CoreTestIteratorAggregate($i, $keys, true), 2, null, true], + [$i, new CoreTestIterator($i, $keys, true), 0, \count($keys) + 10, true], + [[], new CoreTestIterator($i, $keys, true), \count($keys) + 10], + ['de', 'abcdef', 3, 2], + [[], new \SimpleXMLElement('12'), 3], + [[], new \ArrayIterator([1, 2]), 3], + ]; + } + + /** + * @dataProvider provideCompareCases + */ + public function testCompare($expected, $a, $b) + { + $this->assertSame($expected, CoreExtension::compare($a, $b)); + $this->assertSame($expected, -CoreExtension::compare($b, $a)); + } + + public function testCompareNAN() + { + $this->assertSame(1, CoreExtension::compare(\NAN, 'NAN')); + $this->assertSame(1, CoreExtension::compare('NAN', \NAN)); + $this->assertSame(1, CoreExtension::compare(\NAN, 'foo')); + $this->assertSame(1, CoreExtension::compare('foo', \NAN)); + } + + public function provideCompareCases() + { + return [ + [0, 'a', 'a'], + + // from https://wiki.php.net/rfc/string_to_number_comparison + [0, 0, '0'], + [0, 0, '0.0'], + + [-1, 0, 'foo'], + [1, 0, ''], + [0, 42, ' 42'], + [-1, 42, '42foo'], + + [0, '0', '0'], + [0, '0', '0.0'], + [-1, '0', 'foo'], + [1, '0', ''], + [0, '42', ' 42'], + [-1, '42', '42foo'], + + [0, 42, '000042'], + [0, 42, '42.0'], + [0, 42.0, '+42.0E0'], + [0, 0, '0e214987142012'], + + [0, '42', '000042'], + [0, '42', '42.0'], + [0, '42.0', '+42.0E0'], + [0, '0', '0e214987142012'], + + [0, 42, ' 42'], + [0, 42, '42 '], + [-1, 42, '42abc'], + [-1, 42, 'abc42'], + [-1, 0, 'abc42'], + + [0, 42.0, ' 42.0'], + [0, 42.0, '42.0 '], + [-1, 42.0, '42.0abc'], + [-1, 42.0, 'abc42.0'], + [-1, 0.0, 'abc42.0'], + + [0, \INF, 'INF'], + [0, -\INF, '-INF'], + [0, \INF, '1e1000'], + [0, -\INF, '-1e1000'], + + [-1, 10, 20], + [-1, '10', 20], + [-1, 10, '20'], + + [1, 42, ' foo'], + [0, 42, "42\f"], + [1, 42, "\x00\x34\x32"], + ]; + } +} + +final class CoreTestIteratorAggregate implements \IteratorAggregate +{ + private $iterator; + + public function __construct(array $array, array $keys, $allowAccess = false, $maxPosition = false) + { + $this->iterator = new CoreTestIterator($array, $keys, $allowAccess, $maxPosition); + } + + public function getIterator(): \Traversable + { + return $this->iterator; + } +} + +final class CoreTestIteratorAggregateAggregate implements \IteratorAggregate +{ + private $iterator; + + public function __construct(array $array, array $keys, $allowValueAccess = false, $maxPosition = false) + { + $this->iterator = new CoreTestIteratorAggregate($array, $keys, $allowValueAccess, $maxPosition); + } + + public function getIterator(): \Traversable + { + return $this->iterator; + } +} + +final class CoreTestIterator implements \Iterator +{ + private $position; + private $array; + private $arrayKeys; + private $allowValueAccess; + private $maxPosition; + + public function __construct(array $values, array $keys, $allowValueAccess = false, $maxPosition = false) + { + $this->array = $values; + $this->arrayKeys = $keys; + $this->position = 0; + $this->allowValueAccess = $allowValueAccess; + $this->maxPosition = false === $maxPosition ? \count($values) + 1 : $maxPosition; + } + + public function rewind(): void + { + $this->position = 0; + } + + #[\ReturnTypeWillChange] + public function current() + { + if ($this->allowValueAccess) { + return $this->array[$this->key()]; + } + + throw new \LogicException('Code should only use the keys, not the values provided by iterator.'); + } + + #[\ReturnTypeWillChange] + public function key() + { + return $this->arrayKeys[$this->position]; + } + + public function next(): void + { + ++$this->position; + if ($this->position === $this->maxPosition) { + throw new \LogicException(\sprintf('Code should not iterate beyond %d.', $this->maxPosition)); + } + } + + public function valid(): bool + { + return isset($this->arrayKeys[$this->position]); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Extension/EscaperTest.php b/upload/system/storage/vendor/twig/twig/tests/Extension/EscaperTest.php new file mode 100644 index 000000000..4f7071840 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Extension/EscaperTest.php @@ -0,0 +1,83 @@ +getExtension(EscaperExtension::class); + $escaperExt->setEscaper('foo', 'Twig\Tests\legacy_escaper'); + $this->assertSame($expected, $twig->getRuntime(EscaperRuntime::class)->escape($string, $strategy, 'ISO-8859-1')); + } + + public function provideCustomEscaperCases() + { + return [ + ['foo**ISO-8859-1**UTF-8', 'foo', 'foo'], + ['**ISO-8859-1**UTF-8', null, 'foo'], + ['42**ISO-8859-1**UTF-8', 42, 'foo'], + ]; + } + + /** + * @dataProvider provideCustomEscaperCases + * + * @group legacy + */ + public function testCustomEscaperWithoutCallingSetEscaperRuntime($expected, $string, $strategy) + { + $twig = new Environment(new ArrayLoader()); + $escaperExt = $twig->getExtension(EscaperExtension::class); + $escaperExt->setEscaper('foo', 'Twig\Tests\legacy_escaper'); + $this->assertSame($expected, $twig->getRuntime(EscaperRuntime::class)->escape($string, $strategy, 'ISO-8859-1')); + } + + /** + * @group legacy + */ + public function testCustomEscapersOnMultipleEnvs() + { + $env1 = new Environment(new ArrayLoader()); + $escaperExt1 = $env1->getExtension(EscaperExtension::class); + $escaperExt1->setEscaper('foo', 'Twig\Tests\legacy_escaper'); + + $env2 = new Environment(new ArrayLoader()); + $escaperExt2 = $env2->getExtension(EscaperExtension::class); + $escaperExt2->setEscaper('foo', 'Twig\Tests\legacy_escaper_again'); + + $this->assertSame('foo**ISO-8859-1**UTF-8', $env1->getRuntime(EscaperRuntime::class)->escape('foo', 'foo', 'ISO-8859-1')); + $this->assertSame('foo**ISO-8859-1**UTF-8**again', $env2->getRuntime(EscaperRuntime::class)->escape('foo', 'foo', 'ISO-8859-1')); + } +} + +function legacy_escaper(Environment $twig, $string, $charset) +{ + return $string.'**'.$charset.'**'.$twig->getCharset(); +} + +function legacy_escaper_again(Environment $twig, $string, $charset) +{ + return $string.'**'.$charset.'**'.$twig->getCharset().'**again'; +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Extension/LegacyDebugFunctionsTest.php b/upload/system/storage/vendor/twig/twig/tests/Extension/LegacyDebugFunctionsTest.php new file mode 100644 index 000000000..4cab76200 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Extension/LegacyDebugFunctionsTest.php @@ -0,0 +1,30 @@ +assertSame(DebugExtension::dump($env, 'Foo'), twig_var_dump($env, 'Foo')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Extension/LegacyStringLoaderFunctionsTest.php b/upload/system/storage/vendor/twig/twig/tests/Extension/LegacyStringLoaderFunctionsTest.php new file mode 100644 index 000000000..a6cb31df0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Extension/LegacyStringLoaderFunctionsTest.php @@ -0,0 +1,30 @@ +assertSame(StringLoaderExtension::templateFromString($env, 'Foo')->render(), twig_template_from_string($env, 'Foo')->render()); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Extension/SandboxTest.php b/upload/system/storage/vendor/twig/twig/tests/Extension/SandboxTest.php new file mode 100644 index 000000000..d193e7ef9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Extension/SandboxTest.php @@ -0,0 +1,598 @@ + 'Fabien', + 'obj' => new FooObject(), + 'arr' => ['obj' => new FooObject()], + 'child_obj' => new ChildClass(), + ]; + + self::$templates = [ + '1_basic1' => '{{ obj.foo }}', + '1_basic2' => '{{ name|upper }}', + '1_basic3' => '{% if name %}foo{% endif %}', + '1_basic4' => '{{ obj.bar }}', + '1_basic5' => '{{ obj }}', + '1_basic7' => '{{ cycle(["foo","bar"], 1) }}', + '1_basic8' => '{{ obj.getfoobar }}{{ obj.getFooBar }}', + '1_basic9' => '{{ obj.foobar }}{{ obj.fooBar }}', + '1_basic' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}', + '1_layout' => '{% block content %}{% endblock %}', + '1_child' => "{% extends \"1_layout\" %}\n{% block content %}\n{{ \"a\"|json_encode }}\n{% endblock %}", + '1_include' => '{{ include("1_basic1", sandboxed=true) }}', + '1_basic2_include_template_from_string_sandboxed' => '{{ include(template_from_string("{{ name|upper }}"), sandboxed=true) }}', + '1_basic2_include_template_from_string' => '{{ include(template_from_string("{{ name|upper }}")) }}', + '1_range_operator' => '{{ (1..2)[0] }}', + '1_syntax_error_wrapper' => '{% sandbox %}{% include "1_syntax_error" %}{% endsandbox %}', + '1_syntax_error' => '{% syntax error }}', + '1_childobj_parentmethod' => '{{ child_obj.ParentMethod() }}', + '1_childobj_childmethod' => '{{ child_obj.ChildMethod() }}', + '1_empty' => '', + ]; + } + + /** + * @dataProvider getSandboxedForCoreTagsTests + */ + public function testSandboxForCoreTags(string $tag, string $template) + { + $this->expectException(SecurityError::class); + $this->expectExceptionMessageMatches(sprintf('/Tag "%s" is not allowed in "index \(string template .+?\)" at line 1/', $tag)); + + $twig = $this->getEnvironment(true, [], self::$templates, []); + $twig->createTemplate($template, 'index')->render([]); + } + + public function getSandboxedForCoreTagsTests() + { + yield ['apply', '{% apply upper %}foo{% endapply %}']; + yield ['autoescape', '{% autoescape %}foo{% endautoescape %}']; + yield ['block', '{% block foo %}foo{% endblock %}']; + yield ['deprecated', '{% deprecated "message" %}']; + yield ['do', '{% do 1 + 2 %}']; + yield ['embed', '{% embed "base.twig" %}{% endembed %}']; + // To be uncommented in 4.0 + //yield ['extends', '{% extends "base.twig" %}']; + yield ['flush', '{% flush %}']; + yield ['for', '{% for i in 1..2 %}{% endfor %}']; + yield ['from', '{% from "macros" import foo %}']; + yield ['if', '{% if false %}{% endif %}']; + yield ['import', '{% import "macros" as macros %}']; + yield ['include', '{% include "macros" %}']; + yield ['macro', '{% macro foo() %}{% endmacro %}']; + yield ['sandbox', '{% sandbox %}{% endsandbox %}']; + yield ['set', '{% set foo = 1 %}']; + // To be uncommented in 4.0 + //yield ['use', '{% use "1_empty" %}']; + yield ['with', '{% with foo %}{% endwith %}']; + } + + /** + * @dataProvider getSandboxedForExtendsAndUseTagsTests + * + * @group legacy + */ + public function testSandboxForExtendsAndUseTags(string $tag, string $template) + { + $this->expectDeprecation(sprintf('Since twig/twig 3.12: The "%s" tag is always allowed in sandboxes, but won\'t be in 4.0, please enable it explicitly in your sandbox policy if needed.', $tag)); + + $twig = $this->getEnvironment(true, [], self::$templates, []); + $twig->createTemplate($template, 'index')->render([]); + } + + public function getSandboxedForExtendsAndUseTagsTests() + { + yield ['extends', '{% extends "1_empty" %}']; + yield ['use', '{% use "1_empty" %}']; + } + + public function testSandboxWithInheritance() + { + $this->expectException(SecurityError::class); + $this->expectExceptionMessage('Filter "json_encode" is not allowed in "1_child" at line 3.'); + + $twig = $this->getEnvironment(true, [], self::$templates, ['extends', 'block']); + $twig->load('1_child')->render([]); + } + + public function testSandboxGloballySet() + { + $twig = $this->getEnvironment(false, [], self::$templates); + $this->assertEquals('FOO', $twig->load('1_basic')->render(self::$params), 'Sandbox does nothing if it is disabled globally'); + } + + public function testSandboxUnallowedMethodAccessor() + { + $twig = $this->getEnvironment(true, [], self::$templates); + try { + $twig->load('1_basic1')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed method is called'); + } catch (SecurityNotAllowedMethodError $e) { + $this->assertEquals('Twig\Tests\Extension\FooObject', $e->getClassName(), 'Exception should be raised on the "Twig\Tests\Extension\FooObject" class'); + $this->assertEquals('foo', $e->getMethodName(), 'Exception should be raised on the "foo" method'); + } + } + + public function testIfSandBoxIsDisabledAfterSyntaxError() + { + $twig = $this->getEnvironment(false, [], self::$templates); + try { + $twig->load('1_syntax_error_wrapper')->render(self::$params); + } catch (SyntaxError $e) { + /** @var SandboxExtension $sandbox */ + $sandbox = $twig->getExtension(SandboxExtension::class); + $this->assertFalse($sandbox->isSandboxed()); + } + } + + public function testSandboxGloballyFalseUnallowedFilterWithIncludeTemplateFromStringSandboxed() + { + $twig = $this->getEnvironment(false, [], self::$templates); + $twig->addExtension(new StringLoaderExtension()); + try { + $twig->load('1_basic2_include_template_from_string_sandboxed')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called'); + } catch (SecurityNotAllowedFilterError $e) { + $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter'); + } + } + + public function testSandboxGloballyTrueUnallowedFilterWithIncludeTemplateFromStringSandboxed() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], [], [], ['include', 'template_from_string']); + $twig->addExtension(new StringLoaderExtension()); + try { + $twig->load('1_basic2_include_template_from_string_sandboxed')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called'); + } catch (SecurityNotAllowedFilterError $e) { + $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter'); + } + } + + public function testSandboxGloballyFalseUnallowedFilterWithIncludeTemplateFromStringNotSandboxed() + { + $twig = $this->getEnvironment(false, [], self::$templates); + $twig->addExtension(new StringLoaderExtension()); + $this->assertSame('FABIEN', $twig->load('1_basic2_include_template_from_string')->render(self::$params)); + } + + public function testSandboxGloballyTrueUnallowedFilterWithIncludeTemplateFromStringNotSandboxed() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], [], [], ['include', 'template_from_string']); + $twig->addExtension(new StringLoaderExtension()); + try { + $twig->load('1_basic2_include_template_from_string')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called'); + } catch (SecurityNotAllowedFilterError $e) { + $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter'); + } + } + + public function testSandboxUnallowedFilter() + { + $twig = $this->getEnvironment(true, [], self::$templates); + try { + $twig->load('1_basic2')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed filter is called'); + } catch (SecurityNotAllowedFilterError $e) { + $this->assertEquals('upper', $e->getFilterName(), 'Exception should be raised on the "upper" filter'); + } + } + + public function testSandboxUnallowedTag() + { + $twig = $this->getEnvironment(true, [], self::$templates); + try { + $twig->load('1_basic3')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed tag is used in the template'); + } catch (SecurityNotAllowedTagError $e) { + $this->assertEquals('if', $e->getTagName(), 'Exception should be raised on the "if" tag'); + } + } + + public function testSandboxUnallowedProperty() + { + $twig = $this->getEnvironment(true, [], self::$templates); + try { + $twig->load('1_basic4')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed property is called in the template'); + } catch (SecurityNotAllowedPropertyError $e) { + $this->assertEquals('Twig\Tests\Extension\FooObject', $e->getClassName(), 'Exception should be raised on the "Twig\Tests\Extension\FooObject" class'); + $this->assertEquals('bar', $e->getPropertyName(), 'Exception should be raised on the "bar" property'); + } + } + + /** + * @dataProvider getSandboxUnallowedToStringTests + */ + public function testSandboxUnallowedToString($template) + { + $twig = $this->getEnvironment(true, [], ['index' => $template], [], ['upper'], ['Twig\Tests\Extension\FooObject' => 'getAnotherFooObject'], [], ['random']); + try { + $twig->load('index')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed method (__toString()) is called in the template'); + } catch (SecurityNotAllowedMethodError $e) { + $this->assertEquals('Twig\Tests\Extension\FooObject', $e->getClassName(), 'Exception should be raised on the "Twig\Tests\Extension\FooObject" class'); + $this->assertEquals('__tostring', $e->getMethodName(), 'Exception should be raised on the "__toString" method'); + } + } + + public function getSandboxUnallowedToStringTests() + { + return [ + 'simple' => ['{{ obj }}'], + 'object_from_array' => ['{{ arr.obj }}'], + 'object_chain' => ['{{ obj.anotherFooObject }}'], + 'filter' => ['{{ obj|upper }}'], + 'filter_from_array' => ['{{ arr.obj|upper }}'], + 'function' => ['{{ random(obj) }}'], + 'function_from_array' => ['{{ random(arr.obj) }}'], + 'function_and_filter' => ['{{ random(obj|upper) }}'], + 'function_and_filter_from_array' => ['{{ random(arr.obj|upper) }}'], + 'object_chain_and_filter' => ['{{ obj.anotherFooObject|upper }}'], + 'object_chain_and_function' => ['{{ random(obj.anotherFooObject) }}'], + 'concat' => ['{{ obj ~ "" }}'], + 'concat_again' => ['{{ "" ~ obj }}'], + ]; + } + + /** + * @dataProvider getSandboxAllowedToStringTests + */ + public function testSandboxAllowedToString($template, $output) + { + $twig = $this->getEnvironment(true, [], ['index' => $template], ['set'], [], ['Twig\Tests\Extension\FooObject' => ['foo', 'getAnotherFooObject']]); + $this->assertEquals($output, $twig->load('index')->render(self::$params)); + } + + public function getSandboxAllowedToStringTests() + { + return [ + 'constant_test' => ['{{ obj is constant("PHP_INT_MAX") }}', ''], + 'set_object' => ['{% set a = obj.anotherFooObject %}{{ a.foo }}', 'foo'], + 'is_defined' => ['{{ obj.anotherFooObject is defined }}', '1'], + 'is_null' => ['{{ obj is null }}', ''], + 'is_sameas' => ['{{ obj is same as(obj) }}', '1'], + 'is_sameas_no_brackets' => ['{{ obj is same as obj }}', '1'], + 'is_sameas_from_array' => ['{{ arr.obj is same as(arr.obj) }}', '1'], + 'is_sameas_from_array_no_brackets' => ['{{ arr.obj is same as arr.obj }}', '1'], + 'is_sameas_from_another_method' => ['{{ obj.anotherFooObject is same as(obj.anotherFooObject) }}', ''], + 'is_sameas_from_another_method_no_brackets' => ['{{ obj.anotherFooObject is same as obj.anotherFooObject }}', ''], + ]; + } + + public function testSandboxAllowMethodToString() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], ['Twig\Tests\Extension\FooObject' => '__toString']); + FooObject::reset(); + $this->assertEquals('foo', $twig->load('1_basic5')->render(self::$params), 'Sandbox allow some methods'); + $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once'); + } + + public function testSandboxAllowMethodToStringDisabled() + { + $twig = $this->getEnvironment(false, [], self::$templates); + FooObject::reset(); + $this->assertEquals('foo', $twig->load('1_basic5')->render(self::$params), 'Sandbox allows __toString when sandbox disabled'); + $this->assertEquals(1, FooObject::$called['__toString'], 'Sandbox only calls method once'); + } + + public function testSandboxUnallowedFunction() + { + $twig = $this->getEnvironment(true, [], self::$templates); + try { + $twig->load('1_basic7')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if an unallowed function is called in the template'); + } catch (SecurityNotAllowedFunctionError $e) { + $this->assertEquals('cycle', $e->getFunctionName(), 'Exception should be raised on the "cycle" function'); + } + } + + public function testSandboxUnallowedRangeOperator() + { + $twig = $this->getEnvironment(true, [], self::$templates); + try { + $twig->load('1_range_operator')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception if the unallowed range operator is called'); + } catch (SecurityNotAllowedFunctionError $e) { + $this->assertEquals('range', $e->getFunctionName(), 'Exception should be raised on the "range" function'); + } + } + + public function testSandboxAllowMethodFoo() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], ['Twig\Tests\Extension\FooObject' => 'foo']); + FooObject::reset(); + $this->assertEquals('foo', $twig->load('1_basic1')->render(self::$params), 'Sandbox allow some methods'); + $this->assertEquals(1, FooObject::$called['foo'], 'Sandbox only calls method once'); + } + + public function testSandboxAllowFilter() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], ['upper']); + $this->assertEquals('FABIEN', $twig->load('1_basic2')->render(self::$params), 'Sandbox allow some filters'); + } + + public function testSandboxAllowTag() + { + $twig = $this->getEnvironment(true, [], self::$templates, ['if']); + $this->assertEquals('foo', $twig->load('1_basic3')->render(self::$params), 'Sandbox allow some tags'); + } + + public function testSandboxAllowProperty() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], [], ['Twig\Tests\Extension\FooObject' => 'bar']); + $this->assertEquals('bar', $twig->load('1_basic4')->render(self::$params), 'Sandbox allow some properties'); + } + + public function testSandboxAllowFunction() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], [], [], ['cycle']); + $this->assertEquals('bar', $twig->load('1_basic7')->render(self::$params), 'Sandbox allow some functions'); + } + + public function testSandboxAllowRangeOperator() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], [], [], ['range']); + $this->assertEquals('1', $twig->load('1_range_operator')->render(self::$params), 'Sandbox allow the range operator'); + } + + public function testSandboxAllowMethodsCaseInsensitive() + { + foreach (['getfoobar', 'getFoobar', 'getFooBar'] as $name) { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], ['Twig\Tests\Extension\FooObject' => $name]); + FooObject::reset(); + $this->assertEquals('foobarfoobar', $twig->load('1_basic8')->render(self::$params), 'Sandbox allow methods in a case-insensitive way'); + $this->assertEquals(2, FooObject::$called['getFooBar'], 'Sandbox only calls method once'); + + $this->assertEquals('foobarfoobar', $twig->load('1_basic9')->render(self::$params), 'Sandbox allow methods via shortcut names (ie. without get/set)'); + } + } + + public function testSandboxLocallySetForAnInclude() + { + self::$templates = [ + '2_basic' => '{{ obj.foo }}{% include "2_included" %}{{ obj.foo }}', + '2_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}', + ]; + + $twig = $this->getEnvironment(false, [], self::$templates); + $this->assertEquals('fooFOOfoo', $twig->load('2_basic')->render(self::$params), 'Sandbox does nothing if disabled globally and sandboxed not used for the include'); + + self::$templates = [ + '3_basic' => '{{ obj.foo }}{% sandbox %}{% include "3_included" %}{% endsandbox %}{{ obj.foo }}', + '3_included' => '{% if obj.foo %}{{ obj.foo|upper }}{% endif %}', + ]; + + $twig = $this->getEnvironment(true, [], self::$templates); + try { + $twig->load('3_basic')->render(self::$params); + $this->fail('Sandbox throws a SecurityError exception when the included file is sandboxed'); + } catch (SecurityNotAllowedTagError $e) { + $this->assertEquals('sandbox', $e->getTagName()); + } + } + + public function testMacrosInASandbox() + { + $twig = $this->getEnvironment(true, ['autoescape' => 'html'], ['index' => <<{{ text }}

    {% endmacro %} + +{{- macros.test('username') }} +EOF + ], ['macro', 'import'], ['escape']); + + $this->assertEquals('

    username

    ', $twig->load('index')->render([])); + } + + public function testSandboxDisabledAfterIncludeFunctionError() + { + $twig = $this->getEnvironment(false, [], self::$templates); + + $e = null; + try { + $twig->load('1_include')->render(self::$params); + } catch (\Throwable $e) { + } + if (null === $e) { + $this->fail('An exception should be thrown for this test to be valid.'); + } + + $this->assertFalse($twig->getExtension(SandboxExtension::class)->isSandboxed(), 'Sandboxed include() function call should not leave Sandbox enabled when an error occurs.'); + } + + public function testSandboxWithNoClosureFilter() + { + $this->expectException('\Twig\Error\RuntimeError'); + $this->expectExceptionMessage('The callable passed to the "filter" filter must be a Closure in sandbox mode in "index" at line 1.'); + + $twig = $this->getEnvironment(true, ['autoescape' => 'html'], ['index' => <<load('index')->render([]); + } + + public function testSandboxWithClosureFilter() + { + $twig = $this->getEnvironment(true, ['autoescape' => 'html'], ['index' => << v != "")|join(", ") }} +EOF + ], [], ['escape', 'filter', 'join']); + + $this->assertSame('foo, bar', $twig->load('index')->render([])); + } + + public function testMultipleClassMatchesViaInheritanceInAllowedMethods() + { + $twig_child_first = $this->getEnvironment(true, [], self::$templates, [], [], [ + 'Twig\Tests\Extension\ChildClass' => ['ChildMethod'], + 'Twig\Tests\Extension\ParentClass' => ['ParentMethod'], + ]); + $twig_parent_first = $this->getEnvironment(true, [], self::$templates, [], [], [ + 'Twig\Tests\Extension\ParentClass' => ['ParentMethod'], + 'Twig\Tests\Extension\ChildClass' => ['ChildMethod'], + ]); + + try { + $twig_child_first->load('1_childobj_childmethod')->render(self::$params); + } catch (SecurityError $e) { + $this->fail('This test case is malfunctioning as even the child class method which comes first is not being allowed.'); + } + + try { + $twig_parent_first->load('1_childobj_parentmethod')->render(self::$params); + } catch (SecurityError $e) { + $this->fail('This test case is malfunctioning as even the parent class method which comes first is not being allowed.'); + } + + try { + $twig_parent_first->load('1_childobj_childmethod')->render(self::$params); + } catch (SecurityError $e) { + $this->fail('checkMethodAllowed is exiting prematurely after matching a parent class and not seeing a method allowed on a child class later in the list'); + } + + try { + $twig_child_first->load('1_childobj_parentmethod')->render(self::$params); + } catch (SecurityError $e) { + $this->fail('checkMethodAllowed is exiting prematurely after matching a child class and not seeing a method allowed on its parent class later in the list'); + } + + $this->expectNotToPerformAssertions(); + } + + protected function getEnvironment($sandboxed, $options, $templates, $tags = [], $filters = [], $methods = [], $properties = [], $functions = [], $sourcePolicy = null) + { + $loader = new ArrayLoader($templates); + $twig = new Environment($loader, array_merge(['debug' => true, 'cache' => false, 'autoescape' => false], $options)); + $policy = new SecurityPolicy($tags, $filters, $methods, $properties, $functions); + $twig->addExtension(new SandboxExtension($policy, $sandboxed, $sourcePolicy)); + + return $twig; + } + + public function testSandboxSourcePolicyEnableReturningFalse() + { + $twig = $this->getEnvironment(false, [], self::$templates, [], [], [], [], [], new class() implements \Twig\Sandbox\SourcePolicyInterface { + public function enableSandbox(Source $source): bool + { + return '1_basic' != $source->getName(); + } + }); + $this->assertEquals('FOO', $twig->load('1_basic')->render(self::$params)); + } + + public function testSandboxSourcePolicyEnableReturningTrue() + { + $twig = $this->getEnvironment(false, [], self::$templates, [], [], [], [], [], new class() implements \Twig\Sandbox\SourcePolicyInterface { + public function enableSandbox(Source $source): bool + { + return '1_basic' === $source->getName(); + } + }); + $this->expectException(SecurityError::class); + $twig->load('1_basic')->render([]); + } + + public function testSandboxSourcePolicyFalseDoesntOverrideOtherEnables() + { + $twig = $this->getEnvironment(true, [], self::$templates, [], [], [], [], [], new class() implements \Twig\Sandbox\SourcePolicyInterface { + public function enableSandbox(Source $source): bool + { + return false; + } + }); + $this->expectException(SecurityError::class); + $twig->load('1_basic')->render([]); + } +} + +class ParentClass +{ + public function ParentMethod() + { + } +} +class ChildClass extends ParentClass +{ + public function ChildMethod() + { + } +} + +class FooObject +{ + public static $called = ['__toString' => 0, 'foo' => 0, 'getFooBar' => 0]; + + public $bar = 'bar'; + + public static function reset() + { + self::$called = ['__toString' => 0, 'foo' => 0, 'getFooBar' => 0]; + } + + public function __toString() + { + ++self::$called['__toString']; + + return 'foo'; + } + + public function foo() + { + ++self::$called['foo']; + + return 'foo'; + } + + public function getFooBar() + { + ++self::$called['getFooBar']; + + return 'foobar'; + } + + public function getAnotherFooObject() + { + return new self(); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Extension/StringLoaderExtensionTest.php b/upload/system/storage/vendor/twig/twig/tests/Extension/StringLoaderExtensionTest.php new file mode 100644 index 000000000..d37b8f263 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Extension/StringLoaderExtensionTest.php @@ -0,0 +1,28 @@ +addExtension(new StringLoaderExtension()); + $this->assertSame('something', CoreExtension::include($twig, [], StringLoaderExtension::templateFromString($twig, 'something'))); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/FactoryRuntimeLoaderTest.php b/upload/system/storage/vendor/twig/twig/tests/FactoryRuntimeLoaderTest.php new file mode 100644 index 000000000..35c2d5baf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/FactoryRuntimeLoaderTest.php @@ -0,0 +1,37 @@ + '\Twig\Tests\getRuntime']); + + $this->assertInstanceOf('stdClass', $loader->load('stdClass')); + } + + public function testLoadReturnsNullForUnmappedRuntime() + { + $loader = new FactoryRuntimeLoader(); + + $this->assertNull($loader->load('stdClass')); + } +} + +function getRuntime() +{ + return new \stdClass(); +} diff --git a/upload/system/storage/vendor/twig/twig/tests/FileExtensionEscapingStrategyTest.php b/upload/system/storage/vendor/twig/twig/tests/FileExtensionEscapingStrategyTest.php new file mode 100644 index 000000000..883aa882c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/FileExtensionEscapingStrategyTest.php @@ -0,0 +1,56 @@ +assertSame($strategy, FileExtensionEscapingStrategy::guess($filename)); + } + + public function getGuessData() + { + return [ + // default + ['html', 'foo.html'], + ['html', 'foo.html.twig'], + ['html', 'foo'], + ['html', 'foo.bar.twig'], + ['html', 'foo.txt/foo'], + ['html', 'foo.txt/foo.js/'], + + // css + ['css', 'foo.css'], + ['css', 'foo.css.twig'], + ['css', 'foo.twig.css'], + ['css', 'foo.js.css'], + ['css', 'foo.js.css.twig'], + + // js + ['js', 'foo.js'], + ['js', 'foo.js.twig'], + ['js', 'foo.txt/foo.js'], + ['js', 'foo.txt.twig/foo.js'], + + // txt + [false, 'foo.txt'], + [false, 'foo.txt.twig'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/FilesystemHelper.php b/upload/system/storage/vendor/twig/twig/tests/FilesystemHelper.php new file mode 100644 index 000000000..4920d5420 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/FilesystemHelper.php @@ -0,0 +1,28 @@ + $fileInfo) { + if ($fileInfo->isDir()) { + rmdir($filename); + } else { + unlink($filename); + } + } + rmdir($dir); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/autoescape/block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/autoescape/block.test new file mode 100644 index 000000000..a80b80c37 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/autoescape/block.test @@ -0,0 +1,21 @@ +--TEST-- +blocks and autoescape +--TEMPLATE-- +{{ include('unrelated.txt.twig') -}} +{{ include('template.html.twig') -}} +--TEMPLATE(unrelated.txt.twig)-- +{% block content %}{% endblock %} +--TEMPLATE(template.html.twig)-- +{% extends 'parent.html.twig' %} +{% block content %} +{{ br -}} +{% endblock %} +--TEMPLATE(parent.html.twig)-- +{% set _content = block('content')|raw %} +{{ _content|raw }} +--DATA-- +return ['br' => '
    '] +--CONFIG-- +return ['autoescape' => 'name'] +--EXPECT-- +<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/autoescape/name.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/autoescape/name.test new file mode 100644 index 000000000..5ad573cf0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/autoescape/name.test @@ -0,0 +1,22 @@ +--TEST-- +"name" autoescape strategy +--TEMPLATE-- +{{ br -}} +{{ include('index.js.twig') -}} +{{ include('index.html.twig') -}} +{{ include('index.txt.twig') -}} +--TEMPLATE(index.js.twig)-- +{{ br -}} +--TEMPLATE(index.html.twig)-- +{{ br -}} +--TEMPLATE(index.txt.twig)-- +{{ br -}} +--DATA-- +return ['br' => '
    '] +--CONFIG-- +return ['autoescape' => 'name'] +--EXPECT-- +<br /> +\u003Cbr\u0020\/\u003E +<br /> +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/errors/base.html b/upload/system/storage/vendor/twig/twig/tests/Fixtures/errors/base.html new file mode 100644 index 000000000..cb0dbe444 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/errors/base.html @@ -0,0 +1 @@ +{% block content %}{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/errors/index.html b/upload/system/storage/vendor/twig/twig/tests/Fixtures/errors/index.html new file mode 100644 index 000000000..df57c822f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/errors/index.html @@ -0,0 +1,7 @@ +{% extends 'base.html' %} +{% block content %} + {{ foo.bar }} +{% endblock %} +{% block foo %} + {{ foo.bar }} +{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/child_contents_outside_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/child_contents_outside_blocks.test new file mode 100644 index 000000000..74a1cc283 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/child_contents_outside_blocks.test @@ -0,0 +1,15 @@ +--TEST-- +Exception for child templates defining content outside blocks defined by parent +--TEMPLATE-- +{% extends 'base.twig' %} + +Content outside a block. + +{% block sidebar %} + Content inside a block. +{% endblock %} +--TEMPLATE(base.twig)-- +{% block sidebar %} +{% endblock %} +--EXCEPTION-- +Twig\Error\SyntaxError: A template that extends another one cannot include content outside Twig blocks. Did you forget to put the content inside a {% block %} tag in "index.twig" at line 3? diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/exception_in_extension_extends.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/exception_in_extension_extends.test new file mode 100644 index 000000000..fee521878 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/exception_in_extension_extends.test @@ -0,0 +1,12 @@ +--TEST-- +Exception thrown from a child for an extension error +--TEMPLATE-- +{% extends 'base.twig' %} +--TEMPLATE(base.twig)-- + + +{{ random([]) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: The random function cannot pick from an empty sequence/mapping in "base.twig" at line 4. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/exception_in_extension_include.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/exception_in_extension_include.test new file mode 100644 index 000000000..ab09cabb7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/exception_in_extension_include.test @@ -0,0 +1,12 @@ +--TEST-- +Exception thrown from an include for an extension error +--TEMPLATE-- +{% include 'content.twig' %} +--TEMPLATE(content.twig)-- + + +{{ random([]) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: The random function cannot pick from an empty sequence/mapping in "content.twig" at line 4. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test new file mode 100644 index 000000000..66784292c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_array_with_undefined_variable.test @@ -0,0 +1,18 @@ +--TEST-- +Exception for multiline array with undefined variable +--TEMPLATE-- +{% set foo = { + foo: 'foo', + bar: 'bar', + + + foobar: foobar, + + + + foo2: foo2, +} %} +--DATA-- +return ['foobar' => 'foobar'] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "foo2" does not exist in "index.twig" at line 11. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test new file mode 100644 index 000000000..7f5e96b04 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_array_with_undefined_variable_again.test @@ -0,0 +1,18 @@ +--TEST-- +Exception for multiline array with undefined variable +--TEMPLATE-- +{% set foo = { + foo: 'foo', + bar: 'bar', + + + foobar: foobar, + + + + foo2: foo2, +} %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "foobar" does not exist in "index.twig" at line 7. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test new file mode 100644 index 000000000..8bc524ef1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_function_with_undefined_variable.test @@ -0,0 +1,12 @@ +--TEST-- +Exception for multile function with undefined variable +--TEMPLATE-- +{{ include('foo', + with_context=with_context +) }} +--TEMPLATE(foo)-- +Foo +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "with_context" does not exist in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_function_with_unknown_argument.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_function_with_unknown_argument.test new file mode 100644 index 000000000..5f413d406 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_function_with_unknown_argument.test @@ -0,0 +1,9 @@ +--TEST-- +Exception for multiline function with unknown argument +--TEMPLATE-- +{{ include('foo', + with_context=True, + invalid=False +) }} +--EXCEPTION-- +Twig\Error\SyntaxError: Unknown argument "invalid" for function "include(template, variables, with_context, ignore_missing, sandboxed)" in "index.twig" at line 4. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test new file mode 100644 index 000000000..032ad831e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/multiline_tag_with_undefined_variable.test @@ -0,0 +1,12 @@ +--TEST-- +Exception for multiline tag with undefined variable +--TEMPLATE-- +{% include 'foo' + with vars +%} +--TEMPLATE(foo)-- +Foo +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "vars" does not exist in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/strict_comparison_operator.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/strict_comparison_operator.test new file mode 100644 index 000000000..e14beb672 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/strict_comparison_operator.test @@ -0,0 +1,6 @@ +--TEST-- +The PHP === strict comparison operator is not supported +--TEMPLATE-- +{{ 1 === 2 }} +--EXCEPTION-- +Twig\Error\SyntaxError: Unexpected operator of value "=". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/syntax_error_in_reused_template.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/syntax_error_in_reused_template.test new file mode 100644 index 000000000..4ce06854f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/syntax_error_in_reused_template.test @@ -0,0 +1,10 @@ +--TEST-- +Exception for syntax error in reused template +--TEMPLATE-- +{% use 'foo.twig' %} +--TEMPLATE(foo.twig)-- +{% block bar %} + {% do node.data = 5 %} +{% endblock %} +--EXCEPTION-- +Twig\Error\SyntaxError: Unexpected token "operator" of value "=" ("end of statement block" expected) in "foo.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/unclosed_tag.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/unclosed_tag.test new file mode 100644 index 000000000..b4fc16916 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/unclosed_tag.test @@ -0,0 +1,20 @@ +--TEST-- +Exception for an unclosed tag +--TEMPLATE-- +{% block foo %} + {% if foo %} + + + + + {% for i in fo %} + + + + {% endfor %} + + + +{% endblock %} +--EXCEPTION-- +Twig\Error\SyntaxError: Unexpected "endblock" tag (expecting closing tag for the "if" tag defined near line 4) in "index.twig" at line 16. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_parent.test new file mode 100644 index 000000000..07f855a3f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_parent.test @@ -0,0 +1,10 @@ +--TEST-- +Exception for an undefined parent +--TEMPLATE-- +{% extends 'foo.html' %} + +{% set foo = "foo" %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "foo.html" is not defined in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_template_in_child_template.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_template_in_child_template.test new file mode 100644 index 000000000..904faa50a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_template_in_child_template.test @@ -0,0 +1,15 @@ +--TEST-- +Exception for an undefined template in a child template +--TEMPLATE-- +{% extends 'base.twig' %} + +{% block sidebar %} + {{ include('include.twig') }} +{% endblock %} +--TEMPLATE(base.twig)-- +{% block sidebar %} +{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "include.twig" is not defined in "index.twig" at line 5. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_trait.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_trait.test new file mode 100644 index 000000000..5bfdd07e6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/exceptions/undefined_trait.test @@ -0,0 +1,9 @@ +--TEST-- +Exception for an undefined trait +--TEMPLATE-- +{% use 'foo' with foobar as bar %} +--TEMPLATE(foo)-- +{% block bar %} +{% endblock %} +--EXCEPTION-- +Twig\Error\RuntimeError: Block "foobar" is not defined in trait "foo" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/_self.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/_self.test new file mode 100644 index 000000000..fb88f4c85 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/_self.test @@ -0,0 +1,8 @@ +--TEST-- +_self returns the template name +--TEMPLATE-- +{{ _self }} +--DATA-- +return [] +--EXPECT-- +index.twig diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/array.test new file mode 100644 index 000000000..35579dc13 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/array.test @@ -0,0 +1,105 @@ +--TEST-- +Twig supports array notation +--TEMPLATE-- +{# empty array #} +{{ []|join(',') }} + +{{ [1, 2]|join(',') }} +{{ ['foo', "bar"]|join(',') }} +{{ {0: 1, 'foo': 'bar'}|join(',') }} +{{ {0: 1, 'foo': 'bar'}|keys|join(',') }} + +{{ {0: 1, foo: 'bar'}|join(',') }} +{{ {0: 1, foo: 'bar'}|keys|join(',') }} + +{# nested arrays #} +{% set a = [1, 2, [1, 2], {'foo': {'foo': 'bar'}}] %} +{{ a[2]|join(',') }} +{{ a[3]["foo"]|join(',') }} + +{# works even if [] is used inside the array #} +{{ [foo[bar]]|join(',') }} + +{# elements can be any expression #} +{{ ['foo'|upper, bar|upper, bar == foo]|join(',') }} + +{# arrays can have a trailing , like in PHP #} +{{ + [ + 1, + 2, + ]|join(',') +}} + +{# keys can be any expression #} +{% set a = 1 %} +{% set b = "foo" %} +{% set ary = { (a): 'a', (b): 'b', 'c': 'c', (a ~ b): 'd' } %} +{{ ary|keys|join(',') }} +{{ ary|join(',') }} + +{# ArrayAccess #} +{{ array_access['a'] }} + +{# array that does not exist #} +{{ does_not_exist[0]|default('ok') }} +{{ does_not_exist[0].does_not_exist_either|default('ok') }} +{{ does_not_exist[0]['does_not_exist_either']|default('ok') }} +--DATA-- +return ['bar' => 'bar', 'foo' => ['bar' => 'bar'], 'array_access' => new \ArrayObject(['a' => 'b'])] +--EXPECT-- +1,2 +foo,bar +1,bar +0,foo + +1,bar +0,foo + +1,2 +bar + +bar + +FOO,BAR, + +1,2 + +1,foo,c,1foo +a,b,c,d + +b + +ok +ok +ok +--DATA-- +return ['bar' => 'bar', 'foo' => ['bar' => 'bar'], 'array_access' => new \ArrayObject(['a' => 'b'])] +--CONFIG-- +return ['strict_variables' => false] +--EXPECT-- +1,2 +foo,bar +1,bar +0,foo + +1,bar +0,foo + +1,2 +bar + +bar + +FOO,BAR, + +1,2 + +1,foo,c,1foo +a,b,c,d + +b + +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/array_call.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/array_call.test new file mode 100644 index 000000000..8c7a2ee7c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/array_call.test @@ -0,0 +1,14 @@ +--TEST-- +Twig supports method calls +--TEMPLATE-- +{{ items.foo }} +{{ items['foo'] }} +{{ items[foo] }} +{{ items[items[foo]] }} +--DATA-- +return ['foo' => 'bar', 'items' => ['foo' => 'bar', 'bar' => 'foo']] +--EXPECT-- +bar +bar +foo +bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/binary.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/binary.test new file mode 100644 index 000000000..b4e8be58d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/binary.test @@ -0,0 +1,46 @@ +--TEST-- +Twig supports binary operations (+, -, *, /, ~, %, and, or) +--TEMPLATE-- +{{ 1 + 1 }} +{{ 2 - 1 }} +{{ 2 * 2 }} +{{ 2 / 2 }} +{{ 3 % 2 }} +{{ 1 and 1 }} +{{ 1 and 0 }} +{{ 0 and 1 }} +{{ 0 and 0 }} +{{ 1 or 1 }} +{{ 1 or 0 }} +{{ 0 or 1 }} +{{ 0 or 0 }} +{{ 0 or 1 and 0 }} +{{ 1 or 0 and 1 }} +{{ "foo" ~ "bar" }} +{{ foo ~ "bar" }} +{{ "foo" ~ bar }} +{{ foo ~ bar }} +{{ 20 // 7 }} +--DATA-- +return ['foo' => 'bar', 'bar' => 'foo'] +--EXPECT-- +2 +1 +4 +1 +1 +1 + + + +1 +1 +1 + + +1 +foobar +barbar +foofoo +barfoo +2 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/bitwise.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/bitwise.test new file mode 100644 index 000000000..c2bda1cc6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/bitwise.test @@ -0,0 +1,14 @@ +--TEST-- +Twig supports bitwise operations +--TEMPLATE-- +{{ 1 b-and 5 }} +{{ 1 b-or 5 }} +{{ 1 b-xor 5 }} +{{ (1 and 0 b-or 0) is same as(1 and (0 b-or 0)) ? 'ok' : 'ko' }} +--DATA-- +return [] +--EXPECT-- +1 +5 +4 +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/call_argument_defined_twice.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/call_argument_defined_twice.test new file mode 100644 index 000000000..36539a6d1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/call_argument_defined_twice.test @@ -0,0 +1,8 @@ +--TEST-- +Argument is defined twice in a call +--TEMPLATE-- +{{ date(987654, date = 123456) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Argument "date" is defined twice for function "date" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/call_positional_arg_after_named_arg.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/call_positional_arg_after_named_arg.test new file mode 100644 index 000000000..729c67485 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/call_positional_arg_after_named_arg.test @@ -0,0 +1,8 @@ +--TEST-- +Positional arguments after named arguments in a call +--TEMPLATE-- +{{ date(date = 123456, 'Y-m-d') }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Positional arguments cannot be used after named arguments for function "date" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/comparison.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/comparison.test new file mode 100644 index 000000000..2811634f7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/comparison.test @@ -0,0 +1,14 @@ +--TEST-- +Twig supports comparison operators (==, !=, <, >, >=, <=) +--TEMPLATE-- +{{ 1 > 2 }}/{{ 1 > 1 }}/{{ 1 >= 2 }}/{{ 1 >= 1 }} +{{ 1 < 2 }}/{{ 1 < 1 }}/{{ 1 <= 2 }}/{{ 1 <= 1 }} +{{ 1 == 1 }}/{{ 1 == 2 }} +{{ 1 != 1 }}/{{ 1 != 2 }} +--DATA-- +return [] +--EXPECT-- +///1 +1//1/1 +1/ +/1 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/comparison_precedence.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/comparison_precedence.test new file mode 100644 index 000000000..db20a6ec9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/comparison_precedence.test @@ -0,0 +1,14 @@ +--TEST-- +Twig comparison operators precendence +--TEMPLATE-- +{{ not(1 > 2) }}/{{ not(1 > 1) }}/{{ not(1 >= 2) }}/{{ not(1 >= 1) }} +{{ not(1 < 2) }}/{{ not(1 < 1) }}/{{ not(1 <= 2) }}/{{ not(1 <= 1) }} +{{ not(1 == 1) }}/{{ not(1 == 2) }} +{{ not(1 != 1) }}/{{ not(1 != 2) }} +--DATA-- +return [] +--EXPECT-- +1/1/1/ +/1// +/1 +1/ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/divisibleby.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/divisibleby.test new file mode 100644 index 000000000..fc5624a1e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/divisibleby.test @@ -0,0 +1,21 @@ +--TEST-- +Twig supports the "divisible by" operator +--TEMPLATE-- +{{ 8 is divisible by(2) ? 'OK' }} +{{ 8 is divisible by 2 ? 'OK' }} +{{ 8 is not divisible by(3) ? 'OK' }} +{{ 8 is divisible by (2) ? 'OK' }} +{{ 8 is divisible by 2 ? 'OK' }} +{{ 8 is not + divisible + by + (3) ? 'OK' }} +--DATA-- +return [] +--EXPECT-- +OK +OK +OK +OK +OK +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/dot_as_concatenation.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/dot_as_concatenation.test new file mode 100644 index 000000000..7aa1403b6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/dot_as_concatenation.test @@ -0,0 +1,8 @@ +--TEST-- +Twig does not support using . for concatenation +--TEMPLATE-- +{{ 'a'.'b' }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Expected name or number, got value "b" of type string in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/dotdot.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/dotdot.test new file mode 100644 index 000000000..0efa93056 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/dotdot.test @@ -0,0 +1,20 @@ +--TEST-- +Twig supports the .. operator +--TEMPLATE-- +{% for i in 0..10 %}{{ i }} {% endfor %} + +{% for letter in 'a'..'z' %}{{ letter }} {% endfor %} + +{% for letter in 'a'|upper..'z'|upper %}{{ letter }} {% endfor %} + +{% for i in foo[0]..foo[1] %}{{ i }} {% endfor %} + +{% for i in 0 + 1 .. 10 - 1 %}{{ i }} {% endfor %} +--DATA-- +return ['foo' => [1, 10]] +--EXPECT-- +0 1 2 3 4 5 6 7 8 9 10 +a b c d e f g h i j k l m n o p q r s t u v w x y z +A B C D E F G H I J K L M N O P Q R S T U V W X Y Z +1 2 3 4 5 6 7 8 9 10 +1 2 3 4 5 6 7 8 9 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ends_with.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ends_with.test new file mode 100644 index 000000000..c8086d6d4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ends_with.test @@ -0,0 +1,26 @@ +--TEST-- +Twig supports the "ends with" operator +--TEMPLATE-- +{{ 'foo' ends with 'o' ? 'OK' : 'KO' }} +{{ not ('foo' ends with 'f') ? 'OK' : 'KO' }} +{{ not ('foo' ends with 'foowaytoolong') ? 'OK' : 'KO' }} +{{ 'foo' ends with '' ? 'OK' : 'KO' }} +{{ '1' ends with true ? 'OK' : 'KO' }} +{{ 1 ends with true ? 'OK' : 'KO' }} +{{ 0 ends with false ? 'OK' : 'KO' }} +{{ '' ends with false ? 'OK' : 'KO' }} +{{ false ends with false ? 'OK' : 'KO' }} +{{ false ends with '' ? 'OK' : 'KO' }} +--DATA-- +return [] +--EXPECT-- +OK +OK +OK +OK +KO +KO +KO +KO +KO +KO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/exponential_numbers.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/exponential_numbers.test new file mode 100644 index 000000000..f403110bc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/exponential_numbers.test @@ -0,0 +1,8 @@ +--TEST-- +Twig manages exponentiel numbers correctly +--TEMPLATE-- +{{ 1.99E+3 }} +--DATA-- +return [] +--EXPECT-- +1990 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/floats.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/floats.test new file mode 100644 index 000000000..be5bfafa7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/floats.test @@ -0,0 +1,14 @@ +--TEST-- +Twig compiles floats properly +--TEMPLATE-- +{% set val2 = 0.0 %} + +{{ val is same as (0.0) ? 'Yes' : 'No' }} +{{ val2 is same as (0.0) ? 'Yes' : 'No' }} +{{ val is same as (val2) ? 'Yes' : 'No' }} +--DATA-- +return ['val' => 0.0] +--EXPECT-- +Yes +Yes +Yes diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/grouping.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/grouping.test new file mode 100644 index 000000000..069ebf793 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/grouping.test @@ -0,0 +1,8 @@ +--TEST-- +Twig supports grouping of expressions +--TEMPLATE-- +{{ (2 + 2) / 2 }} +--DATA-- +return [] +--EXPECT-- +2 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/has_every.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/has_every.test new file mode 100644 index 000000000..dc43b95ca --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/has_every.test @@ -0,0 +1,19 @@ +--TEST-- +Twig supports the "has every" operator +--TEMPLATE-- +{% if [0, 2, 4] has every v => 0 == v % 2 %}Every{% else %}Not every{% endif %} items are even in array +{{ ([0, 2, 4] has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in array +{{ ({ a: 0, b: 2, c: 4 } has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in object +{{ ({ a: 0, b: 2, c: 4 } has every (v, k) => "d" > k)? 'Every' : 'Not every' }} keys are before "d" in object +{{ (it has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in iterator +{{ ([0, 1, 2] has every v => 0 == v % 2) ? 'Every' : 'Not every' }} items are even in array +--DATA-- +return ['it' => new \ArrayIterator([0, 2, 4])] +--EXPECT-- +Every items are even in array +Every items are even in array +Every items are even in object +Every keys are before "d" in object +Every items are even in iterator +Not every items are even in array + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/has_some.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/has_some.test new file mode 100644 index 000000000..c5d75c77d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/has_some.test @@ -0,0 +1,19 @@ +--TEST-- +Twig supports the "has some" operator +--TEMPLATE-- +{% if [1, 2, 3] has some v => 0 == v % 2 %}At least one{% else %}No{% endif %} item is even in array +{{ ([1, 2, 3] has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in array +{{ ({ a: 1, b: 2, c: 3 } has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in object +{{ ({ a: 1, b: 2, c: 3 } has some (v, k) => "b" == k)? 'At least one' : 'No' }} key is "b" in object +{{ (it has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in iterator +{{ ([1, 3, 5] has some v => 0 == v % 2) ? 'At least one' : 'No' }} item is even in array +--DATA-- +return ['it' => new \ArrayIterator([1, 2, 3])] +--EXPECT-- +At least one item is even in array +At least one item is even in array +At least one item is even in object +At least one key is "b" in object +At least one item is even in iterator +No item is even in array + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/literals.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/literals.test new file mode 100644 index 000000000..308e8df2a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/literals.test @@ -0,0 +1,22 @@ +--TEST-- +Twig supports literals +--TEMPLATE-- +1 {{ true }} +2 {{ TRUE }} +3 {{ false }} +4 {{ FALSE }} +5 {{ none }} +6 {{ NONE }} +7 {{ null }} +8 {{ NULL }} +--DATA-- +return [] +--EXPECT-- +1 1 +2 1 +3 +4 +5 +6 +7 +8 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/magic_call.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/magic_call.test new file mode 100644 index 000000000..60417e04f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/magic_call.test @@ -0,0 +1,27 @@ +--TEST-- +Twig supports __call() for attributes +--TEMPLATE-- +{{ foo.foo }} +{{ foo.bar }} +--DATA-- +class TestClassForMagicCallAttributes +{ + public function getBar() + { + return 'bar_from_getbar'; + } + + public function __call($method, $arguments) + { + if ('foo' === $method) { + return 'foo_from_call'; + } + + return false; + } +} + +return ['foo' => new TestClassForMagicCallAttributes()] +--EXPECT-- +foo_from_call +bar_from_getbar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/matches.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/matches.test new file mode 100644 index 000000000..8f5e3669e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/matches.test @@ -0,0 +1,14 @@ +--TEST-- +Twig supports the "matches" operator +--TEMPLATE-- +{{ 'foo' matches '/o/' ? 'OK' : 'KO' }} +{{ 'foo' matches '/^fo/' ? 'OK' : 'KO' }} +{{ 'foo' matches '/O/i' ? 'OK' : 'KO' }} +{{ null matches '/o/' }} +--DATA-- +return [] +--EXPECT-- +OK +OK +OK +0 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/matches_error.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/matches_error.test new file mode 100644 index 000000000..1220eb422 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/matches_error.test @@ -0,0 +1,8 @@ +--TEST-- +Twig supports the "matches" operator with a great error message +--TEMPLATE-- +{{ 'foo' matches '/o' }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Regexp "/o" passed to "matches" is not valid: No ending delimiter '/' found in "index.twig" at line 2 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/method_call.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/method_call.test new file mode 100644 index 000000000..bf49f389e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/method_call.test @@ -0,0 +1,28 @@ +--TEST-- +Twig supports method calls +--TEMPLATE-- +{{ items.foo.foo }} +{{ items.foo.getFoo() }} +{{ items.foo.bar }} +{{ items.foo['bar'] }} +{{ items.foo.bar('a', 43) }} +{{ items.foo.bar(foo) }} +{{ items.foo.self.foo() }} +{{ items.foo.is }} +{{ items.foo.in }} +{{ items.foo.not }} +--DATA-- +return ['foo' => 'bar', 'items' => ['foo' => new Twig\Tests\TwigTestFoo(), 'bar' => 'foo']] +--CONFIG-- +return ['strict_variables' => false] +--EXPECT-- +foo +foo +bar + +bar_a-43 +bar_bar +foo +is +in +not diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/negative_numbers.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/negative_numbers.test new file mode 100644 index 000000000..c2a6e3402 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/negative_numbers.test @@ -0,0 +1,18 @@ +--TEST-- +Twig manages negative numbers correctly +--TEMPLATE-- +{{ -1 }} +{{ - 1 }} +{{ 5 - 1 }} +{{ 5-1 }} +{{ 5 + -1 }} +{{ 5 + - 1 }} +--DATA-- +return [] +--EXPECT-- +-1 +-1 +4 +4 +4 +4 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/not.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/not.test new file mode 100644 index 000000000..27c5c9ecd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/not.test @@ -0,0 +1,16 @@ +--TEST-- +not +--TEMPLATE-- +{{ (not false) ? 'OK' : 'KO' }} +{{ not false ? 'OK' : 'KO' }} +{{ (not false)?'OK':'KO' }} +{{ not false?'OK':'KO' }} +{{not true ? 'KO' : 'OK'}} +--DATA-- +return [] +--EXPECT-- +OK +OK +OK +OK +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/not_arrow_fn.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/not_arrow_fn.test new file mode 100644 index 000000000..af82c47cf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/not_arrow_fn.test @@ -0,0 +1,8 @@ +--TEST-- +A string in parentheses cannot be confused with an arrow function +--TEMPLATE-- +{{ ["foo", "bar"]|join(("f")) }} +--DATA-- +return [] +--EXPECT-- +foofbar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/operators_as_variables.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/operators_as_variables.test new file mode 100644 index 000000000..fa9a843c0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/operators_as_variables.test @@ -0,0 +1,16 @@ +--TEST-- +Twig allows to use named operators as variable names +--TEMPLATE-- +{% for match in matches %} + {{- match }} +{% endfor %} +{{ in }} +{{ is }} +--DATA-- +return ['matches' => [1, 2, 3], 'in' => 'in', 'is' => 'is'] +--EXPECT-- +1 +2 +3 +in +is diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/postfix.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/postfix.test new file mode 100644 index 000000000..276cbf197 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/postfix.test @@ -0,0 +1,22 @@ +--TEST-- +Twig parses postfix expressions +--TEMPLATE-- +{% import _self as macros %} + +{% macro foo() %}foo{% endmacro %} + +{{ 'a' }} +{{ 'a'|upper }} +{{ ('a')|upper }} +{{ -1|upper }} +{{ macros.foo() }} +{{ (macros).foo() }} +--DATA-- +return [] +--EXPECT-- +a +A +A +-1 +foo +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/power.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/power.test new file mode 100644 index 000000000..84fd23692 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/power.test @@ -0,0 +1,20 @@ +--TEST-- +Twig parses power expressions +--TEMPLATE-- +{{ 2**3 }} +{{ (-2)**3 }} +{{ (-2)**(-3) }} +{{ a ** a }} +{{ a ** b }} +{{ b ** a }} +{{ b ** b }} +--DATA-- +return ['a' => 4, 'b' => -2] +--EXPECT-- +8 +-8 +-0.125 +256 +0.0625 +16 +0.25 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/sameas.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/sameas.test new file mode 100644 index 000000000..b4fdaa212 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/sameas.test @@ -0,0 +1,29 @@ +--TEST-- +Twig supports the "same as" operator +--TEMPLATE-- +{{ 1 is same as(1) ? 'OK' }} +{{ 1 is same as 1 ? 'OK' }} +{{ 1 is not same as(true) ? 'OK' }} +{{ 1 is not same as true ? 'OK' }} +{{ 1 is same as(1) ? 'OK' }} +{{ 1 is not same as(true) ? 'OK' }} +{{ 1 is same as (1) ? 'OK' }} +{{ 1 is same as 1 ? 'OK' }} +{{ 1 is not same as '1' ? 'OK' }} +{{ 1 is not + same + as + (true) ? 'OK' }} +--DATA-- +return [] +--EXPECT-- +OK +OK +OK +OK +OK +OK +OK +OK +OK +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/spread_array_operator.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/spread_array_operator.test new file mode 100644 index 000000000..292488eda --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/spread_array_operator.test @@ -0,0 +1,14 @@ +--TEST-- +Twig supports the spread operator on arrays +--TEMPLATE-- +{{ [1, 2, ...[3, 4]]|join(',') }} +{{ [1, 2, ...moreNumbers]|join(',') }} +{{ [1, 2, ...iterableNumbers]|join(',') }} +{{ [1, 2, ...iterableNumbers, 0, ...moreNumbers]|join(',') }} +--DATA-- +return ['moreNumbers' => [5, 6, 7, 8], 'iterableNumbers' => new \ArrayObject([6, 7, 8, 9])] +--EXPECT-- +1,2,3,4 +1,2,5,6,7,8 +1,2,6,7,8,9 +1,2,6,7,8,9,0,5,6,7,8 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/spread_mapping_operator.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/spread_mapping_operator.test new file mode 100644 index 000000000..e944eee8a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/spread_mapping_operator.test @@ -0,0 +1,37 @@ +--TEST-- +Twig supports the spread operator on mappings +--TEMPLATE-- +{% for key, value in { firstName: 'Ryan', lastName: 'Weaver', favoriteFood: 'popcorn', ...{favoriteFood: 'pizza', sport: 'running'} } %} + {{ key }}: {{ value }} +{% endfor %} + +{% for key, value in { firstName: 'Ryan', ...morePersonalDetails} %} + {{ key }}: {{ value }} +{% endfor %} + +{% for key, value in { firstName: 'Ryan', ...iterablePersonalDetails} %} + {{ key }}: {{ value }} +{% endfor %} + +{# multiple spreads #} +{% for key, value in { firstName: 'Ryan', ...iterablePersonalDetails, lastName: 'Weaver', ...morePersonalDetails} %} + {{ key }}: {{ value }} +{% endfor %} +--DATA-- +return ['morePersonalDetails' => ['favoriteColor' => 'orange'], 'iterablePersonalDetails' => new \ArrayObject(['favoriteShoes' => 'barefoot'])]; +--EXPECT-- + firstName: Ryan + lastName: Weaver + favoriteFood: pizza + sport: running + + firstName: Ryan + favoriteColor: orange + + firstName: Ryan + favoriteShoes: barefoot + + firstName: Ryan + favoriteShoes: barefoot + lastName: Weaver + favoriteColor: orange diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/starts_with.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/starts_with.test new file mode 100644 index 000000000..a78ff1eac --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/starts_with.test @@ -0,0 +1,27 @@ +--TEST-- +Twig supports the "starts with" operator +--TEMPLATE-- +{{ 'foo' starts with 'f' ? 'OK' : 'KO' }} +{{ not ('foo' starts with 'oo') ? 'OK' : 'KO' }} +{{ not ('foo' starts with 'foowaytoolong') ? 'OK' : 'KO' }} +{{ 'foo' starts with 'f' ? 'OK' : 'KO' }} +{{ 'foo' starts +with 'f' ? 'OK' : 'KO' }} +{{ 'foo' starts with '' ? 'OK' : 'KO' }} +{{ '1' starts with true ? 'OK' : 'KO' }} +{{ '' starts with false ? 'OK' : 'KO' }} +{{ 'a' starts with false ? 'OK' : 'KO' }} +{{ false starts with '' ? 'OK' : 'KO' }} +--DATA-- +return [] +--EXPECT-- +OK +OK +OK +OK +OK +OK +KO +KO +KO +KO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/string_operator_as_var_assignment.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/string_operator_as_var_assignment.test new file mode 100644 index 000000000..478d4eb5e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/string_operator_as_var_assignment.test @@ -0,0 +1,18 @@ +--TEST-- +Twig supports the string operators as variable names in assignments +--TEMPLATE-- +{% for matches in [1, 2] %} + {{- matches }} +{% endfor %} + +{% set matches = [1, 2] %} + +OK +--DATA-- +return [] +--EXPECT-- +1 +2 + + +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/strings.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/strings.test new file mode 100644 index 000000000..f402c8939 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/strings.test @@ -0,0 +1,10 @@ +--TEST-- +Twig supports string interpolation +--TEMPLATE-- +{{ "foo #{"foo #{bar} baz"} baz" }} +{{ "foo #{bar}#{bar} baz" }} +--DATA-- +return ['bar' => 'BAR'] +--EXPECT-- +foo foo BAR baz baz +foo BARBAR baz diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator.test new file mode 100644 index 000000000..37eccc0f5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator.test @@ -0,0 +1,18 @@ +--TEST-- +Twig supports the ternary operator +--TEMPLATE-- +{{ 1 ? 'YES' : 'NO' }} +{{ 0 ? 'YES' : 'NO' }} +{{ 0 ? 'YES' : (1 ? 'YES1' : 'NO1') }} +{{ 0 ? 'YES' : (0 ? 'YES1' : 'NO1') }} +{{ 1 == 1 ? 'foo
    ':'' }} +{{ foo ~ (bar ? ('-' ~ bar) : '') }} +--DATA-- +return ['foo' => 'foo', 'bar' => 'bar'] +--EXPECT-- +YES +NO +YES1 +NO1 +foo
    +foo-bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator_noelse.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator_noelse.test new file mode 100644 index 000000000..8b0f7284b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator_noelse.test @@ -0,0 +1,10 @@ +--TEST-- +Twig supports the ternary operator +--TEMPLATE-- +{{ 1 ? 'YES' }} +{{ 0 ? 'YES' }} +--DATA-- +return [] +--EXPECT-- +YES + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator_nothen.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator_nothen.test new file mode 100644 index 000000000..ecd6b7546 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/ternary_operator_nothen.test @@ -0,0 +1,10 @@ +--TEST-- +Twig supports the ternary operator +--TEMPLATE-- +{{ 'YES' ?: 'NO' }} +{{ 0 ?: 'NO' }} +--DATA-- +return [] +--EXPECT-- +YES +NO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/two_word_operators_as_variables.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/two_word_operators_as_variables.test new file mode 100644 index 000000000..eca3b285b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/two_word_operators_as_variables.test @@ -0,0 +1,8 @@ +--TEST-- +Twig does not allow to use two-word named operators as variable names +--TEMPLATE-- +{{ starts with }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unexpected token "operator" of value "starts with" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary.test new file mode 100644 index 000000000..542253119 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary.test @@ -0,0 +1,12 @@ +--TEST-- +Twig supports unary operators (not, -, +) +--TEMPLATE-- +{{ not 1 }}/{{ not 0 }} +{{ +1 + 1 }}/{{ -1 - 1 }} +{{ not (false or true) }} +--DATA-- +return [] +--EXPECT-- +/1 +2/-2 + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary_macro_arguments.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary_macro_arguments.test new file mode 100644 index 000000000..27deba3ef --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary_macro_arguments.test @@ -0,0 +1,22 @@ +--TEST-- +Twig manages negative numbers as default parameters +--TEMPLATE-- +{% import _self as macros %} +{{ macros.negative_number1() }} +{{ macros.negative_number2() }} +{{ macros.negative_number3() }} +{{ macros.positive_number1() }} +{{ macros.positive_number2() }} +{% macro negative_number1(nb=-1) %}{{ nb }}{% endmacro %} +{% macro negative_number2(nb = --1) %}{{ nb }}{% endmacro %} +{% macro negative_number3(nb = - 1) %}{{ nb }}{% endmacro %} +{% macro positive_number1(nb = +1) %}{{ nb }}{% endmacro %} +{% macro positive_number2(nb = ++1) %}{{ nb }}{% endmacro %} +--DATA-- +return [] +--EXPECT-- +-1 +1 +-1 +1 +1 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary_precedence.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary_precedence.test new file mode 100644 index 000000000..9c56ab12c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/expressions/unary_precedence.test @@ -0,0 +1,14 @@ +--TEST-- +Twig unary operators precedence +--TEMPLATE-- +{{ -1 - 1 }} +{{ -1 - -1 }} +{{ -1 * -1 }} +{{ 4 / -1 * 5 }} +--DATA-- +return [] +--EXPECT-- +-2 +0 +1 +-20 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/extensions/anonymous_functions.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/extensions/anonymous_functions.test new file mode 100644 index 000000000..a850eeef1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/extensions/anonymous_functions.test @@ -0,0 +1,10 @@ +--TEST-- +use an anonymous function as a function +--TEMPLATE-- +{{ anon_foo('bar') }} +{{ 'bar'|anon_foo }} +--DATA-- +return [] +--EXPECT-- +*bar* +*bar* diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/abs.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/abs.test new file mode 100644 index 000000000..7518769d4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/abs.test @@ -0,0 +1,30 @@ +--TEST-- +"abs" filter +--TEMPLATE-- +{{ (-5.5)|abs }} +{{ (-5)|abs }} +{{ (-0)|abs }} +{{ 0|abs }} +{{ 5|abs }} +{{ 5.5|abs }} +{{ number1|abs }} +{{ number2|abs }} +{{ number3|abs }} +{{ number4|abs }} +{{ number5|abs }} +{{ number6|abs }} +--DATA-- +return ['number1' => -5.5, 'number2' => -5, 'number3' => -0, 'number4' => 0, 'number5' => 5, 'number6' => 5.5] +--EXPECT-- +5.5 +5 +0 +0 +5 +5.5 +5.5 +5 +0 +0 +5 +5.5 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch.test new file mode 100644 index 000000000..0acf25bd6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch.test @@ -0,0 +1,31 @@ +--TEST-- +"batch" filter +--TEMPLATE-- +{% for row in items|batch(3) %} +
    + {% for column in row %} +
    {{ column }}
    + {% endfor %} +
    +{% endfor %} +--DATA-- +return ['items' => ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']] +--EXPECT-- +
    +
    a
    +
    b
    +
    c
    +
    +
    +
    d
    +
    e
    +
    f
    +
    +
    +
    g
    +
    h
    +
    i
    +
    +
    +
    j
    +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_float.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_float.test new file mode 100644 index 000000000..dad004ec2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_float.test @@ -0,0 +1,29 @@ +--TEST-- +"batch" filter +--TEMPLATE-- +{% for row in items|batch(3.1) %} +
    + {% for column in row %} +
    {{ column }}
    + {% endfor %} +
    +{% endfor %} +--DATA-- +return ['items' => ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']] +--EXPECT-- +
    +
    a
    +
    b
    +
    c
    +
    d
    +
    +
    +
    e
    +
    f
    +
    g
    +
    h
    +
    +
    +
    i
    +
    j
    +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_empty_fill.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_empty_fill.test new file mode 100644 index 000000000..411b4fc74 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_empty_fill.test @@ -0,0 +1,37 @@ +--TEST-- +"batch" filter +--TEMPLATE-- + +{% for row in items|batch(3, '') %} + + {% for column in row %} + + {% endfor %} + +{% endfor %} +
    {{ column }}
    +--DATA-- +return ['items' => ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']] +--EXPECT-- + + + + + + + + + + + + + + + + + + + + + +
    abc
    def
    ghi
    j
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_exact_elements.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_exact_elements.test new file mode 100644 index 000000000..750d1557a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_exact_elements.test @@ -0,0 +1,33 @@ +--TEST-- +"batch" filter +--TEMPLATE-- +{% for row in items|batch(3, 'fill') %} +
    + {% for column in row %} +
    {{ column }}
    + {% endfor %} +
    +{% endfor %} +--DATA-- +return ['items' => ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l']] +--EXPECT-- +
    +
    a
    +
    b
    +
    c
    +
    +
    +
    d
    +
    e
    +
    f
    +
    +
    +
    g
    +
    h
    +
    i
    +
    +
    +
    j
    +
    k
    +
    l
    +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_fill.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_fill.test new file mode 100644 index 000000000..5d470d005 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_fill.test @@ -0,0 +1,37 @@ +--TEST-- +"batch" filter +--TEMPLATE-- + +{% for row in items|batch(3, 'fill') %} + + {% for column in row %} + + {% endfor %} + +{% endfor %} +
    {{ column }}
    +--DATA-- +return ['items' => ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j']] +--EXPECT-- + + + + + + + + + + + + + + + + + + + + + +
    abc
    def
    ghi
    jfillfill
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_keys.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_keys.test new file mode 100644 index 000000000..e56cd79bb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_keys.test @@ -0,0 +1,10 @@ +--TEST-- +"batch" filter preserves array keys +--TEMPLATE-- +{{ {'foo': 'bar', 'key': 'value'}|batch(4)|first|keys|join(',') }} +{{ {'foo': 'bar', 'key': 'value'}|batch(4, 'fill')|first|keys|join(',') }} +--DATA-- +return [] +--EXPECT-- +foo,key +foo,key,0,1 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_more_elements.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_more_elements.test new file mode 100644 index 000000000..90f4de685 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_more_elements.test @@ -0,0 +1,23 @@ +--TEST-- +"batch" filter +--TEMPLATE-- +{% for row in items|batch(3, 'fill') %} +
    + {% for key, column in row %} +
    {{ column }}
    + {% endfor %} +
    +{% endfor %} +--DATA-- +return ['items' => ['a' => 'a', 'b' => 'b', 'c' => 'c', 'd' => 'd', '123' => 'e']] +--EXPECT-- +
    +
    a
    +
    b
    +
    c
    +
    +
    +
    d
    +
    e
    +
    fill
    +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_zero_elements.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_zero_elements.test new file mode 100644 index 000000000..bc303da8a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/batch_with_zero_elements.test @@ -0,0 +1,10 @@ +--TEST-- +"batch" filter with zero elements +--TEMPLATE-- +{{ []|batch(3)|length }} +{{ []|batch(3, 'fill')|length }} +--DATA-- +return [] +--EXPECT-- +0 +0 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/capitalize.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/capitalize.test new file mode 100644 index 000000000..bea82ba7b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/capitalize.test @@ -0,0 +1,14 @@ +--TEST-- +"capitalize" filter +--TEMPLATE-- +{{ "super helpful"|capitalize }} +{{ "a"|capitalize }} +*{{ ""|capitalize }}* +*{{ null|capitalize }}* +--DATA-- +return [] +--EXPECT-- +Super helpful +A +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/column.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/column.test new file mode 100644 index 000000000..d47c44e15 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/column.test @@ -0,0 +1,11 @@ +--TEST-- +"column" filter +--TEMPLATE-- +{{ array|column('foo')|join }} +{{ traversable|column('foo')|join }} +--DATA-- +$items = [['bar' => 'foo', 'foo' => 'bar'], ['foo' => 'foo', 'bar' => 'bar']]; +return ['array' => $items, 'traversable' => new ArrayIterator($items)]; +--EXPECT-- +barfoo +barfoo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/convert_encoding.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/convert_encoding.test new file mode 100644 index 000000000..a2a324855 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/convert_encoding.test @@ -0,0 +1,12 @@ +--TEST-- +"convert_encoding" filter +--TEMPLATE-- +{{ "愛していますか?"|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }} +*{{ ""|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}* +*{{ null|convert_encoding('ISO-2022-JP', 'UTF-8')|convert_encoding('UTF-8', 'ISO-2022-JP') }}* +--DATA-- +return [] +--EXPECT-- +愛していますか? +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date.test new file mode 100644 index 000000000..16816aa99 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date.test @@ -0,0 +1,90 @@ +--TEST-- +"date" filter +--TEMPLATE-- +{{ date1|date }} +{{ date1|date('d/m/Y') }} +{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }} +{{ date1|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }} +{{ date1|date('d/m/Y H:i:s P', 'America/Chicago') }} +{{ date1|date('e') }} +{{ date1|date('d/m/Y H:i:s') }} + +{{ date2|date }} +{{ date2|date('d/m/Y') }} +{{ date2|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }} +{{ date2|date('d/m/Y H:i:s', timezone1) }} +{{ date2|date('d/m/Y H:i:s') }} + +{{ date3|date }} +{{ date3|date('d/m/Y') }} + +{{ date4|date }} +{{ date4|date('d/m/Y') }} + +{{ date5|date }} +{{ date5|date('d/m/Y') }} + +{{ date6|date('d/m/Y H:i:s P', 'Europe/Paris') }} +{{ date6|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }} +{{ date6|date('d/m/Y H:i:s P', false) }} +{{ date6|date('e', 'Europe/Paris') }} +{{ date6|date('e', false) }} + +{{ date7|date }} +{{ date7|date(timezone='Europe/Paris') }} +{{ date7|date(timezone='Asia/Hong_Kong') }} +{{ date7|date(timezone=false) }} +{{ date7|date(timezone='Indian/Mauritius') }} + +{{ '2010-01-28 15:00:00'|date(timezone="Europe/Paris") }} +{{ '2010-01-28 15:00:00'|date(timezone="Asia/Hong_Kong") }} +--DATA-- +date_default_timezone_set('Europe/Paris'); +return [ + 'date1' => mktime(13, 45, 0, 10, 4, 2010), + 'date2' => new \DateTime('2010-10-04 13:45'), + 'date3' => '2010-10-04 13:45', + 'date4' => 1286199900, // \DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new \DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT + 'date5' => -189291360, // \DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new \DateTimeZone('UTC'))->getTimestamp(), + 'date6' => new \DateTime('2010-10-04 13:45', new \DateTimeZone('America/New_York')), + 'date7' => '2010-01-28T15:00:00+04:00', + 'timezone1' => new \DateTimeZone('America/New_York'), +] +--EXPECT-- +October 4, 2010 13:45 +04/10/2010 +04/10/2010 19:45:00 +04/10/2010 19:45:00 +08:00 +04/10/2010 06:45:00 -05:00 +Europe/Paris +04/10/2010 13:45:00 + +October 4, 2010 13:45 +04/10/2010 +04/10/2010 19:45:00 +04/10/2010 07:45:00 +04/10/2010 13:45:00 + +October 4, 2010 13:45 +04/10/2010 + +October 4, 2010 15:45 +04/10/2010 + +January 2, 1964 04:04 +02/01/1964 + +04/10/2010 19:45:00 +02:00 +05/10/2010 01:45:00 +08:00 +04/10/2010 13:45:00 -04:00 +Europe/Paris +America/New_York + +January 28, 2010 12:00 +January 28, 2010 12:00 +January 28, 2010 19:00 +January 28, 2010 15:00 +January 28, 2010 15:00 + +January 28, 2010 15:00 +January 28, 2010 22:00 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_default_format.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_default_format.test new file mode 100644 index 000000000..3f56d4e6d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_default_format.test @@ -0,0 +1,14 @@ +--TEST-- +"date" filter +--TEMPLATE-- +{{ date1|date }} +{{ date1|date('d/m/Y') }} +--DATA-- +date_default_timezone_set('UTC'); +$twig->getExtension(\Twig\Extension\CoreExtension::class)->setDateFormat('Y-m-d', '%d days %h hours'); +return [ + 'date1' => mktime(13, 45, 0, 10, 4, 2010), +] +--EXPECT-- +2010-10-04 +04/10/2010 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_default_format_interval.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_default_format_interval.test new file mode 100644 index 000000000..6b7879f7c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_default_format_interval.test @@ -0,0 +1,14 @@ +--TEST-- +"date" filter (interval support) +--TEMPLATE-- +{{ date2|date }} +{{ date2|date('%d days') }} +--DATA-- +date_default_timezone_set('UTC'); +$twig->getExtension(\Twig\Extension\CoreExtension::class)->setDateFormat('Y-m-d', '%d days %h hours'); +return [ + 'date2' => new \DateInterval('P2D'), +] +--EXPECT-- +2 days 0 hours +2 days diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_immutable.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_immutable.test new file mode 100644 index 000000000..b87a97391 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_immutable.test @@ -0,0 +1,35 @@ +--TEST-- +"date" filter +--TEMPLATE-- +{{ date1|date }} +{{ date1|date('d/m/Y') }} +{{ date1|date('d/m/Y H:i:s', 'Asia/Hong_Kong') }} +{{ date1|date('d/m/Y H:i:s', timezone1) }} +{{ date1|date('d/m/Y H:i:s') }} +{{ date1|date_modify('+1 hour')|date('d/m/Y H:i:s') }} + +{{ date2|date('d/m/Y H:i:s P', 'Europe/Paris') }} +{{ date2|date('d/m/Y H:i:s P', 'Asia/Hong_Kong') }} +{{ date2|date('d/m/Y H:i:s P', false) }} +{{ date2|date('e', 'Europe/Paris') }} +{{ date2|date('e', false) }} +--DATA-- +date_default_timezone_set('Europe/Paris'); +return [ + 'date1' => new \DateTimeImmutable('2010-10-04 13:45'), + 'date2' => new \DateTimeImmutable('2010-10-04 13:45', new \DateTimeZone('America/New_York')), + 'timezone1' => new \DateTimeZone('America/New_York'), +] +--EXPECT-- +October 4, 2010 13:45 +04/10/2010 +04/10/2010 19:45:00 +04/10/2010 07:45:00 +04/10/2010 13:45:00 +04/10/2010 14:45:00 + +04/10/2010 19:45:00 +02:00 +05/10/2010 01:45:00 +08:00 +04/10/2010 13:45:00 -04:00 +Europe/Paris +America/New_York diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_interval.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_interval.test new file mode 100644 index 000000000..89f0c0c59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_interval.test @@ -0,0 +1,17 @@ +--TEST-- +"date" filter (interval support) +--TEMPLATE-- +{{ date1|date }} +{{ date1|date('%d days %h hours') }} +{{ date1|date('%d days %h hours', timezone1) }} +--DATA-- +date_default_timezone_set('UTC'); +return [ + 'date1' => new \DateInterval('P2D'), + // This should have no effect on \DateInterval formatting + 'timezone1' => new \DateTimeZone('America/New_York'), +] +--EXPECT-- +2 days +2 days 0 hours +2 days 0 hours diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_modify.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_modify.test new file mode 100644 index 000000000..d7f8fdf42 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_modify.test @@ -0,0 +1,14 @@ +--TEST-- +"date_modify" filter +--TEMPLATE-- +{{ date1|date_modify('-1day')|date('Y-m-d H:i:s') }} +{{ date2|date_modify('-1day')|date('Y-m-d H:i:s') }} +--DATA-- +date_default_timezone_set('UTC'); +return [ + 'date1' => '2010-10-04 13:45', + 'date2' => new \DateTime('2010-10-04 13:45'), +] +--EXPECT-- +2010-10-03 13:45:00 +2010-10-03 13:45:00 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_namedargs.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_namedargs.test new file mode 100644 index 000000000..2d1aa1344 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/date_namedargs.test @@ -0,0 +1,13 @@ +--TEST-- +"date" filter +--TEMPLATE-- +{{ date|date(format='d/m/Y H:i:s P', timezone='America/Chicago') }} +{{ date|date(timezone='America/Chicago', format='d/m/Y H:i:s P') }} +{{ date|date('d/m/Y H:i:s P', timezone='America/Chicago') }} +--DATA-- +date_default_timezone_set('UTC'); +return ['date' => mktime(13, 45, 0, 10, 4, 2010)] +--EXPECT-- +04/10/2010 08:45:00 -05:00 +04/10/2010 08:45:00 -05:00 +04/10/2010 08:45:00 -05:00 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/default.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/default.test new file mode 100644 index 000000000..c56d63bda --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/default.test @@ -0,0 +1,159 @@ +--TEST-- +"default" filter +--TEMPLATE-- +Variable: +{{ definedVar |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ nullVar |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }} +Array access: +{{ nested.definedVar |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ nested['definedVar'] |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ nested.zeroVar |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ nested.emptyVar |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ nested.nullVar |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ nested.undefinedVar |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ nested['undefinedVar'] |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ undefined['undefined'] |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ undefinedVar.foo |default('default') is same as('default') ? 'ok' : 'ko' }} +Plain values: +{{ 'defined' |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ 0 |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ '' |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ null |default('default') is same as('default') ? 'ok' : 'ko' }} +Precedence: +{{ 'o' ~ nullVar |default('k') }} +{{ 'o' ~ nested.nullVar |default('k') }} +Object methods: +{{ object.foo |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ object.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ object.getFoo() |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ object.getFoo('a') |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ object.undefinedMethod() |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ object.undefinedMethod('a') |default('default') is same as('default') ? 'ok' : 'ko' }} +Deep nested: +{{ nested.undefinedVar.foo.bar |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ nested.definedArray.0 |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ nested['definedArray'][0] |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ nested['undefinedVar'][0] |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ undefined['undefined'][0] |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ object.self.foo |default('default') is same as('default') ? 'ko' : 'ok' }} +{{ object.self.undefinedMethod |default('default') is same as('default') ? 'ok' : 'ko' }} +{{ object.undefinedMethod.self |default('default') is same as('default') ? 'ok' : 'ko' }} +--DATA-- +return [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'emptyVar' => '', + 'nullVar' => null, + 'nested' => [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'emptyVar' => '', + 'nullVar' => null, + 'definedArray' => [0], + ], + 'object' => new Twig\Tests\TwigTestFoo(), +] +--CONFIG-- +return ['strict_variables' => false] +--EXPECT-- +Variable: +ok +ok +ok +ok +ok +Array access: +ok +ok +ok +ok +ok +ok +ok +ok +ok +Plain values: +ok +ok +ok +ok +Precedence: +ok +ok +Object methods: +ok +ok +ok +ok +ok +ok +Deep nested: +ok +ok +ok +ok +ok +ok +ok +ok +--DATA-- +return [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'emptyVar' => '', + 'nullVar' => null, + 'nested' => [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'emptyVar' => '', + 'nullVar' => null, + 'definedArray' => [0], + ], + 'object' => new Twig\Tests\TwigTestFoo(), +] +--CONFIG-- +return ['strict_variables' => true] +--EXPECT-- +Variable: +ok +ok +ok +ok +ok +Array access: +ok +ok +ok +ok +ok +ok +ok +ok +ok +Plain values: +ok +ok +ok +ok +Precedence: +ok +ok +Object methods: +ok +ok +ok +ok +ok +ok +Deep nested: +ok +ok +ok +ok +ok +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/dynamic_filter.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/dynamic_filter.test new file mode 100644 index 000000000..15c47814d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/dynamic_filter.test @@ -0,0 +1,12 @@ +--TEST-- +dynamic filter +--TEMPLATE-- +{{ 'bar'|foo_path }} +{{ 'bar'|bar_path }} +{{ 'bar'|a_foo_b_bar }} +--DATA-- +return [] +--EXPECT-- +foo/bar +bar/bar +a/b/bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape.test new file mode 100644 index 000000000..b5b3f90f4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape.test @@ -0,0 +1,12 @@ +--TEST-- +"escape" filter +--TEMPLATE-- +{{ "foo
    "|e }} +*{{ ""|e }}* +*{{ null|e }}* +--DATA-- +return [] +--EXPECT-- +foo <br /> +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_html_attr.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_html_attr.test new file mode 100644 index 000000000..10e327554 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_html_attr.test @@ -0,0 +1,8 @@ +--TEST-- +"escape" filter does not escape with the html strategy when using the html_attr strategy +--TEMPLATE-- +{{ '
    '|escape('html_attr') }} +--DATA-- +return [] +--EXPECT-- +<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_javascript.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_javascript.test new file mode 100644 index 000000000..4c2fb7a36 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_javascript.test @@ -0,0 +1,8 @@ +--TEST-- +"escape" filter +--TEMPLATE-- +{{ "é ♜ 𝌆"|e('js') }} +--DATA-- +return [] +--EXPECT-- +\u00E9\u0020\u265C\u0020\uD834\uDF06 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_non_supported_charset.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_non_supported_charset.test new file mode 100644 index 000000000..93f34297b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/escape_non_supported_charset.test @@ -0,0 +1,8 @@ +--TEST-- +"escape" filter +--TEMPLATE-- +{{ "愛していますか?
    "|e }} +--DATA-- +return [] +--EXPECT-- +愛していますか? <br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/filter.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/filter.test new file mode 100644 index 000000000..9d015a4d1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/filter.test @@ -0,0 +1,77 @@ +--TEST-- +"filter" filter +--TEMPLATE-- +{% set offset = 3 %} + +{% for k, v in [1, 5, 3, 4, 5]|filter((v) => v > offset) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in {a: 1, b: 2, c: 5, d: 8}|filter(v => v > offset) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in {a: 1, b: 2, c: 5, d: 8}|filter((v, k) => (v > offset) and (k != "d")) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in [1, 5, 3, 4, 5]|filter(v => v > offset) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in it|filter((v) => v > offset) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in ita|filter(v => v > offset) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in xml|filter(x => true) %} +{{ k }}/{{ v }} +{% endfor %} + +{# we can iterate more than once #} +{% for k, v in xml|filter(x => true) %} +{{ k }}/{{ v }} +{% endfor %} + +{% set coll = ['a', 'b']|filter(v => v is same as('a')) %} +{% if coll|length > 0 %} + {{- coll|join(', ') }} +{% endif %} +--DATA-- +return [ + 'it' => new \ArrayIterator(['a' => 1, 'b' => 2, 'c' => 5, 'd' => 8]), + 'ita' => new Twig\Tests\IteratorAggregateStub(['a' => 1, 'b' => 2, 'c' => 5, 'd' => 8]), + 'xml' => new \SimpleXMLElement('foobarbaz'), +] +--EXPECT-- +1 = 5 +3 = 4 +4 = 5 + +c = 5 +d = 8 + +c = 5 + +1 = 5 +3 = 4 +4 = 5 + +c = 5 +d = 8 + +c = 5 +d = 8 + +elem/foo +elem/bar +elem/baz + +elem/foo +elem/bar +elem/baz + +a diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/find.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/find.test new file mode 100644 index 000000000..3d1dbd422 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/find.test @@ -0,0 +1,46 @@ +--TEST-- +"filter" filter +--TEMPLATE-- + +{{ [1, 2]|find((v) => v > 3) }} + +{{ [1, 5, 3, 4, 5]|find((v) => v > 3) }} + +{{ [1, 5, 3, 4, 5]|find((v) => v > 3) }} + +{{ {a: 1, b: 2, c: 5, d: 8}|find(v => v > 3) }} + +{{ {a: 1, b: 2, c: 5, d: 8}|find((v, k) => (v > 3) and (k != "c")) }} + +{{ [1, 5, 3, 4, 5]|find(v => v > 3) }} + +{{ it|find((v) => v > 3) }} + +{{ ita|find(v => v > 3) }} + +{{ xml|find(x => true) }} + +--DATA-- +return [ + 'it' => new \ArrayIterator(['a' => 1, 'b' => 2, 'c' => 5, 'd' => 8]), + 'ita' => new Twig\Tests\IteratorAggregateStub(['a' => 1, 'b' => 2, 'c' => 5, 'd' => 8]), + 'xml' => new \SimpleXMLElement('foobarbaz'), +] +--EXPECT-- + + +5 + +5 + +5 + +8 + +5 + +5 + +5 + +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/first.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/first.test new file mode 100644 index 000000000..6ffc18911 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/first.test @@ -0,0 +1,20 @@ +--TEST-- +"first" filter +--TEMPLATE-- +{{ [1, 2, 3, 4]|first }} +{{ {a: 1, b: 2, c: 3, d: 4}|first }} +{{ '1234'|first }} +{{ arr|first }} +{{ 'Ä€é'|first }} +*{{ ''|first }}* +*{{ null|first }}* +--DATA-- +return ['arr' => new \ArrayObject([1, 2, 3, 4])] +--EXPECT-- +1 +1 +1 +1 +Ä +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/force_escape.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/force_escape.test new file mode 100644 index 000000000..7efbe3200 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/force_escape.test @@ -0,0 +1,18 @@ +--TEST-- +"escape" filter +--TEMPLATE-- +{% set foo %} + foo
    +{% endset %} + +{{ foo|e('html') -}} +{{ foo|e('js') }} +{% autoescape true %} + {{ foo }} +{% endautoescape %} +--DATA-- +return [] +--EXPECT-- + foo<br /> +\u0020\u0020\u0020\u0020foo\u003Cbr\u0020\/\u003E\n + foo
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/format.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/format.test new file mode 100644 index 000000000..dace78704 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/format.test @@ -0,0 +1,12 @@ +--TEST-- +"format" filter +--TEMPLATE-- +{{ string|format(foo, 3) }} +*{{ ""|format(foo, 3) }}* +*{{ null|format(foo, 3) }}* +--DATA-- +return ['string' => '%s/%d', 'foo' => 'bar'] +--EXPECT-- +bar/3 +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/join.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/join.test new file mode 100644 index 000000000..3127ea1a7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/join.test @@ -0,0 +1,38 @@ +--TEST-- +"join" filter +--TEMPLATE-- +{{ ["foo", "bar"]|join(', ') }} +{{ foo|join(', ') }} +{{ bar|join(', ') }} + +{{ ["foo", "bar"]|join(', ', ' and ') }} +{{ foo|join(', ', ' and ') }} +{{ bar|join(', ', ' and ') }} +{{ ["one", "two", "three"]|join(', ', ' and ') }} +{{ ["a", "b", "c"]|join('','-') }} +{{ ["a", "b", "c"]|join('-','-') }} +{{ ["a", "b", "c"]|join('-','') }} +{{ ["hello"]|join('|','-') }} + +{{ {"a": "w", "b": "x", "c": "y", "d": "z"}|join }} +{{ {"a": "w", "b": "x", "c": "y", "d": "z"}|join(',') }} +{{ {"a": "w", "b": "x", "c": "y", "d": "z"}|join(',','-') }} +--DATA-- +return ['foo' => new Twig\Tests\TwigTestFoo(), 'bar' => new \ArrayObject([3, 4])] +--EXPECT-- +foo, bar +1, 2 +3, 4 + +foo and bar +1 and 2 +3 and 4 +one, two and three +ab-c +a-b-c +a-bc +hello + +wxyz +w,x,y,z +w,x,y-z diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/json_encode.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/json_encode.test new file mode 100644 index 000000000..902f90b33 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/json_encode.test @@ -0,0 +1,12 @@ +--TEST-- +"json_encode" filter +--TEMPLATE-- +{{ "foo"|json_encode|raw }} +{{ foo|json_encode|raw }} +{{ [foo, "foo"]|json_encode|raw }} +--DATA-- +return ['foo' => new \Twig\Markup('foo', 'UTF-8')] +--EXPECT-- +"foo" +"foo" +["foo","foo"] diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/last.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/last.test new file mode 100644 index 000000000..2fdc63b33 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/last.test @@ -0,0 +1,20 @@ +--TEST-- +"last" filter +--TEMPLATE-- +{{ [1, 2, 3, 4]|last }} +{{ {a: 1, b: 2, c: 3, d: 4}|last }} +{{ '1234'|last }} +{{ arr|last }} +{{ 'Ä€é'|last }} +*{{ ''|last }}* +*{{ null|last }}* +--DATA-- +return ['arr' => new \ArrayObject([1, 2, 3, 4])] +--EXPECT-- +4 +4 +4 +4 +é +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/length.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/length.test new file mode 100644 index 000000000..6e0cd9927 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/length.test @@ -0,0 +1,42 @@ +--TEST-- +"length" filter +--TEMPLATE-- +{{ array|length }} +{{ string|length }} +{{ number|length }} +{{ to_string_able|length }} +{{ countable|length }} +{{ iterator_aggregate|length }} +{{ ""|length }} +{{ null|length }} +{{ magic|length }} +{{ non_countable|length }} +{{ simple_xml_element|length }} +{{ iterator|length }} +--DATA-- +return [ + 'array' => [1, 4], + 'string' => 'foo', + 'number' => 1000, + 'to_string_able' => new Twig\Tests\ToStringStub('foobar'), + 'countable' => new Twig\Tests\CountableStub(42), /* also asserts we do *not* call __toString() */ + 'iterator_aggregate' => new Twig\Tests\IteratorAggregateStub(['a', 'b', 'c']), /* also asserts we do *not* call __toString() */ + 'null' => null, + 'magic' => new Twig\Tests\MagicCallStub(), /* used to assert we do *not* call __call */ + 'non_countable' => new \StdClass(), + 'simple_xml_element' => new \SimpleXMLElement(''), + 'iterator' => new Twig\Tests\SimpleIteratorForTesting() +] +--EXPECT-- +2 +3 +4 +6 +42 +3 +0 +0 +1 +1 +2 +7 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/length_utf8.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/length_utf8.test new file mode 100644 index 000000000..66b058b42 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/length_utf8.test @@ -0,0 +1,10 @@ +--TEST-- +"length" filter +--TEMPLATE-- +{{ string|length }} +{{ markup|length }} +--DATA-- +return ['string' => 'été', 'markup' => new \Twig\Markup('foo', 'UTF-8')] +--EXPECT-- +3 +3 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/lower.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/lower.test new file mode 100644 index 000000000..ec501e7ca --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/lower.test @@ -0,0 +1,12 @@ +--TEST-- +"lower" filter +--TEMPLATE-- +{{ "I like Twig."|lower }} +*{{ ""|lower }}* +*{{ null|lower }}* +--DATA-- +return [] +--EXPECT-- +i like twig. +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/map.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/map.test new file mode 100644 index 000000000..c60bf158d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/map.test @@ -0,0 +1,52 @@ +--TEST-- +"map" filter +--TEMPLATE-- +{% set offset = 3 %} + +{% for k, v in [1, 2]|map((item) => item + 2 ) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in {a: 1, b: 2}|map((item) => item ~ "*" ) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in {a: 1, b: 2}|map((item, k) => item ~ "*" ~ k ) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in [1, 2]|map(item => item + 2 ) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% for k, v in it|map(item => item + 2 ) -%} + {{ k }} = {{ v }} +{% endfor %} + +{% macro local_lower(string) %} + {{- string|lower }} +{% endmacro %} +{{ ['A']|map(val => _self.local_lower(val))|join }} + +{%- from _self import local_lower as renamed_lower %} +{{ ['A']|map(val => renamed_lower(val))|join }} +--DATA-- +return ['it' => new \ArrayIterator([1, 2])] +--EXPECT-- +0 = 3 +1 = 4 + +a = 1* +b = 2* + +a = 1*a +b = 2*b + +0 = 3 +1 = 4 + +0 = 3 +1 = 4 + +a +a diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/merge.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/merge.test new file mode 100644 index 000000000..8877501d4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/merge.test @@ -0,0 +1,25 @@ +--TEST-- +"merge" filter +--TEMPLATE-- +{{ items|merge({'bar': 'foo'})|join }} +{{ items|merge({'bar': 'foo'})|keys|join }} +{{ {'bar': 'foo'}|merge(items)|join }} +{{ {'bar': 'foo'}|merge(items)|keys|join }} +{{ numerics|merge([4, 5, 6])|join }} +{{ traversable.a|merge(traversable.b)|join }} +--DATA-- +return [ + 'items' => ['foo' => 'bar'], + 'numerics' => [1, 2, 3], + 'traversable' => [ + 'a' => new \ArrayObject([0 => 1, 1 => 2, 2 => 3]), + 'b' => new \ArrayObject(['a' => 'b']) + ] +] +--EXPECT-- +barfoo +foobar +foobar +barfoo +123456 +123b diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/nl2br.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/nl2br.test new file mode 100644 index 000000000..d6849cf0d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/nl2br.test @@ -0,0 +1,18 @@ +--TEST-- +"nl2br" filter +--TEMPLATE-- +{{ "I like Twig.\nYou will like it too.\n\nEverybody like it!"|nl2br }} +{{ text|nl2br }} +*{{ ''|nl2br }}* +*{{ null|nl2br }}* +--DATA-- +return ['text' => "If you have some HTML\nit will be escaped."] +--EXPECT-- +I like Twig.
    +You will like it too.
    +
    +Everybody like it! +If you have some <strong>HTML</strong>
    +it will be escaped. +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/number_format.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/number_format.test new file mode 100644 index 000000000..3d12a789c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/number_format.test @@ -0,0 +1,24 @@ +--TEST-- +"number_format" filter +--TEMPLATE-- +{{ 20|number_format }} +{{ 20.25|number_format }} +{{ 20.25|number_format(2) }} +{{ 20.25|number_format(2, ',') }} +{{ 1020.25|number_format(2, ',') }} +{{ 1020.25|number_format(2, ',', '.') }} +{{ '1020.25'|number_format(2, ',', '.') }} +{{ ''|number_format(2, ',', '.') }} +{{ null|number_format(2, ',', '.') }} +--DATA-- +return [] +--EXPECT-- +20 +20 +20.25 +20,25 +1,020,25 +1.020,25 +1.020,25 +0,00 +0,00 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/number_format_default.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/number_format_default.test new file mode 100644 index 000000000..5946680fb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/number_format_default.test @@ -0,0 +1,21 @@ +--TEST-- +"number_format" filter with defaults. +--TEMPLATE-- +{{ 20|number_format }} +{{ 20.25|number_format }} +{{ 20.25|number_format(1) }} +{{ 20.25|number_format(2, ',') }} +{{ 1020.25|number_format }} +{{ 1020.25|number_format(2, ',') }} +{{ 1020.25|number_format(2, ',', '.') }} +--DATA-- +$twig->getExtension(\Twig\Extension\CoreExtension::class)->setNumberFormat(2, '!', '='); +return [] +--EXPECT-- +20!00 +20!25 +20!3 +20,25 +1=020!25 +1=020,25 +1.020,25 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/raw.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/raw.test new file mode 100644 index 000000000..b23513a60 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/raw.test @@ -0,0 +1,8 @@ +--TEST-- +"raw" filter excludes a variable from being escaped +--TEMPLATE-- +{{ br|raw }} +--DATA-- +return ['br' => '
    '] +--EXPECT-- +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reduce.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reduce.test new file mode 100644 index 000000000..73cad4168 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reduce.test @@ -0,0 +1,14 @@ +--TEST-- +"reduce" filter +--TEMPLATE-- +{% set offset = 3 %} + +{{ [1, -1, 4]|reduce((carry, item) => carry + item + offset, 10) }} + +{{ it|reduce((carry, item) => carry + item + offset, 10) }} +--DATA-- +return ['it' => new \ArrayIterator([1, -1, 4])] +--EXPECT-- +23 + +23 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reduce_key.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reduce_key.test new file mode 100644 index 000000000..fe1fb0a7a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reduce_key.test @@ -0,0 +1,14 @@ +--TEST-- +"reduce" filter passes iterable key to callback +--TEMPLATE-- +{% set status_classes = { + 'success': 200, + 'warning': 400, + 'error': 500, +} %} + +{{ status_classes|reduce((carry, v, k) => status_code >= v ? k : carry, '') }} +--DATA-- +return ['status_code' => 404] +--EXPECT-- +warning diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/replace.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/replace.test new file mode 100644 index 000000000..3b430cd5c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/replace.test @@ -0,0 +1,16 @@ +--TEST-- +"replace" filter +--TEMPLATE-- +{{ "I liké %this% and %that%."|replace({'%this%': "foo", '%that%': "bar"}) }} +{{ 'I like single replace operation only %that%'|replace({'%that%' : '%that%1'}) }} +{{ 'I like %this% and %that%.'|replace(traversable) }} +*{{ ''|replace(traversable) }}* +*{{ null|replace(traversable) }}* +--DATA-- +return ['traversable' => new \ArrayObject(['%this%' => 'foo', '%that%' => 'bar'])] +--EXPECT-- +I liké foo and bar. +I like single replace operation only %that%1 +I like foo and bar. +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/replace_invalid_arg.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/replace_invalid_arg.test new file mode 100644 index 000000000..ea1632500 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/replace_invalid_arg.test @@ -0,0 +1,8 @@ +--TEST-- +Exception for invalid argument type in replace call +--TEMPLATE-- +{{ 'test %foo%'|replace(stdClass) }} +--DATA-- +return ['stdClass' => new \stdClass()] +--EXCEPTION-- +Twig\Error\RuntimeError: The "replace" filter expects a sequence/mapping or "Traversable" as replace values, got "stdClass" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reverse.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reverse.test new file mode 100644 index 000000000..42b4dcc64 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/reverse.test @@ -0,0 +1,22 @@ +--TEST-- +"reverse" filter +--TEMPLATE-- +{{ [1, 2, 3, 4]|reverse|join('') }} +{{ '1234évènement'|reverse }} +{{ arr|reverse|join('') }} +{{ {'a': 'c', 'b': 'a'}|reverse()|join(',') }} +{{ {'a': 'c', 'b': 'a'}|reverse(preserveKeys=true)|join(glue=',') }} +{{ {'a': 'c', 'b': 'a'}|reverse(preserve_keys=true)|join(glue=',') }} +*{{ ''|reverse }}* +*{{ null|reverse }}* +--DATA-- +return ['arr' => new \ArrayObject([1, 2, 3, 4])] +--EXPECT-- +4321 +tnemenèvé4321 +4321 +a,c +a,c +a,c +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/round.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/round.test new file mode 100644 index 000000000..264b230d1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/round.test @@ -0,0 +1,34 @@ +--TEST-- +"round" filter +--TEMPLATE-- +{{ 2.7|round }} +{{ 2.1|round }} +{{ 2.1234|round(3, 'floor') }} +{{ 2.1|round(0, 'ceil') }} + +{{ 21.3|round(-1)}} +{{ 21.3|round(-1, 'ceil')}} +{{ 21.3|round(-1, 'floor')}} +{{ '21.3'|round(-1, 'floor')}} + +{{ ''|round(-1, 'floor')}} +{{ null|round(-1, 'floor')}} +{{ null|round }} +{{ null|round(2, 'ceil') }} +--DATA-- +return [] +--EXPECT-- +3 +2 +2.123 +3 + +20 +30 +20 +20 + +0 +0 +0 +0 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/shuffle.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/shuffle.test new file mode 100644 index 000000000..5a4029dcc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/shuffle.test @@ -0,0 +1,16 @@ +--TEST-- +"shuffle" filter +--TEMPLATE-- +{% set test = 'ok'|shuffle %}{{ 'ok' is same as test or 'ko' is same as test ? 'ok' : 'ko' }} +{% set test = [3, 1]|shuffle %}{{ [3, 1] is same as test or [1, 3] is same as test ? 'ok' : 'ko' }} +{% set test = ['foo', 'bar']|shuffle %}{{ ['foo', 'bar'] is same as test or ['bar', 'foo'] is same as test ? 'ok' : 'ko' }} +{% set test = {'a': 'd', 'b': 'e'}|shuffle %}{{ ['d', 'e'] is same as test or ['e', 'd'] is same as test ? 'ok' : 'ko' }} +{% set test = traversable|shuffle %}{{ [3, 1] is same as test or [1, 3] is same as test ? 'ok' : 'ko' }} +--DATA-- +return ['traversable' => new \ArrayObject([0 => 3, 1 => 1])] +--EXPECT-- +ok +ok +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/slice.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/slice.test new file mode 100644 index 000000000..fc975d7bb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/slice.test @@ -0,0 +1,54 @@ +--TEST-- +"slice" filter +--TEMPLATE-- +{{ [1, 2, 3, 4][1:2]|join('') }} +{{ {a: 1, b: 2, c: 3, d: 4}[1:2]|join('') }} +{{ [1, 2, 3, 4][start:length]|join('') }} +{{ [1, 2, 3, 4]|slice(1, 2)|join('') }} +{{ [1, 2, 3, 4]|slice(1, 2)|keys|join('') }} +{{ [1, 2, 3, 4]|slice(1, 2, true)|keys|join('') }} +{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|join('') }} +{{ {a: 1, b: 2, c: 3, d: 4}|slice(1, 2)|keys|join('') }} +{{ '1234'|slice(1, 2) }} +{{ '1234'[1:2] }} +{{ arr|slice(1, 2)|join('') }} +{{ arr[1:2]|join('') }} +{{ arr[4:1]|join('') }} +{{ arr[3:2]|join('') }} + +{{ [1, 2, 3, 4]|slice(1)|join('') }} +{{ [1, 2, 3, 4][1:]|join('') }} +{{ '1234'|slice(1) }} +{{ '1234'[1:] }} +{{ '1234'[:1] }} + +{{ arr|slice(3)|join('') }} +{{ arr[2:]|join('') }} +{{ xml|slice(1)|join('')}} +--DATA-- +return ['start' => 1, 'length' => 2, 'arr' => new \ArrayObject([1, 2, 3, 4]), 'xml' => new \SimpleXMLElement('12')] +--EXPECT-- +23 +23 +23 +23 +01 +12 +23 +bc +23 +23 +23 +23 + +4 + +234 +234 +234 +234 +1 + +4 +34 +2 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/sort.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/sort.test new file mode 100644 index 000000000..c3b2c70dd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/sort.test @@ -0,0 +1,12 @@ +--TEST-- +"sort" filter +--TEMPLATE-- +{{ array1|sort|join }} +{{ array2|sort|join }} +{{ traversable|sort|join }} +--DATA-- +return ['array1' => [4, 1], 'array2' => ['foo', 'bar'], 'traversable' => new \ArrayObject([0 => 3, 1 => 2, 2 => 1])] +--EXPECT-- +14 +barfoo +123 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/sort_with_arrow.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/sort_with_arrow.test new file mode 100644 index 000000000..a8f977d55 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/sort_with_arrow.test @@ -0,0 +1,16 @@ +--TEST-- +"sort" filter +--TEMPLATE-- +{{ fruits|sort((a, b) => a.quantity == b.quantity ? 0 : (a.quantity > b.quantity ? 1 : -1))|column('name')|join(', ') }} +{{ fruits|sort((a, b) => a.quantity <=> b.quantity)|column('name')|join(', ') }} +--DATA-- +return [ + 'fruits' => [ + [ 'name' => 'Apples', 'quantity' => 5 ], + [ 'name' => 'Oranges', 'quantity' => 2 ], + [ 'name' => 'Grapes', 'quantity' => 4 ], + ], +] +--EXPECT-- +Oranges, Grapes, Apples +Oranges, Grapes, Apples diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/spaceless.legacy.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/spaceless.legacy.test new file mode 100644 index 000000000..6a01241e7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/spaceless.legacy.test @@ -0,0 +1,16 @@ +--TEST-- +"spaceless" filter +--DEPRECATION-- +Since twig/twig 3.12: Twig Filter "spaceless" is deprecated in index.twig at line 2. +Since twig/twig 3.12: Twig Filter "spaceless" is deprecated in index.twig at line 3. +Since twig/twig 3.12: Twig Filter "spaceless" is deprecated in index.twig at line 4. +--TEMPLATE-- +{{ "
    foo
    "|spaceless }} +*{{ ""|spaceless }}* +*{{ null|spaceless }}* +--DATA-- +return [] +--EXPECT-- +
    foo
    +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/special_chars.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/special_chars.test new file mode 100644 index 000000000..9869ec91c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/special_chars.test @@ -0,0 +1,8 @@ +--TEST-- +"§" custom filter +--TEMPLATE-- +{{ 'foo'|§ }} +--DATA-- +return [] +--EXPECT-- +§foo§ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/split.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/split.test new file mode 100644 index 000000000..8b2a94d94 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/split.test @@ -0,0 +1,26 @@ +--TEST-- +"split" filter +--TEMPLATE-- +{{ "one,two,three,four,five"|split(',')|join('-') }} +{{ foo|split(',')|join('-') }} +{{ foo|split(',', 3)|join('-') }} +{{ baz|split('')|join('-') }} +{{ baz|split('', 1)|join('-') }} +{{ baz|split('', 2)|join('-') }} +{{ foo|split(',', -2)|join('-') }} +{{ "hello0world"|split('0')|join('-') }} +*{{ ""|split(',')|join('-') }}* +*{{ null|split(',')|join('-') }}* +--DATA-- +return ['foo' => "one,two,three,four,five", 'baz' => '12345',] +--EXPECT-- +one-two-three-four-five +one-two-three-four-five +one-two-three,four,five +1-2-3-4-5 +1-2-3-4-5 +12-34-5 +one-two-three +hello-world +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/split_utf8.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/split_utf8.test new file mode 100644 index 000000000..af48132fd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/split_utf8.test @@ -0,0 +1,22 @@ +--TEST-- +"split" filter +--TEMPLATE-- +{{ "é"|split('', 10)|join('-') }} +{{ foo|split(',')|join('-') }} +{{ foo|split(',', 1)|join('-') }} +{{ foo|split(',', 2)|join('-') }} +{{ foo|split(',', 3)|join('-') }} +{{ baz|split('')|join('-') }} +{{ baz|split('', 1)|join('-') }} +{{ baz|split('', 2)|join('-') }} +--DATA-- +return ['foo' => 'Ä,é,Äほ', 'baz' => 'éÄßごa',] +--EXPECT-- +é +Ä-é-Äほ +Ä,é,Äほ +Ä-é,Äほ +Ä-é-Äほ +é-Ä-ß-ご-a +é-Ä-ß-ご-a +éÄ-ßご-a \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/static_calls.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/static_calls.test new file mode 100644 index 000000000..1626db0c9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/static_calls.test @@ -0,0 +1,10 @@ +--TEST-- +Filters as static method calls +--TEMPLATE-- +{{ 'foo'|static_call_string }} +{{ 'foo'|static_call_array }} +--DATA-- +return ['foo' => 'foo'] +--EXPECT-- +*foo* +*foo* diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/striptags.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/striptags.test new file mode 100644 index 000000000..878b90bfa --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/striptags.test @@ -0,0 +1,16 @@ +--TEST-- +"striptags" filter +--TEMPLATE-- +{{ "Hello, World!"|striptags }} +{{ text|striptags }} +{{ text|striptags('')|raw }} +*{{ ''|striptags }}* +*{{ null|striptags }}* +--DATA-- +return ['text' => "

    Hello, World!

    "] +--EXPECT-- +Hello, World! +Hello, World! +Hello, World! +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/title.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/title.test new file mode 100644 index 000000000..6f1c0c3e8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/title.test @@ -0,0 +1,12 @@ +--TEST-- +"title" filter +--TEMPLATE-- +{{ "I like Twig."|title }} +*{{ ""|title }}* +*{{ null|title }}* +--DATA-- +return [] +--EXPECT-- +I Like Twig. +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/trailing_commas.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/trailing_commas.test new file mode 100644 index 000000000..25f1584ff --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/trailing_commas.test @@ -0,0 +1,8 @@ +--TEST-- +filters allow trailing commas in their argument list +--TEMPLATE-- +{{ 42.55|round(1, 'floor',) }} +--DATA-- +return [] +--EXPECT-- +42.5 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/trim.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/trim.test new file mode 100644 index 000000000..141f86357 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/trim.test @@ -0,0 +1,36 @@ +--TEST-- +"trim" filter +--TEMPLATE-- +{{ " I like Twig. "|trim }} +{{ text|trim }} +{{ " foo/"|trim("/") }} +{{ "xxxI like Twig.xxx"|trim(character_mask="x", side="left") }} +{{ "xxxI like Twig.xxx"|trim(side="right", character_mask="x") }} +{{ "xxxI like Twig.xxx"|trim("x", "right") }} +{{ "/ foo/"|trim("/", "left") }} +{{ "/ foo/"|trim(character_mask="/", side="left") }} +{{ " do nothing. "|trim("", "right") }} +*{{ ""|trim }}* +*{{ ""|trim("", "left") }}* +*{{ ""|trim("", "right") }}* +*{{ null|trim }}* +*{{ null|trim("", "left") }}* +*{{ null|trim("", "right") }}* +--DATA-- +return ['text' => " If you have some HTML it will be escaped. "] +--EXPECT-- +I like Twig. +If you have some <strong>HTML</strong> it will be escaped. + foo +I like Twig.xxx +xxxI like Twig. +xxxI like Twig. + foo/ + foo/ + do nothing. +** +** +** +** +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/upper.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/upper.test new file mode 100644 index 000000000..df415075e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/upper.test @@ -0,0 +1,12 @@ +--TEST-- +"upper" filter +--TEMPLATE-- +{{ "I like Twig."|upper }} +*{{ ""|upper }}* +*{{ null|upper }}* +--DATA-- +return [] +--EXPECT-- +I LIKE TWIG. +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/urlencode.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/urlencode.test new file mode 100644 index 000000000..5db2e2ded --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/filters/urlencode.test @@ -0,0 +1,18 @@ +--TEST-- +"url_encode" filter +--TEMPLATE-- +{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode }} +{{ {foo: "bar", number: 3, "spéßi%l": "e%c0d@d", "spa ce": ""}|url_encode|raw }} +{{ {}|url_encode|default("default") }} +{{ 'spéßi%le%c0d@dspa ce'|url_encode }} +*{{ ''|url_encode }}* +*{{ null|url_encode }}* +--DATA-- +return [] +--EXPECT-- +foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce= +foo=bar&number=3&sp%C3%A9%C3%9Fi%25l=e%25c0d%40d&spa%20ce= +default +sp%C3%A9%C3%9Fi%25le%25c0d%40dspa%20ce +** +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/attribute.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/attribute.test new file mode 100644 index 000000000..31cca8c46 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/attribute.test @@ -0,0 +1,26 @@ +--TEST-- +"attribute" function +--TEMPLATE-- +{{ attribute(obj, method) }} +{{ attribute(variable=obj, attribute=method) }} +{{ attribute(variable: obj, attribute: method) }} +{{ attribute(array, item) }} +{{ attribute(obj, "bar", ["a", "b"]) }} +{{ attribute(obj, "bar", arguments) }} +{{ attribute(variable=obj, attribute="bar", arguments=arguments) }} +{{ attribute(variable: obj, attribute: "bar", arguments: arguments) }} +{{ attribute(obj, method) is defined ? 'ok' : 'ko' }} +{{ attribute(obj, nonmethod) is defined ? 'ok' : 'ko' }} +--DATA-- +return ['obj' => new Twig\Tests\TwigTestFoo(), 'method' => 'foo', 'array' => ['foo' => 'bar'], 'item' => 'foo', 'nonmethod' => 'xxx', 'arguments' => ['a', 'b']] +--EXPECT-- +foo +foo +foo +bar +bar_a-b +bar_a-b +bar_a-b +bar_a-b +ok +ko diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/attribute_with_wrong_args.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/attribute_with_wrong_args.test new file mode 100644 index 000000000..6e8a17cf8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/attribute_with_wrong_args.test @@ -0,0 +1,8 @@ +--TEST-- +"attribute" function +--TEMPLATE-- +{{ attribute(var=var, template="tpl") }} +--DATA-- +return ['var' => null] +--EXCEPTION-- +Twig\Error\SyntaxError: Value for argument "variable" is required for function "attribute" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block.test new file mode 100644 index 000000000..bf8decd1e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block.test @@ -0,0 +1,13 @@ +--TEST-- +"block" function +--TEMPLATE-- +{% extends 'base.twig' %} +{% block bar %}BAR{% endblock %} +--TEMPLATE(base.twig)-- +{% block foo %}{{ block('bar') }}{% endblock %} +{% block baz %}{{ block(name='bar') }}{% endblock %} +{% block bar %}BAR_BASE{% endblock %} +--DATA-- +return [] +--EXPECT-- +BARBARBAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_with_template.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_with_template.test new file mode 100644 index 000000000..2e0d2916c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_with_template.test @@ -0,0 +1,28 @@ +--TEST-- +"block" function with a template argument +--TEMPLATE-- +{{ block('foo', 'included.twig') }} +{{ block('foo', included_loaded) }} +{{ block('foo', included_loaded_internal) }} +{% set output = block('foo', 'included.twig') %} +{{ output }} +{% set output = block(name='foo', template='included.twig') %} +{{ output }} +{% set output = block(template='included.twig', name='foo') %} +{{ output }} +{% block foo %}NOT FOO{% endblock %} +--TEMPLATE(included.twig)-- +{% block foo %}FOO{% endblock %} +--DATA-- +return [ + 'included_loaded' => $twig->load('included.twig'), + 'included_loaded_internal' => $twig->load('included.twig'), +] +--EXPECT-- +FOO +FOO +FOO +FOO +FOO +FOO +NOT FOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_without_name.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_without_name.test new file mode 100644 index 000000000..61896a8a2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_without_name.test @@ -0,0 +1,12 @@ +--TEST-- +"block" function without arguments +--TEMPLATE-- +{% extends 'base.twig' %} +{% block bar %}BAR{% endblock %} +--TEMPLATE(base.twig)-- +{% block foo %}{{ block() }}{% endblock %} +{% block bar %}BAR_BASE{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Value for argument "name" is required for function "block" in "base.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_without_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_without_parent.test new file mode 100644 index 000000000..0f68cb9d0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/block_without_parent.test @@ -0,0 +1,11 @@ +--TEST-- +"block" calling parent() with no definition in parent template +--TEMPLATE-- +{% extends "parent.twig" %} +{% block label %}{{ parent() }}{% endblock %} +--TEMPLATE(parent.twig)-- +{{ block('label') }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Block "label" should not call parent() in "index.twig" as the block does not exist in the parent template "parent.twig" in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/constant.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/constant.test new file mode 100644 index 000000000..cbad8506e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/constant.test @@ -0,0 +1,12 @@ +--TEST-- +"constant" function +--TEMPLATE-- +{{ constant('DATE_W3C') == expect ? 'true' : 'false' }} +{{ constant('ARRAY_AS_PROPS', object) }} +{{ constant('class', object) }} +--DATA-- +return ['expect' => DATE_W3C, 'object' => new \ArrayObject(['hi'])] +--EXPECT-- +true +2 +ArrayObject diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle.test new file mode 100644 index 000000000..0ac6dccd3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle.test @@ -0,0 +1,16 @@ +--TEST-- +"cycle" function +--TEMPLATE-- +{% for i in 0..6 %} +{{ cycle(array1, i) }}-{{ cycle(array2, i) }} +{% endfor %} +--DATA-- +return ['array1' => ['odd', 'even'], 'array2' => ['apple', 'orange', 'citrus']] +--EXPECT-- +odd-apple +even-orange +odd-citrus +even-apple +odd-orange +even-citrus +odd-apple diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_empty_mapping.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_empty_mapping.test new file mode 100644 index 000000000..6296c2c39 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_empty_mapping.test @@ -0,0 +1,8 @@ +--TEST-- +"cycle" function returns an error on empty mappings +--TEMPLATE-- +{{ cycle({}, 0) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: The "cycle" function does not work on empty sequences/mappings in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_empty_sequence.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_empty_sequence.test new file mode 100644 index 000000000..01d9fe127 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_empty_sequence.test @@ -0,0 +1,8 @@ +--TEST-- +"cycle" function returns an error on empty sequences +--TEMPLATE-- +{{ cycle([], 0) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: The "cycle" function does not work on empty sequences/mappings in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_without_enough_args.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_without_enough_args.test new file mode 100644 index 000000000..ba4b2adfb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/cycle_without_enough_args.test @@ -0,0 +1,8 @@ +--TEST-- +"cycle" function without enough args and a named argument +--TEMPLATE-- +{{ cycle(position=2) }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Value for argument "values" is required for function "cycle" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/date.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/date.test new file mode 100644 index 000000000..c879da3cf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/date.test @@ -0,0 +1,27 @@ +--TEST-- +"date" function +--TEMPLATE-- +{{ date().format('r') == date('now').format('r') ? 'OK' : 'KO' }} +{{ date(date1) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} +{{ date(date2) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} +{{ date(date3) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} +{{ date(date4) == date('2010-10-04 13:45') ? 'OK' : 'KO' }} +{{ date(date5) == date('1964-01-02 03:04') ? 'OK' : 'KO' }} +{{ date() > date('-1day') ? 'OK' : 'KO' }} +--DATA-- +date_default_timezone_set('UTC'); +return [ + 'date1' => mktime(13, 45, 0, 10, 4, 2010), + 'date2' => new \DateTime('2010-10-04 13:45'), + 'date3' => '2010-10-04 13:45', + 'date4' => 1286199900, // \DateTime::createFromFormat('Y-m-d H:i', '2010-10-04 13:45', new \DateTimeZone('UTC'))->getTimestamp() -- A unixtimestamp is always GMT + 'date5' => -189291360, // \DateTime::createFromFormat('Y-m-d H:i', '1964-01-02 03:04', new \DateTimeZone('UTC'))->getTimestamp(), +] +--EXPECT-- +OK +OK +OK +OK +OK +OK +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/date_namedargs.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/date_namedargs.test new file mode 100644 index 000000000..819e8326b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/date_namedargs.test @@ -0,0 +1,13 @@ +--TEST-- +"date" function +--TEMPLATE-- +{{ date(date, "America/New_York")|date('d/m/Y H:i:s P', false) }} +{{ date(timezone="America/New_York", date=date)|date('d/m/Y H:i:s P', false) }} +{{ date(timezone: "America/New_York", date: date)|date('d/m/Y H:i:s P', false) }} +--DATA-- +date_default_timezone_set('UTC'); +return ['date' => mktime(13, 45, 0, 10, 4, 2010)] +--EXPECT-- +04/10/2010 09:45:00 -04:00 +04/10/2010 09:45:00 -04:00 +04/10/2010 09:45:00 -04:00 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/deprecated.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/deprecated.test new file mode 100644 index 000000000..355e43303 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/deprecated.test @@ -0,0 +1,21 @@ +--TEST-- +Functions can be deprecated_function +--DEPRECATION-- +Since foo/bar 1.1: Twig Function "deprecated_function" is deprecated. Use "not_deprecated_function" instead in index.twig at line 2. +Since foo/bar 1.1: Twig Function "deprecated_function" is deprecated. Use "not_deprecated_function" instead in index.twig at line 4. +--TEMPLATE-- +{{ deprecated_function() }} + +{{ deprecated_function() }} +--DATA-- +return [] +--EXPECT-- +foo + +foo +--DATA-- +return [] +--EXPECT-- +foo + +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dump.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dump.test new file mode 100644 index 000000000..691a3abea --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dump.test @@ -0,0 +1,16 @@ +--TEST-- +"dump" function +--CONDITION-- +!extension_loaded('xdebug') +--TEMPLATE-- +{{ dump('foo') }} +{{ dump('foo', 'bar') }} +--DATA-- +return ['foo' => 'foo', 'bar' => 'bar'] +--CONFIG-- +return ['debug' => true, 'autoescape' => false] +--EXPECT-- +string(3) "foo" + +string(3) "foo" +string(3) "bar" diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dump_array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dump_array.test new file mode 100644 index 000000000..5fd9383cd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dump_array.test @@ -0,0 +1,19 @@ +--TEST-- +"dump" function, xdebug is not loaded or xdebug <2.2-dev is loaded +--CONDITION-- +!extension_loaded('xdebug') || (($r = new \ReflectionExtension('xdebug')) && version_compare($r->getVersion(), '2.2-dev', '<')) +--TEMPLATE-- +{{ dump() }} +--DATA-- +return ['foo' => 'foo', 'bar' => 'bar'] +--CONFIG-- +return ['debug' => true, 'autoescape' => false] +--EXPECT-- +array(3) { + ["foo"]=> + string(3) "foo" + ["bar"]=> + string(3) "bar" + ["global"]=> + string(6) "global" +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dynamic_function.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dynamic_function.test new file mode 100644 index 000000000..ea851b6dc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/dynamic_function.test @@ -0,0 +1,12 @@ +--TEST-- +dynamic function +--TEMPLATE-- +{{ foo_path('bar') }} +{{ bar_path('bar') }} +{{ a_foo_b_bar('bar') }} +--DATA-- +return [] +--EXPECT-- +foo/bar +bar/bar +a/b/bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_dynamic_enum.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_dynamic_enum.test new file mode 100644 index 000000000..2a6008bba --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_dynamic_enum.test @@ -0,0 +1,13 @@ +--TEST-- +"enum_cases" function with invalid dynamic enum class +--CONDITION-- +\PHP_VERSION_ID >= 80100 +--TEMPLATE-- +{% set from_variable = 'Twig\\Tests\\NonExistentEnum' %} +{% for c in enum_cases(from_variable) %} + {{~ c.name }} +{% endfor %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Enum "Twig\Tests\NonExistentEnum" does not exist in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_enum.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_enum.test new file mode 100644 index 000000000..3084c37e3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_enum.test @@ -0,0 +1,10 @@ +--TEST-- +"enum_cases" function with invalid enum class +--CONDITION-- +\PHP_VERSION_ID >= 80100 +--TEMPLATE-- +{% for c in enum_cases('Twig\\Tests\\NonExistentEnum') %} + {{~ c.name }} +{% endfor %} +--EXCEPTION-- +Twig\Error\SyntaxError: The first argument of the "enum_cases" function must be the name of an enum, "Twig\Tests\NonExistentEnum" given in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_enum_escaping.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_enum_escaping.test new file mode 100644 index 000000000..1d5828fbc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_enum_escaping.test @@ -0,0 +1,10 @@ +--TEST-- +"enum_cases" function with missing \ escaping +--CONDITION-- +\PHP_VERSION_ID >= 80100 +--TEMPLATE-- +{% for c in enum_cases('Twig\Tests\DummyBackedEnum') %} + {{~ c.name }} +{% endfor %} +--EXCEPTION-- +Twig\Error\SyntaxError: The first argument of the "enum_cases" function must be the name of an enum, "TwigTestsDummyBackedEnum" given in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_literal_type.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_literal_type.test new file mode 100644 index 000000000..6e7945d00 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/invalid_literal_type.test @@ -0,0 +1,10 @@ +--TEST-- +"enum_cases" function with invalid literal type +--CONDITION-- +\PHP_VERSION_ID >= 80100 +--TEMPLATE-- +{% for c in enum_cases(13) %} + {{~ c.name }} +{% endfor %} +--EXCEPTION-- +Twig\Error\SyntaxError: The first argument of the "enum_cases" function must be a string in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/valid.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/valid.test new file mode 100644 index 000000000..011d64786 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/enum_cases/valid.test @@ -0,0 +1,24 @@ +--TEST-- +"enum_cases" function +--CONDITION-- +\PHP_VERSION_ID >= 80100 +--TEMPLATE-- +{% for c in enum_cases('Twig\\Tests\\DummyBackedEnum') %} + {{~ c.name }}: {{ c.value }} +{% endfor %} +{% for c in enum_cases('Twig\\Tests\\DummyUnitEnum') %} + {{~ c.name }} +{% endfor %} +{% set from_variable='Twig\\Tests\\DummyUnitEnum' %} +{% for c in enum_cases(from_variable) %} + {{~ c.name }} +{% endfor %} +--DATA-- +return [] +--EXPECT-- +FOO: foo +BAR: bar +BAR +BAZ +BAR +BAZ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/assignment.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/assignment.test new file mode 100644 index 000000000..c9ce8123f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/assignment.test @@ -0,0 +1,13 @@ +--TEST-- +"include" function +--TEMPLATE-- +{% set tmp = include("foo.twig") %} + +FOO{{ tmp }}BAR +--TEMPLATE(foo.twig)-- +FOOBAR +--DATA-- +return [] +--EXPECT-- +FOO +FOOBARBAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/autoescaping.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/autoescaping.test new file mode 100644 index 000000000..a3666261f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/autoescaping.test @@ -0,0 +1,10 @@ +--TEST-- +"include" function is safe for auto-escaping +--TEMPLATE-- +{{ include("foo.twig") }} +--TEMPLATE(foo.twig)-- +

    Test

    +--DATA-- +return [] +--EXPECT-- +

    Test

    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/basic.test new file mode 100644 index 000000000..f90983c02 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/basic.test @@ -0,0 +1,17 @@ +--TEST-- +"include" function +--TEMPLATE-- +FOO +{{ include("foo.twig") }} + +BAR +--TEMPLATE(foo.twig)-- +FOOBAR +--DATA-- +return [] +--EXPECT-- +FOO + +FOOBAR + +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/expression.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/expression.test new file mode 100644 index 000000000..c6d3d1c53 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/expression.test @@ -0,0 +1,17 @@ +--TEST-- +"include" function allows expressions for the template to include +--TEMPLATE-- +FOO +{{ include(foo) }} + +BAR +--TEMPLATE(foo.twig)-- +FOOBAR +--DATA-- +return ['foo' => 'foo.twig'] +--EXPECT-- +FOO + +FOOBAR + +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/ignore_missing.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/ignore_missing.test new file mode 100644 index 000000000..c05b43e14 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/ignore_missing.test @@ -0,0 +1,10 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include(["foo.twig", "bar.twig"], ignore_missing = true) }} +{{ include("foo.twig", ignore_missing = true) }} +{{ include("foo.twig", ignore_missing = true, variables = {}) }} +{{ include("foo.twig", ignore_missing = true, variables = {}, with_context = true) }} +--DATA-- +return [] +--EXPECT-- diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/ignore_missing_exists.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/ignore_missing_exists.test new file mode 100644 index 000000000..fc2d211ad --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/ignore_missing_exists.test @@ -0,0 +1,11 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include("included.twig", ignore_missing = true) }} +NOT DISPLAYED +--TEMPLATE(included.twig)-- +{{ include("DOES NOT EXIST") }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "DOES NOT EXIST" is not defined in "included.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/include_missing_extends.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/include_missing_extends.test new file mode 100644 index 000000000..810ae8248 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/include_missing_extends.test @@ -0,0 +1,13 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include(['bad.twig', 'good.twig'], ignore_missing = true) }} +NOT DISPLAYED +--TEMPLATE(bad.twig)-- +{% extends 'DOES NOT EXIST' %} +--TEMPLATE(good.twig)-- +NOT DISPLAYED +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "DOES NOT EXIST" is not defined in "bad.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/missing.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/missing.test new file mode 100644 index 000000000..1d50f7ac2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/missing.test @@ -0,0 +1,8 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include("foo.twig") }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "foo.twig" is not defined in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/missing_nested.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/missing_nested.test new file mode 100644 index 000000000..9ae8c9ee7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/missing_nested.test @@ -0,0 +1,16 @@ +--TEST-- +"include" function +--TEMPLATE-- +{% extends "base.twig" %} + +{% block content %} + {{ parent() }} +{% endblock %} +--TEMPLATE(base.twig)-- +{% block content %} + {{ include("foo.twig") }} +{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "foo.twig" is not defined in "base.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox.test new file mode 100644 index 000000000..ebfdb1eb8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox.test @@ -0,0 +1,13 @@ +--TEST-- +"include" tag sandboxed +--TEMPLATE-- +{{ include("foo.twig", sandboxed = true) }} +--TEMPLATE(foo.twig)-- + + +{{ foo|e }} +{{ foo|e }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Sandbox\SecurityNotAllowedFilterError: Filter "e" is not allowed in "foo.twig" at line 4. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox_disabling.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox_disabling.test new file mode 100644 index 000000000..1206b67fe --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox_disabling.test @@ -0,0 +1,16 @@ +--TEST-- +"include" tag sandboxed +--TEMPLATE-- +{{ include("foo.twig", sandboxed = true) }} +{{ include("bar.twig") }} +--TEMPLATE(foo.twig)-- +foo +--TEMPLATE(bar.twig)-- +{{ foo|e }} +--DATA-- +return ['foo' => 'bar
    '] +--EXPECT-- +foo + + +bar<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox_disabling_ignore_missing.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox_disabling_ignore_missing.test new file mode 100644 index 000000000..c5be0088a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/sandbox_disabling_ignore_missing.test @@ -0,0 +1,13 @@ +--TEST-- +"include" tag sandboxed +--TEMPLATE-- +{{ include("unknown.twig", sandboxed = true, ignore_missing = true) }} +{{ include("bar.twig") }} +--TEMPLATE(bar.twig)-- +{{ foo|e }} +--DATA-- +return ['foo' => 'bar
    '] +--EXPECT-- + + +bar<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/template_instance.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/template_instance.test new file mode 100644 index 000000000..be18d244a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/template_instance.test @@ -0,0 +1,10 @@ +--TEST-- +"include" function accepts Twig\Template instance +--TEMPLATE-- +{{ include(foo) }} FOO +--TEMPLATE(foo.twig)-- +BAR +--DATA-- +return ['foo' => $twig->load('foo.twig')] +--EXPECT-- +BAR FOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/templates_as_array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/templates_as_array.test new file mode 100644 index 000000000..21e5bb2ef --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/templates_as_array.test @@ -0,0 +1,12 @@ +--TEST-- +"include" function +--TEMPLATE-- +{{ include(["foo.twig", "bar.twig"]) }} +{{- include(["bar.twig", "foo.twig"]) }} +--TEMPLATE(foo.twig)-- +foo +--DATA-- +return [] +--EXPECT-- +foo +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/with_context.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/with_context.test new file mode 100644 index 000000000..46ac8c79b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/with_context.test @@ -0,0 +1,16 @@ +--TEST-- +"include" function accept variables and with_context +--TEMPLATE-- +{{ include("foo.twig") }} +{{- include("foo.twig", with_context = false) }} +{{- include("foo.twig", {'foo1': 'bar'}) }} +{{- include("foo.twig", {'foo1': 'bar'}, with_context = false) }} +--TEMPLATE(foo.twig)-- +{% for k, v in _context %}{{ k }},{% endfor %} +--DATA-- +return ['foo' => 'bar'] +--EXPECT-- +foo,global,_parent, +global,_parent, +foo,global,foo1,_parent, +foo1,global,_parent, diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/with_variables.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/with_variables.test new file mode 100644 index 000000000..0ed98fed0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include/with_variables.test @@ -0,0 +1,12 @@ +--TEST-- +"include" function accept variables +--TEMPLATE-- +{{ include("foo.twig", {'foo': 'bar'}) }} +{{- include("foo.twig", vars) }} +--TEMPLATE(foo.twig)-- +{{ foo }} +--DATA-- +return ['vars' => ['foo' => 'bar']] +--EXPECT-- +bar +bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include_template_from_string.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include_template_from_string.test new file mode 100644 index 000000000..8d9ba60ce --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/include_template_from_string.test @@ -0,0 +1,11 @@ +--TEST-- +"template_from_string" function works in an "include" +--TEMPLATE-- +{% set embed = '{% embed "embed.twig" %}{% endembed %}' %} +{{ include(template_from_string(embed)) }} +--TEMPLATE(embed.twig)-- +Cool +--DATA-- +return [] +--EXPECT-- +Cool diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/magic_call.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/magic_call.test new file mode 100644 index 000000000..f802de804 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/magic_call.test @@ -0,0 +1,10 @@ +--TEST-- +__call calls +--TEMPLATE-- +{{ 'foo'|magic_call }} +{{ 'foo'|magic_call_closure }} +--DATA-- +return [] +--EXPECT-- +magic_foo +magic_foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/magic_static_call.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/magic_static_call.test new file mode 100644 index 000000000..944f15aff --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/magic_static_call.test @@ -0,0 +1,10 @@ +--TEST-- +__staticCall calls +--TEMPLATE-- +{{ 'foo'|magic_call_string }} +{{ 'foo'|magic_call_array }} +--DATA-- +return [] +--EXPECT-- +static_magic_foo +static_magic_foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/max.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/max.test new file mode 100644 index 000000000..6d2de000d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/max.test @@ -0,0 +1,12 @@ +--TEST-- +"max" function +--TEMPLATE-- +{{ max([2, 1, 3, 5, 4]) }} +{{ max(2, 1, 3, 5, 4) }} +{{ max({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }} +--DATA-- +return [] +--EXPECT-- +5 +5 +two diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/max_without_args.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/max_without_args.test new file mode 100644 index 000000000..b9522192b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/max_without_args.test @@ -0,0 +1,8 @@ +--TEST-- +"max" function without an argument throws a compile time exception +--TEMPLATE-- +{{ max() }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Value for argument "value" is required for function "max" in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/min.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/min.test new file mode 100644 index 000000000..1fe5446b7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/min.test @@ -0,0 +1,12 @@ +--TEST-- +"min" function +--TEMPLATE-- +{{ min(2, 1, 3, 5, 4) }} +{{ min([2, 1, 3, 5, 4]) }} +{{ min({2:"two", 1:"one", 3:"three", 5:"five", 4:"for"}) }} +--DATA-- +return [] +--EXPECT-- +1 +1 +five diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/parent_in_condition.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/parent_in_condition.test new file mode 100644 index 000000000..f3d51d2dd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/parent_in_condition.test @@ -0,0 +1,11 @@ +--TEST-- +"block" calling parent() in a conditional expression +--TEMPLATE-- +{% extends "parent.twig" %} +{% block label %}{{ parent() ?: 'foo' }}{% endblock %} +--TEMPLATE(parent.twig)-- +{% block label %}PARENT_LABEL{% endblock %} +--DATA-- +return [] +--EXPECT-- +PARENT_LABEL diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/parent_outside_of_a_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/parent_outside_of_a_block.test new file mode 100644 index 000000000..03d4f5d66 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/parent_outside_of_a_block.test @@ -0,0 +1,10 @@ +--TEST-- +"parent" cannot be called outside of a block +--TEMPLATE-- +{% extends "parent.twig" %} +{{ parent() }} +--TEMPLATE(parent.twig)-- +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Calling the "parent" function outside of a block is forbidden in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/range.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/range.test new file mode 100644 index 000000000..2927333b9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/range.test @@ -0,0 +1,8 @@ +--TEST-- +"range" function +--TEMPLATE-- +{{ range(low=0+1, high=10+0, step=2)|join(',') }} +--DATA-- +return [] +--EXPECT-- +1,3,5,7,9 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/recursive_block_with_inheritance.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/recursive_block_with_inheritance.test new file mode 100644 index 000000000..1c3fffb5d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/recursive_block_with_inheritance.test @@ -0,0 +1,21 @@ +--TEST-- +"block" function recursively called in a parent template +--TEMPLATE-- +{% extends "ordered_menu.twig" %} +{% block label %}"{{ parent() }}"{% endblock %} +{% block list %}{% set class = 'b' %}{{ parent() }}{% endblock %} +--TEMPLATE(ordered_menu.twig)-- +{% extends "menu.twig" %} +{% block list %}{% set class = class|default('a') %}
      {{ block('children') }}
    {% endblock %} +--TEMPLATE(menu.twig)-- +{% extends "base.twig" %} +{% block list %}
      {{ block('children') }}
    {% endblock %} +{% block children %}{% set currentItem = item %}{% for item in currentItem %}{{ block('item') }}{% endfor %}{% set item = currentItem %}{% endblock %} +{% block item %}
  • {% if item is not iterable %}{{ block('label') }}{% else %}{{ block('list') }}{% endif %}
  • {% endblock %} +{% block label %}{{ item }}{% endblock %} +--TEMPLATE(base.twig)-- +{{ block('list') }} +--DATA-- +return ['item' => ['1', '2', ['3.1', ['3.2.1', '3.2.2'], '3.4']]] +--EXPECT-- +
    1. "1"
    2. "2"
      1. "3.1"
        1. "3.2.1"
        2. "3.2.2"
      2. "3.4"
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/source.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/source.test new file mode 100644 index 000000000..b691ce7bc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/source.test @@ -0,0 +1,17 @@ +--TEST-- +"source" function +--TEMPLATE-- +FOO +{{ source("foo.twig") }} + +BAR +--TEMPLATE(foo.twig)-- +{{ foo }}
    +--DATA-- +return [] +--EXPECT-- +FOO + +{{ foo }}
    + +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/special_chars.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/special_chars.test new file mode 100644 index 000000000..9c9e24945 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/special_chars.test @@ -0,0 +1,8 @@ +--TEST-- +"§" custom function +--TEMPLATE-- +{{ §('foo') }} +--DATA-- +return [] +--EXPECT-- +§foo§ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/static_calls.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/static_calls.test new file mode 100644 index 000000000..dd13abb0b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/static_calls.test @@ -0,0 +1,10 @@ +--TEST-- +Functions as static method calls +--TEMPLATE-- +{{ static_call_string('foo') }} +{{ static_call_array('foo') }} +--DATA-- +return ['foo' => 'foo'] +--EXPECT-- +*foo* +*foo* diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string.test new file mode 100644 index 000000000..33b0e40d7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string.test @@ -0,0 +1,15 @@ +--TEST-- +"template_from_string" function +--TEMPLATE-- +{% include template_from_string(template) %} + +{% include template_from_string("Hello {{ name }}") %} +{% include template_from_string('{% extends "parent.twig" %}{% block content %}Hello {{ name }}{% endblock %}') %} +--TEMPLATE(parent.twig)-- +{% block content %}{% endblock %} +--DATA-- +return ['name' => 'Fabien', 'template' => "Hello {{ name }}"] +--EXPECT-- +Hello Fabien +Hello Fabien +Hello Fabien diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string_error.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string_error.test new file mode 100644 index 000000000..1132296bc --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string_error.test @@ -0,0 +1,10 @@ +--TEST-- +"template_from_string" function +--CONDITION-- +PHP_VERSION_ID >= 80100 +--TEMPLATE-- +{% include template_from_string("{{ not a Twig template ", "foo.twig") %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unclosed "variable" in "foo.twig (string template 85e7b092afbbcd36f11981c2ef8f1569)" at line 1. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string_error_php80.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string_error_php80.test new file mode 100644 index 000000000..8ebe41f2c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/template_from_string_error_php80.test @@ -0,0 +1,10 @@ +--TEST-- +"template_from_string" function +--CONDITION-- +PHP_VERSION_ID < 80100 +--TEMPLATE-- +{% include template_from_string("{{ not a Twig template ", "foo.twig") %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unclosed "variable" in "foo.twig (string template 4900163d56b1af4b704c6b0afee7f98ba53418ce7a93d37a3af1882735baf9cd)" at line 1. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/trailing_commas.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/trailing_commas.test new file mode 100644 index 000000000..64bf4caf1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/trailing_commas.test @@ -0,0 +1,8 @@ +--TEST-- +functions allow trailing commas in their argument list +--TEMPLATE-- +{{ max(1, 2, 3,) }} +--DATA-- +return [] +--EXPECT-- +3 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/undefined_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/undefined_block.test new file mode 100644 index 000000000..36584bd24 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/undefined_block.test @@ -0,0 +1,16 @@ +--TEST-- +"block" function with undefined block +--TEMPLATE-- +{% extends "base.twig" %} +{% block foo %} + {{ parent() }} + {{ block('unknown') }} + {{ block('bar') }} +{% endblock %} +--TEMPLATE(base.twig)-- +{% block foo %}Foo{% endblock %} +{% block bar %}Bar{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Block "unknown" on template "base.twig" does not exist in "index.twig" at line 5. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/undefined_block_deep.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/undefined_block_deep.test new file mode 100644 index 000000000..cc98ec09d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/functions/undefined_block_deep.test @@ -0,0 +1,20 @@ +--TEST-- +"block" function with undefined block on deep inheritance +--TEMPLATE-- +{% extends "base.twig" %} +{% block foo %} + {{ parent() }} + {{ block('unknown') }} + {{ block('bar') }} +{% endblock %} +--TEMPLATE(base.twig)-- +{% extends "layout.twig" %} +{% block foo %}Foo{% endblock %} +{% block bar %}Bar{% endblock %} +--TEMPLATE(layout.twig)-- +{% block foo %}Foo{% endblock %} +{% block bar %}Bar{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Block "unknown" on template "layout.twig" does not exist in "index.twig" at line 5. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/default_values.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/default_values.test new file mode 100644 index 000000000..18bba524a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/default_values.test @@ -0,0 +1,16 @@ +--TEST-- +macro +--TEMPLATE-- +{% from _self import test %} + +{% macro test(a, b = 'bar') -%} +{{ a }}{{ b }} +{%- endmacro %} + +{{ test('foo') }} +{{ test('bar', 'foo') }} +--DATA-- +return [] +--EXPECT-- +foobar +barfoo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/macro_with_capture.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/macro_with_capture.test new file mode 100644 index 000000000..2f9caed6e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/macro_with_capture.test @@ -0,0 +1,14 @@ +--TEST-- +macro +--TEMPLATE-- +{{ _self.some_macro() }} + +{% macro some_macro() %} + {% apply upper %} + {% if true %}foo{% endif %} + {% endapply %} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +FOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/nested_calls.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/nested_calls.test new file mode 100644 index 000000000..4577286d0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/nested_calls.test @@ -0,0 +1,18 @@ +--TEST-- +macro +--TEMPLATE-- +{% import _self as macros %} + +{% macro foo(data) %} + {{ data }} +{% endmacro %} + +{% macro bar() %} +
    +{% endmacro %} + +{{ macros.foo(macros.bar()) }} +--DATA-- +return [] +--EXPECT-- +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/reserved_variables.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/reserved_variables.test new file mode 100644 index 000000000..05dd92130 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/reserved_variables.test @@ -0,0 +1,14 @@ +--TEST-- +macro +--TEMPLATE-- +{% from _self import test %} + +{% macro test(this) -%} + {{ this }} +{%- endmacro %} + +{{ test(this) }} +--DATA-- +return ['this' => 'foo'] +--EXPECT-- +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/simple.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/simple.test new file mode 100644 index 000000000..8fc6b477f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/simple.test @@ -0,0 +1,22 @@ +--TEST-- +macro +--TEMPLATE-- +{% import _self as test %} +{% from _self import test %} + +{% macro test(a, b) -%} + {{ a|default('a') }}
    + {{- b|default('b') }}
    +{%- endmacro %} + +{{ test.test() }} +{{ test() }} +{{ test.test(1, "c") }} +{{ test(1, "c") }} +--DATA-- +return [] +--EXPECT-- +a
    b
    +a
    b
    +1
    c
    +1
    c
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/trailing_commas.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/trailing_commas.test new file mode 100644 index 000000000..600247a82 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/trailing_commas.test @@ -0,0 +1,25 @@ +--TEST-- +macros allow trailing commas in their argument and parameter list +--TEMPLATE-- +{% import _self as test %} + +{% macro test(a, b,) -%} + {{ a|default('a') }}
    + {{- b|default('b') }}
    +{%- endmacro %} +{% macro test2(a, b) -%} + {{ a|default('a') }}
    + {{- b|default('b') }}
    +{%- endmacro %} + +{{ test.test(1, 2,) }} +{{ test.test(3, 4) }} +{{ test.test2(5, 6,) }} +{{ test.test2(7, 8) }} +--DATA-- +return [] +--EXPECT-- +1
    2
    +3
    4
    +5
    6
    +7
    8
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/unknown_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/unknown_macro.test new file mode 100644 index 000000000..10ea6c62a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/unknown_macro.test @@ -0,0 +1,10 @@ +--TEST-- +macro +--TEMPLATE-- +{% import _self as macros %} + +{{ macros.unknown() }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Macro "unknown" is not defined in template "index.twig" in "index.twig" at line 4. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/unknown_macro_different_template.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/unknown_macro_different_template.test new file mode 100644 index 000000000..61604e8a9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/unknown_macro_different_template.test @@ -0,0 +1,11 @@ +--TEST-- +Exception for unknown macro in different template +--TEMPLATE-- +{% import foo_template as macros %} +{{ macros.foo() }} +--TEMPLATE(foo.twig)-- +foo +--DATA-- +return array('foo_template' => 'foo.twig') +--EXCEPTION-- +Twig\Error\RuntimeError: Macro "foo" is not defined in template "foo.twig" in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/varargs.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/varargs.test new file mode 100644 index 000000000..dd4b5c9f4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/varargs.test @@ -0,0 +1,21 @@ +--TEST-- +macro with arbitrary arguments +--TEMPLATE-- +{% from _self import test1, test2 %} + +{% macro test1(var) %} + {{- var }}: {{ varargs|join(", ") }} +{% endmacro %} + +{% macro test2() %} + {{- varargs|join(", ") }} +{% endmacro %} + +{{ test1("foo", "bar", "foobar") }} +{{ test2("foo", "bar", "foobar") }} +--DATA-- +return [] +--EXPECT-- +foo: bar, foobar + +foo, bar, foobar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/varargs_argument.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/varargs_argument.test new file mode 100644 index 000000000..1ad368bf1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/varargs_argument.test @@ -0,0 +1,7 @@ +--TEST-- +macro with varargs argument +--TEMPLATE-- +{% macro test(varargs) %} +{% endmacro %} +--EXCEPTION-- +Twig\Error\SyntaxError: The argument "varargs" in macro "test" cannot be defined because the variable "varargs" is reserved for arbitrary arguments in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/with_filters.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/with_filters.test new file mode 100644 index 000000000..72a7a2f44 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/macros/with_filters.test @@ -0,0 +1,14 @@ +--TEST-- +macro with a filter +--TEMPLATE-- +{% import _self as test %} + +{% macro test() %} + {% apply escape %}foo
    {% endapply %} +{% endmacro %} + +{{ test.test() }} +--DATA-- +return [] +--EXPECT-- +foo<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/4029-iterator_to_array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/4029-iterator_to_array.test new file mode 100644 index 000000000..99afd892f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/4029-iterator_to_array.test @@ -0,0 +1,14 @@ +--TEST-- +#4029 When use_yield is true, CaptureNode fall in iterator_to_array pitfall regarding index overwrite +--TEMPLATE-- +{%- set tmp -%} + {%- block foo 'foo' -%} + {%- block bar 'bar' -%} +{%- endset -%} +{{ tmp }} +--DATA-- +return [] +--CONFIG-- +return ['use_yield' => true] +--EXPECT-- +foobar \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/4033-missing-unwrap.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/4033-missing-unwrap.test new file mode 100644 index 000000000..37778731c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/4033-missing-unwrap.test @@ -0,0 +1,19 @@ +--TEST-- +Call to undefined method Twig\\TemplateWrapper::yieldBlock() +--TEMPLATE-- +{% extends 'parent' %} +{%- block content -%} + {{ parent() }} + child +{%- endblock -%} +--TEMPLATE(parent)-- +{% extends ['unknowngrandparent', 'grandparent'] %} +--TEMPLATE(grandparent)-- +{%- block content -%} + grandparent +{%- endblock -%} +--DATA-- +return [] +--EXPECT-- + grandparent + child \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/block_names_unicity.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/block_names_unicity.test new file mode 100644 index 000000000..194146768 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/block_names_unicity.test @@ -0,0 +1,19 @@ +--TEST-- +Block names are unique per template +--TEMPLATE-- +{% extends 'layout' %} +{% block content -%} + {% apply title -%} + second + {% endapply %} +{% endblock %} +--TEMPLATE(layout)-- +{% apply title -%} + first +{% endapply %} +{% block content %}{% endblock %} +--DATA-- +return [] +--EXPECT-- +First +Second diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/combined_debug_info.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/combined_debug_info.test new file mode 100644 index 000000000..6426d2c1d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/combined_debug_info.test @@ -0,0 +1,15 @@ +--TEST-- +Exception with bad line number +--TEMPLATE-- +{% block content %} + {{ foo }} + {{ include("foo") }} +{% endblock %} +index +--TEMPLATE(foo)-- +foo +{{ foo.bar }} +--DATA-- +return ['foo' => 'foo'] +--EXCEPTION-- +Twig\Error\RuntimeError: Impossible to access an attribute ("bar") on a string variable ("foo") in "foo" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/empty_token.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/empty_token.test new file mode 100644 index 000000000..25bdc9e40 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/empty_token.test @@ -0,0 +1,8 @@ +--TEST-- +Twig outputs 0 nodes correctly +--TEMPLATE-- +{{ foo }}0{{ foo }} +--DATA-- +return ['foo' => 'foo'] +--EXPECT-- +foo0foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/multi_word_tests.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/multi_word_tests.test new file mode 100644 index 000000000..96ca5517a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/multi_word_tests.test @@ -0,0 +1,10 @@ +--TEST-- +Twig allows multi-word tests without a custom node class +--TEMPLATE-- +{{ 'foo' is multi word ? 'yes' : 'no' }} +{{ 'foo bar' is multi word ? 'yes' : 'no' }} +--DATA-- +return [] +--EXPECT-- +no +yes diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/simple_xml_element.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/simple_xml_element.test new file mode 100644 index 000000000..ae633c881 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/simple_xml_element.test @@ -0,0 +1,19 @@ +--TEST-- +Twig is able to deal with SimpleXMLElement instances as variables +--CONDITION-- +version_compare(phpversion(), '8.0', '<') +--TEMPLATE-- +Hello '{{ images.image.0.group }}'! +{{ images.image.0.group.attributes.myattr }} +{{ images.children().image.count() }} +{% for image in images %} + - {{ image.group }} +{% endfor %} +--DATA-- +return ['images' => new \SimpleXMLElement('foobar')] +--EXPECT-- +Hello 'foo'! +example +2 + - foo + - bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/strings_like_numbers.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/strings_like_numbers.test new file mode 100644 index 000000000..3884226fa --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/regression/strings_like_numbers.test @@ -0,0 +1,8 @@ +--TEST-- +Twig does not confuse strings with integers in getAttribute() +--TEMPLATE-- +{{ mapping['2e2'] }} +--DATA-- +return ['mapping' => ['2e2' => 'works']] +--EXPECT-- +works diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/basic.test new file mode 100644 index 000000000..4848ee025 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/basic.test @@ -0,0 +1,10 @@ +--TEST-- +"apply" tag applies a filter on its children +--TEMPLATE-- +{% apply upper %} +Some text with a {{ var }} +{% endapply %} +--DATA-- +return ['var' => 'var'] +--EXPECT-- +SOME TEXT WITH A VAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/json_encode.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/json_encode.test new file mode 100644 index 000000000..8a590b44a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/json_encode.test @@ -0,0 +1,8 @@ +--TEST-- +"apply" tag applies a filter on its children +--TEMPLATE-- +{% apply json_encode|raw %}test{% endapply %} +--DATA-- +return [] +--EXPECT-- +"test" diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/multiple.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/multiple.test new file mode 100644 index 000000000..e16998a52 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/multiple.test @@ -0,0 +1,10 @@ +--TEST-- +"apply" tags accept multiple chained filters +--TEMPLATE-- +{% apply lower|title %} + {{ var }} +{% endapply %} +--DATA-- +return ['var' => 'VAR'] +--EXPECT-- + Var diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/nested.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/nested.test new file mode 100644 index 000000000..b64a6914c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/nested.test @@ -0,0 +1,16 @@ +--TEST-- +"apply" tags can be nested at will +--TEMPLATE-- +{% apply lower|title %} + {{ var }} + {% apply upper %} + {{ var }} + {% endapply %} + {{ var }} +{% endapply %} +--DATA-- +return ['var' => 'var'] +--EXPECT-- + Var + Var + Var diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/scope.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/scope.test new file mode 100644 index 000000000..ff8a23116 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/scope.test @@ -0,0 +1,15 @@ +--TEST-- +"apply" tag does not create a new scope +--TEMPLATE-- +{% set foo = 'baz' %} +{% apply upper %} + {% set foo = 'foo' %} + {% set bar = 'bar' %} +{% endapply %} +{{ 'foo' == foo ? 'OK ' ~ foo : 'KO' }} +{{ 'bar' == bar ? 'OK ' ~ bar : 'KO' }} +--DATA-- +return [] +--EXPECT-- +OK foo +OK bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/with_for_tag.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/with_for_tag.test new file mode 100644 index 000000000..4453880b5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/with_for_tag.test @@ -0,0 +1,13 @@ +--TEST-- +"apply" tag applies the filter on "for" tags +--TEMPLATE-- +{% apply upper %} +{% for item in items %} +{{ item }} +{% endfor %} +{% endapply %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- +A +B diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/with_if_tag.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/with_if_tag.test new file mode 100644 index 000000000..ca7a592cb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/apply/with_if_tag.test @@ -0,0 +1,29 @@ +--TEST-- +"apply" tag applies the filter on "if" tags +--TEMPLATE-- +{% apply upper %} +{% if items %} +{{ items|join(', ') }} +{% endif %} + +{% if items.3 is defined %} +FOO +{% else %} +{{ items.1 }} +{% endif %} + +{% if items.3 is defined %} +FOO +{% elseif items.1 %} +{{ items.0 }} +{% endif %} + +{% endapply %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- +A, B + +B + +A diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/basic.test new file mode 100644 index 000000000..7b037c7af --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/basic.test @@ -0,0 +1,22 @@ +--TEST-- +"autoescape" tag applies escaping on its children +--TEMPLATE-- +{% autoescape %} +{{ var }}
    +{% endautoescape %} +{% autoescape 'html' %} +{{ var }}
    +{% endautoescape %} +{% autoescape false %} +{{ var }}
    +{% endautoescape %} +{% autoescape false %} +{{ var }}
    +{% endautoescape %} +--DATA-- +return ['var' => '
    '] +--EXPECT-- +<br />
    +<br />
    +

    +

    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/blocks.test new file mode 100644 index 000000000..292e1b416 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/blocks.test @@ -0,0 +1,12 @@ +--TEST-- +"autoescape" tag applies escaping on embedded blocks +--TEMPLATE-- +{% autoescape 'html' %} + {% block foo %} + {{ var }} + {% endblock %} +{% endautoescape %} +--DATA-- +return ['var' => '
    '] +--EXPECT-- +<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/double_escaping.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/double_escaping.test new file mode 100644 index 000000000..1724b4878 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/double_escaping.test @@ -0,0 +1,10 @@ +--TEST-- +"autoescape" tag does not double-escape +--TEMPLATE-- +{% autoescape 'html' %} +{{ var|escape }} +{% endautoescape %} +--DATA-- +return ['var' => '
    '] +--EXPECT-- +<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/functions.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/functions.test new file mode 100644 index 000000000..170e7074e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/functions.test @@ -0,0 +1,83 @@ +--TEST-- +"autoescape" tag applies escaping after calling functions +--TEMPLATE-- + +autoescape false +{% autoescape false %} + +safe_br +{{ safe_br() }} + +unsafe_br +{{ unsafe_br() }} + +{% endautoescape %} + +autoescape 'html' +{% autoescape 'html' %} + +safe_br +{{ safe_br() }} + +unsafe_br +{{ unsafe_br() }} + +unsafe_br()|raw +{{ (unsafe_br())|raw }} + +safe_br()|escape +{{ (safe_br())|escape }} + +safe_br()|raw +{{ (safe_br())|raw }} + +unsafe_br()|escape +{{ (unsafe_br())|escape }} + +{% endautoescape %} + +autoescape js +{% autoescape 'js' %} + +safe_br +{{ safe_br() }} + +{% endautoescape %} +--DATA-- +return [] +--EXPECT-- + +autoescape false + +safe_br +
    + +unsafe_br +
    + + +autoescape 'html' + +safe_br +
    + +unsafe_br +<br /> + +unsafe_br()|raw +
    + +safe_br()|escape +<br /> + +safe_br()|raw +
    + +unsafe_br()|escape +<br /> + + +autoescape js + +safe_br +\u003Cbr\u0020\/\u003E diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/literal.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/literal.test new file mode 100644 index 000000000..3d8d4f8fd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/literal.test @@ -0,0 +1,87 @@ +--TEST-- +"autoescape" tag does not apply escaping on literals +--TEMPLATE-- +{% autoescape 'html' %} + +1. Simple literal +{{ "
    " }} + +2. Conditional expression with only literals +{{ true ? "
    " : "
    " }} + +3. Conditional expression with a variable +{{ true ? "
    " : someVar }} +{{ false ? "
    " : someVar }} +{{ true ? someVar : "
    " }} +{{ false ? someVar : "
    " }} + +4. Nested conditionals with only literals +{{ true ? (true ? "
    " : "
    ") : "\n" }} + +5. Nested conditionals with a variable +{{ true ? (true ? "
    " : someVar) : "\n" }} +{{ true ? (false ? "
    " : someVar) : "\n" }} +{{ true ? (true ? someVar : "
    ") : "\n" }} +{{ true ? (false ? someVar : "
    ") : "\n" }} +{{ false ? "\n" : (true ? someVar : "
    ") }} +{{ false ? "\n" : (false ? someVar : "
    ") }} + +6. Nested conditionals with a variable marked safe +{{ true ? (true ? "
    " : someVar|raw) : "\n" }} +{{ true ? (false ? "
    " : someVar|raw) : "\n" }} +{{ true ? (true ? someVar|raw : "
    ") : "\n" }} +{{ true ? (false ? someVar|raw : "
    ") : "\n" }} +{{ false ? "\n" : (true ? someVar|raw : "
    ") }} +{{ false ? "\n" : (false ? someVar|raw : "
    ") }} + +7. Without then clause +{{ "
    " ?: someVar }} +{{ someFalseVar ?: "
    " }} + +8. NullCoalesce +{{ aaaa ?? "
    " }} +{{ "
    " ?? someVar }} + +{% endautoescape %} +--DATA-- +return ['someVar' => '
    ', 'someFalseVar' => false] +--EXPECT-- + +1. Simple literal +
    + +2. Conditional expression with only literals +
    + +3. Conditional expression with a variable +
    +<br /> +<br /> +
    + +4. Nested conditionals with only literals +
    + +5. Nested conditionals with a variable +
    +<br /> +<br /> +
    +<br /> +
    + +6. Nested conditionals with a variable marked safe +
    +
    +
    +
    +
    +
    + +7. Without then clause +
    +
    + +8. NullCoalesce +
    +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/nested.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/nested.test new file mode 100644 index 000000000..0d88c7e3a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/nested.test @@ -0,0 +1,26 @@ +--TEST-- +"autoescape" tags can be nested at will +--TEMPLATE-- +{{ var }} +{% autoescape 'html' %} + {{ var }} + {% autoescape false %} + {{ var }} + {% autoescape 'html' %} + {{ var }} + {% endautoescape %} + {{ var }} + {% endautoescape %} + {{ var }} +{% endautoescape %} +{{ var }} +--DATA-- +return ['var' => '
    '] +--EXPECT-- +<br /> + <br /> +
    + <br /> +
    + <br /> +<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/objects.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/objects.test new file mode 100644 index 000000000..9d959b22b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/objects.test @@ -0,0 +1,26 @@ +--TEST-- +"autoescape" tag applies escaping to object method calls +--TEMPLATE-- +{% autoescape 'html' %} +{{ user.name }} +{{ user.name|lower }} +{{ user }} +{% endautoescape %} +--DATA-- +class UserForAutoEscapeTest +{ + public function getName() + { + return 'Fabien
    '; + } + + public function __toString() + { + return 'Fabien
    '; + } +} +return ['user' => new UserForAutoEscapeTest()] +--EXPECT-- +Fabien<br /> +fabien<br /> +Fabien<br /> diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/raw.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/raw.test new file mode 100644 index 000000000..187327c80 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/raw.test @@ -0,0 +1,10 @@ +--TEST-- +"autoescape" tag does not escape when raw is used as a filter +--TEMPLATE-- +{% autoescape 'html' %} +{{ var|raw }} +{% endautoescape %} +--DATA-- +return ['var' => '
    '] +--EXPECT-- +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/strategy.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/strategy.test new file mode 100644 index 000000000..9a0137ee2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/strategy.test @@ -0,0 +1,11 @@ +--TEST-- +"autoescape" tag accepts an escaping strategy +--TEMPLATE-- +{% autoescape 'js' %}{{ var }}{% endautoescape %} + +{% autoescape 'html' %}{{ var }}{% endautoescape %} +--DATA-- +return ['var' => '
    "'] +--EXPECT-- +\u003Cbr\u0020\/\u003E\u0022 +<br />" diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/type.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/type.test new file mode 100644 index 000000000..9ae8d7bf9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/type.test @@ -0,0 +1,69 @@ +--TEST-- +escape types +--TEMPLATE-- + +1. autoescape 'html' |escape('js') + +{% autoescape 'html' %} + +{% endautoescape %} + +2. autoescape 'html' |escape('js') + +{% autoescape 'html' %} + +{% endautoescape %} + +3. autoescape 'js' |escape('js') + +{% autoescape 'js' %} + +{% endautoescape %} + +4. no escape + +{% autoescape false %} + +{% endautoescape %} + +5. |escape('js')|escape('html') + +{% autoescape false %} + +{% endautoescape %} + +6. autoescape 'html' |escape('js')|escape('html') + +{% autoescape 'html' %} + +{% endautoescape %} + +--DATA-- +return ['msg' => "<>\n'\""] +--EXPECT-- + +1. autoescape 'html' |escape('js') + + + +2. autoescape 'html' |escape('js') + + + +3. autoescape 'js' |escape('js') + + + +4. no escape + + + +5. |escape('js')|escape('html') + + + +6. autoescape 'html' |escape('js')|escape('html') + + + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_filters.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_filters.test new file mode 100644 index 000000000..f97105bbd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_filters.test @@ -0,0 +1,131 @@ +--TEST-- +"autoescape" tag applies escaping after calling filters +--TEMPLATE-- +{% autoescape 'html' %} + +(escape_and_nl2br is an escaper filter) + +1. Don't escape escaper filter output +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is not escaped ) +{{ var|escape_and_nl2br }} + +2. Don't escape escaper filter output +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is not escaped, |raw is redundant ) +{{ var|escape_and_nl2br|raw }} + +3. Explicit escape +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is explicitly escaped by |escape ) +{{ var|escape_and_nl2br|escape }} + +4. Escape non-escaper filter output +( var is upper-cased by |upper, + the output is auto-escaped ) +{{ var|upper }} + +5. Escape if last filter is not an escaper +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is upper-cased by |upper, + the output is auto-escaped as |upper is not an escaper ) +{{ var|escape_and_nl2br|upper }} + +6. Don't escape escaper filter output +( var is upper cased by upper, + the output is escaped by |escape_and_nl2br, line-breaks are added, + the output is not escaped as |escape_and_nl2br is an escaper ) +{{ var|upper|escape_and_nl2br }} + +7. Escape if last filter is not an escaper +( the output of |format is "" ~ var ~ "", + the output is auto-escaped ) +{{ "%s"|format(var) }} + +8. Escape if last filter is not an escaper +( the output of |format is "" ~ var ~ "", + |raw is redundant, + the output is auto-escaped ) +{{ "%s"|raw|format(var) }} + +9. Don't escape escaper filter output +( the output of |format is "" ~ var ~ "", + the output is not escaped due to |raw filter at the end ) +{{ "%s"|format(var)|raw }} + +10. Don't escape escaper filter output +( the output of |format is "" ~ var ~ "", + the output is not escaped due to |raw filter at the end, + the |raw filter on var is redundant ) +{{ "%s"|format(var|raw)|raw }} + +{% endautoescape %} +--DATA-- +return ['var' => "\nTwig"] +--EXPECT-- + +(escape_and_nl2br is an escaper filter) + +1. Don't escape escaper filter output +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is not escaped ) +<Fabien>
    +Twig + +2. Don't escape escaper filter output +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is not escaped, |raw is redundant ) +<Fabien>
    +Twig + +3. Explicit escape +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is explicitly escaped by |escape ) +&lt;Fabien&gt;<br /> +Twig + +4. Escape non-escaper filter output +( var is upper-cased by |upper, + the output is auto-escaped ) +<FABIEN> +TWIG + +5. Escape if last filter is not an escaper +( var is escaped by |escape_and_nl2br, line-breaks are added, + the output is upper-cased by |upper, + the output is auto-escaped as |upper is not an escaper ) +&LT;FABIEN&GT;<BR /> +TWIG + +6. Don't escape escaper filter output +( var is upper cased by upper, + the output is escaped by |escape_and_nl2br, line-breaks are added, + the output is not escaped as |escape_and_nl2br is an escaper ) +<FABIEN>
    +TWIG + +7. Escape if last filter is not an escaper +( the output of |format is "" ~ var ~ "", + the output is auto-escaped ) +<b><Fabien> +Twig</b> + +8. Escape if last filter is not an escaper +( the output of |format is "" ~ var ~ "", + |raw is redundant, + the output is auto-escaped ) +<b><Fabien> +Twig</b> + +9. Don't escape escaper filter output +( the output of |format is "" ~ var ~ "", + the output is not escaped due to |raw filter at the end ) + +Twig + +10. Don't escape escaper filter output +( the output of |format is "" ~ var ~ "", + the output is not escaped due to |raw filter at the end, + the |raw filter on var is redundant ) + +Twig diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_filters_arguments.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_filters_arguments.test new file mode 100644 index 000000000..50f72d83a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_filters_arguments.test @@ -0,0 +1,23 @@ +--TEST-- +"autoescape" tag do not applies escaping on filter arguments +--TEMPLATE-- +{% autoescape 'html' %} +{{ var|nl2br("
    ") }} +{{ var|nl2br("
    "|escape) }} +{{ var|nl2br(sep) }} +{{ var|nl2br(sep|raw) }} +{{ var|nl2br(sep|escape) }} +{% endautoescape %} +--DATA-- +return ['var' => "\nTwig", 'sep' => '
    '] +--EXPECT-- +<Fabien>
    +Twig +<Fabien><br /> +Twig +<Fabien>
    +Twig +<Fabien>
    +Twig +<Fabien><br /> +Twig diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_pre_escape_filters.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_pre_escape_filters.test new file mode 100644 index 000000000..c9c738055 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_pre_escape_filters.test @@ -0,0 +1,68 @@ +--TEST-- +"autoescape" tag applies escaping after calling filters, and before calling pre_escape filters +--TEMPLATE-- +{% autoescape 'html' %} + +(nl2br is pre_escaped for "html" and declared safe for "html") + +1. Pre-escape and don't post-escape +( var|escape|nl2br ) +{{ var|nl2br }} + +2. Don't double-pre-escape +( var|escape|nl2br ) +{{ var|escape|nl2br }} + +3. Don't escape safe values +( var|raw|nl2br ) +{{ var|raw|nl2br }} + +4. Don't escape safe values +( var|escape|nl2br|nl2br ) +{{ var|nl2br|nl2br }} + +5. Re-escape values that are escaped for an other contexts +( var|escape_something|escape|nl2br ) +{{ var|escape_something|nl2br }} + +6. Still escape when using filters not declared safe +( var|escape|nl2br|upper|escape ) +{{ var|nl2br|upper }} + +{% endautoescape %} +--DATA-- +return ['var' => "\nTwig"] +--EXPECT-- + +(nl2br is pre_escaped for "html" and declared safe for "html") + +1. Pre-escape and don't post-escape +( var|escape|nl2br ) +<Fabien>
    +Twig + +2. Don't double-pre-escape +( var|escape|nl2br ) +<Fabien>
    +Twig + +3. Don't escape safe values +( var|raw|nl2br ) +
    +Twig + +4. Don't escape safe values +( var|escape|nl2br|nl2br ) +<Fabien>

    +Twig + +5. Re-escape values that are escaped for an other contexts +( var|escape_something|escape|nl2br ) +<FABIEN>
    +TWIG + +6. Still escape when using filters not declared safe +( var|escape|nl2br|upper|escape ) +&LT;FABIEN&GT;<BR /> +TWIG + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test new file mode 100644 index 000000000..c764d434b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/autoescape/with_preserves_safety_filters.test @@ -0,0 +1,50 @@ +--TEST-- +"autoescape" tag handles filters preserving the safety +--TEMPLATE-- +{% autoescape 'html' %} + +(preserves_safety is preserving safety for "html") + +1. Unsafe values are still unsafe +( var|preserves_safety|escape ) +{{ var|preserves_safety }} + +2. Safe values are still safe +( var|escape|preserves_safety ) +{{ var|escape|preserves_safety }} + +3. Re-escape values that are escaped for an other contexts +( var|escape_something|preserves_safety|escape ) +{{ var|escape_something|preserves_safety }} + +4. Still escape when using filters not declared safe +( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape ) +{{ var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'}) }} + +{% endautoescape %} +--DATA-- +return ['var' => "\nTwig"] +--EXPECT-- + +(preserves_safety is preserving safety for "html") + +1. Unsafe values are still unsafe +( var|preserves_safety|escape ) +<FABIEN> +TWIG + +2. Safe values are still safe +( var|escape|preserves_safety ) +<FABIEN> +TWIG + +3. Re-escape values that are escaped for an other contexts +( var|escape_something|preserves_safety|escape ) +<FABIEN> +TWIG + +4. Still escape when using filters not declared safe +( var|escape|preserves_safety|replace({'FABIEN': 'FABPOT'})|escape ) +&LT;FABPOT&GT; +TWIG + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/basic.test new file mode 100644 index 000000000..988b09ce8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/basic.test @@ -0,0 +1,11 @@ +--TEST-- +"block" tag +--TEMPLATE-- +{% block title1 %}FOO{% endblock %} +{% block title2 foo|lower %} +--TEMPLATE(foo.twig)-- +{% block content %}{% endblock %} +--DATA-- +return ['foo' => 'bar'] +--EXPECT-- +FOObar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/block_unique_name.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/block_unique_name.test new file mode 100644 index 000000000..3009f8b42 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/block_unique_name.test @@ -0,0 +1,11 @@ +--TEST-- +"block" tag +--TEMPLATE-- +{% block content %} + {% block content %} + {% endblock %} +{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: The block 'content' has already been defined line 2 in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/conditional_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/conditional_block.test new file mode 100644 index 000000000..04bf601e7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/conditional_block.test @@ -0,0 +1,9 @@ +--TEST-- +conditional "block" tag +--TEMPLATE-- +{% if false %}{% block foo %}FOO{% endblock %}{% endif %} +{% if true %}{% block bar %}BAR{% endblock %}{% endif %} +--DATA-- +return [] +--EXPECT-- +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/special_chars.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/special_chars.test new file mode 100644 index 000000000..e8e240eb7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/block/special_chars.test @@ -0,0 +1,10 @@ +--TEST-- +"§" special chars in a block name +--TEMPLATE-- +{% block § %} +§ +{% endblock § %} +--DATA-- +return [] +--EXPECT-- +§ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/block.legacy.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/block.legacy.test new file mode 100644 index 000000000..53729dd1f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/block.legacy.test @@ -0,0 +1,20 @@ +--TEST-- +Deprecating a block with "deprecated" tag +--TEMPLATE-- +{% use 'greeting.twig' %} + +{{ block('welcome') }} + +--TEMPLATE(greeting.twig)-- +{% block welcome %} + {% deprecated 'The "welcome" block is deprecated, use "hello" instead.' %} + {{ block('hello') }} +{% endblock %} + +{% block hello %} +Hello Fabien +{% endblock %} +--DATA-- +return [] +--EXPECT-- + Hello Fabien diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/macro.legacy.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/macro.legacy.test new file mode 100644 index 000000000..5cc48dd87 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/macro.legacy.test @@ -0,0 +1,21 @@ +--TEST-- +Deprecating a macro with "deprecated" tag +--TEMPLATE-- +{% import 'greeting.twig' as greeting %} + +{{ greeting.welcome('Fabien') }} + +--TEMPLATE(greeting.twig)-- +{% macro welcome(name) %} + {% deprecated 'The "welcome" macro is deprecated, use "hello" instead.' %} + {% import _self as self %} + {{ self.hello(name) }} +{% endmacro %} + +{% macro hello(name) %} +Hello {{ name }} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + Hello Fabien diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/template.legacy.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/template.legacy.test new file mode 100644 index 000000000..7f786d57f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/template.legacy.test @@ -0,0 +1,12 @@ +--TEST-- +Deprecating a template with "deprecated" tag +--TEMPLATE-- +{% extends 'greeting.twig' %} + +{% deprecated 'The "index.twig" template is deprecated, use "greeting.twig" instead.' %} +--TEMPLATE(greeting.twig)-- +Hello Fabien +--DATA-- +return [] +--EXPECT-- +Hello Fabien diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/with_package.legacy.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/with_package.legacy.test new file mode 100644 index 000000000..877643f01 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/with_package.legacy.test @@ -0,0 +1,10 @@ +--TEST-- +Deprecating a template with "deprecated" tag +--TEMPLATE-- +{% deprecated 'The "index.twig" template is deprecated, use "greeting.twig" instead.' package="foo/bar" %} + +Hello Fabien +--DATA-- +return [] +--EXPECT-- +Hello Fabien diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/with_package_version.legacy.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/with_package_version.legacy.test new file mode 100644 index 000000000..68722994e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/deprecated/with_package_version.legacy.test @@ -0,0 +1,10 @@ +--TEST-- +Deprecating a template with "deprecated" tag +--TEMPLATE-- +{% deprecated 'The "index.twig" template is deprecated, use "greeting.twig" instead.' package="foo/bar" version=1.1 %} + +Hello Fabien +--DATA-- +return [] +--EXPECT-- +Hello Fabien diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/basic.test new file mode 100644 index 000000000..16781e418 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/basic.test @@ -0,0 +1,35 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +FOO +{% embed "foo.twig" %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} +{% endembed %} + +BAR +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return [] +--EXPECT-- +FOO + +A + block1 + + block1extended + B + block2 +C +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/complex_dynamic_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/complex_dynamic_parent.test new file mode 100644 index 000000000..b799a8c95 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/complex_dynamic_parent.test @@ -0,0 +1,35 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +FOO +{% embed foo ~ ".twig" %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} +{% endembed %} + +BAR +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return ['foo' => 'foo'] +--EXPECT-- +FOO + +A + block1 + + block1extended + B + block2 +C +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/dynamic_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/dynamic_parent.test new file mode 100644 index 000000000..6f0879e28 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/dynamic_parent.test @@ -0,0 +1,35 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +FOO +{% embed foo %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} +{% endembed %} + +BAR +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return ['foo' => 'foo.twig'] +--EXPECT-- +FOO + +A + block1 + + block1extended + B + block2 +C +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/error_line.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/error_line.test new file mode 100644 index 000000000..b1c6c85e6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/error_line.test @@ -0,0 +1,16 @@ +--TEST-- +"embed" tag +--TEMPLATE(index.twig)-- +FOO +{% embed "foo.twig" %} + {% block c1 %} + {{ nothing }} + {% endblock %} +{% endembed %} +BAR +--TEMPLATE(foo.twig)-- +{% block c1 %}{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "nothing" does not exist in "index.twig" at line 5. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/multiple.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/multiple.test new file mode 100644 index 000000000..9f7b52f1d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/multiple.test @@ -0,0 +1,50 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +FOO +{% embed "foo.twig" %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} +{% endembed %} + +{% embed "foo.twig" %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} +{% endembed %} + +BAR +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return [] +--EXPECT-- +FOO + +A + block1 + + block1extended + B + block2 +C + +A + block1 + + block1extended + B + block2 +C +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/nested.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/nested.test new file mode 100644 index 000000000..9f3372336 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/nested.test @@ -0,0 +1,42 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +{% embed "foo.twig" %} + {% block c1 %} + {{ parent() }} + {% embed "foo.twig" %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} + {% endembed %} + + {% endblock %} +{% endembed %} +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return [] +--EXPECT-- +A + block1 + + +A + block1 + + block1extended + B + block2 +C + B + block2 +C diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/with_extends.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/with_extends.test new file mode 100644 index 000000000..ce726ac00 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/embed/with_extends.test @@ -0,0 +1,60 @@ +--TEST-- +"embed" tag +--TEMPLATE-- +{% extends "base.twig" %} + +{% block c1 %} + {{ parent() }} + blockc1baseextended +{% endblock %} + +{% block c2 %} + {{ parent() }} + + {% embed "foo.twig" %} + {% block c1 %} + {{ parent() }} + block1extended + {% endblock %} + {% endembed %} + {{ parent() }} +{% endblock %} +--TEMPLATE(base.twig)-- +A +{% block c1 %} + blockc1base +{% endblock %} +{% block c2 %} + blockc2base +{% endblock %} +B +--TEMPLATE(foo.twig)-- +A +{% block c1 %} + block1 +{% endblock %} +B +{% block c2 %} + block2 +{% endblock %} +C +--DATA-- +return [] +--EXPECT-- +A + blockc1base + + blockc1baseextended + blockc2base + + + +A + block1 + + block1extended + B + block2 +C blockc2base + +B \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/context.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/context.test new file mode 100644 index 000000000..3cdd575c8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/context.test @@ -0,0 +1,18 @@ +--TEST-- +"for" tag keeps the context safe +--TEMPLATE-- +{% for item in items %} + {% for item in items %} + * {{ item }} + {% endfor %} + * {{ item }} +{% endfor %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- + * a + * b + * a + * a + * b + * b diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/else.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/else.test new file mode 100644 index 000000000..86ec9c28d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/else.test @@ -0,0 +1,23 @@ +--TEST-- +"for" tag can use an "else" clause +--TEMPLATE-- +{% for item in items %} + * {{ item }} +{% else %} + no item +{% endfor %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- + * a + * b +--DATA-- +return ['items' => []] +--EXPECT-- + no item +--DATA-- +return [] +--CONFIG-- +return ['strict_variables' => false] +--EXPECT-- + no item diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/inner_variables.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/inner_variables.test new file mode 100644 index 000000000..e1ad3c735 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/inner_variables.test @@ -0,0 +1,17 @@ +--TEST-- +"for" tag does not reset inner variables +--TEMPLATE-- +{% for i in 1..2 %} + {% for j in 0..2 %} + {{k}}{% set k = k+1 %} {{ loop.parent.loop.index }} + {% endfor %} +{% endfor %} +--DATA-- +return ['k' => 0] +--EXPECT-- + 0 1 + 1 1 + 2 1 + 3 2 + 4 2 + 5 2 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/keys.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/keys.test new file mode 100644 index 000000000..92135575f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/keys.test @@ -0,0 +1,11 @@ +--TEST-- +"for" tag can iterate over keys +--TEMPLATE-- +{% for key in items|keys %} + * {{ key }} +{% endfor %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- + * 0 + * 1 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/keys_and_values.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/keys_and_values.test new file mode 100644 index 000000000..ab39ddf21 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/keys_and_values.test @@ -0,0 +1,11 @@ +--TEST-- +"for" tag can iterate over keys and values +--TEMPLATE-- +{% for key, item in items %} + * {{ key }}/{{ item }} +{% endfor %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- + * 0/a + * 1/b diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/loop_context.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/loop_context.test new file mode 100644 index 000000000..56a60c2e6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/loop_context.test @@ -0,0 +1,19 @@ +--TEST-- +"for" tag adds a loop variable to the context +--TEMPLATE-- +{% for item in items %} + * {{ loop.index }}/{{ loop.index0 }} + * {{ loop.revindex }}/{{ loop.revindex0 }} + * {{ loop.first }}/{{ loop.last }}/{{ loop.length }} + +{% endfor %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- + * 1/0 + * 2/1 + * 1//2 + + * 2/1 + * 1/0 + * /1/2 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/loop_context_local.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/loop_context_local.test new file mode 100644 index 000000000..58e5a9b34 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/loop_context_local.test @@ -0,0 +1,10 @@ +--TEST-- +"for" tag adds a loop variable to the context locally +--TEMPLATE-- +{% for item in items %} +{% endfor %} +{% if loop is not defined %}WORKS{% endif %} +--DATA-- +return ['items' => []] +--EXPECT-- +WORKS diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/nested_else.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/nested_else.test new file mode 100644 index 000000000..df1bb07ac --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/nested_else.test @@ -0,0 +1,17 @@ +--TEST-- +"for" tag can use an "else" clause +--TEMPLATE-- +{% for item in items %} + {% for item in items1 %} + * {{ item }} + {% else %} + no {{ item }} + {% endfor %} +{% else %} + no item1 +{% endfor %} +--DATA-- +return ['items' => ['a', 'b'], 'items1' => []] +--EXPECT-- +no a + no b diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/objects.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/objects.test new file mode 100644 index 000000000..1de7ab27d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/objects.test @@ -0,0 +1,45 @@ +--TEST-- +"for" tag iterates over iterable objects +--TEMPLATE-- +{% for item in items %} + * {{ item }} + * {{ loop.index }}/{{ loop.index0 }} + * {{ loop.first }} + +{% endfor %} + +{% for key, value in items %} + * {{ key }}/{{ value }} +{% endfor %} + +{% for key in items|keys %} + * {{ key }} +{% endfor %} +--DATA-- +class ItemsIterator implements \Iterator +{ + protected $values = ['foo' => 'bar', 'bar' => 'foo']; + #[\ReturnTypeWillChange] + public function current() { return current($this->values); } + #[\ReturnTypeWillChange] + public function key() { return key($this->values); } + public function next(): void { next($this->values); } + public function rewind(): void { reset($this->values); } + public function valid(): bool { return false !== current($this->values); } +} +return ['items' => new ItemsIterator()] +--EXPECT-- + * bar + * 1/0 + * 1 + + * foo + * 2/1 + * + + + * foo/bar + * bar/foo + + * foo + * bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/objects_countable.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/objects_countable.test new file mode 100644 index 000000000..dc215f3a6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/objects_countable.test @@ -0,0 +1,49 @@ +--TEST-- +"for" tag iterates over iterable and countable objects +--TEMPLATE-- +{% for item in items %} + * {{ item }} + * {{ loop.index }}/{{ loop.index0 }} + * {{ loop.revindex }}/{{ loop.revindex0 }} + * {{ loop.first }}/{{ loop.last }}/{{ loop.length }} + +{% endfor %} + +{% for key, value in items %} + * {{ key }}/{{ value }} +{% endfor %} + +{% for key in items|keys %} + * {{ key }} +{% endfor %} +--DATA-- +class ItemsIteratorCountable implements \Iterator, \Countable +{ + protected $values = ['foo' => 'bar', 'bar' => 'foo']; + #[\ReturnTypeWillChange] + public function current() { return current($this->values); } + #[\ReturnTypeWillChange] + public function key() { return key($this->values); } + public function next(): void { next($this->values); } + public function rewind(): void { reset($this->values); } + public function valid(): bool { return false !== current($this->values); } + public function count(): int { return count($this->values); } +} +return ['items' => new ItemsIteratorCountable()] +--EXPECT-- + * bar + * 1/0 + * 2/1 + * 1//2 + + * foo + * 2/1 + * 1/0 + * /1/2 + + + * foo/bar + * bar/foo + + * foo + * bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/recursive.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/recursive.test new file mode 100644 index 000000000..3b677d60f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/recursive.test @@ -0,0 +1,18 @@ +--TEST-- +"for" tags can be nested +--TEMPLATE-- +{% for key, item in items %} +* {{ key }} ({{ loop.length }}): +{% for value in item %} + * {{ value }} ({{ loop.length }}) +{% endfor %} +{% endfor %} +--DATA-- +return ['items' => ['a' => ['a1', 'a2', 'a3'], 'b' => ['b1']]] +--EXPECT-- +* a (2): + * a1 (3) + * a2 (3) + * a3 (3) +* b (2): + * b1 (1) diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/values.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/values.test new file mode 100644 index 000000000..384c41b28 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/for/values.test @@ -0,0 +1,11 @@ +--TEST-- +"for" tag iterates over item values +--TEMPLATE-- +{% for item in items %} + * {{ item }} +{% endfor %} +--DATA-- +return ['items' => ['a', 'b']] +--EXPECT-- + * a + * b diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/from.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/from.test new file mode 100644 index 000000000..1d3c9e2b5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/from.test @@ -0,0 +1,14 @@ +--TEST-- +global variables +--TEMPLATE-- +{% include "included.twig" %} +{% from "included.twig" import foobar %} +{{ foobar() }} +--TEMPLATE(included.twig)-- +{% macro foobar() %} +called foobar +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +called foobar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/basic.test new file mode 100644 index 000000000..a02165e53 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/basic.test @@ -0,0 +1,22 @@ +--TEST-- +"if" creates a condition +--TEMPLATE-- +{% if a is defined %} + {{ a }} +{% elseif b is defined %} + {{ b }} +{% else %} + NOTHING +{% endif %} +--DATA-- +return ['a' => 'a'] +--EXPECT-- + a +--DATA-- +return ['b' => 'b'] +--EXPECT-- + b +--DATA-- +return [] +--EXPECT-- + NOTHING diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/empty_body.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/empty_body.test new file mode 100644 index 000000000..ba49f6e1c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/empty_body.test @@ -0,0 +1,32 @@ +--TEST-- +empty "if" body in child template +--TEMPLATE-- +{% extends 'base.twig' %} + +{% set foo = '' %} + +{% if a is defined %} + +{% else %} + {% set foo = 'NOTHING' %} +{% endif %} + +{% if a is defined %} + +{% endif %} + +{% if a is defined %} + {% set foo = 'NOTHING' %} +{% else %} + +{% endif %} + +{% block content %} + {{ foo }} +{% endblock %} +--TEMPLATE(base.twig)-- +{% block content %}{% endblock %} +--DATA-- +return [] +--EXPECT-- + NOTHING diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/expression.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/expression.test new file mode 100644 index 000000000..eb65083c6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/if/expression.test @@ -0,0 +1,22 @@ +--TEST-- +"if" takes an expression as a test +--TEMPLATE-- +{% if a < 2 %} + A1 +{% elseif a > 10 %} + A2 +{% else %} + A3 +{% endif %} +--DATA-- +return ['a' => 1] +--EXPECT-- + A1 +--DATA-- +return ['a' => 12] +--EXPECT-- + A2 +--DATA-- +return ['a' => 7] +--EXPECT-- + A3 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/basic.test new file mode 100644 index 000000000..9982b039d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/basic.test @@ -0,0 +1,16 @@ +--TEST-- +"include" tag +--TEMPLATE-- +FOO +{% include "foo.twig" %} + +BAR +--TEMPLATE(foo.twig)-- +FOOBAR +--DATA-- +return [] +--EXPECT-- +FOO + +FOOBAR +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/expression.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/expression.test new file mode 100644 index 000000000..23db18123 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/expression.test @@ -0,0 +1,16 @@ +--TEST-- +"include" tag allows expressions for the template to include +--TEMPLATE-- +FOO +{% include foo %} + +BAR +--TEMPLATE(foo.twig)-- +FOOBAR +--DATA-- +return ['foo' => 'foo.twig'] +--EXPECT-- +FOO + +FOOBAR +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/ignore_missing.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/ignore_missing.test new file mode 100644 index 000000000..6c8bdb1e9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/ignore_missing.test @@ -0,0 +1,10 @@ +--TEST-- +"include" tag +--TEMPLATE-- +{% include ["foo.twig", "bar.twig"] ignore missing %} +{% include "foo.twig" ignore missing %} +{% include "foo.twig" ignore missing with {} %} +{% include "foo.twig" ignore missing with {} only %} +--DATA-- +return [] +--EXPECT-- diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/ignore_missing_exists.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/ignore_missing_exists.test new file mode 100644 index 000000000..1af012210 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/ignore_missing_exists.test @@ -0,0 +1,11 @@ +--TEST-- +"include" tag +--TEMPLATE-- +{% include "included.twig" ignore missing %} +NOT DISPLAYED +--TEMPLATE(included.twig)-- +{% include "DOES NOT EXIST" %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "DOES NOT EXIST" is not defined in "included.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/include_missing_extends.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/include_missing_extends.test new file mode 100644 index 000000000..d0d1bfe59 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/include_missing_extends.test @@ -0,0 +1,13 @@ +--TEST-- +"include" tag +--TEMPLATE-- +{% include ['bad.twig', 'good.twig'] ignore missing %} +NOT DISPLAYED +--TEMPLATE(bad.twig)-- +{% extends 'DOES NOT EXIST' %} +--TEMPLATE(good.twig)-- +NOT DISPLAYED +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "DOES NOT EXIST" is not defined in "bad.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/missing.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/missing.test new file mode 100644 index 000000000..ac72838e8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/missing.test @@ -0,0 +1,8 @@ +--TEST-- +"include" tag +--TEMPLATE-- +{% include "foo.twig" %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "foo.twig" is not defined in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/missing_nested.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/missing_nested.test new file mode 100644 index 000000000..0ee51b786 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/missing_nested.test @@ -0,0 +1,16 @@ +--TEST-- +"include" tag +--TEMPLATE-- +{% extends "base.twig" %} + +{% block content %} + {{ parent() }} +{% endblock %} +--TEMPLATE(base.twig)-- +{% block content %} + {% include "foo.twig" %} +{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\LoaderError: Template "foo.twig" is not defined in "base.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/only.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/only.test new file mode 100644 index 000000000..8da402f7a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/only.test @@ -0,0 +1,20 @@ +--TEST-- +"include" tag accept variables and only +--TEMPLATE-- +{% include "foo.twig" %} +{% include "foo.twig" only %} +{% include "foo.twig" with vars1 %} +{% include "foo.twig" with vars1 only %} +{% include "foo.twig" with vars2 %} +{% include "foo.twig" with vars2 only %} +--TEMPLATE(foo.twig)-- +{% for k, v in _context %}{{ k }},{% endfor %} +--DATA-- +return ['vars1' => ['foo1' => 'bar'], 'vars2' => new ArrayObject(['foo2' => 'bar'])] +--EXPECT-- +vars1,vars2,global,_parent, +global,_parent, +vars1,vars2,global,foo1,_parent, +foo1,global,_parent, +vars1,vars2,global,foo2,_parent, +foo2,global,_parent, diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/template_instance.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/template_instance.test new file mode 100644 index 000000000..4fb862a17 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/template_instance.test @@ -0,0 +1,10 @@ +--TEST-- +"include" tag accepts \Twig\TemplateWrapper instance +--TEMPLATE-- +{% include foo %} FOO +--TEMPLATE(foo.twig)-- +BAR +--DATA-- +return ['foo' => $twig->load('foo.twig')] +--EXPECT-- +BAR FOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/templates_as_array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/templates_as_array.test new file mode 100644 index 000000000..38063952d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/templates_as_array.test @@ -0,0 +1,12 @@ +--TEST-- +"include" tag +--TEMPLATE-- +{% include ["foo.twig", "bar.twig"] %} +{% include ["bar.twig", "foo.twig"] %} +--TEMPLATE(foo.twig)-- +foo +--DATA-- +return [] +--EXPECT-- +foo +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/with_variables.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/with_variables.test new file mode 100644 index 000000000..45a05199e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/include/with_variables.test @@ -0,0 +1,14 @@ +--TEST-- +"include" tag accept variables +--TEMPLATE-- +{% include "foo.twig" with {'foo': 'bar'} %} +{% include "foo.twig" with vars1 %} +{% include "foo.twig" with vars2 %} +--TEMPLATE(foo.twig)-- +{{ foo }} +--DATA-- +return ['vars1' => ['foo' => 'bar'], 'vars2' => new ArrayObject(['foo' => 'bar'])] +--EXPECT-- +bar +bar +bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/basic.test new file mode 100644 index 000000000..703b61be1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/basic.test @@ -0,0 +1,14 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends "foo.twig" %} + +{% block content %} +FOO +{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}{% endblock %} +--DATA-- +return [] +--EXPECT-- +FOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/block_expr.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/block_expr.test new file mode 100644 index 000000000..0b82d4cf2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/block_expr.test @@ -0,0 +1,30 @@ +--TEST-- +block_expr +--TEMPLATE-- +{% extends "base.twig" %} + +{% block element -%} + Element: + {{- parent() -}} +{% endblock %} +--TEMPLATE(base.twig)-- +{% block element -%} +
    + {%- if item.children is defined %} + {%- for item in item.children %} + {{- block('element') -}} + {% endfor %} + {%- endif -%} +
    +{%- endblock %} +--DATA-- +return [ + 'item' => [ + 'children' => [ + null, + null, + ] + ] +] +--EXPECT-- +Element:
    Element:
    Element:
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/block_expr2.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/block_expr2.test new file mode 100644 index 000000000..18f660411 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/block_expr2.test @@ -0,0 +1,32 @@ +--TEST-- +block_expr2 +--TEMPLATE-- +{% extends "base2.twig" %} + +{% block element -%} + Element: + {{- parent() -}} +{% endblock %} +--TEMPLATE(base2.twig)-- +{% extends "base.twig" %} +--TEMPLATE(base.twig)-- +{% block element -%} +
    + {%- if item.children is defined %} + {%- for item in item.children %} + {{- block('element') -}} + {% endfor %} + {%- endif -%} +
    +{%- endblock %} +--DATA-- +return [ + 'item' => [ + 'children' => [ + null, + null, + ] + ] +] +--EXPECT-- +Element:
    Element:
    Element:
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/capturing_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/capturing_block.test new file mode 100644 index 000000000..91db2c22f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/capturing_block.test @@ -0,0 +1,17 @@ +--TEST-- +capturing "block" tag with "extends" tag +--TEMPLATE-- +{% extends "layout.twig" %} + +{% set foo %} + {%- block content %}FOO{% endblock %} +{% endset %} + +{% block content1 %}BAR{{ foo }}{% endblock %} +--TEMPLATE(layout.twig)-- +{% block content %}{% endblock %} +{% block content1 %}{% endblock %} +--DATA-- +return [] +--EXPECT-- +FOOBARFOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/conditional.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/conditional.test new file mode 100644 index 000000000..bd28248d1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/conditional.test @@ -0,0 +1,14 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends standalone ? foo : 'bar.twig' %} + +{% block content %}{{ parent() }}FOO{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}FOO{% endblock %} +--TEMPLATE(bar.twig)-- +{% block content %}BAR{% endblock %} +--DATA-- +return ['foo' => 'foo.twig', 'standalone' => true] +--EXPECT-- +FOOFOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/conditional_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/conditional_block.test new file mode 100644 index 000000000..0b42212dd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/conditional_block.test @@ -0,0 +1,14 @@ +--TEST-- +conditional "block" tag with "extends" tag +--TEMPLATE-- +{% extends "layout.twig" %} + +{% if false %} + {% block content %}FOO{% endblock %} +{% endif %} +--TEMPLATE(layout.twig)-- +{% block content %}{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: A block definition cannot be nested under non-capturing nodes in "index.twig" at line 5. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/dynamic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/dynamic.test new file mode 100644 index 000000000..e6a0f0172 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/dynamic.test @@ -0,0 +1,14 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends foo %} + +{% block content %} +FOO +{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}{% endblock %} +--DATA-- +return ['foo' => 'foo.twig'] +--EXPECT-- +FOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/empty.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/empty.test new file mode 100644 index 000000000..ca11bf20d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/empty.test @@ -0,0 +1,10 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends "foo.twig" %} +--TEMPLATE(foo.twig)-- +{% block content %}FOO{% endblock %} +--DATA-- +return [] +--EXPECT-- +FOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_as_array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_as_array.test new file mode 100644 index 000000000..4d2cb6c65 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_as_array.test @@ -0,0 +1,12 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends ["foo.twig", "bar.twig"] %} +--TEMPLATE(bar.twig)-- +{% block content %} +foo +{% endblock %} +--DATA-- +return [] +--EXPECT-- +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_as_array_with_empty_name.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_as_array_with_empty_name.test new file mode 100644 index 000000000..510865110 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_as_array_with_empty_name.test @@ -0,0 +1,12 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends ["", "bar.twig"] %} +--TEMPLATE(bar.twig)-- +{% block content %} +foo +{% endblock %} +--DATA-- +return [] +--EXPECT-- +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_in_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_in_block.test new file mode 100644 index 000000000..a372ea1c8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_in_block.test @@ -0,0 +1,10 @@ +--TEST-- +"extends" tag in a block +--TEMPLATE-- +{% block foo %} + {% extends "foo.twig" %} +{% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Cannot use "extend" in a block in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_in_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_in_macro.test new file mode 100644 index 000000000..dc87b2a8c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/extends_in_macro.test @@ -0,0 +1,10 @@ +--TEST-- +"extends" tag in a macro +--TEMPLATE-- +{% macro foo() %} + {% extends "foo.twig" %} +{% endmacro %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Cannot use "extend" in a macro in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/multiple.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/multiple.test new file mode 100644 index 000000000..fc25badd3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/multiple.test @@ -0,0 +1,12 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends "layout.twig" %}{% block content %}{{ parent() }}index {% endblock %} +--TEMPLATE(layout.twig)-- +{% extends "base.twig" %}{% block content %}{{ parent() }}layout {% endblock %} +--TEMPLATE(base.twig)-- +{% block content %}base {% endblock %} +--DATA-- +return [] +--EXPECT-- +base layout index diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/multiple_dynamic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/multiple_dynamic.test new file mode 100644 index 000000000..fa887177b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/multiple_dynamic.test @@ -0,0 +1,22 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% set foo = 1 %} +{{ include('parent.twig') }} +{{ include('parent.twig') }} +{% set foo = 2 %} +{{ include('parent.twig') }} +--TEMPLATE(parent.twig)-- +{% extends foo~'_parent.twig' %}{% block content %}{{ parent() }} parent{% endblock %} +--TEMPLATE(1_parent.twig)-- +{% block content %}1{% endblock %} +--TEMPLATE(2_parent.twig)-- +{% block content %}2{% endblock %} +--DATA-- +return [] +--EXPECT-- +1 parent + +1 parent + +2 parent diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_blocks.test new file mode 100644 index 000000000..abea2e9d4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_blocks.test @@ -0,0 +1,22 @@ +--TEST-- +"block" tag +--TEMPLATE-- +{% extends "foo.twig" %} + +{% block content %} + {% block subcontent %} + {% block subsubcontent %} + SUBSUBCONTENT + {% endblock %} + {% endblock %} +{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %} + {% block subcontent %} + SUBCONTENT + {% endblock %} +{% endblock %} +--DATA-- +return [] +--EXPECT-- +SUBSUBCONTENT diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test new file mode 100644 index 000000000..20b7848f8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_blocks_parent_only.test @@ -0,0 +1,15 @@ +--TEST-- +"block" tag +--TEMPLATE-- +{% block content %} + CONTENT + {%- block subcontent -%} + SUBCONTENT + {%- endblock -%} + ENDCONTENT +{% endblock %} +--TEMPLATE(foo.twig)-- +--DATA-- +return [] +--EXPECT-- +CONTENTSUBCONTENTENDCONTENT diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_inheritance.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_inheritance.test new file mode 100644 index 000000000..0b585b480 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/nested_inheritance.test @@ -0,0 +1,16 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends "layout.twig" %} +{% block inside %}INSIDE{% endblock inside %} +--TEMPLATE(layout.twig)-- +{% extends "base.twig" %} +{% block body %} + {% block inside '' %} +{% endblock body %} +--TEMPLATE(base.twig)-- +{% block body '' %} +--DATA-- +return [] +--EXPECT-- +INSIDE diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent.test new file mode 100644 index 000000000..73f4c0ec5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent.test @@ -0,0 +1,12 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends "foo.twig" %} + +{% block content %}{{ parent() }}FOO{{ parent() }}{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}BAR{% endblock %} +--DATA-- +return [] +--EXPECT-- +BARFOOBAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_as_template_wrapper.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_as_template_wrapper.test new file mode 100644 index 000000000..cf257f25d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_as_template_wrapper.test @@ -0,0 +1,12 @@ +--TEST-- +"extends" tag with a parent as a Twig\TemplateWrapper instance +--TEMPLATE-- +{% extends foo %} + +{% block content %}New{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}Default{% endblock %} +--DATA-- +return ['foo' => $twig->load('foo.twig')] +--EXPECT-- +New diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_change.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_change.test new file mode 100644 index 000000000..01bd544f8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_change.test @@ -0,0 +1,16 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends foo ? 'foo.twig' : 'bar.twig' %} +--TEMPLATE(foo.twig)-- +FOO +--TEMPLATE(bar.twig)-- +BAR +--DATA-- +return ['foo' => true] +--EXPECT-- +FOO +--DATA-- +return ['foo' => false] +--EXPECT-- +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_isolation.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_isolation.test new file mode 100644 index 000000000..58a37bd90 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_isolation.test @@ -0,0 +1,20 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends "base.twig" %} +{% block content %}{% include "included.twig" %}{% endblock %} + +{% block footer %}Footer{% endblock %} +--TEMPLATE(included.twig)-- +{% extends "base.twig" %} +{% block content %}Included Content{% endblock %} +--TEMPLATE(base.twig)-- +{% block content %}Default Content{% endblock %} + +{% block footer %}Default Footer{% endblock %} +--DATA-- +return [] +--EXPECT-- +Included Content +Default Footer +Footer diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_nested.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_nested.test new file mode 100644 index 000000000..d4347bac4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_nested.test @@ -0,0 +1,28 @@ +--TEST-- +"extends" tag +--TEMPLATE-- +{% extends "foo.twig" %} + +{% block content %} + {% block inside %} + INSIDE OVERRIDDEN + {% endblock %} + + BEFORE + {{ parent() }} + AFTER +{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %} + BAR +{% endblock %} +--DATA-- +return [] +--EXPECT-- + +INSIDE OVERRIDDEN + + BEFORE + BAR + + AFTER diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_without_extends.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_without_extends.test new file mode 100644 index 000000000..c2025f60c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_without_extends.test @@ -0,0 +1,8 @@ +--TEST-- +"parent" tag +--TEMPLATE-- +{% block content %} + {{ parent() }} +{% endblock %} +--EXCEPTION-- +Twig\Error\SyntaxError: Calling the "parent" function on a template that does not call "extends" or "use" is forbidden in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test new file mode 100644 index 000000000..39882b8b3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/parent_without_extends_but_traits.test @@ -0,0 +1,14 @@ +--TEST-- +"parent" tag +--TEMPLATE-- +{% use 'foo.twig' %} + +{% block content %} + {{ parent() }} +{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}BAR{% endblock %} +--DATA-- +return [] +--EXPECT-- +BAR diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/template_instance.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/template_instance.test new file mode 100644 index 000000000..b9009e5df --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/template_instance.test @@ -0,0 +1,14 @@ +--TEST-- +"extends" tag accepts Twig\Template instance +--TEMPLATE-- +{% extends foo %} + +{% block content %} +{{ parent() }}FOO +{% endblock %} +--TEMPLATE(foo.twig)-- +{% block content %}BAR{% endblock %} +--DATA-- +return ['foo' => $twig->load('foo.twig')] +--EXPECT-- +BARFOO diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/use.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/use.test new file mode 100644 index 000000000..4df32ee9d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/inheritance/use.test @@ -0,0 +1,44 @@ +--TEST-- +"parent" function +--TEMPLATE-- +{% extends "parent.twig" %} + +{% use "use1.twig" %} +{% use "use2.twig" %} + +{% block content_parent %} + {{ parent() }} +{% endblock %} + +{% block content_use1 %} + {{ parent() }} +{% endblock %} + +{% block content_use2 %} + {{ parent() }} +{% endblock %} + +{% block content %} + {{ block('content_use1_only') }} + {{ block('content_use2_only') }} +{% endblock %} +--TEMPLATE(parent.twig)-- +{% block content_parent 'content_parent' %} +{% block content_use1 'content_parent' %} +{% block content_use2 'content_parent' %} +{% block content '' %} +--TEMPLATE(use1.twig)-- +{% block content_use1 'content_use1' %} +{% block content_use2 'content_use1' %} +{% block content_use1_only 'content_use1_only' %} +--TEMPLATE(use2.twig)-- +{% block content_use2 'content_use2' %} +{% block content_use2_only 'content_use2_only' %} +--DATA-- +return [] +--EXPECT-- + content_parent + content_use1 + content_use2 + content_use1_only + content_use2_only diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import.test new file mode 100644 index 000000000..14e0afe00 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import.test @@ -0,0 +1,16 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{{ _self.hello('Fabien') }} + +{% macro hello(name) -%} + Hello {{ _self.up(name) }} +{% endmacro %} + +{% macro up(name) -%} + {{ name|upper }} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +Hello FABIEN diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import_blocks.test new file mode 100644 index 000000000..59b739377 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import_blocks.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% block content %} + {{ _self.hello('Fabien') }} +{% endblock %} + +{% macro hello(name) -%} + Hello {{ _self.up(name) }} +{% endmacro %} + +{% macro up(name) -%} + {{ name|upper }} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +Hello FABIEN diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import_without_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import_without_blocks.test new file mode 100644 index 000000000..79aec5cf1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/auto_import_without_blocks.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% import 'macros' as macro %} +{{ macro.foo() }} +--TEMPLATE(macros)-- +{% macro foo() %} + foo + {{- _self.bar() }} +{% endmacro %} + +{% macro bar() -%} + bar +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +foobar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/basic.test new file mode 100644 index 000000000..ae090f9a0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/basic.test @@ -0,0 +1,17 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% import _self as macros %} + +{{ macros.input('username') }} +{{ macros.input('password', null, 'password', 1) }} + +{% macro input(name, value, type, size) %} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + + + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/endmacro_name.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/endmacro_name.test new file mode 100644 index 000000000..3f3caf777 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/endmacro_name.test @@ -0,0 +1,16 @@ +--TEST-- +"macro" tag supports name for endmacro +--TEMPLATE-- +{% import _self as macros %} + +{{ macros.foo() }} +{{ macros.bar() }} + +{% macro foo() %}foo{% endmacro %} +{% macro bar() %}bar{% endmacro bar %} +--DATA-- +return [] +--EXPECT-- +foo +bar + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/external.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/external.test new file mode 100644 index 000000000..b28ca19f0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/external.test @@ -0,0 +1,17 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% import 'forms.twig' as forms %} + +{{ forms.input('username') }} +{{ forms.input('password', null, 'password', 1) }} +--TEMPLATE(forms.twig)-- +{% macro input(name, value, type, size) %} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + + + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from.test new file mode 100644 index 000000000..8b9aae878 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% from 'forms.twig' import foo %} +{% from 'forms.twig' import foo as foobar, bar %} + +{{ foo('foo') }} +{{ foobar('foo') }} +{{ bar('foo') }} +--TEMPLATE(forms.twig)-- +{% macro foo(name) %}foo{{ name }}{% endmacro %} +{% macro bar(name) %}bar{{ name }}{% endmacro %} +--DATA-- +return [] +--EXPECT-- +foofoo +foofoo +barfoo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_embed_with_global_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_embed_with_global_macro.test new file mode 100644 index 000000000..f06c31c93 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_embed_with_global_macro.test @@ -0,0 +1,21 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% from _self import input %} + +{% embed 'embed' %} + {% block foo %} + {{ input("username") }} + {% endblock %} +{% endembed %} + +{% macro input(name) -%} + +{% endmacro %} +--TEMPLATE(embed)-- + {% block foo %} + {% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unknown "input" function in "index.twig" at line 6. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_in_block_is_local.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_in_block_is_local.test new file mode 100644 index 000000000..0c89ce62a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_in_block_is_local.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% block foo %} + {%- from _self import input as linput %} +{% endblock %} + +{% block bar %} + {{- linput('username') }} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unknown "linput" function in "index.twig" at line 7. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_local_override.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_local_override.test new file mode 100644 index 000000000..27bfbaee1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_local_override.test @@ -0,0 +1,28 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{%- from _self import input %} + +{% block foo %} + {%- from "macros" import input %} + {{- input('username') }} +{% endblock %} + +{% block bar %} + {{- input('username') }} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--TEMPLATE(macros)-- +{% macro input(name) %} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + + + + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_macro_in_a_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_macro_in_a_macro.test new file mode 100644 index 000000000..168c9b3a9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_macro_in_a_macro.test @@ -0,0 +1,18 @@ +--TEST-- +"from" tag with syntax error +--TEMPLATE-- +{% from _self import another, foo %} + +{{ foo() }} + +{% macro foo() %} + {{ another() }} +{% endmacro %} + +{% macro another() %} + OK +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_macros_in_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_macros_in_parent.test new file mode 100644 index 000000000..57cff7ab1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_macros_in_parent.test @@ -0,0 +1,16 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% from "macros" import hello %} + +{{ hello() }} +--TEMPLATE(macros)-- +{% extends "parent" %} +--TEMPLATE(parent)-- +{% macro hello() %} + Test +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +Test diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_nested_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_nested_blocks.test new file mode 100644 index 000000000..8ede5db50 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_nested_blocks.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% block foo %} + {%- from _self import input as linput %} + + {% block bar %} + {{- linput('username') }} + {% endblock %} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unknown "linput" function in "index.twig" at line 6. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_nested_blocks_with_global_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_nested_blocks_with_global_macro.test new file mode 100644 index 000000000..384b02d8f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_nested_blocks_with_global_macro.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{%- from _self import input %} + +{% block foo %} + {% block bar %} + {{- input('username') }} + {% endblock %} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_recursive.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_recursive.test new file mode 100644 index 000000000..09a29839d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_recursive.test @@ -0,0 +1,27 @@ +--TEST-- +"import" tag +--TEMPLATE-- +{% from _self import recursive_macro %} + +{{ recursive_macro(10) }} + +{% macro recursive_macro(n) %} + {% if n > 0 %} + {{- recursive_macro(n - 1) -}} + {% endif %} + {{- n }} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +0 +1 +2 +3 +4 +5 +6 +7 +8 +9 +10 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_self_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_self_parent.test new file mode 100644 index 000000000..2a1987119 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_self_parent.test @@ -0,0 +1,20 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% extends "parent" %} + +{% block test %} + {{ _self.hello() }} +{% endblock test %} +--TEMPLATE(parent)-- +{% block test %} +Hello +{% endblock test %} + +{% macro hello() %} + Test +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +Test diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_syntax_error.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_syntax_error.test new file mode 100644 index 000000000..6223cfe94 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/from_syntax_error.test @@ -0,0 +1,8 @@ +--TEST-- +"from" tag with syntax error +--TEMPLATE-- +{% from 'forms.twig' %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unexpected token "end of statement block" ("name" expected with value "import") in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/global.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/global.test new file mode 100644 index 000000000..832740eac --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/global.test @@ -0,0 +1,14 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% from 'forms.twig' import foo %} + +{{ foo('foo') }} +{{ foo() }} +--TEMPLATE(forms.twig)-- +{% macro foo(name) %}{{ name|default('foo') }}{{ global }}{% endmacro %} +--DATA-- +return [] +--EXPECT-- +fooglobal +fooglobal diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_and_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_and_blocks.test new file mode 100644 index 000000000..721f5506a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_and_blocks.test @@ -0,0 +1,36 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% import _self as macros %} +{% from _self import input %} + +{% block foo %} + {{- macros.input('username') }} + {{- input('username') }} + + {%- import _self as lmacros %} + {%- from _self import input as linput %} + + {{- lmacros.input('username') }} + {{- linput('username') }} +{% endblock %} + +{% block bar %} + {{- macros.input('username') }} + {{- input('username') }} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + + + + + + + + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_embed_with_global_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_embed_with_global_macro.test new file mode 100644 index 000000000..3609881f0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_embed_with_global_macro.test @@ -0,0 +1,21 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% import _self as macros %} + +{% embed 'embed' %} + {% block foo %} + {{ macros.input("username") }} + {% endblock %} +{% endembed %} + +{% macro input(name) -%} + +{% endmacro %} +--TEMPLATE(embed)-- + {% block foo %} + {% endblock %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "macros" does not exist in "index.twig" at line 6. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_from_string_template.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_from_string_template.test new file mode 100644 index 000000000..6c1817be7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_from_string_template.test @@ -0,0 +1,10 @@ +--TEST-- +"import" tag with a template as string +--TEMPLATE-- +{% import template_from_string("{% macro test() %}ok{% endmacro %}") as m %} +{{ m.test() }} +--TEMPLATE(forms.twig)-- +--DATA-- +return [] +--EXPECT-- +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_in_block_is_local.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_in_block_is_local.test new file mode 100644 index 000000000..9443e1222 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_in_block_is_local.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% block foo %} + {%- import _self as lmacros %} +{% endblock %} + +{% block bar %} + {{- lmacros.input('username') }} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "lmacros" does not exist in "index.twig" at line 7. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_local_override.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_local_override.test new file mode 100644 index 000000000..7cf0552f8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_local_override.test @@ -0,0 +1,28 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{%- import _self as macros %} + +{% block foo %} + {%- import "macros" as macros %} + {{- macros.input('username') }} +{% endblock %} + +{% block bar %} + {{- macros.input('username') }} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--TEMPLATE(macros)-- +{% macro input(name) %} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + + + + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_macro_in_a_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_macro_in_a_macro.test new file mode 100644 index 000000000..1851f0974 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_macro_in_a_macro.test @@ -0,0 +1,18 @@ +--TEST-- +"import" tag with syntax error +--TEMPLATE-- +{% import _self as foo %} + +{{ foo.foo() }} + +{% macro foo() %} + {{ foo.another() }} +{% endmacro %} + +{% macro another() %} + OK +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_macros_in_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_macros_in_parent.test new file mode 100644 index 000000000..d8d5d1639 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_macros_in_parent.test @@ -0,0 +1,16 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% import "macros" as m %} + +{{ m.hello() }} +--TEMPLATE(macros)-- +{% extends "parent" %} +--TEMPLATE(parent)-- +{% macro hello() %} + Test +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +Test diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_nested_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_nested_blocks.test new file mode 100644 index 000000000..821f64bf7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_nested_blocks.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% block foo %} + {%- import _self as lmacros %} + + {% block bar %} + {{- lmacros.input('username') }} + {% endblock %} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "lmacros" does not exist in "index.twig" at line 6. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_nested_blocks_with_global_macro.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_nested_blocks_with_global_macro.test new file mode 100644 index 000000000..697d665f8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_nested_blocks_with_global_macro.test @@ -0,0 +1,18 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{%- import _self as macros %} + +{% block foo %} + {% block bar %} + {{- macros.input('username') }} + {% endblock %} +{% endblock %} + +{% macro input(name) -%} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_same_parent_and_child.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_same_parent_and_child.test new file mode 100644 index 000000000..8d9b3caa4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_same_parent_and_child.test @@ -0,0 +1,30 @@ +--TEST-- +"import" tag +--TEMPLATE-- +{% extends "parent" %} + +{% macro anotherThing() -%} + Do it too +{% endmacro %} + +{% import _self as macros %} +{% block content %} + {{ parent() }} + {{ macros.anotherThing() }} +{% endblock %} +--TEMPLATE(parent)-- +{% macro thing() %} + Do it +{% endmacro %} + +{% import _self as macros %} +{% block content %} + {{ macros.thing() }} +{% endblock %} +--DATA-- +return [] +--EXPECT-- +Do it + + + Do it too diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_self_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_self_parent.test new file mode 100644 index 000000000..24a8cdb50 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_self_parent.test @@ -0,0 +1,23 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% extends "parent" %} +{% import _self as me %} + +{% block test %} + {{ me.hello() }} +{% endblock test %} +--TEMPLATE(parent)-- +{% import _self as me %} + +{% block test %} +Hello +{% endblock test %} + +{% macro hello() %} + Test +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +Test diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_syntax_error.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_syntax_error.test new file mode 100644 index 000000000..b9817f0ee --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/import_syntax_error.test @@ -0,0 +1,10 @@ +--TEST-- +"import" tag with reserved name +--TEMPLATE-- +{% import 'forms.twig' %} + +{{ macros.parent() }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: Unexpected token "end of statement block" ("name" expected with value "as") in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/self_import.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/self_import.test new file mode 100644 index 000000000..ca3157dd8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/self_import.test @@ -0,0 +1,17 @@ +--TEST-- +"macro" tag +--TEMPLATE-- +{% import _self as forms %} + +{{ forms.input('username') }} +{{ forms.input('password', null, 'password', 1) }} + +{% macro input(name, value, type, size) %} + +{% endmacro %} +--DATA-- +return [] +--EXPECT-- + + + diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/special_chars.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/special_chars.test new file mode 100644 index 000000000..491e91e80 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/special_chars.test @@ -0,0 +1,14 @@ +--TEST-- +"§" as a macro name +--TEMPLATE-- +{% import _self as macros %} + +{{ macros.§('foo') }} + +{% macro §(foo) %} + §{{ foo }}§ +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +§foo§ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/super_globals.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/super_globals.test new file mode 100644 index 000000000..643697ce2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/macro/super_globals.test @@ -0,0 +1,14 @@ +--TEST-- +Super globals as macro arguments +--TEMPLATE-- +{% import _self as macros %} + +{{ macros.foo('foo') }} + +{% macro foo(GET) %} + {{ GET }} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/array.test new file mode 100644 index 000000000..b432427e4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/array.test @@ -0,0 +1,16 @@ +--TEST-- +sandbox tag +--TEMPLATE-- +{%- sandbox %} + {%- include "foo.twig" %} +{%- endsandbox %} +--TEMPLATE(foo.twig)-- +{{ [a][0] }} +{{ dump([a][0]) }} +--DATA-- +return ['a' => 'b'] +--CONFIG-- +return ['autoescape' => false, 'debug' => true] +--EXPECT-- +b +string(1) "b" diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/not_valid1.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/not_valid1.test new file mode 100644 index 000000000..e26a78bc1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/not_valid1.test @@ -0,0 +1,11 @@ +--TEST-- +sandbox tag +--TEMPLATE-- +{%- sandbox %} + {%- include "foo.twig" %} + a +{%- endsandbox %} +--TEMPLATE(foo.twig)-- +foo +--EXCEPTION-- +Twig\Error\SyntaxError: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 4. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/not_valid2.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/not_valid2.test new file mode 100644 index 000000000..bb2a32971 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/not_valid2.test @@ -0,0 +1,14 @@ +--TEST-- +sandbox tag +--TEMPLATE-- +{%- sandbox %} + {%- include "foo.twig" %} + + {% if 1 %} + {%- include "foo.twig" %} + {% endif %} +{%- endsandbox %} +--TEMPLATE(foo.twig)-- +foo +--EXCEPTION-- +Twig\Error\SyntaxError: Only "include" tags are allowed within a "sandbox" section in "index.twig" at line 5. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/simple.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/simple.test new file mode 100644 index 000000000..4d232d8bb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/sandbox/simple.test @@ -0,0 +1,22 @@ +--TEST-- +sandbox tag +--TEMPLATE-- +{%- sandbox %} + {%- include "foo.twig" %} +{%- endsandbox %} + +{%- sandbox %} + {%- include "foo.twig" %} + {%- include "foo.twig" %} +{%- endsandbox %} + +{%- sandbox %}{% include "foo.twig" %}{% endsandbox %} +--TEMPLATE(foo.twig)-- +foo +--DATA-- +return [] +--EXPECT-- +foo +foo +foo +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/basic.test new file mode 100644 index 000000000..aae1427e8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/basic.test @@ -0,0 +1,20 @@ +--TEST-- +"set" tag +--TEMPLATE-- +{% set foo = 'foo' %} +{% set bar = 'foo
    ' %} + +{{ foo }} +{{ bar }} + +{% set foo, bar = 'foo', 'bar' %} + +{{ foo }}{{ bar }} +--DATA-- +return [] +--EXPECT-- +foo +foo<br /> + + +foobar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture-empty.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture-empty.test new file mode 100644 index 000000000..97fc43cc2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture-empty.test @@ -0,0 +1,9 @@ +--TEST-- +"set" tag block empty capture +--TEMPLATE-- +{% set foo %}{% endset %} + +{% if foo %}FAIL{% endif %} +--DATA-- +return [] +--EXPECT-- diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture.test new file mode 100644 index 000000000..c3faf2500 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture.test @@ -0,0 +1,10 @@ +--TEST-- +"set" tag block capture +--TEMPLATE-- +{% set foo %}f
    o
    o{% endset %} + +{{ foo }} +--DATA-- +return [] +--EXPECT-- +f
    o
    o diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture_scope.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture_scope.test new file mode 100644 index 000000000..bb2bbebaf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/capture_scope.test @@ -0,0 +1,10 @@ +--TEST-- +"set" tag block capture +--TEMPLATE-- +{% set foo %}{{ foo }}{% endset %} + +{{ foo }} +--DATA-- +return ['foo' => 'foo'] +--EXPECT-- +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/expression.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/expression.test new file mode 100644 index 000000000..bd472771a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/expression.test @@ -0,0 +1,12 @@ +--TEST-- +"set" tag +--TEMPLATE-- +{% set foo, bar = 'foo' ~ 'bar', 'bar' ~ 'foo' %} + +{{ foo }} +{{ bar }} +--DATA-- +return [] +--EXPECT-- +foobar +barfoo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/inheritance.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/inheritance.test new file mode 100644 index 000000000..79e89ce79 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/inheritance.test @@ -0,0 +1,24 @@ +--TEST-- +"set" tag with inheritance +--TEMPLATE-- +{% extends "layout.twig" %} + +{% set bar %}bar{% endset %} + +{% block var_from_child %} + {{- bar -}} +{% endblock %} +--TEMPLATE(layout.twig)-- +{% set foo %}foo{% endset %} + +{% block var_from_layout %} + {{- foo -}} +{% endblock %} + +{% block var_from_child %} +{% endblock %} +--DATA-- +return [] +--EXPECT-- +foo +bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/inheritance_overriding.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/inheritance_overriding.test new file mode 100644 index 000000000..2d23c83ae --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/inheritance_overriding.test @@ -0,0 +1,24 @@ +--TEST-- +"set" tag with inheritance +--TEMPLATE-- +{% extends "layout.twig" %} + +{% set foo %}bar{% endset %} + +{% block var_from_child %} + {{- foo -}} +{% endblock %} +--TEMPLATE(layout.twig)-- +{% set foo %}foo{% endset %} + +{% block var_from_layout %} + {{- foo -}} +{% endblock %} + +{% block var_from_child %} +{% endblock %} +--DATA-- +return [] +--EXPECT-- +foo +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/mutating.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/mutating.test new file mode 100644 index 000000000..ae388bb87 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/set/mutating.test @@ -0,0 +1,17 @@ +--TEST-- +"set" tag +--TEMPLATE-- +{% set foo = "foo" %} + +{% set bar %} + {%- set foo = "bar" -%} + bar +{% endset %} + +{{ foo }} +{{ bar }} +--DATA-- +return [] +--EXPECT-- +bar +bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/special_chars.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/special_chars.test new file mode 100644 index 000000000..64ffd1d76 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/special_chars.test @@ -0,0 +1,8 @@ +--TEST-- +"§" custom tag +--TEMPLATE-- +{% § %} +--DATA-- +return [] +--EXPECT-- +§ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/aliases.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/aliases.test new file mode 100644 index 000000000..b6b1d5c66 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/aliases.test @@ -0,0 +1,12 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "blocks.twig" with content as foo %} + +{{ block('foo') }} +--TEMPLATE(blocks.twig)-- +{% block content 'foo' %} +--DATA-- +return [] +--EXPECT-- +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/basic.test new file mode 100644 index 000000000..be622a10e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/basic.test @@ -0,0 +1,12 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "blocks.twig" %} + +{{ block('content') }} +--TEMPLATE(blocks.twig)-- +{% block content 'foo' %} +--DATA-- +return [] +--EXPECT-- +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/deep.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/deep.test new file mode 100644 index 000000000..771ba642b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/deep.test @@ -0,0 +1,22 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "foo.twig" %} + +{{ block('content') }} +{{ block('foo') }} +{{ block('bar') }} +--TEMPLATE(foo.twig)-- +{% use "bar.twig" %} + +{% block content 'foo' %} +{% block foo 'foo' %} +--TEMPLATE(bar.twig)-- +{% block content 'bar' %} +{% block bar 'bar' %} +--DATA-- +return [] +--EXPECT-- +foo +foo +bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/deep_empty.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/deep_empty.test new file mode 100644 index 000000000..f14ce83de --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/deep_empty.test @@ -0,0 +1,10 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "foo.twig" %} +--TEMPLATE(foo.twig)-- +{% use "bar.twig" %} +--TEMPLATE(bar.twig)-- +--DATA-- +return [] +--EXPECT-- diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/inheritance.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/inheritance.test new file mode 100644 index 000000000..1edeaa111 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/inheritance.test @@ -0,0 +1,25 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "parent.twig" %} + +{{ block('container') }} +--TEMPLATE(parent.twig)-- +{% use "ancestor.twig" %} + +{% block sub_container %} +
    overridden sub_container
    +{% endblock %} +--TEMPLATE(ancestor.twig)-- +{% block container %} +
    {{ block('sub_container') }}
    +{% endblock %} + +{% block sub_container %} +
    sub_container
    +{% endblock %} +--DATA-- +return [] +--EXPECT-- +
    overridden sub_container
    +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/inheritance2.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/inheritance2.test new file mode 100644 index 000000000..accec5094 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/inheritance2.test @@ -0,0 +1,24 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "ancestor.twig" %} +{% use "parent.twig" %} + +{{ block('container') }} +--TEMPLATE(parent.twig)-- +{% block sub_container %} +
    overridden sub_container
    +{% endblock %} +--TEMPLATE(ancestor.twig)-- +{% block container %} +
    {{ block('sub_container') }}
    +{% endblock %} + +{% block sub_container %} +
    sub_container
    +{% endblock %} +--DATA-- +return [] +--EXPECT-- +
    overridden sub_container
    +
    diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/multiple.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/multiple.test new file mode 100644 index 000000000..85a63958d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/multiple.test @@ -0,0 +1,21 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "foo.twig" %} +{% use "bar.twig" %} + +{{ block('content') }} +{{ block('foo') }} +{{ block('bar') }} +--TEMPLATE(foo.twig)-- +{% block content 'foo' %} +{% block foo 'foo' %} +--TEMPLATE(bar.twig)-- +{% block content 'bar' %} +{% block bar 'bar' %} +--DATA-- +return [] +--EXPECT-- +bar +foo +bar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/multiple_aliases.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/multiple_aliases.test new file mode 100644 index 000000000..413bdfa4d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/multiple_aliases.test @@ -0,0 +1,23 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use "foo.twig" with content as foo_content %} +{% use "bar.twig" %} + +{{ block('content') }} +{{ block('foo') }} +{{ block('bar') }} +{{ block('foo_content') }} +--TEMPLATE(foo.twig)-- +{% block content 'foo' %} +{% block foo 'foo' %} +--TEMPLATE(bar.twig)-- +{% block content 'bar' %} +{% block bar 'bar' %} +--DATA-- +return [] +--EXPECT-- +bar +foo +bar +foo diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block.test new file mode 100644 index 000000000..49328f6e8 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block.test @@ -0,0 +1,24 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use 'file2.html.twig' with foobar as base_base_foobar %} +{% block foobar %} + {{- block('base_base_foobar') -}} + Content of block (second override) +{% endblock foobar %} +--TEMPLATE(file2.html.twig)-- +{% use 'file1.html.twig' with foobar as base_foobar %} +{% block foobar %} + {{- block('base_foobar') -}} + Content of block (first override) +{% endblock foobar %} +--TEMPLATE(file1.html.twig)-- +{% block foobar -%} + Content of block +{% endblock foobar %} +--DATA-- +return [] +--EXPECT-- +Content of block +Content of block (first override) +Content of block (second override) diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block2.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block2.test new file mode 100644 index 000000000..274baa821 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block2.test @@ -0,0 +1,24 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use 'file2.html.twig'%} +{% block foobar %} + {{- parent() -}} + Content of block (second override) +{% endblock foobar %} +--TEMPLATE(file2.html.twig)-- +{% use 'file1.html.twig' %} +{% block foobar %} + {{- parent() -}} + Content of block (first override) +{% endblock foobar %} +--TEMPLATE(file1.html.twig)-- +{% block foobar -%} + Content of block +{% endblock foobar %} +--DATA-- +return [] +--EXPECT-- +Content of block +Content of block (first override) +Content of block (second override) diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block3.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block3.test new file mode 100644 index 000000000..f6f221273 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/parent_block3.test @@ -0,0 +1,38 @@ +--TEST-- +"use" tag +--TEMPLATE-- +{% use 'file2.html.twig' %} +{% use 'file1.html.twig' with foo %} +{% block foo %} + {{- parent() -}} + Content of foo (second override) +{% endblock foo %} +{% block bar %} + {{- parent() -}} + Content of bar (second override) +{% endblock bar %} +--TEMPLATE(file2.html.twig)-- +{% use 'file1.html.twig' %} +{% block foo %} + {{- parent() -}} + Content of foo (first override) +{% endblock foo %} +{% block bar %} + {{- parent() -}} + Content of bar (first override) +{% endblock bar %} +--TEMPLATE(file1.html.twig)-- +{% block foo -%} + Content of foo +{% endblock foo %} +{% block bar -%} + Content of bar +{% endblock bar %} +--DATA-- +return [] +--EXPECT-- +Content of foo +Content of foo (first override) +Content of foo (second override) +Content of bar +Content of bar (second override) diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/use_with_parent.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/use_with_parent.test new file mode 100644 index 000000000..fad9a2d18 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/use/use_with_parent.test @@ -0,0 +1,24 @@ +--TEST-- +"use" tag with a parent block +--TEMPLATE-- +{% extends "parent.twig" %} + +{% use 'blocks.twig' %} + +{% block body %} + {{ parent() -}} + CHILD + {{ block('content') }} +{% endblock %} +--TEMPLATE(parent.twig)-- +{% block body %} + PARENT +{% endblock %} +--TEMPLATE(blocks.twig)-- +{% block content 'BLOCK' %} +--DATA-- +return [] +--EXPECT-- +PARENT +CHILD + BLOCK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/verbatim/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/verbatim/basic.test new file mode 100644 index 000000000..9b60abc49 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/verbatim/basic.test @@ -0,0 +1,10 @@ +--TEST-- +"verbatim" tag +--TEMPLATE-- +{% verbatim %} +{{ foo }} +{% endverbatim %} +--DATA-- +return [] +--EXPECT-- +{{ foo }} diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/verbatim/whitespace_control.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/verbatim/whitespace_control.test new file mode 100644 index 000000000..501922bdd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/verbatim/whitespace_control.test @@ -0,0 +1,56 @@ +--TEST-- +"verbatim" tag +--TEMPLATE-- +1*** + +{%- verbatim %} + {{ 'bla' }} +{% endverbatim %} + +1*** +2*** + +{%- verbatim -%} + {{ 'bla' }} +{% endverbatim %} + +2*** +3*** + +{%- verbatim -%} + {{ 'bla' }} +{% endverbatim -%} + +3*** +4*** + +{%- verbatim -%} + {{ 'bla' }} +{%- endverbatim %} + +4*** +5*** + +{%- verbatim -%} + {{ 'bla' }} +{%- endverbatim -%} + +5*** +--DATA-- +return [] +--EXPECT-- +1*** + {{ 'bla' }} + + +1*** +2***{{ 'bla' }} + + +2*** +3***{{ 'bla' }} +3*** +4***{{ 'bla' }} + +4*** +5***{{ 'bla' }}5*** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/basic.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/basic.test new file mode 100644 index 000000000..7c2abd0f9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/basic.test @@ -0,0 +1,13 @@ +--TEST-- +"with" tag +--TEMPLATE-- +{% with %} + {% set bar = 'BAZ' %} + {{ foo }}{{ bar }} +{% endwith %} +{{ foo }}{{ bar }} +--DATA-- +return ['foo' => 'foo', 'bar' => 'bar'] +--EXPECT-- +fooBAZ +foobar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/expression.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/expression.test new file mode 100644 index 000000000..e4433d44c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/expression.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag with expression +--TEMPLATE-- +{% with {foo: 'foo', bar: 'BAZ'} %} + {{ foo }}{{ bar }} +{% endwith %} +--DATA-- +return ['foo' => 'baz'] +--EXPECT-- +fooBAZ diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/globals.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/globals.test new file mode 100644 index 000000000..b030e7eff --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/globals.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag +--TEMPLATE-- +{% with [] only %} + {{ global }} +{% endwith %} +--DATA-- +return [] +--EXPECT-- +global diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/iterable.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/iterable.test new file mode 100644 index 000000000..1b0cbc63e --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/iterable.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag with an iterable expression +--TEMPLATE-- +{% with vars %} + {{ foo }}{{ bar }} +{% endwith %} +--DATA-- +return ['vars' => new ArrayObject(['foo' => 'baz', 'bar' => 'qux'])] +--EXPECT-- +bazqux diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/nested.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/nested.test new file mode 100644 index 000000000..33ca390b2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/nested.test @@ -0,0 +1,15 @@ +--TEST-- +nested "with" tags +--TEMPLATE-- +{% set foo, bar = 'foo', 'bar' %} +{% with {bar: 'BAZ'} %} + {% with {foo: 'FOO'} %} + {{ foo }}{{ bar }} + {% endwith %} +{% endwith %} +{{ foo }}{{ bar }} +--DATA-- +return [] +--EXPECT-- +FOOBAZ + foobar diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/with_no_mapping.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/with_no_mapping.test new file mode 100644 index 000000000..7b22c5171 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/with_no_mapping.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag with an expression that is not a mapping +--TEMPLATE-- +{% with vars %} + {{ foo }}{{ bar }} +{% endwith %} +--DATA-- +return ['vars' => 'no-mapping'] +--EXCEPTION-- +Twig\Error\RuntimeError: Variables passed to the "with" tag must be a mapping in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/with_only.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/with_only.test new file mode 100644 index 000000000..1aca34fc1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tags/with/with_only.test @@ -0,0 +1,10 @@ +--TEST-- +"with" tag with expression and only +--TEMPLATE-- +{% with {foo: 'foo', bar: 'BAZ'} only %} + {{ foo }}{{ bar }}{{ baz }} +{% endwith %} +--DATA-- +return ['foo' => 'baz', 'baz' => 'baz'] +--EXCEPTION-- +Twig\Error\RuntimeError: Variable "baz" does not exist in "index.twig" at line 3. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/array.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/array.test new file mode 100644 index 000000000..7c9a6c80a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/array.test @@ -0,0 +1,24 @@ +--TEST-- +array index test +--TEMPLATE-- +{% for key, value in days %} +{{ key }} +{% endfor %} +--DATA-- +return ['days' => [ + 1 => ['money' => 9], + 2 => ['money' => 21], + 3 => ['money' => 38], + 4 => ['money' => 6], + 18 => ['money' => 6], + 19 => ['money' => 3], + 31 => ['money' => 11], +]] +--EXPECT-- +1 +2 +3 +4 +18 +19 +31 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/constant.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/constant.test new file mode 100644 index 000000000..d4a9be776 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/constant.test @@ -0,0 +1,14 @@ +--TEST-- +"const" test +--TEMPLATE-- +{{ 8 is constant('E_NOTICE') ? 'ok' : 'no' }} +{{ 'bar' is constant('Twig\\Tests\\TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }} +{{ value is constant('Twig\\Tests\\TwigTestFoo::BAR_NAME') ? 'ok' : 'no' }} +{{ 2 is constant('ARRAY_AS_PROPS', object) ? 'ok' : 'no' }} +--DATA-- +return ['value' => 'bar', 'object' => new \ArrayObject(['hi'])] +--EXPECT-- +ok +ok +ok +ok \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined.test new file mode 100644 index 000000000..1648c66fb --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined.test @@ -0,0 +1,129 @@ +--TEST-- +"defined" test +--TEMPLATE-- +{{ definedVar is defined ? 'ok' : 'ko' }} +{{ definedVar is not defined ? 'ko' : 'ok' }} +{{ undefinedVar is defined ? 'ko' : 'ok' }} +{{ undefinedVar is not defined ? 'ok' : 'ko' }} +{{ zeroVar is defined ? 'ok' : 'ko' }} +{{ nullVar is defined ? 'ok' : 'ko' }} +{{ nested.definedVar is defined ? 'ok' : 'ko' }} +{{ nested['definedVar'] is defined ? 'ok' : 'ko' }} +{{ nested.definedVar is not defined ? 'ko' : 'ok' }} +{{ nested.undefinedVar is defined ? 'ko' : 'ok' }} +{{ nested['undefinedVar'] is defined ? 'ko' : 'ok' }} +{{ nested.undefinedVar is not defined ? 'ok' : 'ko' }} +{{ nested.zeroVar is defined ? 'ok' : 'ko' }} +{{ nested.nullVar is defined ? 'ok' : 'ko' }} +{{ nested.definedArray.0 is defined ? 'ok' : 'ko' }} +{{ nested['definedArray'][0] is defined ? 'ok' : 'ko' }} +{{ object.foo is defined ? 'ok' : 'ko' }} +{{ object.undefinedMethod is defined ? 'ko' : 'ok' }} +{{ object.getFoo() is defined ? 'ok' : 'ko' }} +{{ object.getFoo('a') is defined ? 'ok' : 'ko' }} +{{ object.undefinedMethod() is defined ? 'ko' : 'ok' }} +{{ object.undefinedMethod('a') is defined ? 'ko' : 'ok' }} +{{ object.self.foo is defined ? 'ok' : 'ko' }} +{{ object.self.undefinedMethod is defined ? 'ko' : 'ok' }} +{{ object.undefinedMethod.self is defined ? 'ko' : 'ok' }} +{{ 0 is defined ? 'ok' : 'ko' }} +{{ "foo" is defined ? 'ok' : 'ko' }} +{{ true is defined ? 'ok' : 'ko' }} +{{ false is defined ? 'ok' : 'ko' }} +{{ null is defined ? 'ok' : 'ko' }} +{{ [1, 2] is defined ? 'ok' : 'ko' }} +{{ { foo: "bar" } is defined ? 'ok' : 'ko' }} +--DATA-- +return [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'nullVar' => null, + 'nested' => [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'nullVar' => null, + 'definedArray' => [0], + ], + 'object' => new Twig\Tests\TwigTestFoo(), +] +--EXPECT-- +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +--DATA-- +return [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'nullVar' => null, + 'nested' => [ + 'definedVar' => 'defined', + 'zeroVar' => 0, + 'nullVar' => null, + 'definedArray' => [0], + ], + 'object' => new Twig\Tests\TwigTestFoo(), +] +--CONFIG-- +return ['strict_variables' => false] +--EXPECT-- +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_attribute.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_attribute.test new file mode 100644 index 000000000..5fd2fe3f2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_attribute.test @@ -0,0 +1,35 @@ +--TEST-- +"defined" support for attribute +--TEMPLATE-- +{{ attribute(nested, "definedVar") is defined ? 'ok' : 'ko' }} +{{ attribute(nested, "undefinedVar") is not defined ? 'ok' : 'ko' }} +{{ attribute(nested, definedVarName) is defined ? 'ok' : 'ko' }} +{{ attribute(nested, undefinedVarName) is not defined ? 'ok' : 'ko' }} +--DATA-- +return [ + 'nested' => [ + 'definedVar' => 'defined', + ], + 'definedVarName' => 'definedVar', + 'undefinedVarName' => 'undefinedVar', +] +--EXPECT-- +ok +ok +ok +ok +--DATA-- +return [ + 'nested' => [ + 'definedVar' => 'defined', + ], + 'definedVarName' => 'definedVar', + 'undefinedVarName' => 'undefinedVar', +] +--CONFIG-- +return ['strict_variables' => false] +--EXPECT-- +ok +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_blocks.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_blocks.test new file mode 100644 index 000000000..c8b90f8c9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_blocks.test @@ -0,0 +1,38 @@ +--TEST-- +"defined" support for blocks +--TEMPLATE-- +{% extends 'parent' %} +{% block icon %}icon{% endblock %} +{% block body %} + {{ parent() }} + {{ block('foo') is defined ? 'ok' : 'ko' }} + {{ block('footer') is defined ? 'ok' : 'ko' }} + {{ block('icon') is defined ? 'ok' : 'ko' }} + {{ block('block1') is defined ? 'ok' : 'ko' }} + {%- embed 'embed' %} + {% block content %}content{% endblock %} + {% endembed %} +{% endblock %} +{% use 'blocks' %} +--TEMPLATE(parent)-- +{% block body %} + {{ block('icon') is defined ? 'ok' : 'ko' -}} +{% endblock %} +{% block footer %}{% endblock %} +--TEMPLATE(embed)-- +{{ block('icon') is defined ? 'ok' : 'ko' }} +{{ block('content') is defined ? 'ok' : 'ko' }} +{{ block('block1') is defined ? 'ok' : 'ko' }} +--TEMPLATE(blocks)-- +{% block block1 %}{%endblock %} +--DATA-- +return [] +--EXPECT-- +ok + ko + ok + ok + ok +ko +ok +ko diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_blocks_with_template.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_blocks_with_template.test new file mode 100644 index 000000000..68540de7a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_blocks_with_template.test @@ -0,0 +1,17 @@ +--TEST-- +"defined" support for blocks with a template argument +--TEMPLATE-- +{{ block('foo', 'included.twig') is defined ? 'ok' : 'ko' }} +{{ block('foo', included_loaded) is defined ? 'ok' : 'ko' }} +{{ block('foo', included_loaded_internal) is defined ? 'ok' : 'ko' }} +--TEMPLATE(included.twig)-- +{% block foo %}FOO{% endblock %} +--DATA-- +return [ + 'included_loaded' => $twig->load('included.twig'), + 'included_loaded_internal' => $twig->load('included.twig'), +] +--EXPECT-- +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_constants.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_constants.test new file mode 100644 index 000000000..62172e4f1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_constants.test @@ -0,0 +1,14 @@ +--TEST-- +"defined" support for constants +--TEMPLATE-- +{{ constant('DATE_W3C') is defined ? 'ok' : 'ko' }} +{{ constant('ARRAY_AS_PROPS', object) is defined ? 'ok' : 'ko' }} +{{ constant('FOOBAR') is not defined ? 'ok' : 'ko' }} +{{ constant('FOOBAR', object) is not defined ? 'ok' : 'ko' }} +--DATA-- +return ['expect' => DATE_W3C, 'object' => new \ArrayObject(['hi'])] +--EXPECT-- +ok +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_macros.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_macros.test new file mode 100644 index 000000000..1aa45fc82 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_for_macros.test @@ -0,0 +1,41 @@ +--TEST-- +"defined" support for macros +--TEMPLATE-- +{% import _self as macros %} +{% from _self import hello, bar %} + +{% if macros.hello is defined -%} + OK +{% endif %} + +{% if macros.foo is not defined -%} + OK +{% endif %} + +{% if hello is defined -%} + OK +{% endif %} + +{% if bar is not defined -%} + OK +{% endif %} + +{% if foo is not defined -%} + OK +{% endif %} + +{% macro hello(name) %} + Hello {{ name }} +{% endmacro %} +--DATA-- +return [] +--EXPECT-- +OK + +OK + +OK + +OK + +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_on_complex_expr.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_on_complex_expr.test new file mode 100644 index 000000000..2d0615832 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/defined_on_complex_expr.test @@ -0,0 +1,8 @@ +--TEST-- +"defined" support for "complex" expressions +--TEMPLATE-- +{{ (1 + 2) is defined ? 'ok' : 'ko' }} +--DATA-- +return [] +--EXCEPTION-- +Twig\Error\SyntaxError: The "defined" test only works with simple variables in "index.twig" at line 2. diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/dynamic_test.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/dynamic_test.test new file mode 100644 index 000000000..41625f6af --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/dynamic_test.test @@ -0,0 +1,14 @@ +--TEST-- +dynamic test +--TEMPLATE-- +{{ 'bar' is test_bar ? '1' :'0' }} +{{ 'foo' is test_foo ? '1' :'0' }} +{{ 'bar' is test_foo ? '1' :'0' }} +{{ 'foo' is test_bar ? '1' :'0' }} +--DATA-- +return [] +--EXPECT-- +1 +1 +0 +0 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/empty.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/empty.test new file mode 100644 index 000000000..70111352c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/empty.test @@ -0,0 +1,54 @@ +--TEST-- +"empty" test +--TEMPLATE-- +{{ string_empty is empty ? 'ok' : 'ko' }} +{{ string_zero is empty ? 'ko' : 'ok' }} +{{ value_null is empty ? 'ok' : 'ko' }} +{{ value_false is empty ? 'ok' : 'ko' }} +{{ value_int_zero is empty ? 'ko' : 'ok' }} +{{ array_empty is empty ? 'ok' : 'ko' }} +{{ array_not_empty is empty ? 'ko' : 'ok' }} +{{ magically_callable is empty ? 'ko' : 'ok' }} +{{ countable_empty is empty ? 'ok' : 'ko' }} +{{ countable_not_empty is empty ? 'ko' : 'ok' }} +{{ tostring_empty is empty ? 'ok' : 'ko' }} +{{ tostring_not_empty is empty ? 'ko' : 'ok' }} +{{ markup_empty is empty ? 'ok' : 'ko' }} +{{ markup_not_empty is empty ? 'ko' : 'ok' }} +{{ iterator is empty ? 'ko' : 'ok' }} +{{ empty_iterator is empty ? 'ok' : 'ko' }} +{{ callback_iterator is empty ? 'ko' : 'ok' }} +{{ empty_callback_iterator is empty ? 'ok' : 'ko' }} +--DATA-- +return [ + 'string_empty' => '', 'string_zero' => '0', + 'value_null' => null, 'value_false' => false, 'value_int_zero' => 0, + 'array_empty' => [], 'array_not_empty' => [1, 2], + 'magically_callable' => new \Twig\Tests\MagicCallStub(), + 'countable_empty' => new \Twig\Tests\CountableStub(0), 'countable_not_empty' => new \Twig\Tests\CountableStub(2), + 'tostring_empty' => new \Twig\Tests\ToStringStub(''), 'tostring_not_empty' => new \Twig\Tests\ToStringStub('0' /* edge case of using "0" as the string */), + 'markup_empty' => new \Twig\Markup('', 'UTF-8'), 'markup_not_empty' => new \Twig\Markup('test', 'UTF-8'), + 'iterator' => $iter = new \ArrayIterator(['bar', 'foo']), + 'empty_iterator' => new \ArrayIterator(), + 'callback_iterator' => new \CallbackFilterIterator($iter, function ($el) { return true; }), + 'empty_callback_iterator' => new \CallbackFilterIterator($iter, function ($el) { return false; }), +] +--EXPECT-- +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/even.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/even.test new file mode 100644 index 000000000..5c73b01e9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/even.test @@ -0,0 +1,14 @@ +--TEST-- +"even" test +--TEMPLATE-- +{{ 1 is even ? 'ko' : 'ok' }} +{{ 2 is even ? 'ok' : 'ko' }} +{{ 1 is not even ? 'ok' : 'ko' }} +{{ 2 is not even ? 'ko' : 'ok' }} +--DATA-- +return [] +--EXPECT-- +ok +ok +ok +ok diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in.test new file mode 100644 index 000000000..d4fcd5e8f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in.test @@ -0,0 +1,128 @@ +--TEST-- +Twig supports the in operator +--TEMPLATE-- +{{ bar in foo ? 'OK' : 'KO' }} +{{ not (bar in foo) ? 'KO' : 'OK' }} +{{ bar not in foo ? 'KO' : 'OK' }} +{{ 'a' in bar ? 'OK' : 'KO' }} +{{ 'c' not in bar ? 'OK' : 'KO' }} +{{ '' in bar ? 'OK' : 'KO' }} +{{ '' in '' ? 'OK' : 'KO' }} +{{ '0' not in '' ? 'OK' : 'KO' }} +{{ 'a' not in '0' ? 'OK' : 'KO' }} +{{ '0' in '0' ? 'OK' : 'KO' }} + +{{ false in [0, 1] ? 'OK' : 'KO' }} +{{ true in [0, 1] ? 'OK' : 'KO' }} +{{ '0' in [0, 1] ? 'OK' : 'KO' }} +{{ '0' in [1, 0] ? 'OK' : 'KO' }} +{{ '' in [0, 1] ? 'KO' : 'OK' }} +{{ '' in [1, 0] ? 'KO' : 'OK' }} +{{ 0 in ['', 1] ? 'KO' : 'OK' }} +{{ 0 in [1, ''] ? 'KO' : 'OK' }} + +{{ '' in 'foo' ? 'OK' : 'KO' }} +{{ 0 in 'foo' ? 'KO' : 'OK' }} +{{ false in 'foo' ? 'KO' : 'OK' }} +{{ false in '100' ? 'KO' : 'OK' }} +{{ true in '100' ? 'KO' : 'OK' }} + +{{ [] in [true, false] ? 'OK' : 'KO' }} +{{ [] in [true, ''] ? 'KO' : 'OK' }} +{{ [] in [true, []] ? 'OK' : 'KO' }} + +{{ resource ? 'OK' : 'KO' }} +{{ resource in 'foo'~resource ? 'KO' : 'OK' }} +{{ object in 'stdClass' ? 'KO' : 'OK' }} +{{ [] in 'Array' ? 'KO' : 'OK' }} +{{ dir_object in 'foo'~dir_object ? 'KO' : 'OK' }} + +{{ ''~resource in resource ? 'KO' : 'OK' }} +{{ 'stdClass' in object ? 'KO' : 'OK' }} +{{ 'Array' in [] ? 'KO' : 'OK' }} +{{ ''~dir_object in dir_object ? 'KO' : 'OK' }} + +{{ resource in [''~resource] ? 'KO' : 'OK' }} +{{ dir_object in [''~dir_object] ? 'KO' : 'OK' }} + +{{ 5 in 125 ? 'KO' : 'OK' }} +{{ 5 in '125' ? 'OK' : 'KO' }} +{{ '5' in 125 ? 'KO' : 'OK' }} +{{ '5' in '125' ? 'OK' : 'KO' }} + +{{ 5.5 in 125.5 ? 'KO' : 'OK' }} +{{ 5.5 in '125.5' ? 'OK' : 'KO' }} +{{ '5.5' in 125.5 ? 'KO' : 'OK' }} + +{{ safe in ['foo', 'bar'] ? 'OK' : 'KO' }} +{{ 'fo' in safe ? 'OK' : 'KO' }} + +{{ foo.not in ['not'] ? 'OK' : 'KO' }} +{{ 'value'|not in ['not value'] ? 'OK' : 'KO' }} +{{ foo.not not in ['not'] ? 'KO' : 'OK' }} +{{ 'value'|not not in ['not value'] ? 'KO' : 'OK' }} +{{ 'value'not in['not value'] ? 'OK' : 'KO' }} +--DATA-- +return ['bar' => 'bar', 'foo' => ['bar' => 'bar', 'not' => 'not'], 'dir_object' => new \SplFileInfo(__DIR__), 'object' => new \stdClass(), 'resource' => opendir(__DIR__), 'safe' => new \Twig\Markup('foo', 'UTF-8')] +--EXPECT-- +OK +OK +OK +OK +OK +OK +OK +OK +OK +OK + +OK +OK +OK +OK +OK +OK +OK +OK + +OK +OK +OK +OK +OK + +OK +OK +OK + +OK +OK +OK +OK +OK + +OK +OK +OK +OK + +OK +OK + +OK +OK +OK +OK + +OK +OK +OK + +OK +OK + +OK +OK +OK +OK +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in_with_iterator.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in_with_iterator.test new file mode 100644 index 000000000..2b3d093f4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in_with_iterator.test @@ -0,0 +1,14 @@ +--TEST-- +Twig supports the in operator when using iterators +--TEMPLATE-- +{{ foo in iter ? 'OK' : 'KO' }} +--DATA-- +$foo = new Twig\Tests\TwigTestFoo(); +$bar = new Twig\Tests\TwigTestFoo(); + +$foo->position = $bar; +$bar->position = $foo; + +return ['foo' => $foo, 'iter' => new \ArrayIterator([$bar, $foo])] +--EXPECT-- +OK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in_with_objects.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in_with_objects.test new file mode 100644 index 000000000..1f9fc6393 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/in_with_objects.test @@ -0,0 +1,19 @@ +--TEST-- +Twig supports the in operator when using objects +--TEMPLATE-- +{% if object in object_list %} +TRUE +{% endif %} +--DATA-- +$foo = new Twig\Tests\TwigTestFoo(); +$foo1 = new Twig\Tests\TwigTestFoo(); + +$foo->position = $foo1; +$foo1->position = $foo; + +return [ + 'object' => $foo, + 'object_list' => [$foo1, $foo], +] +--EXPECT-- +TRUE diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/iterable.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/iterable.test new file mode 100644 index 000000000..75b5756e1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/iterable.test @@ -0,0 +1,19 @@ +--TEST-- +"iterable" test +--TEMPLATE-- +{{ foo is iterable ? 'ok' : 'ko' }} +{{ traversable is iterable ? 'ok' : 'ko' }} +{{ obj is iterable ? 'ok' : 'ko' }} +{{ val is iterable ? 'ok' : 'ko' }} +--DATA-- +return [ + 'foo' => [], + 'traversable' => new \ArrayIterator([]), + 'obj' => new \stdClass(), + 'val' => 'test', +] +--EXPECT-- +ok +ok +ko +ko \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/mapping.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/mapping.test new file mode 100644 index 000000000..3e4fce048 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/mapping.test @@ -0,0 +1,38 @@ +--TEST-- +"mapping" test +--TEMPLATE-- +{{ empty is mapping ? 'ok' : 'ko' }} +{{ sequence is mapping ? 'ok' : 'ko' }} +{{ empty_array_obj is mapping ? 'ok' : 'ko' }} +{{ sequence_array_obj is mapping ? 'ok' : 'ko' }} +{{ mapping_array_obj is mapping ? 'ok' : 'ko' }} +{{ obj is mapping ? 'ok' : 'ko' }} +{{ mapping is mapping ? 'ok' : 'ko' }} +{{ string is mapping ? 'ok' : 'ko' }} +--DATA-- +return [ + 'empty' => [], + 'sequence' => [ + 'foo', + 'bar', + 'baz' + ], + 'empty_array_obj' => new \ArrayObject(), + 'sequence_array_obj' => new \ArrayObject(['foo', 'bar']), + 'mapping_array_obj' => new \ArrayObject(['foo' => 'bar']), + 'obj' => new \stdClass(), + 'mapping' => [ + 'foo' => 'bar', + 'bar' => 'foo' + ], + 'string' => 'test', +] +--EXPECT-- +ko +ko +ko +ko +ok +ok +ok +ko diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/null_coalesce.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/null_coalesce.test new file mode 100644 index 000000000..7af3255d6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/null_coalesce.test @@ -0,0 +1,30 @@ +--TEST-- +Twig supports the ?? operator +--TEMPLATE-- +{{ 'OK' ?? 'KO' }} +{{ null ?? 'OK' }} +{{ bar ?? 'KO' }} +{{ baz ?? 'OK' }} +{{ foo.bar ?? 'KO' }} +{{ foo.missing ?? 'OK' }} +{{ foo.bar.baz.missing ?? 'OK' }} +{{ foo['bar'] ?? 'KO' }} +{{ foo['missing'] ?? 'OK' }} +{{ nope ?? nada ?? 'OK' }} +{{ 1 + nope ?? nada ?? 2 }} +{{ 1 + nope ?? 3 + nada ?? 2 }} +--DATA-- +return ['bar' => 'OK', 'foo' => ['bar' => 'OK']] +--EXPECT-- +OK +OK +OK +OK +OK +OK +OK +OK +OK +OK +3 +6 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/null_coalesce_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/null_coalesce_block.test new file mode 100644 index 000000000..73579140c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/null_coalesce_block.test @@ -0,0 +1,9 @@ +--TEST-- +Twig supports the ?? operator with blocks +--TEMPLATE-- +{% block foo %}OK{% endblock %} +{{ block('foo') ?? 'KO' }} +--DATA-- +return [] +--EXPECT-- +OKOK diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/odd.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/odd.test new file mode 100644 index 000000000..1717ef894 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/odd.test @@ -0,0 +1,12 @@ +--TEST-- +"odd" test +--TEMPLATE-- +{{ 1 is odd ? 'ok' : 'ko' }} +{{ 2 is odd ? 'ko' : 'ok' }} +{{ -1 is odd ? 'ok' : 'ko' }} +--DATA-- +return [] +--EXPECT-- +ok +ok +ok \ No newline at end of file diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/sequence.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/sequence.test new file mode 100644 index 000000000..fb8a31212 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/tests/sequence.test @@ -0,0 +1,38 @@ +--TEST-- +"sequence" test +--TEMPLATE-- +{{ empty is sequence ? 'ok' : 'ko' }} +{{ sequence is sequence ? 'ok' : 'ko' }} +{{ empty_array_obj is sequence ? 'ok' : 'ko' }} +{{ sequence_array_obj is sequence ? 'ok' : 'ko' }} +{{ mapping_array_obj is sequence ? 'ok' : 'ko' }} +{{ obj is sequence ? 'ok' : 'ko' }} +{{ mapping is sequence ? 'ok' : 'ko' }} +{{ string is sequence ? 'ok' : 'ko' }} +--DATA-- +return [ + 'empty' => [], + 'sequence' => [ + 'foo', + 'bar', + 'baz' + ], + 'empty_array_obj' => new \ArrayObject(), + 'sequence_array_obj' => new \ArrayObject(['foo', 'bar']), + 'mapping_array_obj' => new \ArrayObject(['foo' => 'bar']), + 'obj' => new \stdClass(), + 'mapping' => [ + 'foo' => 'bar', + 'bar' => 'foo' + ], + 'string' => 'test', +] +--EXPECT-- +ok +ok +ok +ok +ko +ko +ko +ko diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_block.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_block.test new file mode 100644 index 000000000..346a11076 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_block.test @@ -0,0 +1,68 @@ +--TEST-- +Whitespace trimming on tags. +--TEMPLATE-- +Trim on control tag: +{% for i in range(1, 9) -%} + {{ i }} +{%- endfor %} + + +Trim on output tag: +{% for i in range(1, 9) %} + {{- i -}} +{% endfor %} + + +Trim comments: + +{#- Invisible -#} + +After the comment. + +Trim leading space: +{% if leading %} + + {{- leading }} +{% endif %} + +{%- if leading %} + {{- leading }} + +{%- endif %} + + +Trim trailing space: +{% if trailing -%} + {{ trailing -}} + +{% endif -%} + +Combined: + +{%- if both -%} +
      +
    • {{- both -}}
    • +
    + +{%- endif -%} + +end +--DATA-- +return ['leading' => 'leading space', 'trailing' => 'trailing space', 'both' => 'both'] +--EXPECT-- +Trim on control tag: +123456789 + +Trim on output tag: +123456789 + +Trim comments:After the comment. + +Trim leading space: +leading space +leading space + +Trim trailing space: +trailing spaceCombined:
      +
    • both
    • +
    end diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_delimiter_as_strings.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_delimiter_as_strings.test new file mode 100644 index 000000000..a58818dde --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_delimiter_as_strings.test @@ -0,0 +1,10 @@ +--TEST-- +Whitespace trimming as strings. +--TEMPLATE-- +{{ 5 * '{#-'|length }} +{{ '{{-'|length * 5 + '{%-'|length }} +--DATA-- +return [] +--EXPECT-- +15 +18 diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_left.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_left.test new file mode 100644 index 000000000..75d0f8031 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_left.test @@ -0,0 +1,32 @@ +--TEST-- +Whitespace trimming on tags (left side). +--TEMPLATE-- +**{% if true %} +foo + + {%- endif %}** + +** + + {{- 'foo' }}** + +** + + +{#- comment #}** + +**{% verbatim %} +foo + + {%- endverbatim %}** +--DATA-- +return [] +--EXPECT-- +**foo** + +**foo** + +**** + +** +foo** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_line_left.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_line_left.test new file mode 100644 index 000000000..e5e845f08 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_line_left.test @@ -0,0 +1,33 @@ +--TEST-- +Line whitespace trimming on tags (left side). +--TEMPLATE-- +**{% if true %} +foo + {%~ endif %}** + +** + {{~ 'foo' }}** + +** + {#~ comment #}** + +**{% verbatim %} +foo + + {%~ endverbatim %}** +--DATA-- +return [] +--EXPECT-- +**foo +** + +** +foo** + +** +** + +** +foo + +** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_line_right.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_line_right.test new file mode 100644 index 000000000..e7b510c85 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_line_right.test @@ -0,0 +1,32 @@ +--TEST-- +Line whitespace trimming on tags (right side). +--TEMPLATE-- +**{% if true ~%} +foo{% endif %}** + +**{{ 'foo' ~}} +foo +** + +**{# comment ~#} + foo +** + +**{% verbatim ~%} + foo{% endverbatim %}** +--DATA-- +return [] +--EXPECT-- +** +foo** + +**foo +foo +** + +** + foo +** + +** + foo** diff --git a/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_right.test b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_right.test new file mode 100644 index 000000000..0ec3b0cea --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Fixtures/whitespace/trim_right.test @@ -0,0 +1,28 @@ +--TEST-- +Whitespace trimming on tags (right side). +--TEMPLATE-- +**{% if true -%} + + foo{% endif %}** + +**{{ 'foo' -}} + +** + +**{# comment -#} + +** + +**{% verbatim -%} + +foo{% endverbatim %}** +--DATA-- +return [] +--EXPECT-- +**foo** + +**foo** + +**** + +**foo** diff --git a/upload/system/storage/vendor/twig/twig/tests/IntegrationTest.php b/upload/system/storage/vendor/twig/twig/tests/IntegrationTest.php new file mode 100644 index 000000000..f68ac15cf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/IntegrationTest.php @@ -0,0 +1,402 @@ +position = 0; + } + + #[\ReturnTypeWillChange] + public function current() + { + return $this->array[$this->position]; + } + + #[\ReturnTypeWillChange] + public function key() + { + return 'a'; + } + + public function next(): void + { + ++$this->position; + } + + public function valid(): bool + { + return isset($this->array[$this->position]); + } +} + +class TwigTestTokenParser_§ extends AbstractTokenParser +{ + public function parse(Token $token): Node + { + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); + + return new PrintNode(new ConstantExpression('§', -1), -1); + } + + public function getTag(): string + { + return '§'; + } +} + +class TwigTestExtension extends AbstractExtension +{ + public function getTokenParsers(): array + { + return [ + new TwigTestTokenParser_§(), + ]; + } + + public function getFilters(): array + { + return [ + new TwigFilter('§', [$this, '§Filter']), + new TwigFilter('escape_and_nl2br', [$this, 'escape_and_nl2br'], ['needs_environment' => true, 'is_safe' => ['html']]), + new TwigFilter('nl2br', [$this, 'nl2br'], ['pre_escape' => 'html', 'is_safe' => ['html']]), + new TwigFilter('escape_something', [$this, 'escape_something'], ['is_safe' => ['something']]), + new TwigFilter('preserves_safety', [$this, 'preserves_safety'], ['preserves_safety' => ['html']]), + new TwigFilter('static_call_string', 'Twig\Tests\TwigTestExtension::staticCall'), + new TwigFilter('static_call_array', ['Twig\Tests\TwigTestExtension', 'staticCall']), + new TwigFilter('magic_call', [$this, 'magicCall']), + new TwigFilter('magic_call_closure', \Closure::fromCallable([$this, 'magicCall'])), + new TwigFilter('magic_call_string', 'Twig\Tests\TwigTestExtension::magicStaticCall'), + new TwigFilter('magic_call_array', ['Twig\Tests\TwigTestExtension', 'magicStaticCall']), + new TwigFilter('*_path', [$this, 'dynamic_path']), + new TwigFilter('*_foo_*_bar', [$this, 'dynamic_foo']), + new TwigFilter('not', [$this, 'notFilter']), + new TwigFilter('anon_foo', function ($name) { return '*'.$name.'*'; }), + ]; + } + + public function getFunctions(): array + { + return [ + new TwigFunction('§', [$this, '§Function']), + new TwigFunction('safe_br', [$this, 'br'], ['is_safe' => ['html']]), + new TwigFunction('unsafe_br', [$this, 'br']), + new TwigFunction('static_call_string', 'Twig\Tests\TwigTestExtension::staticCall'), + new TwigFunction('static_call_array', ['Twig\Tests\TwigTestExtension', 'staticCall']), + new TwigFunction('*_path', [$this, 'dynamic_path']), + new TwigFunction('*_foo_*_bar', [$this, 'dynamic_foo']), + new TwigFunction('anon_foo', function ($name) { return '*'.$name.'*'; }), + new TwigFunction('deprecated_function', function () { return 'foo'; }, ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar', 'alternative' => 'not_deprecated_function']), + ]; + } + + public function getTests(): array + { + return [ + new TwigTest('multi word', [$this, 'is_multi_word']), + new TwigTest('test_*', [$this, 'dynamic_test']), + ]; + } + + public function notFilter($value) + { + return 'not '.$value; + } + + public function §Filter($value) + { + return "§{$value}§"; + } + + public function §Function($value) + { + return "§{$value}§"; + } + + /** + * nl2br which also escapes, for testing escaper filters. + */ + public function escape_and_nl2br($env, $value, $sep = '
    ') + { + return $this->nl2br($env->getRuntime(EscaperRuntime::class)->escape($value, 'html'), $sep); + } + + /** + * nl2br only, for testing filters with pre_escape. + */ + public function nl2br($value, $sep = '
    ') + { + // not secure if $value contains html tags (not only entities) + // don't use + return str_replace("\n", "$sep\n", $value ?? ''); + } + + public function dynamic_path($element, $item) + { + return $element.'/'.$item; + } + + public function dynamic_foo($foo, $bar, $item) + { + return $foo.'/'.$bar.'/'.$item; + } + + public function dynamic_test($element, $item) + { + return $element === $item; + } + + public function escape_something($value) + { + return strtoupper($value); + } + + public function preserves_safety($value) + { + return strtoupper($value); + } + + public static function staticCall($value) + { + return "*$value*"; + } + + public function br() + { + return '
    '; + } + + public function is_multi_word($value) + { + return false !== strpos($value, ' '); + } + + public function __call($method, $arguments) + { + if ('magicCall' !== $method) { + throw new \BadMethodCallException('Unexpected call to __call.'); + } + + return 'magic_'.$arguments[0]; + } + + public static function __callStatic($method, $arguments) + { + if ('magicStaticCall' !== $method) { + throw new \BadMethodCallException('Unexpected call to __callStatic.'); + } + + return 'static_magic_'.$arguments[0]; + } +} + +/** + * This class is used in tests for the "length" filter and "empty" test. It asserts that __call is not + * used to convert such objects to strings. + */ +class MagicCallStub +{ + public function __call($name, $args) + { + throw new \Exception('__call shall not be called.'); + } +} + +class ToStringStub +{ + /** + * @var string + */ + private $string; + + public function __construct($string) + { + $this->string = $string; + } + + public function __toString() + { + return $this->string; + } +} + +/** + * This class is used in tests for the length filter and empty test to show + * that when \Countable is implemented, it is preferred over the __toString() + * method. + */ +class CountableStub implements \Countable +{ + private $count; + + public function __construct($count) + { + $this->count = $count; + } + + public function count(): int + { + return $this->count; + } + + public function __toString() + { + throw new \Exception('__toString shall not be called on \Countables.'); + } +} + +/** + * This class is used in tests for the length filter. + */ +class IteratorAggregateStub implements \IteratorAggregate +{ + private $data; + + public function __construct(array $data) + { + $this->data = $data; + } + + public function getIterator(): \Traversable + { + return new \ArrayIterator($this->data); + } +} + +class SimpleIteratorForTesting implements \Iterator +{ + private $data = [1, 2, 3, 4, 5, 6, 7]; + private $key = 0; + + #[\ReturnTypeWillChange] + public function current() + { + return $this->key; + } + + public function next(): void + { + ++$this->key; + } + + #[\ReturnTypeWillChange] + public function key() + { + return $this->key; + } + + public function valid(): bool + { + return isset($this->data[$this->key]); + } + + public function rewind(): void + { + $this->key = 0; + } + + public function __toString() + { + // for testing, make sure string length returned is not the same as the `iterator_count` + return str_repeat('X', iterator_count($this) + 10); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/LexerTest.php b/upload/system/storage/vendor/twig/twig/tests/LexerTest.php new file mode 100644 index 000000000..f07a08684 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/LexerTest.php @@ -0,0 +1,501 @@ +tokenize(new Source($template, 'index')); + + $stream->expect(Token::BLOCK_START_TYPE); + $this->assertSame('§', $stream->expect(Token::NAME_TYPE)->getValue()); + } + + public function testNameLabelForFunction() + { + $template = '{{ §() }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + + $stream->expect(Token::VAR_START_TYPE); + $this->assertSame('§', $stream->expect(Token::NAME_TYPE)->getValue()); + } + + public function testBracketsNesting() + { + $template = '{{ {"a":{"b":"c"}} }}'; + + $this->assertEquals(2, $this->countToken($template, Token::PUNCTUATION_TYPE, '{')); + $this->assertEquals(2, $this->countToken($template, Token::PUNCTUATION_TYPE, '}')); + } + + public function testSpreadOperator() + { + $template = '{{ { a: "a", ...{ b: "b" } } }}'; + + $this->assertEquals(1, $this->countToken($template, Token::SPREAD_TYPE, '...')); + // sanity check on lexing after spread + $this->assertEquals(2, $this->countToken($template, Token::PUNCTUATION_TYPE, '{')); + $this->assertEquals(2, $this->countToken($template, Token::PUNCTUATION_TYPE, '}')); + } + + protected function countToken($template, $type, $value = null) + { + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + + $count = 0; + while (!$stream->isEOF()) { + $token = $stream->next(); + if ($type === $token->getType()) { + if (null === $value || $value === $token->getValue()) { + ++$count; + } + } + } + + return $count; + } + + public function testLineDirective() + { + $template = "foo\n" + ."bar\n" + ."{% line 10 %}\n" + ."{{\n" + ."baz\n" + ."}}\n"; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + + // foo\nbar\n + $this->assertSame(1, $stream->expect(Token::TEXT_TYPE)->getLine()); + // \n (after {% line %}) + $this->assertSame(10, $stream->expect(Token::TEXT_TYPE)->getLine()); + // {{ + $this->assertSame(11, $stream->expect(Token::VAR_START_TYPE)->getLine()); + // baz + $this->assertSame(12, $stream->expect(Token::NAME_TYPE)->getLine()); + } + + public function testLineDirectiveInline() + { + $template = "foo\n" + ."bar{% line 10 %}{{\n" + ."baz\n" + ."}}\n"; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + + // foo\nbar + $this->assertSame(1, $stream->expect(Token::TEXT_TYPE)->getLine()); + // {{ + $this->assertSame(10, $stream->expect(Token::VAR_START_TYPE)->getLine()); + // baz + $this->assertSame(11, $stream->expect(Token::NAME_TYPE)->getLine()); + } + + public function testLongComments() + { + $template = '{# '.str_repeat('*', 100000).' #}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $lexer->tokenize(new Source($template, 'index')); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testLongVerbatim() + { + $template = '{% verbatim %}'.str_repeat('*', 100000).'{% endverbatim %}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $lexer->tokenize(new Source($template, 'index')); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testLongVar() + { + $template = '{{ '.str_repeat('x', 100000).' }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $lexer->tokenize(new Source($template, 'index')); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testLongBlock() + { + $template = '{% '.str_repeat('x', 100000).' %}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $lexer->tokenize(new Source($template, 'index')); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testBigNumbers() + { + $template = '{{ 922337203685477580700 }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->next(); + $node = $stream->next(); + $this->assertEquals('922337203685477580700', $node->getValue()); + } + + /** + * @dataProvider getStringWithEscapedDelimiter + */ + public function testStringWithEscapedDelimiter(string $template, string $expected) + { + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $token = $stream->expect(Token::STRING_TYPE); + $this->assertSame($expected, $token->getValue()); + } + + public function getStringWithEscapedDelimiter() + { + yield '{{ \'\x6\' }} => \x6' => [ + '{{ \'\x6\' }}', + "\x6", + ]; + yield '{{ \'\065\x64\' }} => \065\x64' => [ + '{{ \'\065\x64\' }}', + "\065\x64", + ]; + yield '{{ \'App\\\\Test\' }} => App\Test' => [ + '{{ \'App\\\\Test\' }}', + 'App\\Test', + ]; + yield '{{ "App\#{var}" }} => App#{var}' => [ + '{{ "App\#{var}" }}', + 'App#{var}', + ]; + yield '{{ \'foo \\\' bar\' }} => foo \' bar' => [ + '{{ \'foo \\\' bar\' }}', + 'foo \' bar', + ]; + yield '{{ "foo \" bar" }} => foo " bar' => [ + '{{ "foo \\" bar" }}', + 'foo " bar', + ]; + yield '{{ \'\f\n\r\t\v\' }} => \f\n\r\t\v' => [ + '{{ \'\\f\\n\\r\\t\\v\' }}', + "\f\n\r\t\v", + ]; + yield '{{ \'\\\\f\\\\n\\\\r\\\\t\\\\v\' }} => \\f\\n\\r\\t\\v' => [ + '{{ \'\\\\f\\\\n\\\\r\\\\t\\\\v\' }}', + '\\f\\n\\r\\t\\v', + ]; + } + + /** + * @group legacy + * @dataProvider getStringWithEscapedDelimiterProducingDeprecation + */ + public function testStringWithEscapedDelimiterProducingDeprecation(string $template, string $expected, string $expectedDeprecation) + { + $this->expectDeprecation($expectedDeprecation); + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, $expected); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function getStringWithEscapedDelimiterProducingDeprecation() + { + yield '{{ \'App\Test\' }} => AppTest' => [ + '{{ \'App\\Test\' }}', + 'AppTest', + 'Since twig/twig 3.12: Character "T" at position 5 should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.', + ]; + yield '{{ "foo \\\' bar" }} => foo \' bar' => [ + '{{ "foo \\\' bar" }}', + 'foo \' bar', + 'Since twig/twig 3.12: Character "\'" at position 6 should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.', + ]; + yield '{{ \'foo \" bar\' }} => foo " bar' => [ + '{{ \'foo \\" bar\' }}', + 'foo " bar', + 'Since twig/twig 3.12: Character """ at position 6 should not be escaped; the "\" character is ignored in Twig v3 but will not be in v4. Please remove the extra "\" character.', + ]; + } + + public function testStringWithInterpolation() + { + $template = 'foo {{ "bar #{ baz + 1 }" }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::TEXT_TYPE, 'foo '); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, 'bar '); + $stream->expect(Token::INTERPOLATION_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'baz'); + $stream->expect(Token::OPERATOR_TYPE, '+'); + $stream->expect(Token::NUMBER_TYPE, '1'); + $stream->expect(Token::INTERPOLATION_END_TYPE); + $stream->expect(Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testStringWithEscapedInterpolation() + { + $template = '{{ "bar \#{baz+1}" }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, 'bar #{baz+1}'); + $stream->expect(Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testStringWithHash() + { + $template = '{{ "bar # baz" }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, 'bar # baz'); + $stream->expect(Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testStringWithUnterminatedInterpolation() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unclosed """'); + + $template = '{{ "bar #{x" }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $lexer->tokenize(new Source($template, 'index')); + } + + public function testStringWithNestedInterpolations() + { + $template = '{{ "bar #{ "foo#{bar}" }" }}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, 'bar '); + $stream->expect(Token::INTERPOLATION_START_TYPE); + $stream->expect(Token::STRING_TYPE, 'foo'); + $stream->expect(Token::INTERPOLATION_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'bar'); + $stream->expect(Token::INTERPOLATION_END_TYPE); + $stream->expect(Token::INTERPOLATION_END_TYPE); + $stream->expect(Token::VAR_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testStringWithNestedInterpolationsInBlock() + { + $template = '{% foo "bar #{ "foo#{bar}" }" %}'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::BLOCK_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'foo'); + $stream->expect(Token::STRING_TYPE, 'bar '); + $stream->expect(Token::INTERPOLATION_START_TYPE); + $stream->expect(Token::STRING_TYPE, 'foo'); + $stream->expect(Token::INTERPOLATION_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'bar'); + $stream->expect(Token::INTERPOLATION_END_TYPE); + $stream->expect(Token::INTERPOLATION_END_TYPE); + $stream->expect(Token::BLOCK_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testOperatorEndingWithALetterAtTheEndOfALine() + { + $template = "{{ 1 and\n0}}"; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::NUMBER_TYPE, 1); + $stream->expect(Token::OPERATOR_TYPE, 'and'); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function testUnterminatedVariable() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unclosed "variable" in "index" at line 3'); + + $template = ' + +{{ + +bar + + +'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $lexer->tokenize(new Source($template, 'index')); + } + + public function testUnterminatedBlock() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unclosed "block" in "index" at line 3'); + + $template = ' + +{% + +bar + + +'; + + $lexer = new Lexer(new Environment(new ArrayLoader())); + $lexer->tokenize(new Source($template, 'index')); + } + + public function testOverridingSyntax() + { + $template = '[# comment #]{# variable #}/# if true #/true/# endif #/'; + $lexer = new Lexer(new Environment(new ArrayLoader()), [ + 'tag_comment' => ['[#', '#]'], + 'tag_block' => ['/#', '#/'], + 'tag_variable' => ['{#', '#}'], + ]); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'variable'); + $stream->expect(Token::VAR_END_TYPE); + $stream->expect(Token::BLOCK_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'if'); + $stream->expect(Token::NAME_TYPE, 'true'); + $stream->expect(Token::BLOCK_END_TYPE); + $stream->expect(Token::TEXT_TYPE, 'true'); + $stream->expect(Token::BLOCK_START_TYPE); + $stream->expect(Token::NAME_TYPE, 'endif'); + $stream->expect(Token::BLOCK_END_TYPE); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + /** + * @dataProvider getTemplateForErrorsAtTheEndOfTheStream + */ + public function testErrorsAtTheEndOfTheStream(string $template) + { + $lexer = new Lexer(new Environment(new ArrayLoader())); + set_error_handler(function () { + $this->fail('Lexer should not emit warnings.'); + }); + try { + $lexer->tokenize(new Source($template, 'index')); + $this->addToAssertionCount(1); + } finally { + restore_error_handler(); + } + } + + public function getTemplateForErrorsAtTheEndOfTheStream() + { + yield ['{{ =']; + yield ['{{ ..']; + } + + /** + * @dataProvider getTemplateForStrings + */ + public function testStrings(string $expected) + { + $template = '{{ "'.$expected.'" }}'; + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, $expected); + + $template = "{{ '".$expected."' }}"; + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + $stream->expect(Token::VAR_START_TYPE); + $stream->expect(Token::STRING_TYPE, $expected); + + // add a dummy assertion here to satisfy PHPUnit, the only thing we want to test is that the code above + // can be executed without throwing any exceptions + $this->addToAssertionCount(1); + } + + public function getTemplateForStrings() + { + yield ['日本では、春になると桜の花が咲きます。多くの人々は、公園や川の近くに集まり、お花見を楽しみます。桜の花びらが風に舞い、まるで雪のように見える瞬間は、とても美しいです。']; + yield ['في العالم العربي، يُعتبر الخط العربي أحد أجمل أشكال الفن. يُستخدم الخط في تزيين المساجد والكتب والمخطوطات القديمة. يتميز الخط العربي بجماله وتناسقه، ويُعتبر رمزًا للثقافة الإسلامية.']; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/ArrayTest.php b/upload/system/storage/vendor/twig/twig/tests/Loader/ArrayTest.php new file mode 100644 index 000000000..543fe9ff6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/ArrayTest.php @@ -0,0 +1,89 @@ +expectException(LoaderError::class); + + $loader = new ArrayLoader(); + + $loader->getSourceContext('foo'); + } + + public function testGetCacheKey() + { + $loader = new ArrayLoader(['foo' => 'bar']); + + $this->assertEquals('foo:bar', $loader->getCacheKey('foo')); + } + + public function testGetCacheKeyWhenTemplateHasDuplicateContent() + { + $loader = new ArrayLoader([ + 'foo' => 'bar', + 'baz' => 'bar', + ]); + + $this->assertEquals('foo:bar', $loader->getCacheKey('foo')); + $this->assertEquals('baz:bar', $loader->getCacheKey('baz')); + } + + public function testGetCacheKeyIsProtectedFromEdgeCollisions() + { + $loader = new ArrayLoader([ + 'foo__' => 'bar', + 'foo' => '__bar', + ]); + + $this->assertEquals('foo__:bar', $loader->getCacheKey('foo__')); + $this->assertEquals('foo:__bar', $loader->getCacheKey('foo')); + } + + public function testGetCacheKeyWhenTemplateDoesNotExist() + { + $this->expectException(LoaderError::class); + + $loader = new ArrayLoader(); + + $loader->getCacheKey('foo'); + } + + public function testSetTemplate() + { + $loader = new ArrayLoader(); + $loader->setTemplate('foo', 'bar'); + + $this->assertEquals('bar', $loader->getSourceContext('foo')->getCode()); + } + + public function testIsFresh() + { + $loader = new ArrayLoader(['foo' => 'bar']); + $this->assertTrue($loader->isFresh('foo', time())); + } + + public function testIsFreshWhenTemplateDoesNotExist() + { + $this->expectException(LoaderError::class); + + $loader = new ArrayLoader(); + + $loader->isFresh('foo', time()); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/ChainTest.php b/upload/system/storage/vendor/twig/twig/tests/Loader/ChainTest.php new file mode 100644 index 000000000..52d6d4c72 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/ChainTest.php @@ -0,0 +1,117 @@ + 'bar']), + new ArrayLoader(['errors/index.html' => 'baz']), + new FilesystemLoader([$path]), + ]); + + $this->assertEquals('foo', $loader->getSourceContext('foo')->getName()); + $this->assertSame('', $loader->getSourceContext('foo')->getPath()); + + $this->assertEquals('errors/index.html', $loader->getSourceContext('errors/index.html')->getName()); + $this->assertSame('', $loader->getSourceContext('errors/index.html')->getPath()); + $this->assertEquals('baz', $loader->getSourceContext('errors/index.html')->getCode()); + + $this->assertEquals('errors/base.html', $loader->getSourceContext('errors/base.html')->getName()); + $this->assertEquals(realpath($path.'/errors/base.html'), realpath($loader->getSourceContext('errors/base.html')->getPath())); + $this->assertNotEquals('baz', $loader->getSourceContext('errors/base.html')->getCode()); + } + + public function testGetSourceContextWhenTemplateDoesNotExist() + { + $this->expectException(LoaderError::class); + + $loader = new ChainLoader([]); + + $loader->getSourceContext('foo'); + } + + public function testGetCacheKey() + { + $loader = new ChainLoader([ + new ArrayLoader(['foo' => 'bar']), + new ArrayLoader(['foo' => 'foobar', 'bar' => 'foo']), + ]); + + $this->assertEquals('foo:bar', $loader->getCacheKey('foo')); + $this->assertEquals('bar:foo', $loader->getCacheKey('bar')); + } + + public function testGetCacheKeyWhenTemplateDoesNotExist() + { + $this->expectException(LoaderError::class); + + $loader = new ChainLoader([]); + + $loader->getCacheKey('foo'); + } + + public function testAddLoader() + { + $fooLoader = new ArrayLoader(['foo' => 'foo:code']); + $barLoader = new ArrayLoader(['bar' => 'bar:code']); + $bazLoader = new ArrayLoader(['baz' => 'baz:code']); + $quxLoader = new ArrayLoader(['qux' => 'qux:code']); + + $loader = new ChainLoader((static function () use ($fooLoader, $barLoader): \Generator { + yield $fooLoader; + yield $barLoader; + })()); + + $loader->addLoader($bazLoader); + $loader->addLoader($quxLoader); + + $this->assertEquals('foo:code', $loader->getSourceContext('foo')->getCode()); + $this->assertEquals('bar:code', $loader->getSourceContext('bar')->getCode()); + $this->assertEquals('baz:code', $loader->getSourceContext('baz')->getCode()); + $this->assertEquals('qux:code', $loader->getSourceContext('qux')->getCode()); + + $this->assertEquals([ + $fooLoader, + $barLoader, + $bazLoader, + $quxLoader, + ], $loader->getLoaders()); + } + + public function testExists() + { + $loader1 = $this->createMock(LoaderInterface::class); + $loader1->expects($this->once())->method('exists')->willReturn(false); + $loader1->expects($this->never())->method('getSourceContext'); + + $loader2 = $this->createMock(LoaderInterface::class); + $loader2->expects($this->once())->method('exists')->willReturn(true); + $loader2->expects($this->never())->method('getSourceContext'); + + $loader = new ChainLoader(); + $loader->addLoader($loader1); + $loader->addLoader($loader2); + + $this->assertTrue($loader->exists('foo')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/FilesystemTest.php b/upload/system/storage/vendor/twig/twig/tests/Loader/FilesystemTest.php new file mode 100644 index 000000000..44b3c170d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/FilesystemTest.php @@ -0,0 +1,247 @@ +assertEquals('errors/index.html', $loader->getSourceContext('errors/index.html')->getName()); + $this->assertEquals(realpath($path.'/errors/index.html'), realpath($loader->getSourceContext('errors/index.html')->getPath())); + } + + /** + * @dataProvider getSecurityTests + */ + public function testSecurity($template) + { + $loader = new FilesystemLoader([__DIR__.'/../Fixtures']); + $loader->addPath(__DIR__.'/../Fixtures', 'foo'); + + try { + $loader->getCacheKey($template); + $this->fail(); + } catch (LoaderError $e) { + $this->assertStringNotContainsString('Unable to find template', $e->getMessage()); + } + } + + public function getSecurityTests() + { + return [ + ["AutoloaderTest\0.php"], + ['..\\AutoloaderTest.php'], + ['..\\\\\\AutoloaderTest.php'], + ['../AutoloaderTest.php'], + ['..////AutoloaderTest.php'], + ['./../AutoloaderTest.php'], + ['.\\..\\AutoloaderTest.php'], + ['././././././../AutoloaderTest.php'], + ['.\\./.\\./.\\./../AutoloaderTest.php'], + ['foo/../../AutoloaderTest.php'], + ['foo\\..\\..\\AutoloaderTest.php'], + ['foo/../bar/../../AutoloaderTest.php'], + ['foo/bar/../../../AutoloaderTest.php'], + ['filters/../../AutoloaderTest.php'], + ['filters//..//..//AutoloaderTest.php'], + ['filters\\..\\..\\AutoloaderTest.php'], + ['filters\\\\..\\\\..\\\\AutoloaderTest.php'], + ['filters\\//../\\/\\..\\AutoloaderTest.php'], + ['/../AutoloaderTest.php'], + ['@__main__/../AutoloaderTest.php'], + ['@foo/../AutoloaderTest.php'], + ['@__main__/../../AutoloaderTest.php'], + ['@foo/../../AutoloaderTest.php'], + ]; + } + + /** + * @dataProvider getBasePaths + */ + public function testPaths($basePath, $cacheKey, $rootPath) + { + $loader = new FilesystemLoader([$basePath.'/normal', $basePath.'/normal_bis'], $rootPath); + $loader->setPaths([$basePath.'/named', $basePath.'/named_bis'], 'named'); + $loader->addPath($basePath.'/named_ter', 'named'); + $loader->addPath($basePath.'/normal_ter'); + $loader->prependPath($basePath.'/normal_final'); + $loader->prependPath($basePath.'/named/../named_quater', 'named'); + $loader->prependPath($basePath.'/named_final', 'named'); + + $this->assertEquals([ + $basePath.'/normal_final', + $basePath.'/normal', + $basePath.'/normal_bis', + $basePath.'/normal_ter', + ], $loader->getPaths()); + $this->assertEquals([ + $basePath.'/named_final', + $basePath.'/named/../named_quater', + $basePath.'/named', + $basePath.'/named_bis', + $basePath.'/named_ter', + ], $loader->getPaths('named')); + + // do not use realpath here as it would make the test unuseful + $this->assertEquals($cacheKey, str_replace('\\', '/', $loader->getCacheKey('@named/named_absolute.html'))); + $this->assertEquals("path (final)\n", $loader->getSourceContext('index.html')->getCode()); + $this->assertEquals("path (final)\n", $loader->getSourceContext('@__main__/index.html')->getCode()); + $this->assertEquals("named path (final)\n", $loader->getSourceContext('@named/index.html')->getCode()); + } + + public function getBasePaths() + { + return [ + [ + __DIR__.'/Fixtures', + 'tests/Loader/Fixtures/named_quater/named_absolute.html', + null, + ], + [ + __DIR__.'/Fixtures/../Fixtures', + 'tests/Loader/Fixtures/named_quater/named_absolute.html', + null, + ], + [ + 'tests/Loader/Fixtures', + 'tests/Loader/Fixtures/named_quater/named_absolute.html', + getcwd(), + ], + [ + 'Fixtures', + 'Fixtures/named_quater/named_absolute.html', + getcwd().'/tests/Loader', + ], + [ + 'Fixtures', + 'Fixtures/named_quater/named_absolute.html', + getcwd().'/tests/../tests/Loader', + ], + ]; + } + + public function testEmptyConstructor() + { + $loader = new FilesystemLoader(); + $this->assertEquals([], $loader->getPaths()); + } + + public function testGetNamespaces() + { + $loader = new FilesystemLoader(sys_get_temp_dir()); + $this->assertEquals([FilesystemLoader::MAIN_NAMESPACE], $loader->getNamespaces()); + + $loader->addPath(sys_get_temp_dir(), 'named'); + $this->assertEquals([FilesystemLoader::MAIN_NAMESPACE, 'named'], $loader->getNamespaces()); + } + + public function testFindTemplateExceptionNamespace() + { + $basePath = __DIR__.'/Fixtures'; + + $loader = new FilesystemLoader([$basePath.'/normal']); + $loader->addPath($basePath.'/named', 'named'); + + try { + $loader->getSourceContext('@named/nowhere.html'); + } catch (\Exception $e) { + $this->assertInstanceOf(LoaderError::class, $e); + $this->assertStringContainsString('Unable to find template "@named/nowhere.html"', $e->getMessage()); + } + } + + public function testFindTemplateWithCache() + { + $basePath = __DIR__.'/Fixtures'; + + $loader = new FilesystemLoader([$basePath.'/normal']); + $loader->addPath($basePath.'/named', 'named'); + + // prime the cache for index.html in the named namespace + $namedSource = $loader->getSourceContext('@named/index.html')->getCode(); + $this->assertEquals("named path\n", $namedSource); + + // get index.html from the main namespace + $this->assertEquals("path\n", $loader->getSourceContext('index.html')->getCode()); + } + + public function testLoadTemplateAndRenderBlockWithCache() + { + $loader = new FilesystemLoader([]); + $loader->addPath(__DIR__.'/Fixtures/themes/theme2'); + $loader->addPath(__DIR__.'/Fixtures/themes/theme1'); + $loader->addPath(__DIR__.'/Fixtures/themes/theme1', 'default_theme'); + + $twig = new Environment($loader); + + $template = $twig->load('blocks.html.twig'); + $this->assertSame('block from theme 1', $template->renderBlock('b1', [])); + + $template = $twig->load('blocks.html.twig'); + $this->assertSame('block from theme 2', $template->renderBlock('b2', [])); + } + + public function getArrayInheritanceTests() + { + return [ + 'valid array inheritance' => ['array_inheritance_valid_parent.html.twig'], + 'array inheritance with empty first template' => ['array_inheritance_empty_parent.html.twig'], + 'array inheritance with non-existent first template' => ['array_inheritance_nonexistent_parent.html.twig'], + ]; + } + + /** + * @dataProvider getArrayInheritanceTests + */ + public function testArrayInheritance(string $templateName) + { + $loader = new FilesystemLoader([]); + $loader->addPath(__DIR__.'/Fixtures/inheritance'); + + $twig = new Environment($loader); + + $template = $twig->load($templateName); + $this->assertSame('VALID Child', $template->renderBlock('body', [])); + } + + public function testLoadTemplateFromPhar() + { + $loader = new FilesystemLoader([]); + // phar-sample.phar was created with the following script: + // $f = new Phar('phar-test.phar'); + // $f->addFromString('hello.twig', 'hello from phar'); + $loader->addPath('phar://'.__DIR__.'/Fixtures/phar/phar-sample.phar'); + $this->assertSame('hello from phar', $loader->getSourceContext('hello.twig')->getCode()); + } + + public function testTemplateExistsAlwaysReturnsBool() + { + $loader = new FilesystemLoader([]); + $this->assertFalse($loader->exists("foo\0.twig")); + $this->assertFalse($loader->exists('../foo.twig')); + $this->assertFalse($loader->exists('@foo')); + $this->assertFalse($loader->exists('foo')); + $this->assertFalse($loader->exists('@foo/bar.twig')); + + $loader->addPath(__DIR__.'/Fixtures/normal'); + $this->assertTrue($loader->exists('index.html')); + $loader->addPath(__DIR__.'/Fixtures/normal', 'foo'); + $this->assertTrue($loader->exists('@foo/index.html')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_empty_parent.html.twig b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_empty_parent.html.twig new file mode 100644 index 000000000..6977ebf66 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_empty_parent.html.twig @@ -0,0 +1,3 @@ +{% extends ['','parent.html.twig'] %} + +{% block body %}{{ parent() }} Child{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_nonexistent_parent.html.twig b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_nonexistent_parent.html.twig new file mode 100644 index 000000000..5b50a8b21 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_nonexistent_parent.html.twig @@ -0,0 +1,3 @@ +{% extends ['nonexistent.html.twig','parent.html.twig'] %} + +{% block body %}{{ parent() }} Child{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_valid_parent.html.twig b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_valid_parent.html.twig new file mode 100644 index 000000000..4940dad41 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/array_inheritance_valid_parent.html.twig @@ -0,0 +1,3 @@ +{% extends ['parent.html.twig','spare_parent.html.twig'] %} + +{% block body %}{{ parent() }} Child{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/parent.html.twig b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/parent.html.twig new file mode 100644 index 000000000..d594c0ed4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/parent.html.twig @@ -0,0 +1 @@ +{% block body %}VALID{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/spare_parent.html.twig b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/spare_parent.html.twig new file mode 100644 index 000000000..70b7360a2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/inheritance/spare_parent.html.twig @@ -0,0 +1 @@ +{% block body %}SPARE PARENT{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named/index.html new file mode 100644 index 000000000..9e5449c7c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named/index.html @@ -0,0 +1 @@ +named path diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_bis/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_bis/index.html new file mode 100644 index 000000000..d3a272b19 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_bis/index.html @@ -0,0 +1 @@ +named path (bis) diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_final/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_final/index.html new file mode 100644 index 000000000..9f05d1507 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_final/index.html @@ -0,0 +1 @@ +named path (final) diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_quater/named_absolute.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_quater/named_absolute.html new file mode 100644 index 000000000..b1fb5f5d7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_quater/named_absolute.html @@ -0,0 +1 @@ +named path (quater) diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_ter/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_ter/index.html new file mode 100644 index 000000000..24fb68ad2 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/named_ter/index.html @@ -0,0 +1 @@ +named path (ter) diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal/index.html new file mode 100644 index 000000000..e7a8fd4d0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal/index.html @@ -0,0 +1 @@ +path diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_bis/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_bis/index.html new file mode 100644 index 000000000..bfa916049 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_bis/index.html @@ -0,0 +1 @@ +path (bis) diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_final/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_final/index.html new file mode 100644 index 000000000..73a089bbd --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_final/index.html @@ -0,0 +1 @@ +path (final) diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_ter/index.html b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_ter/index.html new file mode 100644 index 000000000..b7ad97d8f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/normal_ter/index.html @@ -0,0 +1 @@ +path (ter) diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/phar/phar-sample.phar b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/phar/phar-sample.phar new file mode 100644 index 000000000..092bbfae3 Binary files /dev/null and b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/phar/phar-sample.phar differ diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/themes/theme1/blocks.html.twig b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/themes/theme1/blocks.html.twig new file mode 100644 index 000000000..dd0cbc2e7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/themes/theme1/blocks.html.twig @@ -0,0 +1,3 @@ +{% block b1 %}block from theme 1{% endblock %} + +{% block b2 %}block from theme 1{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/themes/theme2/blocks.html.twig b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/themes/theme2/blocks.html.twig new file mode 100644 index 000000000..07cf9db0d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Loader/Fixtures/themes/theme2/blocks.html.twig @@ -0,0 +1,3 @@ +{% use '@default_theme/blocks.html.twig' %} + +{% block b2 %}block from theme 2{% endblock %} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/AutoEscapeTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/AutoEscapeTest.php new file mode 100644 index 000000000..9cf18742b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/AutoEscapeTest.php @@ -0,0 +1,39 @@ +assertEquals($body, $node->getNode('body')); + $this->assertTrue($node->getAttribute('value')); + } + + public function getTests() + { + $body = new Node([new TextNode('foo', 1)]); + $node = new AutoEscapeNode(true, $body, 1); + + return [ + [$node, "// line 1\nyield \"foo\";"], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/BlockReferenceTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/BlockReferenceTest.php new file mode 100644 index 000000000..1211ee17b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/BlockReferenceTest.php @@ -0,0 +1,36 @@ +assertEquals('foo', $node->getAttribute('name')); + } + + public function getTests() + { + return [ + [new BlockReferenceNode('foo', 1), <<<'EOF' +// line 1 +yield from $this->unwrap()->yieldBlock('foo', $context, $blocks); +EOF + ], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/BlockTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/BlockTest.php new file mode 100644 index 000000000..0938e74a1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/BlockTest.php @@ -0,0 +1,48 @@ +assertEquals($body, $node->getNode('body')); + $this->assertEquals('foo', $node->getAttribute('name')); + } + + public function getTests() + { + $tests = []; + $tests[] = [new BlockNode('foo', new TextNode('foo', 1), 1), <<macros; + yield "foo"; + return; yield ''; +} +EOF + , new Environment(new ArrayLoader()), + ]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/DeprecatedTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/DeprecatedTest.php new file mode 100644 index 000000000..2bc23a696 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/DeprecatedTest.php @@ -0,0 +1,94 @@ +assertEquals($expr, $node->getNode('expr')); + } + + public function getTests() + { + $tests = []; + + $expr = new ConstantExpression('This section is deprecated', 1); + $node = new DeprecatedNode($expr, 1); + $node->setSourceContext(new Source('', 'foo.twig')); + $node->setNode('package', new ConstantExpression('twig/twig', 1)); + $node->setNode('version', new ConstantExpression('1.1', 1)); + + $tests[] = [$node, <<setSourceContext(new Source('', 'foo.twig')); + $dep->setNode('package', new ConstantExpression('twig/twig', 1)); + $dep->setNode('version', new ConstantExpression('1.1', 1)); + + $tests[] = [$node, <<addFunction($function = new TwigFunction('foo', 'Twig\Tests\Node\foo', [])); + + $expr = new FunctionExpression($function, new Node(), 1); + $node = new DeprecatedNode($expr, 1); + $node->setSourceContext(new Source('', 'foo.twig')); + $node->setNode('package', new ConstantExpression('twig/twig', 1)); + $node->setNode('version', new ConstantExpression('1.1', 1)); + + $compiler = $this->getCompiler($environment); + $varName = $compiler->getVarName(); + + $tests[] = [$node, <<assertEquals($expr, $node->getNode('expr')); + } + + public function getTests() + { + $tests = []; + + $expr = new ConstantExpression('foo', 1); + $node = new DoNode($expr, 1); + $tests[] = [$node, "// line 1\n\"foo\";"]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ArrayTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ArrayTest.php new file mode 100644 index 000000000..f72eeab75 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ArrayTest.php @@ -0,0 +1,43 @@ +assertEquals($foo, $node->getNode('1')); + } + + public function getTests() + { + $elements = [ + new ConstantExpression('foo', 1), + new ConstantExpression('bar', 1), + + new ConstantExpression('bar', 1), + new ConstantExpression('foo', 1), + ]; + $node = new ArrayExpression($elements, 1); + + return [ + [$node, '["foo" => "bar", "bar" => "foo"]'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/AssignNameTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/AssignNameTest.php new file mode 100644 index 000000000..80dbe94c6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/AssignNameTest.php @@ -0,0 +1,34 @@ +assertEquals('foo', $node->getAttribute('name')); + } + + public function getTests() + { + $node = new AssignNameExpression('foo', 1); + + return [ + [$node, '$context["foo"]'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/AddTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/AddTest.php new file mode 100644 index 000000000..5cff2bcff --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/AddTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new AddBinary($left, $right, 1); + + return [ + [$node, '(1 + 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/AndTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/AndTest.php new file mode 100644 index 000000000..d83aed04d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/AndTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new AndBinary($left, $right, 1); + + return [ + [$node, '(1 && 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/ConcatTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/ConcatTest.php new file mode 100644 index 000000000..0eff603ba --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/ConcatTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new ConcatBinary($left, $right, 1); + + return [ + [$node, '(1 . 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/DivTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/DivTest.php new file mode 100644 index 000000000..20cf4646f --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/DivTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new DivBinary($left, $right, 1); + + return [ + [$node, '(1 / 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/FloorDivTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/FloorDivTest.php new file mode 100644 index 000000000..826859851 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/FloorDivTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new FloorDivBinary($left, $right, 1); + + return [ + [$node, '(int) floor((1 / 2))'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/ModTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/ModTest.php new file mode 100644 index 000000000..2069ef089 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/ModTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new ModBinary($left, $right, 1); + + return [ + [$node, '(1 % 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/MulTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/MulTest.php new file mode 100644 index 000000000..c50dfc12b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/MulTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new MulBinary($left, $right, 1); + + return [ + [$node, '(1 * 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/OrTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/OrTest.php new file mode 100644 index 000000000..94df7c0b1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/OrTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new OrBinary($left, $right, 1); + + return [ + [$node, '(1 || 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/SubTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/SubTest.php new file mode 100644 index 000000000..04eebe290 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Binary/SubTest.php @@ -0,0 +1,40 @@ +assertEquals($left, $node->getNode('left')); + $this->assertEquals($right, $node->getNode('right')); + } + + public function getTests() + { + $left = new ConstantExpression(1, 1); + $right = new ConstantExpression(2, 1); + $node = new SubBinary($left, $right, 1); + + return [ + [$node, '(1 - 2)'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/CallTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/CallTest.php new file mode 100644 index 000000000..a1ea76fd4 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/CallTest.php @@ -0,0 +1,166 @@ +createFunctionExpression('date', 'date'); + $this->assertEquals(['U', null], $this->getArguments($node, ['date', ['format' => 'U', 'timestamp' => null]])); + } + + public function testGetArgumentsWhenPositionalArgumentsAfterNamedArguments() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Positional arguments cannot be used after named arguments for function "date".'); + + $node = $this->createFunctionExpression('date', 'date'); + $this->getArguments($node, ['date', ['timestamp' => 123456, 'Y-m-d']]); + } + + public function testGetArgumentsWhenArgumentIsDefinedTwice() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Argument "format" is defined twice for function "date".'); + + $node = $this->createFunctionExpression('date', 'date'); + $this->getArguments($node, ['date', ['Y-m-d', 'format' => 'U']]); + } + + public function testGetArgumentsWithWrongNamedArgumentName() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown argument "unknown" for function "date(format, timestamp)".'); + + $node = $this->createFunctionExpression('date', 'date'); + $this->getArguments($node, ['date', ['Y-m-d', 'timestamp' => null, 'unknown' => '']]); + } + + public function testGetArgumentsWithWrongNamedArgumentNames() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown arguments "unknown1", "unknown2" for function "date(format, timestamp)".'); + + $node = $this->createFunctionExpression('date', 'date'); + $this->getArguments($node, ['date', ['Y-m-d', 'timestamp' => null, 'unknown1' => '', 'unknown2' => '']]); + } + + public function testResolveArgumentsWithMissingValueForOptionalArgument() + { + if (\PHP_VERSION_ID >= 80000) { + $this->markTestSkipped('substr_compare() has a default value in 8.0, so the test does not work anymore, one should find another PHP built-in function for this test to work in PHP 8.'); + } + + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Argument "case_sensitivity" could not be assigned for function "substr_compare(main_str, str, offset, length, case_sensitivity)" because it is mapped to an internal PHP function which cannot determine default value for optional argument "length".'); + + $node = $this->createFunctionExpression('substr_compare', 'substr_compare'); + $this->getArguments($node, ['substr_compare', ['abcd', 'bc', 'offset' => 1, 'case_sensitivity' => true]]); + } + + public function testResolveArgumentsOnlyNecessaryArgumentsForCustomFunction() + { + $node = $this->createFunctionExpression('custom_function', [$this, 'customFunction']); + $this->assertEquals(['arg1'], $this->getArguments($node, [[$this, 'customFunction'], ['arg1' => 'arg1']])); + } + + public function testGetArgumentsForStaticMethod() + { + $node = $this->createFunctionExpression('custom_static_function', __CLASS__.'::customStaticFunction'); + $this->assertEquals(['arg1'], $this->getArguments($node, [__CLASS__.'::customStaticFunction', ['arg1' => 'arg1']])); + } + + public function testResolveArgumentsWithMissingParameterForArbitraryArguments() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('The last parameter of "Twig\\Tests\\Node\\Expression\\CallTest::customFunctionWithArbitraryArguments" for function "foo" must be an array with default value, eg. "array $arg = []".'); + + $node = $this->createFunctionExpression('foo', [$this, 'customFunctionWithArbitraryArguments'], true); + $this->getArguments($node, [[$this, 'customFunctionWithArbitraryArguments'], []]); + } + + public function testGetArgumentsWithInvalidCallable() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Callback for function "foo" is not callable in the current scope.'); + $node = $this->createFunctionExpression('foo', '', true); + $this->getArguments($node, ['', []]); + } + + public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnFunction() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessageMatches('#^The last parameter of "Twig\\\\Tests\\\\Node\\\\Expression\\\\custom_call_test_function" for function "foo" must be an array with default value, eg\\. "array \\$arg \\= \\[\\]"\\.$#'); + + $node = $this->createFunctionExpression('foo', 'Twig\Tests\Node\Expression\custom_call_test_function', true); + $this->getArguments($node, ['Twig\Tests\Node\Expression\custom_call_test_function', []]); + } + + public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnObject() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessageMatches('#^The last parameter of "Twig\\\\Tests\\\\Node\\\\Expression\\\\CallableTestClass\\:\\:__invoke" for function "foo" must be an array with default value, eg\\. "array \\$arg \\= \\[\\]"\\.$#'); + + $node = $this->createFunctionExpression('foo', new CallableTestClass(), true); + $this->getArguments($node, [new CallableTestClass(), []]); + } + + public static function customStaticFunction($arg1, $arg2 = 'default', $arg3 = []) + { + } + + public function customFunction($arg1, $arg2 = 'default', $arg3 = []) + { + } + + public function customFunctionWithArbitraryArguments() + { + } + + private function getArguments($call, $args) + { + $m = new \ReflectionMethod($call, 'getArguments'); + $m->setAccessible(true); + + return $m->invokeArgs($call, $args); + } + + private function createFunctionExpression($name, $callable, $isVariadic = false): Node_Expression_Call + { + return new Node_Expression_Call(new TwigFunction($name, $callable, ['is_variadic' => $isVariadic]), new Node([]), 0); + } +} + +class Node_Expression_Call extends FunctionExpression +{ +} + +class CallableTestClass +{ + public function __invoke($required) + { + } +} + +function custom_call_test_function($required) +{ +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ConditionalTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ConditionalTest.php new file mode 100644 index 000000000..004e9c951 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ConditionalTest.php @@ -0,0 +1,44 @@ +assertEquals($expr1, $node->getNode('expr1')); + $this->assertEquals($expr2, $node->getNode('expr2')); + $this->assertEquals($expr3, $node->getNode('expr3')); + } + + public function getTests() + { + $tests = []; + + $expr1 = new ConstantExpression(1, 1); + $expr2 = new ConstantExpression(2, 1); + $expr3 = new ConstantExpression(3, 1); + $node = new ConditionalExpression($expr1, $expr2, $expr3, 1); + $tests[] = [$node, '((1) ? (2) : (3))']; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ConstantTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ConstantTest.php new file mode 100644 index 000000000..920892e94 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/ConstantTest.php @@ -0,0 +1,35 @@ +assertEquals('foo', $node->getAttribute('value')); + } + + public function getTests() + { + $tests = []; + + $node = new ConstantExpression('foo', 1); + $tests[] = [$node, '"foo"']; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Filter/RawTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Filter/RawTest.php new file mode 100644 index 000000000..558d17f35 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Filter/RawTest.php @@ -0,0 +1,39 @@ +assertSame(12, $filter->getTemplateLine()); + $this->assertSame('raw', $filter->getAttribute('name')); + $this->assertSame('raw', $filter->getNode('filter', false)->getAttribute('value')); + $this->assertSame($node, $filter->getNode('node')); + $this->assertCount(0, $filter->getNode('arguments')); + } + + public function getTests() + { + $node = new RawFilter(new ConstantExpression('foo', 12)); + + return [ + [$node, '"foo"'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FilterTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FilterTest.php new file mode 100644 index 000000000..996f34e92 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FilterTest.php @@ -0,0 +1,242 @@ +assertEquals($expr, $node->getNode('node')); + $this->assertEquals($name, $node->getAttribute('name')); + $this->assertEquals($args, $node->getNode('arguments')); + } + + protected function tearDown(): void + { + $this->extension = null; + } + + public function getTests() + { + $environment = $this->getEnvironment(); + + $tests = []; + + $expr = new ConstantExpression('foo', 1); + $node = $this->createFilter($environment, $expr, 'upper'); + $node = $this->createFilter($environment, $node, 'number_format', [new ConstantExpression(2, 1), new ConstantExpression('.', 1), new ConstantExpression(',', 1)]); + + $tests[] = [$node, '$this->extensions[\'Twig\Extension\CoreExtension\']->formatNumber(Twig\Extension\CoreExtension::upper($this->env->getCharset(), "foo"), 2, ".", ",")']; + + // named arguments + $date = new ConstantExpression(0, 1); + $node = $this->createFilter($environment, $date, 'date', [ + 'timezone' => new ConstantExpression('America/Chicago', 1), + 'format' => new ConstantExpression('d/m/Y H:i:s P', 1), + ]); + $tests[] = [$node, '$this->extensions[\'Twig\Extension\CoreExtension\']->formatDate(0, "d/m/Y H:i:s P", "America/Chicago")']; + + // skip an optional argument + $date = new ConstantExpression(0, 1); + $node = $this->createFilter($environment, $date, 'date', [ + 'timezone' => new ConstantExpression('America/Chicago', 1), + ]); + $tests[] = [$node, '$this->extensions[\'Twig\Extension\CoreExtension\']->formatDate(0, null, "America/Chicago")']; + + // underscores vs camelCase for named arguments + $string = new ConstantExpression('abc', 1); + $node = $this->createFilter($environment, $string, 'reverse', [ + 'preserve_keys' => new ConstantExpression(true, 1), + ]); + $tests[] = [$node, 'Twig\Extension\CoreExtension::reverse($this->env->getCharset(), "abc", true)']; + $node = $this->createFilter($environment, $string, 'reverse', [ + 'preserveKeys' => new ConstantExpression(true, 1), + ]); + $tests[] = [$node, 'Twig\Extension\CoreExtension::reverse($this->env->getCharset(), "abc", true)']; + + // filter as an anonymous function + $node = $this->createFilter($environment, new ConstantExpression('foo', 1), 'anonymous'); + $tests[] = [$node, '$this->env->getFilter(\'anonymous\')->getCallable()("foo")']; + + // needs environment + $node = $this->createFilter($environment, $string, 'bar'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_dummy($this->env, "abc")', $environment]; + + $node = $this->createFilter($environment, $string, 'bar_closure'); + $tests[] = [$node, twig_tests_filter_dummy::class.'($this->env, "abc")', $environment]; + + $node = $this->createFilter($environment, $string, 'bar', [new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_dummy($this->env, "abc", "bar")', $environment]; + + // arbitrary named arguments + $node = $this->createFilter($environment, $string, 'barbar'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_barbar($context, "abc")', $environment]; + + $node = $this->createFilter($environment, $string, 'barbar', ['foo' => new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_barbar($context, "abc", null, null, ["foo" => "bar"])', $environment]; + + $node = $this->createFilter($environment, $string, 'barbar', ['arg2' => new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_barbar($context, "abc", null, "bar")', $environment]; + + if (\PHP_VERSION_ID >= 80111) { + $node = $this->createFilter($environment, $string, 'first_class_callable_static'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\FilterTestExtension::staticMethod("abc")', $environment]; + + $node = $this->createFilter($environment, $string, 'first_class_callable_object'); + $tests[] = [$node, '$this->extensions[\'Twig\Tests\Node\Expression\FilterTestExtension\']->objectMethod("abc")', $environment]; + } + + $node = $this->createFilter($environment, $string, 'barbar', [ + new ConstantExpression('1', 1), + new ConstantExpression('2', 1), + new ConstantExpression('3', 1), + 'foo' => new ConstantExpression('bar', 1), + ]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_filter_barbar($context, "abc", "1", "2", ["3", "foo" => "bar"])', $environment]; + + // from extension + $node = $this->createFilter($environment, $string, 'foo'); + $tests[] = [$node, \sprintf('$this->extensions[\'%s\']->foo("abc")', \get_class($this->getExtension())), $environment]; + + $node = $this->createFilter($environment, $string, 'foobar'); + $tests[] = [$node, '$this->env->getFilter(\'foobar\')->getCallable()("abc")', $environment]; + + $node = $this->createFilter($environment, $string, 'magic_static'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\ChildMagicCallStub::magicStaticCall("abc")', $environment]; + + return $tests; + } + + public function testCompileWithWrongNamedArgumentName() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown argument "foobar" for filter "date(format, timezone)" at line 1.'); + + $date = new ConstantExpression(0, 1); + $node = $this->createFilter($this->getEnvironment(), $date, 'date', [ + 'foobar' => new ConstantExpression('America/Chicago', 1), + ]); + + $compiler = $this->getCompiler(); + $compiler->compile($node); + } + + public function testCompileWithMissingNamedArgument() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Value for argument "from" is required for filter "replace" at line 1.'); + + $value = new ConstantExpression(0, 1); + $node = $this->createFilter($this->getEnvironment(), $value, 'replace', [ + 'to' => new ConstantExpression('foo', 1), + ]); + + $compiler = $this->getCompiler(); + $compiler->compile($node); + } + + protected function createFilter(Environment $env, $node, $name, array $arguments = []) + { + return new FilterExpression($node, $env->getFilter($name), new Node($arguments), 1); + } + + protected function getEnvironment() + { + $env = new Environment(new ArrayLoader()); + $env->addFilter(new TwigFilter('anonymous', function () {})); + $env->addFilter(new TwigFilter('bar', 'Twig\Tests\Node\Expression\twig_tests_filter_dummy', ['needs_environment' => true])); + $env->addFilter(new TwigFilter('bar_closure', \Closure::fromCallable(twig_tests_filter_dummy::class), ['needs_environment' => true])); + $env->addFilter(new TwigFilter('barbar', 'Twig\Tests\Node\Expression\twig_tests_filter_barbar', ['needs_context' => true, 'is_variadic' => true])); + $env->addFilter(new TwigFilter('magic_static', __NAMESPACE__.'\ChildMagicCallStub::magicStaticCall')); + if (\PHP_VERSION_ID >= 80111) { + $env->addExtension(new FilterTestExtension()); + } + $env->addExtension($this->getExtension()); + + return $env; + } + + private function getExtension() + { + if ($this->extension) { + return $this->extension; + } + + return $this->extension = new class() extends AbstractExtension { + public function getFilters(): array + { + return [ + new TwigFilter('foo', \Closure::fromCallable([$this, 'foo'])), + new TwigFilter('foobar', \Closure::fromCallable([$this, 'foobar'])), + ]; + } + + public function foo() + { + } + + protected function foobar() + { + } + }; + } +} + +function twig_tests_filter_dummy() +{ +} + +function twig_tests_filter_barbar($context, $string, $arg1 = null, $arg2 = null, array $args = []) +{ +} + +class ChildMagicCallStub extends ParentMagicCallStub +{ + public static function identifier() + { + return 'child'; + } +} + +class ParentMagicCallStub +{ + public static function identifier() + { + throw new \Exception('Identifier has not been defined.'); + } + + public static function __callStatic($method, $arguments) + { + if ('magicStaticCall' !== $method) { + throw new \BadMethodCallException('Unexpected call to __callStatic.'); + } + + return 'inherited_static_magic_'.static::identifier().'_'.$arguments[0]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FilterTestExtension.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FilterTestExtension.php new file mode 100644 index 000000000..4a564ae3a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FilterTestExtension.php @@ -0,0 +1,34 @@ +objectMethod(...)), + ]; + } + + public static function staticMethod() + { + } + + public function objectMethod() + { + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FunctionTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FunctionTest.php new file mode 100644 index 000000000..eacafa2d1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/FunctionTest.php @@ -0,0 +1,125 @@ +assertEquals($name, $node->getAttribute('name')); + $this->assertEquals($args, $node->getNode('arguments')); + } + + public function getTests() + { + $environment = $this->getEnvironment(); + + $tests = []; + + $node = $this->createFunction($environment, 'foo'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy()', $environment]; + + $node = $this->createFunction($environment, 'foo_closure'); + $tests[] = [$node, twig_tests_function_dummy::class.'()', $environment]; + + $node = $this->createFunction($environment, 'foo', [new ConstantExpression('bar', 1), new ConstantExpression('foobar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy("bar", "foobar")', $environment]; + + $node = $this->createFunction($environment, 'bar'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy($this->env)', $environment]; + + $node = $this->createFunction($environment, 'bar', [new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy($this->env, "bar")', $environment]; + + $node = $this->createFunction($environment, 'foofoo'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy($context)', $environment]; + + $node = $this->createFunction($environment, 'foofoo', [new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy($context, "bar")', $environment]; + + $node = $this->createFunction($environment, 'foobar'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy($this->env, $context)', $environment]; + + $node = $this->createFunction($environment, 'foobar', [new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_dummy($this->env, $context, "bar")', $environment]; + + // named arguments + $node = $this->createFunction($environment, 'date', [ + 'timezone' => new ConstantExpression('America/Chicago', 1), + 'date' => new ConstantExpression(0, 1), + ]); + $tests[] = [$node, '$this->extensions[\'Twig\Extension\CoreExtension\']->convertDate(0, "America/Chicago")']; + + // arbitrary named arguments + $node = $this->createFunction($environment, 'barbar'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_barbar()', $environment]; + + $node = $this->createFunction($environment, 'barbar', ['foo' => new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_barbar(null, null, ["foo" => "bar"])', $environment]; + + $node = $this->createFunction($environment, 'barbar', ['arg2' => new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_barbar(null, "bar")', $environment]; + + $node = $this->createFunction($environment, 'barbar', [ + new ConstantExpression('1', 1), + new ConstantExpression('2', 1), + new ConstantExpression('3', 1), + 'foo' => new ConstantExpression('bar', 1), + ]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_function_barbar("1", "2", ["3", "foo" => "bar"])', $environment]; + + // function as an anonymous function + $node = $this->createFunction($environment, 'anonymous', [new ConstantExpression('foo', 1)]); + $tests[] = [$node, '$this->env->getFunction(\'anonymous\')->getCallable()("foo")']; + + return $tests; + } + + protected function createFunction(Environment $env, $name, array $arguments = []) + { + return new FunctionExpression($env->getFunction($name), new Node($arguments), 1); + } + + protected function getEnvironment() + { + $env = new Environment(new ArrayLoader()); + $env->addFunction(new TwigFunction('anonymous', function () {})); + $env->addFunction(new TwigFunction('foo', 'Twig\Tests\Node\Expression\twig_tests_function_dummy', [])); + $env->addFunction(new TwigFunction('foo_closure', \Closure::fromCallable(twig_tests_function_dummy::class), [])); + $env->addFunction(new TwigFunction('bar', 'Twig\Tests\Node\Expression\twig_tests_function_dummy', ['needs_environment' => true])); + $env->addFunction(new TwigFunction('foofoo', 'Twig\Tests\Node\Expression\twig_tests_function_dummy', ['needs_context' => true])); + $env->addFunction(new TwigFunction('foobar', 'Twig\Tests\Node\Expression\twig_tests_function_dummy', ['needs_environment' => true, 'needs_context' => true])); + $env->addFunction(new TwigFunction('barbar', 'Twig\Tests\Node\Expression\twig_tests_function_barbar', ['is_variadic' => true])); + + return $env; + } +} + +function twig_tests_function_dummy() +{ +} + +function twig_tests_function_barbar($arg1 = null, $arg2 = null, array $args = []) +{ +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/GetAttrTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/GetAttrTest.php new file mode 100644 index 000000000..38b70555c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/GetAttrTest.php @@ -0,0 +1,60 @@ +addElement(new NameExpression('foo', 1)); + $args->addElement(new ConstantExpression('bar', 1)); + $node = new GetAttrExpression($expr, $attr, $args, Template::ARRAY_CALL, 1); + + $this->assertEquals($expr, $node->getNode('node')); + $this->assertEquals($attr, $node->getNode('attribute')); + $this->assertEquals($args, $node->getNode('arguments')); + $this->assertEquals(Template::ARRAY_CALL, $node->getAttribute('type')); + } + + public function getTests() + { + $tests = []; + + $expr = new NameExpression('foo', 1); + $attr = new ConstantExpression('bar', 1); + $args = new ArrayExpression([], 1); + $node = new GetAttrExpression($expr, $attr, $args, Template::ANY_CALL, 1); + $tests[] = [$node, \sprintf('%s%s, "bar", [], "any", false, false, false, 1)', $this->getAttributeGetter(), $this->getVariableGetter('foo', 1))]; + + $node = new GetAttrExpression($expr, $attr, $args, Template::ARRAY_CALL, 1); + $tests[] = [$node, '(($__internal_%s = // line 1'."\n". + '($context["foo"] ?? null)) && is_array($__internal_%s) || $__internal_%s instanceof ArrayAccess ? ($__internal_%s["bar"] ?? null) : null)', null, true, ]; + + $args = new ArrayExpression([], 1); + $args->addElement(new NameExpression('foo', 1)); + $args->addElement(new ConstantExpression('bar', 1)); + $node = new GetAttrExpression($expr, $attr, $args, Template::METHOD_CALL, 1); + $tests[] = [$node, \sprintf('%s%s, "bar", [%s, "bar"], "method", false, false, false, 1)', $this->getAttributeGetter(), $this->getVariableGetter('foo', 1), $this->getVariableGetter('foo'))]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/NameTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/NameTest.php new file mode 100644 index 000000000..3e5437444 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/NameTest.php @@ -0,0 +1,46 @@ +assertEquals('foo', $node->getAttribute('name')); + } + + public function getTests() + { + $node = new NameExpression('foo', 1); + $self = new NameExpression('_self', 1); + $context = new NameExpression('_context', 1); + + $env = new Environment(new ArrayLoader(), ['strict_variables' => true]); + $env1 = new Environment(new ArrayLoader(), ['strict_variables' => false]); + + $output = '(isset($context["foo"]) || array_key_exists("foo", $context) ? $context["foo"] : (function () { throw new RuntimeError(\'Variable "foo" does not exist.\', 1, $this->source); })())'; + + return [ + [$node, "// line 1\n".$output, $env], + [$node, $this->getVariableGetter('foo', 1), $env1], + [$self, "// line 1\n\$this->getTemplateName()"], + [$context, "// line 1\n\$context"], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/NullCoalesceTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/NullCoalesceTest.php new file mode 100644 index 000000000..188631c7a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/NullCoalesceTest.php @@ -0,0 +1,29 @@ +assertEquals('foo', $node->getAttribute('name')); + } + + public function getTests() + { + $tests = []; + $tests[] = [new ParentExpression('foo', 1), '$this->renderParentBlock("foo", $context, $blocks)']; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/TestTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/TestTest.php new file mode 100644 index 000000000..124a8766c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/TestTest.php @@ -0,0 +1,90 @@ +assertEquals($expr, $node->getNode('node')); + $this->assertEquals($args, $node->getNode('arguments')); + $this->assertEquals($name, $node->getAttribute('name')); + } + + public function getTests() + { + $environment = $this->getEnvironment(); + + $tests = []; + + $expr = new ConstantExpression('foo', 1); + $node = new NullTest($expr, $this->getEnvironment()->getTest('null'), new Node([]), 1); + $tests[] = [$node, '(null === "foo")']; + + // test as an anonymous function + $node = $this->createTest($environment, new ConstantExpression('foo', 1), 'anonymous', [new ConstantExpression('foo', 1)]); + $tests[] = [$node, '$this->env->getTest(\'anonymous\')->getCallable()("foo", "foo")']; + + // arbitrary named arguments + $string = new ConstantExpression('abc', 1); + $node = $this->createTest($environment, $string, 'barbar'); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_test_barbar("abc")', $environment]; + + $node = $this->createTest($environment, $string, 'barbar', ['foo' => new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_test_barbar("abc", null, null, ["foo" => "bar"])', $environment]; + + $node = $this->createTest($environment, $string, 'barbar', ['arg2' => new ConstantExpression('bar', 1)]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_test_barbar("abc", null, "bar")', $environment]; + + $node = $this->createTest($environment, $string, 'barbar', [ + new ConstantExpression('1', 1), + new ConstantExpression('2', 1), + new ConstantExpression('3', 1), + 'foo' => new ConstantExpression('bar', 1), + ]); + $tests[] = [$node, 'Twig\Tests\Node\Expression\twig_tests_test_barbar("abc", "1", "2", ["3", "foo" => "bar"])', $environment]; + + return $tests; + } + + protected function createTest(Environment $env, $node, $name, array $arguments = []) + { + return new TestExpression($node, $env->getTest($name), new Node($arguments), 1); + } + + protected function getEnvironment() + { + $env = new Environment(new ArrayLoader()); + $env->addTest(new TwigTest('anonymous', function () {})); + $env->addTest(new TwigTest('barbar', 'Twig\Tests\Node\Expression\twig_tests_test_barbar', ['is_variadic' => true, 'need_context' => true])); + + return $env; + } +} + +function twig_tests_test_barbar($string, $arg1 = null, $arg2 = null, array $args = []) +{ +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/NegTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/NegTest.php new file mode 100644 index 000000000..fcbf66ece --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/NegTest.php @@ -0,0 +1,38 @@ +assertEquals($expr, $node->getNode('node')); + } + + public function getTests() + { + $node = new ConstantExpression(1, 1); + $node = new NegUnary($node, 1); + + return [ + [$node, '-1'], + [new NegUnary($node, 1), '- -1'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/NotTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/NotTest.php new file mode 100644 index 000000000..8197111e1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/NotTest.php @@ -0,0 +1,37 @@ +assertEquals($expr, $node->getNode('node')); + } + + public function getTests() + { + $node = new ConstantExpression(1, 1); + $node = new NotUnary($node, 1); + + return [ + [$node, '!1'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/PosTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/PosTest.php new file mode 100644 index 000000000..780e339e0 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/Expression/Unary/PosTest.php @@ -0,0 +1,37 @@ +assertEquals($expr, $node->getNode('node')); + } + + public function getTests() + { + $node = new ConstantExpression(1, 1); + $node = new PosUnary($node, 1); + + return [ + [$node, '+1'], + ]; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/ForTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/ForTest.php new file mode 100644 index 000000000..5d59ae804 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/ForTest.php @@ -0,0 +1,203 @@ +setAttribute('with_loop', false); + + $this->assertEquals($keyTarget, $node->getNode('key_target')); + $this->assertEquals($valueTarget, $node->getNode('value_target')); + $this->assertEquals($seq, $node->getNode('seq')); + $this->assertEquals($body, $node->getNode('body')->getNode('0')); + $this->assertFalse($node->hasNode('else')); + + $else = new PrintNode(new NameExpression('foo', 1), 1); + $node = new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, 1); + $node->setAttribute('with_loop', false); + $this->assertEquals($else, $node->getNode('else')); + } + + public function getTests() + { + $tests = []; + + $keyTarget = new AssignNameExpression('key', 1); + $valueTarget = new AssignNameExpression('item', 1); + $seq = new NameExpression('items', 1); + $body = new Node([new PrintNode(new NameExpression('foo', 1), 1)], [], 1); + $else = null; + $node = new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, 1); + $node->setAttribute('with_loop', false); + + $tests[] = [$node, <<getVariableGetter('items')}); +foreach (\$context['_seq'] as \$context["key"] => \$context["item"]) { + yield {$this->getVariableGetter('foo')}; +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['key'], \$context['item'], \$context['_parent']); +\$context = array_intersect_key(\$context, \$_parent) + \$_parent; +EOF + ]; + + $keyTarget = new AssignNameExpression('k', 1); + $valueTarget = new AssignNameExpression('v', 1); + $seq = new NameExpression('values', 1); + $body = new Node([new PrintNode(new NameExpression('foo', 1), 1)], [], 1); + $else = null; + $node = new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, 1); + $node->setAttribute('with_loop', true); + + $tests[] = [$node, <<getVariableGetter('values')}); +\$context['loop'] = [ + 'parent' => \$context['_parent'], + 'index0' => 0, + 'index' => 1, + 'first' => true, +]; +if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof \Countable)) { + \$length = count(\$context['_seq']); + \$context['loop']['revindex0'] = \$length - 1; + \$context['loop']['revindex'] = \$length; + \$context['loop']['length'] = \$length; + \$context['loop']['last'] = 1 === \$length; +} +foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) { + yield {$this->getVariableGetter('foo')}; + ++\$context['loop']['index0']; + ++\$context['loop']['index']; + \$context['loop']['first'] = false; + if (isset(\$context['loop']['length'])) { + --\$context['loop']['revindex0']; + --\$context['loop']['revindex']; + \$context['loop']['last'] = 0 === \$context['loop']['revindex0']; + } +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']); +\$context = array_intersect_key(\$context, \$_parent) + \$_parent; +EOF + ]; + + $keyTarget = new AssignNameExpression('k', 1); + $valueTarget = new AssignNameExpression('v', 1); + $seq = new NameExpression('values', 1); + $body = new Node([new PrintNode(new NameExpression('foo', 1), 1)], [], 1); + $else = null; + $node = new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, 1); + $node->setAttribute('with_loop', true); + + $tests[] = [$node, <<getVariableGetter('values')}); +\$context['loop'] = [ + 'parent' => \$context['_parent'], + 'index0' => 0, + 'index' => 1, + 'first' => true, +]; +if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof \Countable)) { + \$length = count(\$context['_seq']); + \$context['loop']['revindex0'] = \$length - 1; + \$context['loop']['revindex'] = \$length; + \$context['loop']['length'] = \$length; + \$context['loop']['last'] = 1 === \$length; +} +foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) { + yield {$this->getVariableGetter('foo')}; + ++\$context['loop']['index0']; + ++\$context['loop']['index']; + \$context['loop']['first'] = false; + if (isset(\$context['loop']['length'])) { + --\$context['loop']['revindex0']; + --\$context['loop']['revindex']; + \$context['loop']['last'] = 0 === \$context['loop']['revindex0']; + } +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']); +\$context = array_intersect_key(\$context, \$_parent) + \$_parent; +EOF + ]; + + $keyTarget = new AssignNameExpression('k', 1); + $valueTarget = new AssignNameExpression('v', 1); + $seq = new NameExpression('values', 1); + $body = new Node([new PrintNode(new NameExpression('foo', 1), 1)], [], 1); + $else = new PrintNode(new NameExpression('foo', 1), 1); + $node = new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, 1); + $node->setAttribute('with_loop', true); + + $tests[] = [$node, <<getVariableGetter('values')}); +\$context['_iterated'] = false; +\$context['loop'] = [ + 'parent' => \$context['_parent'], + 'index0' => 0, + 'index' => 1, + 'first' => true, +]; +if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof \Countable)) { + \$length = count(\$context['_seq']); + \$context['loop']['revindex0'] = \$length - 1; + \$context['loop']['revindex'] = \$length; + \$context['loop']['length'] = \$length; + \$context['loop']['last'] = 1 === \$length; +} +foreach (\$context['_seq'] as \$context["k"] => \$context["v"]) { + yield {$this->getVariableGetter('foo')}; + \$context['_iterated'] = true; + ++\$context['loop']['index0']; + ++\$context['loop']['index']; + \$context['loop']['first'] = false; + if (isset(\$context['loop']['length'])) { + --\$context['loop']['revindex0']; + --\$context['loop']['revindex']; + \$context['loop']['last'] = 0 === \$context['loop']['revindex0']; + } +} +if (!\$context['_iterated']) { + yield {$this->getVariableGetter('foo')}; +} +\$_parent = \$context['_parent']; +unset(\$context['_seq'], \$context['_iterated'], \$context['k'], \$context['v'], \$context['_parent'], \$context['loop']); +\$context = array_intersect_key(\$context, \$_parent) + \$_parent; +EOF + ]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/IfTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/IfTest.php new file mode 100644 index 000000000..26821a39b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/IfTest.php @@ -0,0 +1,97 @@ +assertEquals($t, $node->getNode('tests')); + $this->assertFalse($node->hasNode('else')); + + $else = new PrintNode(new NameExpression('bar', 1), 1); + $node = new IfNode($t, $else, 1); + $this->assertEquals($else, $node->getNode('else')); + } + + public function getTests() + { + $tests = []; + + $t = new Node([ + new ConstantExpression(true, 1), + new PrintNode(new NameExpression('foo', 1), 1), + ], [], 1); + $else = null; + $node = new IfNode($t, $else, 1); + + $tests[] = [$node, <<getVariableGetter('foo')}; +} +EOF + ]; + + $t = new Node([ + new ConstantExpression(true, 1), + new PrintNode(new NameExpression('foo', 1), 1), + new ConstantExpression(false, 1), + new PrintNode(new NameExpression('bar', 1), 1), + ], [], 1); + $else = null; + $node = new IfNode($t, $else, 1); + + $tests[] = [$node, <<getVariableGetter('foo')}; +} elseif (false) { + yield {$this->getVariableGetter('bar')}; +} +EOF + ]; + + $t = new Node([ + new ConstantExpression(true, 1), + new PrintNode(new NameExpression('foo', 1), 1), + ], [], 1); + $else = new PrintNode(new NameExpression('bar', 1), 1); + $node = new IfNode($t, $else, 1); + + $tests[] = [$node, <<getVariableGetter('foo')}; +} else { + yield {$this->getVariableGetter('bar')}; +} +EOF + ]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/ImportTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/ImportTest.php new file mode 100644 index 000000000..b069cabe5 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/ImportTest.php @@ -0,0 +1,47 @@ +assertEquals($macro, $node->getNode('expr')); + $this->assertEquals($var, $node->getNode('var')); + } + + public function getTests() + { + $tests = []; + + $macro = new ConstantExpression('foo.twig', 1); + $var = new AssignNameExpression('macro', 1); + $node = new ImportNode($macro, $var, 1); + + $tests[] = [$node, <<macros["macro"] = \$this->loadTemplate("foo.twig", null, 1)->unwrap(); +EOF + ]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/IncludeTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/IncludeTest.php new file mode 100644 index 000000000..446fbd293 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/IncludeTest.php @@ -0,0 +1,95 @@ +assertFalse($node->hasNode('variables')); + $this->assertEquals($expr, $node->getNode('expr')); + $this->assertFalse($node->getAttribute('only')); + + $vars = new ArrayExpression([new ConstantExpression('foo', 1), new ConstantExpression(true, 1)], 1); + $node = new IncludeNode($expr, $vars, true, false, 1); + $this->assertEquals($vars, $node->getNode('variables')); + $this->assertTrue($node->getAttribute('only')); + } + + public function getTests() + { + $tests = []; + + $expr = new ConstantExpression('foo.twig', 1); + $node = new IncludeNode($expr, null, false, false, 1); + $tests[] = [$node, <<<'EOF' +// line 1 +yield from $this->loadTemplate("foo.twig", null, 1)->unwrap()->yield($context); +EOF + ]; + + $expr = new ConditionalExpression( + new ConstantExpression(true, 1), + new ConstantExpression('foo', 1), + new ConstantExpression('foo', 1), + 0 + ); + $node = new IncludeNode($expr, null, false, false, 1); + $tests[] = [$node, <<<'EOF' +// line 1 +yield from $this->loadTemplate(((true) ? ("foo") : ("foo")), null, 1)->unwrap()->yield($context); +EOF + ]; + + $expr = new ConstantExpression('foo.twig', 1); + $vars = new ArrayExpression([new ConstantExpression('foo', 1), new ConstantExpression(true, 1)], 1); + $node = new IncludeNode($expr, $vars, false, false, 1); + $tests[] = [$node, <<<'EOF' +// line 1 +yield from $this->loadTemplate("foo.twig", null, 1)->unwrap()->yield(CoreExtension::merge($context, ["foo" => true])); +EOF + ]; + + $node = new IncludeNode($expr, $vars, true, false, 1); + $tests[] = [$node, <<<'EOF' +// line 1 +yield from $this->loadTemplate("foo.twig", null, 1)->unwrap()->yield(CoreExtension::toArray(["foo" => true])); +EOF + ]; + + $node = new IncludeNode($expr, $vars, true, true, 1); + $tests[] = [$node, <<loadTemplate("foo.twig", null, 1); +} catch (LoaderError \$e) { + // ignore missing template +} +if (\$__internal_%s) { + yield from \$__internal_%s->unwrap()->yield(CoreExtension::toArray(["foo" => true])); +} +EOF + , null, true]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/MacroTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/MacroTest.php new file mode 100644 index 000000000..ef36774f7 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/MacroTest.php @@ -0,0 +1,73 @@ +assertEquals($body, $node->getNode('body')); + $this->assertEquals($arguments, $node->getNode('arguments')); + $this->assertEquals('foo', $node->getAttribute('name')); + } + + public function getTests() + { + $tests = []; + + $arguments = new Node([ + 'foo' => new ConstantExpression(null, 1), + 'bar' => new ConstantExpression('Foo', 1), + ], [], 1); + + $body = new BodyNode([new TextNode('foo', 1)]); + $node = new MacroNode('foo', $body, $arguments, 1); + + $text[] = [$node, <<macros; + \$context = \$this->env->mergeGlobals([ + "foo" => \$__foo__, + "bar" => \$__bar__, + "varargs" => \$__varargs__, + ]); + + \$blocks = []; + + return new Markup(implode('', iterator_to_array((function () use (\$context, \$macros, \$blocks) { + yield "foo"; + return; yield ''; + })(), false)), \$this->env->getCharset()); +} +EOF + , new Environment(new ArrayLoader()), + ]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/ModuleTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/ModuleTest.php new file mode 100644 index 000000000..f3081dff9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/ModuleTest.php @@ -0,0 +1,319 @@ +assertEquals($body, $node->getNode('body')); + $this->assertEquals($blocks, $node->getNode('blocks')); + $this->assertEquals($macros, $node->getNode('macros')); + $this->assertEquals($parent, $node->getNode('parent')); + $this->assertEquals($source->getName(), $node->getTemplateName()); + } + + public function getTests() + { + $twig = new Environment(new ArrayLoader(['foo.twig' => '{{ foo }}'])); + + $tests = []; + + $body = new BodyNode([new TextNode('foo', 1)]); + $extends = null; + $blocks = new Node(); + $macros = new Node(); + $traits = new Node(); + $source = new Source('{{ foo }}', 'foo.twig'); + + $node = new ModuleNode($body, $extends, $blocks, $macros, $traits, new Node([]), $source); + $tests[] = [$node, << + */ + private array \$macros = []; + + public function __construct(Environment \$env) + { + parent::__construct(\$env); + + \$this->source = \$this->getSourceContext(); + + \$this->parent = false; + + \$this->blocks = [ + ]; + } + + protected function doDisplay(array \$context, array \$blocks = []) + { + \$macros = \$this->macros; + // line 1 + yield "foo"; + return; yield ''; + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "foo.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 42 => 1,); + } + + public function getSourceContext(): Source + { + return new Source("", "foo.twig", ""); + } +} +EOF + , $twig, true]; + + $import = new ImportNode(new ConstantExpression('foo.twig', 1), new AssignNameExpression('macro', 1), 2); + + $body = new BodyNode([$import]); + $extends = new ConstantExpression('layout.twig', 1); + + $node = new ModuleNode($body, $extends, $blocks, $macros, $traits, new Node([]), $source); + $tests[] = [$node, << + */ + private array \$macros = []; + + public function __construct(Environment \$env) + { + parent::__construct(\$env); + + \$this->source = \$this->getSourceContext(); + + \$this->blocks = [ + ]; + } + + protected function doGetParent(array \$context): bool|string|Template|TemplateWrapper + { + // line 1 + return "layout.twig"; + } + + protected function doDisplay(array \$context, array \$blocks = []) + { + \$macros = \$this->macros; + // line 2 + \$macros["macro"] = \$this->macros["macro"] = \$this->loadTemplate("foo.twig", "foo.twig", 2)->unwrap(); + // line 1 + \$this->parent = \$this->loadTemplate("layout.twig", "foo.twig", 1); + yield from \$this->parent->unwrap()->yield(\$context, array_merge(\$this->blocks, \$blocks)); + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "foo.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function isTraitable(): bool + { + return false; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 48 => 1, 46 => 2, 39 => 1,); + } + + public function getSourceContext(): Source + { + return new Source("", "foo.twig", ""); + } +} +EOF + , $twig, true]; + + $set = new SetNode(false, new Node([new AssignNameExpression('foo', 4)]), new Node([new ConstantExpression('foo', 4)]), 4); + $body = new BodyNode([$set]); + $extends = new ConditionalExpression( + new ConstantExpression(true, 2), + new ConstantExpression('foo', 2), + new ConstantExpression('foo', 2), + 2 + ); + + $twig = new Environment(new ArrayLoader(['foo.twig' => '{{ foo }}']), ['debug' => true]); + $node = new ModuleNode($body, $extends, $blocks, $macros, $traits, new Node([]), $source); + $tests[] = [$node, << + */ + private array \$macros = []; + + public function __construct(Environment \$env) + { + parent::__construct(\$env); + + \$this->source = \$this->getSourceContext(); + + \$this->blocks = [ + ]; + } + + protected function doGetParent(array \$context): bool|string|Template|TemplateWrapper + { + // line 2 + return \$this->loadTemplate(((true) ? ("foo") : ("foo")), "foo.twig", 2); + } + + protected function doDisplay(array \$context, array \$blocks = []) + { + \$macros = \$this->macros; + // line 4 + \$context["foo"] = "foo"; + // line 2 + yield from \$this->getParent(\$context)->unwrap()->yield(\$context, array_merge(\$this->blocks, \$blocks)); + } + + /** + * @codeCoverageIgnore + */ + public function getTemplateName(): string + { + return "foo.twig"; + } + + /** + * @codeCoverageIgnore + */ + public function isTraitable(): bool + { + return false; + } + + /** + * @codeCoverageIgnore + */ + public function getDebugInfo(): array + { + return array ( 48 => 2, 46 => 4, 39 => 2,); + } + + public function getSourceContext(): Source + { + return new Source("{{ foo }}", "foo.twig", ""); + } +} +EOF + , $twig, true]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/NodeTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/NodeTest.php new file mode 100644 index 000000000..4bb913ecf --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/NodeTest.php @@ -0,0 +1,133 @@ + function () { return '1'; }], 1); + + $this->assertEquals(<< new TwigFunction('a_function'), + 'filter' => new TwigFilter('a_filter'), + 'test' => new TwigTest('a_test'), + ], 1); + + $this->assertEquals(<<setNodeTag('tag'); + + $this->assertEquals(<< false]); + $node->deprecateAttribute('foo', new NameDeprecation('foo/bar', '2.0', 'bar')); + + $this->assertFalse($node->getAttribute('foo', false)); + } + + /** + * @group legacy + */ + public function testAttributeDeprecationWithoutAlternative() + { + $node = new Node([], ['foo' => false]); + $node->deprecateAttribute('foo', new NameDeprecation('foo/bar', '2.0')); + + $this->expectDeprecation('Since foo/bar 2.0: Getting attribute "foo" on a "Twig\Node\Node" class is deprecated.'); + $this->assertFalse($node->getAttribute('foo')); + } + + /** + * @group legacy + */ + public function testAttributeDeprecationWithAlternative() + { + $node = new Node([], ['foo' => false]); + $node->deprecateAttribute('foo', new NameDeprecation('foo/bar', '2.0', 'bar')); + + $this->expectDeprecation('Since foo/bar 2.0: Getting attribute "foo" on a "Twig\Node\Node" class is deprecated, get the "bar" attribute instead.'); + $this->assertFalse($node->getAttribute('foo')); + } + + public function testNodeDeprecationIgnore() + { + $node = new Node(['foo' => $foo = new Node()], []); + $node->deprecateNode('foo', new NameDeprecation('foo/bar', '2.0')); + + $this->assertSame($foo, $node->getNode('foo', false)); + } + + /** + * @group legacy + */ + public function testNodeDeprecationWithoutAlternative() + { + $node = new Node(['foo' => $foo = new Node()], []); + $node->deprecateNode('foo', new NameDeprecation('foo/bar', '2.0')); + + $this->expectDeprecation('Since foo/bar 2.0: Getting node "foo" on a "Twig\Node\Node" class is deprecated.'); + $this->assertSame($foo, $node->getNode('foo')); + } + + /** + * @group legacy + */ + public function testNodeAttributeDeprecationWithAlternative() + { + $node = new Node(['foo' => $foo = new Node()], []); + $node->deprecateNode('foo', new NameDeprecation('foo/bar', '2.0', 'bar')); + + $this->expectDeprecation('Since foo/bar 2.0: Getting node "foo" on a "Twig\Node\Node" class is deprecated, get the "bar" node instead.'); + $this->assertSame($foo, $node->getNode('foo')); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/PrintTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/PrintTest.php new file mode 100644 index 000000000..09c2a19ab --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/PrintTest.php @@ -0,0 +1,44 @@ +assertEquals($expr, $node->getNode('expr')); + } + + public function getTests() + { + $tests = []; + $tests[] = [new PrintNode(new ConstantExpression('foo', 1), 1), "// line 1\nyield \"foo\";"]; + + $expr = new NameExpression('foo', 1); + $attr = new ConstantExpression('bar', 1); + $node = new GetAttrExpression($expr, $attr, null, Template::METHOD_CALL, 1); + $node->setAttribute('is_generator', true); + $tests[] = [new PrintNode($node, 1), "// line 1\nyield from CoreExtension::getAttribute(\$this->env, \$this->source, (\$context[\"foo\"] ?? null), \"bar\", [], \"method\", false, false, false, 1);"]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/SandboxTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/SandboxTest.php new file mode 100644 index 000000000..c74feba42 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/SandboxTest.php @@ -0,0 +1,52 @@ +assertEquals($body, $node->getNode('body')); + } + + public function getTests() + { + $tests = []; + + $body = new TextNode('foo', 1); + $node = new SandboxNode($body, 1); + + $tests[] = [$node, <<sandbox->isSandboxed()) { + \$this->sandbox->enableSandbox(); +} +try { + yield "foo"; +} finally { + if (!\$alreadySandboxed) { + \$this->sandbox->disableSandbox(); + } +} +EOF + ]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/SetTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/SetTest.php new file mode 100644 index 000000000..f250b80ee --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/SetTest.php @@ -0,0 +1,97 @@ +assertEquals($names, $node->getNode('names')); + $this->assertEquals($values, $node->getNode('values')); + $this->assertFalse($node->getAttribute('capture')); + } + + public function getTests() + { + $tests = []; + + $names = new Node([new AssignNameExpression('foo', 1)], [], 1); + $values = new Node([new ConstantExpression('foo', 1)], [], 1); + $node = new SetNode(false, $names, $values, 1); + $tests[] = [$node, <<getEnvironment()->useYield()) { + $tests[] = [$node, <<env->getCharset()); +EOF + , new Environment(new ArrayLoader()), + ]; + } else { + $tests[] = [$node, <<<'EOF' +// line 1 +$context["foo"] = ('' === $tmp = \Twig\Extension\CoreExtension::captureOutput((function () use (&$context, $macros, $blocks) { + yield "foo"; + return; yield ''; +})())) ? '' : new Markup($tmp, $this->env->getCharset()); +EOF + , new Environment(new ArrayLoader()), + ]; + } + + $names = new Node([new AssignNameExpression('foo', 1)], [], 1); + $values = new TextNode('foo', 1); + $node = new SetNode(true, $names, $values, 1); + $tests[] = [$node, <<env->getCharset()); +EOF + ]; + + $names = new Node([new AssignNameExpression('foo', 1), new AssignNameExpression('bar', 1)], [], 1); + $values = new Node([new ConstantExpression('foo', 1), new NameExpression('bar', 1)], [], 1); + $node = new SetNode(false, $names, $values, 1); + $tests[] = [$node, <<<'EOF' +// line 1 +[$context["foo"], $context["bar"]] = ["foo", ($context["bar"] ?? null)]; +EOF + ]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Node/TextTest.php b/upload/system/storage/vendor/twig/twig/tests/Node/TextTest.php new file mode 100644 index 000000000..357362c3c --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Node/TextTest.php @@ -0,0 +1,33 @@ +assertEquals('foo', $node->getAttribute('data')); + } + + public function getTests() + { + $tests = []; + $tests[] = [new TextNode('foo', 1), "// line 1\nyield \"foo\";"]; + + return $tests; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/NodeVisitor/OptimizerTest.php b/upload/system/storage/vendor/twig/twig/tests/NodeVisitor/OptimizerTest.php new file mode 100644 index 000000000..12dc1214d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/NodeVisitor/OptimizerTest.php @@ -0,0 +1,149 @@ +expectNotToPerformAssertions(); + + new OptimizerNodeVisitor(OptimizerNodeVisitor::OPTIMIZE_FOR); + } + + public function testRenderBlockOptimizer() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + + $stream = $env->parse($env->tokenize(new Source('{{ block("foo") }}', 'index'))); + + $node = $stream->getNode('body')->getNode('0'); + + $this->assertInstanceOf(BlockReferenceExpression::class, $node); + $this->assertTrue($node->getAttribute('output')); + } + + public function testRenderParentBlockOptimizer() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + + $stream = $env->parse($env->tokenize(new Source('{% extends "foo" %}{% block content %}{{ parent() }}{% endblock %}', 'index'))); + + $node = $stream->getNode('blocks')->getNode('content')->getNode('0')->getNode('body'); + + $this->assertInstanceOf(ParentExpression::class, $node); + $this->assertTrue($node->getAttribute('output')); + } + + public function testForVarOptimizer() + { + $env = new Environment(new ArrayLoader(), ['cache' => false, 'autoescape' => false]); + + $template = '{% for i, j in foo %}{{ loop.index }}{{ i }}{{ j }}{% endfor %}'; + $stream = $env->parse($env->tokenize(new Source($template, 'index'))); + + foreach (['loop', 'i', 'j'] as $target) { + $this->checkForVarConfiguration($stream, $target); + } + } + + public function checkForVarConfiguration(Node $node, $target) + { + foreach ($node as $n) { + if (NameExpression::class === get_class($n) && $target === $n->getAttribute('name')) { + $this->assertTrue($n->getAttribute('always_defined')); + } else { + $this->checkForVarConfiguration($n, $target); + } + } + } + + /** + * @dataProvider getTestsForForLoopOptimizer + */ + public function testForLoopOptimizer($template, $expected) + { + $env = new Environment(new ArrayLoader(), ['cache' => false]); + + $stream = $env->parse($env->tokenize(new Source($template, 'index'))); + + foreach ($expected as $target => $withLoop) { + $this->assertTrue($this->checkForLoopConfiguration($stream, $target, $withLoop), \sprintf('variable %s is %soptimized', $target, $withLoop ? 'not ' : '')); + } + } + + public function getTestsForForLoopOptimizer() + { + return [ + ['{% for i in foo %}{% endfor %}', ['i' => false]], + + ['{% for i in foo %}{{ loop.index }}{% endfor %}', ['i' => true]], + + ['{% for i in foo %}{% for j in foo %}{% endfor %}{% endfor %}', ['i' => false, 'j' => false]], + + ['{% for i in foo %}{% include "foo" %}{% endfor %}', ['i' => true]], + + ['{% for i in foo %}{% include "foo" only %}{% endfor %}', ['i' => false]], + + ['{% for i in foo %}{% include "foo" with { "foo": "bar" } only %}{% endfor %}', ['i' => false]], + + ['{% for i in foo %}{% include "foo" with { "foo": loop.index } only %}{% endfor %}', ['i' => true]], + + ['{% for i in foo %}{% for j in foo %}{{ loop.index }}{% endfor %}{% endfor %}', ['i' => false, 'j' => true]], + + ['{% for i in foo %}{% for j in foo %}{{ loop.parent.loop.index }}{% endfor %}{% endfor %}', ['i' => true, 'j' => true]], + + ['{% for i in foo %}{% set l = loop %}{% for j in foo %}{{ l.index }}{% endfor %}{% endfor %}', ['i' => true, 'j' => false]], + + ['{% for i in foo %}{% for j in foo %}{{ foo.parent.loop.index }}{% endfor %}{% endfor %}', ['i' => false, 'j' => false]], + + ['{% for i in foo %}{% for j in foo %}{{ loop["parent"].loop.index }}{% endfor %}{% endfor %}', ['i' => true, 'j' => true]], + + ['{% for i in foo %}{{ include("foo") }}{% endfor %}', ['i' => true]], + + ['{% for i in foo %}{{ include("foo", with_context = false) }}{% endfor %}', ['i' => false]], + + ['{% for i in foo %}{{ include("foo", with_context = true) }}{% endfor %}', ['i' => true]], + + ['{% for i in foo %}{{ include("foo", { "foo": "bar" }, with_context = false) }}{% endfor %}', ['i' => false]], + + ['{% for i in foo %}{{ include("foo", { "foo": loop.index }, with_context = false) }}{% endfor %}', ['i' => true]], + ]; + } + + public function checkForLoopConfiguration(Node $node, $target, $withLoop) + { + foreach ($node as $n) { + if ($n instanceof ForNode) { + if ($target === $n->getNode('value_target')->getAttribute('name')) { + return $withLoop == $n->getAttribute('with_loop'); + } + } + + $ret = $this->checkForLoopConfiguration($n, $target, $withLoop); + if (null !== $ret) { + return $ret; + } + } + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/NodeVisitor/SandboxTest.php b/upload/system/storage/vendor/twig/twig/tests/NodeVisitor/SandboxTest.php new file mode 100644 index 000000000..e0b8cbae6 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/NodeVisitor/SandboxTest.php @@ -0,0 +1,41 @@ +setAttribute('is_generator', true); + $node = new ModuleNode(new BodyNode([new PrintNode($expr, 1)]), null, new Node(), new Node(), new Node(), new Node([]), new Source('foo', 'foo')); + $traverser = new NodeTraverser($env, [new SandboxNodeVisitor($env)]); + $node = $traverser->traverse($node); + + $this->assertNotInstanceOf(CheckToStringNode::class, $node->getNode('body')->getNode(0)->getNode('expr')); + $this->assertSame("// line 1\nyield from (\$context[\"foo\"] ?? null);\n", $env->compile($node->getNode('body'))); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/ParserTest.php b/upload/system/storage/vendor/twig/twig/tests/ParserTest.php new file mode 100644 index 000000000..e0c0de7fe --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/ParserTest.php @@ -0,0 +1,237 @@ +expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "foo" tag. Did you mean "for" at line 1?'); + + $stream = new TokenStream([ + new Token(Token::BLOCK_START_TYPE, '', 1), + new Token(Token::NAME_TYPE, 'foo', 1), + new Token(Token::BLOCK_END_TYPE, '', 1), + new Token(Token::EOF_TYPE, '', 1), + ]); + $parser = new Parser(new Environment(new ArrayLoader())); + $parser->parse($stream); + } + + public function testUnknownTagWithoutSuggestions() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown "foobar" tag at line 1.'); + + $stream = new TokenStream([ + new Token(Token::BLOCK_START_TYPE, '', 1), + new Token(Token::NAME_TYPE, 'foobar', 1), + new Token(Token::BLOCK_END_TYPE, '', 1), + new Token(Token::EOF_TYPE, '', 1), + ]); + $parser = new Parser(new Environment(new ArrayLoader())); + $parser->parse($stream); + } + + /** + * @dataProvider getFilterBodyNodesData + */ + public function testFilterBodyNodes($input, $expected) + { + $parser = $this->getParser(); + $m = new \ReflectionMethod($parser, 'filterBodyNodes'); + $m->setAccessible(true); + + $this->assertEquals($expected, $m->invoke($parser, $input)); + } + + public function getFilterBodyNodesData() + { + return [ + [ + new Node([new TextNode(' ', 1)]), + new Node([]), + ], + [ + $input = new Node([new SetNode(false, new Node(), new Node(), 1)]), + $input, + ], + [ + $input = new Node([new SetNode(true, new Node(), new Node([new Node([new TextNode('foo', 1)])]), 1)]), + $input, + ], + ]; + } + + /** + * @dataProvider getFilterBodyNodesDataThrowsException + */ + public function testFilterBodyNodesThrowsException($input) + { + $this->expectException(SyntaxError::class); + + $parser = $this->getParser(); + + $m = new \ReflectionMethod($parser, 'filterBodyNodes'); + $m->setAccessible(true); + + $m->invoke($parser, $input); + } + + public function getFilterBodyNodesDataThrowsException() + { + return [ + [new TextNode('foo', 1)], + [new Node([new Node([new TextNode('foo', 1)])])], + ]; + } + + /** + * @dataProvider getFilterBodyNodesWithBOMData + */ + public function testFilterBodyNodesWithBOM($emptyNode) + { + $parser = $this->getParser(); + + $m = new \ReflectionMethod($parser, 'filterBodyNodes'); + $m->setAccessible(true); + $this->assertNull($m->invoke($parser, new TextNode(\chr(0xEF).\chr(0xBB).\chr(0xBF).$emptyNode, 1))); + } + + public function getFilterBodyNodesWithBOMData() + { + return [ + [' '], + ["\t"], + ["\n"], + ["\n\t\n "], + ]; + } + + public function testParseIsReentrant() + { + $twig = new Environment(new ArrayLoader(), [ + 'autoescape' => false, + 'optimizations' => 0, + ]); + $twig->addTokenParser(new TestTokenParser()); + + $parser = new Parser($twig); + + $parser->parse(new TokenStream([ + new Token(Token::BLOCK_START_TYPE, '', 1), + new Token(Token::NAME_TYPE, 'test', 1), + new Token(Token::BLOCK_END_TYPE, '', 1), + new Token(Token::VAR_START_TYPE, '', 1), + new Token(Token::NAME_TYPE, 'foo', 1), + new Token(Token::VAR_END_TYPE, '', 1), + new Token(Token::EOF_TYPE, '', 1), + ])); + + $p = new \ReflectionProperty($parser, 'parent'); + $p->setAccessible(true); + $this->assertNull($p->getValue($parser)); + } + + public function testGetVarName() + { + $twig = new Environment(new ArrayLoader(), [ + 'autoescape' => false, + 'optimizations' => 0, + ]); + + $twig->parse($twig->tokenize(new Source(<<addToAssertionCount(1); + } + + public function testImplicitMacroArgumentDefaultValues() + { + $template = '{% macro marco (po, lo = true) %}{% endmacro %}'; + $lexer = new Lexer(new Environment(new ArrayLoader())); + $stream = $lexer->tokenize(new Source($template, 'index')); + + $argumentNodes = $this->getParser() + ->parse($stream) + ->getNode('macros') + ->getNode('marco') + ->getNode('arguments') + ; + + $this->assertTrue($argumentNodes->getNode('po')->hasAttribute('is_implicit')); + $this->assertTrue($argumentNodes->getNode('po')->getAttribute('is_implicit')); + $this->assertNull($argumentNodes->getNode('po')->getAttribute('value')); + + $this->assertFalse($argumentNodes->getNode('lo')->hasAttribute('is_implicit')); + $this->assertSame(true, $argumentNodes->getNode('lo')->getAttribute('value')); + } + + protected function getParser() + { + $parser = new Parser(new Environment(new ArrayLoader())); + $parser->setParent(new Node()); + + $p = new \ReflectionProperty($parser, 'stream'); + $p->setAccessible(true); + $p->setValue($parser, new TokenStream([])); + + return $parser; + } +} + +class TestTokenParser extends AbstractTokenParser +{ + public function parse(Token $token): Node + { + // simulate the parsing of another template right in the middle of the parsing of the current template + $this->parser->parse(new TokenStream([ + new Token(Token::BLOCK_START_TYPE, '', 1), + new Token(Token::NAME_TYPE, 'extends', 1), + new Token(Token::STRING_TYPE, 'base', 1), + new Token(Token::BLOCK_END_TYPE, '', 1), + new Token(Token::EOF_TYPE, '', 1), + ])); + + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); + + return new Node([], [], 1); + } + + public function getTag(): string + { + return 'test'; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/AbstractTest.php b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/AbstractTest.php new file mode 100644 index 000000000..1891c2750 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/AbstractTest.php @@ -0,0 +1,106 @@ +getIndexProfile( + [ + $this->getEmbeddedBlockProfile(), + $this->getEmbeddedTemplateProfile( + [ + $this->getIncludedTemplateProfile(), + ] + ), + $this->getMacroProfile(), + $this->getEmbeddedTemplateProfile( + [ + $this->getIncludedTemplateProfile(), + ] + ), + ] + ), + ]; + + $p = new \ReflectionProperty($profile, 'profiles'); + $p->setAccessible(true); + $p->setValue($profile, $subProfiles); + + return $profile; + } + + private function getIndexProfile(array $subProfiles = []) + { + return $this->generateProfile('main', 1, 'template', 'index.twig', $subProfiles); + } + + private function getEmbeddedBlockProfile(array $subProfiles = []) + { + return $this->generateProfile('body', 0.0001, 'block', 'embedded.twig', $subProfiles); + } + + private function getEmbeddedTemplateProfile(array $subProfiles = []) + { + return $this->generateProfile('main', 0.0001, 'template', 'embedded.twig', $subProfiles); + } + + private function getIncludedTemplateProfile(array $subProfiles = []) + { + return $this->generateProfile('main', 0.0001, 'template', 'included.twig', $subProfiles); + } + + private function getMacroProfile(array $subProfiles = []) + { + return $this->generateProfile('foo', 0.0001, 'macro', 'index.twig', $subProfiles); + } + + /** + * @param string $name + * @param float $duration + * @param string $type + * @param string $templateName + * + * @return Profile + */ + private function generateProfile($name, $duration, $type, $templateName, array $subProfiles = []) + { + $profile = new Profile($templateName, $type, $name); + + $p = new \ReflectionProperty($profile, 'profiles'); + $p->setAccessible(true); + $p->setValue($profile, $subProfiles); + + $starts = new \ReflectionProperty($profile, 'starts'); + $starts->setAccessible(true); + $starts->setValue($profile, [ + 'wt' => 0, + 'mu' => 0, + 'pmu' => 0, + ]); + $ends = new \ReflectionProperty($profile, 'ends'); + $ends->setAccessible(true); + $ends->setValue($profile, [ + 'wt' => $duration, + 'mu' => 0, + 'pmu' => 0, + ]); + + return $profile; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/BlackfireTest.php b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/BlackfireTest.php new file mode 100644 index 000000000..3a33d9403 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/BlackfireTest.php @@ -0,0 +1,36 @@ +assertStringMatchesFormat(<<index.twig//1 %d %d %d +index.twig==>embedded.twig::block(body)//1 %d %d 0 +index.twig==>embedded.twig//2 %d %d %d +embedded.twig==>included.twig//2 %d %d %d +index.twig==>index.twig::macro(foo)//1 %d %d %d +EOF + , $dumper->dump($this->getProfile())); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/HtmlTest.php b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/HtmlTest.php new file mode 100644 index 000000000..2dcbb9aec --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/HtmlTest.php @@ -0,0 +1,34 @@ +assertStringMatchesFormat(<<main %d.%dms/%d% +└ index.twig %d.%dms/%d% + └ embedded.twig::block(body) + └ embedded.twig + │ └ included.twig + └ index.twig::macro(foo) + └ embedded.twig + └ included.twig + +EOF + , $dumper->dump($this->getProfile())); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/TextTest.php b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/TextTest.php new file mode 100644 index 000000000..ba19c2c90 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Profiler/Dumper/TextTest.php @@ -0,0 +1,34 @@ +assertStringMatchesFormat(<<dump($this->getProfile())); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Profiler/ProfileTest.php b/upload/system/storage/vendor/twig/twig/tests/Profiler/ProfileTest.php new file mode 100644 index 000000000..5f41fc9c1 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Profiler/ProfileTest.php @@ -0,0 +1,115 @@ +assertEquals('template', $profile->getTemplate()); + $this->assertEquals('type', $profile->getType()); + $this->assertEquals('name', $profile->getName()); + } + + public function testIsRoot() + { + $profile = new Profile('template', Profile::ROOT); + $this->assertTrue($profile->isRoot()); + + $profile = new Profile('template', Profile::TEMPLATE); + $this->assertFalse($profile->isRoot()); + } + + public function testIsTemplate() + { + $profile = new Profile('template', Profile::TEMPLATE); + $this->assertTrue($profile->isTemplate()); + + $profile = new Profile('template', Profile::ROOT); + $this->assertFalse($profile->isTemplate()); + } + + public function testIsBlock() + { + $profile = new Profile('template', Profile::BLOCK); + $this->assertTrue($profile->isBlock()); + + $profile = new Profile('template', Profile::ROOT); + $this->assertFalse($profile->isBlock()); + } + + public function testIsMacro() + { + $profile = new Profile('template', Profile::MACRO); + $this->assertTrue($profile->isMacro()); + + $profile = new Profile('template', Profile::ROOT); + $this->assertFalse($profile->isMacro()); + } + + public function testGetAddProfile() + { + $profile = new Profile(); + $profile->addProfile($a = new Profile()); + $profile->addProfile($b = new Profile()); + + $this->assertSame([$a, $b], $profile->getProfiles()); + $this->assertSame([$a, $b], iterator_to_array($profile)); + } + + public function testGetDuration() + { + $profile = new Profile(); + usleep(1); + $profile->leave(); + + $this->assertTrue($profile->getDuration() > 0, \sprintf('Expected duration > 0, got: %f', $profile->getDuration())); + } + + public function testSerialize() + { + $profile = new Profile('template', 'type', 'name'); + $profile1 = new Profile('template1', 'type1', 'name1'); + $profile->addProfile($profile1); + $profile->leave(); + $profile1->leave(); + + $profile2 = unserialize(serialize($profile)); + $profiles = $profile->getProfiles(); + $this->assertCount(1, $profiles); + $profile3 = $profiles[0]; + + $this->assertEquals($profile->getTemplate(), $profile2->getTemplate()); + $this->assertEquals($profile->getType(), $profile2->getType()); + $this->assertEquals($profile->getName(), $profile2->getName()); + $this->assertEquals($profile->getDuration(), $profile2->getDuration()); + + $this->assertEquals($profile1->getTemplate(), $profile3->getTemplate()); + $this->assertEquals($profile1->getType(), $profile3->getType()); + $this->assertEquals($profile1->getName(), $profile3->getName()); + } + + public function testReset() + { + $profile = new Profile(); + usleep(1); + $profile->leave(); + $profile->reset(); + + $this->assertEquals(0, $profile->getDuration()); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Runtime/EscaperRuntimeTest.php b/upload/system/storage/vendor/twig/twig/tests/Runtime/EscaperRuntimeTest.php new file mode 100644 index 000000000..593563a3b --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Runtime/EscaperRuntimeTest.php @@ -0,0 +1,406 @@ + ''', + '"' => '"', + '<' => '<', + '>' => '>', + '&' => '&', + ]; + + protected $htmlAttrSpecialChars = [ + '\'' => ''', + /* Characters beyond ASCII value 255 to unicode escape */ + 'Ā' => 'Ā', + '😀' => '😀', + /* Immune chars excluded */ + ',' => ',', + '.' => '.', + '-' => '-', + '_' => '_', + /* Basic alnums excluded */ + 'a' => 'a', + 'A' => 'A', + 'z' => 'z', + 'Z' => 'Z', + '0' => '0', + '9' => '9', + /* Basic control characters and null */ + "\r" => ' ', + "\n" => ' ', + "\t" => ' ', + "\0" => '�', // should use Unicode replacement char + /* Encode chars as named entities where possible */ + '<' => '<', + '>' => '>', + '&' => '&', + '"' => '"', + /* Encode spaces for quoteless attribute protection */ + ' ' => ' ', + ]; + + protected $jsSpecialChars = [ + /* HTML special chars - escape without exception to hex */ + '<' => '\\u003C', + '>' => '\\u003E', + '\'' => '\\u0027', + '"' => '\\u0022', + '&' => '\\u0026', + '/' => '\\/', + /* Characters beyond ASCII value 255 to unicode escape */ + 'Ā' => '\\u0100', + '😀' => '\\uD83D\\uDE00', + /* Immune chars excluded */ + ',' => ',', + '.' => '.', + '_' => '_', + /* Basic alnums excluded */ + 'a' => 'a', + 'A' => 'A', + 'z' => 'z', + 'Z' => 'Z', + '0' => '0', + '9' => '9', + /* Basic control characters and null */ + "\r" => '\r', + "\n" => '\n', + "\x08" => '\b', + "\t" => '\t', + "\x0C" => '\f', + "\0" => '\\u0000', + /* Encode spaces for quoteless attribute protection */ + ' ' => '\\u0020', + ]; + + protected $urlSpecialChars = [ + /* HTML special chars - escape without exception to percent encoding */ + '<' => '%3C', + '>' => '%3E', + '\'' => '%27', + '"' => '%22', + '&' => '%26', + /* Characters beyond ASCII value 255 to hex sequence */ + 'Ā' => '%C4%80', + /* Punctuation and unreserved check */ + ',' => '%2C', + '.' => '.', + '_' => '_', + '-' => '-', + ':' => '%3A', + ';' => '%3B', + '!' => '%21', + /* Basic alnums excluded */ + 'a' => 'a', + 'A' => 'A', + 'z' => 'z', + 'Z' => 'Z', + '0' => '0', + '9' => '9', + /* Basic control characters and null */ + "\r" => '%0D', + "\n" => '%0A', + "\t" => '%09', + "\0" => '%00', + /* PHP quirks from the past */ + ' ' => '%20', + '~' => '~', + '+' => '%2B', + ]; + + protected $cssSpecialChars = [ + /* HTML special chars - escape without exception to hex */ + '<' => '\\3C ', + '>' => '\\3E ', + '\'' => '\\27 ', + '"' => '\\22 ', + '&' => '\\26 ', + /* Characters beyond ASCII value 255 to unicode escape */ + 'Ā' => '\\100 ', + /* Immune chars excluded */ + ',' => '\\2C ', + '.' => '\\2E ', + '_' => '\\5F ', + /* Basic alnums excluded */ + 'a' => 'a', + 'A' => 'A', + 'z' => 'z', + 'Z' => 'Z', + '0' => '0', + '9' => '9', + /* Basic control characters and null */ + "\r" => '\\D ', + "\n" => '\\A ', + "\t" => '\\9 ', + "\0" => '\\0 ', + /* Encode spaces for quoteless attribute protection */ + ' ' => '\\20 ', + ]; + + public function testHtmlEscapingConvertsSpecialChars() + { + foreach ($this->htmlSpecialChars as $key => $value) { + $this->assertEquals($value, (new EscaperRuntime())->escape($key, 'html'), 'Failed to escape: '.$key); + } + } + + public function testHtmlAttributeEscapingConvertsSpecialChars() + { + foreach ($this->htmlAttrSpecialChars as $key => $value) { + $this->assertEquals($value, (new EscaperRuntime())->escape($key, 'html_attr'), 'Failed to escape: '.$key); + } + } + + public function testJavascriptEscapingConvertsSpecialChars() + { + foreach ($this->jsSpecialChars as $key => $value) { + $this->assertEquals($value, (new EscaperRuntime())->escape($key, 'js'), 'Failed to escape: '.$key); + } + } + + public function testJavascriptEscapingConvertsSpecialCharsWithInternalEncoding() + { + $previousInternalEncoding = mb_internal_encoding(); + try { + mb_internal_encoding('ISO-8859-1'); + foreach ($this->jsSpecialChars as $key => $value) { + $this->assertEquals($value, (new EscaperRuntime())->escape($key, 'js'), 'Failed to escape: '.$key); + } + } finally { + if (false !== $previousInternalEncoding) { + mb_internal_encoding($previousInternalEncoding); + } + } + } + + public function testJavascriptEscapingReturnsStringIfZeroLength() + { + $this->assertEquals('', (new EscaperRuntime())->escape('', 'js')); + } + + public function testJavascriptEscapingReturnsStringIfContainsOnlyDigits() + { + $this->assertEquals('123', (new EscaperRuntime())->escape('123', 'js')); + } + + public function testCssEscapingConvertsSpecialChars() + { + foreach ($this->cssSpecialChars as $key => $value) { + $this->assertEquals($value, (new EscaperRuntime())->escape($key, 'css'), 'Failed to escape: '.$key); + } + } + + public function testCssEscapingReturnsStringIfZeroLength() + { + $this->assertEquals('', (new EscaperRuntime())->escape('', 'css')); + } + + public function testCssEscapingReturnsStringIfContainsOnlyDigits() + { + $this->assertEquals('123', (new EscaperRuntime())->escape('123', 'css')); + } + + public function testUrlEscapingConvertsSpecialChars() + { + foreach ($this->urlSpecialChars as $key => $value) { + $this->assertEquals($value, (new EscaperRuntime())->escape($key, 'url'), 'Failed to escape: '.$key); + } + } + + /** + * Range tests to confirm escaped range of characters is within OWASP recommendation. + */ + + /** + * Only testing the first few 2 ranges on this prot. function as that's all these + * other range tests require. + */ + public function testUnicodeCodepointConversionToUtf8() + { + $expected = ' ~ޙ'; + $codepoints = [0x20, 0x7E, 0x799]; + $result = ''; + foreach ($codepoints as $value) { + $result .= $this->codepointToUtf8($value); + } + $this->assertEquals($expected, $result); + } + + /** + * Convert a Unicode Codepoint to a literal UTF-8 character. + * + * @param int $codepoint Unicode codepoint in hex notation + * + * @return string UTF-8 literal string + */ + protected function codepointToUtf8($codepoint) + { + if ($codepoint < 0x80) { + return \chr($codepoint); + } + if ($codepoint < 0x800) { + return \chr($codepoint >> 6 & 0x3F | 0xC0) + .\chr($codepoint & 0x3F | 0x80); + } + if ($codepoint < 0x10000) { + return \chr($codepoint >> 12 & 0x0F | 0xE0) + .\chr($codepoint >> 6 & 0x3F | 0x80) + .\chr($codepoint & 0x3F | 0x80); + } + if ($codepoint < 0x110000) { + return \chr($codepoint >> 18 & 0x07 | 0xF0) + .\chr($codepoint >> 12 & 0x3F | 0x80) + .\chr($codepoint >> 6 & 0x3F | 0x80) + .\chr($codepoint & 0x3F | 0x80); + } + throw new \Exception('Codepoint requested outside of Unicode range.'); + } + + public function testJavascriptEscapingEscapesOwaspRecommendedRanges() + { + $immune = [',', '.', '_']; // Exceptions to escaping ranges + for ($chr = 0; $chr < 0xFF; ++$chr) { + if ($chr >= 0x30 && $chr <= 0x39 + || $chr >= 0x41 && $chr <= 0x5A + || $chr >= 0x61 && $chr <= 0x7A) { + $literal = $this->codepointToUtf8($chr); + $this->assertEquals($literal, (new EscaperRuntime())->escape($literal, 'js')); + } else { + $literal = $this->codepointToUtf8($chr); + if (\in_array($literal, $immune)) { + $this->assertEquals($literal, (new EscaperRuntime())->escape($literal, 'js')); + } else { + $this->assertNotEquals( + $literal, + (new EscaperRuntime())->escape($literal, 'js'), + "$literal should be escaped!"); + } + } + } + } + + public function testHtmlAttributeEscapingEscapesOwaspRecommendedRanges() + { + $immune = [',', '.', '-', '_']; // Exceptions to escaping ranges + for ($chr = 0; $chr < 0xFF; ++$chr) { + if ($chr >= 0x30 && $chr <= 0x39 + || $chr >= 0x41 && $chr <= 0x5A + || $chr >= 0x61 && $chr <= 0x7A) { + $literal = $this->codepointToUtf8($chr); + $this->assertEquals($literal, (new EscaperRuntime())->escape($literal, 'html_attr')); + } else { + $literal = $this->codepointToUtf8($chr); + if (\in_array($literal, $immune)) { + $this->assertEquals($literal, (new EscaperRuntime())->escape($literal, 'html_attr')); + } else { + $this->assertNotEquals( + $literal, + (new EscaperRuntime())->escape($literal, 'html_attr'), + "$literal should be escaped!"); + } + } + } + } + + public function testCssEscapingEscapesOwaspRecommendedRanges() + { + // CSS has no exceptions to escaping ranges + for ($chr = 0; $chr < 0xFF; ++$chr) { + if ($chr >= 0x30 && $chr <= 0x39 + || $chr >= 0x41 && $chr <= 0x5A + || $chr >= 0x61 && $chr <= 0x7A) { + $literal = $this->codepointToUtf8($chr); + $this->assertEquals($literal, (new EscaperRuntime())->escape($literal, 'css')); + } else { + $literal = $this->codepointToUtf8($chr); + $this->assertNotEquals( + $literal, + (new EscaperRuntime())->escape($literal, 'css'), + "$literal should be escaped!"); + } + } + } + + public function testUnknownCustomEscaper() + { + $this->expectException(RuntimeError::class); + + (new EscaperRuntime())->escape('foo', 'bar'); + } + + /** + * @dataProvider provideCustomEscaperCases + */ + public function testCustomEscaper($expected, $string, $strategy, $charset) + { + $escaper = new EscaperRuntime(); + $escaper->setEscaper('foo', 'Twig\Tests\Runtime\escaper'); + $this->assertSame($expected, $escaper->escape($string, $strategy, $charset)); + } + + public function provideCustomEscaperCases() + { + return [ + ['foo**ISO-8859-1', 'foo', 'foo', 'ISO-8859-1'], + ['**ISO-8859-1', null, 'foo', 'ISO-8859-1'], + ['42**UTF-8', 42, 'foo', null], + ]; + } + + /** + * @dataProvider provideObjectsForEscaping + */ + public function testObjectEscaping(string $escapedHtml, string $escapedJs, array $safeClasses) + { + $obj = new Extension_TestClass(); + $escaper = new EscaperRuntime(); + $escaper->setSafeClasses($safeClasses); + $this->assertSame($escapedHtml, $escaper->escape($obj, 'html', null, true)); + $this->assertSame($escapedJs, $escaper->escape($obj, 'js', null, true)); + } + + public function provideObjectsForEscaping() + { + return [ + ['<br />', '
    ', ['\Twig\Tests\Runtime\Extension_TestClass' => ['js']]], + ['
    ', '\u003Cbr\u0020\/\u003E', ['\Twig\Tests\Runtime\Extension_TestClass' => ['html']]], + ['<br />', '
    ', ['\Twig\Tests\Runtime\Extension_SafeHtmlInterface' => ['js']]], + ['
    ', '
    ', ['\Twig\Tests\Runtime\Extension_SafeHtmlInterface' => ['all']]], + ]; + } +} + +function escaper($string, $charset) +{ + return $string.'**'.$charset; +} + +interface Extension_SafeHtmlInterface +{ +} +class Extension_TestClass implements Extension_SafeHtmlInterface +{ + public function __toString() + { + return '
    '; + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/TemplateTest.php b/upload/system/storage/vendor/twig/twig/tests/TemplateTest.php new file mode 100644 index 000000000..884226e0a --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/TemplateTest.php @@ -0,0 +1,768 @@ +expectException(\LogicException::class); + + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig); + $template->displayBlock('foo', [], ['foo' => [new \stdClass(), 'foo']]); + } + + /** + * @dataProvider getAttributeExceptions + */ + public function testGetAttributeExceptions($template, $message) + { + $templates = ['index' => $template]; + $env = new Environment(new ArrayLoader($templates), ['strict_variables' => true]); + $template = $env->load('index'); + + $context = [ + 'string' => 'foo', + 'null' => null, + 'empty_array' => [], + 'array' => ['foo' => 'foo'], + 'array_access' => new TemplateArrayAccessObject(), + 'magic_exception' => new TemplateMagicPropertyObjectWithException(), + 'object' => new \stdClass(), + ]; + + try { + $template->render($context); + $this->fail('Accessing an invalid attribute should throw an exception.'); + } catch (RuntimeError $e) { + $this->assertSame(\sprintf($message, 'index'), $e->getMessage()); + } + } + + public function getAttributeExceptions() + { + return [ + ['{{ string["a"] }}', 'Impossible to access a key ("a") on a string variable ("foo") in "%s" at line 1.'], + ['{{ null["a"] }}', 'Impossible to access a key ("a") on a null variable in "%s" at line 1.'], + ['{{ empty_array["a"] }}', 'Key "a" does not exist as the sequence/mapping is empty in "%s" at line 1.'], + ['{{ array["a"] }}', 'Key "a" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'], + ['{{ array_access["a"] }}', 'Key "a" in object with ArrayAccess of class "Twig\Tests\TemplateArrayAccessObject" does not exist in "%s" at line 1.'], + ['{{ string.a }}', 'Impossible to access an attribute ("a") on a string variable ("foo") in "%s" at line 1.'], + ['{{ string.a() }}', 'Impossible to invoke a method ("a") on a string variable ("foo") in "%s" at line 1.'], + ['{{ null.a }}', 'Impossible to access an attribute ("a") on a null variable in "%s" at line 1.'], + ['{{ null.a() }}', 'Impossible to invoke a method ("a") on a null variable in "%s" at line 1.'], + ['{{ array.a() }}', 'Impossible to invoke a method ("a") on a sequence/mapping in "%s" at line 1.'], + ['{{ empty_array.a }}', 'Key "a" does not exist as the sequence/mapping is empty in "%s" at line 1.'], + ['{{ array.a }}', 'Key "a" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'], + ['{{ attribute(array, -10) }}', 'Key "-10" for sequence/mapping with keys "foo" does not exist in "%s" at line 1.'], + ['{{ array_access.a }}', 'Neither the property "a" nor one of the methods "a()", "geta()"/"isa()"/"hasa()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'], + ['{% from _self import foo %}{% macro foo(obj) %}{{ obj.missing_method() }}{% endmacro %}{{ foo(array_access) }}', 'Neither the property "missing_method" nor one of the methods "missing_method()", "getmissing_method()"/"ismissing_method()"/"hasmissing_method()" or "__call()" exist and have public access in class "Twig\Tests\TemplateArrayAccessObject" in "%s" at line 1.'], + ['{{ magic_exception.test }}', 'An exception has been thrown during the rendering of a template ("Hey! Don\'t try to isset me!") in "%s" at line 1.'], + ['{{ object["a"] }}', 'Impossible to access a key "a" on an object of class "stdClass" that does not implement ArrayAccess interface in "%s" at line 1.'], + ]; + } + + /** + * @dataProvider getGetAttributeWithSandbox + */ + public function testGetAttributeWithSandbox($object, $item, $allowed) + { + $twig = new Environment(new ArrayLoader()); + $policy = new SecurityPolicy([], [], [/* method */], [/* prop */], []); + $twig->addExtension(new SandboxExtension($policy, !$allowed)); + $template = new TemplateForTest($twig); + + try { + CoreExtension::getAttribute($twig, $template->getSourceContext(), $object, $item, [], 'any', false, false, true); + + if (!$allowed) { + $this->fail(); + } else { + $this->addToAssertionCount(1); + } + } catch (SecurityError $e) { + if ($allowed) { + $this->fail(); + } else { + $this->addToAssertionCount(1); + } + + $this->assertStringContainsString('is not allowed', $e->getMessage()); + } + } + + public function getGetAttributeWithSandbox() + { + return [ + [new TemplatePropertyObject(), 'defined', false], + [new TemplatePropertyObject(), 'defined', true], + [new TemplateMethodObject(), 'defined', false], + [new TemplateMethodObject(), 'defined', true], + ]; + } + + /** + * @dataProvider getRenderTemplateWithoutOutputData + */ + public function testRenderTemplateWithoutOutput(string $template) + { + $twig = new Environment(new ArrayLoader(['index' => $template])); + $this->assertSame('', $twig->render('index')); + } + + public function getRenderTemplateWithoutOutputData() + { + return [ + [''], + ['{% for var in [] %}{% endfor %}'], + ['{% if false %}{% endif %}'], + ]; + } + + public function testRenderBlockWithUndefinedBlock() + { + $this->expectException(RuntimeError::class); + $this->expectExceptionMessage('Block "unknown" on template "index.twig" does not exist in "index.twig".'); + + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig, 'index.twig'); + $template->renderBlock('unknown', []); + } + + public function testDisplayBlockWithUndefinedBlock() + { + $this->expectException(RuntimeError::class); + $this->expectExceptionMessage('Block "unknown" on template "index.twig" does not exist in "index.twig".'); + + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig, 'index.twig'); + $template->displayBlock('unknown', []); + } + + public function testDisplayBlockWithUndefinedParentBlock() + { + $this->expectException(RuntimeError::class); + $this->expectExceptionMessage('Block "foo" should not call parent() in "index.twig" as the block does not exist in the parent template "parent.twig"'); + + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig, 'parent.twig'); + $template->displayBlock('foo', [], ['foo' => [new TemplateForTest($twig, 'index.twig'), 'block_foo']], false); + } + + public function testGetAttributeOnArrayWithConfusableKey() + { + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig); + + $array = ['Zero', 'One', -1 => 'MinusOne', '' => 'EmptyString', '1.5' => 'FloatButString', '01' => 'IntegerButStringWithLeadingZeros']; + + $this->assertSame('Zero', $array[false]); + $this->assertSame('One', $array[true]); + if (\PHP_VERSION_ID < 80100) { + // This line will trigger a deprecation warning on PHP 8.1. + $this->assertSame('One', $array[1.5]); + } + $this->assertSame('One', $array['1']); + if (\PHP_VERSION_ID < 80100) { + // This line will trigger a deprecation warning on PHP 8.1. + $this->assertSame('MinusOne', $array[-1.5]); + } + $this->assertSame('FloatButString', $array['1.5']); + $this->assertSame('IntegerButStringWithLeadingZeros', $array['01']); + $this->assertSame('EmptyString', $array[null]); + + $this->assertSame('Zero', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, false), 'false is treated as 0 when accessing a sequence/mapping (equals PHP behavior)'); + $this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, true), 'true is treated as 1 when accessing a sequence/mapping (equals PHP behavior)'); + $this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, 1.5), 'float is casted to int when accessing a sequence/mapping (equals PHP behavior)'); + $this->assertSame('One', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '1'), '"1" is treated as integer 1 when accessing a sequence/mapping (equals PHP behavior)'); + $this->assertSame('MinusOne', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, -1.5), 'negative float is casted to int when accessing a sequence/mapping (equals PHP behavior)'); + $this->assertSame('FloatButString', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '1.5'), '"1.5" is treated as-is when accessing a sequence/mapping (equals PHP behavior)'); + $this->assertSame('IntegerButStringWithLeadingZeros', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, '01'), '"01" is treated as-is when accessing a sequence/mapping (equals PHP behavior)'); + $this->assertSame('EmptyString', CoreExtension::getAttribute($twig, $template->getSourceContext(), $array, null), 'null is treated as "" when accessing a sequence/mapping (equals PHP behavior)'); + } + + /** + * @dataProvider getGetAttributeTests + */ + public function testGetAttribute($defined, $value, $object, $item, $arguments, $type) + { + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig); + + $this->assertEquals($value, CoreExtension::getAttribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type)); + } + + /** + * @dataProvider getGetAttributeTests + */ + public function testGetAttributeStrict($defined, $value, $object, $item, $arguments, $type, $exceptionMessage = null) + { + $twig = new Environment(new ArrayLoader(), ['strict_variables' => true]); + $template = new TemplateForTest($twig); + + if ($defined) { + $this->assertEquals($value, CoreExtension::getAttribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type)); + } else { + $this->expectException(RuntimeError::class); + if (null !== $exceptionMessage) { + $this->expectExceptionMessage($exceptionMessage); + } + $this->assertEquals($value, CoreExtension::getAttribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type)); + } + } + + /** + * @dataProvider getGetAttributeTests + */ + public function testGetAttributeDefined($defined, $value, $object, $item, $arguments, $type) + { + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig); + + $this->assertEquals($defined, CoreExtension::getAttribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type, true)); + } + + /** + * @dataProvider getGetAttributeTests + */ + public function testGetAttributeDefinedStrict($defined, $value, $object, $item, $arguments, $type) + { + $twig = new Environment(new ArrayLoader(), ['strict_variables' => true]); + $template = new TemplateForTest($twig); + + $this->assertEquals($defined, CoreExtension::getAttribute($twig, $template->getSourceContext(), $object, $item, $arguments, $type, true)); + } + + public function testGetAttributeCallExceptions() + { + $twig = new Environment(new ArrayLoader()); + $template = new TemplateForTest($twig); + + $object = new TemplateMagicMethodExceptionObject(); + + $this->assertNull(CoreExtension::getAttribute($twig, $template->getSourceContext(), $object, 'foo')); + } + + public function getGetAttributeTests() + { + $array = [ + 'defined' => 'defined', + 'zero' => 0, + 'null' => null, + '1' => 1, + 'bar' => true, + 'foo' => true, + 'baz' => 'baz', + 'baf' => 'baf', + '09' => '09', + '+4' => '+4', + ]; + + $objectArray = new TemplateArrayAccessObject(); + $arrayObject = new \ArrayObject($array); + $stdObject = (object) $array; + $magicPropertyObject = new TemplateMagicPropertyObject(); + $propertyObject = new TemplatePropertyObject(); + $propertyObject1 = new TemplatePropertyObjectAndIterator(); + $propertyObject2 = new TemplatePropertyObjectAndArrayAccess(); + $propertyObject3 = new TemplatePropertyObjectDefinedWithUndefinedValue(); + $methodObject = new TemplateMethodObject(); + $magicMethodObject = new TemplateMagicMethodObject(); + + $anyType = Template::ANY_CALL; + $methodType = Template::METHOD_CALL; + $arrayType = Template::ARRAY_CALL; + + $basicTests = [ + // array(defined, value, property to fetch) + [true, 'defined', 'defined'], + [false, null, 'undefined'], + [false, null, 'protected'], + [true, 0, 'zero'], + [true, 1, 1], + [true, 1, 1.0], + [true, null, 'null'], + [true, true, 'bar'], + [true, true, 'foo'], + [true, 'baz', 'baz'], + [true, 'baf', 'baf'], + [true, '09', '09'], + [true, '+4', '+4'], + ]; + $testObjects = [ + // array(object, type of fetch) + [$array, $arrayType], + [$objectArray, $arrayType], + [$arrayObject, $anyType], + [$stdObject, $anyType], + [$magicPropertyObject, $anyType], + [$methodObject, $methodType], + [$methodObject, $anyType], + [$propertyObject, $anyType], + [$propertyObject1, $anyType], + [$propertyObject2, $anyType], + ]; + + $tests = []; + foreach ($testObjects as $testObject) { + foreach ($basicTests as $test) { + // properties cannot be numbers + if (($testObject[0] instanceof \stdClass || $testObject[0] instanceof TemplatePropertyObject) && is_numeric($test[2])) { + continue; + } + + if ('+4' === $test[2] && $methodObject === $testObject[0]) { + continue; + } + + $tests[] = [$test[0], $test[1], $testObject[0], $test[2], [], $testObject[1]]; + } + } + + // additional properties tests + $tests = array_merge($tests, [ + [true, null, $propertyObject3, 'foo', [], $anyType], + ]); + + // additional method tests + $tests = array_merge($tests, [ + [true, 'defined', $methodObject, 'defined', [], $methodType], + [true, 'defined', $methodObject, 'DEFINED', [], $methodType], + [true, 'defined', $methodObject, 'getDefined', [], $methodType], + [true, 'defined', $methodObject, 'GETDEFINED', [], $methodType], + [true, 'static', $methodObject, 'static', [], $methodType], + [true, 'static', $methodObject, 'getStatic', [], $methodType], + + [true, '__call_undefined', $magicMethodObject, 'undefined', [], $methodType], + [true, '__call_UNDEFINED', $magicMethodObject, 'UNDEFINED', [], $methodType], + ]); + + // add the same tests for the any type + foreach ($tests as $test) { + if ($anyType !== $test[5]) { + $test[5] = $anyType; + $tests[] = $test; + } + } + + $methodAndPropObject = new TemplateMethodAndPropObject(); + + // additional method tests + $tests = array_merge($tests, [ + [true, 'a', $methodAndPropObject, 'a', [], $anyType], + [true, 'a', $methodAndPropObject, 'a', [], $methodType], + [false, null, $methodAndPropObject, 'a', [], $arrayType], + + [true, 'b_prop', $methodAndPropObject, 'b', [], $anyType], + [true, 'b', $methodAndPropObject, 'B', [], $anyType], + [true, 'b', $methodAndPropObject, 'b', [], $methodType], + [true, 'b', $methodAndPropObject, 'B', [], $methodType], + [false, null, $methodAndPropObject, 'b', [], $arrayType], + + [false, null, $methodAndPropObject, 'c', [], $anyType], + [false, null, $methodAndPropObject, 'c', [], $methodType], + [false, null, $methodAndPropObject, 'c', [], $arrayType], + ]); + + $arrayAccess = new TemplateArrayAccess(); + $tests = array_merge($tests, [ + [true, ['foo' => 'bar'], $arrayAccess, 'vars', [], $anyType], + ]); + + // tests when input is not an array or object + $tests = array_merge($tests, [ + [false, null, 42, 'a', [], $anyType, 'Impossible to access an attribute ("a") on a integer variable ("42") in "index.twig".'], + [false, null, 'string', 'a', [], $anyType, 'Impossible to access an attribute ("a") on a string variable ("string") in "index.twig".'], + [false, null, [], 'a', [], $anyType, 'Key "a" does not exist as the sequence/mapping is empty in "index.twig".'], + ]); + + return $tests; + } + + public function testGetIsMethods() + { + $twig = new Environment(new ArrayLoader()); + + $getIsObject = new TemplateGetIsMethods(); + $template = new TemplateForTest($twig, 'index.twig'); + // first time should not create a cache for "get" + $this->assertNull(CoreExtension::getAttribute($twig, $template->getSourceContext(), $getIsObject, 'get')); + // 0 should be in the method cache now, so this should fail + $this->assertNull(CoreExtension::getAttribute($twig, $template->getSourceContext(), $getIsObject, 0)); + } +} + +class TemplateForTest extends Template +{ + private $name; + + public function __construct(Environment $env, $name = 'index.twig') + { + parent::__construct($env); + $this->name = $name; + } + + public function getZero() + { + return 0; + } + + public function getEmpty() + { + return ''; + } + + public function getString() + { + return 'some_string'; + } + + public function getTrue() + { + return true; + } + + public function getTemplateName(): string + { + return $this->name; + } + + public function getDebugInfo() : array + { + return []; + } + + public function getSourceContext() : Source + { + return new Source('', $this->getTemplateName()); + } + + protected function doGetParent(array $context): bool|string|Template|TemplateWrapper + { + return false; + } + + protected function doDisplay(array $context, array $blocks = []) + { + } + + public function block_name($context, array $blocks = []) + { + } +} + +class TemplateArrayAccessObject implements \ArrayAccess +{ + protected $protected = 'protected'; + + public $attributes = [ + 'defined' => 'defined', + 'zero' => 0, + 'null' => null, + '1' => 1, + 'bar' => true, + 'foo' => true, + 'baz' => 'baz', + 'baf' => 'baf', + '09' => '09', + '+4' => '+4', + ]; + + public function offsetExists($name): bool + { + return \array_key_exists($name, $this->attributes); + } + + #[\ReturnTypeWillChange] + public function offsetGet($name) + { + return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null; + } + + public function offsetSet($name, $value): void + { + } + + public function offsetUnset($name): void + { + } +} + +class TemplateMagicPropertyObject +{ + public $defined = 'defined'; + + public $attributes = [ + 'zero' => 0, + 'null' => null, + '1' => 1, + 'bar' => true, + 'foo' => true, + 'baz' => 'baz', + 'baf' => 'baf', + '09' => '09', + '+4' => '+4', + ]; + + protected $protected = 'protected'; + + public function __isset($name): bool + { + return \array_key_exists($name, $this->attributes); + } + + public function __get($name) + { + return \array_key_exists($name, $this->attributes) ? $this->attributes[$name] : null; + } +} + +class TemplateMagicPropertyObjectWithException +{ + public function __isset($key): bool + { + throw new \Exception('Hey! Don\'t try to isset me!'); + } +} + +class TemplatePropertyObject +{ + public $defined = 'defined'; + public $zero = 0; + public $null = null; + public $bar = true; + public $foo = true; + public $baz = 'baz'; + public $baf = 'baf'; + + protected $protected = 'protected'; +} + +class TemplatePropertyObjectAndIterator extends TemplatePropertyObject implements \IteratorAggregate +{ + public function getIterator(): \Traversable + { + return new \ArrayIterator(['foo', 'bar']); + } +} + +class TemplatePropertyObjectAndArrayAccess extends TemplatePropertyObject implements \ArrayAccess +{ + private $data = [ + 'defined' => 'defined', + 'zero' => 0, + 'null' => null, + 'bar' => true, + 'foo' => true, + 'baz' => 'baz', + 'baf' => 'baf', + ]; + + public function offsetExists($offset): bool + { + return \array_key_exists($offset, $this->data); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->offsetExists($offset) ? $this->data[$offset] : 'n/a'; + } + + public function offsetSet($offset, $value): void + { + } + + public function offsetUnset($offset): void + { + } +} + +class TemplatePropertyObjectDefinedWithUndefinedValue +{ + public $foo; + + public function __construct() + { + $this->foo = @$notExist; + } +} + +class TemplateMethodObject +{ + public function getDefined() + { + return 'defined'; + } + + public function get1() + { + return 1; + } + + public function get09() + { + return '09'; + } + + public function getZero() + { + return 0; + } + + public function getNull() + { + } + + public function isBar() + { + return true; + } + + public function hasFoo() + { + return true; + } + + public function hasBaz() + { + return 'should never be returned (has)'; + } + + public function isBaz() + { + return 'should never be returned (is)'; + } + + public function getBaz() + { + return 'Baz'; + } + + public function baz() + { + return 'baz'; + } + + public function hasBaf() + { + return 'should never be returned (has)'; + } + + public function isBaf() + { + return 'baf'; + } + + protected function getProtected() + { + return 'protected'; + } + + public static function getStatic() + { + return 'static'; + } +} + +class TemplateGetIsMethods +{ + public function get() + { + } + + public function is() + { + } +} + +class TemplateMethodAndPropObject +{ + private $a = 'a_prop'; + + public function getA() + { + return 'a'; + } + + public $b = 'b_prop'; + + public function getB() + { + return 'b'; + } + + private $c = 'c_prop'; + + private function getC() + { + return 'c'; + } +} + +class TemplateArrayAccess implements \ArrayAccess +{ + public $vars = [ + 'foo' => 'bar', + ]; + private $children = []; + + public function offsetExists($offset): bool + { + return \array_key_exists($offset, $this->children); + } + + #[\ReturnTypeWillChange] + public function offsetGet($offset) + { + return $this->children[$offset]; + } + + public function offsetSet($offset, $value): void + { + $this->children[$offset] = $value; + } + + public function offsetUnset($offset): void + { + unset($this->children[$offset]); + } +} + +class TemplateMagicMethodObject +{ + public function __call($method, $arguments) + { + return '__call_'.$method; + } +} + +class TemplateMagicMethodExceptionObject +{ + public function __call($method, $arguments) + { + throw new \BadMethodCallException(\sprintf('Unknown method "%s".', $method)); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/TemplateWrapperTest.php b/upload/system/storage/vendor/twig/twig/tests/TemplateWrapperTest.php new file mode 100644 index 000000000..a302aba05 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/TemplateWrapperTest.php @@ -0,0 +1,72 @@ + '{% block foo %}{% endblock %}', + 'index_with_use' => '{% use "imported" %}{% block foo %}{% endblock %}', + 'index_with_extends' => '{% extends "extended" %}{% block foo %}{% endblock %}', + 'imported' => '{% block imported %}{% endblock %}', + 'extended' => '{% block extended %}{% endblock %}', + ])); + + $wrapper = $twig->load('index'); + $this->assertTrue($wrapper->hasBlock('foo')); + $this->assertFalse($wrapper->hasBlock('bar')); + $this->assertEquals(['foo'], $wrapper->getBlockNames()); + + $wrapper = $twig->load('index_with_use'); + $this->assertTrue($wrapper->hasBlock('foo')); + $this->assertTrue($wrapper->hasBlock('imported')); + $this->assertEquals(['imported', 'foo'], $wrapper->getBlockNames()); + + $wrapper = $twig->load('index_with_extends'); + $this->assertTrue($wrapper->hasBlock('foo')); + $this->assertTrue($wrapper->hasBlock('extended')); + $this->assertEquals(['foo', 'extended'], $wrapper->getBlockNames()); + } + + public function testRenderBlock() + { + $twig = new Environment(new ArrayLoader([ + 'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}', + ])); + $twig->addGlobal('bar', 'BAR'); + + $wrapper = $twig->load('index'); + $this->assertEquals('FOOBAR', $wrapper->renderBlock('foo', ['foo' => 'FOO'])); + } + + public function testDisplayBlock() + { + $twig = new Environment(new ArrayLoader([ + 'index' => '{% block foo %}{{ foo }}{{ bar }}{% endblock %}', + ])); + + $twig->addGlobal('bar', 'BAR'); + + $wrapper = $twig->load('index'); + + ob_start(); + $wrapper->displayBlock('foo', ['foo' => 'FOO']); + + $this->assertEquals('FOOBAR', ob_get_clean()); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/TokenStreamTest.php b/upload/system/storage/vendor/twig/twig/tests/TokenStreamTest.php new file mode 100644 index 000000000..e8cb474b3 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/TokenStreamTest.php @@ -0,0 +1,75 @@ +isEOF()) { + $token = $stream->next(); + + $repr[] = $token->getValue(); + } + $this->assertEquals('1, 2, 3, 4, 5, 6, 7', implode(', ', $repr), '->next() advances the pointer and returns the current token'); + } + + public function testEndOfTemplateNext() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unexpected end of template'); + + $stream = new TokenStream([ + new Token(Token::BLOCK_START_TYPE, 1, 1), + ]); + while (!$stream->isEOF()) { + $stream->next(); + } + } + + public function testEndOfTemplateLook() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unexpected end of template'); + + $stream = new TokenStream([ + new Token(Token::BLOCK_START_TYPE, 1, 1), + ]); + while (!$stream->isEOF()) { + $stream->look(); + $stream->next(); + } + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Util/CallableArgumentsExtractorTest.php b/upload/system/storage/vendor/twig/twig/tests/Util/CallableArgumentsExtractorTest.php new file mode 100644 index 000000000..cfea0f8a9 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Util/CallableArgumentsExtractorTest.php @@ -0,0 +1,156 @@ +assertEquals(['U', null], $this->getArguments('date', 'date', ['format' => 'U', 'timestamp' => null])); + } + + public function testGetArgumentsWhenPositionalArgumentsAfterNamedArguments() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Positional arguments cannot be used after named arguments for function "date".'); + + $this->getArguments('date', 'date', ['timestamp' => 123456, 'Y-m-d']); + } + + public function testGetArgumentsWhenArgumentIsDefinedTwice() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Argument "format" is defined twice for function "date".'); + + $this->getArguments('date', 'date', ['Y-m-d', 'format' => 'U']); + } + + public function testGetArgumentsWithWrongNamedArgumentName() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown argument "unknown" for function "date(format, timestamp)".'); + + $this->getArguments('date', 'date', ['Y-m-d', 'timestamp' => null, 'unknown' => '']); + } + + public function testGetArgumentsWithWrongNamedArgumentNames() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Unknown arguments "unknown1", "unknown2" for function "date(format, timestamp)".'); + + $this->getArguments('date', 'date', ['Y-m-d', 'timestamp' => null, 'unknown1' => '', 'unknown2' => '']); + } + + public function testResolveArgumentsWithMissingValueForOptionalArgument() + { + if (\PHP_VERSION_ID >= 80000) { + $this->markTestSkipped('substr_compare() has a default value in 8.0, so the test does not work anymore, one should find another PHP built-in function for this test to work in PHP 8.'); + } + + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('Argument "case_sensitivity" could not be assigned for function "substr_compare(main_str, str, offset, length, case_sensitivity)" because it is mapped to an internal PHP function which cannot determine default value for optional argument "length".'); + + $this->getArguments('substr_compare', 'substr_compare', ['abcd', 'bc', 'offset' => 1, 'case_sensitivity' => true]); + } + + public function testResolveArgumentsOnlyNecessaryArgumentsForCustomFunction() + { + $this->assertEquals(['arg1'], $this->getArguments('custom_function', [$this, 'customFunction'], ['arg1' => 'arg1'])); + } + + public function testGetArgumentsForStaticMethod() + { + $this->assertEquals(['arg1'], $this->getArguments('custom_static_function', __CLASS__.'::customStaticFunction', ['arg1' => 'arg1'])); + } + + public function testResolveArgumentsWithMissingParameterForArbitraryArguments() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessage('The last parameter of "Twig\\Tests\\Util\\CallableArgumentsExtractorTest::customFunctionWithArbitraryArguments" for function "foo" must be an array with default value, eg. "array $arg = []".'); + + $this->getArguments('foo', [$this, 'customFunctionWithArbitraryArguments'], [], true); + } + + public function testGetArgumentsWithInvalidCallable() + { + $this->expectException(\LogicException::class); + $this->expectExceptionMessage('Callback for function "foo" is not callable in the current scope.'); + $this->getArguments('foo', '', [], true); + } + + public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnFunction() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessageMatches('#^The last parameter of "Twig\\\\Tests\\\\Util\\\\custom_call_test_function" for function "foo" must be an array with default value, eg\\. "array \\$arg \\= \\[\\]"\\.$#'); + + $this->getArguments('foo', 'Twig\Tests\Util\custom_call_test_function', [], true); + } + + public function testResolveArgumentsWithMissingParameterForArbitraryArgumentsOnObject() + { + $this->expectException(SyntaxError::class); + $this->expectExceptionMessageMatches('#^The last parameter of "Twig\\\\Tests\\\\Util\\\\CallableTestClass\\:\\:__invoke" for function "foo" must be an array with default value, eg\\. "array \\$arg \\= \\[\\]"\\.$#'); + + $this->getArguments('foo', new CallableTestClass(), [], true); + } + + public static function customStaticFunction($arg1, $arg2 = 'default', $arg3 = []) + { + } + + public function customFunction($arg1, $arg2 = 'default', $arg3 = []) + { + } + + public function customFunctionWithArbitraryArguments() + { + } + + private function getArguments(string $name, $callable, array $args, bool $isVariadic = false): array + { + $function = new TwigFunction($name, $callable, ['is_variadic' => $isVariadic]); + $node = new ExpressionCall($function, new Node([]), 0); + foreach ($args as $name => $arg) { + $args[$name] = new ConstantExpression($arg, 0); + } + + $arguments = (new CallableArgumentsExtractor($node, $function))->extractArguments(new Node($args)); + foreach ($arguments as $name => $argument) { + $arguments[$name] = $argument->getAttribute('value'); + } + + return $arguments; + } +} + +class ExpressionCall extends FunctionExpression +{ +} + +class CallableTestClass +{ + public function __invoke($required) + { + } +} + +function custom_call_test_function($required) +{ +} diff --git a/upload/system/storage/vendor/twig/twig/tests/Util/DeprecationCollectorTest.php b/upload/system/storage/vendor/twig/twig/tests/Util/DeprecationCollectorTest.php new file mode 100644 index 000000000..a3010d14d --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/Util/DeprecationCollectorTest.php @@ -0,0 +1,50 @@ +addFunction(new TwigFunction('deprec', [$this, 'deprec'], ['deprecated' => '1.1', 'deprecating_package' => 'foo/bar'])); + + $collector = new DeprecationCollector($twig); + $deprecations = $collector->collect(new Iterator()); + + $this->assertEquals(['Since foo/bar 1.1: Twig Function "deprec" is deprecated in deprec.twig at line 1.'], $deprecations); + } + + public function deprec() + { + } +} + +class Iterator implements \IteratorAggregate +{ + public function getIterator(): \Traversable + { + return new \ArrayIterator([ + 'ok.twig' => '{{ foo }}', + 'deprec.twig' => '{{ deprec("foo") }}', + ]); + } +} diff --git a/upload/system/storage/vendor/twig/twig/tests/drupal_test.sh b/upload/system/storage/vendor/twig/twig/tests/drupal_test.sh new file mode 100644 index 000000000..eff75f249 --- /dev/null +++ b/upload/system/storage/vendor/twig/twig/tests/drupal_test.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +set -x +set -e + +REPO=`pwd` +cd /tmp +rm -rf drupal-twig-test +composer create-project --no-interaction drupal/recommended-project:10.1.x-dev drupal-twig-test +cd drupal-twig-test +(cd vendor/twig && rm -rf twig && ln -sf $REPO twig) +composer dump-autoload +php ./web/core/scripts/drupal install --no-interaction demo_umami > output +perl -p -i -e 's/^([A-Za-z]+)\: (.+)$/export DRUPAL_\1=\2/' output +source output +#echo '$config["system.logging"]["error_level"] = "verbose";' >> web/sites/default/settings.php + +wget https://get.symfony.com/cli/installer -O - | bash +export PATH="$HOME/.symfony5/bin:$PATH" +symfony server:start -d --no-tls + +curl -LsS -o blackfire-player.phar https://get.blackfire.io/blackfire-player-v1.31.0.phar +chmod +x blackfire-player.phar +cat > drupal-tests.bkf <isTry && in_array($exception->getCode(), ['40014', '40001', '41001', '42001'])) { [$this->delAccessToken(), $this->isTry = true]; @@ -223,15 +224,15 @@ protected function registerApi(&$url, $method, $arguments = []) * 接口通用POST请求方法 * @param string $url 接口URL * @param array $data POST提交接口参数 - * @param bool $isBuildJson + * @param bool $toJson 是否转换为JSON参数 * @return array * @throws \WeChat\Exceptions\InvalidResponseException * @throws \WeChat\Exceptions\LocalCacheException */ - public function callPostApi($url, array $data, $isBuildJson = true) + public function callPostApi($url, array $data, $toJson = true, array $options = []) { $this->registerApi($url, __FUNCTION__, func_get_args()); - return $this->httpPostForJson($url, $data, $isBuildJson); + return $this->httpPostForJson($url, $data, $toJson, $options); } /** diff --git a/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Live.php b/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Live.php index 089fa25f5..0401abe22 100644 --- a/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Live.php +++ b/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Live.php @@ -168,4 +168,303 @@ public function getGoods($data) $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getapproved?access_token=ACCESS_TOKEN"; return $this->callPostApi($url, $data, true); } + + /** + * 删除直播间 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function delLive($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/deleteroom?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 编辑直播间 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function editLive($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/editroom?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 获取直播间推流地址 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getPushUrl($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getpushurl?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 获取直播间分享二维码 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getShareCode($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getsharedcode?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 添加管理直播间小助手 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function addAssistant($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/addassistant?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 修改管理直播间小助手 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function modifyAssistant($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/modifyassistant?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 删除管理直播间小助手 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function removeAssistant($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/removeassistant?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 查询管理直播间小助手 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getAssistantList($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getassistantlist?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 添加主播副号 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function addSubAnchor($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/addsubanchor?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 修改主播副号 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function modifySubAnchor($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/modifysubanchor?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 修删除主播副号 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function delSubAnchor($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/deletesubanchor?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 查询除主播副号 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getSubAnchor($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/getsubanchor?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 开启/关闭直播间官方收录 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function updateFeedPublic($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatefeedpublic?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 开启/关闭回放功能 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function updateReplay($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatereplay?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 开启/关闭客服功能 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function updateKf($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatekf?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 开启/关闭直播间全局禁言 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function updateComment($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/room/updatecomment?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 上下架商品 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function goodsOnsale($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/onsale?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 删除直播间商品 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function goodsDeleteInRoom($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/deleteInRoom?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 推送商品 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function goodsPush($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/push?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 商品排序 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function goodsSort($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/sort?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 下载商品讲解视频 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getVideo($data) + { + $url = "https://api.weixin.qq.com/wxaapi/broadcast/goods/getVideo?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 获取长期订阅用户 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function getFollowers($data) + { + $url = "https://api.weixin.qq.com/wxa/business/get_wxa_followers?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } + + /** + * 长期订阅群发接口 + * @param array $data + * @return array + * @throws \WeChat\Exceptions\InvalidResponseException + * @throws \WeChat\Exceptions\LocalCacheException + */ + public function pushMessage($data) + { + $url = "https://api.weixin.qq.com/wxa/business/push_message?access_token=ACCESS_TOKEN"; + return $this->callPostApi($url, $data, true); + } } \ No newline at end of file diff --git a/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Security.php b/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Security.php index 0a5e7315b..060729e66 100644 --- a/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Security.php +++ b/upload/system/storage/vendor/zoujingli/wechat-developer/WeMini/Security.php @@ -36,7 +36,7 @@ class Security extends BasicWeChat public function imgSecCheck($media) { $url = 'https://api.weixin.qq.com/wxa/img_sec_check?access_token=ACCESS_TOKEN'; - return $this->callPostApi($url, ['media' => $media], false); + return $this->callPostApi($url, ['media' => $media], false, ['headers' => ['Content-Type: application/octet-stream']]); } /** diff --git a/upload/system/storage/vendor/zoujingli/wechat-developer/WePayV3/Transfers.php b/upload/system/storage/vendor/zoujingli/wechat-developer/WePayV3/Transfers.php index 78ba5a683..d32289b2a 100644 --- a/upload/system/storage/vendor/zoujingli/wechat-developer/WePayV3/Transfers.php +++ b/upload/system/storage/vendor/zoujingli/wechat-developer/WePayV3/Transfers.php @@ -42,9 +42,12 @@ public function batchs($body) foreach ($body['transfer_detail_list'] as &$item) if (isset($item['user_name'])) { $item['user_name'] = $this->rsaEncode($item['user_name']); } - } - if (empty($body['total_num'])) { - $body['total_num'] = count($body['transfer_detail_list']); + if (empty($body['total_num'])) { + $body['total_num'] = count($body['transfer_detail_list']); + } + if (empty($body['total_amount'])) { + $body['total_amount'] = array_sum(array_column($body['transfer_detail_list'], 'transfer_amount')); + } } return $this->doRequest('POST', '/v3/transfer/batches', json_encode($body, JSON_UNESCAPED_UNICODE), true); } diff --git a/upload/system/storage/vendor/zoujingli/wechat-developer/composer.json b/upload/system/storage/vendor/zoujingli/wechat-developer/composer.json index ed28ef599..9bb246f9c 100644 --- a/upload/system/storage/vendor/zoujingli/wechat-developer/composer.json +++ b/upload/system/storage/vendor/zoujingli/wechat-developer/composer.json @@ -30,6 +30,9 @@ "ext-simplexml": "*" }, "autoload": { + "files": [ + "helper.php" + ], "classmap": [ "We.php" ], diff --git a/upload/system/storage/vendor/zoujingli/wechat-developer/helper.php b/upload/system/storage/vendor/zoujingli/wechat-developer/helper.php new file mode 100644 index 000000000..6a2dbec54 --- /dev/null +++ b/upload/system/storage/vendor/zoujingli/wechat-developer/helper.php @@ -0,0 +1,25 @@ +