diff --git a/composer.json b/composer.json index e1f56d5..872a3da 100644 --- a/composer.json +++ b/composer.json @@ -1,11 +1,20 @@ { "require": { - "tightenco/collect": "^7.5", - "twig/twig": "2.12.5", "gregwar/formidable": "^2.1", - "rmccue/requests": "^1.7" + "rmccue/requests": "^1.7", + "timber/timber": "^2.3", + "illuminate/collections": "^12.21", + "upstatement/routes": "^0.9.2" }, - "require-dev": { - "agencearcange/wp-twig-dump": "^1.1" - } + "config": { + "allow-plugins": { + "composer/installers": true + } + }, + "autoload": { + "psr-4": { + "App\\": "src/" + } + } + } diff --git a/composer.lock b/composer.lock index aa1c4ee..9b67168 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,212 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "b41f6e3517249ce761847b8c33b3809d", + "content-hash": "da4f3b212b96744f0677688650913540", "packages": [ + { + "name": "altorouter/altorouter", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/dannyvankooten/AltoRouter.git", + "reference": "9931b976423f7334c94f7b5b348be8ab1da3415d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dannyvankooten/AltoRouter/zipball/9931b976423f7334c94f7b5b348be8ab1da3415d", + "reference": "9931b976423f7334c94f7b5b348be8ab1da3415d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "9.6.*", + "squizlabs/php_codesniffer": "3.6.2" + }, + "type": "library", + "autoload": { + "classmap": [ + "AltoRouter.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Danny van Kooten", + "email": "dannyvankooten@gmail.com", + "homepage": "http://dannyvankooten.com/" + }, + { + "name": "Koen Punt", + "homepage": "https://github.com/koenpunt" + }, + { + "name": "niahoo", + "homepage": "https://github.com/niahoo" + } + ], + "description": "A lightning fast router for PHP", + "homepage": "https://github.com/dannyvankooten/AltoRouter", + "keywords": [ + "lightweight", + "router", + "routing" + ], + "support": { + "source": "https://github.com/dannyvankooten/AltoRouter/tree/2.0.3" + }, + "time": "2025-01-05T20:33:28+00:00" + }, + { + "name": "composer/installers", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-phpunit": "^1", + "symfony/phpunit-bridge": "^7.1.1", + "symfony/process": "^5 || ^6 || ^7" + }, + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true + }, + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "MantisBT", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Starbug", + "Thelia", + "Whmcs", + "WolfCMS", + "agl", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "concreteCMS", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "known", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "matomo", + "mediawiki", + "miaoxing", + "modulework", + "modx", + "moodle", + "osclass", + "pantheon", + "phpbb", + "piwik", + "ppi", + "processwire", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "sylius", + "tastyigniter", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "support": { + "issues": "https://github.com/composer/installers/issues", + "source": "https://github.com/composer/installers/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "time": "2024-06-24T20:46:46+00:00" + }, { "name": "gregwar/cache", "version": "v1.0.12", @@ -108,6 +312,307 @@ ], "time": "2018-05-17T19:22:38+00:00" }, + { + "name": "illuminate/collections", + "version": "v12.21.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "a048b4fbbef4742ff2eee843971bb8278239c610" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/a048b4fbbef4742ff2eee843971bb8278239c610", + "reference": "a048b4fbbef4742ff2eee843971bb8278239c610", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "php": "^8.2" + }, + "suggest": { + "illuminate/http": "Required to convert collections to API resources (^12.0).", + "symfony/var-dumper": "Required to use the dump method (^7.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "files": [ + "functions.php", + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-07-15T20:29:59+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v12.21.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/ec677967c1f2faf90b8428919124d2184a4c9b49", + "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-05-13T15:08:45+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v12.21.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "c2eef857b808810f5cb187de58e23d25c1d443d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/c2eef857b808810f5cb187de58e23d25c1d443d9", + "reference": "c2eef857b808810f5cb187de58e23d25c1d443d9", + "shasum": "" + }, + "require": { + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2025-07-16T13:18:38+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v12.21.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "e862e5648ee34004fa56046b746f490dfa86c613" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/e862e5648ee34004fa56046b746f490dfa86c613", + "reference": "e862e5648ee34004fa56046b746f490dfa86c613", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-07-23T16:31:01+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/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, { "name": "rmccue/requests", "version": "v1.7.0", @@ -157,6 +662,73 @@ ], "time": "2016-10-13T00:11:37+00:00" }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "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.6.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": "2024-09-25T14:21:43+00:00" + }, { "name": "symfony/polyfill-ctype", "version": "v1.15.0", @@ -231,37 +803,42 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=5.3.3" + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" }, "suggest": { "ext-mbstring": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-master": "1.15-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, "files": [ "bootstrap.php" - ] + ], + "psr-4": { + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -286,6 +863,9 @@ "portable", "shim" ], + "support": { + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + }, "funding": [ { "url": "https://symfony.com/sponsor", @@ -300,7 +880,7 @@ "type": "tidelift" } ], - "time": "2020-03-09T19:04:49+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/property-access", @@ -364,57 +944,50 @@ "time": "2018-11-11T11:18:13+00:00" }, { - "name": "symfony/var-dumper", - "version": "v5.0.7", + "name": "timber/timber", + "version": "v2.3.2", "source": { "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "f74a126acd701392eef2492a17228d42552c86b5" + "url": "https://github.com/timber/timber.git", + "reference": "08ce601167a03ed7a4fa7da740481c17ed650ef0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/f74a126acd701392eef2492a17228d42552c86b5", - "reference": "f74a126acd701392eef2492a17228d42552c86b5", + "url": "https://api.github.com/repos/timber/timber/zipball/08ce601167a03ed7a4fa7da740481c17ed650ef0", + "reference": "08ce601167a03ed7a4fa7da740481c17ed650ef0", "shasum": "" }, "require": { - "php": "^7.2.5", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" + "php": "^8.1", + "twig/twig": "^3.19" }, "require-dev": { - "ext-iconv": "*", - "symfony/console": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "twig/twig": "^2.4|^3.0" + "ergebnis/composer-normalize": "^2.28", + "php-parallel-lint/php-parallel-lint": "^1.3", + "php-stubs/acf-pro-stubs": "^6.0", + "php-stubs/wp-cli-stubs": "^2.0", + "phpro/grumphp": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.7", + "phpunit/phpunit": "^9.0", + "rector/rector": "^1.0", + "squizlabs/php_codesniffer": "^3.0", + "symplify/easy-coding-standard": "^12.2", + "szepeviktor/phpstan-wordpress": "^1.1", + "twig/cache-extra": "^3.17", + "wpackagist-plugin/advanced-custom-fields": "^6.0", + "wpackagist-plugin/co-authors-plus": "^3.6", + "yoast/wp-test-utils": "^1.2" }, "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" + "php-coveralls/php-coveralls": "^2.0 for code coverage", + "twig/cache-extra": "For using the cache tag in Twig" }, - "bin": [ - "Resources/bin/var-dump-server" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { - "files": [ - "Resources/functions/dump.php" - ], "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Timber\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -422,119 +995,89 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Erik van der Bas", + "email": "erik@basedonline.nl", + "homepage": "https://basedonline.nl" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Lukas Gächter", + "email": "lukas.gaechter@mind.ch", + "homepage": "https://www.mind.ch" + }, + { + "name": "Nicolas Lemoine", + "email": "nico@n5s.dev", + "homepage": "https://n5s.dev" + }, + { + "name": "Jared Novack", + "email": "jared@upstatement.com", + "homepage": "https://upstatement.com" + }, + { + "name": "Timber Community", + "homepage": "https://github.com/timber/timber" } ], - "description": "Symfony mechanism for exploring and dumping PHP variables", - "homepage": "https://symfony.com", + "description": "Create WordPress themes with beautiful OOP code and the Twig Template Engine", + "homepage": "https://timber.upstatement.com", "keywords": [ - "debug", - "dump" + "templating", + "themes", + "timber", + "twig", + "wordpress" ], + "support": { + "docs": "https://timber.github.io/docs/", + "issues": "https://github.com/timber/timber/issues", + "source": "https://github.com/timber/timber" + }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/timber", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "url": "https://opencollective.com/timber", + "type": "open_collective" } ], - "time": "2020-03-27T16:56:45+00:00" - }, - { - "name": "tightenco/collect", - "version": "v7.5.0", - "source": { - "type": "git", - "url": "https://github.com/tightenco/collect.git", - "reference": "5024db45b1ec1956e6d5959cbb5cdf5f00f26f56" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tightenco/collect/zipball/5024db45b1ec1956e6d5959cbb5cdf5f00f26f56", - "reference": "5024db45b1ec1956e6d5959cbb5cdf5f00f26f56", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "nesbot/carbon": "^2.23.0", - "phpunit/phpunit": "^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "src/Collect/Support/helpers.php", - "src/Collect/Support/alias.php" - ], - "psr-4": { - "Tightenco\\Collect\\": "src/Collect" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "description": "Collect - Illuminate Collections as a separate package.", - "keywords": [ - "collection", - "laravel" - ], - "time": "2020-03-31T17:56:15+00:00" + "time": "2025-05-13T13:41:56+00:00" }, { "name": "twig/twig", - "version": "v2.12.5", + "version": "v3.21.1", "source": { "type": "git", "url": "https://github.com/twigphp/Twig.git", - "reference": "18772e0190734944277ee97a02a9a6c6555fcd94" + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/18772e0190734944277ee97a02a9a6c6555fcd94", - "reference": "18772e0190734944277ee97a02a9a6c6555fcd94", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", "shasum": "" }, "require": { - "php": "^7.0", + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-ctype": "^1.8", "symfony/polyfill-mbstring": "^1.3" }, "require-dev": { - "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.0" + "phpstan/phpstan": "^2.0", + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.12-dev" - } - }, "autoload": { - "psr-0": { - "Twig_": "lib/" - }, + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4": { "Twig\\": "src/" } @@ -565,82 +1108,80 @@ "keywords": [ "templating" ], - "time": "2020-02-11T15:31:23+00:00" - } - ], - "packages-dev": [ - { - "name": "agencearcange/wp-twig-dump", - "version": "v1.1", - "source": { - "type": "git", - "url": "https://github.com/agencearcange/wp-twig-dump.git", - "reference": "d782ae81fe59f01578b97d3d526434c3879e90fe" + "support": { + "issues": "https://github.com/twigphp/Twig/issues", + "source": "https://github.com/twigphp/Twig/tree/v3.21.1" }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/agencearcange/wp-twig-dump/zipball/d782ae81fe59f01578b97d3d526434c3879e90fe", - "reference": "d782ae81fe59f01578b97d3d526434c3879e90fe", - "shasum": "" - }, - "require": { - "hellonico/twig-dump-extension": "^1.0" - }, - "type": "wordpress-muplugin", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPLv2" + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } ], - "description": "Allow dump() in twig templates", - "homepage": "https://github.com/agencearcange/arcange-wpdump", - "keywords": [ - "plugin", - "timber", - "twig", - "wordpress" - ], - "time": "2020-01-03T19:45:46+00:00" + "time": "2025-05-03T07:21:55+00:00" }, { - "name": "hellonico/twig-dump-extension", - "version": "1.1.0", + "name": "upstatement/routes", + "version": "0.9.2", "source": { "type": "git", - "url": "https://github.com/nlemoine/twig-dump-extension.git", - "reference": "a162573838ce18e7948b92a6eac74ff9db610639" + "url": "https://github.com/Upstatement/routes.git", + "reference": "196a4dbc06231e5f6eb760130b8aa42d47e3c22f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nlemoine/twig-dump-extension/zipball/a162573838ce18e7948b92a6eac74ff9db610639", - "reference": "a162573838ce18e7948b92a6eac74ff9db610639", + "url": "https://api.github.com/repos/Upstatement/routes/zipball/196a4dbc06231e5f6eb760130b8aa42d47e3c22f", + "reference": "196a4dbc06231e5f6eb760130b8aa42d47e3c22f", "shasum": "" }, "require": { - "symfony/var-dumper": "^4.2 || ^5.0", - "twig/twig": "^2.4 || ^3.0" + "altorouter/altorouter": "^2.0.2", + "composer/installers": "^1.0 || ^2.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "5.7.16", + "satooshi/php-coveralls": "*", + "wp-cli/wp-cli": "*" }, "type": "library", "autoload": { - "psr-4": { - "HelloNico\\Twig\\": "src/" + "psr-0": { + "Routes": "" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "GPL-3.0+" + "MIT" ], "authors": [ { - "name": "Nicolas Lemoine", - "email": "dev@helloni.co", - "homepage": "https://github.com/nlemoine" + "name": "Jared Novack", + "email": "jared@upstatement.com", + "homepage": "https://www.upstatement.com" } ], - "description": "VarDumper Twig extension", - "homepage": "https://github.com/nlemoine/twig-dump-extension", - "time": "2020-03-06T15:18:29+00:00" + "description": "Manage rewrites and routes in WordPress with this dead-simple plugin", + "homepage": "https://www.upstatement.com", + "keywords": [ + "redirects", + "rewrite", + "routes", + "routing" + ], + "support": { + "issues": "https://github.com/Upstatement/routes/issues", + "source": "https://github.com/Upstatement/routes", + "wiki": "https://github.com/Upstatement/routes/wiki" + }, + "time": "2025-02-25T18:07:09+00:00" } ], + "packages-dev": [], "aliases": [], "minimum-stability": "stable", "stability-flags": [], @@ -648,5 +1189,5 @@ "prefer-lowest": false, "platform": [], "platform-dev": [], - "plugin-api-version": "1.1.0" + "plugin-api-version": "2.6.0" } diff --git a/functions.php b/functions.php index 42ce2fe..38d6b8b 100644 --- a/functions.php +++ b/functions.php @@ -1,6 +1,12 @@ build_cpt());*/ register_post_type( $obj->slug, $obj->build_cpt() ); } -add_action( 'init', 'create_team_type' ); - -Routes::map('equipo/:slug', function($params){ - $query = ''; - Routes::load('team-member.php', $params, $query, 200); -}); - -Routes::map('actualidad', function($params){ - $query = ''; - Routes::load('actualidad.php', $params, $query, 200); -}); -//~ Routes::map('actualidad/page/:page', function($params){ - //~ $query = ''; - //~ $params = [ - //~ 'pg' => $params['page'] - //~ ]; - //~ $query = 'posts_per_page=3&paged='.intval($params['pg']); - //~ Routes::load('actualidad.php', $params, $query, 200); -//~ }); - -Routes::map('proyectos', function($params){ - $query = ''; - Routes::load('proyectos.php', $params, $query, 200); -}); +//add_action( 'init', 'create_team_type' ); // templatetag para rellenar con contenido dinamico la plantilla en desarollo function frontpage_posts($numberposts=-1,$template_name='frontpage_post.twig') { @@ -85,55 +68,17 @@ function andaira_gallery($attr) { } // Fin galeria -class AndairaSite extends StarterSite{ - public $enable_ua = true; - function __construct() { - parent::__construct(); - $this->recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify'; - $this->recaptcha_secret = '6Ldp7-cUAAAAALND-F6N211yRaam8jBF_W_jkzg_'; - $this->recaptcha_public = '6Ldp7-cUAAAAANJ68d2DomQJn-TbnUxfVPNHm95K'; - $this->CONTACT_FORM = dirname( __FILE__ ).'/templates/form_contact.html'; - } - function add_to_context( $context ) { - $context = parent::add_to_context($context); - // Servicios - $svs = array( - 'child_of' => 27, - 'parent ' => 27, - 'hierarchical' => 0, - 'sort_column' => 'menu_order', - 'sort_order' => 'asc', - 'post_type' => 'page', - 'post_status' => 'publish', - ); - $servicios = get_pages( $svs ); - $context['servicios'] = new Timber\PostQuery($servicios); - //return $context; - - // Areas - $areas = get_terms( [ - 'taxonomy' => 'category', - 'parent' => 29, - 'hide_empty' => false, - ] - ); - //dump($areas); - $context['areas'] = $areas; - - //Logotipos - $context['logotipo_cabecera'] = get_field('logotipo_cabecera', 'option'); - - return $context; - } -} // Paginacion -$context['pagination'] = Timber::get_pagination(); +//$context['pagination'] = Timber::get_pagination(); // Galeria add_shortcode('gallery', 'andaira_gallery'); +Timber::init(); + $site = new AndairaSite(); //~ $site->main_menu_id = 2; + if (defined('WP_CLI') && WP_CLI) {require_once dirname(__FILE__).'/scripts/commands.php';} -?> + diff --git a/page.php b/page.php index 912170d..cb2c8a8 100644 --- a/page.php +++ b/page.php @@ -1,6 +1,6 @@ recaptcha_url = 'https://www.google.com/recaptcha/api/siteverify'; + $this->recaptcha_secret = '6Ldp7-cUAAAAALND-F6N211yRaam8jBF_W_jkzg_'; + $this->recaptcha_public = '6Ldp7-cUAAAAANJ68d2DomQJn-TbnUxfVPNHm95K'; + $this->CONTACT_FORM = dirname( __FILE__ ).'/templates/form_contact.html'; + } + function add_to_context( $context ) { + $context = parent::add_to_context($context); + // Servicios + $svs = array( + 'child_of' => 27, + 'parent ' => 27, + 'hierarchical' => 0, + 'sort_column' => 'menu_order', + 'sort_order' => 'asc', + 'post_type' => 'page', + 'post_status' => 'publish', + ); + $servicios = get_pages( $svs ); + $context['servicios'] = new Timber\PostQuery($servicios); + //return $context; + + // Areas + $areas = get_terms( [ + 'taxonomy' => 'category', + 'parent' => 29, + 'hide_empty' => false, + ] + ); + //dump($areas); + $context['areas'] = $areas; + + //Logotipos + $context['logotipo_cabecera'] = get_field('logotipo_cabecera', 'option'); + + return $context; + } +} + diff --git a/style.css b/style.css index 08a1155..81c23f3 100644 --- a/style.css +++ b/style.css @@ -5,7 +5,6 @@ Version: 1.0 Description: Child Theme de Timber adpatado para este sitio web. Author: Freepress S. Coop. Mad. y Estudio Nexos Author URI: http://www.freepress.coop -Template: TimberBase */ body { font-family: 'Karla', sans-serif; font-size:16px; line-height:25px; } diff --git a/vendor/agencearcange/wp-twig-dump/README.md b/vendor/agencearcange/wp-twig-dump/README.md deleted file mode 100644 index acb8e85..0000000 --- a/vendor/agencearcange/wp-twig-dump/README.md +++ /dev/null @@ -1,25 +0,0 @@ -# Wordpress Twig dump() - -This must use plugin allow `{{ dump() }}` for your WordPress development. - -## Specifications - -* Timber libray was needed -* Autoload your must-use plugins with [bedrock-autoloader](https://github.com/roots/bedrock/blob/master/web/app/mu-plugins/bedrock-autoloader.php) - -Tested with : - -* Wordpress 4.5.* => 5.3.* -* Wordpress single / multi website - -### Installation - -#### With composer - -``` -composer require agencearcange/wp-twig-dump --dev -``` - -#### Without composer - -Just copy this plugin into your `plugin` / `muplugin` folder \ No newline at end of file diff --git a/vendor/agencearcange/wp-twig-dump/composer.json b/vendor/agencearcange/wp-twig-dump/composer.json deleted file mode 100644 index 6a6d83f..0000000 --- a/vendor/agencearcange/wp-twig-dump/composer.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "name": "agencearcange/wp-twig-dump", - "description": "Allow dump() in twig templates", - "keywords": ["wordpress", "plugin", "timber", "twig"], - "homepage": "https://github.com/agencearcange/arcange-wpdump", - "type": "wordpress-muplugin", - "license": "GPLv2", - "require": { - "hellonico/twig-dump-extension": "^1.0" - } -} diff --git a/vendor/agencearcange/wp-twig-dump/wp-twig-dump.php b/vendor/agencearcange/wp-twig-dump/wp-twig-dump.php deleted file mode 100644 index ad591ab..0000000 --- a/vendor/agencearcange/wp-twig-dump/wp-twig-dump.php +++ /dev/null @@ -1,13 +0,0 @@ -addExtension(new HelloNico\Twig\DumpExtension()); - return $twig; - }); -} diff --git a/vendor/altorouter/altorouter/.github/workflows/php-check-syntax.yml b/vendor/altorouter/altorouter/.github/workflows/php-check-syntax.yml new file mode 100644 index 0000000..edf16c3 --- /dev/null +++ b/vendor/altorouter/altorouter/.github/workflows/php-check-syntax.yml @@ -0,0 +1,20 @@ +name: Check PHP syntax + +on: [ push, pull_request ] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: ['7.3', '7.4', '8.0', '8.1', '8.2', '8.3', 'highest'] + steps: + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + + - name: checkout repo + uses: actions/checkout@v3 + + - run: composer run check-syntax diff --git a/vendor/altorouter/altorouter/.github/workflows/php.yml b/vendor/altorouter/altorouter/.github/workflows/php.yml new file mode 100644 index 0000000..acd5e2f --- /dev/null +++ b/vendor/altorouter/altorouter/.github/workflows/php.yml @@ -0,0 +1,39 @@ +name: PHP + +on: [ push, pull_request ] + +jobs: + build: + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: [ '7.3', 'highest' ] + + steps: + - uses: actions/checkout@v2 + + - uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + tools: composer + + - name: Validate composer.json and composer.lock + run: composer validate + + - name: Get composer cache directory + id: composer-cache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer- + + - name: Install dependencies + if: steps.composer-cache.outputs.cache-hit != 'true' + run: composer install --no-progress + + - name: Run test suite + run: composer run-script test diff --git a/vendor/altorouter/altorouter/AltoRouter.php b/vendor/altorouter/altorouter/AltoRouter.php new file mode 100644 index 0000000..a0081b3 --- /dev/null +++ b/vendor/altorouter/altorouter/AltoRouter.php @@ -0,0 +1,300 @@ + + +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. +*/ + +class AltoRouter +{ + + /** + * @var array Array of all routes (incl. named routes). + */ + protected $routes = []; + + /** + * @var array Array of all named routes. + */ + protected $namedRoutes = []; + + /** + * @var string Can be used to ignore leading part of the Request URL (if main file lives in subdirectory of host) + */ + protected $basePath = ''; + + /** + * @var array Array of default match types (regex helpers) + */ + protected $matchTypes = [ + 'i' => '[0-9]++', + 'a' => '[0-9A-Za-z]++', + 'h' => '[0-9A-Fa-f]++', + '*' => '.+?', + '**' => '.++', + '' => '[^/\.]++' + ]; + + /** + * Create router in one call from config. + * + * @param array $routes + * @param string $basePath + * @param array $matchTypes + * @throws Exception + */ + public function __construct(array $routes = [], string $basePath = '', array $matchTypes = []) + { + $this->addRoutes($routes); + $this->setBasePath($basePath); + $this->addMatchTypes($matchTypes); + } + + /** + * Retrieves all routes. + * Useful if you want to process or display routes. + * @return array All routes. + */ + public function getRoutes(): array + { + return $this->routes; + } + + /** + * Add multiple routes at once from array in the following format: + * + * $routes = [ + * [$method, $route, $target, $name] + * ]; + * + * @param array $routes + * @return void + * @author Koen Punt + * @throws Exception + */ + public function addRoutes($routes) + { + if (!is_array($routes) && !$routes instanceof Traversable) { + throw new RuntimeException('Routes should be an array or an instance of Traversable'); + } + foreach ($routes as $route) { + call_user_func_array([$this, 'map'], $route); + } + } + + /** + * Set the base path. + * Useful if you are running your application from a subdirectory. + * @param string $basePath + */ + public function setBasePath(string $basePath) + { + $this->basePath = $basePath; + } + + /** + * Add named match types. It uses array_merge so keys can be overwritten. + * + * @param array $matchTypes The key is the name and the value is the regex. + */ + public function addMatchTypes(array $matchTypes) + { + $this->matchTypes = array_merge($this->matchTypes, $matchTypes); + } + + /** + * Map a route to a target + * + * @param string $method One of 5 HTTP Methods, or a pipe-separated list of multiple HTTP Methods (GET|POST|PATCH|PUT|DELETE) + * @param string $route The route regex, custom regex must start with an @. You can use multiple pre-set regex filters, like [i:id] + * @param mixed $target The target where this route should point to. Can be anything. + * @param string $name Optional name of this route. Supply if you want to reverse route this url in your application. + * @throws Exception + */ + public function map(string $method, string $route, $target, ?string $name = null) + { + + $this->routes[] = [$method, $route, $target, $name]; + + if ($name) { + if (isset($this->namedRoutes[$name])) { + throw new RuntimeException("Can not redeclare route '{$name}'"); + } + $this->namedRoutes[$name] = $route; + } + } + + /** + * Reversed routing + * + * Generate the URL for a named route. Replace regexes with supplied parameters + * + * @param string $routeName The name of the route. + * @param array $params Associative array of parameters to replace placeholders with. + * @return string The URL of the route with named parameters in place. + * @throws Exception + */ + public function generate(string $routeName, array $params = []): string + { + + // Check if named route exists + if (!isset($this->namedRoutes[$routeName])) { + throw new RuntimeException("Route '{$routeName}' does not exist."); + } + + // Replace named parameters + $route = $this->namedRoutes[$routeName]; + + // prepend base path to route url again + $url = $this->basePath . $route; + + if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) { + foreach ($matches as $index => $match) { + list($block, $pre, $type, $param, $optional) = $match; + + if ($pre) { + $block = substr($block, 1); + } + + if (isset($params[$param])) { + // Part is found, replace for param value + $url = str_replace($block, $params[$param], $url); + } elseif ($optional && $index !== 0) { + // Only strip preceding slash if it's not at the base + $url = str_replace($pre . $block, '', $url); + } else { + // Strip match block + $url = str_replace($block, '', $url); + } + } + } + + return $url; + } + + /** + * Match a given Request Url against stored routes + * @param string $requestUrl + * @param string $requestMethod + * @return array|boolean Array with route information on success, false on failure (no match). + */ + public function match(?string $requestUrl = null, ?string $requestMethod = null) + { + + $params = []; + + // set Request Url if it isn't passed as parameter + if ($requestUrl === null) { + $requestUrl = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '/'; + } + + // strip base path from request url + $requestUrl = substr($requestUrl, strlen($this->basePath)); + + // Strip query string (?a=b) from Request Url + if (($strpos = strpos($requestUrl, '?')) !== false) { + $requestUrl = substr($requestUrl, 0, $strpos); + } + + $lastRequestUrlChar = $requestUrl ? $requestUrl[strlen($requestUrl)-1] : ''; + + // set Request Method if it isn't passed as a parameter + if ($requestMethod === null) { + $requestMethod = isset($_SERVER['REQUEST_METHOD']) ? $_SERVER['REQUEST_METHOD'] : 'GET'; + } + + foreach ($this->routes as $handler) { + list($methods, $route, $target, $name) = $handler; + + $method_match = (stripos($methods, $requestMethod) !== false); + + // Method did not match, continue to next route. + if (!$method_match) { + continue; + } + + if ($route === '*') { + // * wildcard (matches all) + $match = true; + } elseif (isset($route[0]) && $route[0] === '@') { + // @ regex delimiter + $pattern = '`' . substr($route, 1) . '`u'; + $match = preg_match($pattern, $requestUrl, $params) === 1; + } elseif (($position = strpos($route, '[')) === false) { + // No params in url, do string comparison + $match = strcmp($requestUrl, $route) === 0; + } else { + // Compare longest non-param string with url before moving on to regex + // Check if last character before param is a slash, because it could be optional if param is optional too (see https://github.com/dannyvankooten/AltoRouter/issues/241) + if (strncmp($requestUrl, $route, $position) !== 0 && ($lastRequestUrlChar === '/' || $route[$position-1] !== '/')) { + continue; + } + + $regex = $this->compileRoute($route); + $match = preg_match($regex, $requestUrl, $params) === 1; + } + + if ($match) { + if ($params) { + foreach ($params as $key => $value) { + if (is_numeric($key)) { + unset($params[$key]); + } + } + } + + return [ + 'target' => $target, + 'params' => $params, + 'name' => $name + ]; + } + } + + return false; + } + + /** + * Compile the regex for a given route (EXPENSIVE) + * @param string $route + * @return string + */ + protected function compileRoute(string $route): string + { + if (preg_match_all('`(/|\.|)\[([^:\]]*+)(?::([^:\]]*+))?\](\?|)`', $route, $matches, PREG_SET_ORDER)) { + $matchTypes = $this->matchTypes; + foreach ($matches as $match) { + list($block, $pre, $type, $param, $optional) = $match; + + if (isset($matchTypes[$type])) { + $type = $matchTypes[$type]; + } + if ($pre === '.') { + $pre = '\.'; + } + + $optional = $optional !== '' ? '?' : null; + + //Older versions of PCRE require the 'P' in (?P) + $pattern = '(?:' + . ($pre !== '' ? $pre : null) + . '(' + . ($param !== '' ? "?P<$param>" : null) + . $type + . ')' + . $optional + . ')' + . $optional; + + $route = str_replace($block, $pattern, $route); + } + } + return "`^$route$`u"; + } +} diff --git a/vendor/altorouter/altorouter/LICENSE.md b/vendor/altorouter/altorouter/LICENSE.md new file mode 100644 index 0000000..a1130bb --- /dev/null +++ b/vendor/altorouter/altorouter/LICENSE.md @@ -0,0 +1,9 @@ +MIT License + +Copyright (c) 2012 Danny van Kooten + +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. \ No newline at end of file diff --git a/vendor/altorouter/altorouter/README.md b/vendor/altorouter/altorouter/README.md new file mode 100644 index 0000000..3f512b6 --- /dev/null +++ b/vendor/altorouter/altorouter/README.md @@ -0,0 +1,57 @@ +# AltoRouter ![PHP status](https://github.com/dannyvankooten/AltoRouter/workflows/PHP/badge.svg) [![Latest Stable Version](https://poser.pugx.org/altorouter/altorouter/v/stable.svg)](https://packagist.org/packages/altorouter/altorouter) [![License](https://poser.pugx.org/altorouter/altorouter/license.svg)](https://packagist.org/packages/altorouter/altorouter) + +AltoRouter is a small but powerful routing class, heavily inspired by [klein.php](https://github.com/chriso/klein.php/). + +```php +$router = new AltoRouter(); + +// map homepage +$router->map('GET', '/', function() { + require __DIR__ . '/views/home.php'; +}); + +// dynamic named route +$router->map('GET|POST', '/users/[i:id]/', function($id) { + $user = ..... + require __DIR__ . '/views/user/details.php'; +}, 'user-details'); + +// echo URL to user-details page for ID 5 +echo $router->generate('user-details', ['id' => 5]); // Output: "/users/5" +``` + +## Features + +* Can be used with all HTTP Methods +* Dynamic routing with named route parameters +* Reversed routing +* Flexible regular expression routing (inspired by [Sinatra](http://www.sinatrarb.com/)) +* Custom regexes + +## Getting started + +You need PHP >= 7.3 to use AltoRouter, although we highly recommend you [use an officially supported PHP version](https://secure.php.net/supported-versions.php) that is not EOL. + +- [Install AltoRouter](https://dannyvankooten.github.io/AltoRouter//usage/install.html) +- [Rewrite all requests to AltoRouter](https://dannyvankooten.github.io/AltoRouter//usage/rewrite-requests.html) +- [Map your routes](https://dannyvankooten.github.io/AltoRouter//usage/mapping-routes.html) +- [Match requests](https://dannyvankooten.github.io/AltoRouter//usage/matching-requests.html) +- [Process the request your preferred way](https://dannyvankooten.github.io/AltoRouter//usage/processing-requests.html) + +## Contributors +- [Danny van Kooten](https://github.com/dannyvankooten) +- [Koen Punt](https://github.com/koenpunt) +- [John Long](https://github.com/adduc) +- [Niahoo Osef](https://github.com/niahoo) + +## License + +MIT License + +Copyright (c) 2012 Danny van Kooten + +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/vendor/altorouter/altorouter/composer.json b/vendor/altorouter/altorouter/composer.json new file mode 100644 index 0000000..1146b8d --- /dev/null +++ b/vendor/altorouter/altorouter/composer.json @@ -0,0 +1,36 @@ +{ + "name": "altorouter/altorouter", + "description": "A lightning fast router for PHP", + "keywords": ["router", "routing", "lightweight"], + "homepage": "https://github.com/dannyvankooten/AltoRouter", + "license": "MIT", + "authors": [ + { + "name": "Danny van Kooten", + "email": "dannyvankooten@gmail.com", + "homepage": "http://dannyvankooten.com/" + }, + { + "name": "Koen Punt", + "homepage": "https://github.com/koenpunt" + }, + { + "name": "niahoo", + "homepage": "https://github.com/niahoo" + } + ], + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "9.6.*", + "squizlabs/php_codesniffer": "3.6.2" + }, + "autoload": { + "classmap": ["AltoRouter.php"] + }, + "scripts": { + "test": "vendor/bin/phpunit", + "check-syntax": "find . -name '*.php' -not -path './vendor/*' -print0 | xargs -0 -n1 php -l" + } +} diff --git a/vendor/altorouter/altorouter/phpcs.xml b/vendor/altorouter/altorouter/phpcs.xml new file mode 100644 index 0000000..d59cccf --- /dev/null +++ b/vendor/altorouter/altorouter/phpcs.xml @@ -0,0 +1,10 @@ + + + rules + + + tests + AltoRouter.php + examples/ + + diff --git a/vendor/autoload.php b/vendor/autoload.php index 787dc33..d1a0732 100644 --- a/vendor/autoload.php +++ b/vendor/autoload.php @@ -2,6 +2,24 @@ // autoload.php @generated by Composer +if (PHP_VERSION_ID < 50600) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + $err = 'Composer 2.3.0 dropped support for autoloading on PHP <5.6 and you are running '.PHP_VERSION.', please upgrade PHP or use Composer 2.2 LTS via "composer self-update --2.2". Aborting.'.PHP_EOL; + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, $err); + } elseif (!headers_sent()) { + echo $err; + } + } + trigger_error( + $err, + E_USER_ERROR + ); +} + require_once __DIR__ . '/composer/autoload_real.php'; return ComposerAutoloaderInite360deab1e3925e3deb6282d6a5a1f68::getLoader(); diff --git a/vendor/bin/var-dump-server b/vendor/bin/var-dump-server deleted file mode 120000 index 6bd4e93..0000000 --- a/vendor/bin/var-dump-server +++ /dev/null @@ -1 +0,0 @@ -../symfony/var-dumper/Resources/bin/var-dump-server \ No newline at end of file diff --git a/vendor/composer/ClassLoader.php b/vendor/composer/ClassLoader.php index fce8549..7824d8f 100644 --- a/vendor/composer/ClassLoader.php +++ b/vendor/composer/ClassLoader.php @@ -37,57 +37,126 @@ namespace Composer\Autoload; * * @author Fabien Potencier * @author Jordi Boggiano - * @see http://www.php-fig.org/psr/psr-0/ - * @see http://www.php-fig.org/psr/psr-4/ + * @see https://www.php-fig.org/psr/psr-0/ + * @see https://www.php-fig.org/psr/psr-4/ */ class ClassLoader { + /** @var \Closure(string):void */ + private static $includeFile; + + /** @var string|null */ + private $vendorDir; + // PSR-4 + /** + * @var array> + */ private $prefixLengthsPsr4 = array(); + /** + * @var array> + */ private $prefixDirsPsr4 = array(); + /** + * @var list + */ private $fallbackDirsPsr4 = array(); // PSR-0 + /** + * List of PSR-0 prefixes + * + * Structured as array('F (first letter)' => array('Foo\Bar (full prefix)' => array('path', 'path2'))) + * + * @var array>> + */ private $prefixesPsr0 = array(); + /** + * @var list + */ private $fallbackDirsPsr0 = array(); + /** @var bool */ private $useIncludePath = false; + + /** + * @var array + */ private $classMap = array(); + + /** @var bool */ private $classMapAuthoritative = false; + + /** + * @var array + */ private $missingClasses = array(); + + /** @var string|null */ private $apcuPrefix; + /** + * @var array + */ + private static $registeredLoaders = array(); + + /** + * @param string|null $vendorDir + */ + public function __construct($vendorDir = null) + { + $this->vendorDir = $vendorDir; + self::initializeIncludeClosure(); + } + + /** + * @return array> + */ public function getPrefixes() { if (!empty($this->prefixesPsr0)) { - return call_user_func_array('array_merge', $this->prefixesPsr0); + return call_user_func_array('array_merge', array_values($this->prefixesPsr0)); } return array(); } + /** + * @return array> + */ public function getPrefixesPsr4() { return $this->prefixDirsPsr4; } + /** + * @return list + */ public function getFallbackDirs() { return $this->fallbackDirsPsr0; } + /** + * @return list + */ public function getFallbackDirsPsr4() { return $this->fallbackDirsPsr4; } + /** + * @return array Array of classname => path + */ public function getClassMap() { return $this->classMap; } /** - * @param array $classMap Class to filename map + * @param array $classMap Class to filename map + * + * @return void */ public function addClassMap(array $classMap) { @@ -102,22 +171,25 @@ class ClassLoader * Registers a set of PSR-0 directories for a given prefix, either * appending or prepending to the ones previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 root directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 root directories + * @param bool $prepend Whether to prepend the directories + * + * @return void */ public function add($prefix, $paths, $prepend = false) { + $paths = (array) $paths; if (!$prefix) { if ($prepend) { $this->fallbackDirsPsr0 = array_merge( - (array) $paths, + $paths, $this->fallbackDirsPsr0 ); } else { $this->fallbackDirsPsr0 = array_merge( $this->fallbackDirsPsr0, - (array) $paths + $paths ); } @@ -126,19 +198,19 @@ class ClassLoader $first = $prefix[0]; if (!isset($this->prefixesPsr0[$first][$prefix])) { - $this->prefixesPsr0[$first][$prefix] = (array) $paths; + $this->prefixesPsr0[$first][$prefix] = $paths; return; } if ($prepend) { $this->prefixesPsr0[$first][$prefix] = array_merge( - (array) $paths, + $paths, $this->prefixesPsr0[$first][$prefix] ); } else { $this->prefixesPsr0[$first][$prefix] = array_merge( $this->prefixesPsr0[$first][$prefix], - (array) $paths + $paths ); } } @@ -147,25 +219,28 @@ class ClassLoader * Registers a set of PSR-4 directories for a given namespace, either * appending or prepending to the ones previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories - * @param bool $prepend Whether to prepend the directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories + * @param bool $prepend Whether to prepend the directories * * @throws \InvalidArgumentException + * + * @return void */ public function addPsr4($prefix, $paths, $prepend = false) { + $paths = (array) $paths; if (!$prefix) { // Register directories for the root namespace. if ($prepend) { $this->fallbackDirsPsr4 = array_merge( - (array) $paths, + $paths, $this->fallbackDirsPsr4 ); } else { $this->fallbackDirsPsr4 = array_merge( $this->fallbackDirsPsr4, - (array) $paths + $paths ); } } elseif (!isset($this->prefixDirsPsr4[$prefix])) { @@ -175,18 +250,18 @@ class ClassLoader throw new \InvalidArgumentException("A non-empty PSR-4 prefix must end with a namespace separator."); } $this->prefixLengthsPsr4[$prefix[0]][$prefix] = $length; - $this->prefixDirsPsr4[$prefix] = (array) $paths; + $this->prefixDirsPsr4[$prefix] = $paths; } elseif ($prepend) { // Prepend directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( - (array) $paths, + $paths, $this->prefixDirsPsr4[$prefix] ); } else { // Append directories for an already registered namespace. $this->prefixDirsPsr4[$prefix] = array_merge( $this->prefixDirsPsr4[$prefix], - (array) $paths + $paths ); } } @@ -195,8 +270,10 @@ class ClassLoader * Registers a set of PSR-0 directories for a given prefix, * replacing any others previously set for this prefix. * - * @param string $prefix The prefix - * @param array|string $paths The PSR-0 base directories + * @param string $prefix The prefix + * @param list|string $paths The PSR-0 base directories + * + * @return void */ public function set($prefix, $paths) { @@ -211,10 +288,12 @@ class ClassLoader * Registers a set of PSR-4 directories for a given namespace, * replacing any others previously set for this namespace. * - * @param string $prefix The prefix/namespace, with trailing '\\' - * @param array|string $paths The PSR-4 base directories + * @param string $prefix The prefix/namespace, with trailing '\\' + * @param list|string $paths The PSR-4 base directories * * @throws \InvalidArgumentException + * + * @return void */ public function setPsr4($prefix, $paths) { @@ -234,6 +313,8 @@ class ClassLoader * Turns on searching the include path for class files. * * @param bool $useIncludePath + * + * @return void */ public function setUseIncludePath($useIncludePath) { @@ -256,6 +337,8 @@ class ClassLoader * that have not been registered with the class map. * * @param bool $classMapAuthoritative + * + * @return void */ public function setClassMapAuthoritative($classMapAuthoritative) { @@ -276,6 +359,8 @@ class ClassLoader * APCu prefix to use to cache found/not-found classes, if the extension is enabled. * * @param string|null $apcuPrefix + * + * @return void */ public function setApcuPrefix($apcuPrefix) { @@ -296,33 +381,55 @@ class ClassLoader * Registers this instance as an autoloader. * * @param bool $prepend Whether to prepend the autoloader or not + * + * @return void */ public function register($prepend = false) { spl_autoload_register(array($this, 'loadClass'), true, $prepend); + + if (null === $this->vendorDir) { + return; + } + + if ($prepend) { + self::$registeredLoaders = array($this->vendorDir => $this) + self::$registeredLoaders; + } else { + unset(self::$registeredLoaders[$this->vendorDir]); + self::$registeredLoaders[$this->vendorDir] = $this; + } } /** * Unregisters this instance as an autoloader. + * + * @return void */ public function unregister() { spl_autoload_unregister(array($this, 'loadClass')); + + if (null !== $this->vendorDir) { + unset(self::$registeredLoaders[$this->vendorDir]); + } } /** * Loads the given class or interface. * * @param string $class The name of the class - * @return bool|null True if loaded, null otherwise + * @return true|null True if loaded, null otherwise */ public function loadClass($class) { if ($file = $this->findFile($class)) { - includeFile($file); + $includeFile = self::$includeFile; + $includeFile($file); return true; } + + return null; } /** @@ -367,6 +474,21 @@ class ClassLoader return $file; } + /** + * Returns the currently registered loaders keyed by their corresponding vendor directories. + * + * @return array + */ + public static function getRegisteredLoaders() + { + return self::$registeredLoaders; + } + + /** + * @param string $class + * @param string $ext + * @return string|false + */ private function findFileWithExtension($class, $ext) { // PSR-4 lookup @@ -432,14 +554,26 @@ class ClassLoader return false; } -} -/** - * Scope isolated include. - * - * Prevents access to $this/self from included files. - */ -function includeFile($file) -{ - include $file; + /** + * @return void + */ + private static function initializeIncludeClosure() + { + if (self::$includeFile !== null) { + return; + } + + /** + * Scope isolated include. + * + * Prevents access to $this/self from included files. + * + * @param string $file + * @return void + */ + self::$includeFile = \Closure::bind(static function($file) { + include $file; + }, null, null); + } } diff --git a/vendor/composer/InstalledVersions.php b/vendor/composer/InstalledVersions.php new file mode 100644 index 0000000..51e734a --- /dev/null +++ b/vendor/composer/InstalledVersions.php @@ -0,0 +1,359 @@ + + * Jordi Boggiano + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +namespace Composer; + +use Composer\Autoload\ClassLoader; +use Composer\Semver\VersionParser; + +/** + * This class is copied in every Composer installed project and available to all + * + * See also https://getcomposer.org/doc/07-runtime.md#installed-versions + * + * To require its presence, you can require `composer-runtime-api ^2.0` + * + * @final + */ +class InstalledVersions +{ + /** + * @var mixed[]|null + * @psalm-var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array}|array{}|null + */ + private static $installed; + + /** + * @var bool|null + */ + private static $canGetVendors; + + /** + * @var array[] + * @psalm-var array}> + */ + private static $installedByVendor = array(); + + /** + * Returns a list of all package names which are present, either by being installed, replaced or provided + * + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackages() + { + $packages = array(); + foreach (self::getInstalled() as $installed) { + $packages[] = array_keys($installed['versions']); + } + + if (1 === \count($packages)) { + return $packages[0]; + } + + return array_keys(array_flip(\call_user_func_array('array_merge', $packages))); + } + + /** + * Returns a list of all package names with a specific type e.g. 'library' + * + * @param string $type + * @return string[] + * @psalm-return list + */ + public static function getInstalledPackagesByType($type) + { + $packagesByType = array(); + + foreach (self::getInstalled() as $installed) { + foreach ($installed['versions'] as $name => $package) { + if (isset($package['type']) && $package['type'] === $type) { + $packagesByType[] = $name; + } + } + } + + return $packagesByType; + } + + /** + * Checks whether the given package is installed + * + * This also returns true if the package name is provided or replaced by another package + * + * @param string $packageName + * @param bool $includeDevRequirements + * @return bool + */ + public static function isInstalled($packageName, $includeDevRequirements = true) + { + foreach (self::getInstalled() as $installed) { + if (isset($installed['versions'][$packageName])) { + return $includeDevRequirements || !isset($installed['versions'][$packageName]['dev_requirement']) || $installed['versions'][$packageName]['dev_requirement'] === false; + } + } + + return false; + } + + /** + * Checks whether the given package satisfies a version constraint + * + * e.g. If you want to know whether version 2.3+ of package foo/bar is installed, you would call: + * + * Composer\InstalledVersions::satisfies(new VersionParser, 'foo/bar', '^2.3') + * + * @param VersionParser $parser Install composer/semver to have access to this class and functionality + * @param string $packageName + * @param string|null $constraint A version constraint to check for, if you pass one you have to make sure composer/semver is required by your package + * @return bool + */ + public static function satisfies(VersionParser $parser, $packageName, $constraint) + { + $constraint = $parser->parseConstraints((string) $constraint); + $provided = $parser->parseConstraints(self::getVersionRanges($packageName)); + + return $provided->matches($constraint); + } + + /** + * Returns a version constraint representing all the range(s) which are installed for a given package + * + * It is easier to use this via isInstalled() with the $constraint argument if you need to check + * whether a given version of a package is installed, and not just whether it exists + * + * @param string $packageName + * @return string Version constraint usable with composer/semver + */ + public static function getVersionRanges($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + $ranges = array(); + if (isset($installed['versions'][$packageName]['pretty_version'])) { + $ranges[] = $installed['versions'][$packageName]['pretty_version']; + } + if (array_key_exists('aliases', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['aliases']); + } + if (array_key_exists('replaced', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['replaced']); + } + if (array_key_exists('provided', $installed['versions'][$packageName])) { + $ranges = array_merge($ranges, $installed['versions'][$packageName]['provided']); + } + + return implode(' || ', $ranges); + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['version'])) { + return null; + } + + return $installed['versions'][$packageName]['version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as version, use satisfies or getVersionRanges if you need to know if a given version is present + */ + public static function getPrettyVersion($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['pretty_version'])) { + return null; + } + + return $installed['versions'][$packageName]['pretty_version']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as reference + */ + public static function getReference($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + if (!isset($installed['versions'][$packageName]['reference'])) { + return null; + } + + return $installed['versions'][$packageName]['reference']; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @param string $packageName + * @return string|null If the package is being replaced or provided but is not really installed, null will be returned as install path. Packages of type metapackages also have a null install path. + */ + public static function getInstallPath($packageName) + { + foreach (self::getInstalled() as $installed) { + if (!isset($installed['versions'][$packageName])) { + continue; + } + + return isset($installed['versions'][$packageName]['install_path']) ? $installed['versions'][$packageName]['install_path'] : null; + } + + throw new \OutOfBoundsException('Package "' . $packageName . '" is not installed'); + } + + /** + * @return array + * @psalm-return array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool} + */ + public static function getRootPackage() + { + $installed = self::getInstalled(); + + return $installed[0]['root']; + } + + /** + * Returns the raw installed.php data for custom implementations + * + * @deprecated Use getAllRawData() instead which returns all datasets for all autoloaders present in the process. getRawData only returns the first dataset loaded, which may not be what you expect. + * @return array[] + * @psalm-return array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} + */ + public static function getRawData() + { + @trigger_error('getRawData only returns the first dataset loaded, which may not be what you expect. Use getAllRawData() instead which returns all datasets for all autoloaders present in the process.', E_USER_DEPRECATED); + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + self::$installed = include __DIR__ . '/installed.php'; + } else { + self::$installed = array(); + } + } + + return self::$installed; + } + + /** + * Returns the raw data of all installed.php which are currently loaded for custom implementations + * + * @return array[] + * @psalm-return list}> + */ + public static function getAllRawData() + { + return self::getInstalled(); + } + + /** + * Lets you reload the static array from another file + * + * This is only useful for complex integrations in which a project needs to use + * this class but then also needs to execute another project's autoloader in process, + * and wants to ensure both projects have access to their version of installed.php. + * + * A typical case would be PHPUnit, where it would need to make sure it reads all + * the data it needs from this class, then call reload() with + * `require $CWD/vendor/composer/installed.php` (or similar) as input to make sure + * the project in which it runs can then also use this class safely, without + * interference between PHPUnit's dependencies and the project's dependencies. + * + * @param array[] $data A vendor/composer/installed.php data set + * @return void + * + * @psalm-param array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $data + */ + public static function reload($data) + { + self::$installed = $data; + self::$installedByVendor = array(); + } + + /** + * @return array[] + * @psalm-return list}> + */ + private static function getInstalled() + { + if (null === self::$canGetVendors) { + self::$canGetVendors = method_exists('Composer\Autoload\ClassLoader', 'getRegisteredLoaders'); + } + + $installed = array(); + + if (self::$canGetVendors) { + foreach (ClassLoader::getRegisteredLoaders() as $vendorDir => $loader) { + if (isset(self::$installedByVendor[$vendorDir])) { + $installed[] = self::$installedByVendor[$vendorDir]; + } elseif (is_file($vendorDir.'/composer/installed.php')) { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require $vendorDir.'/composer/installed.php'; + $installed[] = self::$installedByVendor[$vendorDir] = $required; + if (null === self::$installed && strtr($vendorDir.'/composer', '\\', '/') === strtr(__DIR__, '\\', '/')) { + self::$installed = $installed[count($installed) - 1]; + } + } + } + } + + if (null === self::$installed) { + // only require the installed.php file if this file is loaded from its dumped location, + // and not from its source location in the composer/composer package, see https://github.com/composer/composer/issues/9937 + if (substr(__DIR__, -8, 1) !== 'C') { + /** @var array{root: array{name: string, pretty_version: string, version: string, reference: string|null, type: string, install_path: string, aliases: string[], dev: bool}, versions: array} $required */ + $required = require __DIR__ . '/installed.php'; + self::$installed = $required; + } else { + self::$installed = array(); + } + } + + if (self::$installed !== array()) { + $installed[] = self::$installed; + } + + return $installed; + } +} diff --git a/vendor/composer/autoload_classmap.php b/vendor/composer/autoload_classmap.php index 7a91153..6d812c2 100644 --- a/vendor/composer/autoload_classmap.php +++ b/vendor/composer/autoload_classmap.php @@ -2,8 +2,10 @@ // autoload_classmap.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( + 'AltoRouter' => $vendorDir . '/altorouter/altorouter/AltoRouter.php', + 'Composer\\InstalledVersions' => $vendorDir . '/composer/InstalledVersions.php', ); diff --git a/vendor/composer/autoload_files.php b/vendor/composer/autoload_files.php index 9e33b8c..5a8edd0 100644 --- a/vendor/composer/autoload_files.php +++ b/vendor/composer/autoload_files.php @@ -2,13 +2,17 @@ // autoload_files.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( - '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => $vendorDir . '/symfony/polyfill-ctype/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => $vendorDir . '/symfony/var-dumper/Resources/functions/dump.php', - 'fe62ba7e10580d903cc46d808b5961a4' => $vendorDir . '/tightenco/collect/src/Collect/Support/helpers.php', - 'caf31cc6ec7cf2241cb6f12c226c3846' => $vendorDir . '/tightenco/collect/src/Collect/Support/alias.php', + '6e3fae29631ef280660b3cdad06f25a8' => $vendorDir . '/symfony/deprecation-contracts/function.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => $vendorDir . '/symfony/polyfill-mbstring/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', + '23f09fe3194f8c2f70923f90d6702129' => $vendorDir . '/illuminate/collections/functions.php', + '60799491728b879e74601d83e38b2cad' => $vendorDir . '/illuminate/collections/helpers.php', ); diff --git a/vendor/composer/autoload_namespaces.php b/vendor/composer/autoload_namespaces.php index de88a12..1c89090 100644 --- a/vendor/composer/autoload_namespaces.php +++ b/vendor/composer/autoload_namespaces.php @@ -2,11 +2,11 @@ // autoload_namespaces.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( - 'Twig_' => array($vendorDir . '/twig/twig/lib'), + 'Routes' => array($vendorDir . '/upstatement/routes'), 'Requests' => array($vendorDir . '/rmccue/requests/library'), 'Gregwar\\Formidable' => array($vendorDir . '/gregwar/formidable'), 'Gregwar\\Cache' => array($vendorDir . '/gregwar/cache'), diff --git a/vendor/composer/autoload_psr4.php b/vendor/composer/autoload_psr4.php index 1a91b14..9783f3b 100644 --- a/vendor/composer/autoload_psr4.php +++ b/vendor/composer/autoload_psr4.php @@ -2,15 +2,19 @@ // autoload_psr4.php @generated by Composer -$vendorDir = dirname(dirname(__FILE__)); +$vendorDir = dirname(__DIR__); $baseDir = dirname($vendorDir); return array( 'Twig\\' => array($vendorDir . '/twig/twig/src'), - 'Tightenco\\Collect\\' => array($vendorDir . '/tightenco/collect/src/Collect'), + 'Timber\\' => array($vendorDir . '/timber/timber/src'), 'Symfony\\Polyfill\\Mbstring\\' => array($vendorDir . '/symfony/polyfill-mbstring'), 'Symfony\\Polyfill\\Ctype\\' => array($vendorDir . '/symfony/polyfill-ctype'), - 'Symfony\\Component\\VarDumper\\' => array($vendorDir . '/symfony/var-dumper'), 'Symfony\\Component\\PropertyAccess\\' => array($vendorDir . '/symfony/property-access'), - 'HelloNico\\Twig\\' => array($vendorDir . '/hellonico/twig-dump-extension/src'), + 'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'), + 'Psr\\Container\\' => array($vendorDir . '/psr/container/src'), + 'Illuminate\\Support\\' => array($vendorDir . '/illuminate/collections', $vendorDir . '/illuminate/conditionable', $vendorDir . '/illuminate/macroable'), + 'Illuminate\\Contracts\\' => array($vendorDir . '/illuminate/contracts'), + 'Composer\\Installers\\' => array($vendorDir . '/composer/installers/src/Composer/Installers'), + 'App\\' => array($baseDir . '/src'), ); diff --git a/vendor/composer/autoload_real.php b/vendor/composer/autoload_real.php index f2d6e2c..75a54c2 100644 --- a/vendor/composer/autoload_real.php +++ b/vendor/composer/autoload_real.php @@ -22,52 +22,29 @@ class ComposerAutoloaderInite360deab1e3925e3deb6282d6a5a1f68 return self::$loader; } + require __DIR__ . '/platform_check.php'; + spl_autoload_register(array('ComposerAutoloaderInite360deab1e3925e3deb6282d6a5a1f68', 'loadClassLoader'), true, true); - self::$loader = $loader = new \Composer\Autoload\ClassLoader(); + self::$loader = $loader = new \Composer\Autoload\ClassLoader(\dirname(__DIR__)); spl_autoload_unregister(array('ComposerAutoloaderInite360deab1e3925e3deb6282d6a5a1f68', 'loadClassLoader')); - $useStaticLoader = PHP_VERSION_ID >= 50600 && !defined('HHVM_VERSION') && (!function_exists('zend_loader_file_encoded') || !zend_loader_file_encoded()); - if ($useStaticLoader) { - require_once __DIR__ . '/autoload_static.php'; - - call_user_func(\Composer\Autoload\ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::getInitializer($loader)); - } else { - $map = require __DIR__ . '/autoload_namespaces.php'; - foreach ($map as $namespace => $path) { - $loader->set($namespace, $path); - } - - $map = require __DIR__ . '/autoload_psr4.php'; - foreach ($map as $namespace => $path) { - $loader->setPsr4($namespace, $path); - } - - $classMap = require __DIR__ . '/autoload_classmap.php'; - if ($classMap) { - $loader->addClassMap($classMap); - } - } + require __DIR__ . '/autoload_static.php'; + call_user_func(\Composer\Autoload\ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::getInitializer($loader)); $loader->register(true); - if ($useStaticLoader) { - $includeFiles = Composer\Autoload\ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::$files; - } else { - $includeFiles = require __DIR__ . '/autoload_files.php'; - } - foreach ($includeFiles as $fileIdentifier => $file) { - composerRequiree360deab1e3925e3deb6282d6a5a1f68($fileIdentifier, $file); + $filesToLoad = \Composer\Autoload\ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::$files; + $requireFile = \Closure::bind(static function ($fileIdentifier, $file) { + if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { + $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; + + require $file; + } + }, null, null); + foreach ($filesToLoad as $fileIdentifier => $file) { + $requireFile($fileIdentifier, $file); } return $loader; } } - -function composerRequiree360deab1e3925e3deb6282d6a5a1f68($fileIdentifier, $file) -{ - if (empty($GLOBALS['__composer_autoload_files'][$fileIdentifier])) { - require $file; - - $GLOBALS['__composer_autoload_files'][$fileIdentifier] = true; - } -} diff --git a/vendor/composer/autoload_static.php b/vendor/composer/autoload_static.php index a2d5664..741cb2c 100644 --- a/vendor/composer/autoload_static.php +++ b/vendor/composer/autoload_static.php @@ -7,29 +7,46 @@ namespace Composer\Autoload; class ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68 { public static $files = array ( - '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/bootstrap.php', '320cde22f66dd4f5d3fd621d3e88b98f' => __DIR__ . '/..' . '/symfony/polyfill-ctype/bootstrap.php', - '667aeda72477189d0494fecd327c3641' => __DIR__ . '/..' . '/symfony/var-dumper/Resources/functions/dump.php', - 'fe62ba7e10580d903cc46d808b5961a4' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/helpers.php', - 'caf31cc6ec7cf2241cb6f12c226c3846' => __DIR__ . '/..' . '/tightenco/collect/src/Collect/Support/alias.php', + '6e3fae29631ef280660b3cdad06f25a8' => __DIR__ . '/..' . '/symfony/deprecation-contracts/function.php', + '0e6d7bf4a5811bfa5cf40c5ccd6fae6a' => __DIR__ . '/..' . '/symfony/polyfill-mbstring/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', + '23f09fe3194f8c2f70923f90d6702129' => __DIR__ . '/..' . '/illuminate/collections/functions.php', + '60799491728b879e74601d83e38b2cad' => __DIR__ . '/..' . '/illuminate/collections/helpers.php', ); public static $prefixLengthsPsr4 = array ( 'T' => array ( 'Twig\\' => 5, - 'Tightenco\\Collect\\' => 18, + 'Timber\\' => 7, ), 'S' => array ( 'Symfony\\Polyfill\\Mbstring\\' => 26, 'Symfony\\Polyfill\\Ctype\\' => 23, - 'Symfony\\Component\\VarDumper\\' => 28, 'Symfony\\Component\\PropertyAccess\\' => 33, ), - 'H' => + 'P' => array ( - 'HelloNico\\Twig\\' => 15, + 'Psr\\SimpleCache\\' => 16, + 'Psr\\Container\\' => 14, + ), + 'I' => + array ( + 'Illuminate\\Support\\' => 19, + 'Illuminate\\Contracts\\' => 21, + ), + 'C' => + array ( + 'Composer\\Installers\\' => 20, + ), + 'A' => + array ( + 'App\\' => 4, ), ); @@ -38,9 +55,9 @@ class ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68 array ( 0 => __DIR__ . '/..' . '/twig/twig/src', ), - 'Tightenco\\Collect\\' => + 'Timber\\' => array ( - 0 => __DIR__ . '/..' . '/tightenco/collect/src/Collect', + 0 => __DIR__ . '/..' . '/timber/timber/src', ), 'Symfony\\Polyfill\\Mbstring\\' => array ( @@ -50,30 +67,45 @@ class ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68 array ( 0 => __DIR__ . '/..' . '/symfony/polyfill-ctype', ), - 'Symfony\\Component\\VarDumper\\' => - array ( - 0 => __DIR__ . '/..' . '/symfony/var-dumper', - ), 'Symfony\\Component\\PropertyAccess\\' => array ( 0 => __DIR__ . '/..' . '/symfony/property-access', ), - 'HelloNico\\Twig\\' => + 'Psr\\SimpleCache\\' => array ( - 0 => __DIR__ . '/..' . '/hellonico/twig-dump-extension/src', + 0 => __DIR__ . '/..' . '/psr/simple-cache/src', + ), + 'Psr\\Container\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/container/src', + ), + 'Illuminate\\Support\\' => + array ( + 0 => __DIR__ . '/..' . '/illuminate/collections', + 1 => __DIR__ . '/..' . '/illuminate/conditionable', + 2 => __DIR__ . '/..' . '/illuminate/macroable', + ), + 'Illuminate\\Contracts\\' => + array ( + 0 => __DIR__ . '/..' . '/illuminate/contracts', + ), + 'Composer\\Installers\\' => + array ( + 0 => __DIR__ . '/..' . '/composer/installers/src/Composer/Installers', + ), + 'App\\' => + array ( + 0 => __DIR__ . '/../..' . '/src', ), ); public static $prefixesPsr0 = array ( - 'T' => - array ( - 'Twig_' => - array ( - 0 => __DIR__ . '/..' . '/twig/twig/lib', - ), - ), 'R' => array ( + 'Routes' => + array ( + 0 => __DIR__ . '/..' . '/upstatement/routes', + ), 'Requests' => array ( 0 => __DIR__ . '/..' . '/rmccue/requests/library', @@ -92,12 +124,18 @@ class ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68 ), ); + public static $classMap = array ( + 'AltoRouter' => __DIR__ . '/..' . '/altorouter/altorouter/AltoRouter.php', + 'Composer\\InstalledVersions' => __DIR__ . '/..' . '/composer/InstalledVersions.php', + ); + public static function getInitializer(ClassLoader $loader) { return \Closure::bind(function () use ($loader) { $loader->prefixLengthsPsr4 = ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::$prefixLengthsPsr4; $loader->prefixDirsPsr4 = ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::$prefixDirsPsr4; $loader->prefixesPsr0 = ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::$prefixesPsr0; + $loader->classMap = ComposerStaticInite360deab1e3925e3deb6282d6a5a1f68::$classMap; }, null, ClassLoader::class); } diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 44ce63e..6b86efe 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -1,656 +1,1234 @@ -[ - { - "name": "agencearcange/wp-twig-dump", - "version": "v1.1", - "version_normalized": "1.1.0.0", - "source": { - "type": "git", - "url": "https://github.com/agencearcange/wp-twig-dump.git", - "reference": "d782ae81fe59f01578b97d3d526434c3879e90fe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/agencearcange/wp-twig-dump/zipball/d782ae81fe59f01578b97d3d526434c3879e90fe", - "reference": "d782ae81fe59f01578b97d3d526434c3879e90fe", - "shasum": "" - }, - "require": { - "hellonico/twig-dump-extension": "^1.0" - }, - "time": "2020-01-03T19:45:46+00:00", - "type": "wordpress-muplugin", - "installation-source": "dist", - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPLv2" +{ + "packages": [ + { + "name": "altorouter/altorouter", + "version": "2.0.3", + "version_normalized": "2.0.3.0", + "source": { + "type": "git", + "url": "https://github.com/dannyvankooten/AltoRouter.git", + "reference": "9931b976423f7334c94f7b5b348be8ab1da3415d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/dannyvankooten/AltoRouter/zipball/9931b976423f7334c94f7b5b348be8ab1da3415d", + "reference": "9931b976423f7334c94f7b5b348be8ab1da3415d", + "shasum": "" + }, + "require": { + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "9.6.*", + "squizlabs/php_codesniffer": "3.6.2" + }, + "time": "2025-01-05T20:33:28+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "classmap": [ + "AltoRouter.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Danny van Kooten", + "email": "dannyvankooten@gmail.com", + "homepage": "http://dannyvankooten.com/" + }, + { + "name": "Koen Punt", + "homepage": "https://github.com/koenpunt" + }, + { + "name": "niahoo", + "homepage": "https://github.com/niahoo" + } + ], + "description": "A lightning fast router for PHP", + "homepage": "https://github.com/dannyvankooten/AltoRouter", + "keywords": [ + "lightweight", + "router", + "routing" + ], + "support": { + "source": "https://github.com/dannyvankooten/AltoRouter/tree/2.0.3" + }, + "install-path": "../altorouter/altorouter" + }, + { + "name": "composer/installers", + "version": "v2.3.0", + "version_normalized": "2.3.0.0", + "source": { + "type": "git", + "url": "https://github.com/composer/installers.git", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/installers/zipball/12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "reference": "12fb2dfe5e16183de69e784a7b84046c43d97e8e", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^1.0 || ^2.0", + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "phpstan/phpstan": "^1.11", + "phpstan/phpstan-phpunit": "^1", + "symfony/phpunit-bridge": "^7.1.1", + "symfony/process": "^5 || ^6 || ^7" + }, + "time": "2024-06-24T20:46:46+00:00", + "type": "composer-plugin", + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Composer\\Installers\\": "src/Composer/Installers" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "description": "A multi-framework Composer library installer", + "homepage": "https://composer.github.io/installers/", + "keywords": [ + "Dolibarr", + "Eliasis", + "Hurad", + "ImageCMS", + "Kanboard", + "Lan Management System", + "MODX Evo", + "MantisBT", + "Mautic", + "Maya", + "OXID", + "Plentymarkets", + "Porto", + "RadPHP", + "SMF", + "Starbug", + "Thelia", + "Whmcs", + "WolfCMS", + "agl", + "annotatecms", + "attogram", + "bitrix", + "cakephp", + "chef", + "cockpit", + "codeigniter", + "concrete5", + "concreteCMS", + "croogo", + "dokuwiki", + "drupal", + "eZ Platform", + "elgg", + "expressionengine", + "fuelphp", + "grav", + "installer", + "itop", + "known", + "kohana", + "laravel", + "lavalite", + "lithium", + "magento", + "majima", + "mako", + "matomo", + "mediawiki", + "miaoxing", + "modulework", + "modx", + "moodle", + "osclass", + "pantheon", + "phpbb", + "piwik", + "ppi", + "processwire", + "puppet", + "pxcms", + "reindex", + "roundcube", + "shopware", + "silverstripe", + "sydes", + "sylius", + "tastyigniter", + "wordpress", + "yawik", + "zend", + "zikula" + ], + "support": { + "issues": "https://github.com/composer/installers/issues", + "source": "https://github.com/composer/installers/tree/v2.3.0" + }, + "funding": [ + { + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "type": "tidelift" + } + ], + "install-path": "./installers" + }, + { + "name": "gregwar/cache", + "version": "v1.0.12", + "version_normalized": "1.0.12.0", + "target-dir": "Gregwar/Cache", + "source": { + "type": "git", + "url": "https://github.com/Gregwar/Cache.git", + "reference": "305d0f5a12c0beecbbd7e1de236f59f39e0c0ac3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gregwar/Cache/zipball/305d0f5a12c0beecbbd7e1de236f59f39e0c0ac3", + "reference": "305d0f5a12c0beecbbd7e1de236f59f39e0c0ac3", + "shasum": "" + }, + "require": { + "php": ">=5.3" + }, + "time": "2016-09-23T08:16:04+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Gregwar\\Cache": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Gregwar", + "email": "g.passault@gmail.com" + } + ], + "description": "A lightweight file-system cache system", + "keywords": [ + "cache", + "caching", + "file-system", + "system" + ], + "install-path": "../gregwar/cache/Gregwar/Cache" + }, + { + "name": "gregwar/formidable", + "version": "2.1.0", + "version_normalized": "2.1.0.0", + "target-dir": "Gregwar/Formidable", + "source": { + "type": "git", + "url": "https://github.com/Gregwar/Formidable.git", + "reference": "b1a63d96b5f52b1e8d5b094ee5b861f880469998" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Gregwar/Formidable/zipball/b1a63d96b5f52b1e8d5b094ee5b861f880469998", + "reference": "b1a63d96b5f52b1e8d5b094ee5b861f880469998", + "shasum": "" + }, + "require": { + "gregwar/cache": "~1.0.9", + "php": ">=5.6", + "symfony/property-access": "~2.8.0" + }, + "require-dev": { + "gregwar/captcha": "~1.1.6", + "phpunit/phpunit": "~4.8.9" + }, + "suggest": { + "gregwar/captcha": "To implement Captcha capability into forms" + }, + "time": "2018-05-17T19:22:38+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Gregwar\\Formidable": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Grégoire Passault", + "email": "g.passault@gmail.com", + "homepage": "http://www.gregwar.com/" + }, + { + "name": "Ashley Kitson aka chippyash", + "email": "info@zf4.biz", + "homepage": "http://zf4.biz" + } + ], + "description": "Formidable, the pragmatic forms library", + "homepage": "https://gregwar.com/Formidable/", + "keywords": [ + "Forms", + "fields", + "html", + "input" + ], + "install-path": "../gregwar/formidable/Gregwar/Formidable" + }, + { + "name": "illuminate/collections", + "version": "v12.21.0", + "version_normalized": "12.21.0.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "a048b4fbbef4742ff2eee843971bb8278239c610" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/a048b4fbbef4742ff2eee843971bb8278239c610", + "reference": "a048b4fbbef4742ff2eee843971bb8278239c610", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0", + "php": "^8.2" + }, + "suggest": { + "illuminate/http": "Required to convert collections to API resources (^12.0).", + "symfony/var-dumper": "Required to use the dump method (^7.2)." + }, + "time": "2025-07-15T20:29:59+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "files": [ + "functions.php", + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "install-path": "../illuminate/collections" + }, + { + "name": "illuminate/conditionable", + "version": "v12.21.0", + "version_normalized": "12.21.0.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/ec677967c1f2faf90b8428919124d2184a4c9b49", + "reference": "ec677967c1f2faf90b8428919124d2184a4c9b49", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "time": "2025-05-13T15:08:45+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "install-path": "../illuminate/conditionable" + }, + { + "name": "illuminate/contracts", + "version": "v12.21.0", + "version_normalized": "12.21.0.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "c2eef857b808810f5cb187de58e23d25c1d443d9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/c2eef857b808810f5cb187de58e23d25c1d443d9", + "reference": "c2eef857b808810f5cb187de58e23d25c1d443d9", + "shasum": "" + }, + "require": { + "php": "^8.2", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "time": "2025-07-16T13:18:38+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "install-path": "../illuminate/contracts" + }, + { + "name": "illuminate/macroable", + "version": "v12.21.0", + "version_normalized": "12.21.0.0", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "e862e5648ee34004fa56046b746f490dfa86c613" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/e862e5648ee34004fa56046b746f490dfa86c613", + "reference": "e862e5648ee34004fa56046b746f490dfa86c613", + "shasum": "" + }, + "require": { + "php": "^8.2" + }, + "time": "2024-07-23T16:31:01+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "install-path": "../illuminate/macroable" + }, + { + "name": "psr/container", + "version": "2.0.2", + "version_normalized": "2.0.2.0", + "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" + }, + "time": "2021-11-05T16:47:00+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "installation-source": "dist", + "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" + }, + "install-path": "../psr/container" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "time": "2021-10-29T13:26:27+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "install-path": "../psr/simple-cache" + }, + { + "name": "rmccue/requests", + "version": "v1.7.0", + "version_normalized": "1.7.0.0", + "source": { + "type": "git", + "url": "https://github.com/rmccue/Requests.git", + "reference": "87932f52ffad70504d93f04f15690cf16a089546" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546", + "reference": "87932f52ffad70504d93f04f15690cf16a089546", + "shasum": "" + }, + "require": { + "php": ">=5.2" + }, + "require-dev": { + "requests/test-server": "dev-master" + }, + "time": "2016-10-13T00:11:37+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Requests": "library/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "ISC" + ], + "authors": [ + { + "name": "Ryan McCue", + "homepage": "http://ryanmccue.info" + } + ], + "description": "A HTTP library written in PHP, for human beings.", + "homepage": "http://github.com/rmccue/Requests", + "keywords": [ + "curl", + "fsockopen", + "http", + "idna", + "ipv6", + "iri", + "sockets" + ], + "install-path": "../rmccue/requests" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", + "version_normalized": "3.6.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "time": "2024-09-25T14:21:43+00:00", + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" + } + }, + "installation-source": "dist", + "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.6.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" + } + ], + "install-path": "../symfony/deprecation-contracts" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.15.0", + "version_normalized": "1.15.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", + "shasum": "" + }, + "require": { + "php": ">=5.3.3" + }, + "suggest": { + "ext-ctype": "For best performance" + }, + "time": "2020-02-27T09:26:54+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.15-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + }, + "files": [ + "bootstrap.php" + ] + }, + "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" + ], + "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" + } + ], + "install-path": "../symfony/polyfill-ctype" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.32.0", + "version_normalized": "1.32.0.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "shasum": "" + }, + "require": { + "ext-iconv": "*", + "php": ">=7.2" + }, + "provide": { + "ext-mbstring": "*" + }, + "suggest": { + "ext-mbstring": "For best performance" + }, + "time": "2024-12-23T08:48:59+00:00", + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, + "installation-source": "dist", + "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.32.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" + } + ], + "install-path": "../symfony/polyfill-mbstring" + }, + { + "name": "symfony/property-access", + "version": "v2.8.52", + "version_normalized": "2.8.52.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/property-access.git", + "reference": "c8f10191183be9bb0d5a1b8364d3891f1bde07b6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/property-access/zipball/c8f10191183be9bb0d5a1b8364d3891f1bde07b6", + "reference": "c8f10191183be9bb0d5a1b8364d3891f1bde07b6", + "shasum": "" + }, + "require": { + "php": ">=5.3.9", + "symfony/polyfill-ctype": "~1.8" + }, + "time": "2018-11-11T11:18:13+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.8-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Symfony\\Component\\PropertyAccess\\": "" + }, + "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": "Symfony PropertyAccess Component", + "homepage": "https://symfony.com", + "keywords": [ + "access", + "array", + "extraction", + "index", + "injection", + "object", + "property", + "property path", + "reflection" + ], + "install-path": "../symfony/property-access" + }, + { + "name": "timber/timber", + "version": "v2.3.2", + "version_normalized": "2.3.2.0", + "source": { + "type": "git", + "url": "https://github.com/timber/timber.git", + "reference": "08ce601167a03ed7a4fa7da740481c17ed650ef0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/timber/timber/zipball/08ce601167a03ed7a4fa7da740481c17ed650ef0", + "reference": "08ce601167a03ed7a4fa7da740481c17ed650ef0", + "shasum": "" + }, + "require": { + "php": "^8.1", + "twig/twig": "^3.19" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.28", + "php-parallel-lint/php-parallel-lint": "^1.3", + "php-stubs/acf-pro-stubs": "^6.0", + "php-stubs/wp-cli-stubs": "^2.0", + "phpro/grumphp": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.7", + "phpunit/phpunit": "^9.0", + "rector/rector": "^1.0", + "squizlabs/php_codesniffer": "^3.0", + "symplify/easy-coding-standard": "^12.2", + "szepeviktor/phpstan-wordpress": "^1.1", + "twig/cache-extra": "^3.17", + "wpackagist-plugin/advanced-custom-fields": "^6.0", + "wpackagist-plugin/co-authors-plus": "^3.6", + "yoast/wp-test-utils": "^1.2" + }, + "suggest": { + "php-coveralls/php-coveralls": "^2.0 for code coverage", + "twig/cache-extra": "For using the cache tag in Twig" + }, + "time": "2025-05-13T13:41:56+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-4": { + "Timber\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Erik van der Bas", + "email": "erik@basedonline.nl", + "homepage": "https://basedonline.nl" + }, + { + "name": "Lukas Gächter", + "email": "lukas.gaechter@mind.ch", + "homepage": "https://www.mind.ch" + }, + { + "name": "Nicolas Lemoine", + "email": "nico@n5s.dev", + "homepage": "https://n5s.dev" + }, + { + "name": "Jared Novack", + "email": "jared@upstatement.com", + "homepage": "https://upstatement.com" + }, + { + "name": "Timber Community", + "homepage": "https://github.com/timber/timber" + } + ], + "description": "Create WordPress themes with beautiful OOP code and the Twig Template Engine", + "homepage": "https://timber.upstatement.com", + "keywords": [ + "templating", + "themes", + "timber", + "twig", + "wordpress" + ], + "support": { + "docs": "https://timber.github.io/docs/", + "issues": "https://github.com/timber/timber/issues", + "source": "https://github.com/timber/timber" + }, + "funding": [ + { + "url": "https://github.com/timber", + "type": "github" + }, + { + "url": "https://opencollective.com/timber", + "type": "open_collective" + } + ], + "install-path": "../timber/timber" + }, + { + "name": "twig/twig", + "version": "v3.21.1", + "version_normalized": "3.21.1.0", + "source": { + "type": "git", + "url": "https://github.com/twigphp/Twig.git", + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/twigphp/Twig/zipball/285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "reference": "285123877d4dd97dd7c11842ac5fb7e86e60d81d", + "shasum": "" + }, + "require": { + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-mbstring": "^1.3" + }, + "require-dev": { + "phpstan/phpstan": "^2.0", + "psr/container": "^1.0|^2.0", + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0" + }, + "time": "2025-05-03T07:21:55+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], + "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.21.1" + }, + "funding": [ + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/twig/twig", + "type": "tidelift" + } + ], + "install-path": "../twig/twig" + }, + { + "name": "upstatement/routes", + "version": "0.9.2", + "version_normalized": "0.9.2.0", + "source": { + "type": "git", + "url": "https://github.com/Upstatement/routes.git", + "reference": "196a4dbc06231e5f6eb760130b8aa42d47e3c22f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Upstatement/routes/zipball/196a4dbc06231e5f6eb760130b8aa42d47e3c22f", + "reference": "196a4dbc06231e5f6eb760130b8aa42d47e3c22f", + "shasum": "" + }, + "require": { + "altorouter/altorouter": "^2.0.2", + "composer/installers": "^1.0 || ^2.0", + "php": ">=7.3" + }, + "require-dev": { + "phpunit/phpunit": "5.7.16", + "satooshi/php-coveralls": "*", + "wp-cli/wp-cli": "*" + }, + "time": "2025-02-25T18:07:09+00:00", + "type": "library", + "installation-source": "dist", + "autoload": { + "psr-0": { + "Routes": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jared Novack", + "email": "jared@upstatement.com", + "homepage": "https://www.upstatement.com" + } + ], + "description": "Manage rewrites and routes in WordPress with this dead-simple plugin", + "homepage": "https://www.upstatement.com", + "keywords": [ + "redirects", + "rewrite", + "routes", + "routing" + ], + "support": { + "issues": "https://github.com/Upstatement/routes/issues", + "source": "https://github.com/Upstatement/routes", + "wiki": "https://github.com/Upstatement/routes/wiki" + }, + "install-path": "../upstatement/routes" + } ], - "description": "Allow dump() in twig templates", - "homepage": "https://github.com/agencearcange/arcange-wpdump", - "keywords": [ - "plugin", - "timber", - "twig", - "wordpress" - ] - }, - { - "name": "gregwar/cache", - "version": "v1.0.12", - "version_normalized": "1.0.12.0", - "target-dir": "Gregwar/Cache", - "source": { - "type": "git", - "url": "https://github.com/Gregwar/Cache.git", - "reference": "305d0f5a12c0beecbbd7e1de236f59f39e0c0ac3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Gregwar/Cache/zipball/305d0f5a12c0beecbbd7e1de236f59f39e0c0ac3", - "reference": "305d0f5a12c0beecbbd7e1de236f59f39e0c0ac3", - "shasum": "" - }, - "require": { - "php": ">=5.3" - }, - "time": "2016-09-23T08:16:04+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Gregwar\\Cache": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gregwar", - "email": "g.passault@gmail.com" - } - ], - "description": "A lightweight file-system cache system", - "keywords": [ - "cache", - "caching", - "file-system", - "system" - ] - }, - { - "name": "gregwar/formidable", - "version": "2.1.0", - "version_normalized": "2.1.0.0", - "target-dir": "Gregwar/Formidable", - "source": { - "type": "git", - "url": "https://github.com/Gregwar/Formidable.git", - "reference": "b1a63d96b5f52b1e8d5b094ee5b861f880469998" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Gregwar/Formidable/zipball/b1a63d96b5f52b1e8d5b094ee5b861f880469998", - "reference": "b1a63d96b5f52b1e8d5b094ee5b861f880469998", - "shasum": "" - }, - "require": { - "gregwar/cache": "~1.0.9", - "php": ">=5.6", - "symfony/property-access": "~2.8.0" - }, - "require-dev": { - "gregwar/captcha": "~1.1.6", - "phpunit/phpunit": "~4.8.9" - }, - "suggest": { - "gregwar/captcha": "To implement Captcha capability into forms" - }, - "time": "2018-05-17T19:22:38+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Gregwar\\Formidable": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Grégoire Passault", - "email": "g.passault@gmail.com", - "homepage": "http://www.gregwar.com/" - }, - { - "name": "Ashley Kitson aka chippyash", - "email": "info@zf4.biz", - "homepage": "http://zf4.biz" - } - ], - "description": "Formidable, the pragmatic forms library", - "homepage": "https://gregwar.com/Formidable/", - "keywords": [ - "Forms", - "fields", - "html", - "input" - ] - }, - { - "name": "hellonico/twig-dump-extension", - "version": "1.1.0", - "version_normalized": "1.1.0.0", - "source": { - "type": "git", - "url": "https://github.com/nlemoine/twig-dump-extension.git", - "reference": "a162573838ce18e7948b92a6eac74ff9db610639" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nlemoine/twig-dump-extension/zipball/a162573838ce18e7948b92a6eac74ff9db610639", - "reference": "a162573838ce18e7948b92a6eac74ff9db610639", - "shasum": "" - }, - "require": { - "symfony/var-dumper": "^4.2 || ^5.0", - "twig/twig": "^2.4 || ^3.0" - }, - "time": "2020-03-06T15:18:29+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-4": { - "HelloNico\\Twig\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "GPL-3.0+" - ], - "authors": [ - { - "name": "Nicolas Lemoine", - "email": "dev@helloni.co", - "homepage": "https://github.com/nlemoine" - } - ], - "description": "VarDumper Twig extension", - "homepage": "https://github.com/nlemoine/twig-dump-extension" - }, - { - "name": "rmccue/requests", - "version": "v1.7.0", - "version_normalized": "1.7.0.0", - "source": { - "type": "git", - "url": "https://github.com/rmccue/Requests.git", - "reference": "87932f52ffad70504d93f04f15690cf16a089546" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/rmccue/Requests/zipball/87932f52ffad70504d93f04f15690cf16a089546", - "reference": "87932f52ffad70504d93f04f15690cf16a089546", - "shasum": "" - }, - "require": { - "php": ">=5.2" - }, - "require-dev": { - "requests/test-server": "dev-master" - }, - "time": "2016-10-13T00:11:37+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "psr-0": { - "Requests": "library/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "ISC" - ], - "authors": [ - { - "name": "Ryan McCue", - "homepage": "http://ryanmccue.info" - } - ], - "description": "A HTTP library written in PHP, for human beings.", - "homepage": "http://github.com/rmccue/Requests", - "keywords": [ - "curl", - "fsockopen", - "http", - "idna", - "ipv6", - "iri", - "sockets" - ] - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.15.0", - "version_normalized": "1.15.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "reference": "4719fa9c18b0464d399f1a63bf624b42b6fa8d14", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "time": "2020-02-27T09:26:54+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - }, - "files": [ - "bootstrap.php" - ] - }, - "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" - ], - "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" - } - ] - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.15.0", - "version_normalized": "1.15.0.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "reference": "81ffd3a9c6d707be22e3012b827de1c9775fc5ac", - "shasum": "" - }, - "require": { - "php": ">=5.3.3" - }, - "suggest": { - "ext-mbstring": "For best performance" - }, - "time": "2020-03-09T19:04:49+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.15-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - }, - "files": [ - "bootstrap.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": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "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" - } - ] - }, - { - "name": "symfony/property-access", - "version": "v2.8.52", - "version_normalized": "2.8.52.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/property-access.git", - "reference": "c8f10191183be9bb0d5a1b8364d3891f1bde07b6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/property-access/zipball/c8f10191183be9bb0d5a1b8364d3891f1bde07b6", - "reference": "c8f10191183be9bb0d5a1b8364d3891f1bde07b6", - "shasum": "" - }, - "require": { - "php": ">=5.3.9", - "symfony/polyfill-ctype": "~1.8" - }, - "time": "2018-11-11T11:18:13+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.8-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-4": { - "Symfony\\Component\\PropertyAccess\\": "" - }, - "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": "Symfony PropertyAccess Component", - "homepage": "https://symfony.com", - "keywords": [ - "access", - "array", - "extraction", - "index", - "injection", - "object", - "property", - "property path", - "reflection" - ] - }, - { - "name": "symfony/var-dumper", - "version": "v5.0.7", - "version_normalized": "5.0.7.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "f74a126acd701392eef2492a17228d42552c86b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/f74a126acd701392eef2492a17228d42552c86b5", - "reference": "f74a126acd701392eef2492a17228d42552c86b5", - "shasum": "" - }, - "require": { - "php": "^7.2.5", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "twig/twig": "^2.4|^3.0" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, - "time": "2020-03-27T16:56:45+00:00", - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, - "installation-source": "dist", - "autoload": { - "files": [ - "Resources/functions/dump.php" - ], - "psr-4": { - "Symfony\\Component\\VarDumper\\": "" - }, - "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": "Symfony mechanism for exploring and dumping PHP variables", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "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" - } - ] - }, - { - "name": "tightenco/collect", - "version": "v7.5.0", - "version_normalized": "7.5.0.0", - "source": { - "type": "git", - "url": "https://github.com/tightenco/collect.git", - "reference": "5024db45b1ec1956e6d5959cbb5cdf5f00f26f56" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/tightenco/collect/zipball/5024db45b1ec1956e6d5959cbb5cdf5f00f26f56", - "reference": "5024db45b1ec1956e6d5959cbb5cdf5f00f26f56", - "shasum": "" - }, - "require": { - "php": "^7.1.3", - "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "nesbot/carbon": "^2.23.0", - "phpunit/phpunit": "^7.0" - }, - "time": "2020-03-31T17:56:15+00:00", - "type": "library", - "installation-source": "dist", - "autoload": { - "files": [ - "src/Collect/Support/helpers.php", - "src/Collect/Support/alias.php" - ], - "psr-4": { - "Tightenco\\Collect\\": "src/Collect" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "description": "Collect - Illuminate Collections as a separate package.", - "keywords": [ - "collection", - "laravel" - ] - }, - { - "name": "twig/twig", - "version": "v2.12.5", - "version_normalized": "2.12.5.0", - "source": { - "type": "git", - "url": "https://github.com/twigphp/Twig.git", - "reference": "18772e0190734944277ee97a02a9a6c6555fcd94" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/twigphp/Twig/zipball/18772e0190734944277ee97a02a9a6c6555fcd94", - "reference": "18772e0190734944277ee97a02a9a6c6555fcd94", - "shasum": "" - }, - "require": { - "php": "^7.0", - "symfony/polyfill-ctype": "^1.8", - "symfony/polyfill-mbstring": "^1.3" - }, - "require-dev": { - "psr/container": "^1.0", - "symfony/phpunit-bridge": "^4.4|^5.0" - }, - "time": "2020-02-11T15:31:23+00:00", - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.12-dev" - } - }, - "installation-source": "dist", - "autoload": { - "psr-0": { - "Twig_": "lib/" - }, - "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" - ] - } -] + "dev": true, + "dev-package-names": [] +} diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php new file mode 100644 index 0000000..86119cb --- /dev/null +++ b/vendor/composer/installed.php @@ -0,0 +1,185 @@ + array( + 'name' => '__root__', + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'f4f413ec23c013281b14540789f7c4d74aff121d', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev' => true, + ), + 'versions' => array( + '__root__' => array( + 'pretty_version' => 'dev-main', + 'version' => 'dev-main', + 'reference' => 'f4f413ec23c013281b14540789f7c4d74aff121d', + 'type' => 'library', + 'install_path' => __DIR__ . '/../../', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'altorouter/altorouter' => array( + 'pretty_version' => '2.0.3', + 'version' => '2.0.3.0', + 'reference' => '9931b976423f7334c94f7b5b348be8ab1da3415d', + 'type' => 'library', + 'install_path' => __DIR__ . '/../altorouter/altorouter', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'composer/installers' => array( + 'pretty_version' => 'v2.3.0', + 'version' => '2.3.0.0', + 'reference' => '12fb2dfe5e16183de69e784a7b84046c43d97e8e', + 'type' => 'composer-plugin', + 'install_path' => __DIR__ . '/./installers', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'gregwar/cache' => array( + 'pretty_version' => 'v1.0.12', + 'version' => '1.0.12.0', + 'reference' => '305d0f5a12c0beecbbd7e1de236f59f39e0c0ac3', + 'type' => 'library', + 'install_path' => __DIR__ . '/../gregwar/cache/Gregwar/Cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'gregwar/formidable' => array( + 'pretty_version' => '2.1.0', + 'version' => '2.1.0.0', + 'reference' => 'b1a63d96b5f52b1e8d5b094ee5b861f880469998', + 'type' => 'library', + 'install_path' => __DIR__ . '/../gregwar/formidable/Gregwar/Formidable', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'illuminate/collections' => array( + 'pretty_version' => 'v12.21.0', + 'version' => '12.21.0.0', + 'reference' => 'a048b4fbbef4742ff2eee843971bb8278239c610', + 'type' => 'library', + 'install_path' => __DIR__ . '/../illuminate/collections', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'illuminate/conditionable' => array( + 'pretty_version' => 'v12.21.0', + 'version' => '12.21.0.0', + 'reference' => 'ec677967c1f2faf90b8428919124d2184a4c9b49', + 'type' => 'library', + 'install_path' => __DIR__ . '/../illuminate/conditionable', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'illuminate/contracts' => array( + 'pretty_version' => 'v12.21.0', + 'version' => '12.21.0.0', + 'reference' => 'c2eef857b808810f5cb187de58e23d25c1d443d9', + 'type' => 'library', + 'install_path' => __DIR__ . '/../illuminate/contracts', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'illuminate/macroable' => array( + 'pretty_version' => 'v12.21.0', + 'version' => '12.21.0.0', + 'reference' => 'e862e5648ee34004fa56046b746f490dfa86c613', + 'type' => 'library', + 'install_path' => __DIR__ . '/../illuminate/macroable', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/container' => array( + 'pretty_version' => '2.0.2', + 'version' => '2.0.2.0', + 'reference' => 'c71ecc56dfe541dbd90c5360474fbc405f8d5963', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/container', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'psr/simple-cache' => array( + 'pretty_version' => '3.0.0', + 'version' => '3.0.0.0', + 'reference' => '764e0b3939f5ca87cb904f570ef9be2d78a07865', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/simple-cache', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'rmccue/requests' => array( + 'pretty_version' => 'v1.7.0', + 'version' => '1.7.0.0', + 'reference' => '87932f52ffad70504d93f04f15690cf16a089546', + 'type' => 'library', + 'install_path' => __DIR__ . '/../rmccue/requests', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/deprecation-contracts' => array( + 'pretty_version' => 'v3.6.0', + 'version' => '3.6.0.0', + 'reference' => '63afe740e99a13ba87ec199bb07bbdee937a5b62', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/deprecation-contracts', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/polyfill-ctype' => array( + 'pretty_version' => 'v1.15.0', + 'version' => '1.15.0.0', + 'reference' => '4719fa9c18b0464d399f1a63bf624b42b6fa8d14', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-ctype', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/polyfill-mbstring' => array( + 'pretty_version' => 'v1.32.0', + 'version' => '1.32.0.0', + 'reference' => '6d857f4d76bd4b343eac26d6b539585d2bc56493', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/polyfill-mbstring', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'symfony/property-access' => array( + 'pretty_version' => 'v2.8.52', + 'version' => '2.8.52.0', + 'reference' => 'c8f10191183be9bb0d5a1b8364d3891f1bde07b6', + 'type' => 'library', + 'install_path' => __DIR__ . '/../symfony/property-access', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'timber/timber' => array( + 'pretty_version' => 'v2.3.2', + 'version' => '2.3.2.0', + 'reference' => '08ce601167a03ed7a4fa7da740481c17ed650ef0', + 'type' => 'library', + 'install_path' => __DIR__ . '/../timber/timber', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'twig/twig' => array( + 'pretty_version' => 'v3.21.1', + 'version' => '3.21.1.0', + 'reference' => '285123877d4dd97dd7c11842ac5fb7e86e60d81d', + 'type' => 'library', + 'install_path' => __DIR__ . '/../twig/twig', + 'aliases' => array(), + 'dev_requirement' => false, + ), + 'upstatement/routes' => array( + 'pretty_version' => '0.9.2', + 'version' => '0.9.2.0', + 'reference' => '196a4dbc06231e5f6eb760130b8aa42d47e3c22f', + 'type' => 'library', + 'install_path' => __DIR__ . '/../upstatement/routes', + 'aliases' => array(), + 'dev_requirement' => false, + ), + ), +); diff --git a/vendor/composer/installers/.github/workflows/continuous-integration.yml b/vendor/composer/installers/.github/workflows/continuous-integration.yml new file mode 100644 index 0000000..2ecae13 --- /dev/null +++ b/vendor/composer/installers/.github/workflows/continuous-integration.yml @@ -0,0 +1,65 @@ +name: "Continuous Integration" + +on: + - push + - pull_request + +env: + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist" + SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT: "1" + +permissions: + contents: read + +jobs: + tests: + name: "CI" + + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: + - "7.2" + - "7.3" + - "7.4" + - "8.0" + - "8.1" + dependencies: [locked] + include: + - php-version: "7.2" + dependencies: lowest + - php-version: "8.1" + dependencies: lowest + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + tools: composer:snapshot + + - name: Get composer cache directory + id: composercache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: "Handle lowest dependencies update" + if: "contains(matrix.dependencies, 'lowest')" + run: "echo \"COMPOSER_FLAGS=$COMPOSER_FLAGS --prefer-lowest\" >> $GITHUB_ENV" + + - name: "Install latest dependencies" + run: "composer update ${{ env.COMPOSER_FLAGS }}" + + - name: "Run tests" + run: "vendor/bin/simple-phpunit --verbose" diff --git a/vendor/composer/installers/.github/workflows/lint.yml b/vendor/composer/installers/.github/workflows/lint.yml new file mode 100644 index 0000000..61b5633 --- /dev/null +++ b/vendor/composer/installers/.github/workflows/lint.yml @@ -0,0 +1,33 @@ +name: "PHP Lint" + +on: + - push + - pull_request + +permissions: + contents: read + +jobs: + tests: + name: "Lint" + + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: + - "7.2" + - "latest" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + + - name: "Lint PHP files" + run: "find src/ -type f -name '*.php' -print0 | xargs -0 -L1 -P4 -- php -l -f" diff --git a/vendor/composer/installers/.github/workflows/phpstan.yml b/vendor/composer/installers/.github/workflows/phpstan.yml new file mode 100644 index 0000000..c638b44 --- /dev/null +++ b/vendor/composer/installers/.github/workflows/phpstan.yml @@ -0,0 +1,52 @@ +name: "PHPStan" + +on: + - push + - pull_request + +env: + COMPOSER_FLAGS: "--ansi --no-interaction --no-progress --prefer-dist" + SYMFONY_PHPUNIT_VERSION: "" + +permissions: + contents: read + +jobs: + tests: + name: "PHPStan" + + runs-on: ubuntu-latest + + strategy: + matrix: + php-version: + - "8.0" + + steps: + - name: "Checkout" + uses: "actions/checkout@v2" + + - name: "Install PHP" + uses: "shivammathur/setup-php@v2" + with: + coverage: "none" + php-version: "${{ matrix.php-version }}" + + - name: Get composer cache directory + id: composercache + run: echo "::set-output name=dir::$(composer config cache-files-dir)" + + - name: Cache dependencies + uses: actions/cache@v2 + with: + path: ${{ steps.composercache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ hashFiles('**/composer.json') }} + restore-keys: ${{ runner.os }}-composer- + + - name: "Install latest dependencies" + run: "composer update ${{ env.COMPOSER_FLAGS }}" + + - name: Run PHPStan + run: | + composer require --dev phpunit/phpunit:^8.5.18 --with-all-dependencies ${{ env.COMPOSER_FLAGS }} + vendor/bin/phpstan analyse diff --git a/vendor/composer/installers/LICENSE b/vendor/composer/installers/LICENSE new file mode 100644 index 0000000..85f97fc --- /dev/null +++ b/vendor/composer/installers/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 Kyle Robinson Young + +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. \ No newline at end of file diff --git a/vendor/composer/installers/composer.json b/vendor/composer/installers/composer.json new file mode 100644 index 0000000..9103484 --- /dev/null +++ b/vendor/composer/installers/composer.json @@ -0,0 +1,117 @@ +{ + "name": "composer/installers", + "type": "composer-plugin", + "license": "MIT", + "description": "A multi-framework Composer library installer", + "keywords": [ + "installer", + "AGL", + "AnnotateCms", + "Attogram", + "Bitrix", + "CakePHP", + "Chef", + "Cockpit", + "CodeIgniter", + "concrete5", + "ConcreteCMS", + "Croogo", + "DokuWiki", + "Dolibarr", + "Drupal", + "Elgg", + "Eliasis", + "ExpressionEngine", + "eZ Platform", + "FuelPHP", + "Grav", + "Hurad", + "ImageCMS", + "iTop", + "Kanboard", + "Known", + "Kohana", + "Lan Management System", + "Laravel", + "Lavalite", + "Lithium", + "Magento", + "majima", + "Mako", + "MantisBT", + "Matomo", + "Mautic", + "Maya", + "MODX", + "MODX Evo", + "MediaWiki", + "Miaoxing", + "OXID", + "osclass", + "MODULEWork", + "Moodle", + "Pantheon", + "Piwik", + "pxcms", + "phpBB", + "Plentymarkets", + "PPI", + "Puppet", + "Porto", + "ProcessWire", + "RadPHP", + "ReIndex", + "Roundcube", + "shopware", + "SilverStripe", + "SMF", + "Starbug", + "SyDES", + "Sylius", + "TastyIgniter", + "Thelia", + "WHMCS", + "WolfCMS", + "WordPress", + "YAWIK", + "Zend", + "Zikula" + ], + "homepage": "https://composer.github.io/installers/", + "authors": [ + { + "name": "Kyle Robinson Young", + "email": "kyle@dontkry.com", + "homepage": "https://github.com/shama" + } + ], + "autoload": { + "psr-4": { "Composer\\Installers\\": "src/Composer/Installers" } + }, + "autoload-dev": { + "psr-4": { "Composer\\Installers\\Test\\": "tests/Composer/Installers/Test" } + }, + "extra": { + "class": "Composer\\Installers\\Plugin", + "branch-alias": { + "dev-main": "2.x-dev" + }, + "plugin-modifies-install-path": true + }, + "require": { + "php": "^7.2 || ^8.0", + "composer-plugin-api": "^1.0 || ^2.0" + }, + "require-dev": { + "composer/composer": "^1.10.27 || ^2.7", + "composer/semver": "^1.7.2 || ^3.4.0", + "symfony/phpunit-bridge": "^7.1.1", + "phpstan/phpstan": "^1.11", + "symfony/process": "^5 || ^6 || ^7", + "phpstan/phpstan-phpunit": "^1" + }, + "scripts": { + "test": "@php vendor/bin/simple-phpunit", + "phpstan": "@php vendor/bin/phpstan analyse" + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/AglInstaller.php b/vendor/composer/installers/src/Composer/Installers/AglInstaller.php new file mode 100644 index 0000000..b0996a6 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/AglInstaller.php @@ -0,0 +1,29 @@ + */ + protected $locations = array( + 'module' => 'More/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $name = preg_replace_callback('/(?:^|_|-)(.?)/', function ($matches) { + return strtoupper($matches[1]); + }, $vars['name']); + + if (null === $name) { + throw new \RuntimeException('Failed to run preg_replace_callback: '.preg_last_error()); + } + + $vars['name'] = $name; + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/AkauntingInstaller.php b/vendor/composer/installers/src/Composer/Installers/AkauntingInstaller.php new file mode 100644 index 0000000..c504c70 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/AkauntingInstaller.php @@ -0,0 +1,23 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php b/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php new file mode 100644 index 0000000..58a0f66 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/AnnotateCmsInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'module' => 'addons/modules/{$name}/', + 'component' => 'addons/components/{$name}/', + 'service' => 'addons/services/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php b/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php new file mode 100644 index 0000000..f01b399 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/AsgardInstaller.php @@ -0,0 +1,58 @@ + */ + protected $locations = array( + 'module' => 'Modules/{$name}/', + 'theme' => 'Themes/{$name}/' + ); + + /** + * Format package name. + * + * For package type asgard-module, cut off a trailing '-plugin' if present. + * + * For package type asgard-theme, cut off a trailing '-theme' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'asgard-module') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'asgard-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectPluginVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-module$/', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectThemeVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-theme$/', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/AttogramInstaller.php b/vendor/composer/installers/src/Composer/Installers/AttogramInstaller.php new file mode 100644 index 0000000..bd7dd8d --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/AttogramInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php b/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php new file mode 100644 index 0000000..663ec2a --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/BaseInstaller.php @@ -0,0 +1,137 @@ + */ + protected $locations = array(); + /** @var Composer */ + protected $composer; + /** @var PackageInterface */ + protected $package; + /** @var IOInterface */ + protected $io; + + /** + * Initializes base installer. + */ + public function __construct(PackageInterface $package, Composer $composer, IOInterface $io) + { + $this->composer = $composer; + $this->package = $package; + $this->io = $io; + } + + /** + * Return the install path based on package type. + */ + public function getInstallPath(PackageInterface $package, string $frameworkType = ''): string + { + $type = $this->package->getType(); + + $prettyName = $this->package->getPrettyName(); + if (strpos($prettyName, '/') !== false) { + list($vendor, $name) = explode('/', $prettyName); + } else { + $vendor = ''; + $name = $prettyName; + } + + $availableVars = $this->inflectPackageVars(compact('name', 'vendor', 'type')); + + $extra = $package->getExtra(); + if (!empty($extra['installer-name'])) { + $availableVars['name'] = $extra['installer-name']; + } + + $extra = $this->composer->getPackage()->getExtra(); + if (!empty($extra['installer-paths'])) { + $customPath = $this->mapCustomInstallPaths($extra['installer-paths'], $prettyName, $type, $vendor); + if ($customPath !== false) { + return $this->templatePath($customPath, $availableVars); + } + } + + $packageType = substr($type, strlen($frameworkType) + 1); + $locations = $this->getLocations($frameworkType); + if (!isset($locations[$packageType])) { + throw new \InvalidArgumentException(sprintf('Package type "%s" is not supported', $type)); + } + + return $this->templatePath($locations[$packageType], $availableVars); + } + + /** + * For an installer to override to modify the vars per installer. + * + * @param array $vars This will normally receive array{name: string, vendor: string, type: string} + * @return array + */ + public function inflectPackageVars(array $vars): array + { + return $vars; + } + + /** + * Gets the installer's locations + * + * @return array map of package types => install path + */ + public function getLocations(string $frameworkType) + { + return $this->locations; + } + + /** + * Replace vars in a path + * + * @param array $vars + */ + protected function templatePath(string $path, array $vars = array()): string + { + if (strpos($path, '{') !== false) { + extract($vars); + preg_match_all('@\{\$([A-Za-z0-9_]*)\}@i', $path, $matches); + if (!empty($matches[1])) { + foreach ($matches[1] as $var) { + $path = str_replace('{$' . $var . '}', $$var, $path); + } + } + } + + return $path; + } + + /** + * Search through a passed paths array for a custom install path. + * + * @param array $paths + * @return string|false + */ + protected function mapCustomInstallPaths(array $paths, string $name, string $type, ?string $vendor = null) + { + foreach ($paths as $path => $names) { + $names = (array) $names; + if (in_array($name, $names) || in_array('type:' . $type, $names) || in_array('vendor:' . $vendor, $names)) { + return $path; + } + } + + return false; + } + + protected function pregReplace(string $pattern, string $replacement, string $subject): string + { + $result = preg_replace($pattern, $replacement, $subject); + if (null === $result) { + throw new \RuntimeException('Failed to run preg_replace with '.$pattern.': '.preg_last_error()); + } + + return $result; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php b/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php new file mode 100644 index 0000000..705ecb4 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/BitrixInstaller.php @@ -0,0 +1,123 @@ +.`. + * - `bitrix-d7-component` — copy the component to directory `bitrix/components//`. + * - `bitrix-d7-template` — copy the template to directory `bitrix/templates/_`. + * + * You can set custom path to directory with Bitrix kernel in `composer.json`: + * + * ```json + * { + * "extra": { + * "bitrix-dir": "s1/bitrix" + * } + * } + * ``` + * + * @author Nik Samokhvalov + * @author Denis Kulichkin + */ +class BitrixInstaller extends BaseInstaller +{ + /** @var array */ + protected $locations = array( + 'module' => '{$bitrix_dir}/modules/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken) + 'component' => '{$bitrix_dir}/components/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken) + 'theme' => '{$bitrix_dir}/templates/{$name}/', // deprecated, remove on the major release (Backward compatibility will be broken) + 'd7-module' => '{$bitrix_dir}/modules/{$vendor}.{$name}/', + 'd7-component' => '{$bitrix_dir}/components/{$vendor}/{$name}/', + 'd7-template' => '{$bitrix_dir}/templates/{$vendor}_{$name}/', + ); + + /** + * @var string[] Storage for informations about duplicates at all the time of installation packages. + */ + private static $checkedDuplicates = array(); + + public function inflectPackageVars(array $vars): array + { + /** @phpstan-ignore-next-line */ + if ($this->composer->getPackage()) { + $extra = $this->composer->getPackage()->getExtra(); + + if (isset($extra['bitrix-dir'])) { + $vars['bitrix_dir'] = $extra['bitrix-dir']; + } + } + + if (!isset($vars['bitrix_dir'])) { + $vars['bitrix_dir'] = 'bitrix'; + } + + return parent::inflectPackageVars($vars); + } + + /** + * {@inheritdoc} + */ + protected function templatePath(string $path, array $vars = array()): string + { + $templatePath = parent::templatePath($path, $vars); + $this->checkDuplicates($templatePath, $vars); + + return $templatePath; + } + + /** + * Duplicates search packages. + * + * @param array $vars + */ + protected function checkDuplicates(string $path, array $vars = array()): void + { + $packageType = substr($vars['type'], strlen('bitrix') + 1); + $localDir = explode('/', $vars['bitrix_dir']); + array_pop($localDir); + $localDir[] = 'local'; + $localDir = implode('/', $localDir); + + $oldPath = str_replace( + array('{$bitrix_dir}', '{$name}'), + array($localDir, $vars['name']), + $this->locations[$packageType] + ); + + if (in_array($oldPath, static::$checkedDuplicates)) { + return; + } + + if ($oldPath !== $path && file_exists($oldPath) && $this->io->isInteractive()) { + $this->io->writeError(' Duplication of packages:'); + $this->io->writeError(' Package ' . $oldPath . ' will be called instead package ' . $path . ''); + + while (true) { + switch ($this->io->ask(' Delete ' . $oldPath . ' [y,n,?]? ', '?')) { + case 'y': + $fs = new Filesystem(); + $fs->removeDirectory($oldPath); + break 2; + + case 'n': + break 2; + + case '?': + default: + $this->io->writeError(array( + ' y - delete package ' . $oldPath . ' and to continue with the installation', + ' n - don\'t delete and to continue with the installation', + )); + $this->io->writeError(' ? - print help'); + break; + } + } + } + + static::$checkedDuplicates[] = $oldPath; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/BonefishInstaller.php b/vendor/composer/installers/src/Composer/Installers/BonefishInstaller.php new file mode 100644 index 0000000..ab022d9 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/BonefishInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'package' => 'Packages/{$vendor}/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/BotbleInstaller.php b/vendor/composer/installers/src/Composer/Installers/BotbleInstaller.php new file mode 100644 index 0000000..35e1cb8 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/BotbleInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'plugin' => 'platform/plugins/{$name}/', + 'theme' => 'platform/themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php b/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php new file mode 100644 index 0000000..12b4ed4 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/CakePHPInstaller.php @@ -0,0 +1,67 @@ + */ + protected $locations = array( + 'plugin' => 'Plugin/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + if ($this->matchesCakeVersion('>=', '3.0.0')) { + return $vars; + } + + $nameParts = explode('/', $vars['name']); + foreach ($nameParts as &$value) { + $value = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $value)); + $value = str_replace(array('-', '_'), ' ', $value); + $value = str_replace(' ', '', ucwords($value)); + } + $vars['name'] = implode('/', $nameParts); + + return $vars; + } + + /** + * Change the default plugin location when cakephp >= 3.0 + */ + public function getLocations(string $frameworkType): array + { + if ($this->matchesCakeVersion('>=', '3.0.0')) { + $this->locations['plugin'] = $this->composer->getConfig()->get('vendor-dir') . '/{$vendor}/{$name}/'; + } + return $this->locations; + } + + /** + * Check if CakePHP version matches against a version + * + * @phpstan-param '='|'=='|'<'|'<='|'>'|'>='|'<>'|'!=' $matcher + */ + protected function matchesCakeVersion(string $matcher, string $version): bool + { + $repositoryManager = $this->composer->getRepositoryManager(); + /** @phpstan-ignore-next-line */ + if (!$repositoryManager) { + return false; + } + + $repos = $repositoryManager->getLocalRepository(); + /** @phpstan-ignore-next-line */ + if (!$repos) { + return false; + } + + return $repos->findPackage('cakephp/cakephp', new Constraint($matcher, $version)) !== null; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php b/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php new file mode 100644 index 0000000..b0d3c5f --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ChefInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'cookbook' => 'Chef/{$vendor}/{$name}/', + 'role' => 'Chef/roles/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/CiviCrmInstaller.php b/vendor/composer/installers/src/Composer/Installers/CiviCrmInstaller.php new file mode 100644 index 0000000..1c52e0c --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/CiviCrmInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'ext' => 'ext/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php b/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php new file mode 100644 index 0000000..2c943b2 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ClanCatsFrameworkInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'ship' => 'CCF/orbit/{$name}/', + 'theme' => 'CCF/app/themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/CockpitInstaller.php b/vendor/composer/installers/src/Composer/Installers/CockpitInstaller.php new file mode 100644 index 0000000..d3fcdf7 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/CockpitInstaller.php @@ -0,0 +1,36 @@ + */ + protected $locations = array( + 'module' => 'cockpit/modules/addons/{$name}/', + ); + + /** + * Format module name. + * + * Strip `module-` prefix from package name. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] == 'cockpit-module') { + return $this->inflectModuleVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + public function inflectModuleVars(array $vars): array + { + $vars['name'] = ucfirst($this->pregReplace('/cockpit-/i', '', $vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php b/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php new file mode 100644 index 0000000..a183e07 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/CodeIgniterInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'library' => 'application/libraries/{$name}/', + 'third-party' => 'application/third_party/{$name}/', + 'module' => 'application/modules/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php b/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php new file mode 100644 index 0000000..2f5fecb --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/Concrete5Installer.php @@ -0,0 +1,15 @@ + */ + protected $locations = array( + 'core' => 'concrete/', + 'block' => 'application/blocks/{$name}/', + 'package' => 'packages/{$name}/', + 'theme' => 'application/themes/{$name}/', + 'update' => 'updates/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ConcreteCMSInstaller.php b/vendor/composer/installers/src/Composer/Installers/ConcreteCMSInstaller.php new file mode 100644 index 0000000..b6e7f00 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ConcreteCMSInstaller.php @@ -0,0 +1,15 @@ + */ + protected $locations = array( + 'core' => 'concrete/', + 'block' => 'application/blocks/{$name}/', + 'package' => 'packages/{$name}/', + 'theme' => 'application/themes/{$name}/', + 'update' => 'updates/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php b/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php new file mode 100644 index 0000000..31d4939 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/CroogoInstaller.php @@ -0,0 +1,23 @@ + */ + protected $locations = array( + 'plugin' => 'Plugin/{$name}/', + 'theme' => 'View/Themed/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower(str_replace(array('-', '_'), ' ', $vars['name'])); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/DecibelInstaller.php b/vendor/composer/installers/src/Composer/Installers/DecibelInstaller.php new file mode 100644 index 0000000..88f53f7 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/DecibelInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'app' => 'app/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/DframeInstaller.php b/vendor/composer/installers/src/Composer/Installers/DframeInstaller.php new file mode 100644 index 0000000..196f60e --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/DframeInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'module' => 'modules/{$vendor}/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php b/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php new file mode 100644 index 0000000..aa3a2e6 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/DokuWikiInstaller.php @@ -0,0 +1,57 @@ + */ + protected $locations = array( + 'plugin' => 'lib/plugins/{$name}/', + 'template' => 'lib/tpl/{$name}/', + ); + + /** + * Format package name. + * + * For package type dokuwiki-plugin, cut off a trailing '-plugin', + * or leading dokuwiki_ if present. + * + * For package type dokuwiki-template, cut off a trailing '-template' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'dokuwiki-plugin') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'dokuwiki-template') { + return $this->inflectTemplateVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectPluginVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-plugin$/', '', $vars['name']); + $vars['name'] = $this->pregReplace('/^dokuwiki_?-?/', '', $vars['name']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectTemplateVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-template$/', '', $vars['name']); + $vars['name'] = $this->pregReplace('/^dokuwiki_?-?/', '', $vars['name']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php b/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php new file mode 100644 index 0000000..c583619 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/DolibarrInstaller.php @@ -0,0 +1,18 @@ + + */ +class DolibarrInstaller extends BaseInstaller +{ + //TODO: Add support for scripts and themes + /** @var array */ + protected $locations = array( + 'module' => 'htdocs/custom/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php b/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php new file mode 100644 index 0000000..65a3a91 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/DrupalInstaller.php @@ -0,0 +1,25 @@ + */ + protected $locations = array( + 'core' => 'core/', + 'module' => 'modules/{$name}/', + 'theme' => 'themes/{$name}/', + 'library' => 'libraries/{$name}/', + 'profile' => 'profiles/{$name}/', + 'database-driver' => 'drivers/lib/Drupal/Driver/Database/{$name}/', + 'drush' => 'drush/{$name}/', + 'custom-theme' => 'themes/custom/{$name}/', + 'custom-module' => 'modules/custom/{$name}/', + 'custom-profile' => 'profiles/custom/{$name}/', + 'drupal-multisite' => 'sites/{$name}/', + 'console' => 'console/{$name}/', + 'console-language' => 'console/language/{$name}/', + 'config' => 'config/sync/', + 'recipe' => 'recipes/{$name}', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php b/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php new file mode 100644 index 0000000..48ef2ec --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ElggInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'plugin' => 'mod/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/EliasisInstaller.php b/vendor/composer/installers/src/Composer/Installers/EliasisInstaller.php new file mode 100644 index 0000000..d7dd9a9 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/EliasisInstaller.php @@ -0,0 +1,14 @@ + */ + protected $locations = array( + 'component' => 'components/{$name}/', + 'module' => 'modules/{$name}/', + 'plugin' => 'plugins/{$name}/', + 'template' => 'templates/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ExpressionEngineInstaller.php b/vendor/composer/installers/src/Composer/Installers/ExpressionEngineInstaller.php new file mode 100644 index 0000000..fe1d468 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ExpressionEngineInstaller.php @@ -0,0 +1,31 @@ + */ + private $ee2Locations = array( + 'addon' => 'system/expressionengine/third_party/{$name}/', + 'theme' => 'themes/third_party/{$name}/', + ); + + /** @var array */ + private $ee3Locations = array( + 'addon' => 'system/user/addons/{$name}/', + 'theme' => 'themes/user/{$name}/', + ); + + public function getLocations(string $frameworkType): array + { + if ($frameworkType === 'ee2') { + $this->locations = $this->ee2Locations; + } else { + $this->locations = $this->ee3Locations; + } + + return $this->locations; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/EzPlatformInstaller.php b/vendor/composer/installers/src/Composer/Installers/EzPlatformInstaller.php new file mode 100644 index 0000000..1f5b84e --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/EzPlatformInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'meta-assets' => 'web/assets/ezplatform/', + 'assets' => 'web/assets/ezplatform/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ForkCMSInstaller.php b/vendor/composer/installers/src/Composer/Installers/ForkCMSInstaller.php new file mode 100644 index 0000000..cf62926 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ForkCMSInstaller.php @@ -0,0 +1,58 @@ + */ + protected $locations = [ + 'module' => 'src/Modules/{$name}/', + 'theme' => 'src/Themes/{$name}/' + ]; + + /** + * Format package name. + * + * For package type fork-cms-module, cut off a trailing '-plugin' if present. + * + * For package type fork-cms-theme, cut off a trailing '-theme' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'fork-cms-module') { + return $this->inflectModuleVars($vars); + } + + if ($vars['type'] === 'fork-cms-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectModuleVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^fork-cms-|-module|ForkCMS|ForkCms|Forkcms|forkcms|Module$/', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); // replace hyphens with spaces + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); // make module name camelcased + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectThemeVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^fork-cms-|-theme|ForkCMS|ForkCms|Forkcms|forkcms|Theme$/', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); // replace hyphens with spaces + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); // make theme name camelcased + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php b/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php new file mode 100644 index 0000000..5948572 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/FuelInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'module' => 'fuel/app/modules/{$name}/', + 'package' => 'fuel/packages/{$name}/', + 'theme' => 'fuel/app/themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php b/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php new file mode 100644 index 0000000..b4d80ed --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/FuelphpInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'component' => 'components/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/GravInstaller.php b/vendor/composer/installers/src/Composer/Installers/GravInstaller.php new file mode 100644 index 0000000..f5792e3 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/GravInstaller.php @@ -0,0 +1,29 @@ + */ + protected $locations = array( + 'plugin' => 'user/plugins/{$name}/', + 'theme' => 'user/themes/{$name}/', + ); + + /** + * Format package name + */ + public function inflectPackageVars(array $vars): array + { + $restrictedWords = implode('|', array_keys($this->locations)); + + $vars['name'] = strtolower($vars['name']); + $vars['name'] = $this->pregReplace( + '/^(?:grav-)?(?:(?:'.$restrictedWords.')-)?(.*?)(?:-(?:'.$restrictedWords.'))?$/ui', + '$1', + $vars['name'] + ); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php b/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php new file mode 100644 index 0000000..dd76c5b --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/HuradInstaller.php @@ -0,0 +1,27 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + 'theme' => 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $nameParts = explode('/', $vars['name']); + foreach ($nameParts as &$value) { + $value = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $value)); + $value = str_replace(array('-', '_'), ' ', $value); + $value = str_replace(' ', '', ucwords($value)); + } + $vars['name'] = implode('/', $nameParts); + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/ImageCMSInstaller.php b/vendor/composer/installers/src/Composer/Installers/ImageCMSInstaller.php new file mode 100644 index 0000000..4157cec --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ImageCMSInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'template' => 'templates/{$name}/', + 'module' => 'application/modules/{$name}/', + 'library' => 'application/libraries/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/Installer.php b/vendor/composer/installers/src/Composer/Installers/Installer.php new file mode 100644 index 0000000..862d8ae --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/Installer.php @@ -0,0 +1,288 @@ + + */ + private $supportedTypes = array( + 'akaunting' => 'AkauntingInstaller', + 'asgard' => 'AsgardInstaller', + 'attogram' => 'AttogramInstaller', + 'agl' => 'AglInstaller', + 'annotatecms' => 'AnnotateCmsInstaller', + 'bitrix' => 'BitrixInstaller', + 'botble' => 'BotbleInstaller', + 'bonefish' => 'BonefishInstaller', + 'cakephp' => 'CakePHPInstaller', + 'chef' => 'ChefInstaller', + 'civicrm' => 'CiviCrmInstaller', + 'ccframework' => 'ClanCatsFrameworkInstaller', + 'cockpit' => 'CockpitInstaller', + 'codeigniter' => 'CodeIgniterInstaller', + 'concrete5' => 'Concrete5Installer', + 'concretecms' => 'ConcreteCMSInstaller', + 'croogo' => 'CroogoInstaller', + 'dframe' => 'DframeInstaller', + 'dokuwiki' => 'DokuWikiInstaller', + 'dolibarr' => 'DolibarrInstaller', + 'decibel' => 'DecibelInstaller', + 'drupal' => 'DrupalInstaller', + 'elgg' => 'ElggInstaller', + 'eliasis' => 'EliasisInstaller', + 'ee3' => 'ExpressionEngineInstaller', + 'ee2' => 'ExpressionEngineInstaller', + 'ezplatform' => 'EzPlatformInstaller', + 'fork' => 'ForkCMSInstaller', + 'fuel' => 'FuelInstaller', + 'fuelphp' => 'FuelphpInstaller', + 'grav' => 'GravInstaller', + 'hurad' => 'HuradInstaller', + 'tastyigniter' => 'TastyIgniterInstaller', + 'imagecms' => 'ImageCMSInstaller', + 'itop' => 'ItopInstaller', + 'kanboard' => 'KanboardInstaller', + 'known' => 'KnownInstaller', + 'kodicms' => 'KodiCMSInstaller', + 'kohana' => 'KohanaInstaller', + 'lms' => 'LanManagementSystemInstaller', + 'laravel' => 'LaravelInstaller', + 'lavalite' => 'LavaLiteInstaller', + 'lithium' => 'LithiumInstaller', + 'magento' => 'MagentoInstaller', + 'majima' => 'MajimaInstaller', + 'mantisbt' => 'MantisBTInstaller', + 'mako' => 'MakoInstaller', + 'matomo' => 'MatomoInstaller', + 'maya' => 'MayaInstaller', + 'mautic' => 'MauticInstaller', + 'mediawiki' => 'MediaWikiInstaller', + 'miaoxing' => 'MiaoxingInstaller', + 'microweber' => 'MicroweberInstaller', + 'modulework' => 'MODULEWorkInstaller', + 'modx' => 'ModxInstaller', + 'modxevo' => 'MODXEvoInstaller', + 'moodle' => 'MoodleInstaller', + 'october' => 'OctoberInstaller', + 'ontowiki' => 'OntoWikiInstaller', + 'oxid' => 'OxidInstaller', + 'osclass' => 'OsclassInstaller', + 'pxcms' => 'PxcmsInstaller', + 'phpbb' => 'PhpBBInstaller', + 'piwik' => 'PiwikInstaller', + 'plentymarkets'=> 'PlentymarketsInstaller', + 'ppi' => 'PPIInstaller', + 'puppet' => 'PuppetInstaller', + 'radphp' => 'RadPHPInstaller', + 'phifty' => 'PhiftyInstaller', + 'porto' => 'PortoInstaller', + 'processwire' => 'ProcessWireInstaller', + 'quicksilver' => 'PantheonInstaller', + 'redaxo' => 'RedaxoInstaller', + 'redaxo5' => 'Redaxo5Installer', + 'reindex' => 'ReIndexInstaller', + 'roundcube' => 'RoundcubeInstaller', + 'shopware' => 'ShopwareInstaller', + 'sitedirect' => 'SiteDirectInstaller', + 'silverstripe' => 'SilverStripeInstaller', + 'smf' => 'SMFInstaller', + 'starbug' => 'StarbugInstaller', + 'sydes' => 'SyDESInstaller', + 'sylius' => 'SyliusInstaller', + 'tao' => 'TaoInstaller', + 'thelia' => 'TheliaInstaller', + 'tusk' => 'TuskInstaller', + 'userfrosting' => 'UserFrostingInstaller', + 'vanilla' => 'VanillaInstaller', + 'whmcs' => 'WHMCSInstaller', + 'winter' => 'WinterInstaller', + 'wolfcms' => 'WolfCMSInstaller', + 'wordpress' => 'WordPressInstaller', + 'yawik' => 'YawikInstaller', + 'zend' => 'ZendInstaller', + 'zikula' => 'ZikulaInstaller', + 'prestashop' => 'PrestashopInstaller' + ); + + /** + * Disables installers specified in main composer extra installer-disable + * list + */ + public function __construct( + IOInterface $io, + Composer $composer, + string $type = 'library', + ?Filesystem $filesystem = null, + ?BinaryInstaller $binaryInstaller = null + ) { + parent::__construct($io, $composer, $type, $filesystem, $binaryInstaller); + $this->removeDisabledInstallers(); + } + + /** + * {@inheritDoc} + */ + public function getInstallPath(PackageInterface $package) + { + $type = $package->getType(); + $frameworkType = $this->findFrameworkType($type); + + if ($frameworkType === false) { + throw new \InvalidArgumentException( + 'Sorry the package type of this package is not yet supported.' + ); + } + + $class = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType]; + /** + * @var BaseInstaller + */ + $installer = new $class($package, $this->composer, $this->getIO()); + + $path = $installer->getInstallPath($package, $frameworkType); + if (!$this->filesystem->isAbsolutePath($path)) { + $path = getcwd() . '/' . $path; + } + + return $path; + } + + public function uninstall(InstalledRepositoryInterface $repo, PackageInterface $package) + { + $installPath = $this->getPackageBasePath($package); + $io = $this->io; + $outputStatus = function () use ($io, $installPath) { + $io->write(sprintf('Deleting %s - %s', $installPath, !file_exists($installPath) ? 'deleted' : 'not deleted')); + }; + + $promise = parent::uninstall($repo, $package); + + // Composer v2 might return a promise here + if ($promise instanceof PromiseInterface) { + return $promise->then($outputStatus); + } + + // If not, execute the code right away as parent::uninstall executed synchronously (composer v1, or v2 without async) + $outputStatus(); + + return null; + } + + /** + * {@inheritDoc} + * + * @param string $packageType + */ + public function supports($packageType) + { + $frameworkType = $this->findFrameworkType($packageType); + + if ($frameworkType === false) { + return false; + } + + $locationPattern = $this->getLocationPattern($frameworkType); + + return preg_match('#' . $frameworkType . '-' . $locationPattern . '#', $packageType, $matches) === 1; + } + + /** + * Finds a supported framework type if it exists and returns it + * + * @return string|false + */ + protected function findFrameworkType(string $type) + { + krsort($this->supportedTypes); + + foreach ($this->supportedTypes as $key => $val) { + if ($key === substr($type, 0, strlen($key))) { + return substr($type, 0, strlen($key)); + } + } + + return false; + } + + /** + * Get the second part of the regular expression to check for support of a + * package type + */ + protected function getLocationPattern(string $frameworkType): string + { + $pattern = null; + if (!empty($this->supportedTypes[$frameworkType])) { + $frameworkClass = 'Composer\\Installers\\' . $this->supportedTypes[$frameworkType]; + /** @var BaseInstaller $framework */ + $framework = new $frameworkClass(new Package('dummy/pkg', '1.0.0.0', '1.0.0'), $this->composer, $this->getIO()); + $locations = array_keys($framework->getLocations($frameworkType)); + if ($locations) { + $pattern = '(' . implode('|', $locations) . ')'; + } + } + + return $pattern ?: '(\w+)'; + } + + private function getIO(): IOInterface + { + return $this->io; + } + + /** + * Look for installers set to be disabled in composer's extra config and + * remove them from the list of supported installers. + * + * Globals: + * - true, "all", and "*" - disable all installers. + * - false - enable all installers (useful with + * wikimedia/composer-merge-plugin or similar) + */ + protected function removeDisabledInstallers(): void + { + $extra = $this->composer->getPackage()->getExtra(); + + if (!isset($extra['installer-disable']) || $extra['installer-disable'] === false) { + // No installers are disabled + return; + } + + // Get installers to disable + $disable = $extra['installer-disable']; + + // Ensure $disabled is an array + if (!is_array($disable)) { + $disable = array($disable); + } + + // Check which installers should be disabled + $all = array(true, "all", "*"); + $intersect = array_intersect($all, $disable); + if (!empty($intersect)) { + // Disable all installers + $this->supportedTypes = array(); + return; + } + + // Disable specified installers + foreach ($disable as $key => $installer) { + if (is_string($installer) && key_exists($installer, $this->supportedTypes)) { + unset($this->supportedTypes[$installer]); + } + } + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/ItopInstaller.php b/vendor/composer/installers/src/Composer/Installers/ItopInstaller.php new file mode 100644 index 0000000..06af068 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ItopInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'extension' => 'extensions/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/KanboardInstaller.php b/vendor/composer/installers/src/Composer/Installers/KanboardInstaller.php new file mode 100644 index 0000000..bca954b --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/KanboardInstaller.php @@ -0,0 +1,20 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/KnownInstaller.php b/vendor/composer/installers/src/Composer/Installers/KnownInstaller.php new file mode 100644 index 0000000..61910a8 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/KnownInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'plugin' => 'IdnoPlugins/{$name}/', + 'theme' => 'Themes/{$name}/', + 'console' => 'ConsolePlugins/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/KodiCMSInstaller.php b/vendor/composer/installers/src/Composer/Installers/KodiCMSInstaller.php new file mode 100644 index 0000000..2505ac6 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/KodiCMSInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'plugin' => 'cms/plugins/{$name}/', + 'media' => 'cms/media/vendor/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php b/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php new file mode 100644 index 0000000..b6aa809 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/KohanaInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php b/vendor/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php new file mode 100644 index 0000000..7fe9d9b --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/LanManagementSystemInstaller.php @@ -0,0 +1,27 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + 'template' => 'templates/{$name}/', + 'document-template' => 'documents/templates/{$name}/', + 'userpanel-module' => 'userpanel/modules/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php b/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php new file mode 100644 index 0000000..a69dc88 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/LaravelInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'library' => 'libraries/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/LavaLiteInstaller.php b/vendor/composer/installers/src/Composer/Installers/LavaLiteInstaller.php new file mode 100644 index 0000000..e4a7c7d --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/LavaLiteInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'package' => 'packages/{$vendor}/{$name}/', + 'theme' => 'public/themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php b/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php new file mode 100644 index 0000000..b24bea2 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/LithiumInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'library' => 'libraries/{$name}/', + 'source' => 'libraries/_source/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php b/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php new file mode 100644 index 0000000..369e8b4 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MODULEWorkInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php b/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php new file mode 100644 index 0000000..062a839 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MODXEvoInstaller.php @@ -0,0 +1,18 @@ + */ + protected $locations = array( + 'snippet' => 'assets/snippets/{$name}/', + 'plugin' => 'assets/plugins/{$name}/', + 'module' => 'assets/modules/{$name}/', + 'template' => 'assets/templates/{$name}/', + 'lib' => 'assets/lib/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php b/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php new file mode 100644 index 0000000..ec07cd6 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MagentoInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'theme' => 'app/design/frontend/{$name}/', + 'skin' => 'skin/frontend/default/{$name}/', + 'library' => 'lib/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/MajimaInstaller.php b/vendor/composer/installers/src/Composer/Installers/MajimaInstaller.php new file mode 100644 index 0000000..6fc3089 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MajimaInstaller.php @@ -0,0 +1,46 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); + + /** + * Transforms the names + * + * @param array $vars + * @return array + */ + public function inflectPackageVars(array $vars): array + { + return $this->correctPluginName($vars); + } + + /** + * Change hyphenated names to camelcase + * + * @param array $vars + * @return array + */ + private function correctPluginName(array $vars): array + { + $camelCasedName = preg_replace_callback('/(-[a-z])/', function ($matches) { + return strtoupper($matches[0][1]); + }, $vars['name']); + + if (null === $camelCasedName) { + throw new \RuntimeException('Failed to run preg_replace_callback: '.preg_last_error()); + } + + $vars['name'] = ucfirst($camelCasedName); + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php b/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php new file mode 100644 index 0000000..cbe3760 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MakoInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'package' => 'app/packages/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/MantisBTInstaller.php b/vendor/composer/installers/src/Composer/Installers/MantisBTInstaller.php new file mode 100644 index 0000000..98e230f --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MantisBTInstaller.php @@ -0,0 +1,25 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/MatomoInstaller.php b/vendor/composer/installers/src/Composer/Installers/MatomoInstaller.php new file mode 100644 index 0000000..57fdb03 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MatomoInstaller.php @@ -0,0 +1,28 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/MauticInstaller.php b/vendor/composer/installers/src/Composer/Installers/MauticInstaller.php new file mode 100644 index 0000000..e48c133 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MauticInstaller.php @@ -0,0 +1,43 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + 'theme' => 'themes/{$name}/', + 'core' => 'app/', + ); + + private function getDirectoryName(): string + { + $extra = $this->package->getExtra(); + if (!empty($extra['install-directory-name'])) { + return $extra['install-directory-name']; + } + + return $this->toCamelCase($this->package->getPrettyName()); + } + + private function toCamelCase(string $packageName): string + { + return str_replace(' ', '', ucwords(str_replace('-', ' ', basename($packageName)))); + } + + /** + * Format package name of mautic-plugins to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] == 'mautic-plugin' || $vars['type'] == 'mautic-theme') { + $directoryName = $this->getDirectoryName(); + $vars['name'] = $directoryName; + } + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/MayaInstaller.php b/vendor/composer/installers/src/Composer/Installers/MayaInstaller.php new file mode 100644 index 0000000..df486da --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MayaInstaller.php @@ -0,0 +1,38 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + ); + + /** + * Format package name. + * + * For package type maya-module, cut off a trailing '-module' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'maya-module') { + return $this->inflectModuleVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectModuleVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-module$/', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php b/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php new file mode 100644 index 0000000..8e9d771 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MediaWikiInstaller.php @@ -0,0 +1,58 @@ + */ + protected $locations = array( + 'core' => 'core/', + 'extension' => 'extensions/{$name}/', + 'skin' => 'skins/{$name}/', + ); + + /** + * Format package name. + * + * For package type mediawiki-extension, cut off a trailing '-extension' if present and transform + * to CamelCase keeping existing uppercase chars. + * + * For package type mediawiki-skin, cut off a trailing '-skin' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'mediawiki-extension') { + return $this->inflectExtensionVars($vars); + } + + if ($vars['type'] === 'mediawiki-skin') { + return $this->inflectSkinVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectExtensionVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-extension$/', '', $vars['name']); + $vars['name'] = str_replace('-', ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectSkinVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-skin$/', '', $vars['name']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/MiaoxingInstaller.php b/vendor/composer/installers/src/Composer/Installers/MiaoxingInstaller.php new file mode 100644 index 0000000..0254177 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MiaoxingInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php b/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php new file mode 100644 index 0000000..a4d97ab --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MicroweberInstaller.php @@ -0,0 +1,145 @@ + */ + protected $locations = array( + 'module' => 'userfiles/modules/{$install_item_dir}/', + 'module-skin' => 'userfiles/modules/{$install_item_dir}/templates/', + 'template' => 'userfiles/templates/{$install_item_dir}/', + 'element' => 'userfiles/elements/{$install_item_dir}/', + 'vendor' => 'vendor/{$install_item_dir}/', + 'components' => 'components/{$install_item_dir}/' + ); + + /** + * Format package name. + * + * For package type microweber-module, cut off a trailing '-module' if present + * + * For package type microweber-template, cut off a trailing '-template' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($this->package->getTargetDir() !== null && $this->package->getTargetDir() !== '') { + $vars['install_item_dir'] = $this->package->getTargetDir(); + } else { + $vars['install_item_dir'] = $vars['name']; + if ($vars['type'] === 'microweber-template') { + return $this->inflectTemplateVars($vars); + } + if ($vars['type'] === 'microweber-templates') { + return $this->inflectTemplatesVars($vars); + } + if ($vars['type'] === 'microweber-core') { + return $this->inflectCoreVars($vars); + } + if ($vars['type'] === 'microweber-adapter') { + return $this->inflectCoreVars($vars); + } + if ($vars['type'] === 'microweber-module') { + return $this->inflectModuleVars($vars); + } + if ($vars['type'] === 'microweber-modules') { + return $this->inflectModulesVars($vars); + } + if ($vars['type'] === 'microweber-skin') { + return $this->inflectSkinVars($vars); + } + if ($vars['type'] === 'microweber-element' or $vars['type'] === 'microweber-elements') { + return $this->inflectElementVars($vars); + } + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectTemplateVars(array $vars): array + { + $vars['install_item_dir'] = $this->pregReplace('/-template$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/template-$/', '', $vars['install_item_dir']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectTemplatesVars(array $vars): array + { + $vars['install_item_dir'] = $this->pregReplace('/-templates$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/templates-$/', '', $vars['install_item_dir']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectCoreVars(array $vars): array + { + $vars['install_item_dir'] = $this->pregReplace('/-providers$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/-provider$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/-adapter$/', '', $vars['install_item_dir']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectModuleVars(array $vars): array + { + $vars['install_item_dir'] = $this->pregReplace('/-module$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/module-$/', '', $vars['install_item_dir']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectModulesVars(array $vars): array + { + $vars['install_item_dir'] = $this->pregReplace('/-modules$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/modules-$/', '', $vars['install_item_dir']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectSkinVars(array $vars): array + { + $vars['install_item_dir'] = $this->pregReplace('/-skin$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/skin-$/', '', $vars['install_item_dir']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectElementVars(array $vars): array + { + $vars['install_item_dir'] = $this->pregReplace('/-elements$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/elements-$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/-element$/', '', $vars['install_item_dir']); + $vars['install_item_dir'] = $this->pregReplace('/element-$/', '', $vars['install_item_dir']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/ModxInstaller.php b/vendor/composer/installers/src/Composer/Installers/ModxInstaller.php new file mode 100644 index 0000000..e2dddec --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ModxInstaller.php @@ -0,0 +1,14 @@ + */ + protected $locations = array( + 'extra' => 'core/packages/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php b/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php new file mode 100644 index 0000000..eb2b8ac --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/MoodleInstaller.php @@ -0,0 +1,73 @@ + */ + protected $locations = array( + 'mod' => 'mod/{$name}/', + 'admin_report' => 'admin/report/{$name}/', + 'atto' => 'lib/editor/atto/plugins/{$name}/', + 'tool' => 'admin/tool/{$name}/', + 'assignment' => 'mod/assignment/type/{$name}/', + 'assignsubmission' => 'mod/assign/submission/{$name}/', + 'assignfeedback' => 'mod/assign/feedback/{$name}/', + 'antivirus' => 'lib/antivirus/{$name}/', + 'auth' => 'auth/{$name}/', + 'availability' => 'availability/condition/{$name}/', + 'block' => 'blocks/{$name}/', + 'booktool' => 'mod/book/tool/{$name}/', + 'cachestore' => 'cache/stores/{$name}/', + 'cachelock' => 'cache/locks/{$name}/', + 'calendartype' => 'calendar/type/{$name}/', + 'communication' => 'communication/provider/{$name}/', + 'customfield' => 'customfield/field/{$name}/', + 'fileconverter' => 'files/converter/{$name}/', + 'format' => 'course/format/{$name}/', + 'coursereport' => 'course/report/{$name}/', + 'contenttype' => 'contentbank/contenttype/{$name}/', + 'customcertelement' => 'mod/customcert/element/{$name}/', + 'datafield' => 'mod/data/field/{$name}/', + 'dataformat' => 'dataformat/{$name}/', + 'datapreset' => 'mod/data/preset/{$name}/', + 'editor' => 'lib/editor/{$name}/', + 'enrol' => 'enrol/{$name}/', + 'filter' => 'filter/{$name}/', + 'forumreport' => 'mod/forum/report/{$name}/', + 'gradeexport' => 'grade/export/{$name}/', + 'gradeimport' => 'grade/import/{$name}/', + 'gradereport' => 'grade/report/{$name}/', + 'gradingform' => 'grade/grading/form/{$name}/', + 'h5plib' => 'h5p/h5plib/{$name}/', + 'local' => 'local/{$name}/', + 'logstore' => 'admin/tool/log/store/{$name}/', + 'ltisource' => 'mod/lti/source/{$name}/', + 'ltiservice' => 'mod/lti/service/{$name}/', + 'media' => 'media/player/{$name}/', + 'message' => 'message/output/{$name}/', + 'mlbackend' => 'lib/mlbackend/{$name}/', + 'mnetservice' => 'mnet/service/{$name}/', + 'paygw' => 'payment/gateway/{$name}/', + 'plagiarism' => 'plagiarism/{$name}/', + 'portfolio' => 'portfolio/{$name}/', + 'qbank' => 'question/bank/{$name}/', + 'qbehaviour' => 'question/behaviour/{$name}/', + 'qformat' => 'question/format/{$name}/', + 'qtype' => 'question/type/{$name}/', + 'quizaccess' => 'mod/quiz/accessrule/{$name}/', + 'quiz' => 'mod/quiz/report/{$name}/', + 'report' => 'report/{$name}/', + 'repository' => 'repository/{$name}/', + 'scormreport' => 'mod/scorm/report/{$name}/', + 'search' => 'search/engine/{$name}/', + 'theme' => 'theme/{$name}/', + 'tiny' => 'lib/editor/tiny/plugins/{$name}/', + 'tinymce' => 'lib/editor/tinymce/plugins/{$name}/', + 'profilefield' => 'user/profile/field/{$name}/', + 'webservice' => 'webservice/{$name}/', + 'workshopallocation' => 'mod/workshop/allocation/{$name}/', + 'workshopeval' => 'mod/workshop/eval/{$name}/', + 'workshopform' => 'mod/workshop/form/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php b/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php new file mode 100644 index 0000000..524f17d --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/OctoberInstaller.php @@ -0,0 +1,57 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + 'plugin' => 'plugins/{$vendor}/{$name}/', + 'theme' => 'themes/{$vendor}-{$name}/' + ); + + /** + * Format package name. + * + * For package type october-plugin, cut off a trailing '-plugin' if present. + * + * For package type october-theme, cut off a trailing '-theme' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'october-plugin') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'october-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectPluginVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^oc-|-plugin$/', '', $vars['name']); + $vars['vendor'] = $this->pregReplace('/[^a-z0-9_]/i', '', $vars['vendor']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectThemeVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^oc-|-theme$/', '', $vars['name']); + $vars['vendor'] = $this->pregReplace('/[^a-z0-9_]/i', '', $vars['vendor']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/OntoWikiInstaller.php b/vendor/composer/installers/src/Composer/Installers/OntoWikiInstaller.php new file mode 100644 index 0000000..fd20c1a --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/OntoWikiInstaller.php @@ -0,0 +1,26 @@ + */ + protected $locations = array( + 'extension' => 'extensions/{$name}/', + 'theme' => 'extensions/themes/{$name}/', + 'translation' => 'extensions/translations/{$name}/', + ); + + /** + * Format package name to lower case and remove ".ontowiki" suffix + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($vars['name']); + $vars['name'] = $this->pregReplace('/.ontowiki$/', '', $vars['name']); + $vars['name'] = $this->pregReplace('/-theme$/', '', $vars['name']); + $vars['name'] = $this->pregReplace('/-translation$/', '', $vars['name']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/OsclassInstaller.php b/vendor/composer/installers/src/Composer/Installers/OsclassInstaller.php new file mode 100644 index 0000000..e61d61f --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/OsclassInstaller.php @@ -0,0 +1,14 @@ + */ + protected $locations = array( + 'plugin' => 'oc-content/plugins/{$name}/', + 'theme' => 'oc-content/themes/{$name}/', + 'language' => 'oc-content/languages/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php b/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php new file mode 100644 index 0000000..6e1e862 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/OxidInstaller.php @@ -0,0 +1,49 @@ +.+)\/.+/'; + + /** @var array */ + protected $locations = array( + 'module' => 'modules/{$name}/', + 'theme' => 'application/views/{$name}/', + 'out' => 'out/{$name}/', + ); + + public function getInstallPath(PackageInterface $package, string $frameworkType = ''): string + { + $installPath = parent::getInstallPath($package, $frameworkType); + $type = $this->package->getType(); + if ($type === 'oxid-module') { + $this->prepareVendorDirectory($installPath); + } + return $installPath; + } + + /** + * Makes sure there is a vendormetadata.php file inside + * the vendor folder if there is a vendor folder. + */ + protected function prepareVendorDirectory(string $installPath): void + { + $matches = ''; + $hasVendorDirectory = preg_match(self::VENDOR_PATTERN, $installPath, $matches); + if (!$hasVendorDirectory) { + return; + } + + $vendorDirectory = $matches['vendor']; + $vendorPath = getcwd() . '/modules/' . $vendorDirectory; + if (!file_exists($vendorPath)) { + mkdir($vendorPath, 0755, true); + } + + $vendorMetaDataPath = $vendorPath . '/vendormetadata.php'; + touch($vendorMetaDataPath); + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php b/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php new file mode 100644 index 0000000..714c467 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PPIInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/PantheonInstaller.php b/vendor/composer/installers/src/Composer/Installers/PantheonInstaller.php new file mode 100644 index 0000000..439f61a --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PantheonInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'script' => 'web/private/scripts/quicksilver/{$name}', + 'module' => 'web/private/scripts/quicksilver/{$name}', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/PhiftyInstaller.php b/vendor/composer/installers/src/Composer/Installers/PhiftyInstaller.php new file mode 100644 index 0000000..3c970e2 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PhiftyInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'bundle' => 'bundles/{$name}/', + 'library' => 'libraries/{$name}/', + 'framework' => 'frameworks/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php b/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php new file mode 100644 index 0000000..d53ee4f --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PhpBBInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'extension' => 'ext/{$vendor}/{$name}/', + 'language' => 'language/{$name}/', + 'style' => 'styles/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php b/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php new file mode 100644 index 0000000..b2faf44 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PiwikInstaller.php @@ -0,0 +1,28 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/PlentymarketsInstaller.php b/vendor/composer/installers/src/Composer/Installers/PlentymarketsInstaller.php new file mode 100644 index 0000000..0c06359 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PlentymarketsInstaller.php @@ -0,0 +1,28 @@ + */ + protected $locations = array( + 'plugin' => '{$name}/' + ); + + /** + * Remove hyphen, "plugin" and format to camelcase + */ + public function inflectPackageVars(array $vars): array + { + $nameBits = explode("-", $vars['name']); + foreach ($nameBits as $key => $name) { + $nameBits[$key] = ucfirst($name); + if (strcasecmp($name, "Plugin") == 0) { + unset($nameBits[$key]); + } + } + $vars['name'] = implode('', $nameBits); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/Plugin.php b/vendor/composer/installers/src/Composer/Installers/Plugin.php new file mode 100644 index 0000000..437a949 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/Plugin.php @@ -0,0 +1,28 @@ +installer = new Installer($io, $composer); + $composer->getInstallationManager()->addInstaller($this->installer); + } + + public function deactivate(Composer $composer, IOInterface $io): void + { + $composer->getInstallationManager()->removeInstaller($this->installer); + } + + public function uninstall(Composer $composer, IOInterface $io): void + { + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/PortoInstaller.php b/vendor/composer/installers/src/Composer/Installers/PortoInstaller.php new file mode 100644 index 0000000..a01d7a0 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PortoInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'container' => 'app/Containers/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php b/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php new file mode 100644 index 0000000..23f156f --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PrestashopInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + 'theme' => 'themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ProcessWireInstaller.php b/vendor/composer/installers/src/Composer/Installers/ProcessWireInstaller.php new file mode 100644 index 0000000..a7eb1ee --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ProcessWireInstaller.php @@ -0,0 +1,23 @@ + */ + protected $locations = array( + 'module' => 'site/modules/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php b/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php new file mode 100644 index 0000000..1a0a8a3 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PuppetInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/PxcmsInstaller.php b/vendor/composer/installers/src/Composer/Installers/PxcmsInstaller.php new file mode 100644 index 0000000..fc58b8a --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/PxcmsInstaller.php @@ -0,0 +1,62 @@ + */ + protected $locations = array( + 'module' => 'app/Modules/{$name}/', + 'theme' => 'themes/{$name}/', + ); + + /** + * Format package name. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'pxcms-module') { + return $this->inflectModuleVars($vars); + } + + if ($vars['type'] === 'pxcms-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + /** + * For package type pxcms-module, cut off a trailing '-plugin' if present. + * + * @param array $vars + * @return array + */ + protected function inflectModuleVars(array $vars): array + { + $vars['name'] = str_replace('pxcms-', '', $vars['name']); // strip out pxcms- just incase (legacy) + $vars['name'] = str_replace('module-', '', $vars['name']); // strip out module- + $vars['name'] = $this->pregReplace('/-module$/', '', $vars['name']); // strip out -module + $vars['name'] = str_replace('-', '_', $vars['name']); // make -'s be _'s + $vars['name'] = ucwords($vars['name']); // make module name camelcased + + return $vars; + } + + /** + * For package type pxcms-module, cut off a trailing '-plugin' if present. + * + * @param array $vars + * @return array + */ + protected function inflectThemeVars(array $vars): array + { + $vars['name'] = str_replace('pxcms-', '', $vars['name']); // strip out pxcms- just incase (legacy) + $vars['name'] = str_replace('theme-', '', $vars['name']); // strip out theme- + $vars['name'] = $this->pregReplace('/-theme$/', '', $vars['name']); // strip out -theme + $vars['name'] = str_replace('-', '_', $vars['name']); // make -'s be _'s + $vars['name'] = ucwords($vars['name']); // make module name camelcased + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/RadPHPInstaller.php b/vendor/composer/installers/src/Composer/Installers/RadPHPInstaller.php new file mode 100644 index 0000000..4caae51 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/RadPHPInstaller.php @@ -0,0 +1,26 @@ + */ + protected $locations = array( + 'bundle' => 'src/{$name}/' + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $nameParts = explode('/', $vars['name']); + foreach ($nameParts as &$value) { + $value = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $value)); + $value = str_replace(array('-', '_'), ' ', $value); + $value = str_replace(' ', '', ucwords($value)); + } + $vars['name'] = implode('/', $nameParts); + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/ReIndexInstaller.php b/vendor/composer/installers/src/Composer/Installers/ReIndexInstaller.php new file mode 100644 index 0000000..a19eaaf --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ReIndexInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'theme' => 'themes/{$name}/', + 'plugin' => 'plugins/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php b/vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php new file mode 100644 index 0000000..b62c926 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/Redaxo5Installer.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'addon' => 'redaxo/src/addons/{$name}/', + 'bestyle-plugin' => 'redaxo/src/addons/be_style/plugins/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php b/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php new file mode 100644 index 0000000..26b3aa8 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/RedaxoInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'addon' => 'redaxo/include/addons/{$name}/', + 'bestyle-plugin' => 'redaxo/include/addons/be_style/plugins/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php b/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php new file mode 100644 index 0000000..7e71674 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/RoundcubeInstaller.php @@ -0,0 +1,21 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + ); + + /** + * Lowercase name and changes the name to a underscores + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower(str_replace('-', '_', $vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php b/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php new file mode 100644 index 0000000..7321046 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/SMFInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'module' => 'Sources/{$name}/', + 'theme' => 'Themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php b/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php new file mode 100644 index 0000000..82b8e28 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ShopwareInstaller.php @@ -0,0 +1,66 @@ + */ + protected $locations = array( + 'backend-plugin' => 'engine/Shopware/Plugins/Local/Backend/{$name}/', + 'core-plugin' => 'engine/Shopware/Plugins/Local/Core/{$name}/', + 'frontend-plugin' => 'engine/Shopware/Plugins/Local/Frontend/{$name}/', + 'theme' => 'templates/{$name}/', + 'plugin' => 'custom/plugins/{$name}/', + 'frontend-theme' => 'themes/Frontend/{$name}/', + ); + + /** + * Transforms the names + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'shopware-theme') { + return $this->correctThemeName($vars); + } + + return $this->correctPluginName($vars); + } + + /** + * Changes the name to a camelcased combination of vendor and name + * + * @param array $vars + * @return array + */ + private function correctPluginName(array $vars): array + { + $camelCasedName = preg_replace_callback('/(-[a-z])/', function ($matches) { + return strtoupper($matches[0][1]); + }, $vars['name']); + + if (null === $camelCasedName) { + throw new \RuntimeException('Failed to run preg_replace_callback: '.preg_last_error()); + } + + $vars['name'] = ucfirst($vars['vendor']) . ucfirst($camelCasedName); + + return $vars; + } + + /** + * Changes the name to a underscore separated name + * + * @param array $vars + * @return array + */ + private function correctThemeName(array $vars): array + { + $vars['name'] = str_replace('-', '_', $vars['name']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php b/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php new file mode 100644 index 0000000..aa2de21 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/SilverStripeInstaller.php @@ -0,0 +1,33 @@ + */ + protected $locations = array( + 'module' => '{$name}/', + 'theme' => 'themes/{$name}/', + ); + + /** + * Return the install path based on package type. + * + * Relies on built-in BaseInstaller behaviour with one exception: silverstripe/framework + * must be installed to 'sapphire' and not 'framework' if the version is <3.0.0 + */ + public function getInstallPath(PackageInterface $package, string $frameworkType = ''): string + { + if ( + $package->getName() == 'silverstripe/framework' + && preg_match('/^\d+\.\d+\.\d+/', $package->getVersion()) + && version_compare($package->getVersion(), '2.999.999') < 0 + ) { + return $this->templatePath($this->locations['module'], array('name' => 'sapphire')); + } + + return parent::getInstallPath($package, $frameworkType); + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/SiteDirectInstaller.php b/vendor/composer/installers/src/Composer/Installers/SiteDirectInstaller.php new file mode 100644 index 0000000..0af3239 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/SiteDirectInstaller.php @@ -0,0 +1,34 @@ + */ + protected $locations = array( + 'module' => 'modules/{$vendor}/{$name}/', + 'plugin' => 'plugins/{$vendor}/{$name}/' + ); + + /** + * @param array $vars + * @return array + */ + public function inflectPackageVars(array $vars): array + { + return $this->parseVars($vars); + } + + /** + * @param array $vars + * @return array + */ + protected function parseVars(array $vars): array + { + $vars['vendor'] = strtolower($vars['vendor']) == 'sitedirect' ? 'SiteDirect' : $vars['vendor']; + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/StarbugInstaller.php b/vendor/composer/installers/src/Composer/Installers/StarbugInstaller.php new file mode 100644 index 0000000..72afa08 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/StarbugInstaller.php @@ -0,0 +1,14 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + 'theme' => 'themes/{$name}/', + 'custom-module' => 'app/modules/{$name}/', + 'custom-theme' => 'app/themes/{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/SyDESInstaller.php b/vendor/composer/installers/src/Composer/Installers/SyDESInstaller.php new file mode 100644 index 0000000..24673d2 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/SyDESInstaller.php @@ -0,0 +1,55 @@ + */ + protected $locations = array( + 'module' => 'app/modules/{$name}/', + 'theme' => 'themes/{$name}/', + ); + + /** + * Format module name. + * + * Strip `sydes-` prefix and a trailing '-theme' or '-module' from package name if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] == 'sydes-module') { + return $this->inflectModuleVars($vars); + } + + if ($vars['type'] === 'sydes-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + public function inflectModuleVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/(^sydes-|-module$)/i', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectThemeVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/(^sydes-|-theme$)/', '', $vars['name']); + $vars['name'] = strtolower($vars['name']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/SyliusInstaller.php b/vendor/composer/installers/src/Composer/Installers/SyliusInstaller.php new file mode 100644 index 0000000..c82bd85 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/SyliusInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'theme' => 'themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/TaoInstaller.php b/vendor/composer/installers/src/Composer/Installers/TaoInstaller.php new file mode 100644 index 0000000..8c1d814 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/TaoInstaller.php @@ -0,0 +1,32 @@ + */ + protected $locations = array( + 'extension' => '{$name}' + ); + + public function inflectPackageVars(array $vars): array + { + $extra = $this->package->getExtra(); + + if (array_key_exists(self::EXTRA_TAO_EXTENSION_NAME, $extra)) { + $vars['name'] = $extra[self::EXTRA_TAO_EXTENSION_NAME]; + return $vars; + } + + $vars['name'] = str_replace('extension-', '', $vars['name']); + $vars['name'] = str_replace('-', ' ', $vars['name']); + $vars['name'] = lcfirst(str_replace(' ', '', ucwords($vars['name']))); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/TastyIgniterInstaller.php b/vendor/composer/installers/src/Composer/Installers/TastyIgniterInstaller.php new file mode 100644 index 0000000..39ceae0 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/TastyIgniterInstaller.php @@ -0,0 +1,85 @@ + */ + protected $locations = [ + 'module' => 'app/{$name}/', + 'extension' => 'extensions/{$vendor}/{$name}/', + 'theme' => 'themes/{$name}/', + ]; + + /** + * Format package name. + * + * Cut off leading 'ti-ext-' or 'ti-theme-' if present. + * Strip vendor name of characters that is not alphanumeric or an underscore + * + */ + public function inflectPackageVars(array $vars): array + { + $extra = $this->package->getExtra(); + + if ($vars['type'] === 'tastyigniter-module') { + return $this->inflectModuleVars($vars); + } + + if ($vars['type'] === 'tastyigniter-extension') { + return $this->inflectExtensionVars($vars, $extra); + } + + if ($vars['type'] === 'tastyigniter-theme') { + return $this->inflectThemeVars($vars, $extra); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectModuleVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^ti-module-/', '', $vars['name']); + + return $vars; + } + + /** + * @param array $vars + * @param array $extra + * @return array + */ + protected function inflectExtensionVars(array $vars, array $extra): array + { + if (!empty($extra['tastyigniter-extension']['code'])) { + $parts = explode('.', $extra['tastyigniter-extension']['code']); + $vars['vendor'] = (string)$parts[0]; + $vars['name'] = (string)($parts[1] ?? ''); + } + + $vars['vendor'] = $this->pregReplace('/[^a-z0-9_]/i', '', $vars['vendor']); + $vars['name'] = $this->pregReplace('/^ti-ext-/', '', $vars['name']); + + return $vars; + } + + /** + * @param array $vars + * @param array $extra + * @return array + */ + protected function inflectThemeVars(array $vars, array $extra): array + { + if (!empty($extra['tastyigniter-theme']['code'])) { + $vars['name'] = $extra['tastyigniter-theme']['code']; + } + + $vars['name'] = $this->pregReplace('/^ti-theme-/', '', $vars['name']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php b/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php new file mode 100644 index 0000000..896bed5 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/TheliaInstaller.php @@ -0,0 +1,14 @@ + */ + protected $locations = array( + 'module' => 'local/modules/{$name}/', + 'frontoffice-template' => 'templates/frontOffice/{$name}/', + 'backoffice-template' => 'templates/backOffice/{$name}/', + 'email-template' => 'templates/email/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php b/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php new file mode 100644 index 0000000..3b5f142 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/TuskInstaller.php @@ -0,0 +1,17 @@ + + */ +class TuskInstaller extends BaseInstaller +{ + /** @var array */ + protected $locations = array( + 'task' => '.tusk/tasks/{$name}/', + 'command' => '.tusk/commands/{$name}/', + 'asset' => 'assets/tusk/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/UserFrostingInstaller.php b/vendor/composer/installers/src/Composer/Installers/UserFrostingInstaller.php new file mode 100644 index 0000000..a646c5b --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/UserFrostingInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'sprinkle' => 'app/sprinkles/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/VanillaInstaller.php b/vendor/composer/installers/src/Composer/Installers/VanillaInstaller.php new file mode 100644 index 0000000..06d5db3 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/VanillaInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'plugin' => 'plugins/{$name}/', + 'theme' => 'themes/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/VgmcpInstaller.php b/vendor/composer/installers/src/Composer/Installers/VgmcpInstaller.php new file mode 100644 index 0000000..cf094dd --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/VgmcpInstaller.php @@ -0,0 +1,59 @@ + */ + protected $locations = array( + 'bundle' => 'src/{$vendor}/{$name}/', + 'theme' => 'themes/{$name}/' + ); + + /** + * Format package name. + * + * For package type vgmcp-bundle, cut off a trailing '-bundle' if present. + * + * For package type vgmcp-theme, cut off a trailing '-theme' if present. + * + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'vgmcp-bundle') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'vgmcp-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectPluginVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-bundle$/', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectThemeVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/-theme$/', '', $vars['name']); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php b/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php new file mode 100644 index 0000000..91b19fd --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/WHMCSInstaller.php @@ -0,0 +1,22 @@ + */ + protected $locations = array( + 'addons' => 'modules/addons/{$vendor}_{$name}/', + 'fraud' => 'modules/fraud/{$vendor}_{$name}/', + 'gateways' => 'modules/gateways/{$vendor}_{$name}/', + 'notifications' => 'modules/notifications/{$vendor}_{$name}/', + 'registrars' => 'modules/registrars/{$vendor}_{$name}/', + 'reports' => 'modules/reports/{$vendor}_{$name}/', + 'security' => 'modules/security/{$vendor}_{$name}/', + 'servers' => 'modules/servers/{$vendor}_{$name}/', + 'social' => 'modules/social/{$vendor}_{$name}/', + 'support' => 'modules/support/{$vendor}_{$name}/', + 'templates' => 'templates/{$vendor}_{$name}/', + 'includes' => 'includes/{$vendor}_{$name}/' + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/WinterInstaller.php b/vendor/composer/installers/src/Composer/Installers/WinterInstaller.php new file mode 100644 index 0000000..f75a681 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/WinterInstaller.php @@ -0,0 +1,71 @@ + */ + protected $locations = array( + 'module' => 'modules/{$name}/', + 'plugin' => 'plugins/{$vendor}/{$name}/', + 'theme' => 'themes/{$name}/' + ); + + /** + * Format package name. + * + * For package type winter-plugin, cut off a trailing '-plugin' if present. + * + * For package type winter-theme, cut off a trailing '-theme' if present. + */ + public function inflectPackageVars(array $vars): array + { + if ($vars['type'] === 'winter-module') { + return $this->inflectModuleVars($vars); + } + + if ($vars['type'] === 'winter-plugin') { + return $this->inflectPluginVars($vars); + } + + if ($vars['type'] === 'winter-theme') { + return $this->inflectThemeVars($vars); + } + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectModuleVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^wn-|-module$/', '', $vars['name']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectPluginVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^wn-|-plugin$/', '', $vars['name']); + $vars['vendor'] = $this->pregReplace('/[^a-z0-9_]/i', '', $vars['vendor']); + + return $vars; + } + + /** + * @param array $vars + * @return array + */ + protected function inflectThemeVars(array $vars): array + { + $vars['name'] = $this->pregReplace('/^wn-|-theme$/', '', $vars['name']); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php b/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php new file mode 100644 index 0000000..58a9587 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/WolfCMSInstaller.php @@ -0,0 +1,11 @@ + */ + protected $locations = array( + 'plugin' => 'wolf/plugins/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php b/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php new file mode 100644 index 0000000..d46d5ab --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/WordPressInstaller.php @@ -0,0 +1,14 @@ + */ + protected $locations = array( + 'plugin' => 'wp-content/plugins/{$name}/', + 'theme' => 'wp-content/themes/{$name}/', + 'muplugin' => 'wp-content/mu-plugins/{$name}/', + 'dropin' => 'wp-content/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/YawikInstaller.php b/vendor/composer/installers/src/Composer/Installers/YawikInstaller.php new file mode 100644 index 0000000..d609dea --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/YawikInstaller.php @@ -0,0 +1,23 @@ + */ + protected $locations = array( + 'module' => 'module/{$name}/', + ); + + /** + * Format package name to CamelCase + */ + public function inflectPackageVars(array $vars): array + { + $vars['name'] = strtolower($this->pregReplace('/(?<=\\w)([A-Z])/', '_\\1', $vars['name'])); + $vars['name'] = str_replace(array('-', '_'), ' ', $vars['name']); + $vars['name'] = str_replace(' ', '', ucwords($vars['name'])); + + return $vars; + } +} diff --git a/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php b/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php new file mode 100644 index 0000000..ccfcd4a --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ZendInstaller.php @@ -0,0 +1,13 @@ + */ + protected $locations = array( + 'library' => 'library/{$name}/', + 'extra' => 'extras/library/{$name}/', + 'module' => 'module/{$name}/', + ); +} diff --git a/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php b/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php new file mode 100644 index 0000000..d1fd1d7 --- /dev/null +++ b/vendor/composer/installers/src/Composer/Installers/ZikulaInstaller.php @@ -0,0 +1,12 @@ + */ + protected $locations = array( + 'module' => 'modules/{$vendor}-{$name}/', + 'theme' => 'themes/{$vendor}-{$name}/' + ); +} diff --git a/vendor/composer/installers/src/bootstrap.php b/vendor/composer/installers/src/bootstrap.php new file mode 100644 index 0000000..a5bb9ad --- /dev/null +++ b/vendor/composer/installers/src/bootstrap.php @@ -0,0 +1,18 @@ += 80200)) { + $issues[] = 'Your Composer dependencies require a PHP version ">= 8.2.0". You are running ' . PHP_VERSION . '.'; +} + +if ($issues) { + if (!headers_sent()) { + header('HTTP/1.1 500 Internal Server Error'); + } + if (!ini_get('display_errors')) { + if (PHP_SAPI === 'cli' || PHP_SAPI === 'phpdbg') { + fwrite(STDERR, 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . implode(PHP_EOL, $issues) . PHP_EOL.PHP_EOL); + } elseif (!headers_sent()) { + echo 'Composer detected issues in your platform:' . PHP_EOL.PHP_EOL . str_replace('You are running '.PHP_VERSION.'.', '', implode(PHP_EOL, $issues)) . PHP_EOL.PHP_EOL; + } + } + trigger_error( + 'Composer detected issues in your platform: ' . implode(' ', $issues), + E_USER_ERROR + ); +} diff --git a/vendor/hellonico/twig-dump-extension/.gitignore b/vendor/hellonico/twig-dump-extension/.gitignore deleted file mode 100644 index de4a392..0000000 --- a/vendor/hellonico/twig-dump-extension/.gitignore +++ /dev/null @@ -1,2 +0,0 @@ -/vendor -/composer.lock diff --git a/vendor/hellonico/twig-dump-extension/README.md b/vendor/hellonico/twig-dump-extension/README.md deleted file mode 100644 index 9614dd4..0000000 --- a/vendor/hellonico/twig-dump-extension/README.md +++ /dev/null @@ -1,24 +0,0 @@ -# Twig Dump Extension - -Standalone Symfony Var Dumper Twig extension. - -## Installation - -```bash -composer require hellonico/twig-dump-extension -``` - -## Usage - -```php -$twig = new Twig_Environment($loader, $options); -$twig->addExtension(new HelloNico\Twig\DumpExtension()); -``` - -In Twig templates: - -```twig -{{ dump(foo) }} -{% dump foo %} -{% dump foo, bar %} -``` diff --git a/vendor/hellonico/twig-dump-extension/composer.json b/vendor/hellonico/twig-dump-extension/composer.json deleted file mode 100644 index 976a8a9..0000000 --- a/vendor/hellonico/twig-dump-extension/composer.json +++ /dev/null @@ -1,23 +0,0 @@ -{ - "name": "hellonico/twig-dump-extension", - "type": "library", - "description": "VarDumper Twig extension", - "homepage": "https://github.com/nlemoine/twig-dump-extension", - "license": "GPL-3.0+", - "authors": [ - { - "name": "Nicolas Lemoine", - "email": "dev@helloni.co", - "homepage": "https://github.com/nlemoine" - } - ], - "require": { - "symfony/var-dumper": "^4.2 || ^5.0", - "twig/twig": "^2.4 || ^3.0" - }, - "autoload": { - "psr-4": { - "HelloNico\\Twig\\": "src/" - } - } -} diff --git a/vendor/hellonico/twig-dump-extension/src/DumpExtension.php b/vendor/hellonico/twig-dump-extension/src/DumpExtension.php deleted file mode 100644 index 96908c5..0000000 --- a/vendor/hellonico/twig-dump-extension/src/DumpExtension.php +++ /dev/null @@ -1,77 +0,0 @@ - - */ -final class DumpExtension extends AbstractExtension -{ - private $cloner; - private $dumper; - - public function __construct() - { - $this->cloner = new VarCloner(); - $this->dumper = new HtmlDumper(); - } - - /** - * {@inheritdoc} - */ - public function getFunctions(): array - { - return [ - new TwigFunction('dump', [$this, 'dump'], ['is_safe' => ['html'], 'needs_context' => true, 'needs_environment' => true]), - ]; - } - - /** - * {@inheritdoc} - */ - public function getTokenParsers(): array - { - return [new DumpTokenParser()]; - } - - public function dump(Environment $env, array $context): ?string - { - if (!$env->isDebug()) { - return null; - } - - if (2 === \func_num_args()) { - $vars = []; - foreach ($context as $key => $value) { - if (!$value instanceof Template) { - $vars[$key] = $value; - } - } - - $vars = [$vars]; - } else { - $vars = \func_get_args(); - unset($vars[0], $vars[1]); - } - - $dump = fopen('php://memory', 'r+b'); - $this->dumper = $this->dumper ?: new HtmlDumper(); - $this->dumper->setCharset($env->getCharset()); - - foreach ($vars as $value) { - $this->dumper->dump($this->cloner->cloneVar($value), $dump); - } - - return stream_get_contents($dump, -1, 0); - } -} diff --git a/vendor/hellonico/twig-dump-extension/src/DumpNode.php b/vendor/hellonico/twig-dump-extension/src/DumpNode.php deleted file mode 100644 index a00fc0d..0000000 --- a/vendor/hellonico/twig-dump-extension/src/DumpNode.php +++ /dev/null @@ -1,78 +0,0 @@ - - */ -final class DumpNode extends Node -{ - private $varPrefix; - - public function __construct($varPrefix, ?Node $values, int $lineno, string $tag = null) - { - $nodes = []; - if (null !== $values) { - $nodes['values'] = $values; - } - - parent::__construct($nodes, [], $lineno, $tag); - $this->varPrefix = $varPrefix; - } - - public function compile(Compiler $compiler): void - { - $compiler - ->write("if (\$this->env->isDebug()) {\n") - ->indent(); - - if (!$this->hasNode('values')) { - // remove embedded templates (macros) from the context - $compiler - ->write(sprintf('$%svars = [];'."\n", $this->varPrefix)) - ->write(sprintf('foreach ($context as $%1$skey => $%1$sval) {'."\n", $this->varPrefix)) - ->indent() - ->write(sprintf('if (!$%sval instanceof \Twig\Template) {'."\n", $this->varPrefix)) - ->indent() - ->write(sprintf('$%1$svars[$%1$skey] = $%1$sval;'."\n", $this->varPrefix)) - ->outdent() - ->write("}\n") - ->outdent() - ->write("}\n") - ->addDebugInfo($this) - ->write(sprintf('\Symfony\Component\VarDumper\VarDumper::dump($%svars);'."\n", $this->varPrefix)); - } elseif (($values = $this->getNode('values')) && 1 === $values->count()) { - $compiler - ->addDebugInfo($this) - ->write('\Symfony\Component\VarDumper\VarDumper::dump(') - ->subcompile($values->getNode(0)) - ->raw(");\n"); - } else { - $compiler - ->addDebugInfo($this) - ->write('\Symfony\Component\VarDumper\VarDumper::dump(['."\n") - ->indent(); - foreach ($values as $node) { - $compiler->write(''); - if ($node->hasAttribute('name')) { - $compiler - ->string($node->getAttribute('name')) - ->raw(' => '); - } - $compiler - ->subcompile($node) - ->raw(",\n"); - } - $compiler - ->outdent() - ->write("]);\n"); - } - - $compiler - ->outdent() - ->write("}\n"); - } -} diff --git a/vendor/hellonico/twig-dump-extension/src/DumpTokenParser.php b/vendor/hellonico/twig-dump-extension/src/DumpTokenParser.php deleted file mode 100644 index c61ca4a..0000000 --- a/vendor/hellonico/twig-dump-extension/src/DumpTokenParser.php +++ /dev/null @@ -1,44 +0,0 @@ - - */ -final class DumpTokenParser extends AbstractTokenParser -{ - /** - * {@inheritdoc} - */ - public function parse(Token $token): Node - { - $values = null; - if (!$this->parser->getStream()->test(Token::BLOCK_END_TYPE)) { - $values = $this->parser->getExpressionParser()->parseMultitargetExpression(); - } - $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); - - return new DumpNode($this->parser->getVarName(), $values, $token->getLine(), $this->getTag()); - } - - /** - * {@inheritdoc} - */ - public function getTag(): string - { - return 'dump'; - } -} diff --git a/vendor/illuminate/collections/Arr.php b/vendor/illuminate/collections/Arr.php new file mode 100644 index 0000000..e499ef7 --- /dev/null +++ b/vendor/illuminate/collections/Arr.php @@ -0,0 +1,1185 @@ +all(); + } elseif (! is_array($values)) { + continue; + } + + $results[] = $values; + } + + return array_merge([], ...$results); + } + + /** + * Cross join the given arrays, returning all possible permutations. + * + * @param iterable ...$arrays + * @return array + */ + public static function crossJoin(...$arrays) + { + $results = [[]]; + + foreach ($arrays as $index => $array) { + $append = []; + + foreach ($results as $product) { + foreach ($array as $item) { + $product[$index] = $item; + + $append[] = $product; + } + } + + $results = $append; + } + + return $results; + } + + /** + * Divide an array into two arrays. One with keys and the other with values. + * + * @param array $array + * @return array + */ + public static function divide($array) + { + return [array_keys($array), array_values($array)]; + } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @param iterable $array + * @param string $prepend + * @return array + */ + public static function dot($array, $prepend = '') + { + $results = []; + + $flatten = function ($data, $prefix) use (&$results, &$flatten): void { + foreach ($data as $key => $value) { + $newKey = $prefix.$key; + + if (is_array($value) && ! empty($value)) { + $flatten($value, $newKey.'.'); + } else { + $results[$newKey] = $value; + } + } + }; + + $flatten($array, $prepend); + + return $results; + } + + /** + * Convert a flatten "dot" notation array into an expanded array. + * + * @param iterable $array + * @return array + */ + public static function undot($array) + { + $results = []; + + foreach ($array as $key => $value) { + static::set($results, $key, $value); + } + + return $results; + } + + /** + * Get all of the given array except for a specified array of keys. + * + * @param array $array + * @param array|string|int|float $keys + * @return array + */ + public static function except($array, $keys) + { + static::forget($array, $keys); + + return $array; + } + + /** + * Determine if the given key exists in the provided array. + * + * @param \ArrayAccess|array $array + * @param string|int|float $key + * @return bool + */ + public static function exists($array, $key) + { + if ($array instanceof Enumerable) { + return $array->has($key); + } + + if ($array instanceof ArrayAccess) { + return $array->offsetExists($key); + } + + if (is_float($key)) { + $key = (string) $key; + } + + return array_key_exists($key, $array); + } + + /** + * Return the first element in an array passing a given truth test. + * + * @template TKey + * @template TValue + * @template TFirstDefault + * + * @param iterable $array + * @param (callable(TValue, TKey): bool)|null $callback + * @param TFirstDefault|(\Closure(): TFirstDefault) $default + * @return TValue|TFirstDefault + */ + public static function first($array, ?callable $callback = null, $default = null) + { + if (is_null($callback)) { + if (empty($array)) { + return value($default); + } + + foreach ($array as $item) { + return $item; + } + + return value($default); + } + + foreach ($array as $key => $value) { + if ($callback($value, $key)) { + return $value; + } + } + + return value($default); + } + + /** + * Return the last element in an array passing a given truth test. + * + * @template TKey + * @template TValue + * @template TLastDefault + * + * @param iterable $array + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault|(\Closure(): TLastDefault) $default + * @return TValue|TLastDefault + */ + public static function last($array, ?callable $callback = null, $default = null) + { + if (is_null($callback)) { + return empty($array) ? value($default) : end($array); + } + + return static::first(array_reverse($array, true), $callback, $default); + } + + /** + * Take the first or last {$limit} items from an array. + * + * @param array $array + * @param int $limit + * @return array + */ + public static function take($array, $limit) + { + if ($limit < 0) { + return array_slice($array, $limit, abs($limit)); + } + + return array_slice($array, 0, $limit); + } + + /** + * Flatten a multi-dimensional array into a single level. + * + * @param iterable $array + * @param int $depth + * @return array + */ + public static function flatten($array, $depth = INF) + { + $result = []; + + foreach ($array as $item) { + $item = $item instanceof Collection ? $item->all() : $item; + + if (! is_array($item)) { + $result[] = $item; + } else { + $values = $depth === 1 + ? array_values($item) + : static::flatten($item, $depth - 1); + + foreach ($values as $value) { + $result[] = $value; + } + } + } + + return $result; + } + + /** + * Get a float item from an array using "dot" notation. + */ + public static function float(ArrayAccess|array $array, string|int|null $key, ?float $default = null): float + { + $value = Arr::get($array, $key, $default); + + if (! is_float($value)) { + throw new InvalidArgumentException( + sprintf('Array value for key [%s] must be a float, %s found.', $key, gettype($value)) + ); + } + + return $value; + } + + /** + * Remove one or many array items from a given array using "dot" notation. + * + * @param array $array + * @param array|string|int|float $keys + * @return void + */ + public static function forget(&$array, $keys) + { + $original = &$array; + + $keys = (array) $keys; + + if (count($keys) === 0) { + return; + } + + foreach ($keys as $key) { + // if the exact key exists in the top-level, remove it + if (static::exists($array, $key)) { + unset($array[$key]); + + continue; + } + + $parts = explode('.', $key); + + // clean up before each pass + $array = &$original; + + while (count($parts) > 1) { + $part = array_shift($parts); + + if (isset($array[$part]) && static::accessible($array[$part])) { + $array = &$array[$part]; + } else { + continue 2; + } + } + + unset($array[array_shift($parts)]); + } + } + + /** + * Get the underlying array of items from the given argument. + * + * @template TKey of array-key = array-key + * @template TValue = mixed + * + * @param array|Enumerable|Arrayable|WeakMap|Traversable|Jsonable|JsonSerializable|object $items + * @return ($items is WeakMap ? list : array) + * + * @throws \InvalidArgumentException + */ + public static function from($items) + { + return match (true) { + is_array($items) => $items, + $items instanceof Enumerable => $items->all(), + $items instanceof Arrayable => $items->toArray(), + $items instanceof WeakMap => iterator_to_array($items, false), + $items instanceof Traversable => iterator_to_array($items), + $items instanceof Jsonable => json_decode($items->toJson(), true), + $items instanceof JsonSerializable => (array) $items->jsonSerialize(), + is_object($items) => (array) $items, + default => throw new InvalidArgumentException('Items cannot be represented by a scalar value.'), + }; + } + + /** + * Get an item from an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|int|null $key + * @param mixed $default + * @return mixed + */ + public static function get($array, $key, $default = null) + { + if (! static::accessible($array)) { + return value($default); + } + + if (is_null($key)) { + return $array; + } + + if (static::exists($array, $key)) { + return $array[$key]; + } + + if (! str_contains($key, '.')) { + return $array[$key] ?? value($default); + } + + foreach (explode('.', $key) as $segment) { + if (static::accessible($array) && static::exists($array, $segment)) { + $array = $array[$segment]; + } else { + return value($default); + } + } + + return $array; + } + + /** + * Check if an item or items exist in an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|array $keys + * @return bool + */ + public static function has($array, $keys) + { + $keys = (array) $keys; + + if (! $array || $keys === []) { + return false; + } + + foreach ($keys as $key) { + $subKeyArray = $array; + + if (static::exists($array, $key)) { + continue; + } + + foreach (explode('.', $key) as $segment) { + if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { + $subKeyArray = $subKeyArray[$segment]; + } else { + return false; + } + } + } + + return true; + } + + /** + * Determine if all keys exist in an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|array $keys + * @return bool + */ + public static function hasAll($array, $keys) + { + $keys = (array) $keys; + + if (! $array || $keys === []) { + return false; + } + + foreach ($keys as $key) { + if (! static::has($array, $key)) { + return false; + } + } + + return true; + } + + /** + * Determine if any of the keys exist in an array using "dot" notation. + * + * @param \ArrayAccess|array $array + * @param string|array $keys + * @return bool + */ + public static function hasAny($array, $keys) + { + if (is_null($keys)) { + return false; + } + + $keys = (array) $keys; + + if (! $array) { + return false; + } + + if ($keys === []) { + return false; + } + + foreach ($keys as $key) { + if (static::has($array, $key)) { + return true; + } + } + + return false; + } + + /** + * Get an integer item from an array using "dot" notation. + */ + public static function integer(ArrayAccess|array $array, string|int|null $key, ?int $default = null): int + { + $value = Arr::get($array, $key, $default); + + if (! is_int($value)) { + throw new InvalidArgumentException( + sprintf('Array value for key [%s] must be an integer, %s found.', $key, gettype($value)) + ); + } + + return $value; + } + + /** + * Determines if an array is associative. + * + * An array is "associative" if it doesn't have sequential numerical keys beginning with zero. + * + * @param array $array + * @return bool + */ + public static function isAssoc(array $array) + { + return ! array_is_list($array); + } + + /** + * Determines if an array is a list. + * + * An array is a "list" if all array keys are sequential integers starting from 0 with no gaps in between. + * + * @param array $array + * @return bool + */ + public static function isList($array) + { + return array_is_list($array); + } + + /** + * Join all items using a string. The final items can use a separate glue string. + * + * @param array $array + * @param string $glue + * @param string $finalGlue + * @return string + */ + public static function join($array, $glue, $finalGlue = '') + { + if ($finalGlue === '') { + return implode($glue, $array); + } + + if (count($array) === 0) { + return ''; + } + + if (count($array) === 1) { + return end($array); + } + + $finalItem = array_pop($array); + + return implode($glue, $array).$finalGlue.$finalItem; + } + + /** + * Key an associative array by a field or using a callback. + * + * @param array $array + * @param callable|array|string $keyBy + * @return array + */ + public static function keyBy($array, $keyBy) + { + return (new Collection($array))->keyBy($keyBy)->all(); + } + + /** + * Prepend the key names of an associative array. + * + * @param array $array + * @param string $prependWith + * @return array + */ + public static function prependKeysWith($array, $prependWith) + { + return static::mapWithKeys($array, fn ($item, $key) => [$prependWith.$key => $item]); + } + + /** + * Get a subset of the items from the given array. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function only($array, $keys) + { + return array_intersect_key($array, array_flip((array) $keys)); + } + + /** + * Select an array of values from an array. + * + * @param array $array + * @param array|string $keys + * @return array + */ + public static function select($array, $keys) + { + $keys = static::wrap($keys); + + return static::map($array, function ($item) use ($keys) { + $result = []; + + foreach ($keys as $key) { + if (Arr::accessible($item) && Arr::exists($item, $key)) { + $result[$key] = $item[$key]; + } elseif (is_object($item) && isset($item->{$key})) { + $result[$key] = $item->{$key}; + } + } + + return $result; + }); + } + + /** + * Pluck an array of values from an array. + * + * @param iterable $array + * @param string|array|int|Closure|null $value + * @param string|array|Closure|null $key + * @return array + */ + public static function pluck($array, $value, $key = null) + { + $results = []; + + [$value, $key] = static::explodePluckParameters($value, $key); + + foreach ($array as $item) { + $itemValue = $value instanceof Closure + ? $value($item) + : data_get($item, $value); + + // If the key is "null", we will just append the value to the array and keep + // looping. Otherwise we will key the array using the value of the key we + // received from the developer. Then we'll return the final array form. + if (is_null($key)) { + $results[] = $itemValue; + } else { + $itemKey = $key instanceof Closure + ? $key($item) + : data_get($item, $key); + + if (is_object($itemKey) && method_exists($itemKey, '__toString')) { + $itemKey = (string) $itemKey; + } + + $results[$itemKey] = $itemValue; + } + } + + return $results; + } + + /** + * Explode the "value" and "key" arguments passed to "pluck". + * + * @param string|array|Closure $value + * @param string|array|Closure|null $key + * @return array + */ + protected static function explodePluckParameters($value, $key) + { + $value = is_string($value) ? explode('.', $value) : $value; + + $key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key); + + return [$value, $key]; + } + + /** + * Run a map over each of the items in the array. + * + * @param array $array + * @param callable $callback + * @return array + */ + public static function map(array $array, callable $callback) + { + $keys = array_keys($array); + + try { + $items = array_map($callback, $array, $keys); + } catch (ArgumentCountError) { + $items = array_map($callback, $array); + } + + return array_combine($keys, $items); + } + + /** + * Run an associative map over each of the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TKey + * @template TValue + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param array $array + * @param callable(TValue, TKey): array $callback + * @return array + */ + public static function mapWithKeys(array $array, callable $callback) + { + $result = []; + + foreach ($array as $key => $value) { + $assoc = $callback($value, $key); + + foreach ($assoc as $mapKey => $mapValue) { + $result[$mapKey] = $mapValue; + } + } + + return $result; + } + + /** + * Run a map over each nested chunk of items. + * + * @template TKey + * @template TValue + * + * @param array $array + * @param callable(mixed...): TValue $callback + * @return array + */ + public static function mapSpread(array $array, callable $callback) + { + return static::map($array, function ($chunk, $key) use ($callback) { + $chunk[] = $key; + + return $callback(...$chunk); + }); + } + + /** + * Push an item onto the beginning of an array. + * + * @param array $array + * @param mixed $value + * @param mixed $key + * @return array + */ + public static function prepend($array, $value, $key = null) + { + if (func_num_args() == 2) { + array_unshift($array, $value); + } else { + $array = [$key => $value] + $array; + } + + return $array; + } + + /** + * Get a value from the array, and remove it. + * + * @param array $array + * @param string|int $key + * @param mixed $default + * @return mixed + */ + public static function pull(&$array, $key, $default = null) + { + $value = static::get($array, $key, $default); + + static::forget($array, $key); + + return $value; + } + + /** + * Convert the array into a query string. + * + * @param array $array + * @return string + */ + public static function query($array) + { + return http_build_query($array, '', '&', PHP_QUERY_RFC3986); + } + + /** + * Get one or a specified number of random values from an array. + * + * @param array $array + * @param int|null $number + * @param bool $preserveKeys + * @return mixed + * + * @throws \InvalidArgumentException + */ + public static function random($array, $number = null, $preserveKeys = false) + { + $requested = is_null($number) ? 1 : $number; + + $count = count($array); + + if ($requested > $count) { + throw new InvalidArgumentException( + "You requested {$requested} items, but there are only {$count} items available." + ); + } + + if (empty($array) || (! is_null($number) && $number <= 0)) { + return is_null($number) ? null : []; + } + + $keys = (new Randomizer)->pickArrayKeys($array, $requested); + + if (is_null($number)) { + return $array[$keys[0]]; + } + + $results = []; + + if ($preserveKeys) { + foreach ($keys as $key) { + $results[$key] = $array[$key]; + } + } else { + foreach ($keys as $key) { + $results[] = $array[$key]; + } + } + + return $results; + } + + /** + * Set an array item to a given value using "dot" notation. + * + * If no key is given to the method, the entire array will be replaced. + * + * @param array $array + * @param string|int|null $key + * @param mixed $value + * @return array + */ + public static function set(&$array, $key, $value) + { + if (is_null($key)) { + return $array = $value; + } + + $keys = explode('.', $key); + + foreach ($keys as $i => $key) { + if (count($keys) === 1) { + break; + } + + unset($keys[$i]); + + // If the key doesn't exist at this depth, we will just create an empty array + // to hold the next value, allowing us to create the arrays to hold final + // values at the correct depth. Then we'll keep digging into the array. + if (! isset($array[$key]) || ! is_array($array[$key])) { + $array[$key] = []; + } + + $array = &$array[$key]; + } + + $array[array_shift($keys)] = $value; + + return $array; + } + + /** + * Shuffle the given array and return the result. + * + * @param array $array + * @return array + */ + public static function shuffle($array) + { + return (new Randomizer)->shuffleArray($array); + } + + /** + * Get the first item in the array, but only if exactly one item exists. Otherwise, throw an exception. + * + * @param array $array + * @param (callable(mixed, array-key): array)|null $callback + * + * @throws \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException + */ + public static function sole($array, ?callable $callback = null) + { + if ($callback) { + $array = static::where($array, $callback); + } + + $count = count($array); + + if ($count === 0) { + throw new ItemNotFoundException; + } + + if ($count > 1) { + throw new MultipleItemsFoundException($count); + } + + return static::first($array); + } + + /** + * Sort the array using the given callback or "dot" notation. + * + * @param array $array + * @param callable|array|string|null $callback + * @return array + */ + public static function sort($array, $callback = null) + { + return (new Collection($array))->sortBy($callback)->all(); + } + + /** + * Sort the array in descending order using the given callback or "dot" notation. + * + * @param array $array + * @param callable|array|string|null $callback + * @return array + */ + public static function sortDesc($array, $callback = null) + { + return (new Collection($array))->sortByDesc($callback)->all(); + } + + /** + * Recursively sort an array by keys and values. + * + * @param array $array + * @param int $options + * @param bool $descending + * @return array + */ + public static function sortRecursive($array, $options = SORT_REGULAR, $descending = false) + { + foreach ($array as &$value) { + if (is_array($value)) { + $value = static::sortRecursive($value, $options, $descending); + } + } + + if (! array_is_list($array)) { + $descending + ? krsort($array, $options) + : ksort($array, $options); + } else { + $descending + ? rsort($array, $options) + : sort($array, $options); + } + + return $array; + } + + /** + * Recursively sort an array by keys and values in descending order. + * + * @param array $array + * @param int $options + * @return array + */ + public static function sortRecursiveDesc($array, $options = SORT_REGULAR) + { + return static::sortRecursive($array, $options, true); + } + + /** + * Get a string item from an array using "dot" notation. + */ + public static function string(ArrayAccess|array $array, string|int|null $key, ?string $default = null): string + { + $value = Arr::get($array, $key, $default); + + if (! is_string($value)) { + throw new InvalidArgumentException( + sprintf('Array value for key [%s] must be a string, %s found.', $key, gettype($value)) + ); + } + + return $value; + } + + /** + * Conditionally compile classes from an array into a CSS class list. + * + * @param array|string $array + * @return string + */ + public static function toCssClasses($array) + { + $classList = static::wrap($array); + + $classes = []; + + foreach ($classList as $class => $constraint) { + if (is_numeric($class)) { + $classes[] = $constraint; + } elseif ($constraint) { + $classes[] = $class; + } + } + + return implode(' ', $classes); + } + + /** + * Conditionally compile styles from an array into a style list. + * + * @param array|string $array + * @return string + */ + public static function toCssStyles($array) + { + $styleList = static::wrap($array); + + $styles = []; + + foreach ($styleList as $class => $constraint) { + if (is_numeric($class)) { + $styles[] = Str::finish($constraint, ';'); + } elseif ($constraint) { + $styles[] = Str::finish($class, ';'); + } + } + + return implode(' ', $styles); + } + + /** + * Filter the array using the given callback. + * + * @param array $array + * @param callable $callback + * @return array + */ + public static function where($array, callable $callback) + { + return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); + } + + /** + * Filter the array using the negation of the given callback. + * + * @param array $array + * @param callable $callback + * @return array + */ + public static function reject($array, callable $callback) + { + return static::where($array, fn ($value, $key) => ! $callback($value, $key)); + } + + /** + * Partition the array into two arrays using the given callback. + * + * @template TKey of array-key + * @template TValue of mixed + * + * @param iterable $array + * @param callable(TValue, TKey): bool $callback + * @return array, array> + */ + public static function partition($array, callable $callback) + { + $passed = []; + $failed = []; + + foreach ($array as $key => $item) { + if ($callback($item, $key)) { + $passed[$key] = $item; + } else { + $failed[$key] = $item; + } + } + + return [$passed, $failed]; + } + + /** + * Filter items where the value is not null. + * + * @param array $array + * @return array + */ + public static function whereNotNull($array) + { + return static::where($array, fn ($value) => ! is_null($value)); + } + + /** + * If the given value is not an array and not null, wrap it in one. + * + * @param mixed $value + * @return array + */ + public static function wrap($value) + { + if (is_null($value)) { + return []; + } + + return is_array($value) ? $value : [$value]; + } +} diff --git a/vendor/illuminate/collections/Collection.php b/vendor/illuminate/collections/Collection.php new file mode 100644 index 0000000..e021037 --- /dev/null +++ b/vendor/illuminate/collections/Collection.php @@ -0,0 +1,1938 @@ + + * @implements \Illuminate\Support\Enumerable + */ +class Collection implements ArrayAccess, CanBeEscapedWhenCastToString, Enumerable +{ + /** + * @use \Illuminate\Support\Traits\EnumeratesValues + */ + use EnumeratesValues, Macroable, TransformsToResourceCollection; + + /** + * The items contained in the collection. + * + * @var array + */ + protected $items = []; + + /** + * Create a new collection. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items + */ + public function __construct($items = []) + { + $this->items = $this->getArrayableItems($items); + } + + /** + * Create a collection with the given range. + * + * @param int $from + * @param int $to + * @param int $step + * @return static + */ + public static function range($from, $to, $step = 1) + { + return new static(range($from, $to, $step)); + } + + /** + * Get all of the items in the collection. + * + * @return array + */ + public function all() + { + return $this->items; + } + + /** + * Get a lazy collection for the items in this collection. + * + * @return \Illuminate\Support\LazyCollection + */ + public function lazy() + { + return new LazyCollection($this->items); + } + + /** + * Get the median of a given key. + * + * @param string|array|null $key + * @return float|int|null + */ + public function median($key = null) + { + $values = (isset($key) ? $this->pluck($key) : $this) + ->reject(fn ($item) => is_null($item)) + ->sort()->values(); + + $count = $values->count(); + + if ($count === 0) { + return; + } + + $middle = (int) ($count / 2); + + if ($count % 2) { + return $values->get($middle); + } + + return (new static([ + $values->get($middle - 1), $values->get($middle), + ]))->average(); + } + + /** + * Get the mode of a given key. + * + * @param string|array|null $key + * @return array|null + */ + public function mode($key = null) + { + if ($this->count() === 0) { + return; + } + + $collection = isset($key) ? $this->pluck($key) : $this; + + $counts = new static; + + $collection->each(fn ($value) => $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1); + + $sorted = $counts->sort(); + + $highestValue = $sorted->last(); + + return $sorted->filter(fn ($value) => $value == $highestValue) + ->sort()->keys()->all(); + } + + /** + * Collapse the collection of items into a single array. + * + * @return static + */ + public function collapse() + { + return new static(Arr::collapse($this->items)); + } + + /** + * Collapse the collection of items into a single array while preserving its keys. + * + * @return static + */ + public function collapseWithKeys() + { + if (! $this->items) { + return new static; + } + + $results = []; + + foreach ($this->items as $key => $values) { + if ($values instanceof Collection) { + $values = $values->all(); + } elseif (! is_array($values)) { + continue; + } + + $results[$key] = $values; + } + + if (! $results) { + return new static; + } + + return new static(array_replace(...$results)); + } + + /** + * Determine if an item exists in the collection. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function contains($key, $operator = null, $value = null) + { + if (func_num_args() === 1) { + if ($this->useAsCallable($key)) { + $placeholder = new stdClass; + + return $this->first($key, $placeholder) !== $placeholder; + } + + return in_array($key, $this->items); + } + + return $this->contains($this->operatorForWhere(...func_get_args())); + } + + /** + * Determine if an item exists, using strict comparison. + * + * @param (callable(TValue): bool)|TValue|array-key $key + * @param TValue|null $value + * @return bool + */ + public function containsStrict($key, $value = null) + { + if (func_num_args() === 2) { + return $this->contains(fn ($item) => data_get($item, $key) === $value); + } + + if ($this->useAsCallable($key)) { + return ! is_null($this->first($key)); + } + + return in_array($key, $this->items, true); + } + + /** + * Determine if an item is not contained in the collection. + * + * @param mixed $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function doesntContain($key, $operator = null, $value = null) + { + return ! $this->contains(...func_get_args()); + } + + /** + * Cross join with the given lists, returning all possible permutations. + * + * @template TCrossJoinKey + * @template TCrossJoinValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists + * @return static> + */ + public function crossJoin(...$lists) + { + return new static(Arr::crossJoin( + $this->items, ...array_map($this->getArrayableItems(...), $lists) + )); + } + + /** + * Get the items in the collection that are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diff($items) + { + return new static(array_diff($this->items, $this->getArrayableItems($items))); + } + + /** + * Get the items in the collection that are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function diffUsing($items, callable $callback) + { + return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback)); + } + + /** + * Get the items in the collection whose keys and values are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diffAssoc($items) + { + return new static(array_diff_assoc($this->items, $this->getArrayableItems($items))); + } + + /** + * Get the items in the collection whose keys and values are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diffAssocUsing($items, callable $callback) + { + return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback)); + } + + /** + * Get the items in the collection whose keys are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diffKeys($items) + { + return new static(array_diff_key($this->items, $this->getArrayableItems($items))); + } + + /** + * Get the items in the collection whose keys are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diffKeysUsing($items, callable $callback) + { + return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); + } + + /** + * Retrieve duplicate items from the collection. + * + * @template TMapValue + * + * @param (callable(TValue): TMapValue)|string|null $callback + * @param bool $strict + * @return static + */ + public function duplicates($callback = null, $strict = false) + { + $items = $this->map($this->valueRetriever($callback)); + + $uniqueItems = $items->unique(null, $strict); + + $compare = $this->duplicateComparator($strict); + + $duplicates = new static; + + foreach ($items as $key => $value) { + if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) { + $uniqueItems->shift(); + } else { + $duplicates[$key] = $value; + } + } + + return $duplicates; + } + + /** + * Retrieve duplicate items from the collection using strict comparison. + * + * @template TMapValue + * + * @param (callable(TValue): TMapValue)|string|null $callback + * @return static + */ + public function duplicatesStrict($callback = null) + { + return $this->duplicates($callback, true); + } + + /** + * Get the comparison function to detect duplicates. + * + * @param bool $strict + * @return callable(TValue, TValue): bool + */ + protected function duplicateComparator($strict) + { + if ($strict) { + return fn ($a, $b) => $a === $b; + } + + return fn ($a, $b) => $a == $b; + } + + /** + * Get all items except for those with the specified keys. + * + * @param \Illuminate\Support\Enumerable|array|string $keys + * @return static + */ + public function except($keys) + { + if (is_null($keys)) { + return new static($this->items); + } + + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } elseif (! is_array($keys)) { + $keys = func_get_args(); + } + + return new static(Arr::except($this->items, $keys)); + } + + /** + * Run a filter over each of the items. + * + * @param (callable(TValue, TKey): bool)|null $callback + * @return static + */ + public function filter(?callable $callback = null) + { + if ($callback) { + return new static(Arr::where($this->items, $callback)); + } + + return new static(array_filter($this->items)); + } + + /** + * Get the first item from the collection passing the given truth test. + * + * @template TFirstDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TFirstDefault|(\Closure(): TFirstDefault) $default + * @return TValue|TFirstDefault + */ + public function first(?callable $callback = null, $default = null) + { + return Arr::first($this->items, $callback, $default); + } + + /** + * Get a flattened array of the items in the collection. + * + * @param int $depth + * @return static + */ + public function flatten($depth = INF) + { + return new static(Arr::flatten($this->items, $depth)); + } + + /** + * Flip the items in the collection. + * + * @return static + */ + public function flip() + { + return new static(array_flip($this->items)); + } + + /** + * Remove an item from the collection by key. + * + * \Illuminate\Contracts\Support\Arrayable|iterable|TKey $keys + * + * @return $this + */ + public function forget($keys) + { + foreach ($this->getArrayableItems($keys) as $key) { + $this->offsetUnset($key); + } + + return $this; + } + + /** + * Get an item from the collection by key. + * + * @template TGetDefault + * + * @param TKey $key + * @param TGetDefault|(\Closure(): TGetDefault) $default + * @return TValue|TGetDefault + */ + public function get($key, $default = null) + { + if (array_key_exists($key, $this->items)) { + return $this->items[$key]; + } + + return value($default); + } + + /** + * Get an item from the collection by key or add it to collection if it does not exist. + * + * @template TGetOrPutValue + * + * @param mixed $key + * @param TGetOrPutValue|(\Closure(): TGetOrPutValue) $value + * @return TValue|TGetOrPutValue + */ + public function getOrPut($key, $value) + { + if (array_key_exists($key, $this->items)) { + return $this->items[$key]; + } + + $this->offsetSet($key, $value = value($value)); + + return $value; + } + + /** + * Group an associative array by a field or using a callback. + * + * @template TGroupKey of array-key + * + * @param (callable(TValue, TKey): TGroupKey)|array|string $groupBy + * @param bool $preserveKeys + * @return static<($groupBy is string ? array-key : ($groupBy is array ? array-key : TGroupKey)), static<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)>> + */ + public function groupBy($groupBy, $preserveKeys = false) + { + if (! $this->useAsCallable($groupBy) && is_array($groupBy)) { + $nextGroups = $groupBy; + + $groupBy = array_shift($nextGroups); + } + + $groupBy = $this->valueRetriever($groupBy); + + $results = []; + + foreach ($this->items as $key => $value) { + $groupKeys = $groupBy($value, $key); + + if (! is_array($groupKeys)) { + $groupKeys = [$groupKeys]; + } + + foreach ($groupKeys as $groupKey) { + $groupKey = match (true) { + is_bool($groupKey) => (int) $groupKey, + $groupKey instanceof \BackedEnum => $groupKey->value, + $groupKey instanceof \Stringable => (string) $groupKey, + default => $groupKey, + }; + + if (! array_key_exists($groupKey, $results)) { + $results[$groupKey] = new static; + } + + $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); + } + } + + $result = new static($results); + + if (! empty($nextGroups)) { + return $result->map->groupBy($nextGroups, $preserveKeys); + } + + return $result; + } + + /** + * Key an associative array by a field or using a callback. + * + * @template TNewKey of array-key + * + * @param (callable(TValue, TKey): TNewKey)|array|string $keyBy + * @return static<($keyBy is string ? array-key : ($keyBy is array ? array-key : TNewKey)), TValue> + */ + public function keyBy($keyBy) + { + $keyBy = $this->valueRetriever($keyBy); + + $results = []; + + foreach ($this->items as $key => $item) { + $resolvedKey = $keyBy($item, $key); + + if (is_object($resolvedKey)) { + $resolvedKey = (string) $resolvedKey; + } + + $results[$resolvedKey] = $item; + } + + return new static($results); + } + + /** + * Determine if an item exists in the collection by key. + * + * @param TKey|array $key + * @return bool + */ + public function has($key) + { + $keys = is_array($key) ? $key : func_get_args(); + + foreach ($keys as $value) { + if (! array_key_exists($value, $this->items)) { + return false; + } + } + + return true; + } + + /** + * Determine if any of the keys exist in the collection. + * + * @param TKey|array $key + * @return bool + */ + public function hasAny($key) + { + if ($this->isEmpty()) { + return false; + } + + $keys = is_array($key) ? $key : func_get_args(); + + foreach ($keys as $value) { + if (array_key_exists($value, $this->items)) { + return true; + } + } + + return false; + } + + /** + * Concatenate values of a given key as a string. + * + * @param (callable(TValue, TKey): mixed)|string|null $value + * @param string|null $glue + * @return string + */ + public function implode($value, $glue = null) + { + if ($this->useAsCallable($value)) { + return implode($glue ?? '', $this->map($value)->all()); + } + + $first = $this->first(); + + if (is_array($first) || (is_object($first) && ! $first instanceof Stringable)) { + return implode($glue ?? '', $this->pluck($value)->all()); + } + + return implode($value ?? '', $this->items); + } + + /** + * Intersect the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersect($items) + { + return new static(array_intersect($this->items, $this->getArrayableItems($items))); + } + + /** + * Intersect the collection with the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersectUsing($items, callable $callback) + { + return new static(array_uintersect($this->items, $this->getArrayableItems($items), $callback)); + } + + /** + * Intersect the collection with the given items with additional index check. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersectAssoc($items) + { + return new static(array_intersect_assoc($this->items, $this->getArrayableItems($items))); + } + + /** + * Intersect the collection with the given items with additional index check, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersectAssocUsing($items, callable $callback) + { + return new static(array_intersect_uassoc($this->items, $this->getArrayableItems($items), $callback)); + } + + /** + * Intersect the collection with the given items by key. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersectByKeys($items) + { + return new static(array_intersect_key( + $this->items, $this->getArrayableItems($items) + )); + } + + /** + * Determine if the collection is empty or not. + * + * @phpstan-assert-if-true null $this->first() + * @phpstan-assert-if-true null $this->last() + * + * @phpstan-assert-if-false TValue $this->first() + * @phpstan-assert-if-false TValue $this->last() + * + * @return bool + */ + public function isEmpty() + { + return empty($this->items); + } + + /** + * Determine if the collection contains exactly one item. If a callback is provided, determine if exactly one item matches the condition. + * + * @param (callable(TValue, TKey): bool)|null $callback + * @return bool + */ + public function containsOneItem(?callable $callback = null): bool + { + if ($callback) { + return $this->filter($callback)->count() === 1; + } + + return $this->count() === 1; + } + + /** + * Join all items from the collection using a string. The final items can use a separate glue string. + * + * @param string $glue + * @param string $finalGlue + * @return string + */ + public function join($glue, $finalGlue = '') + { + if ($finalGlue === '') { + return $this->implode($glue); + } + + $count = $this->count(); + + if ($count === 0) { + return ''; + } + + if ($count === 1) { + return $this->last(); + } + + $collection = new static($this->items); + + $finalItem = $collection->pop(); + + return $collection->implode($glue).$finalGlue.$finalItem; + } + + /** + * Get the keys of the collection items. + * + * @return static + */ + public function keys() + { + return new static(array_keys($this->items)); + } + + /** + * Get the last item from the collection. + * + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault|(\Closure(): TLastDefault) $default + * @return TValue|TLastDefault + */ + public function last(?callable $callback = null, $default = null) + { + return Arr::last($this->items, $callback, $default); + } + + /** + * Get the values of a given key. + * + * @param string|int|array|null $value + * @param string|null $key + * @return static + */ + public function pluck($value, $key = null) + { + return new static(Arr::pluck($this->items, $value, $key)); + } + + /** + * Run a map over each of the items. + * + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static + */ + public function map(callable $callback) + { + return new static(Arr::map($this->items, $callback)); + } + + /** + * Run a dictionary map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> + */ + public function mapToDictionary(callable $callback) + { + $dictionary = []; + + foreach ($this->items as $key => $item) { + $pair = $callback($item, $key); + + $key = key($pair); + + $value = reset($pair); + + if (! isset($dictionary[$key])) { + $dictionary[$key] = []; + } + + $dictionary[$key][] = $value; + } + + return new static($dictionary); + } + + /** + * Run an associative map over each of the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static + */ + public function mapWithKeys(callable $callback) + { + return new static(Arr::mapWithKeys($this->items, $callback)); + } + + /** + * Merge the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function merge($items) + { + return new static(array_merge($this->items, $this->getArrayableItems($items))); + } + + /** + * Recursively merge the collection with the given items. + * + * @template TMergeRecursiveValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function mergeRecursive($items) + { + return new static(array_merge_recursive($this->items, $this->getArrayableItems($items))); + } + + /** + * Multiply the items in the collection by the multiplier. + * + * @param int $multiplier + * @return static + */ + public function multiply(int $multiplier) + { + $new = new static; + + for ($i = 0; $i < $multiplier; $i++) { + $new->push(...$this->items); + } + + return $new; + } + + /** + * Create a collection by using this collection for keys and another for its values. + * + * @template TCombineValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function combine($values) + { + return new static(array_combine($this->all(), $this->getArrayableItems($values))); + } + + /** + * Union the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function union($items) + { + return new static($this->items + $this->getArrayableItems($items)); + } + + /** + * Create a new collection consisting of every n-th element. + * + * @param int $step + * @param int $offset + * @return static + */ + public function nth($step, $offset = 0) + { + $new = []; + + $position = 0; + + foreach ($this->slice($offset)->items as $item) { + if ($position % $step === 0) { + $new[] = $item; + } + + $position++; + } + + return new static($new); + } + + /** + * Get the items with the specified keys. + * + * @param \Illuminate\Support\Enumerable|array|string|null $keys + * @return static + */ + public function only($keys) + { + if (is_null($keys)) { + return new static($this->items); + } + + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } + + $keys = is_array($keys) ? $keys : func_get_args(); + + return new static(Arr::only($this->items, $keys)); + } + + /** + * Select specific values from the items within the collection. + * + * @param \Illuminate\Support\Enumerable|array|string|null $keys + * @return static + */ + public function select($keys) + { + if (is_null($keys)) { + return new static($this->items); + } + + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } + + $keys = is_array($keys) ? $keys : func_get_args(); + + return new static(Arr::select($this->items, $keys)); + } + + /** + * Get and remove the last N items from the collection. + * + * @param int $count + * @return static|TValue|null + */ + public function pop($count = 1) + { + if ($count < 1) { + return new static; + } + + if ($count === 1) { + return array_pop($this->items); + } + + if ($this->isEmpty()) { + return new static; + } + + $results = []; + + $collectionCount = $this->count(); + + foreach (range(1, min($count, $collectionCount)) as $item) { + $results[] = array_pop($this->items); + } + + return new static($results); + } + + /** + * Push an item onto the beginning of the collection. + * + * @param TValue $value + * @param TKey $key + * @return $this + */ + public function prepend($value, $key = null) + { + $this->items = Arr::prepend($this->items, ...func_get_args()); + + return $this; + } + + /** + * Push one or more items onto the end of the collection. + * + * @param TValue ...$values + * @return $this + */ + public function push(...$values) + { + foreach ($values as $value) { + $this->items[] = $value; + } + + return $this; + } + + /** + * Prepend one or more items to the beginning of the collection. + * + * @param TValue ...$values + * @return $this + */ + public function unshift(...$values) + { + array_unshift($this->items, ...$values); + + return $this; + } + + /** + * Push all of the given items onto the collection. + * + * @template TConcatKey of array-key + * @template TConcatValue + * + * @param iterable $source + * @return static + */ + public function concat($source) + { + $result = new static($this); + + foreach ($source as $item) { + $result->push($item); + } + + return $result; + } + + /** + * Get and remove an item from the collection. + * + * @template TPullDefault + * + * @param TKey $key + * @param TPullDefault|(\Closure(): TPullDefault) $default + * @return TValue|TPullDefault + */ + public function pull($key, $default = null) + { + return Arr::pull($this->items, $key, $default); + } + + /** + * Put an item in the collection by key. + * + * @param TKey $key + * @param TValue $value + * @return $this + */ + public function put($key, $value) + { + $this->offsetSet($key, $value); + + return $this; + } + + /** + * Get one or a specified number of items randomly from the collection. + * + * @param (callable(self): int)|int|null $number + * @param bool $preserveKeys + * @return static|TValue + * + * @throws \InvalidArgumentException + */ + public function random($number = null, $preserveKeys = false) + { + if (is_null($number)) { + return Arr::random($this->items); + } + + if (is_callable($number)) { + return new static(Arr::random($this->items, $number($this), $preserveKeys)); + } + + return new static(Arr::random($this->items, $number, $preserveKeys)); + } + + /** + * Replace the collection items with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replace($items) + { + return new static(array_replace($this->items, $this->getArrayableItems($items))); + } + + /** + * Recursively replace the collection items with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replaceRecursive($items) + { + return new static(array_replace_recursive($this->items, $this->getArrayableItems($items))); + } + + /** + * Reverse items order. + * + * @return static + */ + public function reverse() + { + return new static(array_reverse($this->items, true)); + } + + /** + * Search the collection for a given value and return the corresponding key if successful. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TKey|false + */ + public function search($value, $strict = false) + { + if (! $this->useAsCallable($value)) { + return array_search($value, $this->items, $strict); + } + + foreach ($this->items as $key => $item) { + if ($value($item, $key)) { + return $key; + } + } + + return false; + } + + /** + * Get the item before the given item. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TValue|null + */ + public function before($value, $strict = false) + { + $key = $this->search($value, $strict); + + if ($key === false) { + return null; + } + + $position = ($keys = $this->keys())->search($key); + + if ($position === 0) { + return null; + } + + return $this->get($keys->get($position - 1)); + } + + /** + * Get the item after the given item. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TValue|null + */ + public function after($value, $strict = false) + { + $key = $this->search($value, $strict); + + if ($key === false) { + return null; + } + + $position = ($keys = $this->keys())->search($key); + + if ($position === $keys->count() - 1) { + return null; + } + + return $this->get($keys->get($position + 1)); + } + + /** + * Get and remove the first N items from the collection. + * + * @param int<0, max> $count + * @return static|TValue|null + * + * @throws \InvalidArgumentException + */ + public function shift($count = 1) + { + if ($count < 0) { + throw new InvalidArgumentException('Number of shifted items may not be less than zero.'); + } + + if ($this->isEmpty()) { + return null; + } + + if ($count === 0) { + return new static; + } + + if ($count === 1) { + return array_shift($this->items); + } + + $results = []; + + $collectionCount = $this->count(); + + foreach (range(1, min($count, $collectionCount)) as $item) { + $results[] = array_shift($this->items); + } + + return new static($results); + } + + /** + * Shuffle the items in the collection. + * + * @return static + */ + public function shuffle() + { + return new static(Arr::shuffle($this->items)); + } + + /** + * Create chunks representing a "sliding window" view of the items in the collection. + * + * @param int $size + * @param int $step + * @return static + */ + public function sliding($size = 2, $step = 1) + { + $chunks = floor(($this->count() - $size) / $step) + 1; + + return static::times($chunks, fn ($number) => $this->slice(($number - 1) * $step, $size)); + } + + /** + * Skip the first {$count} items. + * + * @param int $count + * @return static + */ + public function skip($count) + { + return $this->slice($count); + } + + /** + * Skip items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skipUntil($value) + { + return new static($this->lazy()->skipUntil($value)->all()); + } + + /** + * Skip items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skipWhile($value) + { + return new static($this->lazy()->skipWhile($value)->all()); + } + + /** + * Slice the underlying collection array. + * + * @param int $offset + * @param int|null $length + * @return static + */ + public function slice($offset, $length = null) + { + return new static(array_slice($this->items, $offset, $length, true)); + } + + /** + * Split a collection into a certain number of groups. + * + * @param int $numberOfGroups + * @return static + */ + public function split($numberOfGroups) + { + if ($this->isEmpty()) { + return new static; + } + + $groups = new static; + + $groupSize = floor($this->count() / $numberOfGroups); + + $remain = $this->count() % $numberOfGroups; + + $start = 0; + + for ($i = 0; $i < $numberOfGroups; $i++) { + $size = $groupSize; + + if ($i < $remain) { + $size++; + } + + if ($size) { + $groups->push(new static(array_slice($this->items, $start, $size))); + + $start += $size; + } + } + + return $groups; + } + + /** + * Split a collection into a certain number of groups, and fill the first groups completely. + * + * @param int $numberOfGroups + * @return static + */ + public function splitIn($numberOfGroups) + { + return $this->chunk((int) ceil($this->count() / $numberOfGroups)); + } + + /** + * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException + */ + public function sole($key = null, $operator = null, $value = null) + { + $filter = func_num_args() > 1 + ? $this->operatorForWhere(...func_get_args()) + : $key; + + $items = $this->unless($filter == null)->filter($filter); + + $count = $items->count(); + + if ($count === 0) { + throw new ItemNotFoundException; + } + + if ($count > 1) { + throw new MultipleItemsFoundException($count); + } + + return $items->first(); + } + + /** + * Get the first item in the collection but throw an exception if no matching items exist. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Illuminate\Support\ItemNotFoundException + */ + public function firstOrFail($key = null, $operator = null, $value = null) + { + $filter = func_num_args() > 1 + ? $this->operatorForWhere(...func_get_args()) + : $key; + + $placeholder = new stdClass(); + + $item = $this->first($filter, $placeholder); + + if ($item === $placeholder) { + throw new ItemNotFoundException; + } + + return $item; + } + + /** + * Chunk the collection into chunks of the given size. + * + * @param int $size + * @param bool $preserveKeys + * @return ($preserveKeys is true ? static : static>) + */ + public function chunk($size, $preserveKeys = true) + { + if ($size <= 0) { + return new static; + } + + $chunks = []; + + foreach (array_chunk($this->items, $size, $preserveKeys) as $chunk) { + $chunks[] = new static($chunk); + } + + return new static($chunks); + } + + /** + * Chunk the collection into chunks with a callback. + * + * @param callable(TValue, TKey, static): bool $callback + * @return static> + */ + public function chunkWhile(callable $callback) + { + return new static( + $this->lazy()->chunkWhile($callback)->mapInto(static::class) + ); + } + + /** + * Sort through each item with a callback. + * + * @param (callable(TValue, TValue): int)|null|int $callback + * @return static + */ + public function sort($callback = null) + { + $items = $this->items; + + $callback && is_callable($callback) + ? uasort($items, $callback) + : asort($items, $callback ?? SORT_REGULAR); + + return new static($items); + } + + /** + * Sort items in descending order. + * + * @param int $options + * @return static + */ + public function sortDesc($options = SORT_REGULAR) + { + $items = $this->items; + + arsort($items, $options); + + return new static($items); + } + + /** + * Sort the collection using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @param bool $descending + * @return static + */ + public function sortBy($callback, $options = SORT_REGULAR, $descending = false) + { + if (is_array($callback) && ! is_callable($callback)) { + return $this->sortByMany($callback, $options); + } + + $results = []; + + $callback = $this->valueRetriever($callback); + + // First we will loop through the items and get the comparator from a callback + // function which we were given. Then, we will sort the returned values and + // grab all the corresponding values for the sorted keys from this array. + foreach ($this->items as $key => $value) { + $results[$key] = $callback($value, $key); + } + + $descending ? arsort($results, $options) + : asort($results, $options); + + // Once we have sorted all of the keys in the array, we will loop through them + // and grab the corresponding model so we can set the underlying items list + // to the sorted version. Then we'll just return the collection instance. + foreach (array_keys($results) as $key) { + $results[$key] = $this->items[$key]; + } + + return new static($results); + } + + /** + * Sort the collection using multiple comparisons. + * + * @param array $comparisons + * @param int $options + * @return static + */ + protected function sortByMany(array $comparisons = [], int $options = SORT_REGULAR) + { + $items = $this->items; + + uasort($items, function ($a, $b) use ($comparisons, $options) { + foreach ($comparisons as $comparison) { + $comparison = Arr::wrap($comparison); + + $prop = $comparison[0]; + + $ascending = Arr::get($comparison, 1, true) === true || + Arr::get($comparison, 1, true) === 'asc'; + + if (! is_string($prop) && is_callable($prop)) { + $result = $prop($a, $b); + } else { + $values = [data_get($a, $prop), data_get($b, $prop)]; + + if (! $ascending) { + $values = array_reverse($values); + } + + if (($options & SORT_FLAG_CASE) === SORT_FLAG_CASE) { + if (($options & SORT_NATURAL) === SORT_NATURAL) { + $result = strnatcasecmp($values[0], $values[1]); + } else { + $result = strcasecmp($values[0], $values[1]); + } + } else { + $result = match ($options) { + SORT_NUMERIC => intval($values[0]) <=> intval($values[1]), + SORT_STRING => strcmp($values[0], $values[1]), + SORT_NATURAL => strnatcmp((string) $values[0], (string) $values[1]), + SORT_LOCALE_STRING => strcoll($values[0], $values[1]), + default => $values[0] <=> $values[1], + }; + } + } + + if ($result === 0) { + continue; + } + + return $result; + } + }); + + return new static($items); + } + + /** + * Sort the collection in descending order using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @return static + */ + public function sortByDesc($callback, $options = SORT_REGULAR) + { + if (is_array($callback) && ! is_callable($callback)) { + foreach ($callback as $index => $key) { + $comparison = Arr::wrap($key); + + $comparison[1] = 'desc'; + + $callback[$index] = $comparison; + } + } + + return $this->sortBy($callback, $options, true); + } + + /** + * Sort the collection keys. + * + * @param int $options + * @param bool $descending + * @return static + */ + public function sortKeys($options = SORT_REGULAR, $descending = false) + { + $items = $this->items; + + $descending ? krsort($items, $options) : ksort($items, $options); + + return new static($items); + } + + /** + * Sort the collection keys in descending order. + * + * @param int $options + * @return static + */ + public function sortKeysDesc($options = SORT_REGULAR) + { + return $this->sortKeys($options, true); + } + + /** + * Sort the collection keys using a callback. + * + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function sortKeysUsing(callable $callback) + { + $items = $this->items; + + uksort($items, $callback); + + return new static($items); + } + + /** + * Splice a portion of the underlying collection array. + * + * @param int $offset + * @param int|null $length + * @param array $replacement + * @return static + */ + public function splice($offset, $length = null, $replacement = []) + { + if (func_num_args() === 1) { + return new static(array_splice($this->items, $offset)); + } + + return new static(array_splice($this->items, $offset, $length, $this->getArrayableItems($replacement))); + } + + /** + * Take the first or last {$limit} items. + * + * @param int $limit + * @return static + */ + public function take($limit) + { + if ($limit < 0) { + return $this->slice($limit, abs($limit)); + } + + return $this->slice(0, $limit); + } + + /** + * Take items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function takeUntil($value) + { + return new static($this->lazy()->takeUntil($value)->all()); + } + + /** + * Take items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function takeWhile($value) + { + return new static($this->lazy()->takeWhile($value)->all()); + } + + /** + * Transform each item in the collection using a callback. + * + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return $this + * + * @phpstan-this-out static + */ + public function transform(callable $callback) + { + $this->items = $this->map($callback)->all(); + + return $this; + } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @return static + */ + public function dot() + { + return new static(Arr::dot($this->all())); + } + + /** + * Convert a flatten "dot" notation array into an expanded array. + * + * @return static + */ + public function undot() + { + return new static(Arr::undot($this->all())); + } + + /** + * Return only unique items from the collection array. + * + * @param (callable(TValue, TKey): mixed)|string|null $key + * @param bool $strict + * @return static + */ + public function unique($key = null, $strict = false) + { + if (is_null($key) && $strict === false) { + return new static(array_unique($this->items, SORT_REGULAR)); + } + + $callback = $this->valueRetriever($key); + + $exists = []; + + return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { + if (in_array($id = $callback($item, $key), $exists, $strict)) { + return true; + } + + $exists[] = $id; + }); + } + + /** + * Reset the keys on the underlying array. + * + * @return static + */ + public function values() + { + return new static(array_values($this->items)); + } + + /** + * Zip the collection together with one or more arrays. + * + * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); + * => [[1, 4], [2, 5], [3, 6]] + * + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return static> + */ + public function zip($items) + { + $arrayableItems = array_map(fn ($items) => $this->getArrayableItems($items), func_get_args()); + + $params = array_merge([fn () => new static(func_get_args()), $this->items], $arrayableItems); + + return new static(array_map(...$params)); + } + + /** + * Pad collection to the specified length with a value. + * + * @template TPadValue + * + * @param int $size + * @param TPadValue $value + * @return static + */ + public function pad($size, $value) + { + return new static(array_pad($this->items, $size, $value)); + } + + /** + * Get an iterator for the items. + * + * @return \ArrayIterator + */ + public function getIterator(): Traversable + { + return new ArrayIterator($this->items); + } + + /** + * Count the number of items in the collection. + * + * @return int<0, max> + */ + public function count(): int + { + return count($this->items); + } + + /** + * Count the number of items in the collection by a field or using a callback. + * + * @param (callable(TValue, TKey): array-key)|string|null $countBy + * @return static + */ + public function countBy($countBy = null) + { + return new static($this->lazy()->countBy($countBy)->all()); + } + + /** + * Add an item to the collection. + * + * @param TValue $item + * @return $this + */ + public function add($item) + { + $this->items[] = $item; + + return $this; + } + + /** + * Get a base Support collection instance from this collection. + * + * @return \Illuminate\Support\Collection + */ + public function toBase() + { + return new self($this); + } + + /** + * Determine if an item exists at an offset. + * + * @param TKey $key + * @return bool + */ + public function offsetExists($key): bool + { + return isset($this->items[$key]); + } + + /** + * Get an item at a given offset. + * + * @param TKey $key + * @return TValue + */ + public function offsetGet($key): mixed + { + return $this->items[$key]; + } + + /** + * Set the item at a given offset. + * + * @param TKey|null $key + * @param TValue $value + * @return void + */ + public function offsetSet($key, $value): void + { + if (is_null($key)) { + $this->items[] = $value; + } else { + $this->items[$key] = $value; + } + } + + /** + * Unset the item at a given offset. + * + * @param TKey $key + * @return void + */ + public function offsetUnset($key): void + { + unset($this->items[$key]); + } +} diff --git a/vendor/illuminate/collections/Enumerable.php b/vendor/illuminate/collections/Enumerable.php new file mode 100644 index 0000000..78187b7 --- /dev/null +++ b/vendor/illuminate/collections/Enumerable.php @@ -0,0 +1,1312 @@ + + * @extends \IteratorAggregate + */ +interface Enumerable extends Arrayable, Countable, IteratorAggregate, Jsonable, JsonSerializable +{ + /** + * Create a new collection instance if the value isn't one already. + * + * @template TMakeKey of array-key + * @template TMakeValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items + * @return static + */ + public static function make($items = []); + + /** + * Create a new instance by invoking the callback a given amount of times. + * + * @param int $number + * @param callable|null $callback + * @return static + */ + public static function times($number, ?callable $callback = null); + + /** + * Create a collection with the given range. + * + * @param int $from + * @param int $to + * @param int $step + * @return static + */ + public static function range($from, $to, $step = 1); + + /** + * Wrap the given value in a collection if applicable. + * + * @template TWrapValue + * + * @param iterable|TWrapValue $value + * @return static + */ + public static function wrap($value); + + /** + * Get the underlying items from the given collection if applicable. + * + * @template TUnwrapKey of array-key + * @template TUnwrapValue + * + * @param array|static $value + * @return array + */ + public static function unwrap($value); + + /** + * Create a new instance with no items. + * + * @return static + */ + public static function empty(); + + /** + * Get all items in the enumerable. + * + * @return array + */ + public function all(); + + /** + * Alias for the "avg" method. + * + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null + */ + public function average($callback = null); + + /** + * Get the median of a given key. + * + * @param string|array|null $key + * @return float|int|null + */ + public function median($key = null); + + /** + * Get the mode of a given key. + * + * @param string|array|null $key + * @return array|null + */ + public function mode($key = null); + + /** + * Collapse the items into a single enumerable. + * + * @return static + */ + public function collapse(); + + /** + * Alias for the "contains" method. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function some($key, $operator = null, $value = null); + + /** + * Determine if an item exists, using strict comparison. + * + * @param (callable(TValue): bool)|TValue|array-key $key + * @param TValue|null $value + * @return bool + */ + public function containsStrict($key, $value = null); + + /** + * Get the average value of a given key. + * + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null + */ + public function avg($callback = null); + + /** + * Determine if an item exists in the enumerable. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function contains($key, $operator = null, $value = null); + + /** + * Determine if an item is not contained in the collection. + * + * @param mixed $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function doesntContain($key, $operator = null, $value = null); + + /** + * Cross join with the given lists, returning all possible permutations. + * + * @template TCrossJoinKey + * @template TCrossJoinValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$lists + * @return static> + */ + public function crossJoin(...$lists); + + /** + * Dump the collection and end the script. + * + * @param mixed ...$args + * @return never + */ + public function dd(...$args); + + /** + * Dump the collection. + * + * @param mixed ...$args + * @return $this + */ + public function dump(...$args); + + /** + * Get the items that are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diff($items); + + /** + * Get the items that are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function diffUsing($items, callable $callback); + + /** + * Get the items whose keys and values are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diffAssoc($items); + + /** + * Get the items whose keys and values are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diffAssocUsing($items, callable $callback); + + /** + * Get the items whose keys are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diffKeys($items); + + /** + * Get the items whose keys are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diffKeysUsing($items, callable $callback); + + /** + * Retrieve duplicate items. + * + * @param (callable(TValue): bool)|string|null $callback + * @param bool $strict + * @return static + */ + public function duplicates($callback = null, $strict = false); + + /** + * Retrieve duplicate items using strict comparison. + * + * @param (callable(TValue): bool)|string|null $callback + * @return static + */ + public function duplicatesStrict($callback = null); + + /** + * Execute a callback over each item. + * + * @param callable(TValue, TKey): mixed $callback + * @return $this + */ + public function each(callable $callback); + + /** + * Execute a callback over each nested chunk of items. + * + * @param callable $callback + * @return static + */ + public function eachSpread(callable $callback); + + /** + * Determine if all items pass the given truth test. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function every($key, $operator = null, $value = null); + + /** + * Get all items except for those with the specified keys. + * + * @param \Illuminate\Support\Enumerable|array $keys + * @return static + */ + public function except($keys); + + /** + * Run a filter over each of the items. + * + * @param (callable(TValue): bool)|null $callback + * @return static + */ + public function filter(?callable $callback = null); + + /** + * Apply the callback if the given "value" is (or resolves to) truthy. + * + * @template TWhenReturnType as null + * + * @param bool $value + * @param (callable($this): TWhenReturnType)|null $callback + * @param (callable($this): TWhenReturnType)|null $default + * @return $this|TWhenReturnType + */ + public function when($value, ?callable $callback = null, ?callable $default = null); + + /** + * Apply the callback if the collection is empty. + * + * @template TWhenEmptyReturnType + * + * @param (callable($this): TWhenEmptyReturnType) $callback + * @param (callable($this): TWhenEmptyReturnType)|null $default + * @return $this|TWhenEmptyReturnType + */ + public function whenEmpty(callable $callback, ?callable $default = null); + + /** + * Apply the callback if the collection is not empty. + * + * @template TWhenNotEmptyReturnType + * + * @param callable($this): TWhenNotEmptyReturnType $callback + * @param (callable($this): TWhenNotEmptyReturnType)|null $default + * @return $this|TWhenNotEmptyReturnType + */ + public function whenNotEmpty(callable $callback, ?callable $default = null); + + /** + * Apply the callback if the given "value" is (or resolves to) falsy. + * + * @template TUnlessReturnType + * + * @param bool $value + * @param (callable($this): TUnlessReturnType) $callback + * @param (callable($this): TUnlessReturnType)|null $default + * @return $this|TUnlessReturnType + */ + public function unless($value, callable $callback, ?callable $default = null); + + /** + * Apply the callback unless the collection is empty. + * + * @template TUnlessEmptyReturnType + * + * @param callable($this): TUnlessEmptyReturnType $callback + * @param (callable($this): TUnlessEmptyReturnType)|null $default + * @return $this|TUnlessEmptyReturnType + */ + public function unlessEmpty(callable $callback, ?callable $default = null); + + /** + * Apply the callback unless the collection is not empty. + * + * @template TUnlessNotEmptyReturnType + * + * @param callable($this): TUnlessNotEmptyReturnType $callback + * @param (callable($this): TUnlessNotEmptyReturnType)|null $default + * @return $this|TUnlessNotEmptyReturnType + */ + public function unlessNotEmpty(callable $callback, ?callable $default = null); + + /** + * Filter items by the given key value pair. + * + * @param string $key + * @param mixed $operator + * @param mixed $value + * @return static + */ + public function where($key, $operator = null, $value = null); + + /** + * Filter items where the value for the given key is null. + * + * @param string|null $key + * @return static + */ + public function whereNull($key = null); + + /** + * Filter items where the value for the given key is not null. + * + * @param string|null $key + * @return static + */ + public function whereNotNull($key = null); + + /** + * Filter items by the given key value pair using strict comparison. + * + * @param string $key + * @param mixed $value + * @return static + */ + public function whereStrict($key, $value); + + /** + * Filter items by the given key value pair. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @param bool $strict + * @return static + */ + public function whereIn($key, $values, $strict = false); + + /** + * Filter items by the given key value pair using strict comparison. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereInStrict($key, $values); + + /** + * Filter items such that the value of the given key is between the given values. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereBetween($key, $values); + + /** + * Filter items such that the value of the given key is not between the given values. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereNotBetween($key, $values); + + /** + * Filter items by the given key value pair. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @param bool $strict + * @return static + */ + public function whereNotIn($key, $values, $strict = false); + + /** + * Filter items by the given key value pair using strict comparison. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereNotInStrict($key, $values); + + /** + * Filter the items, removing any items that don't match the given type(s). + * + * @template TWhereInstanceOf + * + * @param class-string|array> $type + * @return static + */ + public function whereInstanceOf($type); + + /** + * Get the first item from the enumerable passing the given truth test. + * + * @template TFirstDefault + * + * @param (callable(TValue,TKey): bool)|null $callback + * @param TFirstDefault|(\Closure(): TFirstDefault) $default + * @return TValue|TFirstDefault + */ + public function first(?callable $callback = null, $default = null); + + /** + * Get the first item by the given key value pair. + * + * @param string $key + * @param mixed $operator + * @param mixed $value + * @return TValue|null + */ + public function firstWhere($key, $operator = null, $value = null); + + /** + * Get a flattened array of the items in the collection. + * + * @param int $depth + * @return static + */ + public function flatten($depth = INF); + + /** + * Flip the values with their keys. + * + * @return static + */ + public function flip(); + + /** + * Get an item from the collection by key. + * + * @template TGetDefault + * + * @param TKey $key + * @param TGetDefault|(\Closure(): TGetDefault) $default + * @return TValue|TGetDefault + */ + public function get($key, $default = null); + + /** + * Group an associative array by a field or using a callback. + * + * @template TGroupKey of array-key + * + * @param (callable(TValue, TKey): TGroupKey)|array|string $groupBy + * @param bool $preserveKeys + * @return static<($groupBy is string ? array-key : ($groupBy is array ? array-key : TGroupKey)), static<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)>> + */ + public function groupBy($groupBy, $preserveKeys = false); + + /** + * Key an associative array by a field or using a callback. + * + * @template TNewKey of array-key + * + * @param (callable(TValue, TKey): TNewKey)|array|string $keyBy + * @return static<($keyBy is string ? array-key : ($keyBy is array ? array-key : TNewKey)), TValue> + */ + public function keyBy($keyBy); + + /** + * Determine if an item exists in the collection by key. + * + * @param TKey|array $key + * @return bool + */ + public function has($key); + + /** + * Determine if any of the keys exist in the collection. + * + * @param mixed $key + * @return bool + */ + public function hasAny($key); + + /** + * Concatenate values of a given key as a string. + * + * @param (callable(TValue, TKey): mixed)|string $value + * @param string|null $glue + * @return string + */ + public function implode($value, $glue = null); + + /** + * Intersect the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersect($items); + + /** + * Intersect the collection with the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersectUsing($items, callable $callback); + + /** + * Intersect the collection with the given items with additional index check. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersectAssoc($items); + + /** + * Intersect the collection with the given items with additional index check, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersectAssocUsing($items, callable $callback); + + /** + * Intersect the collection with the given items by key. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersectByKeys($items); + + /** + * Determine if the collection is empty or not. + * + * @return bool + */ + public function isEmpty(); + + /** + * Determine if the collection is not empty. + * + * @return bool + */ + public function isNotEmpty(); + + /** + * Determine if the collection contains a single item. + * + * @return bool + */ + public function containsOneItem(); + + /** + * Join all items from the collection using a string. The final items can use a separate glue string. + * + * @param string $glue + * @param string $finalGlue + * @return string + */ + public function join($glue, $finalGlue = ''); + + /** + * Get the keys of the collection items. + * + * @return static + */ + public function keys(); + + /** + * Get the last item from the collection. + * + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault|(\Closure(): TLastDefault) $default + * @return TValue|TLastDefault + */ + public function last(?callable $callback = null, $default = null); + + /** + * Run a map over each of the items. + * + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static + */ + public function map(callable $callback); + + /** + * Run a map over each nested chunk of items. + * + * @param callable $callback + * @return static + */ + public function mapSpread(callable $callback); + + /** + * Run a dictionary map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> + */ + public function mapToDictionary(callable $callback); + + /** + * Run a grouping map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapToGroupsKey of array-key + * @template TMapToGroupsValue + * + * @param callable(TValue, TKey): array $callback + * @return static> + */ + public function mapToGroups(callable $callback); + + /** + * Run an associative map over each of the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static + */ + public function mapWithKeys(callable $callback); + + /** + * Map a collection and flatten the result by a single level. + * + * @template TFlatMapKey of array-key + * @template TFlatMapValue + * + * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback + * @return static + */ + public function flatMap(callable $callback); + + /** + * Map the values into a new class. + * + * @template TMapIntoValue + * + * @param class-string $class + * @return static + */ + public function mapInto($class); + + /** + * Merge the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function merge($items); + + /** + * Recursively merge the collection with the given items. + * + * @template TMergeRecursiveValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function mergeRecursive($items); + + /** + * Create a collection by using this collection for keys and another for its values. + * + * @template TCombineValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function combine($values); + + /** + * Union the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function union($items); + + /** + * Get the min value of a given key. + * + * @param (callable(TValue):mixed)|string|null $callback + * @return mixed + */ + public function min($callback = null); + + /** + * Get the max value of a given key. + * + * @param (callable(TValue):mixed)|string|null $callback + * @return mixed + */ + public function max($callback = null); + + /** + * Create a new collection consisting of every n-th element. + * + * @param int $step + * @param int $offset + * @return static + */ + public function nth($step, $offset = 0); + + /** + * Get the items with the specified keys. + * + * @param \Illuminate\Support\Enumerable|array|string $keys + * @return static + */ + public function only($keys); + + /** + * "Paginate" the collection by slicing it into a smaller collection. + * + * @param int $page + * @param int $perPage + * @return static + */ + public function forPage($page, $perPage); + + /** + * Partition the collection into two arrays using the given callback or key. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return static, static> + */ + public function partition($key, $operator = null, $value = null); + + /** + * Push all of the given items onto the collection. + * + * @template TConcatKey of array-key + * @template TConcatValue + * + * @param iterable $source + * @return static + */ + public function concat($source); + + /** + * Get one or a specified number of items randomly from the collection. + * + * @param int|null $number + * @return static|TValue + * + * @throws \InvalidArgumentException + */ + public function random($number = null); + + /** + * Reduce the collection to a single value. + * + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback + * @param TReduceInitial $initial + * @return TReduceInitial|TReduceReturnType + */ + public function reduce(callable $callback, $initial = null); + + /** + * Reduce the collection to multiple aggregate values. + * + * @param callable $callback + * @param mixed ...$initial + * @return array + * + * @throws \UnexpectedValueException + */ + public function reduceSpread(callable $callback, ...$initial); + + /** + * Replace the collection items with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replace($items); + + /** + * Recursively replace the collection items with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replaceRecursive($items); + + /** + * Reverse items order. + * + * @return static + */ + public function reverse(); + + /** + * Search the collection for a given value and return the corresponding key if successful. + * + * @param TValue|callable(TValue,TKey): bool $value + * @param bool $strict + * @return TKey|bool + */ + public function search($value, $strict = false); + + /** + * Get the item before the given item. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TValue|null + */ + public function before($value, $strict = false); + + /** + * Get the item after the given item. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TValue|null + */ + public function after($value, $strict = false); + + /** + * Shuffle the items in the collection. + * + * @return static + */ + public function shuffle(); + + /** + * Create chunks representing a "sliding window" view of the items in the collection. + * + * @param int $size + * @param int $step + * @return static + */ + public function sliding($size = 2, $step = 1); + + /** + * Skip the first {$count} items. + * + * @param int $count + * @return static + */ + public function skip($count); + + /** + * Skip items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skipUntil($value); + + /** + * Skip items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skipWhile($value); + + /** + * Get a slice of items from the enumerable. + * + * @param int $offset + * @param int|null $length + * @return static + */ + public function slice($offset, $length = null); + + /** + * Split a collection into a certain number of groups. + * + * @param int $numberOfGroups + * @return static + */ + public function split($numberOfGroups); + + /** + * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException + */ + public function sole($key = null, $operator = null, $value = null); + + /** + * Get the first item in the collection but throw an exception if no matching items exist. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Illuminate\Support\ItemNotFoundException + */ + public function firstOrFail($key = null, $operator = null, $value = null); + + /** + * Chunk the collection into chunks of the given size. + * + * @param int $size + * @return static + */ + public function chunk($size); + + /** + * Chunk the collection into chunks with a callback. + * + * @param callable(TValue, TKey, static): bool $callback + * @return static> + */ + public function chunkWhile(callable $callback); + + /** + * Split a collection into a certain number of groups, and fill the first groups completely. + * + * @param int $numberOfGroups + * @return static + */ + public function splitIn($numberOfGroups); + + /** + * Sort through each item with a callback. + * + * @param (callable(TValue, TValue): int)|null|int $callback + * @return static + */ + public function sort($callback = null); + + /** + * Sort items in descending order. + * + * @param int $options + * @return static + */ + public function sortDesc($options = SORT_REGULAR); + + /** + * Sort the collection using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @param bool $descending + * @return static + */ + public function sortBy($callback, $options = SORT_REGULAR, $descending = false); + + /** + * Sort the collection in descending order using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @return static + */ + public function sortByDesc($callback, $options = SORT_REGULAR); + + /** + * Sort the collection keys. + * + * @param int $options + * @param bool $descending + * @return static + */ + public function sortKeys($options = SORT_REGULAR, $descending = false); + + /** + * Sort the collection keys in descending order. + * + * @param int $options + * @return static + */ + public function sortKeysDesc($options = SORT_REGULAR); + + /** + * Sort the collection keys using a callback. + * + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function sortKeysUsing(callable $callback); + + /** + * Get the sum of the given values. + * + * @param (callable(TValue): mixed)|string|null $callback + * @return mixed + */ + public function sum($callback = null); + + /** + * Take the first or last {$limit} items. + * + * @param int $limit + * @return static + */ + public function take($limit); + + /** + * Take items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function takeUntil($value); + + /** + * Take items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function takeWhile($value); + + /** + * Pass the collection to the given callback and then return it. + * + * @param callable(TValue): mixed $callback + * @return $this + */ + public function tap(callable $callback); + + /** + * Pass the enumerable to the given callback and return the result. + * + * @template TPipeReturnType + * + * @param callable($this): TPipeReturnType $callback + * @return TPipeReturnType + */ + public function pipe(callable $callback); + + /** + * Pass the collection into a new class. + * + * @template TPipeIntoValue + * + * @param class-string $class + * @return TPipeIntoValue + */ + public function pipeInto($class); + + /** + * Pass the collection through a series of callable pipes and return the result. + * + * @param array $pipes + * @return mixed + */ + public function pipeThrough($pipes); + + /** + * Get the values of a given key. + * + * @param string|array $value + * @param string|null $key + * @return static + */ + public function pluck($value, $key = null); + + /** + * Create a collection of all elements that do not pass a given truth test. + * + * @param (callable(TValue, TKey): bool)|bool|TValue $callback + * @return static + */ + public function reject($callback = true); + + /** + * Convert a flatten "dot" notation array into an expanded array. + * + * @return static + */ + public function undot(); + + /** + * Return only unique items from the collection array. + * + * @param (callable(TValue, TKey): mixed)|string|null $key + * @param bool $strict + * @return static + */ + public function unique($key = null, $strict = false); + + /** + * Return only unique items from the collection array using strict comparison. + * + * @param (callable(TValue, TKey): mixed)|string|null $key + * @return static + */ + public function uniqueStrict($key = null); + + /** + * Reset the keys on the underlying array. + * + * @return static + */ + public function values(); + + /** + * Pad collection to the specified length with a value. + * + * @template TPadValue + * + * @param int $size + * @param TPadValue $value + * @return static + */ + public function pad($size, $value); + + /** + * Get the values iterator. + * + * @return \Traversable + */ + public function getIterator(): Traversable; + + /** + * Count the number of items in the collection. + * + * @return int + */ + public function count(): int; + + /** + * Count the number of items in the collection by a field or using a callback. + * + * @param (callable(TValue, TKey): array-key)|string|null $countBy + * @return static + */ + public function countBy($countBy = null); + + /** + * Zip the collection together with one or more arrays. + * + * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); + * => [[1, 4], [2, 5], [3, 6]] + * + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return static> + */ + public function zip($items); + + /** + * Collect the values into a collection. + * + * @return \Illuminate\Support\Collection + */ + public function collect(); + + /** + * Get the collection of items as a plain array. + * + * @return array + */ + public function toArray(); + + /** + * Convert the object into something JSON serializable. + * + * @return mixed + */ + public function jsonSerialize(): mixed; + + /** + * Get the collection of items as JSON. + * + * @param int $options + * @return string + */ + public function toJson($options = 0); + + /** + * Get a CachingIterator instance. + * + * @param int $flags + * @return \CachingIterator + */ + public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING); + + /** + * Convert the collection to its string representation. + * + * @return string + */ + public function __toString(); + + /** + * Indicate that the model's string representation should be escaped when __toString is invoked. + * + * @param bool $escape + * @return $this + */ + public function escapeWhenCastingToString($escape = true); + + /** + * Add a method to the list of proxied methods. + * + * @param string $method + * @return void + */ + public static function proxy($method); + + /** + * Dynamically access collection proxies. + * + * @param string $key + * @return mixed + * + * @throws \Exception + */ + public function __get($key); +} diff --git a/vendor/tightenco/collect/src/Collect/Support/HigherOrderCollectionProxy.php b/vendor/illuminate/collections/HigherOrderCollectionProxy.php similarity index 72% rename from vendor/tightenco/collect/src/Collect/Support/HigherOrderCollectionProxy.php rename to vendor/illuminate/collections/HigherOrderCollectionProxy.php index 01ac43f..035d0fd 100644 --- a/vendor/tightenco/collect/src/Collect/Support/HigherOrderCollectionProxy.php +++ b/vendor/illuminate/collections/HigherOrderCollectionProxy.php @@ -1,16 +1,21 @@ + * @mixin TValue */ class HigherOrderCollectionProxy { /** * The collection being operated on. * - * @var \Tightenco\Collect\Support\Enumerable + * @var \Illuminate\Support\Enumerable */ protected $collection; @@ -24,9 +29,8 @@ class HigherOrderCollectionProxy /** * Create a new proxy instance. * - * @param \Tightenco\Collect\Support\Enumerable $collection + * @param \Illuminate\Support\Enumerable $collection * @param string $method - * @return void */ public function __construct(Enumerable $collection, $method) { @@ -57,7 +61,9 @@ class HigherOrderCollectionProxy public function __call($method, $parameters) { return $this->collection->{$this->method}(function ($value) use ($method, $parameters) { - return $value->{$method}(...$parameters); + return is_string($value) + ? $value::{$method}(...$parameters) + : $value->{$method}(...$parameters); }); } } diff --git a/vendor/illuminate/collections/ItemNotFoundException.php b/vendor/illuminate/collections/ItemNotFoundException.php new file mode 100644 index 0000000..05a51d9 --- /dev/null +++ b/vendor/illuminate/collections/ItemNotFoundException.php @@ -0,0 +1,9 @@ + + */ +class LazyCollection implements CanBeEscapedWhenCastToString, Enumerable +{ + /** + * @use \Illuminate\Support\Traits\EnumeratesValues + */ + use EnumeratesValues, Macroable; + + /** + * The source from which to generate items. + * + * @var (Closure(): \Generator)|static|array + */ + public $source; + + /** + * Create a new lazy collection instance. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $source + */ + public function __construct($source = null) + { + if ($source instanceof Closure || $source instanceof self) { + $this->source = $source; + } elseif (is_null($source)) { + $this->source = static::empty(); + } elseif ($source instanceof Generator) { + throw new InvalidArgumentException( + 'Generators should not be passed directly to LazyCollection. Instead, pass a generator function.' + ); + } else { + $this->source = $this->getArrayableItems($source); + } + } + + /** + * Create a new collection instance if the value isn't one already. + * + * @template TMakeKey of array-key + * @template TMakeValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|(Closure(): \Generator)|self|array|null $items + * @return static + */ + public static function make($items = []) + { + return new static($items); + } + + /** + * Create a collection with the given range. + * + * @param int $from + * @param int $to + * @param int $step + * @return static + */ + public static function range($from, $to, $step = 1) + { + if ($step == 0) { + throw new InvalidArgumentException('Step value cannot be zero.'); + } + + return new static(function () use ($from, $to, $step) { + if ($from <= $to) { + for (; $from <= $to; $from += abs($step)) { + yield $from; + } + } else { + for (; $from >= $to; $from -= abs($step)) { + yield $from; + } + } + }); + } + + /** + * Get all items in the enumerable. + * + * @return array + */ + public function all() + { + if (is_array($this->source)) { + return $this->source; + } + + return iterator_to_array($this->getIterator()); + } + + /** + * Eager load all items into a new lazy collection backed by an array. + * + * @return static + */ + public function eager() + { + return new static($this->all()); + } + + /** + * Cache values as they're enumerated. + * + * @return static + */ + public function remember() + { + $iterator = $this->getIterator(); + + $iteratorIndex = 0; + + $cache = []; + + return new static(function () use ($iterator, &$iteratorIndex, &$cache) { + for ($index = 0; true; $index++) { + if (array_key_exists($index, $cache)) { + yield $cache[$index][0] => $cache[$index][1]; + + continue; + } + + if ($iteratorIndex < $index) { + $iterator->next(); + + $iteratorIndex++; + } + + if (! $iterator->valid()) { + break; + } + + $cache[$index] = [$iterator->key(), $iterator->current()]; + + yield $cache[$index][0] => $cache[$index][1]; + } + }); + } + + /** + * Get the median of a given key. + * + * @param string|array|null $key + * @return float|int|null + */ + public function median($key = null) + { + return $this->collect()->median($key); + } + + /** + * Get the mode of a given key. + * + * @param string|array|null $key + * @return array|null + */ + public function mode($key = null) + { + return $this->collect()->mode($key); + } + + /** + * Collapse the collection of items into a single array. + * + * @return static + */ + public function collapse() + { + return new static(function () { + foreach ($this as $values) { + if (is_array($values) || $values instanceof Enumerable) { + foreach ($values as $value) { + yield $value; + } + } + } + }); + } + + /** + * Collapse the collection of items into a single array while preserving its keys. + * + * @return static + */ + public function collapseWithKeys() + { + return new static(function () { + foreach ($this as $values) { + if (is_array($values) || $values instanceof Enumerable) { + foreach ($values as $key => $value) { + yield $key => $value; + } + } + } + }); + } + + /** + * Determine if an item exists in the enumerable. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function contains($key, $operator = null, $value = null) + { + if (func_num_args() === 1 && $this->useAsCallable($key)) { + $placeholder = new stdClass; + + /** @var callable $key */ + return $this->first($key, $placeholder) !== $placeholder; + } + + if (func_num_args() === 1) { + $needle = $key; + + foreach ($this as $value) { + if ($value == $needle) { + return true; + } + } + + return false; + } + + return $this->contains($this->operatorForWhere(...func_get_args())); + } + + /** + * Determine if an item exists, using strict comparison. + * + * @param (callable(TValue): bool)|TValue|array-key $key + * @param TValue|null $value + * @return bool + */ + public function containsStrict($key, $value = null) + { + if (func_num_args() === 2) { + return $this->contains(fn ($item) => data_get($item, $key) === $value); + } + + if ($this->useAsCallable($key)) { + return ! is_null($this->first($key)); + } + + foreach ($this as $item) { + if ($item === $key) { + return true; + } + } + + return false; + } + + /** + * Determine if an item is not contained in the enumerable. + * + * @param mixed $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function doesntContain($key, $operator = null, $value = null) + { + return ! $this->contains(...func_get_args()); + } + + /** + * Cross join the given iterables, returning all possible permutations. + * + * @template TCrossJoinKey + * @template TCrossJoinValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$arrays + * @return static> + */ + public function crossJoin(...$arrays) + { + return $this->passthru('crossJoin', func_get_args()); + } + + /** + * Count the number of items in the collection by a field or using a callback. + * + * @param (callable(TValue, TKey): array-key)|string|null $countBy + * @return static + */ + public function countBy($countBy = null) + { + $countBy = is_null($countBy) + ? $this->identity() + : $this->valueRetriever($countBy); + + return new static(function () use ($countBy) { + $counts = []; + + foreach ($this as $key => $value) { + $group = $countBy($value, $key); + + if (empty($counts[$group])) { + $counts[$group] = 0; + } + + $counts[$group]++; + } + + yield from $counts; + }); + } + + /** + * Get the items that are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diff($items) + { + return $this->passthru('diff', func_get_args()); + } + + /** + * Get the items that are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function diffUsing($items, callable $callback) + { + return $this->passthru('diffUsing', func_get_args()); + } + + /** + * Get the items whose keys and values are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diffAssoc($items) + { + return $this->passthru('diffAssoc', func_get_args()); + } + + /** + * Get the items whose keys and values are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diffAssocUsing($items, callable $callback) + { + return $this->passthru('diffAssocUsing', func_get_args()); + } + + /** + * Get the items whose keys are not present in the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function diffKeys($items) + { + return $this->passthru('diffKeys', func_get_args()); + } + + /** + * Get the items whose keys are not present in the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function diffKeysUsing($items, callable $callback) + { + return $this->passthru('diffKeysUsing', func_get_args()); + } + + /** + * Retrieve duplicate items. + * + * @template TMapValue + * + * @param (callable(TValue): TMapValue)|string|null $callback + * @param bool $strict + * @return static + */ + public function duplicates($callback = null, $strict = false) + { + return $this->passthru('duplicates', func_get_args()); + } + + /** + * Retrieve duplicate items using strict comparison. + * + * @template TMapValue + * + * @param (callable(TValue): TMapValue)|string|null $callback + * @return static + */ + public function duplicatesStrict($callback = null) + { + return $this->passthru('duplicatesStrict', func_get_args()); + } + + /** + * Get all items except for those with the specified keys. + * + * @param \Illuminate\Support\Enumerable|array $keys + * @return static + */ + public function except($keys) + { + return $this->passthru('except', func_get_args()); + } + + /** + * Run a filter over each of the items. + * + * @param (callable(TValue, TKey): bool)|null $callback + * @return static + */ + public function filter(?callable $callback = null) + { + if (is_null($callback)) { + $callback = fn ($value) => (bool) $value; + } + + return new static(function () use ($callback) { + foreach ($this as $key => $value) { + if ($callback($value, $key)) { + yield $key => $value; + } + } + }); + } + + /** + * Get the first item from the enumerable passing the given truth test. + * + * @template TFirstDefault + * + * @param (callable(TValue): bool)|null $callback + * @param TFirstDefault|(\Closure(): TFirstDefault) $default + * @return TValue|TFirstDefault + */ + public function first(?callable $callback = null, $default = null) + { + $iterator = $this->getIterator(); + + if (is_null($callback)) { + if (! $iterator->valid()) { + return value($default); + } + + return $iterator->current(); + } + + foreach ($iterator as $key => $value) { + if ($callback($value, $key)) { + return $value; + } + } + + return value($default); + } + + /** + * Get a flattened list of the items in the collection. + * + * @param int $depth + * @return static + */ + public function flatten($depth = INF) + { + $instance = new static(function () use ($depth) { + foreach ($this as $item) { + if (! is_array($item) && ! $item instanceof Enumerable) { + yield $item; + } elseif ($depth === 1) { + yield from $item; + } else { + yield from (new static($item))->flatten($depth - 1); + } + } + }); + + return $instance->values(); + } + + /** + * Flip the items in the collection. + * + * @return static + */ + public function flip() + { + return new static(function () { + foreach ($this as $key => $value) { + yield $value => $key; + } + }); + } + + /** + * Get an item by key. + * + * @template TGetDefault + * + * @param TKey|null $key + * @param TGetDefault|(\Closure(): TGetDefault) $default + * @return TValue|TGetDefault + */ + public function get($key, $default = null) + { + if (is_null($key)) { + return; + } + + foreach ($this as $outerKey => $outerValue) { + if ($outerKey == $key) { + return $outerValue; + } + } + + return value($default); + } + + /** + * Group an associative array by a field or using a callback. + * + * @template TGroupKey of array-key + * + * @param (callable(TValue, TKey): TGroupKey)|array|string $groupBy + * @param bool $preserveKeys + * @return static<($groupBy is string ? array-key : ($groupBy is array ? array-key : TGroupKey)), static<($preserveKeys is true ? TKey : int), ($groupBy is array ? mixed : TValue)>> + */ + public function groupBy($groupBy, $preserveKeys = false) + { + return $this->passthru('groupBy', func_get_args()); + } + + /** + * Key an associative array by a field or using a callback. + * + * @template TNewKey of array-key + * + * @param (callable(TValue, TKey): TNewKey)|array|string $keyBy + * @return static<($keyBy is string ? array-key : ($keyBy is array ? array-key : TNewKey)), TValue> + */ + public function keyBy($keyBy) + { + return new static(function () use ($keyBy) { + $keyBy = $this->valueRetriever($keyBy); + + foreach ($this as $key => $item) { + $resolvedKey = $keyBy($item, $key); + + if (is_object($resolvedKey)) { + $resolvedKey = (string) $resolvedKey; + } + + yield $resolvedKey => $item; + } + }); + } + + /** + * Determine if an item exists in the collection by key. + * + * @param mixed $key + * @return bool + */ + public function has($key) + { + $keys = array_flip(is_array($key) ? $key : func_get_args()); + $count = count($keys); + + foreach ($this as $key => $value) { + if (array_key_exists($key, $keys) && --$count == 0) { + return true; + } + } + + return false; + } + + /** + * Determine if any of the keys exist in the collection. + * + * @param mixed $key + * @return bool + */ + public function hasAny($key) + { + $keys = array_flip(is_array($key) ? $key : func_get_args()); + + foreach ($this as $key => $value) { + if (array_key_exists($key, $keys)) { + return true; + } + } + + return false; + } + + /** + * Concatenate values of a given key as a string. + * + * @param (callable(TValue, TKey): mixed)|string $value + * @param string|null $glue + * @return string + */ + public function implode($value, $glue = null) + { + return $this->collect()->implode(...func_get_args()); + } + + /** + * Intersect the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersect($items) + { + return $this->passthru('intersect', func_get_args()); + } + + /** + * Intersect the collection with the given items, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersectUsing($items, callable $callback) + { + return $this->passthru('intersectUsing', func_get_args()); + } + + /** + * Intersect the collection with the given items with additional index check. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersectAssoc($items) + { + return $this->passthru('intersectAssoc', func_get_args()); + } + + /** + * Intersect the collection with the given items with additional index check, using the callback. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @param callable(TValue, TValue): int $callback + * @return static + */ + public function intersectAssocUsing($items, callable $callback) + { + return $this->passthru('intersectAssocUsing', func_get_args()); + } + + /** + * Intersect the collection with the given items by key. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function intersectByKeys($items) + { + return $this->passthru('intersectByKeys', func_get_args()); + } + + /** + * Determine if the items are empty or not. + * + * @return bool + */ + public function isEmpty() + { + return ! $this->getIterator()->valid(); + } + + /** + * Determine if the collection contains a single item. + * + * @return bool + */ + public function containsOneItem() + { + return $this->take(2)->count() === 1; + } + + /** + * Join all items from the collection using a string. The final items can use a separate glue string. + * + * @param string $glue + * @param string $finalGlue + * @return string + */ + public function join($glue, $finalGlue = '') + { + return $this->collect()->join(...func_get_args()); + } + + /** + * Get the keys of the collection items. + * + * @return static + */ + public function keys() + { + return new static(function () { + foreach ($this as $key => $value) { + yield $key; + } + }); + } + + /** + * Get the last item from the collection. + * + * @template TLastDefault + * + * @param (callable(TValue, TKey): bool)|null $callback + * @param TLastDefault|(\Closure(): TLastDefault) $default + * @return TValue|TLastDefault + */ + public function last(?callable $callback = null, $default = null) + { + $needle = $placeholder = new stdClass; + + foreach ($this as $key => $value) { + if (is_null($callback) || $callback($value, $key)) { + $needle = $value; + } + } + + return $needle === $placeholder ? value($default) : $needle; + } + + /** + * Get the values of a given key. + * + * @param string|array $value + * @param string|null $key + * @return static + */ + public function pluck($value, $key = null) + { + return new static(function () use ($value, $key) { + [$value, $key] = $this->explodePluckParameters($value, $key); + + foreach ($this as $item) { + $itemValue = $value instanceof Closure + ? $value($item) + : data_get($item, $value); + + if (is_null($key)) { + yield $itemValue; + } else { + $itemKey = $key instanceof Closure + ? $key($item) + : data_get($item, $key); + + if (is_object($itemKey) && method_exists($itemKey, '__toString')) { + $itemKey = (string) $itemKey; + } + + yield $itemKey => $itemValue; + } + } + }); + } + + /** + * Run a map over each of the items. + * + * @template TMapValue + * + * @param callable(TValue, TKey): TMapValue $callback + * @return static + */ + public function map(callable $callback) + { + return new static(function () use ($callback) { + foreach ($this as $key => $value) { + yield $key => $callback($value, $key); + } + }); + } + + /** + * Run a dictionary map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapToDictionaryKey of array-key + * @template TMapToDictionaryValue + * + * @param callable(TValue, TKey): array $callback + * @return static> + */ + public function mapToDictionary(callable $callback) + { + return $this->passthru('mapToDictionary', func_get_args()); + } + + /** + * Run an associative map over each of the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapWithKeysKey of array-key + * @template TMapWithKeysValue + * + * @param callable(TValue, TKey): array $callback + * @return static + */ + public function mapWithKeys(callable $callback) + { + return new static(function () use ($callback) { + foreach ($this as $key => $value) { + yield from $callback($value, $key); + } + }); + } + + /** + * Merge the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function merge($items) + { + return $this->passthru('merge', func_get_args()); + } + + /** + * Recursively merge the collection with the given items. + * + * @template TMergeRecursiveValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function mergeRecursive($items) + { + return $this->passthru('mergeRecursive', func_get_args()); + } + + /** + * Multiply the items in the collection by the multiplier. + * + * @param int $multiplier + * @return static + */ + public function multiply(int $multiplier) + { + return $this->passthru('multiply', func_get_args()); + } + + /** + * Create a collection by using this collection for keys and another for its values. + * + * @template TCombineValue + * + * @param \IteratorAggregate|array|(callable(): \Generator) $values + * @return static + */ + public function combine($values) + { + return new static(function () use ($values) { + $values = $this->makeIterator($values); + + $errorMessage = 'Both parameters should have an equal number of elements'; + + foreach ($this as $key) { + if (! $values->valid()) { + trigger_error($errorMessage, E_USER_WARNING); + + break; + } + + yield $key => $values->current(); + + $values->next(); + } + + if ($values->valid()) { + trigger_error($errorMessage, E_USER_WARNING); + } + }); + } + + /** + * Union the collection with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function union($items) + { + return $this->passthru('union', func_get_args()); + } + + /** + * Create a new collection consisting of every n-th element. + * + * @param int $step + * @param int $offset + * @return static + */ + public function nth($step, $offset = 0) + { + return new static(function () use ($step, $offset) { + $position = 0; + + foreach ($this->slice($offset) as $item) { + if ($position % $step === 0) { + yield $item; + } + + $position++; + } + }); + } + + /** + * Get the items with the specified keys. + * + * @param \Illuminate\Support\Enumerable|array|string $keys + * @return static + */ + public function only($keys) + { + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } elseif (! is_null($keys)) { + $keys = is_array($keys) ? $keys : func_get_args(); + } + + return new static(function () use ($keys) { + if (is_null($keys)) { + yield from $this; + } else { + $keys = array_flip($keys); + + foreach ($this as $key => $value) { + if (array_key_exists($key, $keys)) { + yield $key => $value; + + unset($keys[$key]); + + if (empty($keys)) { + break; + } + } + } + } + }); + } + + /** + * Select specific values from the items within the collection. + * + * @param \Illuminate\Support\Enumerable|array|string $keys + * @return static + */ + public function select($keys) + { + if ($keys instanceof Enumerable) { + $keys = $keys->all(); + } elseif (! is_null($keys)) { + $keys = is_array($keys) ? $keys : func_get_args(); + } + + return new static(function () use ($keys) { + if (is_null($keys)) { + yield from $this; + } else { + foreach ($this as $item) { + $result = []; + + foreach ($keys as $key) { + if (Arr::accessible($item) && Arr::exists($item, $key)) { + $result[$key] = $item[$key]; + } elseif (is_object($item) && isset($item->{$key})) { + $result[$key] = $item->{$key}; + } + } + + yield $result; + } + } + }); + } + + /** + * Push all of the given items onto the collection. + * + * @template TConcatKey of array-key + * @template TConcatValue + * + * @param iterable $source + * @return static + */ + public function concat($source) + { + return (new static(function () use ($source) { + yield from $this; + yield from $source; + }))->values(); + } + + /** + * Get one or a specified number of items randomly from the collection. + * + * @param int|null $number + * @return static|TValue + * + * @throws \InvalidArgumentException + */ + public function random($number = null) + { + $result = $this->collect()->random(...func_get_args()); + + return is_null($number) ? $result : new static($result); + } + + /** + * Replace the collection items with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replace($items) + { + return new static(function () use ($items) { + $items = $this->getArrayableItems($items); + + foreach ($this as $key => $value) { + if (array_key_exists($key, $items)) { + yield $key => $items[$key]; + + unset($items[$key]); + } else { + yield $key => $value; + } + } + + foreach ($items as $key => $value) { + yield $key => $value; + } + }); + } + + /** + * Recursively replace the collection items with the given items. + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable $items + * @return static + */ + public function replaceRecursive($items) + { + return $this->passthru('replaceRecursive', func_get_args()); + } + + /** + * Reverse items order. + * + * @return static + */ + public function reverse() + { + return $this->passthru('reverse', func_get_args()); + } + + /** + * Search the collection for a given value and return the corresponding key if successful. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TKey|false + */ + public function search($value, $strict = false) + { + /** @var (callable(TValue,TKey): bool) $predicate */ + $predicate = $this->useAsCallable($value) + ? $value + : function ($item) use ($value, $strict) { + return $strict ? $item === $value : $item == $value; + }; + + foreach ($this as $key => $item) { + if ($predicate($item, $key)) { + return $key; + } + } + + return false; + } + + /** + * Get the item before the given item. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TValue|null + */ + public function before($value, $strict = false) + { + $previous = null; + + /** @var (callable(TValue,TKey): bool) $predicate */ + $predicate = $this->useAsCallable($value) + ? $value + : function ($item) use ($value, $strict) { + return $strict ? $item === $value : $item == $value; + }; + + foreach ($this as $key => $item) { + if ($predicate($item, $key)) { + return $previous; + } + + $previous = $item; + } + + return null; + } + + /** + * Get the item after the given item. + * + * @param TValue|(callable(TValue,TKey): bool) $value + * @param bool $strict + * @return TValue|null + */ + public function after($value, $strict = false) + { + $found = false; + + /** @var (callable(TValue,TKey): bool) $predicate */ + $predicate = $this->useAsCallable($value) + ? $value + : function ($item) use ($value, $strict) { + return $strict ? $item === $value : $item == $value; + }; + + foreach ($this as $key => $item) { + if ($found) { + return $item; + } + + if ($predicate($item, $key)) { + $found = true; + } + } + + return null; + } + + /** + * Shuffle the items in the collection. + * + * @return static + */ + public function shuffle() + { + return $this->passthru('shuffle', []); + } + + /** + * Create chunks representing a "sliding window" view of the items in the collection. + * + * @param int $size + * @param int $step + * @return static + */ + public function sliding($size = 2, $step = 1) + { + return new static(function () use ($size, $step) { + $iterator = $this->getIterator(); + + $chunk = []; + + while ($iterator->valid()) { + $chunk[$iterator->key()] = $iterator->current(); + + if (count($chunk) == $size) { + yield (new static($chunk))->tap(function () use (&$chunk, $step) { + $chunk = array_slice($chunk, $step, null, true); + }); + + // If the $step between chunks is bigger than each chunk's $size + // we will skip the extra items (which should never be in any + // chunk) before we continue to the next chunk in the loop. + if ($step > $size) { + $skip = $step - $size; + + for ($i = 0; $i < $skip && $iterator->valid(); $i++) { + $iterator->next(); + } + } + } + + $iterator->next(); + } + }); + } + + /** + * Skip the first {$count} items. + * + * @param int $count + * @return static + */ + public function skip($count) + { + return new static(function () use ($count) { + $iterator = $this->getIterator(); + + while ($iterator->valid() && $count--) { + $iterator->next(); + } + + while ($iterator->valid()) { + yield $iterator->key() => $iterator->current(); + + $iterator->next(); + } + }); + } + + /** + * Skip items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skipUntil($value) + { + $callback = $this->useAsCallable($value) ? $value : $this->equality($value); + + return $this->skipWhile($this->negate($callback)); + } + + /** + * Skip items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function skipWhile($value) + { + $callback = $this->useAsCallable($value) ? $value : $this->equality($value); + + return new static(function () use ($callback) { + $iterator = $this->getIterator(); + + while ($iterator->valid() && $callback($iterator->current(), $iterator->key())) { + $iterator->next(); + } + + while ($iterator->valid()) { + yield $iterator->key() => $iterator->current(); + + $iterator->next(); + } + }); + } + + /** + * Get a slice of items from the enumerable. + * + * @param int $offset + * @param int|null $length + * @return static + */ + public function slice($offset, $length = null) + { + if ($offset < 0 || $length < 0) { + return $this->passthru('slice', func_get_args()); + } + + $instance = $this->skip($offset); + + return is_null($length) ? $instance : $instance->take($length); + } + + /** + * Split a collection into a certain number of groups. + * + * @param int $numberOfGroups + * @return static + */ + public function split($numberOfGroups) + { + return $this->passthru('split', func_get_args()); + } + + /** + * Get the first item in the collection, but only if exactly one item exists. Otherwise, throw an exception. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Illuminate\Support\ItemNotFoundException + * @throws \Illuminate\Support\MultipleItemsFoundException + */ + public function sole($key = null, $operator = null, $value = null) + { + $filter = func_num_args() > 1 + ? $this->operatorForWhere(...func_get_args()) + : $key; + + return $this + ->unless($filter == null) + ->filter($filter) + ->take(2) + ->collect() + ->sole(); + } + + /** + * Get the first item in the collection but throw an exception if no matching items exist. + * + * @param (callable(TValue, TKey): bool)|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue + * + * @throws \Illuminate\Support\ItemNotFoundException + */ + public function firstOrFail($key = null, $operator = null, $value = null) + { + $filter = func_num_args() > 1 + ? $this->operatorForWhere(...func_get_args()) + : $key; + + return $this + ->unless($filter == null) + ->filter($filter) + ->take(1) + ->collect() + ->firstOrFail(); + } + + /** + * Chunk the collection into chunks of the given size. + * + * @param int $size + * @param bool $preserveKeys + * @return ($preserveKeys is true ? static : static>) + */ + public function chunk($size, $preserveKeys = true) + { + if ($size <= 0) { + return static::empty(); + } + + $add = match ($preserveKeys) { + true => fn (array &$chunk, Traversable $iterator) => $chunk[$iterator->key()] = $iterator->current(), + false => fn (array &$chunk, Traversable $iterator) => $chunk[] = $iterator->current(), + }; + + return new static(function () use ($size, $add) { + $iterator = $this->getIterator(); + + while ($iterator->valid()) { + $chunk = []; + + while (true) { + $add($chunk, $iterator); + + if (count($chunk) < $size) { + $iterator->next(); + + if (! $iterator->valid()) { + break; + } + } else { + break; + } + } + + yield new static($chunk); + + $iterator->next(); + } + }); + } + + /** + * Split a collection into a certain number of groups, and fill the first groups completely. + * + * @param int $numberOfGroups + * @return static + */ + public function splitIn($numberOfGroups) + { + return $this->chunk((int) ceil($this->count() / $numberOfGroups)); + } + + /** + * Chunk the collection into chunks with a callback. + * + * @param callable(TValue, TKey, Collection): bool $callback + * @return static> + */ + public function chunkWhile(callable $callback) + { + return new static(function () use ($callback) { + $iterator = $this->getIterator(); + + $chunk = new Collection; + + if ($iterator->valid()) { + $chunk[$iterator->key()] = $iterator->current(); + + $iterator->next(); + } + + while ($iterator->valid()) { + if (! $callback($iterator->current(), $iterator->key(), $chunk)) { + yield new static($chunk); + + $chunk = new Collection; + } + + $chunk[$iterator->key()] = $iterator->current(); + + $iterator->next(); + } + + if ($chunk->isNotEmpty()) { + yield new static($chunk); + } + }); + } + + /** + * Sort through each item with a callback. + * + * @param (callable(TValue, TValue): int)|null|int $callback + * @return static + */ + public function sort($callback = null) + { + return $this->passthru('sort', func_get_args()); + } + + /** + * Sort items in descending order. + * + * @param int $options + * @return static + */ + public function sortDesc($options = SORT_REGULAR) + { + return $this->passthru('sortDesc', func_get_args()); + } + + /** + * Sort the collection using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @param bool $descending + * @return static + */ + public function sortBy($callback, $options = SORT_REGULAR, $descending = false) + { + return $this->passthru('sortBy', func_get_args()); + } + + /** + * Sort the collection in descending order using the given callback. + * + * @param array|(callable(TValue, TKey): mixed)|string $callback + * @param int $options + * @return static + */ + public function sortByDesc($callback, $options = SORT_REGULAR) + { + return $this->passthru('sortByDesc', func_get_args()); + } + + /** + * Sort the collection keys. + * + * @param int $options + * @param bool $descending + * @return static + */ + public function sortKeys($options = SORT_REGULAR, $descending = false) + { + return $this->passthru('sortKeys', func_get_args()); + } + + /** + * Sort the collection keys in descending order. + * + * @param int $options + * @return static + */ + public function sortKeysDesc($options = SORT_REGULAR) + { + return $this->passthru('sortKeysDesc', func_get_args()); + } + + /** + * Sort the collection keys using a callback. + * + * @param callable(TKey, TKey): int $callback + * @return static + */ + public function sortKeysUsing(callable $callback) + { + return $this->passthru('sortKeysUsing', func_get_args()); + } + + /** + * Take the first or last {$limit} items. + * + * @param int $limit + * @return static + */ + public function take($limit) + { + if ($limit < 0) { + return new static(function () use ($limit) { + $limit = abs($limit); + $ringBuffer = []; + $position = 0; + + foreach ($this as $key => $value) { + $ringBuffer[$position] = [$key, $value]; + $position = ($position + 1) % $limit; + } + + for ($i = 0, $end = min($limit, count($ringBuffer)); $i < $end; $i++) { + $pointer = ($position + $i) % $limit; + yield $ringBuffer[$pointer][0] => $ringBuffer[$pointer][1]; + } + }); + } + + return new static(function () use ($limit) { + $iterator = $this->getIterator(); + + while ($limit--) { + if (! $iterator->valid()) { + break; + } + + yield $iterator->key() => $iterator->current(); + + if ($limit) { + $iterator->next(); + } + } + }); + } + + /** + * Take items in the collection until the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function takeUntil($value) + { + /** @var callable(TValue, TKey): bool $callback */ + $callback = $this->useAsCallable($value) ? $value : $this->equality($value); + + return new static(function () use ($callback) { + foreach ($this as $key => $item) { + if ($callback($item, $key)) { + break; + } + + yield $key => $item; + } + }); + } + + /** + * Take items in the collection until a given point in time. + * + * @param \DateTimeInterface $timeout + * @return static + */ + public function takeUntilTimeout(DateTimeInterface $timeout) + { + $timeout = $timeout->getTimestamp(); + + return new static(function () use ($timeout) { + if ($this->now() >= $timeout) { + return; + } + + foreach ($this as $key => $value) { + yield $key => $value; + + if ($this->now() >= $timeout) { + break; + } + } + }); + } + + /** + * Take items in the collection while the given condition is met. + * + * @param TValue|callable(TValue,TKey): bool $value + * @return static + */ + public function takeWhile($value) + { + /** @var callable(TValue, TKey): bool $callback */ + $callback = $this->useAsCallable($value) ? $value : $this->equality($value); + + return $this->takeUntil(fn ($item, $key) => ! $callback($item, $key)); + } + + /** + * Pass each item in the collection to the given callback, lazily. + * + * @param callable(TValue, TKey): mixed $callback + * @return static + */ + public function tapEach(callable $callback) + { + return new static(function () use ($callback) { + foreach ($this as $key => $value) { + $callback($value, $key); + + yield $key => $value; + } + }); + } + + /** + * Throttle the values, releasing them at most once per the given seconds. + * + * @return static + */ + public function throttle(float $seconds) + { + return new static(function () use ($seconds) { + $microseconds = $seconds * 1_000_000; + + foreach ($this as $key => $value) { + $fetchedAt = $this->preciseNow(); + + yield $key => $value; + + $sleep = $microseconds - ($this->preciseNow() - $fetchedAt); + + $this->usleep((int) $sleep); + } + }); + } + + /** + * Flatten a multi-dimensional associative array with dots. + * + * @return static + */ + public function dot() + { + return $this->passthru('dot', []); + } + + /** + * Convert a flatten "dot" notation array into an expanded array. + * + * @return static + */ + public function undot() + { + return $this->passthru('undot', []); + } + + /** + * Return only unique items from the collection array. + * + * @param (callable(TValue, TKey): mixed)|string|null $key + * @param bool $strict + * @return static + */ + public function unique($key = null, $strict = false) + { + $callback = $this->valueRetriever($key); + + return new static(function () use ($callback, $strict) { + $exists = []; + + foreach ($this as $key => $item) { + if (! in_array($id = $callback($item, $key), $exists, $strict)) { + yield $key => $item; + + $exists[] = $id; + } + } + }); + } + + /** + * Reset the keys on the underlying array. + * + * @return static + */ + public function values() + { + return new static(function () { + foreach ($this as $item) { + yield $item; + } + }); + } + + /** + * Zip the collection together with one or more arrays. + * + * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]); + * => [[1, 4], [2, 5], [3, 6]] + * + * @template TZipValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable ...$items + * @return static> + */ + public function zip($items) + { + $iterables = func_get_args(); + + return new static(function () use ($iterables) { + $iterators = (new Collection($iterables)) + ->map(fn ($iterable) => $this->makeIterator($iterable)) + ->prepend($this->getIterator()); + + while ($iterators->contains->valid()) { + yield new static($iterators->map->current()); + + $iterators->each->next(); + } + }); + } + + /** + * Pad collection to the specified length with a value. + * + * @template TPadValue + * + * @param int $size + * @param TPadValue $value + * @return static + */ + public function pad($size, $value) + { + if ($size < 0) { + return $this->passthru('pad', func_get_args()); + } + + return new static(function () use ($size, $value) { + $yielded = 0; + + foreach ($this as $index => $item) { + yield $index => $item; + + $yielded++; + } + + while ($yielded++ < $size) { + yield $value; + } + }); + } + + /** + * Get the values iterator. + * + * @return \Traversable + */ + public function getIterator(): Traversable + { + return $this->makeIterator($this->source); + } + + /** + * Count the number of items in the collection. + * + * @return int + */ + public function count(): int + { + if (is_array($this->source)) { + return count($this->source); + } + + return iterator_count($this->getIterator()); + } + + /** + * Make an iterator from the given source. + * + * @template TIteratorKey of array-key + * @template TIteratorValue + * + * @param \IteratorAggregate|array|(callable(): \Generator) $source + * @return \Traversable + */ + protected function makeIterator($source) + { + if ($source instanceof IteratorAggregate) { + return $source->getIterator(); + } + + if (is_array($source)) { + return new ArrayIterator($source); + } + + if (is_callable($source)) { + $maybeTraversable = $source(); + + return $maybeTraversable instanceof Traversable + ? $maybeTraversable + : new ArrayIterator(Arr::wrap($maybeTraversable)); + } + + return new ArrayIterator((array) $source); + } + + /** + * Explode the "value" and "key" arguments passed to "pluck". + * + * @param string|string[] $value + * @param string|string[]|null $key + * @return array{string[],string[]|null} + */ + protected function explodePluckParameters($value, $key) + { + $value = is_string($value) ? explode('.', $value) : $value; + + $key = is_null($key) || is_array($key) || $key instanceof Closure ? $key : explode('.', $key); + + return [$value, $key]; + } + + /** + * Pass this lazy collection through a method on the collection class. + * + * @param string $method + * @param array $params + * @return static + */ + protected function passthru($method, array $params) + { + return new static(function () use ($method, $params) { + yield from $this->collect()->$method(...$params); + }); + } + + /** + * Get the current time. + * + * @return int + */ + protected function now() + { + return class_exists(Carbon::class) + ? Carbon::now()->timestamp + : time(); + } + + /** + * Get the precise current time. + * + * @return float + */ + protected function preciseNow() + { + return class_exists(Carbon::class) + ? Carbon::now()->getPreciseTimestamp() + : microtime(true) * 1_000_000; + } + + /** + * Sleep for the given amount of microseconds. + * + * @return void + */ + protected function usleep(int $microseconds) + { + if ($microseconds <= 0) { + return; + } + + class_exists(Sleep::class) + ? Sleep::usleep($microseconds) + : usleep($microseconds); + } +} diff --git a/vendor/illuminate/collections/MultipleItemsFoundException.php b/vendor/illuminate/collections/MultipleItemsFoundException.php new file mode 100644 index 0000000..9c5c7c5 --- /dev/null +++ b/vendor/illuminate/collections/MultipleItemsFoundException.php @@ -0,0 +1,39 @@ +count = $count; + + parent::__construct("$count items were found.", $code, $previous); + } + + /** + * Get the number of items found. + * + * @return int + */ + public function getCount() + { + return $this->count; + } +} diff --git a/vendor/illuminate/collections/Traits/EnumeratesValues.php b/vendor/illuminate/collections/Traits/EnumeratesValues.php new file mode 100644 index 0000000..c11c9c4 --- /dev/null +++ b/vendor/illuminate/collections/Traits/EnumeratesValues.php @@ -0,0 +1,1180 @@ + $average + * @property-read HigherOrderCollectionProxy $avg + * @property-read HigherOrderCollectionProxy $contains + * @property-read HigherOrderCollectionProxy $doesntContain + * @property-read HigherOrderCollectionProxy $each + * @property-read HigherOrderCollectionProxy $every + * @property-read HigherOrderCollectionProxy $filter + * @property-read HigherOrderCollectionProxy $first + * @property-read HigherOrderCollectionProxy $flatMap + * @property-read HigherOrderCollectionProxy $groupBy + * @property-read HigherOrderCollectionProxy $keyBy + * @property-read HigherOrderCollectionProxy $last + * @property-read HigherOrderCollectionProxy $map + * @property-read HigherOrderCollectionProxy $max + * @property-read HigherOrderCollectionProxy $min + * @property-read HigherOrderCollectionProxy $partition + * @property-read HigherOrderCollectionProxy $percentage + * @property-read HigherOrderCollectionProxy $reject + * @property-read HigherOrderCollectionProxy $skipUntil + * @property-read HigherOrderCollectionProxy $skipWhile + * @property-read HigherOrderCollectionProxy $some + * @property-read HigherOrderCollectionProxy $sortBy + * @property-read HigherOrderCollectionProxy $sortByDesc + * @property-read HigherOrderCollectionProxy $sum + * @property-read HigherOrderCollectionProxy $takeUntil + * @property-read HigherOrderCollectionProxy $takeWhile + * @property-read HigherOrderCollectionProxy $unique + * @property-read HigherOrderCollectionProxy $unless + * @property-read HigherOrderCollectionProxy $until + * @property-read HigherOrderCollectionProxy $when + */ +trait EnumeratesValues +{ + use Conditionable; + + /** + * Indicates that the object's string representation should be escaped when __toString is invoked. + * + * @var bool + */ + protected $escapeWhenCastingToString = false; + + /** + * The methods that can be proxied. + * + * @var array + */ + protected static $proxies = [ + 'average', + 'avg', + 'contains', + 'doesntContain', + 'each', + 'every', + 'filter', + 'first', + 'flatMap', + 'groupBy', + 'keyBy', + 'last', + 'map', + 'max', + 'min', + 'partition', + 'percentage', + 'reject', + 'skipUntil', + 'skipWhile', + 'some', + 'sortBy', + 'sortByDesc', + 'sum', + 'takeUntil', + 'takeWhile', + 'unique', + 'unless', + 'until', + 'when', + ]; + + /** + * Create a new collection instance if the value isn't one already. + * + * @template TMakeKey of array-key + * @template TMakeValue + * + * @param \Illuminate\Contracts\Support\Arrayable|iterable|null $items + * @return static + */ + public static function make($items = []) + { + return new static($items); + } + + /** + * Wrap the given value in a collection if applicable. + * + * @template TWrapValue + * + * @param iterable|TWrapValue $value + * @return static + */ + public static function wrap($value) + { + return $value instanceof Enumerable + ? new static($value) + : new static(Arr::wrap($value)); + } + + /** + * Get the underlying items from the given collection if applicable. + * + * @template TUnwrapKey of array-key + * @template TUnwrapValue + * + * @param array|static $value + * @return array + */ + public static function unwrap($value) + { + return $value instanceof Enumerable ? $value->all() : $value; + } + + /** + * Create a new instance with no items. + * + * @return static + */ + public static function empty() + { + return new static([]); + } + + /** + * Create a new collection by invoking the callback a given amount of times. + * + * @template TTimesValue + * + * @param int $number + * @param (callable(int): TTimesValue)|null $callback + * @return static + */ + public static function times($number, ?callable $callback = null) + { + if ($number < 1) { + return new static; + } + + return static::range(1, $number) + ->unless($callback == null) + ->map($callback); + } + + /** + * Create a new collection by decoding a JSON string. + * + * @param string $json + * @param int $depth + * @param int $flags + * @return static + */ + public static function fromJson($json, $depth = 512, $flags = 0) + { + return new static(json_decode($json, true, $depth, $flags)); + } + + /** + * Get the average value of a given key. + * + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null + */ + public function avg($callback = null) + { + $callback = $this->valueRetriever($callback); + + $reduced = $this->reduce(static function (&$reduce, $value) use ($callback) { + if (! is_null($resolved = $callback($value))) { + $reduce[0] += $resolved; + $reduce[1]++; + } + + return $reduce; + }, [0, 0]); + + return $reduced[1] ? $reduced[0] / $reduced[1] : null; + } + + /** + * Alias for the "avg" method. + * + * @param (callable(TValue): float|int)|string|null $callback + * @return float|int|null + */ + public function average($callback = null) + { + return $this->avg($callback); + } + + /** + * Alias for the "contains" method. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function some($key, $operator = null, $value = null) + { + return $this->contains(...func_get_args()); + } + + /** + * Dump the given arguments and terminate execution. + * + * @param mixed ...$args + * @return never + */ + public function dd(...$args) + { + dd($this->all(), ...$args); + } + + /** + * Dump the items. + * + * @param mixed ...$args + * @return $this + */ + public function dump(...$args) + { + dump($this->all(), ...$args); + + return $this; + } + + /** + * Execute a callback over each item. + * + * @param callable(TValue, TKey): mixed $callback + * @return $this + */ + public function each(callable $callback) + { + foreach ($this as $key => $item) { + if ($callback($item, $key) === false) { + break; + } + } + + return $this; + } + + /** + * Execute a callback over each nested chunk of items. + * + * @param callable(...mixed): mixed $callback + * @return static + */ + public function eachSpread(callable $callback) + { + return $this->each(function ($chunk, $key) use ($callback) { + $chunk[] = $key; + + return $callback(...$chunk); + }); + } + + /** + * Determine if all items pass the given truth test. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param mixed $operator + * @param mixed $value + * @return bool + */ + public function every($key, $operator = null, $value = null) + { + if (func_num_args() === 1) { + $callback = $this->valueRetriever($key); + + foreach ($this as $k => $v) { + if (! $callback($v, $k)) { + return false; + } + } + + return true; + } + + return $this->every($this->operatorForWhere(...func_get_args())); + } + + /** + * Get the first item by the given key value pair. + * + * @param callable|string $key + * @param mixed $operator + * @param mixed $value + * @return TValue|null + */ + public function firstWhere($key, $operator = null, $value = null) + { + return $this->first($this->operatorForWhere(...func_get_args())); + } + + /** + * Get a single key's value from the first matching item in the collection. + * + * @template TValueDefault + * + * @param string $key + * @param TValueDefault|(\Closure(): TValueDefault) $default + * @return TValue|TValueDefault + */ + public function value($key, $default = null) + { + if ($value = $this->firstWhere($key)) { + return data_get($value, $key, $default); + } + + return value($default); + } + + /** + * Ensure that every item in the collection is of the expected type. + * + * @template TEnsureOfType + * + * @param class-string|array>|'string'|'int'|'float'|'bool'|'array'|'null' $type + * @return static + * + * @throws \UnexpectedValueException + */ + public function ensure($type) + { + $allowedTypes = is_array($type) ? $type : [$type]; + + return $this->each(function ($item, $index) use ($allowedTypes) { + $itemType = get_debug_type($item); + + foreach ($allowedTypes as $allowedType) { + if ($itemType === $allowedType || $item instanceof $allowedType) { + return true; + } + } + + throw new UnexpectedValueException( + sprintf("Collection should only include [%s] items, but '%s' found at position %d.", implode(', ', $allowedTypes), $itemType, $index) + ); + }); + } + + /** + * Determine if the collection is not empty. + * + * @phpstan-assert-if-true TValue $this->first() + * @phpstan-assert-if-true TValue $this->last() + * + * @phpstan-assert-if-false null $this->first() + * @phpstan-assert-if-false null $this->last() + * + * @return bool + */ + public function isNotEmpty() + { + return ! $this->isEmpty(); + } + + /** + * Run a map over each nested chunk of items. + * + * @template TMapSpreadValue + * + * @param callable(mixed...): TMapSpreadValue $callback + * @return static + */ + public function mapSpread(callable $callback) + { + return $this->map(function ($chunk, $key) use ($callback) { + $chunk[] = $key; + + return $callback(...$chunk); + }); + } + + /** + * Run a grouping map over the items. + * + * The callback should return an associative array with a single key/value pair. + * + * @template TMapToGroupsKey of array-key + * @template TMapToGroupsValue + * + * @param callable(TValue, TKey): array $callback + * @return static> + */ + public function mapToGroups(callable $callback) + { + $groups = $this->mapToDictionary($callback); + + return $groups->map($this->make(...)); + } + + /** + * Map a collection and flatten the result by a single level. + * + * @template TFlatMapKey of array-key + * @template TFlatMapValue + * + * @param callable(TValue, TKey): (\Illuminate\Support\Collection|array) $callback + * @return static + */ + public function flatMap(callable $callback) + { + return $this->map($callback)->collapse(); + } + + /** + * Map the values into a new class. + * + * @template TMapIntoValue + * + * @param class-string $class + * @return static + */ + public function mapInto($class) + { + if (is_subclass_of($class, BackedEnum::class)) { + return $this->map(fn ($value, $key) => $class::from($value)); + } + + return $this->map(fn ($value, $key) => new $class($value, $key)); + } + + /** + * Get the min value of a given key. + * + * @param (callable(TValue):mixed)|string|null $callback + * @return mixed + */ + public function min($callback = null) + { + $callback = $this->valueRetriever($callback); + + return $this->map(fn ($value) => $callback($value)) + ->reject(fn ($value) => is_null($value)) + ->reduce(fn ($result, $value) => is_null($result) || $value < $result ? $value : $result); + } + + /** + * Get the max value of a given key. + * + * @param (callable(TValue):mixed)|string|null $callback + * @return mixed + */ + public function max($callback = null) + { + $callback = $this->valueRetriever($callback); + + return $this->reject(fn ($value) => is_null($value))->reduce(function ($result, $item) use ($callback) { + $value = $callback($item); + + return is_null($result) || $value > $result ? $value : $result; + }); + } + + /** + * "Paginate" the collection by slicing it into a smaller collection. + * + * @param int $page + * @param int $perPage + * @return static + */ + public function forPage($page, $perPage) + { + $offset = max(0, ($page - 1) * $perPage); + + return $this->slice($offset, $perPage); + } + + /** + * Partition the collection into two arrays using the given callback or key. + * + * @param (callable(TValue, TKey): bool)|TValue|string $key + * @param TValue|string|null $operator + * @param TValue|null $value + * @return static, static> + */ + public function partition($key, $operator = null, $value = null) + { + $callback = func_num_args() === 1 + ? $this->valueRetriever($key) + : $this->operatorForWhere(...func_get_args()); + + [$passed, $failed] = Arr::partition($this->getIterator(), $callback); + + return new static([new static($passed), new static($failed)]); + } + + /** + * Calculate the percentage of items that pass a given truth test. + * + * @param (callable(TValue, TKey): bool) $callback + * @param int $precision + * @return float|null + */ + public function percentage(callable $callback, int $precision = 2) + { + if ($this->isEmpty()) { + return null; + } + + return round( + $this->filter($callback)->count() / $this->count() * 100, + $precision + ); + } + + /** + * Get the sum of the given values. + * + * @template TReturnType + * + * @param (callable(TValue): TReturnType)|string|null $callback + * @return ($callback is callable ? TReturnType : mixed) + */ + public function sum($callback = null) + { + $callback = is_null($callback) + ? $this->identity() + : $this->valueRetriever($callback); + + return $this->reduce(fn ($result, $item) => $result + $callback($item), 0); + } + + /** + * Apply the callback if the collection is empty. + * + * @template TWhenEmptyReturnType + * + * @param (callable($this): TWhenEmptyReturnType) $callback + * @param (callable($this): TWhenEmptyReturnType)|null $default + * @return $this|TWhenEmptyReturnType + */ + public function whenEmpty(callable $callback, ?callable $default = null) + { + return $this->when($this->isEmpty(), $callback, $default); + } + + /** + * Apply the callback if the collection is not empty. + * + * @template TWhenNotEmptyReturnType + * + * @param callable($this): TWhenNotEmptyReturnType $callback + * @param (callable($this): TWhenNotEmptyReturnType)|null $default + * @return $this|TWhenNotEmptyReturnType + */ + public function whenNotEmpty(callable $callback, ?callable $default = null) + { + return $this->when($this->isNotEmpty(), $callback, $default); + } + + /** + * Apply the callback unless the collection is empty. + * + * @template TUnlessEmptyReturnType + * + * @param callable($this): TUnlessEmptyReturnType $callback + * @param (callable($this): TUnlessEmptyReturnType)|null $default + * @return $this|TUnlessEmptyReturnType + */ + public function unlessEmpty(callable $callback, ?callable $default = null) + { + return $this->whenNotEmpty($callback, $default); + } + + /** + * Apply the callback unless the collection is not empty. + * + * @template TUnlessNotEmptyReturnType + * + * @param callable($this): TUnlessNotEmptyReturnType $callback + * @param (callable($this): TUnlessNotEmptyReturnType)|null $default + * @return $this|TUnlessNotEmptyReturnType + */ + public function unlessNotEmpty(callable $callback, ?callable $default = null) + { + return $this->whenEmpty($callback, $default); + } + + /** + * Filter items by the given key value pair. + * + * @param callable|string $key + * @param mixed $operator + * @param mixed $value + * @return static + */ + public function where($key, $operator = null, $value = null) + { + return $this->filter($this->operatorForWhere(...func_get_args())); + } + + /** + * Filter items where the value for the given key is null. + * + * @param string|null $key + * @return static + */ + public function whereNull($key = null) + { + return $this->whereStrict($key, null); + } + + /** + * Filter items where the value for the given key is not null. + * + * @param string|null $key + * @return static + */ + public function whereNotNull($key = null) + { + return $this->where($key, '!==', null); + } + + /** + * Filter items by the given key value pair using strict comparison. + * + * @param string $key + * @param mixed $value + * @return static + */ + public function whereStrict($key, $value) + { + return $this->where($key, '===', $value); + } + + /** + * Filter items by the given key value pair. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @param bool $strict + * @return static + */ + public function whereIn($key, $values, $strict = false) + { + $values = $this->getArrayableItems($values); + + return $this->filter(fn ($item) => in_array(data_get($item, $key), $values, $strict)); + } + + /** + * Filter items by the given key value pair using strict comparison. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereInStrict($key, $values) + { + return $this->whereIn($key, $values, true); + } + + /** + * Filter items such that the value of the given key is between the given values. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereBetween($key, $values) + { + return $this->where($key, '>=', reset($values))->where($key, '<=', end($values)); + } + + /** + * Filter items such that the value of the given key is not between the given values. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereNotBetween($key, $values) + { + return $this->filter( + fn ($item) => data_get($item, $key) < reset($values) || data_get($item, $key) > end($values) + ); + } + + /** + * Filter items by the given key value pair. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @param bool $strict + * @return static + */ + public function whereNotIn($key, $values, $strict = false) + { + $values = $this->getArrayableItems($values); + + return $this->reject(fn ($item) => in_array(data_get($item, $key), $values, $strict)); + } + + /** + * Filter items by the given key value pair using strict comparison. + * + * @param string $key + * @param \Illuminate\Contracts\Support\Arrayable|iterable $values + * @return static + */ + public function whereNotInStrict($key, $values) + { + return $this->whereNotIn($key, $values, true); + } + + /** + * Filter the items, removing any items that don't match the given type(s). + * + * @template TWhereInstanceOf + * + * @param class-string|array> $type + * @return static + */ + public function whereInstanceOf($type) + { + return $this->filter(function ($value) use ($type) { + if (is_array($type)) { + foreach ($type as $classType) { + if ($value instanceof $classType) { + return true; + } + } + + return false; + } + + return $value instanceof $type; + }); + } + + /** + * Pass the collection to the given callback and return the result. + * + * @template TPipeReturnType + * + * @param callable($this): TPipeReturnType $callback + * @return TPipeReturnType + */ + public function pipe(callable $callback) + { + return $callback($this); + } + + /** + * Pass the collection into a new class. + * + * @template TPipeIntoValue + * + * @param class-string $class + * @return TPipeIntoValue + */ + public function pipeInto($class) + { + return new $class($this); + } + + /** + * Pass the collection through a series of callable pipes and return the result. + * + * @param array $callbacks + * @return mixed + */ + public function pipeThrough($callbacks) + { + return (new Collection($callbacks))->reduce( + fn ($carry, $callback) => $callback($carry), + $this, + ); + } + + /** + * Reduce the collection to a single value. + * + * @template TReduceInitial + * @template TReduceReturnType + * + * @param callable(TReduceInitial|TReduceReturnType, TValue, TKey): TReduceReturnType $callback + * @param TReduceInitial $initial + * @return TReduceReturnType + */ + public function reduce(callable $callback, $initial = null) + { + $result = $initial; + + foreach ($this as $key => $value) { + $result = $callback($result, $value, $key); + } + + return $result; + } + + /** + * Reduce the collection to multiple aggregate values. + * + * @param callable $callback + * @param mixed ...$initial + * @return array + * + * @throws \UnexpectedValueException + */ + public function reduceSpread(callable $callback, ...$initial) + { + $result = $initial; + + foreach ($this as $key => $value) { + $result = call_user_func_array($callback, array_merge($result, [$value, $key])); + + if (! is_array($result)) { + throw new UnexpectedValueException(sprintf( + "%s::reduceSpread expects reducer to return an array, but got a '%s' instead.", + class_basename(static::class), gettype($result) + )); + } + } + + return $result; + } + + /** + * Reduce an associative collection to a single value. + * + * @template TReduceWithKeysInitial + * @template TReduceWithKeysReturnType + * + * @param callable(TReduceWithKeysInitial|TReduceWithKeysReturnType, TValue, TKey): TReduceWithKeysReturnType $callback + * @param TReduceWithKeysInitial $initial + * @return TReduceWithKeysReturnType + */ + public function reduceWithKeys(callable $callback, $initial = null) + { + return $this->reduce($callback, $initial); + } + + /** + * Create a collection of all elements that do not pass a given truth test. + * + * @param (callable(TValue, TKey): bool)|bool|TValue $callback + * @return static + */ + public function reject($callback = true) + { + $useAsCallable = $this->useAsCallable($callback); + + return $this->filter(function ($value, $key) use ($callback, $useAsCallable) { + return $useAsCallable + ? ! $callback($value, $key) + : $value != $callback; + }); + } + + /** + * Pass the collection to the given callback and then return it. + * + * @param callable($this): mixed $callback + * @return $this + */ + public function tap(callable $callback) + { + $callback($this); + + return $this; + } + + /** + * Return only unique items from the collection array. + * + * @param (callable(TValue, TKey): mixed)|string|null $key + * @param bool $strict + * @return static + */ + public function unique($key = null, $strict = false) + { + $callback = $this->valueRetriever($key); + + $exists = []; + + return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { + if (in_array($id = $callback($item, $key), $exists, $strict)) { + return true; + } + + $exists[] = $id; + }); + } + + /** + * Return only unique items from the collection array using strict comparison. + * + * @param (callable(TValue, TKey): mixed)|string|null $key + * @return static + */ + public function uniqueStrict($key = null) + { + return $this->unique($key, true); + } + + /** + * Collect the values into a collection. + * + * @return \Illuminate\Support\Collection + */ + public function collect() + { + return new Collection($this->all()); + } + + /** + * Get the collection of items as a plain array. + * + * @return array + */ + public function toArray() + { + return $this->map(fn ($value) => $value instanceof Arrayable ? $value->toArray() : $value)->all(); + } + + /** + * Convert the object into something JSON serializable. + * + * @return array + */ + public function jsonSerialize(): array + { + return array_map(function ($value) { + if ($value instanceof JsonSerializable) { + return $value->jsonSerialize(); + } elseif ($value instanceof Jsonable) { + return json_decode($value->toJson(), true); + } elseif ($value instanceof Arrayable) { + return $value->toArray(); + } + + return $value; + }, $this->all()); + } + + /** + * Get the collection of items as JSON. + * + * @param int $options + * @return string + */ + public function toJson($options = 0) + { + return json_encode($this->jsonSerialize(), $options); + } + + /** + * Get a CachingIterator instance. + * + * @param int $flags + * @return \CachingIterator + */ + public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING) + { + return new CachingIterator($this->getIterator(), $flags); + } + + /** + * Convert the collection to its string representation. + * + * @return string + */ + public function __toString() + { + return $this->escapeWhenCastingToString + ? e($this->toJson()) + : $this->toJson(); + } + + /** + * Indicate that the model's string representation should be escaped when __toString is invoked. + * + * @param bool $escape + * @return $this + */ + public function escapeWhenCastingToString($escape = true) + { + $this->escapeWhenCastingToString = $escape; + + return $this; + } + + /** + * Add a method to the list of proxied methods. + * + * @param string $method + * @return void + */ + public static function proxy($method) + { + static::$proxies[] = $method; + } + + /** + * Dynamically access collection proxies. + * + * @param string $key + * @return mixed + * + * @throws \Exception + */ + public function __get($key) + { + if (! in_array($key, static::$proxies)) { + throw new Exception("Property [{$key}] does not exist on this collection instance."); + } + + return new HigherOrderCollectionProxy($this, $key); + } + + /** + * Results array of items from Collection or Arrayable. + * + * @param mixed $items + * @return array + */ + protected function getArrayableItems($items) + { + return is_null($items) || is_scalar($items) || $items instanceof UnitEnum + ? Arr::wrap($items) + : Arr::from($items); + } + + /** + * Get an operator checker callback. + * + * @param callable|string $key + * @param string|null $operator + * @param mixed $value + * @return \Closure + */ + protected function operatorForWhere($key, $operator = null, $value = null) + { + if ($this->useAsCallable($key)) { + return $key; + } + + if (func_num_args() === 1) { + $value = true; + + $operator = '='; + } + + if (func_num_args() === 2) { + $value = $operator; + + $operator = '='; + } + + return function ($item) use ($key, $operator, $value) { + $retrieved = enum_value(data_get($item, $key)); + $value = enum_value($value); + + $strings = array_filter([$retrieved, $value], function ($value) { + return match (true) { + is_string($value) => true, + $value instanceof \Stringable => true, + default => false, + }; + }); + + if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) { + return in_array($operator, ['!=', '<>', '!==']); + } + + switch ($operator) { + default: + case '=': + case '==': return $retrieved == $value; + case '!=': + case '<>': return $retrieved != $value; + case '<': return $retrieved < $value; + case '>': return $retrieved > $value; + case '<=': return $retrieved <= $value; + case '>=': return $retrieved >= $value; + case '===': return $retrieved === $value; + case '!==': return $retrieved !== $value; + case '<=>': return $retrieved <=> $value; + } + }; + } + + /** + * Determine if the given value is callable, but not a string. + * + * @param mixed $value + * @return bool + */ + protected function useAsCallable($value) + { + return ! is_string($value) && is_callable($value); + } + + /** + * Get a value retrieving callback. + * + * @param callable|string|null $value + * @return callable + */ + protected function valueRetriever($value) + { + if ($this->useAsCallable($value)) { + return $value; + } + + return fn ($item) => data_get($item, $value); + } + + /** + * Make a function to check an item's equality. + * + * @param mixed $value + * @return \Closure(mixed): bool + */ + protected function equality($value) + { + return fn ($item) => $item === $value; + } + + /** + * Make a function using another function, by negating its result. + * + * @param \Closure $callback + * @return \Closure + */ + protected function negate(Closure $callback) + { + return fn (...$params) => ! $callback(...$params); + } + + /** + * Make a function that returns what's passed to it. + * + * @return \Closure(TValue): TValue + */ + protected function identity() + { + return fn ($value) => $value; + } +} diff --git a/vendor/illuminate/collections/Traits/TransformsToResourceCollection.php b/vendor/illuminate/collections/Traits/TransformsToResourceCollection.php new file mode 100644 index 0000000..22143b3 --- /dev/null +++ b/vendor/illuminate/collections/Traits/TransformsToResourceCollection.php @@ -0,0 +1,68 @@ +|null $resourceClass + * @return \Illuminate\Http\Resources\Json\ResourceCollection + * + * @throws \Throwable + */ + public function toResourceCollection(?string $resourceClass = null): ResourceCollection + { + if ($resourceClass === null) { + return $this->guessResourceCollection(); + } + + return $resourceClass::collection($this); + } + + /** + * Guess the resource collection for the items. + * + * @return \Illuminate\Http\Resources\Json\ResourceCollection + * + * @throws \Throwable + */ + protected function guessResourceCollection(): ResourceCollection + { + if ($this->isEmpty()) { + return new ResourceCollection($this); + } + + $model = $this->items[0] ?? null; + + throw_unless(is_object($model), LogicException::class, 'Resource collection guesser expects the collection to contain objects.'); + + /** @var class-string $className */ + $className = get_class($model); + + throw_unless(method_exists($className, 'guessResourceName'), LogicException::class, sprintf('Expected class %s to implement guessResourceName method. Make sure the model uses the TransformsToResource trait.', $className)); + + $resourceClasses = $className::guessResourceName(); + + foreach ($resourceClasses as $resourceClass) { + $resourceCollection = $resourceClass.'Collection'; + + if (is_string($resourceCollection) && class_exists($resourceCollection)) { + return new $resourceCollection($this); + } + } + + foreach ($resourceClasses as $resourceClass) { + if (is_string($resourceClass) && class_exists($resourceClass)) { + return $resourceClass::collection($this); + } + } + + throw new LogicException(sprintf('Failed to find resource class for model [%s].', $className)); + } +} diff --git a/vendor/illuminate/collections/composer.json b/vendor/illuminate/collections/composer.json new file mode 100644 index 0000000..8d9c961 --- /dev/null +++ b/vendor/illuminate/collections/composer.json @@ -0,0 +1,44 @@ +{ + "name": "illuminate/collections", + "description": "The Illuminate Collections package.", + "license": "MIT", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "require": { + "php": "^8.2", + "illuminate/conditionable": "^12.0", + "illuminate/contracts": "^12.0", + "illuminate/macroable": "^12.0" + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + }, + "files": [ + "functions.php", + "helpers.php" + ] + }, + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "suggest": { + "illuminate/http": "Required to convert collections to API resources (^12.0).", + "symfony/var-dumper": "Required to use the dump method (^7.2)." + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev" +} diff --git a/vendor/illuminate/collections/functions.php b/vendor/illuminate/collections/functions.php new file mode 100644 index 0000000..6ccd9b3 --- /dev/null +++ b/vendor/illuminate/collections/functions.php @@ -0,0 +1,27 @@ + $value->value, + $value instanceof \UnitEnum => $value->name, + + default => $value ?? value($default), + }; + } +} diff --git a/vendor/illuminate/collections/helpers.php b/vendor/illuminate/collections/helpers.php new file mode 100644 index 0000000..16c8f01 --- /dev/null +++ b/vendor/illuminate/collections/helpers.php @@ -0,0 +1,259 @@ +|iterable|null $value + * @return \Illuminate\Support\Collection + */ + function collect($value = []) + { + return new Collection($value); + } +} + +if (! function_exists('data_fill')) { + /** + * Fill in data where it's missing. + * + * @param mixed $target + * @param string|array $key + * @param mixed $value + * @return mixed + */ + function data_fill(&$target, $key, $value) + { + return data_set($target, $key, $value, false); + } +} + +if (! function_exists('data_get')) { + /** + * Get an item from an array or object using "dot" notation. + * + * @param mixed $target + * @param string|array|int|null $key + * @param mixed $default + * @return mixed + */ + function data_get($target, $key, $default = null) + { + if (is_null($key)) { + return $target; + } + + $key = is_array($key) ? $key : explode('.', $key); + + foreach ($key as $i => $segment) { + unset($key[$i]); + + if (is_null($segment)) { + return $target; + } + + if ($segment === '*') { + if ($target instanceof Collection) { + $target = $target->all(); + } elseif (! is_iterable($target)) { + return value($default); + } + + $result = []; + + foreach ($target as $item) { + $result[] = data_get($item, $key); + } + + return in_array('*', $key) ? Arr::collapse($result) : $result; + } + + $segment = match ($segment) { + '\*' => '*', + '\{first}' => '{first}', + '{first}' => array_key_first(Arr::from($target)), + '\{last}' => '{last}', + '{last}' => array_key_last(Arr::from($target)), + default => $segment, + }; + + if (Arr::accessible($target) && Arr::exists($target, $segment)) { + $target = $target[$segment]; + } elseif (is_object($target) && isset($target->{$segment})) { + $target = $target->{$segment}; + } else { + return value($default); + } + } + + return $target; + } +} + +if (! function_exists('data_set')) { + /** + * Set an item on an array or object using dot notation. + * + * @param mixed $target + * @param string|array $key + * @param mixed $value + * @param bool $overwrite + * @return mixed + */ + function data_set(&$target, $key, $value, $overwrite = true) + { + $segments = is_array($key) ? $key : explode('.', $key); + + if (($segment = array_shift($segments)) === '*') { + if (! Arr::accessible($target)) { + $target = []; + } + + if ($segments) { + foreach ($target as &$inner) { + data_set($inner, $segments, $value, $overwrite); + } + } elseif ($overwrite) { + foreach ($target as &$inner) { + $inner = $value; + } + } + } elseif (Arr::accessible($target)) { + if ($segments) { + if (! Arr::exists($target, $segment)) { + $target[$segment] = []; + } + + data_set($target[$segment], $segments, $value, $overwrite); + } elseif ($overwrite || ! Arr::exists($target, $segment)) { + $target[$segment] = $value; + } + } elseif (is_object($target)) { + if ($segments) { + if (! isset($target->{$segment})) { + $target->{$segment} = []; + } + + data_set($target->{$segment}, $segments, $value, $overwrite); + } elseif ($overwrite || ! isset($target->{$segment})) { + $target->{$segment} = $value; + } + } else { + $target = []; + + if ($segments) { + data_set($target[$segment], $segments, $value, $overwrite); + } elseif ($overwrite) { + $target[$segment] = $value; + } + } + + return $target; + } +} + +if (! function_exists('data_forget')) { + /** + * Remove / unset an item from an array or object using "dot" notation. + * + * @param mixed $target + * @param string|array|int|null $key + * @return mixed + */ + function data_forget(&$target, $key) + { + $segments = is_array($key) ? $key : explode('.', $key); + + if (($segment = array_shift($segments)) === '*' && Arr::accessible($target)) { + if ($segments) { + foreach ($target as &$inner) { + data_forget($inner, $segments); + } + } + } elseif (Arr::accessible($target)) { + if ($segments && Arr::exists($target, $segment)) { + data_forget($target[$segment], $segments); + } else { + Arr::forget($target, $segment); + } + } elseif (is_object($target)) { + if ($segments && isset($target->{$segment})) { + data_forget($target->{$segment}, $segments); + } elseif (isset($target->{$segment})) { + unset($target->{$segment}); + } + } + + return $target; + } +} + +if (! function_exists('head')) { + /** + * Get the first element of an array. Useful for method chaining. + * + * @param array $array + * @return mixed + */ + function head($array) + { + return reset($array); + } +} + +if (! function_exists('last')) { + /** + * Get the last element from an array. + * + * @param array $array + * @return mixed + */ + function last($array) + { + return end($array); + } +} + +if (! function_exists('value')) { + /** + * Return the default value of the given value. + * + * @template TValue + * @template TArgs + * + * @param TValue|\Closure(TArgs): TValue $value + * @param TArgs ...$args + * @return TValue + */ + function value($value, ...$args) + { + return $value instanceof Closure ? $value(...$args) : $value; + } +} + +if (! function_exists('when')) { + /** + * Return a value if the given condition is true. + * + * @param mixed $condition + * @param \Closure|mixed $value + * @param \Closure|mixed $default + * @return mixed + */ + function when($condition, $value, $default = null) + { + $condition = $condition instanceof Closure ? $condition() : $condition; + + if ($condition) { + return value($value, $condition); + } + + return value($default, $condition); + } +} diff --git a/vendor/illuminate/conditionable/HigherOrderWhenProxy.php b/vendor/illuminate/conditionable/HigherOrderWhenProxy.php new file mode 100644 index 0000000..0a694c2 --- /dev/null +++ b/vendor/illuminate/conditionable/HigherOrderWhenProxy.php @@ -0,0 +1,108 @@ +target = $target; + } + + /** + * Set the condition on the proxy. + * + * @param bool $condition + * @return $this + */ + public function condition($condition) + { + [$this->condition, $this->hasCondition] = [$condition, true]; + + return $this; + } + + /** + * Indicate that the condition should be negated. + * + * @return $this + */ + public function negateConditionOnCapture() + { + $this->negateConditionOnCapture = true; + + return $this; + } + + /** + * Proxy accessing an attribute onto the target. + * + * @param string $key + * @return mixed + */ + public function __get($key) + { + if (! $this->hasCondition) { + $condition = $this->target->{$key}; + + return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); + } + + return $this->condition + ? $this->target->{$key} + : $this->target; + } + + /** + * Proxy a method call on the target. + * + * @param string $method + * @param array $parameters + * @return mixed + */ + public function __call($method, $parameters) + { + if (! $this->hasCondition) { + $condition = $this->target->{$method}(...$parameters); + + return $this->condition($this->negateConditionOnCapture ? ! $condition : $condition); + } + + return $this->condition + ? $this->target->{$method}(...$parameters) + : $this->target; + } +} diff --git a/vendor/illuminate/conditionable/LICENSE.md b/vendor/illuminate/conditionable/LICENSE.md new file mode 100644 index 0000000..79810c8 --- /dev/null +++ b/vendor/illuminate/conditionable/LICENSE.md @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) Taylor Otwell + +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/vendor/illuminate/conditionable/Traits/Conditionable.php b/vendor/illuminate/conditionable/Traits/Conditionable.php new file mode 100644 index 0000000..5e3194b --- /dev/null +++ b/vendor/illuminate/conditionable/Traits/Conditionable.php @@ -0,0 +1,73 @@ +condition($value); + } + + if ($value) { + return $callback($this, $value) ?? $this; + } elseif ($default) { + return $default($this, $value) ?? $this; + } + + return $this; + } + + /** + * Apply the callback if the given "value" is (or resolves to) falsy. + * + * @template TUnlessParameter + * @template TUnlessReturnType + * + * @param (\Closure($this): TUnlessParameter)|TUnlessParameter|null $value + * @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $callback + * @param (callable($this, TUnlessParameter): TUnlessReturnType)|null $default + * @return $this|TUnlessReturnType + */ + public function unless($value = null, ?callable $callback = null, ?callable $default = null) + { + $value = $value instanceof Closure ? $value($this) : $value; + + if (func_num_args() === 0) { + return (new HigherOrderWhenProxy($this))->negateConditionOnCapture(); + } + + if (func_num_args() === 1) { + return (new HigherOrderWhenProxy($this))->condition(! $value); + } + + if (! $value) { + return $callback($this, $value) ?? $this; + } elseif ($default) { + return $default($this, $value) ?? $this; + } + + return $this; + } +} diff --git a/vendor/illuminate/conditionable/composer.json b/vendor/illuminate/conditionable/composer.json new file mode 100644 index 0000000..9e0ddfb --- /dev/null +++ b/vendor/illuminate/conditionable/composer.json @@ -0,0 +1,33 @@ +{ + "name": "illuminate/conditionable", + "description": "The Illuminate Conditionable package.", + "license": "MIT", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "require": { + "php": "^8.2" + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev" +} diff --git a/vendor/illuminate/contracts/Auth/Access/Authorizable.php b/vendor/illuminate/contracts/Auth/Access/Authorizable.php new file mode 100644 index 0000000..cedeb6e --- /dev/null +++ b/vendor/illuminate/contracts/Auth/Access/Authorizable.php @@ -0,0 +1,15 @@ + $id + * @return ($id is class-string ? TClass : mixed) + */ + public function get(string $id); + + /** + * Determine if the given abstract type has been bound. + * + * @param string $abstract + * @return bool + */ + public function bound($abstract); + + /** + * Alias a type to a different name. + * + * @param string $abstract + * @param string $alias + * @return void + * + * @throws \LogicException + */ + public function alias($abstract, $alias); + + /** + * Assign a set of tags to a given binding. + * + * @param array|string $abstracts + * @param array|mixed ...$tags + * @return void + */ + public function tag($abstracts, $tags); + + /** + * Resolve all of the bindings for a given tag. + * + * @param string $tag + * @return iterable + */ + public function tagged($tag); + + /** + * Register a binding with the container. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @param bool $shared + * @return void + */ + public function bind($abstract, $concrete = null, $shared = false); + + /** + * Bind a callback to resolve with Container::call. + * + * @param array|string $method + * @param \Closure $callback + * @return void + */ + public function bindMethod($method, $callback); + + /** + * Register a binding if it hasn't already been registered. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @param bool $shared + * @return void + */ + public function bindIf($abstract, $concrete = null, $shared = false); + + /** + * Register a shared binding in the container. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function singleton($abstract, $concrete = null); + + /** + * Register a shared binding if it hasn't already been registered. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function singletonIf($abstract, $concrete = null); + + /** + * Register a scoped binding in the container. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scoped($abstract, $concrete = null); + + /** + * Register a scoped binding if it hasn't already been registered. + * + * @param \Closure|string $abstract + * @param \Closure|string|null $concrete + * @return void + */ + public function scopedIf($abstract, $concrete = null); + + /** + * "Extend" an abstract type in the container. + * + * @param \Closure|string $abstract + * @param \Closure $closure + * @return void + * + * @throws \InvalidArgumentException + */ + public function extend($abstract, Closure $closure); + + /** + * Register an existing instance as shared in the container. + * + * @template TInstance of mixed + * + * @param \Closure|string $abstract + * @param TInstance $instance + * @return TInstance + */ + public function instance($abstract, $instance); + + /** + * Add a contextual binding to the container. + * + * @param string $concrete + * @param \Closure|string $abstract + * @param \Closure|string $implementation + * @return void + */ + public function addContextualBinding($concrete, $abstract, $implementation); + + /** + * Define a contextual binding. + * + * @param string|array $concrete + * @return \Illuminate\Contracts\Container\ContextualBindingBuilder + */ + public function when($concrete); + + /** + * Get a closure to resolve the given type from the container. + * + * @template TClass of object + * + * @param string|class-string $abstract + * @return ($abstract is class-string ? \Closure(): TClass : \Closure(): mixed) + */ + public function factory($abstract); + + /** + * Flush the container of all bindings and resolved instances. + * + * @return void + */ + public function flush(); + + /** + * Resolve the given type from the container. + * + * @template TClass of object + * + * @param string|class-string $abstract + * @param array $parameters + * @return ($abstract is class-string ? TClass : mixed) + * + * @throws \Illuminate\Contracts\Container\BindingResolutionException + */ + public function make($abstract, array $parameters = []); + + /** + * Call the given Closure / class@method and inject its dependencies. + * + * @param callable|string $callback + * @param array $parameters + * @param string|null $defaultMethod + * @return mixed + */ + public function call($callback, array $parameters = [], $defaultMethod = null); + + /** + * Determine if the given abstract type has been resolved. + * + * @param string $abstract + * @return bool + */ + public function resolved($abstract); + + /** + * Register a new before resolving callback. + * + * @param \Closure|string $abstract + * @param \Closure|null $callback + * @return void + */ + public function beforeResolving($abstract, ?Closure $callback = null); + + /** + * Register a new resolving callback. + * + * @param \Closure|string $abstract + * @param \Closure|null $callback + * @return void + */ + public function resolving($abstract, ?Closure $callback = null); + + /** + * Register a new after resolving callback. + * + * @param \Closure|string $abstract + * @param \Closure|null $callback + * @return void + */ + public function afterResolving($abstract, ?Closure $callback = null); +} diff --git a/vendor/illuminate/contracts/Container/ContextualAttribute.php b/vendor/illuminate/contracts/Container/ContextualAttribute.php new file mode 100644 index 0000000..06f6f06 --- /dev/null +++ b/vendor/illuminate/contracts/Container/ContextualAttribute.php @@ -0,0 +1,8 @@ +|CastsAttributes|CastsInboundAttributes + */ + public static function castUsing(array $arguments); +} diff --git a/vendor/illuminate/contracts/Database/Eloquent/CastsAttributes.php b/vendor/illuminate/contracts/Database/Eloquent/CastsAttributes.php new file mode 100644 index 0000000..89cec66 --- /dev/null +++ b/vendor/illuminate/contracts/Database/Eloquent/CastsAttributes.php @@ -0,0 +1,34 @@ + $attributes + * @return TGet|null + */ + public function get(Model $model, string $key, mixed $value, array $attributes); + + /** + * Transform the attribute to its underlying model values. + * + * @param \Illuminate\Database\Eloquent\Model $model + * @param string $key + * @param TSet|null $value + * @param array $attributes + * @return mixed + */ + public function set(Model $model, string $key, mixed $value, array $attributes); +} diff --git a/vendor/illuminate/contracts/Database/Eloquent/CastsInboundAttributes.php b/vendor/illuminate/contracts/Database/Eloquent/CastsInboundAttributes.php new file mode 100644 index 0000000..312f7ae --- /dev/null +++ b/vendor/illuminate/contracts/Database/Eloquent/CastsInboundAttributes.php @@ -0,0 +1,19 @@ + $attributes + * @return mixed + */ + public function set(Model $model, string $key, mixed $value, array $attributes); +} diff --git a/vendor/illuminate/contracts/Database/Eloquent/ComparesCastableAttributes.php b/vendor/illuminate/contracts/Database/Eloquent/ComparesCastableAttributes.php new file mode 100644 index 0000000..5c9ad19 --- /dev/null +++ b/vendor/illuminate/contracts/Database/Eloquent/ComparesCastableAttributes.php @@ -0,0 +1,19 @@ + $attributes + * @return mixed + */ + public function serialize(Model $model, string $key, mixed $value, array $attributes); +} diff --git a/vendor/illuminate/contracts/Database/Eloquent/SupportsPartialRelations.php b/vendor/illuminate/contracts/Database/Eloquent/SupportsPartialRelations.php new file mode 100644 index 0000000..c82125a --- /dev/null +++ b/vendor/illuminate/contracts/Database/Eloquent/SupportsPartialRelations.php @@ -0,0 +1,30 @@ + + */ + public $class; + + /** + * The unique identifier of the model. + * + * This may be either a single ID or an array of IDs. + * + * @var mixed + */ + public $id; + + /** + * The relationships loaded on the model. + * + * @var array + */ + public $relations; + + /** + * The connection name of the model. + * + * @var string|null + */ + public $connection; + + /** + * The class name of the model collection. + * + * @var class-string<\Illuminate\Database\Eloquent\Collection>|null + */ + public $collectionClass; + + /** + * Create a new model identifier. + * + * @param class-string<\Illuminate\Database\Eloquent\Model> $class + * @param mixed $id + * @param array $relations + * @param mixed $connection + */ + public function __construct($class, $id, array $relations, $connection) + { + $this->id = $id; + $this->class = $class; + $this->relations = $relations; + $this->connection = $connection; + } + + /** + * Specify the collection class that should be used when serializing / restoring collections. + * + * @param class-string<\Illuminate\Database\Eloquent\Collection> $collectionClass + * @return $this + */ + public function useCollectionClass(?string $collectionClass) + { + $this->collectionClass = $collectionClass; + + return $this; + } +} diff --git a/vendor/illuminate/contracts/Database/Query/Builder.php b/vendor/illuminate/contracts/Database/Query/Builder.php new file mode 100644 index 0000000..e116ebf --- /dev/null +++ b/vendor/illuminate/contracts/Database/Query/Builder.php @@ -0,0 +1,12 @@ + + */ + public function files($directory = null, $recursive = false); + + /** + * Get all of the files from the given directory (recursive). + * + * @param string|null $directory + * @return array + */ + public function allFiles($directory = null); + + /** + * Get all of the directories within a given directory. + * + * @param string|null $directory + * @param bool $recursive + * @return array + */ + public function directories($directory = null, $recursive = false); + + /** + * Get all (recursive) of the directories within a given directory. + * + * @param string|null $directory + * @return array + */ + public function allDirectories($directory = null); + + /** + * Create a directory. + * + * @param string $path + * @return bool + */ + public function makeDirectory($path); + + /** + * Recursively delete a directory. + * + * @param string $directory + * @return bool + */ + public function deleteDirectory($directory); +} diff --git a/vendor/illuminate/contracts/Filesystem/LockTimeoutException.php b/vendor/illuminate/contracts/Filesystem/LockTimeoutException.php new file mode 100644 index 0000000..f03f5c4 --- /dev/null +++ b/vendor/illuminate/contracts/Filesystem/LockTimeoutException.php @@ -0,0 +1,10 @@ + + */ + public function items(); + + /** + * Get the "cursor" of the previous set of items. + * + * @return \Illuminate\Pagination\Cursor|null + */ + public function previousCursor(); + + /** + * Get the "cursor" of the next set of items. + * + * @return \Illuminate\Pagination\Cursor|null + */ + public function nextCursor(); + + /** + * Determine how many items are being shown per page. + * + * @return int + */ + public function perPage(); + + /** + * Get the current cursor being paginated. + * + * @return \Illuminate\Pagination\Cursor|null + */ + public function cursor(); + + /** + * Determine if there are enough items to split into multiple pages. + * + * @return bool + */ + public function hasPages(); + + /** + * Determine if there are more items in the data source. + * + * @return bool + */ + public function hasMorePages(); + + /** + * Get the base path for paginator generated URLs. + * + * @return string|null + */ + public function path(); + + /** + * Determine if the list of items is empty or not. + * + * @return bool + */ + public function isEmpty(); + + /** + * Determine if the list of items is not empty. + * + * @return bool + */ + public function isNotEmpty(); + + /** + * Render the paginator using a given view. + * + * @param string|null $view + * @param array $data + * @return string + */ + public function render($view = null, $data = []); +} diff --git a/vendor/illuminate/contracts/Pagination/LengthAwarePaginator.php b/vendor/illuminate/contracts/Pagination/LengthAwarePaginator.php new file mode 100644 index 0000000..bd08082 --- /dev/null +++ b/vendor/illuminate/contracts/Pagination/LengthAwarePaginator.php @@ -0,0 +1,36 @@ + + */ +interface LengthAwarePaginator extends Paginator +{ + /** + * Create a range of pagination URLs. + * + * @param int $start + * @param int $end + * @return array + */ + public function getUrlRange($start, $end); + + /** + * Determine the total number of items in the data store. + * + * @return int + */ + public function total(); + + /** + * Get the page number of the last available page. + * + * @return int + */ + public function lastPage(); +} diff --git a/vendor/illuminate/contracts/Pagination/Paginator.php b/vendor/illuminate/contracts/Pagination/Paginator.php new file mode 100644 index 0000000..409ea1d --- /dev/null +++ b/vendor/illuminate/contracts/Pagination/Paginator.php @@ -0,0 +1,138 @@ + + */ + public function items(); + + /** + * Get the "index" of the first item being paginated. + * + * @return int|null + */ + public function firstItem(); + + /** + * Get the "index" of the last item being paginated. + * + * @return int|null + */ + public function lastItem(); + + /** + * Determine how many items are being shown per page. + * + * @return int + */ + public function perPage(); + + /** + * Determine the current page being paginated. + * + * @return int + */ + public function currentPage(); + + /** + * Determine if there are enough items to split into multiple pages. + * + * @return bool + */ + public function hasPages(); + + /** + * Determine if there are more items in the data store. + * + * @return bool + */ + public function hasMorePages(); + + /** + * Get the base path for paginator generated URLs. + * + * @return string|null + */ + public function path(); + + /** + * Determine if the list of items is empty or not. + * + * @return bool + */ + public function isEmpty(); + + /** + * Determine if the list of items is not empty. + * + * @return bool + */ + public function isNotEmpty(); + + /** + * Render the paginator using a given view. + * + * @param string|null $view + * @param array $data + * @return string + */ + public function render($view = null, $data = []); +} diff --git a/vendor/illuminate/contracts/Pipeline/Hub.php b/vendor/illuminate/contracts/Pipeline/Hub.php new file mode 100644 index 0000000..1ae675f --- /dev/null +++ b/vendor/illuminate/contracts/Pipeline/Hub.php @@ -0,0 +1,15 @@ + + */ + public function getQueueableIds(); + + /** + * Get the relationships of the entities being queued. + * + * @return array + */ + public function getQueueableRelations(); + + /** + * Get the connection of the entities being queued. + * + * @return string|null + */ + public function getQueueableConnection(); +} diff --git a/vendor/illuminate/contracts/Queue/QueueableEntity.php b/vendor/illuminate/contracts/Queue/QueueableEntity.php new file mode 100644 index 0000000..366f0c8 --- /dev/null +++ b/vendor/illuminate/contracts/Queue/QueueableEntity.php @@ -0,0 +1,27 @@ + + */ + public function toArray(); +} diff --git a/vendor/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php b/vendor/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php new file mode 100644 index 0000000..e1be6fe --- /dev/null +++ b/vendor/illuminate/contracts/Support/CanBeEscapedWhenCastToString.php @@ -0,0 +1,14 @@ +name)) { - $method->setAccessible(true); static::macro($method->name, $method->invoke($mixin)); } } @@ -62,6 +64,16 @@ trait Macroable return isset(static::$macros[$name]); } + /** + * Flush the existing macros. + * + * @return void + */ + public static function flushMacros() + { + static::$macros = []; + } + /** * Dynamically handle calls to the class. * @@ -82,7 +94,7 @@ trait Macroable $macro = static::$macros[$method]; if ($macro instanceof Closure) { - return call_user_func_array(Closure::bind($macro, null, static::class), $parameters); + $macro = $macro->bindTo(null, static::class); } return $macro(...$parameters); @@ -108,7 +120,7 @@ trait Macroable $macro = static::$macros[$method]; if ($macro instanceof Closure) { - return call_user_func_array($macro->bindTo($this, static::class), $parameters); + $macro = $macro->bindTo($this, static::class); } return $macro(...$parameters); diff --git a/vendor/illuminate/macroable/composer.json b/vendor/illuminate/macroable/composer.json new file mode 100644 index 0000000..38dc6f1 --- /dev/null +++ b/vendor/illuminate/macroable/composer.json @@ -0,0 +1,33 @@ +{ + "name": "illuminate/macroable", + "description": "The Illuminate Macroable package.", + "license": "MIT", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "require": { + "php": "^8.2" + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "extra": { + "branch-alias": { + "dev-master": "12.x-dev" + } + }, + "config": { + "sort-packages": true + }, + "minimum-stability": "dev" +} diff --git a/vendor/psr/container/.gitignore b/vendor/psr/container/.gitignore new file mode 100644 index 0000000..b2395aa --- /dev/null +++ b/vendor/psr/container/.gitignore @@ -0,0 +1,3 @@ +composer.lock +composer.phar +/vendor/ diff --git a/vendor/psr/container/LICENSE b/vendor/psr/container/LICENSE new file mode 100644 index 0000000..2877a48 --- /dev/null +++ b/vendor/psr/container/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2013-2016 container-interop +Copyright (c) 2016 PHP Framework Interoperability Group + +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/vendor/psr/container/README.md b/vendor/psr/container/README.md new file mode 100644 index 0000000..1b9d9e5 --- /dev/null +++ b/vendor/psr/container/README.md @@ -0,0 +1,13 @@ +Container interface +============== + +This repository holds all interfaces related to [PSR-11 (Container Interface)][psr-url]. + +Note that this is not a Container implementation of its own. It is merely abstractions that describe the components of a Dependency Injection Container. + +The installable [package][package-url] and [implementations][implementation-url] are listed on Packagist. + +[psr-url]: https://www.php-fig.org/psr/psr-11/ +[package-url]: https://packagist.org/packages/psr/container +[implementation-url]: https://packagist.org/providers/psr/container-implementation + diff --git a/vendor/psr/container/composer.json b/vendor/psr/container/composer.json new file mode 100644 index 0000000..baf6cd1 --- /dev/null +++ b/vendor/psr/container/composer.json @@ -0,0 +1,27 @@ +{ + "name": "psr/container", + "type": "library", + "description": "Common Container Interface (PHP FIG PSR-11)", + "keywords": ["psr", "psr-11", "container", "container-interop", "container-interface"], + "homepage": "https://github.com/php-fig/container", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=7.4.0" + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + } +} diff --git a/vendor/psr/container/src/ContainerExceptionInterface.php b/vendor/psr/container/src/ContainerExceptionInterface.php new file mode 100644 index 0000000..0f213f2 --- /dev/null +++ b/vendor/psr/container/src/ContainerExceptionInterface.php @@ -0,0 +1,12 @@ + 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/vendor/psr/simple-cache/README.md b/vendor/psr/simple-cache/README.md new file mode 100644 index 0000000..43641d1 --- /dev/null +++ b/vendor/psr/simple-cache/README.md @@ -0,0 +1,8 @@ +PHP FIG Simple Cache PSR +======================== + +This repository holds all interfaces related to PSR-16. + +Note that this is not a cache implementation of its own. It is merely an interface that describes a cache implementation. See [the specification](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-16-simple-cache.md) for more details. + +You can find implementations of the specification by looking for packages providing the [psr/simple-cache-implementation](https://packagist.org/providers/psr/simple-cache-implementation) virtual package. diff --git a/vendor/psr/simple-cache/composer.json b/vendor/psr/simple-cache/composer.json new file mode 100644 index 0000000..f307a84 --- /dev/null +++ b/vendor/psr/simple-cache/composer.json @@ -0,0 +1,25 @@ +{ + "name": "psr/simple-cache", + "description": "Common interfaces for simple caching", + "keywords": ["psr", "psr-16", "cache", "simple-cache", "caching"], + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=8.0.0" + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + } +} diff --git a/vendor/psr/simple-cache/src/CacheException.php b/vendor/psr/simple-cache/src/CacheException.php new file mode 100644 index 0000000..f61b24c --- /dev/null +++ b/vendor/psr/simple-cache/src/CacheException.php @@ -0,0 +1,10 @@ + $keys A list of keys that can be obtained in a single operation. + * @param mixed $default Default value to return for keys that do not exist. + * + * @return iterable A list of key => value pairs. Cache keys that do not exist or are stale will have $default as value. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function getMultiple(iterable $keys, mixed $default = null): iterable; + + /** + * Persists a set of key => value pairs in the cache, with an optional TTL. + * + * @param iterable $values A list of key => value pairs for a multiple-set operation. + * @param null|int|\DateInterval $ttl Optional. The TTL value of this item. If no value is sent and + * the driver supports TTL then the library may set a default value + * for it or let the driver take care of that. + * + * @return bool True on success and false on failure. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $values is neither an array nor a Traversable, + * or if any of the $values are not a legal value. + */ + public function setMultiple(iterable $values, null|int|\DateInterval $ttl = null): bool; + + /** + * Deletes multiple cache items in a single operation. + * + * @param iterable $keys A list of string-based keys to be deleted. + * + * @return bool True if the items were successfully removed. False if there was an error. + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if $keys is neither an array nor a Traversable, + * or if any of the $keys are not a legal value. + */ + public function deleteMultiple(iterable $keys): bool; + + /** + * Determines whether an item is present in the cache. + * + * NOTE: It is recommended that has() is only to be used for cache warming type purposes + * and not to be used within your live applications operations for get/set, as this method + * is subject to a race condition where your has() will return true and immediately after, + * another script can remove it making the state of your app out of date. + * + * @param string $key The cache item key. + * + * @return bool + * + * @throws \Psr\SimpleCache\InvalidArgumentException + * MUST be thrown if the $key string is not a legal value. + */ + public function has(string $key): bool; +} diff --git a/vendor/psr/simple-cache/src/InvalidArgumentException.php b/vendor/psr/simple-cache/src/InvalidArgumentException.php new file mode 100644 index 0000000..6a9524a --- /dev/null +++ b/vendor/psr/simple-cache/src/InvalidArgumentException.php @@ -0,0 +1,13 @@ +=8.1" + }, + "autoload": { + "files": [ + "function.php" + ] + }, + "minimum-stability": "dev", + "extra": { + "branch-alias": { + "dev-main": "3.6-dev" + }, + "thanks": { + "name": "symfony/contracts", + "url": "https://github.com/symfony/contracts" + } + } +} diff --git a/vendor/symfony/deprecation-contracts/function.php b/vendor/symfony/deprecation-contracts/function.php new file mode 100644 index 0000000..2d56512 --- /dev/null +++ b/vendor/symfony/deprecation-contracts/function.php @@ -0,0 +1,27 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +if (!function_exists('trigger_deprecation')) { + /** + * Triggers a silenced deprecation notice. + * + * @param string $package The name of the Composer package that is triggering the deprecation + * @param string $version The version of the package that introduced the deprecation + * @param string $message The message of the deprecation + * @param mixed ...$args Values to insert in the message using printf() formatting + * + * @author Nicolas Grekas + */ + function trigger_deprecation(string $package, string $version, string $message, mixed ...$args): void + { + @trigger_error(($package || $version ? "Since $package $version: " : '').($args ? vsprintf($message, $args) : $message), \E_USER_DEPRECATED); + } +} diff --git a/vendor/symfony/polyfill-mbstring/LICENSE b/vendor/symfony/polyfill-mbstring/LICENSE index 4cd8bdd..6e3afce 100644 --- a/vendor/symfony/polyfill-mbstring/LICENSE +++ b/vendor/symfony/polyfill-mbstring/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2015-2019 Fabien Potencier +Copyright (c) 2015-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 diff --git a/vendor/symfony/polyfill-mbstring/Mbstring.php b/vendor/symfony/polyfill-mbstring/Mbstring.php index 15503bc..31e36a3 100644 --- a/vendor/symfony/polyfill-mbstring/Mbstring.php +++ b/vendor/symfony/polyfill-mbstring/Mbstring.php @@ -48,6 +48,11 @@ namespace Symfony\Polyfill\Mbstring; * - mb_strstr - Finds first occurrence of a string within another * - mb_strwidth - Return width of string * - mb_substr_count - Count the number of substring occurrences + * - mb_ucfirst - Make a string's first character uppercase + * - mb_lcfirst - Make a string's first character lowercase + * - mb_trim - Strip whitespace (or other characters) from the beginning and end of a string + * - mb_ltrim - Strip whitespace (or other characters) from the beginning of a string + * - mb_rtrim - Strip whitespace (or other characters) from the end of a string * * Not implemented: * - mb_convert_kana - Convert "kana" one from another ("zen-kaku", "han-kaku" and more) @@ -67,19 +72,29 @@ namespace Symfony\Polyfill\Mbstring; */ final class Mbstring { - const MB_CASE_FOLD = PHP_INT_MAX; + public const MB_CASE_FOLD = \PHP_INT_MAX; - private static $encodingList = array('ASCII', 'UTF-8'); + private const SIMPLE_CASE_FOLD = [ + ['µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"], + ['μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'], + ]; + + private static $encodingList = ['ASCII', 'UTF-8']; private static $language = 'neutral'; private static $internalEncoding = 'UTF-8'; - private static $caseFold = array( - array('µ', 'ſ', "\xCD\x85", 'ς', "\xCF\x90", "\xCF\x91", "\xCF\x95", "\xCF\x96", "\xCF\xB0", "\xCF\xB1", "\xCF\xB5", "\xE1\xBA\x9B", "\xE1\xBE\xBE"), - array('μ', 's', 'ι', 'σ', 'β', 'θ', 'φ', 'π', 'κ', 'ρ', 'ε', "\xE1\xB9\xA1", 'ι'), - ); public static function mb_convert_encoding($s, $toEncoding, $fromEncoding = null) { - if (\is_array($fromEncoding) || false !== strpos($fromEncoding, ',')) { + if (\is_array($s)) { + $r = []; + foreach ($s as $str) { + $r[] = self::mb_convert_encoding($str, $toEncoding, $fromEncoding); + } + + return $r; + } + + if (\is_array($fromEncoding) || (null !== $fromEncoding && false !== strpos($fromEncoding, ','))) { $fromEncoding = self::mb_detect_encoding($s, $fromEncoding); } else { $fromEncoding = self::getEncoding($fromEncoding); @@ -104,24 +119,22 @@ final class Mbstring $s = iconv($fromEncoding, 'UTF-8//IGNORE', $s); } - return preg_replace_callback('/[\x80-\xFF]+/', array(__CLASS__, 'html_encoding_callback'), $s); + return preg_replace_callback('/[\x80-\xFF]+/', [__CLASS__, 'html_encoding_callback'], $s); } if ('HTML-ENTITIES' === $fromEncoding) { - $s = html_entity_decode($s, ENT_COMPAT, 'UTF-8'); + $s = html_entity_decode($s, \ENT_COMPAT, 'UTF-8'); $fromEncoding = 'UTF-8'; } return iconv($fromEncoding, $toEncoding.'//IGNORE', $s); } - public static function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) + public static function mb_convert_variables($toEncoding, $fromEncoding, &...$vars) { - $vars = array(&$a, &$b, &$c, &$d, &$e, &$f); - $ok = true; array_walk_recursive($vars, function (&$v) use (&$ok, $toEncoding, $fromEncoding) { - if (false === $v = Mbstring::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { + if (false === $v = self::mb_convert_encoding($v, $toEncoding, $fromEncoding)) { $ok = false; } }); @@ -136,23 +149,23 @@ final class Mbstring public static function mb_encode_mimeheader($s, $charset = null, $transferEncoding = null, $linefeed = null, $indent = null) { - trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', E_USER_WARNING); + trigger_error('mb_encode_mimeheader() is bugged. Please use iconv_mime_encode() instead', \E_USER_WARNING); } public static function mb_decode_numericentity($s, $convmap, $encoding = null) { - if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { - trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING); + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_decode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; } - if (!\is_array($convmap) || !$convmap) { + if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { return false; } if (null !== $encoding && !\is_scalar($encoding)) { - trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING); + trigger_error('mb_decode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return ''; // Instead of null (cf. mb_encode_numericentity). } @@ -185,7 +198,7 @@ final class Mbstring $c = isset($m[2]) ? (int) hexdec($m[2]) : $m[1]; for ($i = 0; $i < $cnt; $i += 4) { if ($c >= $convmap[$i] && $c <= $convmap[$i + 1]) { - return Mbstring::mb_chr($c - $convmap[$i + 2]); + return self::mb_chr($c - $convmap[$i + 2]); } } @@ -201,24 +214,24 @@ final class Mbstring public static function mb_encode_numericentity($s, $convmap, $encoding = null, $is_hex = false) { - if (null !== $s && !\is_scalar($s) && !(\is_object($s) && \method_exists($s, '__toString'))) { - trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', E_USER_WARNING); + if (null !== $s && !\is_scalar($s) && !(\is_object($s) && method_exists($s, '__toString'))) { + trigger_error('mb_encode_numericentity() expects parameter 1 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; } - if (!\is_array($convmap) || !$convmap) { + if (!\is_array($convmap) || (80000 > \PHP_VERSION_ID && !$convmap)) { return false; } if (null !== $encoding && !\is_scalar($encoding)) { - trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', E_USER_WARNING); + trigger_error('mb_encode_numericentity() expects parameter 3 to be string, '.\gettype($s).' given', \E_USER_WARNING); return null; // Instead of '' (cf. mb_decode_numericentity). } if (null !== $is_hex && !\is_scalar($is_hex)) { - trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', E_USER_WARNING); + trigger_error('mb_encode_numericentity() expects parameter 4 to be boolean, '.\gettype($s).' given', \E_USER_WARNING); return null; } @@ -239,7 +252,7 @@ final class Mbstring $s = iconv($encoding, 'UTF-8//IGNORE', $s); } - static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; $cnt = floor(\count($convmap) / 4) * 4; $i = 0; @@ -287,14 +300,14 @@ final class Mbstring $s = iconv($encoding, 'UTF-8//IGNORE', $s); } - if (MB_CASE_TITLE == $mode) { + if (\MB_CASE_TITLE == $mode) { static $titleRegexp = null; if (null === $titleRegexp) { $titleRegexp = self::getData('titleCaseRegexp'); } - $s = preg_replace_callback($titleRegexp, array(__CLASS__, 'title_case'), $s); + $s = preg_replace_callback($titleRegexp, [__CLASS__, 'title_case'], $s); } else { - if (MB_CASE_UPPER == $mode) { + if (\MB_CASE_UPPER == $mode) { static $upper = null; if (null === $upper) { $upper = self::getData('upperCase'); @@ -302,7 +315,11 @@ final class Mbstring $map = $upper; } else { if (self::MB_CASE_FOLD === $mode) { - $s = str_replace(self::$caseFold[0], self::$caseFold[1], $s); + static $caseFolding = null; + if (null === $caseFolding) { + $caseFolding = self::getData('caseFolding'); + } + $s = strtr($s, $caseFolding); } static $lower = null; @@ -312,7 +329,7 @@ final class Mbstring $map = $lower; } - static $ulenMask = array("\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4); + static $ulenMask = ["\xC0" => 2, "\xD0" => 2, "\xE0" => 3, "\xF0" => 4]; $i = 0; $len = \strlen($s); @@ -353,15 +370,19 @@ final class Mbstring return self::$internalEncoding; } - $encoding = self::getEncoding($encoding); + $normalizedEncoding = self::getEncoding($encoding); - if ('UTF-8' === $encoding || false !== @iconv($encoding, $encoding, ' ')) { - self::$internalEncoding = $encoding; + if ('UTF-8' === $normalizedEncoding || false !== @iconv($normalizedEncoding, $normalizedEncoding, ' ')) { + self::$internalEncoding = $normalizedEncoding; return true; } - return false; + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($encoding) must be a valid encoding, "%s" given', $encoding)); } public static function mb_language($lang = null) @@ -370,20 +391,24 @@ final class Mbstring return self::$language; } - switch ($lang = strtolower($lang)) { + switch ($normalizedLang = strtolower($lang)) { case 'uni': case 'neutral': - self::$language = $lang; + self::$language = $normalizedLang; return true; } - return false; + if (80000 > \PHP_VERSION_ID) { + return false; + } + + throw new \ValueError(sprintf('Argument #1 ($language) must be a valid language, "%s" given', $lang)); } public static function mb_list_encodings() { - return array('UTF-8'); + return ['UTF-8']; } public static function mb_encoding_aliases($encoding) @@ -391,7 +416,7 @@ final class Mbstring switch (strtoupper($encoding)) { case 'UTF8': case 'UTF-8': - return array('utf8'); + return ['utf8']; } return false; @@ -406,7 +431,20 @@ final class Mbstring $encoding = self::$internalEncoding; } - return self::mb_detect_encoding($var, array($encoding)) || false !== @iconv($encoding, $encoding, $var); + if (!\is_array($var)) { + return self::mb_detect_encoding($var, [$encoding]) || false !== @iconv($encoding, $encoding, $var); + } + + foreach ($var as $key => $value) { + if (!self::mb_check_encoding($key, $encoding)) { + return false; + } + if (!self::mb_check_encoding($value, $encoding)) { + return false; + } + } + + return true; } public static function mb_detect_encoding($str, $encodingList = null, $strict = false) @@ -493,9 +531,13 @@ final class Mbstring $needle = (string) $needle; if ('' === $needle) { - trigger_error(__METHOD__.': Empty delimiter', E_USER_WARNING); + if (80000 > \PHP_VERSION_ID) { + trigger_error(__METHOD__.': Empty delimiter', \E_USER_WARNING); - return false; + return false; + } + + return 0; } return iconv_strpos($haystack, $needle, $offset, $encoding); @@ -521,23 +563,29 @@ final class Mbstring } } - $pos = iconv_strrpos($haystack, $needle, $encoding); + $pos = '' !== $needle || 80000 > \PHP_VERSION_ID + ? iconv_strrpos($haystack, $needle, $encoding) + : self::mb_strlen($haystack, $encoding); return false !== $pos ? $offset + $pos : false; } public static function mb_str_split($string, $split_length = 1, $encoding = null) { - if (null !== $string && !\is_scalar($string) && !(\is_object($string) && \method_exists($string, '__toString'))) { - trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', E_USER_WARNING); + if (null !== $string && !\is_scalar($string) && !(\is_object($string) && method_exists($string, '__toString'))) { + trigger_error('mb_str_split() expects parameter 1 to be string, '.\gettype($string).' given', \E_USER_WARNING); return null; } if (1 > $split_length = (int) $split_length) { - trigger_error('The length of each segment must be greater than zero', E_USER_WARNING); + if (80000 > \PHP_VERSION_ID) { + trigger_error('The length of each segment must be greater than zero', \E_USER_WARNING); - return false; + return false; + } + + throw new \ValueError('Argument #2 ($length) must be greater than 0'); } if (null === $encoding) { @@ -552,10 +600,10 @@ final class Mbstring } $rx .= '.{'.$split_length.'})/us'; - return preg_split($rx, $string, null, PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY); + return preg_split($rx, $string, -1, \PREG_SPLIT_DELIM_CAPTURE | \PREG_SPLIT_NO_EMPTY); } - $result = array(); + $result = []; $length = mb_strlen($string, $encoding); for ($i = 0; $i < $length; $i += $split_length) { @@ -567,21 +615,30 @@ final class Mbstring public static function mb_strtolower($s, $encoding = null) { - return self::mb_convert_case($s, MB_CASE_LOWER, $encoding); + return self::mb_convert_case($s, \MB_CASE_LOWER, $encoding); } public static function mb_strtoupper($s, $encoding = null) { - return self::mb_convert_case($s, MB_CASE_UPPER, $encoding); + return self::mb_convert_case($s, \MB_CASE_UPPER, $encoding); } public static function mb_substitute_character($c = null) { + if (null === $c) { + return 'none'; + } if (0 === strcasecmp($c, 'none')) { return true; } + if (80000 > \PHP_VERSION_ID) { + return false; + } + if (\is_int($c) || 'long' === $c || 'entity' === $c) { + return false; + } - return null !== $c ? false : 'none'; + throw new \ValueError('Argument #1 ($substitute_character) must be "none", "long", "entity" or a valid codepoint'); } public static function mb_substr($s, $start, $length = null, $encoding = null) @@ -612,8 +669,10 @@ final class Mbstring public static function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { - $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); - $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + [$haystack, $needle] = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], [ + self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding), + self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding), + ]); return self::mb_strpos($haystack, $needle, $offset, $encoding); } @@ -629,10 +688,11 @@ final class Mbstring { $encoding = self::getEncoding($encoding); if ('CP850' === $encoding || 'ASCII' === $encoding) { - return strrchr($haystack, $needle, $part); + $pos = strrpos($haystack, $needle); + } else { + $needle = self::mb_substr($needle, 0, 1, $encoding); + $pos = iconv_strrpos($haystack, $needle, $encoding); } - $needle = self::mb_substr($needle, 0, 1, $encoding); - $pos = iconv_strrpos($haystack, $needle, $encoding); return self::getSubpart($pos, $part, $haystack, $encoding); } @@ -647,8 +707,11 @@ final class Mbstring public static function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { - $haystack = self::mb_convert_case($haystack, self::MB_CASE_FOLD, $encoding); - $needle = self::mb_convert_case($needle, self::MB_CASE_FOLD, $encoding); + $haystack = self::mb_convert_case($haystack, \MB_CASE_LOWER, $encoding); + $needle = self::mb_convert_case($needle, \MB_CASE_LOWER, $encoding); + + $haystack = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $haystack); + $needle = str_replace(self::SIMPLE_CASE_FOLD[0], self::SIMPLE_CASE_FOLD[1], $needle); return self::mb_strrpos($haystack, $needle, $offset, $encoding); } @@ -668,7 +731,7 @@ final class Mbstring public static function mb_get_info($type = 'all') { - $info = array( + $info = [ 'internal_encoding' => self::$internalEncoding, 'http_output' => 'pass', 'http_output_conv_mimetypes' => '^(text/|application/xhtml\+xml)', @@ -683,7 +746,7 @@ final class Mbstring 'detect_order' => self::$encodingList, 'substitute_character' => 'none', 'strict_detection' => 'Off', - ); + ]; if ('all' === $type) { return $info; @@ -771,6 +834,69 @@ final class Mbstring return $code; } + public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string + { + if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) { + throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH'); + } + + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, 'mb_str_pad(): Argument #5 ($encoding) must be a valid encoding, "%s" given'); + } + + if (self::mb_strlen($pad_string, $encoding) <= 0) { + throw new \ValueError('mb_str_pad(): Argument #3 ($pad_string) must be a non-empty string'); + } + + $paddingRequired = $length - self::mb_strlen($string, $encoding); + + if ($paddingRequired < 1) { + return $string; + } + + switch ($pad_type) { + case \STR_PAD_LEFT: + return self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding).$string; + case \STR_PAD_RIGHT: + return $string.self::mb_substr(str_repeat($pad_string, $paddingRequired), 0, $paddingRequired, $encoding); + default: + $leftPaddingLength = floor($paddingRequired / 2); + $rightPaddingLength = $paddingRequired - $leftPaddingLength; + + return self::mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.self::mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding); + } + } + + public static function mb_ucfirst(string $string, ?string $encoding = null): string + { + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, 'mb_ucfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given'); + } + + $firstChar = mb_substr($string, 0, 1, $encoding); + $firstChar = mb_convert_case($firstChar, \MB_CASE_TITLE, $encoding); + + return $firstChar.mb_substr($string, 1, null, $encoding); + } + + public static function mb_lcfirst(string $string, ?string $encoding = null): string + { + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, 'mb_lcfirst(): Argument #2 ($encoding) must be a valid encoding, "%s" given'); + } + + $firstChar = mb_substr($string, 0, 1, $encoding); + $firstChar = mb_convert_case($firstChar, \MB_CASE_LOWER, $encoding); + + return $firstChar.mb_substr($string, 1, null, $encoding); + } + private static function getSubpart($pos, $part, $haystack, $encoding) { if (false === $pos) { @@ -787,7 +913,7 @@ final class Mbstring { $i = 1; $entities = ''; - $m = unpack('C*', htmlentities($m[0], ENT_COMPAT, 'UTF-8')); + $m = unpack('C*', htmlentities($m[0], \ENT_COMPAT, 'UTF-8')); while (isset($m[$i])) { if (0x80 > $m[$i]) { @@ -810,7 +936,7 @@ final class Mbstring private static function title_case(array $s) { - return self::mb_convert_case($s[1], MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], MB_CASE_LOWER, 'UTF-8'); + return self::mb_convert_case($s[1], \MB_CASE_UPPER, 'UTF-8').self::mb_convert_case($s[2], \MB_CASE_LOWER, 'UTF-8'); } private static function getData($file) @@ -844,4 +970,76 @@ final class Mbstring return $encoding; } + + public static function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+|[%1$s]+$}Du', $string, $characters, $encoding, __FUNCTION__); + } + + public static function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{^[%s]+}Du', $string, $characters, $encoding, __FUNCTION__); + } + + public static function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string + { + return self::mb_internal_trim('{[%s]+$}Du', $string, $characters, $encoding, __FUNCTION__); + } + + private static function mb_internal_trim(string $regex, string $string, ?string $characters, ?string $encoding, string $function): string + { + if (null === $encoding) { + $encoding = self::mb_internal_encoding(); + } else { + self::assertEncoding($encoding, $function.'(): Argument #3 ($encoding) must be a valid encoding, "%s" given'); + } + + if ('' === $characters) { + return null === $encoding ? $string : self::mb_convert_encoding($string, $encoding); + } + + if ('UTF-8' === $encoding) { + $encoding = null; + if (!preg_match('//u', $string)) { + $string = @iconv('UTF-8', 'UTF-8//IGNORE', $string); + } + if (null !== $characters && !preg_match('//u', $characters)) { + $characters = @iconv('UTF-8', 'UTF-8//IGNORE', $characters); + } + } else { + $string = iconv($encoding, 'UTF-8//IGNORE', $string); + + if (null !== $characters) { + $characters = iconv($encoding, 'UTF-8//IGNORE', $characters); + } + } + + if (null === $characters) { + $characters = "\\0 \f\n\r\t\v\u{00A0}\u{1680}\u{2000}\u{2001}\u{2002}\u{2003}\u{2004}\u{2005}\u{2006}\u{2007}\u{2008}\u{2009}\u{200A}\u{2028}\u{2029}\u{202F}\u{205F}\u{3000}\u{0085}\u{180E}"; + } else { + $characters = preg_quote($characters); + } + + $string = preg_replace(sprintf($regex, $characters), '', $string); + + if (null === $encoding) { + return $string; + } + + return iconv('UTF-8', $encoding.'//IGNORE', $string); + } + + private static function assertEncoding(string $encoding, string $errorFormat): void + { + try { + $validEncoding = @self::mb_check_encoding('', $encoding); + } catch (\ValueError $e) { + throw new \ValueError(sprintf($errorFormat, $encoding)); + } + + // BC for PHP 7.3 and lower + if (!$validEncoding) { + throw new \ValueError(sprintf($errorFormat, $encoding)); + } + } } diff --git a/vendor/symfony/polyfill-mbstring/README.md b/vendor/symfony/polyfill-mbstring/README.md index 4efb599..478b40d 100644 --- a/vendor/symfony/polyfill-mbstring/README.md +++ b/vendor/symfony/polyfill-mbstring/README.md @@ -5,7 +5,7 @@ This component provides a partial, native PHP implementation for the [Mbstring](https://php.net/mbstring) extension. More information can be found in the -[main Polyfill README](https://github.com/symfony/polyfill/blob/master/README.md). +[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md). License ======= diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php new file mode 100644 index 0000000..512bba0 --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/caseFolding.php @@ -0,0 +1,119 @@ + 'i̇', + 'µ' => 'μ', + 'ſ' => 's', + 'ͅ' => 'ι', + 'ς' => 'σ', + 'ϐ' => 'β', + 'ϑ' => 'θ', + 'ϕ' => 'φ', + 'ϖ' => 'π', + 'ϰ' => 'κ', + 'ϱ' => 'ρ', + 'ϵ' => 'ε', + 'ẛ' => 'ṡ', + 'ι' => 'ι', + 'ß' => 'ss', + 'ʼn' => 'ʼn', + 'ǰ' => 'ǰ', + 'ΐ' => 'ΐ', + 'ΰ' => 'ΰ', + 'և' => 'եւ', + 'ẖ' => 'ẖ', + 'ẗ' => 'ẗ', + 'ẘ' => 'ẘ', + 'ẙ' => 'ẙ', + 'ẚ' => 'aʾ', + 'ẞ' => 'ss', + 'ὐ' => 'ὐ', + 'ὒ' => 'ὒ', + 'ὔ' => 'ὔ', + 'ὖ' => 'ὖ', + 'ᾀ' => 'ἀι', + 'ᾁ' => 'ἁι', + 'ᾂ' => 'ἂι', + 'ᾃ' => 'ἃι', + 'ᾄ' => 'ἄι', + 'ᾅ' => 'ἅι', + 'ᾆ' => 'ἆι', + 'ᾇ' => 'ἇι', + 'ᾈ' => 'ἀι', + 'ᾉ' => 'ἁι', + 'ᾊ' => 'ἂι', + 'ᾋ' => 'ἃι', + 'ᾌ' => 'ἄι', + 'ᾍ' => 'ἅι', + 'ᾎ' => 'ἆι', + 'ᾏ' => 'ἇι', + 'ᾐ' => 'ἠι', + 'ᾑ' => 'ἡι', + 'ᾒ' => 'ἢι', + 'ᾓ' => 'ἣι', + 'ᾔ' => 'ἤι', + 'ᾕ' => 'ἥι', + 'ᾖ' => 'ἦι', + 'ᾗ' => 'ἧι', + 'ᾘ' => 'ἠι', + 'ᾙ' => 'ἡι', + 'ᾚ' => 'ἢι', + 'ᾛ' => 'ἣι', + 'ᾜ' => 'ἤι', + 'ᾝ' => 'ἥι', + 'ᾞ' => 'ἦι', + 'ᾟ' => 'ἧι', + 'ᾠ' => 'ὠι', + 'ᾡ' => 'ὡι', + 'ᾢ' => 'ὢι', + 'ᾣ' => 'ὣι', + 'ᾤ' => 'ὤι', + 'ᾥ' => 'ὥι', + 'ᾦ' => 'ὦι', + 'ᾧ' => 'ὧι', + 'ᾨ' => 'ὠι', + 'ᾩ' => 'ὡι', + 'ᾪ' => 'ὢι', + 'ᾫ' => 'ὣι', + 'ᾬ' => 'ὤι', + 'ᾭ' => 'ὥι', + 'ᾮ' => 'ὦι', + 'ᾯ' => 'ὧι', + 'ᾲ' => 'ὰι', + 'ᾳ' => 'αι', + 'ᾴ' => 'άι', + 'ᾶ' => 'ᾶ', + 'ᾷ' => 'ᾶι', + 'ᾼ' => 'αι', + 'ῂ' => 'ὴι', + 'ῃ' => 'ηι', + 'ῄ' => 'ήι', + 'ῆ' => 'ῆ', + 'ῇ' => 'ῆι', + 'ῌ' => 'ηι', + 'ῒ' => 'ῒ', + 'ῖ' => 'ῖ', + 'ῗ' => 'ῗ', + 'ῢ' => 'ῢ', + 'ῤ' => 'ῤ', + 'ῦ' => 'ῦ', + 'ῧ' => 'ῧ', + 'ῲ' => 'ὼι', + 'ῳ' => 'ωι', + 'ῴ' => 'ώι', + 'ῶ' => 'ῶ', + 'ῷ' => 'ῶι', + 'ῼ' => 'ωι', + 'ff' => 'ff', + 'fi' => 'fi', + 'fl' => 'fl', + 'ffi' => 'ffi', + 'ffl' => 'ffl', + 'ſt' => 'st', + 'st' => 'st', + 'ﬓ' => 'մն', + 'ﬔ' => 'մե', + 'ﬕ' => 'մի', + 'ﬖ' => 'վն', + 'ﬗ' => 'մխ', +]; diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php index e6fbfa6..fac60b0 100644 --- a/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/lowerCase.php @@ -1,6 +1,6 @@ 'a', 'B' => 'b', 'C' => 'c', @@ -81,7 +81,7 @@ return array( 'Ī' => 'ī', 'Ĭ' => 'ĭ', 'Į' => 'į', - 'İ' => 'i', + 'İ' => 'i̇', 'IJ' => 'ij', 'Ĵ' => 'ĵ', 'Ķ' => 'ķ', @@ -510,6 +510,138 @@ return array( 'Ⴥ' => 'ⴥ', 'Ⴧ' => 'ⴧ', 'Ⴭ' => 'ⴭ', + 'Ꭰ' => 'ꭰ', + 'Ꭱ' => 'ꭱ', + 'Ꭲ' => 'ꭲ', + 'Ꭳ' => 'ꭳ', + 'Ꭴ' => 'ꭴ', + 'Ꭵ' => 'ꭵ', + 'Ꭶ' => 'ꭶ', + 'Ꭷ' => 'ꭷ', + 'Ꭸ' => 'ꭸ', + 'Ꭹ' => 'ꭹ', + 'Ꭺ' => 'ꭺ', + 'Ꭻ' => 'ꭻ', + 'Ꭼ' => 'ꭼ', + 'Ꭽ' => 'ꭽ', + 'Ꭾ' => 'ꭾ', + 'Ꭿ' => 'ꭿ', + 'Ꮀ' => 'ꮀ', + 'Ꮁ' => 'ꮁ', + 'Ꮂ' => 'ꮂ', + 'Ꮃ' => 'ꮃ', + 'Ꮄ' => 'ꮄ', + 'Ꮅ' => 'ꮅ', + 'Ꮆ' => 'ꮆ', + 'Ꮇ' => 'ꮇ', + 'Ꮈ' => 'ꮈ', + 'Ꮉ' => 'ꮉ', + 'Ꮊ' => 'ꮊ', + 'Ꮋ' => 'ꮋ', + 'Ꮌ' => 'ꮌ', + 'Ꮍ' => 'ꮍ', + 'Ꮎ' => 'ꮎ', + 'Ꮏ' => 'ꮏ', + 'Ꮐ' => 'ꮐ', + 'Ꮑ' => 'ꮑ', + 'Ꮒ' => 'ꮒ', + 'Ꮓ' => 'ꮓ', + 'Ꮔ' => 'ꮔ', + 'Ꮕ' => 'ꮕ', + 'Ꮖ' => 'ꮖ', + 'Ꮗ' => 'ꮗ', + 'Ꮘ' => 'ꮘ', + 'Ꮙ' => 'ꮙ', + 'Ꮚ' => 'ꮚ', + 'Ꮛ' => 'ꮛ', + 'Ꮜ' => 'ꮜ', + 'Ꮝ' => 'ꮝ', + 'Ꮞ' => 'ꮞ', + 'Ꮟ' => 'ꮟ', + 'Ꮠ' => 'ꮠ', + 'Ꮡ' => 'ꮡ', + 'Ꮢ' => 'ꮢ', + 'Ꮣ' => 'ꮣ', + 'Ꮤ' => 'ꮤ', + 'Ꮥ' => 'ꮥ', + 'Ꮦ' => 'ꮦ', + 'Ꮧ' => 'ꮧ', + 'Ꮨ' => 'ꮨ', + 'Ꮩ' => 'ꮩ', + 'Ꮪ' => 'ꮪ', + 'Ꮫ' => 'ꮫ', + 'Ꮬ' => 'ꮬ', + 'Ꮭ' => 'ꮭ', + 'Ꮮ' => 'ꮮ', + 'Ꮯ' => 'ꮯ', + 'Ꮰ' => 'ꮰ', + 'Ꮱ' => 'ꮱ', + 'Ꮲ' => 'ꮲ', + 'Ꮳ' => 'ꮳ', + 'Ꮴ' => 'ꮴ', + 'Ꮵ' => 'ꮵ', + 'Ꮶ' => 'ꮶ', + 'Ꮷ' => 'ꮷ', + 'Ꮸ' => 'ꮸ', + 'Ꮹ' => 'ꮹ', + 'Ꮺ' => 'ꮺ', + 'Ꮻ' => 'ꮻ', + 'Ꮼ' => 'ꮼ', + 'Ꮽ' => 'ꮽ', + 'Ꮾ' => 'ꮾ', + 'Ꮿ' => 'ꮿ', + 'Ᏸ' => 'ᏸ', + 'Ᏹ' => 'ᏹ', + 'Ᏺ' => 'ᏺ', + 'Ᏻ' => 'ᏻ', + 'Ᏼ' => 'ᏼ', + 'Ᏽ' => 'ᏽ', + 'Ა' => 'ა', + 'Ბ' => 'ბ', + 'Გ' => 'გ', + 'Დ' => 'დ', + 'Ე' => 'ე', + 'Ვ' => 'ვ', + 'Ზ' => 'ზ', + 'Თ' => 'თ', + 'Ი' => 'ი', + 'Კ' => 'კ', + 'Ლ' => 'ლ', + 'Მ' => 'მ', + 'Ნ' => 'ნ', + 'Ო' => 'ო', + 'Პ' => 'პ', + 'Ჟ' => 'ჟ', + 'Რ' => 'რ', + 'Ს' => 'ს', + 'Ტ' => 'ტ', + 'Უ' => 'უ', + 'Ფ' => 'ფ', + 'Ქ' => 'ქ', + 'Ღ' => 'ღ', + 'Ყ' => 'ყ', + 'Შ' => 'შ', + 'Ჩ' => 'ჩ', + 'Ც' => 'ც', + 'Ძ' => 'ძ', + 'Წ' => 'წ', + 'Ჭ' => 'ჭ', + 'Ხ' => 'ხ', + 'Ჯ' => 'ჯ', + 'Ჰ' => 'ჰ', + 'Ჱ' => 'ჱ', + 'Ჲ' => 'ჲ', + 'Ჳ' => 'ჳ', + 'Ჴ' => 'ჴ', + 'Ჵ' => 'ჵ', + 'Ჶ' => 'ჶ', + 'Ჷ' => 'ჷ', + 'Ჸ' => 'ჸ', + 'Ჹ' => 'ჹ', + 'Ჺ' => 'ჺ', + 'Ჽ' => 'ჽ', + 'Ჾ' => 'ჾ', + 'Ჿ' => 'ჿ', 'Ḁ' => 'ḁ', 'Ḃ' => 'ḃ', 'Ḅ' => 'ḅ', @@ -993,8 +1125,24 @@ return array( 'Ɜ' => 'ɜ', 'Ɡ' => 'ɡ', 'Ɬ' => 'ɬ', + 'Ɪ' => 'ɪ', 'Ʞ' => 'ʞ', 'Ʇ' => 'ʇ', + 'Ʝ' => 'ʝ', + 'Ꭓ' => 'ꭓ', + 'Ꞵ' => 'ꞵ', + 'Ꞷ' => 'ꞷ', + 'Ꞹ' => 'ꞹ', + 'Ꞻ' => 'ꞻ', + 'Ꞽ' => 'ꞽ', + 'Ꞿ' => 'ꞿ', + 'Ꟃ' => 'ꟃ', + 'Ꞔ' => 'ꞔ', + 'Ʂ' => 'ʂ', + 'Ᶎ' => 'ᶎ', + 'Ꟈ' => 'ꟈ', + 'Ꟊ' => 'ꟊ', + 'Ꟶ' => 'ꟶ', 'A' => 'a', 'B' => 'b', 'C' => 'c', @@ -1061,6 +1209,93 @@ return array( '𐐥' => '𐑍', '𐐦' => '𐑎', '𐐧' => '𐑏', + '𐒰' => '𐓘', + '𐒱' => '𐓙', + '𐒲' => '𐓚', + '𐒳' => '𐓛', + '𐒴' => '𐓜', + '𐒵' => '𐓝', + '𐒶' => '𐓞', + '𐒷' => '𐓟', + '𐒸' => '𐓠', + '𐒹' => '𐓡', + '𐒺' => '𐓢', + '𐒻' => '𐓣', + '𐒼' => '𐓤', + '𐒽' => '𐓥', + '𐒾' => '𐓦', + '𐒿' => '𐓧', + '𐓀' => '𐓨', + '𐓁' => '𐓩', + '𐓂' => '𐓪', + '𐓃' => '𐓫', + '𐓄' => '𐓬', + '𐓅' => '𐓭', + '𐓆' => '𐓮', + '𐓇' => '𐓯', + '𐓈' => '𐓰', + '𐓉' => '𐓱', + '𐓊' => '𐓲', + '𐓋' => '𐓳', + '𐓌' => '𐓴', + '𐓍' => '𐓵', + '𐓎' => '𐓶', + '𐓏' => '𐓷', + '𐓐' => '𐓸', + '𐓑' => '𐓹', + '𐓒' => '𐓺', + '𐓓' => '𐓻', + '𐲀' => '𐳀', + '𐲁' => '𐳁', + '𐲂' => '𐳂', + '𐲃' => '𐳃', + '𐲄' => '𐳄', + '𐲅' => '𐳅', + '𐲆' => '𐳆', + '𐲇' => '𐳇', + '𐲈' => '𐳈', + '𐲉' => '𐳉', + '𐲊' => '𐳊', + '𐲋' => '𐳋', + '𐲌' => '𐳌', + '𐲍' => '𐳍', + '𐲎' => '𐳎', + '𐲏' => '𐳏', + '𐲐' => '𐳐', + '𐲑' => '𐳑', + '𐲒' => '𐳒', + '𐲓' => '𐳓', + '𐲔' => '𐳔', + '𐲕' => '𐳕', + '𐲖' => '𐳖', + '𐲗' => '𐳗', + '𐲘' => '𐳘', + '𐲙' => '𐳙', + '𐲚' => '𐳚', + '𐲛' => '𐳛', + '𐲜' => '𐳜', + '𐲝' => '𐳝', + '𐲞' => '𐳞', + '𐲟' => '𐳟', + '𐲠' => '𐳠', + '𐲡' => '𐳡', + '𐲢' => '𐳢', + '𐲣' => '𐳣', + '𐲤' => '𐳤', + '𐲥' => '𐳥', + '𐲦' => '𐳦', + '𐲧' => '𐳧', + '𐲨' => '𐳨', + '𐲩' => '𐳩', + '𐲪' => '𐳪', + '𐲫' => '𐳫', + '𐲬' => '𐳬', + '𐲭' => '𐳭', + '𐲮' => '𐳮', + '𐲯' => '𐳯', + '𐲰' => '𐳰', + '𐲱' => '𐳱', + '𐲲' => '𐳲', '𑢠' => '𑣀', '𑢡' => '𑣁', '𑢢' => '𑣂', @@ -1093,4 +1328,70 @@ return array( '𑢽' => '𑣝', '𑢾' => '𑣞', '𑢿' => '𑣟', + '𖹀' => '𖹠', + '𖹁' => '𖹡', + '𖹂' => '𖹢', + '𖹃' => '𖹣', + '𖹄' => '𖹤', + '𖹅' => '𖹥', + '𖹆' => '𖹦', + '𖹇' => '𖹧', + '𖹈' => '𖹨', + '𖹉' => '𖹩', + '𖹊' => '𖹪', + '𖹋' => '𖹫', + '𖹌' => '𖹬', + '𖹍' => '𖹭', + '𖹎' => '𖹮', + '𖹏' => '𖹯', + '𖹐' => '𖹰', + '𖹑' => '𖹱', + '𖹒' => '𖹲', + '𖹓' => '𖹳', + '𖹔' => '𖹴', + '𖹕' => '𖹵', + '𖹖' => '𖹶', + '𖹗' => '𖹷', + '𖹘' => '𖹸', + '𖹙' => '𖹹', + '𖹚' => '𖹺', + '𖹛' => '𖹻', + '𖹜' => '𖹼', + '𖹝' => '𖹽', + '𖹞' => '𖹾', + '𖹟' => '𖹿', + '𞤀' => '𞤢', + '𞤁' => '𞤣', + '𞤂' => '𞤤', + '𞤃' => '𞤥', + '𞤄' => '𞤦', + '𞤅' => '𞤧', + '𞤆' => '𞤨', + '𞤇' => '𞤩', + '𞤈' => '𞤪', + '𞤉' => '𞤫', + '𞤊' => '𞤬', + '𞤋' => '𞤭', + '𞤌' => '𞤮', + '𞤍' => '𞤯', + '𞤎' => '𞤰', + '𞤏' => '𞤱', + '𞤐' => '𞤲', + '𞤑' => '𞤳', + '𞤒' => '𞤴', + '𞤓' => '𞤵', + '𞤔' => '𞤶', + '𞤕' => '𞤷', + '𞤖' => '𞤸', + '𞤗' => '𞤹', + '𞤘' => '𞤺', + '𞤙' => '𞤻', + '𞤚' => '𞤼', + '𞤛' => '𞤽', + '𞤜' => '𞤾', + '𞤝' => '𞤿', + '𞤞' => '𞥀', + '𞤟' => '𞥁', + '𞤠' => '𞥂', + '𞤡' => '𞥃', ); diff --git a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php index b8103b2..56b9cb8 100644 --- a/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php +++ b/vendor/symfony/polyfill-mbstring/Resources/unidata/upperCase.php @@ -1,6 +1,6 @@ 'A', 'b' => 'B', 'c' => 'C', @@ -225,6 +225,7 @@ return array( 'ɦ' => 'Ɦ', 'ɨ' => 'Ɨ', 'ɩ' => 'Ɩ', + 'ɪ' => 'Ɪ', 'ɫ' => 'Ɫ', 'ɬ' => 'Ɬ', 'ɯ' => 'Ɯ', @@ -233,6 +234,7 @@ return array( 'ɵ' => 'Ɵ', 'ɽ' => 'Ɽ', 'ʀ' => 'Ʀ', + 'ʂ' => 'Ʂ', 'ʃ' => 'Ʃ', 'ʇ' => 'Ʇ', 'ʈ' => 'Ʈ', @@ -241,6 +243,7 @@ return array( 'ʋ' => 'Ʋ', 'ʌ' => 'Ʌ', 'ʒ' => 'Ʒ', + 'ʝ' => 'Ʝ', 'ʞ' => 'Ʞ', 'ͅ' => 'Ι', 'ͱ' => 'Ͱ', @@ -493,8 +496,70 @@ return array( 'ք' => 'Ք', 'օ' => 'Օ', 'ֆ' => 'Ֆ', + 'ა' => 'Ა', + 'ბ' => 'Ბ', + 'გ' => 'Გ', + 'დ' => 'Დ', + 'ე' => 'Ე', + 'ვ' => 'Ვ', + 'ზ' => 'Ზ', + 'თ' => 'Თ', + 'ი' => 'Ი', + 'კ' => 'Კ', + 'ლ' => 'Ლ', + 'მ' => 'Მ', + 'ნ' => 'Ნ', + 'ო' => 'Ო', + 'პ' => 'Პ', + 'ჟ' => 'Ჟ', + 'რ' => 'Რ', + 'ს' => 'Ს', + 'ტ' => 'Ტ', + 'უ' => 'Უ', + 'ფ' => 'Ფ', + 'ქ' => 'Ქ', + 'ღ' => 'Ღ', + 'ყ' => 'Ყ', + 'შ' => 'Შ', + 'ჩ' => 'Ჩ', + 'ც' => 'Ც', + 'ძ' => 'Ძ', + 'წ' => 'Წ', + 'ჭ' => 'Ჭ', + 'ხ' => 'Ხ', + 'ჯ' => 'Ჯ', + 'ჰ' => 'Ჰ', + 'ჱ' => 'Ჱ', + 'ჲ' => 'Ჲ', + 'ჳ' => 'Ჳ', + 'ჴ' => 'Ჴ', + 'ჵ' => 'Ჵ', + 'ჶ' => 'Ჶ', + 'ჷ' => 'Ჷ', + 'ჸ' => 'Ჸ', + 'ჹ' => 'Ჹ', + 'ჺ' => 'Ჺ', + 'ჽ' => 'Ჽ', + 'ჾ' => 'Ჾ', + 'ჿ' => 'Ჿ', + 'ᏸ' => 'Ᏸ', + 'ᏹ' => 'Ᏹ', + 'ᏺ' => 'Ᏺ', + 'ᏻ' => 'Ᏻ', + 'ᏼ' => 'Ᏼ', + 'ᏽ' => 'Ᏽ', + 'ᲀ' => 'В', + 'ᲁ' => 'Д', + 'ᲂ' => 'О', + 'ᲃ' => 'С', + 'ᲄ' => 'Т', + 'ᲅ' => 'Т', + 'ᲆ' => 'Ъ', + 'ᲇ' => 'Ѣ', + 'ᲈ' => 'Ꙋ', 'ᵹ' => 'Ᵹ', 'ᵽ' => 'Ᵽ', + 'ᶎ' => 'Ᶎ', 'ḁ' => 'Ḁ', 'ḃ' => 'Ḃ', 'ḅ' => 'Ḅ', @@ -681,41 +746,41 @@ return array( 'ύ' => 'Ύ', 'ὼ' => 'Ὼ', 'ώ' => 'Ώ', - 'ᾀ' => 'ᾈ', - 'ᾁ' => 'ᾉ', - 'ᾂ' => 'ᾊ', - 'ᾃ' => 'ᾋ', - 'ᾄ' => 'ᾌ', - 'ᾅ' => 'ᾍ', - 'ᾆ' => 'ᾎ', - 'ᾇ' => 'ᾏ', - 'ᾐ' => 'ᾘ', - 'ᾑ' => 'ᾙ', - 'ᾒ' => 'ᾚ', - 'ᾓ' => 'ᾛ', - 'ᾔ' => 'ᾜ', - 'ᾕ' => 'ᾝ', - 'ᾖ' => 'ᾞ', - 'ᾗ' => 'ᾟ', - 'ᾠ' => 'ᾨ', - 'ᾡ' => 'ᾩ', - 'ᾢ' => 'ᾪ', - 'ᾣ' => 'ᾫ', - 'ᾤ' => 'ᾬ', - 'ᾥ' => 'ᾭ', - 'ᾦ' => 'ᾮ', - 'ᾧ' => 'ᾯ', + 'ᾀ' => 'ἈΙ', + 'ᾁ' => 'ἉΙ', + 'ᾂ' => 'ἊΙ', + 'ᾃ' => 'ἋΙ', + 'ᾄ' => 'ἌΙ', + 'ᾅ' => 'ἍΙ', + 'ᾆ' => 'ἎΙ', + 'ᾇ' => 'ἏΙ', + 'ᾐ' => 'ἨΙ', + 'ᾑ' => 'ἩΙ', + 'ᾒ' => 'ἪΙ', + 'ᾓ' => 'ἫΙ', + 'ᾔ' => 'ἬΙ', + 'ᾕ' => 'ἭΙ', + 'ᾖ' => 'ἮΙ', + 'ᾗ' => 'ἯΙ', + 'ᾠ' => 'ὨΙ', + 'ᾡ' => 'ὩΙ', + 'ᾢ' => 'ὪΙ', + 'ᾣ' => 'ὫΙ', + 'ᾤ' => 'ὬΙ', + 'ᾥ' => 'ὭΙ', + 'ᾦ' => 'ὮΙ', + 'ᾧ' => 'ὯΙ', 'ᾰ' => 'Ᾰ', 'ᾱ' => 'Ᾱ', - 'ᾳ' => 'ᾼ', + 'ᾳ' => 'ΑΙ', 'ι' => 'Ι', - 'ῃ' => 'ῌ', + 'ῃ' => 'ΗΙ', 'ῐ' => 'Ῐ', 'ῑ' => 'Ῑ', 'ῠ' => 'Ῠ', 'ῡ' => 'Ῡ', 'ῥ' => 'Ῥ', - 'ῳ' => 'ῼ', + 'ῳ' => 'ΩΙ', 'ⅎ' => 'Ⅎ', 'ⅰ' => 'Ⅰ', 'ⅱ' => 'Ⅱ', @@ -993,6 +1058,7 @@ return array( 'ꞌ' => 'Ꞌ', 'ꞑ' => 'Ꞑ', 'ꞓ' => 'Ꞓ', + 'ꞔ' => 'Ꞔ', 'ꞗ' => 'Ꞗ', 'ꞙ' => 'Ꞙ', 'ꞛ' => 'Ꞛ', @@ -1003,6 +1069,97 @@ return array( 'ꞥ' => 'Ꞥ', 'ꞧ' => 'Ꞧ', 'ꞩ' => 'Ꞩ', + 'ꞵ' => 'Ꞵ', + 'ꞷ' => 'Ꞷ', + 'ꞹ' => 'Ꞹ', + 'ꞻ' => 'Ꞻ', + 'ꞽ' => 'Ꞽ', + 'ꞿ' => 'Ꞿ', + 'ꟃ' => 'Ꟃ', + 'ꟈ' => 'Ꟈ', + 'ꟊ' => 'Ꟊ', + 'ꟶ' => 'Ꟶ', + 'ꭓ' => 'Ꭓ', + 'ꭰ' => 'Ꭰ', + 'ꭱ' => 'Ꭱ', + 'ꭲ' => 'Ꭲ', + 'ꭳ' => 'Ꭳ', + 'ꭴ' => 'Ꭴ', + 'ꭵ' => 'Ꭵ', + 'ꭶ' => 'Ꭶ', + 'ꭷ' => 'Ꭷ', + 'ꭸ' => 'Ꭸ', + 'ꭹ' => 'Ꭹ', + 'ꭺ' => 'Ꭺ', + 'ꭻ' => 'Ꭻ', + 'ꭼ' => 'Ꭼ', + 'ꭽ' => 'Ꭽ', + 'ꭾ' => 'Ꭾ', + 'ꭿ' => 'Ꭿ', + 'ꮀ' => 'Ꮀ', + 'ꮁ' => 'Ꮁ', + 'ꮂ' => 'Ꮂ', + 'ꮃ' => 'Ꮃ', + 'ꮄ' => 'Ꮄ', + 'ꮅ' => 'Ꮅ', + 'ꮆ' => 'Ꮆ', + 'ꮇ' => 'Ꮇ', + 'ꮈ' => 'Ꮈ', + 'ꮉ' => 'Ꮉ', + 'ꮊ' => 'Ꮊ', + 'ꮋ' => 'Ꮋ', + 'ꮌ' => 'Ꮌ', + 'ꮍ' => 'Ꮍ', + 'ꮎ' => 'Ꮎ', + 'ꮏ' => 'Ꮏ', + 'ꮐ' => 'Ꮐ', + 'ꮑ' => 'Ꮑ', + 'ꮒ' => 'Ꮒ', + 'ꮓ' => 'Ꮓ', + 'ꮔ' => 'Ꮔ', + 'ꮕ' => 'Ꮕ', + 'ꮖ' => 'Ꮖ', + 'ꮗ' => 'Ꮗ', + 'ꮘ' => 'Ꮘ', + 'ꮙ' => 'Ꮙ', + 'ꮚ' => 'Ꮚ', + 'ꮛ' => 'Ꮛ', + 'ꮜ' => 'Ꮜ', + 'ꮝ' => 'Ꮝ', + 'ꮞ' => 'Ꮞ', + 'ꮟ' => 'Ꮟ', + 'ꮠ' => 'Ꮠ', + 'ꮡ' => 'Ꮡ', + 'ꮢ' => 'Ꮢ', + 'ꮣ' => 'Ꮣ', + 'ꮤ' => 'Ꮤ', + 'ꮥ' => 'Ꮥ', + 'ꮦ' => 'Ꮦ', + 'ꮧ' => 'Ꮧ', + 'ꮨ' => 'Ꮨ', + 'ꮩ' => 'Ꮩ', + 'ꮪ' => 'Ꮪ', + 'ꮫ' => 'Ꮫ', + 'ꮬ' => 'Ꮬ', + 'ꮭ' => 'Ꮭ', + 'ꮮ' => 'Ꮮ', + 'ꮯ' => 'Ꮯ', + 'ꮰ' => 'Ꮰ', + 'ꮱ' => 'Ꮱ', + 'ꮲ' => 'Ꮲ', + 'ꮳ' => 'Ꮳ', + 'ꮴ' => 'Ꮴ', + 'ꮵ' => 'Ꮵ', + 'ꮶ' => 'Ꮶ', + 'ꮷ' => 'Ꮷ', + 'ꮸ' => 'Ꮸ', + 'ꮹ' => 'Ꮹ', + 'ꮺ' => 'Ꮺ', + 'ꮻ' => 'Ꮻ', + 'ꮼ' => 'Ꮼ', + 'ꮽ' => 'Ꮽ', + 'ꮾ' => 'Ꮾ', + 'ꮿ' => 'Ꮿ', 'a' => 'A', 'b' => 'B', 'c' => 'C', @@ -1069,6 +1226,93 @@ return array( '𐑍' => '𐐥', '𐑎' => '𐐦', '𐑏' => '𐐧', + '𐓘' => '𐒰', + '𐓙' => '𐒱', + '𐓚' => '𐒲', + '𐓛' => '𐒳', + '𐓜' => '𐒴', + '𐓝' => '𐒵', + '𐓞' => '𐒶', + '𐓟' => '𐒷', + '𐓠' => '𐒸', + '𐓡' => '𐒹', + '𐓢' => '𐒺', + '𐓣' => '𐒻', + '𐓤' => '𐒼', + '𐓥' => '𐒽', + '𐓦' => '𐒾', + '𐓧' => '𐒿', + '𐓨' => '𐓀', + '𐓩' => '𐓁', + '𐓪' => '𐓂', + '𐓫' => '𐓃', + '𐓬' => '𐓄', + '𐓭' => '𐓅', + '𐓮' => '𐓆', + '𐓯' => '𐓇', + '𐓰' => '𐓈', + '𐓱' => '𐓉', + '𐓲' => '𐓊', + '𐓳' => '𐓋', + '𐓴' => '𐓌', + '𐓵' => '𐓍', + '𐓶' => '𐓎', + '𐓷' => '𐓏', + '𐓸' => '𐓐', + '𐓹' => '𐓑', + '𐓺' => '𐓒', + '𐓻' => '𐓓', + '𐳀' => '𐲀', + '𐳁' => '𐲁', + '𐳂' => '𐲂', + '𐳃' => '𐲃', + '𐳄' => '𐲄', + '𐳅' => '𐲅', + '𐳆' => '𐲆', + '𐳇' => '𐲇', + '𐳈' => '𐲈', + '𐳉' => '𐲉', + '𐳊' => '𐲊', + '𐳋' => '𐲋', + '𐳌' => '𐲌', + '𐳍' => '𐲍', + '𐳎' => '𐲎', + '𐳏' => '𐲏', + '𐳐' => '𐲐', + '𐳑' => '𐲑', + '𐳒' => '𐲒', + '𐳓' => '𐲓', + '𐳔' => '𐲔', + '𐳕' => '𐲕', + '𐳖' => '𐲖', + '𐳗' => '𐲗', + '𐳘' => '𐲘', + '𐳙' => '𐲙', + '𐳚' => '𐲚', + '𐳛' => '𐲛', + '𐳜' => '𐲜', + '𐳝' => '𐲝', + '𐳞' => '𐲞', + '𐳟' => '𐲟', + '𐳠' => '𐲠', + '𐳡' => '𐲡', + '𐳢' => '𐲢', + '𐳣' => '𐲣', + '𐳤' => '𐲤', + '𐳥' => '𐲥', + '𐳦' => '𐲦', + '𐳧' => '𐲧', + '𐳨' => '𐲨', + '𐳩' => '𐲩', + '𐳪' => '𐲪', + '𐳫' => '𐲫', + '𐳬' => '𐲬', + '𐳭' => '𐲭', + '𐳮' => '𐲮', + '𐳯' => '𐲯', + '𐳰' => '𐲰', + '𐳱' => '𐲱', + '𐳲' => '𐲲', '𑣀' => '𑢠', '𑣁' => '𑢡', '𑣂' => '𑢢', @@ -1101,4 +1345,145 @@ return array( '𑣝' => '𑢽', '𑣞' => '𑢾', '𑣟' => '𑢿', + '𖹠' => '𖹀', + '𖹡' => '𖹁', + '𖹢' => '𖹂', + '𖹣' => '𖹃', + '𖹤' => '𖹄', + '𖹥' => '𖹅', + '𖹦' => '𖹆', + '𖹧' => '𖹇', + '𖹨' => '𖹈', + '𖹩' => '𖹉', + '𖹪' => '𖹊', + '𖹫' => '𖹋', + '𖹬' => '𖹌', + '𖹭' => '𖹍', + '𖹮' => '𖹎', + '𖹯' => '𖹏', + '𖹰' => '𖹐', + '𖹱' => '𖹑', + '𖹲' => '𖹒', + '𖹳' => '𖹓', + '𖹴' => '𖹔', + '𖹵' => '𖹕', + '𖹶' => '𖹖', + '𖹷' => '𖹗', + '𖹸' => '𖹘', + '𖹹' => '𖹙', + '𖹺' => '𖹚', + '𖹻' => '𖹛', + '𖹼' => '𖹜', + '𖹽' => '𖹝', + '𖹾' => '𖹞', + '𖹿' => '𖹟', + '𞤢' => '𞤀', + '𞤣' => '𞤁', + '𞤤' => '𞤂', + '𞤥' => '𞤃', + '𞤦' => '𞤄', + '𞤧' => '𞤅', + '𞤨' => '𞤆', + '𞤩' => '𞤇', + '𞤪' => '𞤈', + '𞤫' => '𞤉', + '𞤬' => '𞤊', + '𞤭' => '𞤋', + '𞤮' => '𞤌', + '𞤯' => '𞤍', + '𞤰' => '𞤎', + '𞤱' => '𞤏', + '𞤲' => '𞤐', + '𞤳' => '𞤑', + '𞤴' => '𞤒', + '𞤵' => '𞤓', + '𞤶' => '𞤔', + '𞤷' => '𞤕', + '𞤸' => '𞤖', + '𞤹' => '𞤗', + '𞤺' => '𞤘', + '𞤻' => '𞤙', + '𞤼' => '𞤚', + '𞤽' => '𞤛', + '𞤾' => '𞤜', + '𞤿' => '𞤝', + '𞥀' => '𞤞', + '𞥁' => '𞤟', + '𞥂' => '𞤠', + '𞥃' => '𞤡', + 'ß' => 'SS', + 'ff' => 'FF', + 'fi' => 'FI', + 'fl' => 'FL', + 'ffi' => 'FFI', + 'ffl' => 'FFL', + 'ſt' => 'ST', + 'st' => 'ST', + 'և' => 'ԵՒ', + 'ﬓ' => 'ՄՆ', + 'ﬔ' => 'ՄԵ', + 'ﬕ' => 'ՄԻ', + 'ﬖ' => 'ՎՆ', + 'ﬗ' => 'ՄԽ', + 'ʼn' => 'ʼN', + 'ΐ' => 'Ϊ́', + 'ΰ' => 'Ϋ́', + 'ǰ' => 'J̌', + 'ẖ' => 'H̱', + 'ẗ' => 'T̈', + 'ẘ' => 'W̊', + 'ẙ' => 'Y̊', + 'ẚ' => 'Aʾ', + 'ὐ' => 'Υ̓', + 'ὒ' => 'Υ̓̀', + 'ὔ' => 'Υ̓́', + 'ὖ' => 'Υ̓͂', + 'ᾶ' => 'Α͂', + 'ῆ' => 'Η͂', + 'ῒ' => 'Ϊ̀', + 'ΐ' => 'Ϊ́', + 'ῖ' => 'Ι͂', + 'ῗ' => 'Ϊ͂', + 'ῢ' => 'Ϋ̀', + 'ΰ' => 'Ϋ́', + 'ῤ' => 'Ρ̓', + 'ῦ' => 'Υ͂', + 'ῧ' => 'Ϋ͂', + 'ῶ' => 'Ω͂', + 'ᾈ' => 'ἈΙ', + 'ᾉ' => 'ἉΙ', + 'ᾊ' => 'ἊΙ', + 'ᾋ' => 'ἋΙ', + 'ᾌ' => 'ἌΙ', + 'ᾍ' => 'ἍΙ', + 'ᾎ' => 'ἎΙ', + 'ᾏ' => 'ἏΙ', + 'ᾘ' => 'ἨΙ', + 'ᾙ' => 'ἩΙ', + 'ᾚ' => 'ἪΙ', + 'ᾛ' => 'ἫΙ', + 'ᾜ' => 'ἬΙ', + 'ᾝ' => 'ἭΙ', + 'ᾞ' => 'ἮΙ', + 'ᾟ' => 'ἯΙ', + 'ᾨ' => 'ὨΙ', + 'ᾩ' => 'ὩΙ', + 'ᾪ' => 'ὪΙ', + 'ᾫ' => 'ὫΙ', + 'ᾬ' => 'ὬΙ', + 'ᾭ' => 'ὭΙ', + 'ᾮ' => 'ὮΙ', + 'ᾯ' => 'ὯΙ', + 'ᾼ' => 'ΑΙ', + 'ῌ' => 'ΗΙ', + 'ῼ' => 'ΩΙ', + 'ᾲ' => 'ᾺΙ', + 'ᾴ' => 'ΆΙ', + 'ῂ' => 'ῊΙ', + 'ῄ' => 'ΉΙ', + 'ῲ' => 'ῺΙ', + 'ῴ' => 'ΏΙ', + 'ᾷ' => 'Α͂Ι', + 'ῇ' => 'Η͂Ι', + 'ῷ' => 'Ω͂Ι', ); diff --git a/vendor/symfony/polyfill-mbstring/bootstrap.php b/vendor/symfony/polyfill-mbstring/bootstrap.php index 8c8225c..ff51ae0 100644 --- a/vendor/symfony/polyfill-mbstring/bootstrap.php +++ b/vendor/symfony/polyfill-mbstring/bootstrap.php @@ -11,54 +11,162 @@ use Symfony\Polyfill\Mbstring as p; -if (!defined('MB_CASE_UPPER')) { - define('MB_CASE_UPPER', 0); - define('MB_CASE_LOWER', 1); - define('MB_CASE_TITLE', 2); +if (\PHP_VERSION_ID >= 80000) { + return require __DIR__.'/bootstrap80.php'; } -if (!function_exists('mb_strlen')) { - function mb_convert_encoding($s, $to, $from = null) { return p\Mbstring::mb_convert_encoding($s, $to, $from); } - function mb_decode_mimeheader($s) { return p\Mbstring::mb_decode_mimeheader($s); } - function mb_encode_mimeheader($s, $charset = null, $transferEnc = null, $lf = null, $indent = null) { return p\Mbstring::mb_encode_mimeheader($s, $charset, $transferEnc, $lf, $indent); } - function mb_decode_numericentity($s, $convmap, $enc = null) { return p\Mbstring::mb_decode_numericentity($s, $convmap, $enc); } - function mb_encode_numericentity($s, $convmap, $enc = null, $is_hex = false) { return p\Mbstring::mb_encode_numericentity($s, $convmap, $enc, $is_hex); } - function mb_convert_case($s, $mode, $enc = null) { return p\Mbstring::mb_convert_case($s, $mode, $enc); } - function mb_internal_encoding($enc = null) { return p\Mbstring::mb_internal_encoding($enc); } - function mb_language($lang = null) { return p\Mbstring::mb_language($lang); } +if (!function_exists('mb_convert_encoding')) { + function mb_convert_encoding($string, $to_encoding, $from_encoding = null) { return p\Mbstring::mb_convert_encoding($string, $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader($string) { return p\Mbstring::mb_decode_mimeheader($string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader($string, $charset = null, $transfer_encoding = null, $newline = "\r\n", $indent = 0) { return p\Mbstring::mb_encode_mimeheader($string, $charset, $transfer_encoding, $newline, $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity($string, $map, $encoding = null) { return p\Mbstring::mb_decode_numericentity($string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity($string, $map, $encoding = null, $hex = false) { return p\Mbstring::mb_encode_numericentity($string, $map, $encoding, $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case($string, $mode, $encoding = null) { return p\Mbstring::mb_convert_case($string, $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding($encoding = null) { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language($language = null) { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { function mb_list_encodings() { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { function mb_encoding_aliases($encoding) { return p\Mbstring::mb_encoding_aliases($encoding); } - function mb_check_encoding($var = null, $encoding = null) { return p\Mbstring::mb_check_encoding($var, $encoding); } - function mb_detect_encoding($str, $encodingList = null, $strict = false) { return p\Mbstring::mb_detect_encoding($str, $encodingList, $strict); } - function mb_detect_order($encodingList = null) { return p\Mbstring::mb_detect_order($encodingList); } - function mb_parse_str($s, &$result = array()) { parse_str($s, $result); } - function mb_strlen($s, $enc = null) { return p\Mbstring::mb_strlen($s, $enc); } - function mb_strpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strpos($s, $needle, $offset, $enc); } - function mb_strtolower($s, $enc = null) { return p\Mbstring::mb_strtolower($s, $enc); } - function mb_strtoupper($s, $enc = null) { return p\Mbstring::mb_strtoupper($s, $enc); } - function mb_substitute_character($char = null) { return p\Mbstring::mb_substitute_character($char); } - function mb_substr($s, $start, $length = 2147483647, $enc = null) { return p\Mbstring::mb_substr($s, $start, $length, $enc); } - function mb_stripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_stripos($s, $needle, $offset, $enc); } - function mb_stristr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_stristr($s, $needle, $part, $enc); } - function mb_strrchr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrchr($s, $needle, $part, $enc); } - function mb_strrichr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strrichr($s, $needle, $part, $enc); } - function mb_strripos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strripos($s, $needle, $offset, $enc); } - function mb_strrpos($s, $needle, $offset = 0, $enc = null) { return p\Mbstring::mb_strrpos($s, $needle, $offset, $enc); } - function mb_strstr($s, $needle, $part = false, $enc = null) { return p\Mbstring::mb_strstr($s, $needle, $part, $enc); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding($value = null, $encoding = null) { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding($string, $encodings = null, $strict = false) { return p\Mbstring::mb_detect_encoding($string, $encodings, $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order($encoding = null) { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str($string, &$result = []) { parse_str($string, $result); return (bool) $result; } +} +if (!function_exists('mb_strlen')) { + function mb_strlen($string, $encoding = null) { return p\Mbstring::mb_strlen($string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower($string, $encoding = null) { return p\Mbstring::mb_strtolower($string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper($string, $encoding = null) { return p\Mbstring::mb_strtoupper($string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character($substitute_character = null) { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr($string, $start, $length = 2147483647, $encoding = null) { return p\Mbstring::mb_substr($string, $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_stripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_stristr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrchr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strrichr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strripos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos($haystack, $needle, $offset = 0, $encoding = null) { return p\Mbstring::mb_strrpos($haystack, $needle, $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr($haystack, $needle, $before_needle = false, $encoding = null) { return p\Mbstring::mb_strstr($haystack, $needle, $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { function mb_get_info($type = 'all') { return p\Mbstring::mb_get_info($type); } - function mb_http_output($enc = null) { return p\Mbstring::mb_http_output($enc); } - function mb_strwidth($s, $enc = null) { return p\Mbstring::mb_strwidth($s, $enc); } - function mb_substr_count($haystack, $needle, $enc = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $enc); } - function mb_output_handler($contents, $status) { return p\Mbstring::mb_output_handler($contents, $status); } - function mb_http_input($type = '') { return p\Mbstring::mb_http_input($type); } - function mb_convert_variables($toEncoding, $fromEncoding, &$a = null, &$b = null, &$c = null, &$d = null, &$e = null, &$f = null) { return p\Mbstring::mb_convert_variables($toEncoding, $fromEncoding, $a, $b, $c, $d, $e, $f); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output($encoding = null) { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth($string, $encoding = null) { return p\Mbstring::mb_strwidth($string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count($haystack, $needle, $encoding = null) { return p\Mbstring::mb_substr_count($haystack, $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler($string, $status) { return p\Mbstring::mb_output_handler($string, $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input($type = null) { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables($to_encoding, $from_encoding, &...$vars) { return p\Mbstring::mb_convert_variables($to_encoding, $from_encoding, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord($string, $encoding = null) { return p\Mbstring::mb_ord($string, $encoding); } } if (!function_exists('mb_chr')) { - function mb_ord($s, $enc = null) { return p\Mbstring::mb_ord($s, $enc); } - function mb_chr($code, $enc = null) { return p\Mbstring::mb_chr($code, $enc); } - function mb_scrub($s, $enc = null) { $enc = null === $enc ? mb_internal_encoding() : $enc; return mb_convert_encoding($s, $enc, $enc); } + function mb_chr($codepoint, $encoding = null) { return p\Mbstring::mb_chr($codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub($string, $encoding = null) { $encoding = null === $encoding ? mb_internal_encoding() : $encoding; return mb_convert_encoding($string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split($string, $length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $length, $encoding); } } -if (!function_exists('mb_str_split')) { - function mb_str_split($string, $split_length = 1, $encoding = null) { return p\Mbstring::mb_str_split($string, $split_length, $encoding); } +if (!function_exists('mb_str_pad')) { + function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } +} + +if (!function_exists('mb_ucfirst')) { + function mb_ucfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst($string, $encoding); } +} + +if (!function_exists('mb_lcfirst')) { + function mb_lcfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); } +} + +if (!function_exists('mb_trim')) { + function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim($string, $characters, $encoding); } +} + +if (!function_exists('mb_ltrim')) { + function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim($string, $characters, $encoding); } +} + +if (!function_exists('mb_rtrim')) { + function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim($string, $characters, $encoding); } +} + + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); } diff --git a/vendor/symfony/polyfill-mbstring/bootstrap80.php b/vendor/symfony/polyfill-mbstring/bootstrap80.php new file mode 100644 index 0000000..5236e6d --- /dev/null +++ b/vendor/symfony/polyfill-mbstring/bootstrap80.php @@ -0,0 +1,167 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Symfony\Polyfill\Mbstring as p; + +if (!function_exists('mb_convert_encoding')) { + function mb_convert_encoding(array|string|null $string, ?string $to_encoding, array|string|null $from_encoding = null): array|string|false { return p\Mbstring::mb_convert_encoding($string ?? '', (string) $to_encoding, $from_encoding); } +} +if (!function_exists('mb_decode_mimeheader')) { + function mb_decode_mimeheader(?string $string): string { return p\Mbstring::mb_decode_mimeheader((string) $string); } +} +if (!function_exists('mb_encode_mimeheader')) { + function mb_encode_mimeheader(?string $string, ?string $charset = null, ?string $transfer_encoding = null, ?string $newline = "\r\n", ?int $indent = 0): string { return p\Mbstring::mb_encode_mimeheader((string) $string, $charset, $transfer_encoding, (string) $newline, (int) $indent); } +} +if (!function_exists('mb_decode_numericentity')) { + function mb_decode_numericentity(?string $string, array $map, ?string $encoding = null): string { return p\Mbstring::mb_decode_numericentity((string) $string, $map, $encoding); } +} +if (!function_exists('mb_encode_numericentity')) { + function mb_encode_numericentity(?string $string, array $map, ?string $encoding = null, ?bool $hex = false): string { return p\Mbstring::mb_encode_numericentity((string) $string, $map, $encoding, (bool) $hex); } +} +if (!function_exists('mb_convert_case')) { + function mb_convert_case(?string $string, ?int $mode, ?string $encoding = null): string { return p\Mbstring::mb_convert_case((string) $string, (int) $mode, $encoding); } +} +if (!function_exists('mb_internal_encoding')) { + function mb_internal_encoding(?string $encoding = null): string|bool { return p\Mbstring::mb_internal_encoding($encoding); } +} +if (!function_exists('mb_language')) { + function mb_language(?string $language = null): string|bool { return p\Mbstring::mb_language($language); } +} +if (!function_exists('mb_list_encodings')) { + function mb_list_encodings(): array { return p\Mbstring::mb_list_encodings(); } +} +if (!function_exists('mb_encoding_aliases')) { + function mb_encoding_aliases(?string $encoding): array { return p\Mbstring::mb_encoding_aliases((string) $encoding); } +} +if (!function_exists('mb_check_encoding')) { + function mb_check_encoding(array|string|null $value = null, ?string $encoding = null): bool { return p\Mbstring::mb_check_encoding($value, $encoding); } +} +if (!function_exists('mb_detect_encoding')) { + function mb_detect_encoding(?string $string, array|string|null $encodings = null, ?bool $strict = false): string|false { return p\Mbstring::mb_detect_encoding((string) $string, $encodings, (bool) $strict); } +} +if (!function_exists('mb_detect_order')) { + function mb_detect_order(array|string|null $encoding = null): array|bool { return p\Mbstring::mb_detect_order($encoding); } +} +if (!function_exists('mb_parse_str')) { + function mb_parse_str(?string $string, &$result = []): bool { parse_str((string) $string, $result); return (bool) $result; } +} +if (!function_exists('mb_strlen')) { + function mb_strlen(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strlen((string) $string, $encoding); } +} +if (!function_exists('mb_strpos')) { + function mb_strpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strtolower')) { + function mb_strtolower(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtolower((string) $string, $encoding); } +} +if (!function_exists('mb_strtoupper')) { + function mb_strtoupper(?string $string, ?string $encoding = null): string { return p\Mbstring::mb_strtoupper((string) $string, $encoding); } +} +if (!function_exists('mb_substitute_character')) { + function mb_substitute_character(string|int|null $substitute_character = null): string|int|bool { return p\Mbstring::mb_substitute_character($substitute_character); } +} +if (!function_exists('mb_substr')) { + function mb_substr(?string $string, ?int $start, ?int $length = null, ?string $encoding = null): string { return p\Mbstring::mb_substr((string) $string, (int) $start, $length, $encoding); } +} +if (!function_exists('mb_stripos')) { + function mb_stripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_stripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_stristr')) { + function mb_stristr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_stristr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strrchr')) { + function mb_strrchr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrchr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strrichr')) { + function mb_strrichr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strrichr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_strripos')) { + function mb_strripos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strripos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strrpos')) { + function mb_strrpos(?string $haystack, ?string $needle, ?int $offset = 0, ?string $encoding = null): int|false { return p\Mbstring::mb_strrpos((string) $haystack, (string) $needle, (int) $offset, $encoding); } +} +if (!function_exists('mb_strstr')) { + function mb_strstr(?string $haystack, ?string $needle, ?bool $before_needle = false, ?string $encoding = null): string|false { return p\Mbstring::mb_strstr((string) $haystack, (string) $needle, (bool) $before_needle, $encoding); } +} +if (!function_exists('mb_get_info')) { + function mb_get_info(?string $type = 'all'): array|string|int|false|null { return p\Mbstring::mb_get_info((string) $type); } +} +if (!function_exists('mb_http_output')) { + function mb_http_output(?string $encoding = null): string|bool { return p\Mbstring::mb_http_output($encoding); } +} +if (!function_exists('mb_strwidth')) { + function mb_strwidth(?string $string, ?string $encoding = null): int { return p\Mbstring::mb_strwidth((string) $string, $encoding); } +} +if (!function_exists('mb_substr_count')) { + function mb_substr_count(?string $haystack, ?string $needle, ?string $encoding = null): int { return p\Mbstring::mb_substr_count((string) $haystack, (string) $needle, $encoding); } +} +if (!function_exists('mb_output_handler')) { + function mb_output_handler(?string $string, ?int $status): string { return p\Mbstring::mb_output_handler((string) $string, (int) $status); } +} +if (!function_exists('mb_http_input')) { + function mb_http_input(?string $type = null): array|string|false { return p\Mbstring::mb_http_input($type); } +} + +if (!function_exists('mb_convert_variables')) { + function mb_convert_variables(?string $to_encoding, array|string|null $from_encoding, mixed &$var, mixed &...$vars): string|false { return p\Mbstring::mb_convert_variables((string) $to_encoding, $from_encoding ?? '', $var, ...$vars); } +} + +if (!function_exists('mb_ord')) { + function mb_ord(?string $string, ?string $encoding = null): int|false { return p\Mbstring::mb_ord((string) $string, $encoding); } +} +if (!function_exists('mb_chr')) { + function mb_chr(?int $codepoint, ?string $encoding = null): string|false { return p\Mbstring::mb_chr((int) $codepoint, $encoding); } +} +if (!function_exists('mb_scrub')) { + function mb_scrub(?string $string, ?string $encoding = null): string { $encoding ??= mb_internal_encoding(); return mb_convert_encoding((string) $string, $encoding, $encoding); } +} +if (!function_exists('mb_str_split')) { + function mb_str_split(?string $string, ?int $length = 1, ?string $encoding = null): array { return p\Mbstring::mb_str_split((string) $string, (int) $length, $encoding); } +} + +if (!function_exists('mb_str_pad')) { + function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = STR_PAD_RIGHT, ?string $encoding = null): string { return p\Mbstring::mb_str_pad($string, $length, $pad_string, $pad_type, $encoding); } +} + +if (!function_exists('mb_ucfirst')) { + function mb_ucfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_ucfirst($string, $encoding); } +} + +if (!function_exists('mb_lcfirst')) { + function mb_lcfirst(string $string, ?string $encoding = null): string { return p\Mbstring::mb_lcfirst($string, $encoding); } +} + +if (!function_exists('mb_trim')) { + function mb_trim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_trim($string, $characters, $encoding); } +} + +if (!function_exists('mb_ltrim')) { + function mb_ltrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_ltrim($string, $characters, $encoding); } +} + +if (!function_exists('mb_rtrim')) { + function mb_rtrim(string $string, ?string $characters = null, ?string $encoding = null): string { return p\Mbstring::mb_rtrim($string, $characters, $encoding); } +} + +if (extension_loaded('mbstring')) { + return; +} + +if (!defined('MB_CASE_UPPER')) { + define('MB_CASE_UPPER', 0); +} +if (!defined('MB_CASE_LOWER')) { + define('MB_CASE_LOWER', 1); +} +if (!defined('MB_CASE_TITLE')) { + define('MB_CASE_TITLE', 2); +} diff --git a/vendor/symfony/polyfill-mbstring/composer.json b/vendor/symfony/polyfill-mbstring/composer.json index 02b8896..daa07f8 100644 --- a/vendor/symfony/polyfill-mbstring/composer.json +++ b/vendor/symfony/polyfill-mbstring/composer.json @@ -16,7 +16,11 @@ } ], "require": { - "php": ">=5.3.3" + "php": ">=7.2", + "ext-iconv": "*" + }, + "provide": { + "ext-mbstring": "*" }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Mbstring\\": "" }, @@ -27,8 +31,9 @@ }, "minimum-stability": "dev", "extra": { - "branch-alias": { - "dev-master": "1.15-dev" + "thanks": { + "name": "symfony/polyfill", + "url": "https://github.com/symfony/polyfill" } } } diff --git a/vendor/symfony/var-dumper/CHANGELOG.md b/vendor/symfony/var-dumper/CHANGELOG.md deleted file mode 100644 index 94b1c17..0000000 --- a/vendor/symfony/var-dumper/CHANGELOG.md +++ /dev/null @@ -1,53 +0,0 @@ -CHANGELOG -========= - -4.4.0 ------ - - * added `VarDumperTestTrait::setUpVarDumper()` and `VarDumperTestTrait::tearDownVarDumper()` - to configure casters & flags to use in tests - * added `ImagineCaster` and infrastructure to dump images - * added the stamps of a message after it is dispatched in `TraceableMessageBus` and `MessengerDataCollector` collected data - * added `UuidCaster` - * made all casters final - * added support for the `NO_COLOR` env var (https://no-color.org/) - -4.3.0 ------ - - * added `DsCaster` to support dumping the contents of data structures from the Ds extension - -4.2.0 ------ - - * support selecting the format to use by setting the environment variable `VAR_DUMPER_FORMAT` to `html` or `cli` - -4.1.0 ------ - - * added a `ServerDumper` to send serialized Data clones to a server - * added a `ServerDumpCommand` and `DumpServer` to run a server collecting - and displaying dumps on a single place with multiple formats support - * added `CliDescriptor` and `HtmlDescriptor` descriptors for `server:dump` CLI and HTML formats support - -4.0.0 ------ - - * support for passing `\ReflectionClass` instances to the `Caster::castObject()` - method has been dropped, pass class names as strings instead - * the `Data::getRawData()` method has been removed - * the `VarDumperTestTrait::assertDumpEquals()` method expects a 3rd `$filter = 0` - argument and moves `$message = ''` argument at 4th position. - * the `VarDumperTestTrait::assertDumpMatchesFormat()` method expects a 3rd `$filter = 0` - argument and moves `$message = ''` argument at 4th position. - -3.4.0 ------ - - * added `AbstractCloner::setMinDepth()` function to ensure minimum tree depth - * deprecated `MongoCaster` - -2.7.0 ------ - - * deprecated `Cloner\Data::getLimitedClone()`. Use `withMaxDepth`, `withMaxItemsPerDepth` or `withRefHandles` instead. diff --git a/vendor/symfony/var-dumper/Caster/AmqpCaster.php b/vendor/symfony/var-dumper/Caster/AmqpCaster.php deleted file mode 100644 index 883d02b..0000000 --- a/vendor/symfony/var-dumper/Caster/AmqpCaster.php +++ /dev/null @@ -1,212 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Amqp related classes to array representation. - * - * @author Grégoire Pineau - * - * @final - */ -class AmqpCaster -{ - private static $flags = [ - AMQP_DURABLE => 'AMQP_DURABLE', - AMQP_PASSIVE => 'AMQP_PASSIVE', - AMQP_EXCLUSIVE => 'AMQP_EXCLUSIVE', - AMQP_AUTODELETE => 'AMQP_AUTODELETE', - AMQP_INTERNAL => 'AMQP_INTERNAL', - AMQP_NOLOCAL => 'AMQP_NOLOCAL', - AMQP_AUTOACK => 'AMQP_AUTOACK', - AMQP_IFEMPTY => 'AMQP_IFEMPTY', - AMQP_IFUNUSED => 'AMQP_IFUNUSED', - AMQP_MANDATORY => 'AMQP_MANDATORY', - AMQP_IMMEDIATE => 'AMQP_IMMEDIATE', - AMQP_MULTIPLE => 'AMQP_MULTIPLE', - AMQP_NOWAIT => 'AMQP_NOWAIT', - AMQP_REQUEUE => 'AMQP_REQUEUE', - ]; - - private static $exchangeTypes = [ - AMQP_EX_TYPE_DIRECT => 'AMQP_EX_TYPE_DIRECT', - AMQP_EX_TYPE_FANOUT => 'AMQP_EX_TYPE_FANOUT', - AMQP_EX_TYPE_TOPIC => 'AMQP_EX_TYPE_TOPIC', - AMQP_EX_TYPE_HEADERS => 'AMQP_EX_TYPE_HEADERS', - ]; - - public static function castConnection(\AMQPConnection $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'is_connected' => $c->isConnected(), - ]; - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPConnection\x00login"])) { - return $a; - } - - // BC layer in the amqp lib - if (method_exists($c, 'getReadTimeout')) { - $timeout = $c->getReadTimeout(); - } else { - $timeout = $c->getTimeout(); - } - - $a += [ - $prefix.'is_connected' => $c->isConnected(), - $prefix.'login' => $c->getLogin(), - $prefix.'password' => $c->getPassword(), - $prefix.'host' => $c->getHost(), - $prefix.'vhost' => $c->getVhost(), - $prefix.'port' => $c->getPort(), - $prefix.'read_timeout' => $timeout, - ]; - - return $a; - } - - public static function castChannel(\AMQPChannel $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'is_connected' => $c->isConnected(), - $prefix.'channel_id' => $c->getChannelId(), - ]; - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPChannel\x00connection"])) { - return $a; - } - - $a += [ - $prefix.'connection' => $c->getConnection(), - $prefix.'prefetch_size' => $c->getPrefetchSize(), - $prefix.'prefetch_count' => $c->getPrefetchCount(), - ]; - - return $a; - } - - public static function castQueue(\AMQPQueue $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'flags' => self::extractFlags($c->getFlags()), - ]; - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPQueue\x00name"])) { - return $a; - } - - $a += [ - $prefix.'connection' => $c->getConnection(), - $prefix.'channel' => $c->getChannel(), - $prefix.'name' => $c->getName(), - $prefix.'arguments' => $c->getArguments(), - ]; - - return $a; - } - - public static function castExchange(\AMQPExchange $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'flags' => self::extractFlags($c->getFlags()), - ]; - - $type = isset(self::$exchangeTypes[$c->getType()]) ? new ConstStub(self::$exchangeTypes[$c->getType()], $c->getType()) : $c->getType(); - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPExchange\x00name"])) { - $a["\x00AMQPExchange\x00type"] = $type; - - return $a; - } - - $a += [ - $prefix.'connection' => $c->getConnection(), - $prefix.'channel' => $c->getChannel(), - $prefix.'name' => $c->getName(), - $prefix.'type' => $type, - $prefix.'arguments' => $c->getArguments(), - ]; - - return $a; - } - - public static function castEnvelope(\AMQPEnvelope $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $deliveryMode = new ConstStub($c->getDeliveryMode().(2 === $c->getDeliveryMode() ? ' (persistent)' : ' (non-persistent)'), $c->getDeliveryMode()); - - // Recent version of the extension already expose private properties - if (isset($a["\x00AMQPEnvelope\x00body"])) { - $a["\0AMQPEnvelope\0delivery_mode"] = $deliveryMode; - - return $a; - } - - if (!($filter & Caster::EXCLUDE_VERBOSE)) { - $a += [$prefix.'body' => $c->getBody()]; - } - - $a += [ - $prefix.'delivery_tag' => $c->getDeliveryTag(), - $prefix.'is_redelivery' => $c->isRedelivery(), - $prefix.'exchange_name' => $c->getExchangeName(), - $prefix.'routing_key' => $c->getRoutingKey(), - $prefix.'content_type' => $c->getContentType(), - $prefix.'content_encoding' => $c->getContentEncoding(), - $prefix.'headers' => $c->getHeaders(), - $prefix.'delivery_mode' => $deliveryMode, - $prefix.'priority' => $c->getPriority(), - $prefix.'correlation_id' => $c->getCorrelationId(), - $prefix.'reply_to' => $c->getReplyTo(), - $prefix.'expiration' => $c->getExpiration(), - $prefix.'message_id' => $c->getMessageId(), - $prefix.'timestamp' => $c->getTimeStamp(), - $prefix.'type' => $c->getType(), - $prefix.'user_id' => $c->getUserId(), - $prefix.'app_id' => $c->getAppId(), - ]; - - return $a; - } - - private static function extractFlags(int $flags): ConstStub - { - $flagsArray = []; - - foreach (self::$flags as $value => $name) { - if ($flags & $value) { - $flagsArray[] = $name; - } - } - - if (!$flagsArray) { - $flagsArray = ['AMQP_NOPARAM']; - } - - return new ConstStub(implode('|', $flagsArray), $flags); - } -} diff --git a/vendor/symfony/var-dumper/Caster/ArgsStub.php b/vendor/symfony/var-dumper/Caster/ArgsStub.php deleted file mode 100644 index 591c7e2..0000000 --- a/vendor/symfony/var-dumper/Caster/ArgsStub.php +++ /dev/null @@ -1,80 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a list of function arguments. - * - * @author Nicolas Grekas - */ -class ArgsStub extends EnumStub -{ - private static $parameters = []; - - public function __construct(array $args, string $function, ?string $class) - { - list($variadic, $params) = self::getParameters($function, $class); - - $values = []; - foreach ($args as $k => $v) { - $values[$k] = !is_scalar($v) && !$v instanceof Stub ? new CutStub($v) : $v; - } - if (null === $params) { - parent::__construct($values, false); - - return; - } - if (\count($values) < \count($params)) { - $params = \array_slice($params, 0, \count($values)); - } elseif (\count($values) > \count($params)) { - $values[] = new EnumStub(array_splice($values, \count($params)), false); - $params[] = $variadic; - } - if (['...'] === $params) { - $this->dumpKeys = false; - $this->value = $values[0]->value; - } else { - $this->value = array_combine($params, $values); - } - } - - private static function getParameters(string $function, ?string $class): array - { - if (isset(self::$parameters[$k = $class.'::'.$function])) { - return self::$parameters[$k]; - } - - try { - $r = null !== $class ? new \ReflectionMethod($class, $function) : new \ReflectionFunction($function); - } catch (\ReflectionException $e) { - return [null, null]; - } - - $variadic = '...'; - $params = []; - foreach ($r->getParameters() as $v) { - $k = '$'.$v->name; - if ($v->isPassedByReference()) { - $k = '&'.$k; - } - if ($v->isVariadic()) { - $variadic .= $k; - } else { - $params[] = $k; - } - } - - return self::$parameters[$k] = [$variadic, $params]; - } -} diff --git a/vendor/symfony/var-dumper/Caster/Caster.php b/vendor/symfony/var-dumper/Caster/Caster.php deleted file mode 100644 index 5f3550f..0000000 --- a/vendor/symfony/var-dumper/Caster/Caster.php +++ /dev/null @@ -1,170 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Helper for filtering out properties in casters. - * - * @author Nicolas Grekas - * - * @final - */ -class Caster -{ - const EXCLUDE_VERBOSE = 1; - const EXCLUDE_VIRTUAL = 2; - const EXCLUDE_DYNAMIC = 4; - const EXCLUDE_PUBLIC = 8; - const EXCLUDE_PROTECTED = 16; - const EXCLUDE_PRIVATE = 32; - const EXCLUDE_NULL = 64; - const EXCLUDE_EMPTY = 128; - const EXCLUDE_NOT_IMPORTANT = 256; - const EXCLUDE_STRICT = 512; - - const PREFIX_VIRTUAL = "\0~\0"; - const PREFIX_DYNAMIC = "\0+\0"; - const PREFIX_PROTECTED = "\0*\0"; - - /** - * Casts objects to arrays and adds the dynamic property prefix. - * - * @param bool $hasDebugInfo Whether the __debugInfo method exists on $obj or not - * - * @return array The array-cast of the object, with prefixed dynamic properties - */ - public static function castObject(object $obj, string $class, bool $hasDebugInfo = false): array - { - if ($hasDebugInfo) { - try { - $debugInfo = $obj->__debugInfo(); - } catch (\Exception $e) { - // ignore failing __debugInfo() - $hasDebugInfo = false; - } - } - - $a = $obj instanceof \Closure ? [] : (array) $obj; - - if ($obj instanceof \__PHP_Incomplete_Class) { - return $a; - } - - if ($a) { - static $publicProperties = []; - - $i = 0; - $prefixedKeys = []; - foreach ($a as $k => $v) { - if ("\0" !== ($k[0] ?? '')) { - if (!isset($publicProperties[$class])) { - foreach ((new \ReflectionClass($class))->getProperties(\ReflectionProperty::IS_PUBLIC) as $prop) { - $publicProperties[$class][$prop->name] = true; - } - } - if (!isset($publicProperties[$class][$k])) { - $prefixedKeys[$i] = self::PREFIX_DYNAMIC.$k; - } - } elseif (isset($k[16]) && "\0" === $k[16] && 0 === strpos($k, "\0class@anonymous\0")) { - $prefixedKeys[$i] = "\0".get_parent_class($class).'@anonymous'.strrchr($k, "\0"); - } - ++$i; - } - if ($prefixedKeys) { - $keys = array_keys($a); - foreach ($prefixedKeys as $i => $k) { - $keys[$i] = $k; - } - $a = array_combine($keys, $a); - } - } - - if ($hasDebugInfo && \is_array($debugInfo)) { - foreach ($debugInfo as $k => $v) { - if (!isset($k[0]) || "\0" !== $k[0]) { - $k = self::PREFIX_VIRTUAL.$k; - } - - unset($a[$k]); - $a[$k] = $v; - } - } - - return $a; - } - - /** - * Filters out the specified properties. - * - * By default, a single match in the $filter bit field filters properties out, following an "or" logic. - * When EXCLUDE_STRICT is set, an "and" logic is applied: all bits must match for a property to be removed. - * - * @param array $a The array containing the properties to filter - * @param int $filter A bit field of Caster::EXCLUDE_* constants specifying which properties to filter out - * @param string[] $listedProperties List of properties to exclude when Caster::EXCLUDE_VERBOSE is set, and to preserve when Caster::EXCLUDE_NOT_IMPORTANT is set - * @param int &$count Set to the number of removed properties - * - * @return array The filtered array - */ - public static function filter(array $a, int $filter, array $listedProperties = [], ?int &$count = 0): array - { - $count = 0; - - foreach ($a as $k => $v) { - $type = self::EXCLUDE_STRICT & $filter; - - if (null === $v) { - $type |= self::EXCLUDE_NULL & $filter; - $type |= self::EXCLUDE_EMPTY & $filter; - } elseif (false === $v || '' === $v || '0' === $v || 0 === $v || 0.0 === $v || [] === $v) { - $type |= self::EXCLUDE_EMPTY & $filter; - } - if ((self::EXCLUDE_NOT_IMPORTANT & $filter) && !\in_array($k, $listedProperties, true)) { - $type |= self::EXCLUDE_NOT_IMPORTANT; - } - if ((self::EXCLUDE_VERBOSE & $filter) && \in_array($k, $listedProperties, true)) { - $type |= self::EXCLUDE_VERBOSE; - } - - if (!isset($k[1]) || "\0" !== $k[0]) { - $type |= self::EXCLUDE_PUBLIC & $filter; - } elseif ('~' === $k[1]) { - $type |= self::EXCLUDE_VIRTUAL & $filter; - } elseif ('+' === $k[1]) { - $type |= self::EXCLUDE_DYNAMIC & $filter; - } elseif ('*' === $k[1]) { - $type |= self::EXCLUDE_PROTECTED & $filter; - } else { - $type |= self::EXCLUDE_PRIVATE & $filter; - } - - if ((self::EXCLUDE_STRICT & $filter) ? $type === $filter : $type) { - unset($a[$k]); - ++$count; - } - } - - return $a; - } - - public static function castPhpIncompleteClass(\__PHP_Incomplete_Class $c, array $a, Stub $stub, bool $isNested): array - { - if (isset($a['__PHP_Incomplete_Class_Name'])) { - $stub->class .= '('.$a['__PHP_Incomplete_Class_Name'].')'; - unset($a['__PHP_Incomplete_Class_Name']); - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/ClassStub.php b/vendor/symfony/var-dumper/Caster/ClassStub.php deleted file mode 100644 index c998b49..0000000 --- a/vendor/symfony/var-dumper/Caster/ClassStub.php +++ /dev/null @@ -1,106 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a PHP class identifier. - * - * @author Nicolas Grekas - */ -class ClassStub extends ConstStub -{ - /** - * @param string $identifier A PHP identifier, e.g. a class, method, interface, etc. name - * @param callable $callable The callable targeted by the identifier when it is ambiguous or not a real PHP identifier - */ - public function __construct(string $identifier, $callable = null) - { - $this->value = $identifier; - - try { - if (null !== $callable) { - if ($callable instanceof \Closure) { - $r = new \ReflectionFunction($callable); - } elseif (\is_object($callable)) { - $r = [$callable, '__invoke']; - } elseif (\is_array($callable)) { - $r = $callable; - } elseif (false !== $i = strpos($callable, '::')) { - $r = [substr($callable, 0, $i), substr($callable, 2 + $i)]; - } else { - $r = new \ReflectionFunction($callable); - } - } elseif (0 < $i = strpos($identifier, '::') ?: strpos($identifier, '->')) { - $r = [substr($identifier, 0, $i), substr($identifier, 2 + $i)]; - } else { - $r = new \ReflectionClass($identifier); - } - - if (\is_array($r)) { - try { - $r = new \ReflectionMethod($r[0], $r[1]); - } catch (\ReflectionException $e) { - $r = new \ReflectionClass($r[0]); - } - } - - if (false !== strpos($identifier, "class@anonymous\0")) { - $this->value = $identifier = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { - return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0]; - }, $identifier); - } - - if (null !== $callable && $r instanceof \ReflectionFunctionAbstract) { - $s = ReflectionCaster::castFunctionAbstract($r, [], new Stub(), true, Caster::EXCLUDE_VERBOSE); - $s = ReflectionCaster::getSignature($s); - - if ('()' === substr($identifier, -2)) { - $this->value = substr_replace($identifier, $s, -2); - } else { - $this->value .= $s; - } - } - } catch (\ReflectionException $e) { - return; - } finally { - if (0 < $i = strrpos($this->value, '\\')) { - $this->attr['ellipsis'] = \strlen($this->value) - $i; - $this->attr['ellipsis-type'] = 'class'; - $this->attr['ellipsis-tail'] = 1; - } - } - - if ($f = $r->getFileName()) { - $this->attr['file'] = $f; - $this->attr['line'] = $r->getStartLine(); - } - } - - public static function wrapCallable($callable) - { - if (\is_object($callable) || !\is_callable($callable)) { - return $callable; - } - - if (!\is_array($callable)) { - $callable = new static($callable, $callable); - } elseif (\is_string($callable[0])) { - $callable[0] = new static($callable[0], $callable); - } else { - $callable[1] = new static($callable[1], $callable); - } - - return $callable; - } -} diff --git a/vendor/symfony/var-dumper/Caster/ConstStub.php b/vendor/symfony/var-dumper/Caster/ConstStub.php deleted file mode 100644 index 8b01797..0000000 --- a/vendor/symfony/var-dumper/Caster/ConstStub.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a PHP constant and its value. - * - * @author Nicolas Grekas - */ -class ConstStub extends Stub -{ - public function __construct(string $name, $value = null) - { - $this->class = $name; - $this->value = 1 < \func_num_args() ? $value : $name; - } - - /** - * @return string - */ - public function __toString() - { - return (string) $this->value; - } -} diff --git a/vendor/symfony/var-dumper/Caster/CutArrayStub.php b/vendor/symfony/var-dumper/Caster/CutArrayStub.php deleted file mode 100644 index 0e4fb36..0000000 --- a/vendor/symfony/var-dumper/Caster/CutArrayStub.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * Represents a cut array. - * - * @author Nicolas Grekas - */ -class CutArrayStub extends CutStub -{ - public $preservedSubset; - - public function __construct(array $value, array $preservedKeys) - { - parent::__construct($value); - - $this->preservedSubset = array_intersect_key($value, array_flip($preservedKeys)); - $this->cut -= \count($this->preservedSubset); - } -} diff --git a/vendor/symfony/var-dumper/Caster/CutStub.php b/vendor/symfony/var-dumper/Caster/CutStub.php deleted file mode 100644 index 464c6db..0000000 --- a/vendor/symfony/var-dumper/Caster/CutStub.php +++ /dev/null @@ -1,64 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents the main properties of a PHP variable, pre-casted by a caster. - * - * @author Nicolas Grekas - */ -class CutStub extends Stub -{ - public function __construct($value) - { - $this->value = $value; - - switch (\gettype($value)) { - case 'object': - $this->type = self::TYPE_OBJECT; - $this->class = \get_class($value); - - if ($value instanceof \Closure) { - ReflectionCaster::castClosure($value, [], $this, true, Caster::EXCLUDE_VERBOSE); - } - - $this->cut = -1; - break; - - case 'array': - $this->type = self::TYPE_ARRAY; - $this->class = self::ARRAY_ASSOC; - $this->cut = $this->value = \count($value); - break; - - case 'resource': - case 'unknown type': - case 'resource (closed)': - $this->type = self::TYPE_RESOURCE; - $this->handle = (int) $value; - if ('Unknown' === $this->class = @get_resource_type($value)) { - $this->class = 'Closed'; - } - $this->cut = -1; - break; - - case 'string': - $this->type = self::TYPE_STRING; - $this->class = preg_match('//u', $value) ? self::STRING_UTF8 : self::STRING_BINARY; - $this->cut = self::STRING_BINARY === $this->class ? \strlen($value) : mb_strlen($value, 'UTF-8'); - $this->value = ''; - break; - } - } -} diff --git a/vendor/symfony/var-dumper/Caster/DOMCaster.php b/vendor/symfony/var-dumper/Caster/DOMCaster.php deleted file mode 100644 index 98359ff..0000000 --- a/vendor/symfony/var-dumper/Caster/DOMCaster.php +++ /dev/null @@ -1,304 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts DOM related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class DOMCaster -{ - private static $errorCodes = [ - DOM_PHP_ERR => 'DOM_PHP_ERR', - DOM_INDEX_SIZE_ERR => 'DOM_INDEX_SIZE_ERR', - DOMSTRING_SIZE_ERR => 'DOMSTRING_SIZE_ERR', - DOM_HIERARCHY_REQUEST_ERR => 'DOM_HIERARCHY_REQUEST_ERR', - DOM_WRONG_DOCUMENT_ERR => 'DOM_WRONG_DOCUMENT_ERR', - DOM_INVALID_CHARACTER_ERR => 'DOM_INVALID_CHARACTER_ERR', - DOM_NO_DATA_ALLOWED_ERR => 'DOM_NO_DATA_ALLOWED_ERR', - DOM_NO_MODIFICATION_ALLOWED_ERR => 'DOM_NO_MODIFICATION_ALLOWED_ERR', - DOM_NOT_FOUND_ERR => 'DOM_NOT_FOUND_ERR', - DOM_NOT_SUPPORTED_ERR => 'DOM_NOT_SUPPORTED_ERR', - DOM_INUSE_ATTRIBUTE_ERR => 'DOM_INUSE_ATTRIBUTE_ERR', - DOM_INVALID_STATE_ERR => 'DOM_INVALID_STATE_ERR', - DOM_SYNTAX_ERR => 'DOM_SYNTAX_ERR', - DOM_INVALID_MODIFICATION_ERR => 'DOM_INVALID_MODIFICATION_ERR', - DOM_NAMESPACE_ERR => 'DOM_NAMESPACE_ERR', - DOM_INVALID_ACCESS_ERR => 'DOM_INVALID_ACCESS_ERR', - DOM_VALIDATION_ERR => 'DOM_VALIDATION_ERR', - ]; - - private static $nodeTypes = [ - XML_ELEMENT_NODE => 'XML_ELEMENT_NODE', - XML_ATTRIBUTE_NODE => 'XML_ATTRIBUTE_NODE', - XML_TEXT_NODE => 'XML_TEXT_NODE', - XML_CDATA_SECTION_NODE => 'XML_CDATA_SECTION_NODE', - XML_ENTITY_REF_NODE => 'XML_ENTITY_REF_NODE', - XML_ENTITY_NODE => 'XML_ENTITY_NODE', - XML_PI_NODE => 'XML_PI_NODE', - XML_COMMENT_NODE => 'XML_COMMENT_NODE', - XML_DOCUMENT_NODE => 'XML_DOCUMENT_NODE', - XML_DOCUMENT_TYPE_NODE => 'XML_DOCUMENT_TYPE_NODE', - XML_DOCUMENT_FRAG_NODE => 'XML_DOCUMENT_FRAG_NODE', - XML_NOTATION_NODE => 'XML_NOTATION_NODE', - XML_HTML_DOCUMENT_NODE => 'XML_HTML_DOCUMENT_NODE', - XML_DTD_NODE => 'XML_DTD_NODE', - XML_ELEMENT_DECL_NODE => 'XML_ELEMENT_DECL_NODE', - XML_ATTRIBUTE_DECL_NODE => 'XML_ATTRIBUTE_DECL_NODE', - XML_ENTITY_DECL_NODE => 'XML_ENTITY_DECL_NODE', - XML_NAMESPACE_DECL_NODE => 'XML_NAMESPACE_DECL_NODE', - ]; - - public static function castException(\DOMException $e, array $a, Stub $stub, bool $isNested) - { - $k = Caster::PREFIX_PROTECTED.'code'; - if (isset($a[$k], self::$errorCodes[$a[$k]])) { - $a[$k] = new ConstStub(self::$errorCodes[$a[$k]], $a[$k]); - } - - return $a; - } - - public static function castLength($dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'length' => $dom->length, - ]; - - return $a; - } - - public static function castImplementation($dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'Core' => '1.0', - Caster::PREFIX_VIRTUAL.'XML' => '2.0', - ]; - - return $a; - } - - public static function castNode(\DOMNode $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'nodeName' => $dom->nodeName, - 'nodeValue' => new CutStub($dom->nodeValue), - 'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType), - 'parentNode' => new CutStub($dom->parentNode), - 'childNodes' => $dom->childNodes, - 'firstChild' => new CutStub($dom->firstChild), - 'lastChild' => new CutStub($dom->lastChild), - 'previousSibling' => new CutStub($dom->previousSibling), - 'nextSibling' => new CutStub($dom->nextSibling), - 'attributes' => $dom->attributes, - 'ownerDocument' => new CutStub($dom->ownerDocument), - 'namespaceURI' => $dom->namespaceURI, - 'prefix' => $dom->prefix, - 'localName' => $dom->localName, - 'baseURI' => $dom->baseURI ? new LinkStub($dom->baseURI) : $dom->baseURI, - 'textContent' => new CutStub($dom->textContent), - ]; - - return $a; - } - - public static function castNameSpaceNode(\DOMNameSpaceNode $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'nodeName' => $dom->nodeName, - 'nodeValue' => new CutStub($dom->nodeValue), - 'nodeType' => new ConstStub(self::$nodeTypes[$dom->nodeType], $dom->nodeType), - 'prefix' => $dom->prefix, - 'localName' => $dom->localName, - 'namespaceURI' => $dom->namespaceURI, - 'ownerDocument' => new CutStub($dom->ownerDocument), - 'parentNode' => new CutStub($dom->parentNode), - ]; - - return $a; - } - - public static function castDocument(\DOMDocument $dom, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - 'doctype' => $dom->doctype, - 'implementation' => $dom->implementation, - 'documentElement' => new CutStub($dom->documentElement), - 'actualEncoding' => $dom->actualEncoding, - 'encoding' => $dom->encoding, - 'xmlEncoding' => $dom->xmlEncoding, - 'standalone' => $dom->standalone, - 'xmlStandalone' => $dom->xmlStandalone, - 'version' => $dom->version, - 'xmlVersion' => $dom->xmlVersion, - 'strictErrorChecking' => $dom->strictErrorChecking, - 'documentURI' => $dom->documentURI ? new LinkStub($dom->documentURI) : $dom->documentURI, - 'config' => $dom->config, - 'formatOutput' => $dom->formatOutput, - 'validateOnParse' => $dom->validateOnParse, - 'resolveExternals' => $dom->resolveExternals, - 'preserveWhiteSpace' => $dom->preserveWhiteSpace, - 'recover' => $dom->recover, - 'substituteEntities' => $dom->substituteEntities, - ]; - - if (!($filter & Caster::EXCLUDE_VERBOSE)) { - $formatOutput = $dom->formatOutput; - $dom->formatOutput = true; - $a += [Caster::PREFIX_VIRTUAL.'xml' => $dom->saveXML()]; - $dom->formatOutput = $formatOutput; - } - - return $a; - } - - public static function castCharacterData(\DOMCharacterData $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'data' => $dom->data, - 'length' => $dom->length, - ]; - - return $a; - } - - public static function castAttr(\DOMAttr $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'name' => $dom->name, - 'specified' => $dom->specified, - 'value' => $dom->value, - 'ownerElement' => $dom->ownerElement, - 'schemaTypeInfo' => $dom->schemaTypeInfo, - ]; - - return $a; - } - - public static function castElement(\DOMElement $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'tagName' => $dom->tagName, - 'schemaTypeInfo' => $dom->schemaTypeInfo, - ]; - - return $a; - } - - public static function castText(\DOMText $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'wholeText' => $dom->wholeText, - ]; - - return $a; - } - - public static function castTypeinfo(\DOMTypeinfo $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'typeName' => $dom->typeName, - 'typeNamespace' => $dom->typeNamespace, - ]; - - return $a; - } - - public static function castDomError(\DOMDomError $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'severity' => $dom->severity, - 'message' => $dom->message, - 'type' => $dom->type, - 'relatedException' => $dom->relatedException, - 'related_data' => $dom->related_data, - 'location' => $dom->location, - ]; - - return $a; - } - - public static function castLocator(\DOMLocator $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'lineNumber' => $dom->lineNumber, - 'columnNumber' => $dom->columnNumber, - 'offset' => $dom->offset, - 'relatedNode' => $dom->relatedNode, - 'uri' => $dom->uri ? new LinkStub($dom->uri, $dom->lineNumber) : $dom->uri, - ]; - - return $a; - } - - public static function castDocumentType(\DOMDocumentType $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'name' => $dom->name, - 'entities' => $dom->entities, - 'notations' => $dom->notations, - 'publicId' => $dom->publicId, - 'systemId' => $dom->systemId, - 'internalSubset' => $dom->internalSubset, - ]; - - return $a; - } - - public static function castNotation(\DOMNotation $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'publicId' => $dom->publicId, - 'systemId' => $dom->systemId, - ]; - - return $a; - } - - public static function castEntity(\DOMEntity $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'publicId' => $dom->publicId, - 'systemId' => $dom->systemId, - 'notationName' => $dom->notationName, - 'actualEncoding' => $dom->actualEncoding, - 'encoding' => $dom->encoding, - 'version' => $dom->version, - ]; - - return $a; - } - - public static function castProcessingInstruction(\DOMProcessingInstruction $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'target' => $dom->target, - 'data' => $dom->data, - ]; - - return $a; - } - - public static function castXPath(\DOMXPath $dom, array $a, Stub $stub, bool $isNested) - { - $a += [ - 'document' => $dom->document, - ]; - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/DateCaster.php b/vendor/symfony/var-dumper/Caster/DateCaster.php deleted file mode 100644 index e3708b7..0000000 --- a/vendor/symfony/var-dumper/Caster/DateCaster.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts DateTimeInterface related classes to array representation. - * - * @author Dany Maillard - * - * @final - */ -class DateCaster -{ - private const PERIOD_LIMIT = 3; - - public static function castDateTime(\DateTimeInterface $d, array $a, Stub $stub, bool $isNested, int $filter) - { - $prefix = Caster::PREFIX_VIRTUAL; - $location = $d->getTimezone()->getLocation(); - $fromNow = (new \DateTime())->diff($d); - - $title = $d->format('l, F j, Y') - ."\n".self::formatInterval($fromNow).' from now' - .($location ? ($d->format('I') ? "\nDST On" : "\nDST Off") : '') - ; - - unset( - $a[Caster::PREFIX_DYNAMIC.'date'], - $a[Caster::PREFIX_DYNAMIC.'timezone'], - $a[Caster::PREFIX_DYNAMIC.'timezone_type'] - ); - $a[$prefix.'date'] = new ConstStub(self::formatDateTime($d, $location ? ' e (P)' : ' P'), $title); - - $stub->class .= $d->format(' @U'); - - return $a; - } - - public static function castInterval(\DateInterval $interval, array $a, Stub $stub, bool $isNested, int $filter) - { - $now = new \DateTimeImmutable(); - $numberOfSeconds = $now->add($interval)->getTimestamp() - $now->getTimestamp(); - $title = number_format($numberOfSeconds, 0, '.', ' ').'s'; - - $i = [Caster::PREFIX_VIRTUAL.'interval' => new ConstStub(self::formatInterval($interval), $title)]; - - return $filter & Caster::EXCLUDE_VERBOSE ? $i : $i + $a; - } - - private static function formatInterval(\DateInterval $i): string - { - $format = '%R '; - - if (0 === $i->y && 0 === $i->m && ($i->h >= 24 || $i->i >= 60 || $i->s >= 60)) { - $i = date_diff($d = new \DateTime(), date_add(clone $d, $i)); // recalculate carry over points - $format .= 0 < $i->days ? '%ad ' : ''; - } else { - $format .= ($i->y ? '%yy ' : '').($i->m ? '%mm ' : '').($i->d ? '%dd ' : ''); - } - - $format .= $i->h || $i->i || $i->s || $i->f ? '%H:%I:'.self::formatSeconds($i->s, substr($i->f, 2)) : ''; - $format = '%R ' === $format ? '0s' : $format; - - return $i->format(rtrim($format)); - } - - public static function castTimeZone(\DateTimeZone $timeZone, array $a, Stub $stub, bool $isNested, int $filter) - { - $location = $timeZone->getLocation(); - $formatted = (new \DateTime('now', $timeZone))->format($location ? 'e (P)' : 'P'); - $title = $location && \extension_loaded('intl') ? \Locale::getDisplayRegion('-'.$location['country_code']) : ''; - - $z = [Caster::PREFIX_VIRTUAL.'timezone' => new ConstStub($formatted, $title)]; - - return $filter & Caster::EXCLUDE_VERBOSE ? $z : $z + $a; - } - - public static function castPeriod(\DatePeriod $p, array $a, Stub $stub, bool $isNested, int $filter) - { - $dates = []; - foreach (clone $p as $i => $d) { - if (self::PERIOD_LIMIT === $i) { - $now = new \DateTimeImmutable(); - $dates[] = sprintf('%s more', ($end = $p->getEndDate()) - ? ceil(($end->format('U.u') - $d->format('U.u')) / ((int) $now->add($p->getDateInterval())->format('U.u') - (int) $now->format('U.u'))) - : $p->recurrences - $i - ); - break; - } - $dates[] = sprintf('%s) %s', $i + 1, self::formatDateTime($d)); - } - - $period = sprintf( - 'every %s, from %s (%s) %s', - self::formatInterval($p->getDateInterval()), - self::formatDateTime($p->getStartDate()), - $p->include_start_date ? 'included' : 'excluded', - ($end = $p->getEndDate()) ? 'to '.self::formatDateTime($end) : 'recurring '.$p->recurrences.' time/s' - ); - - $p = [Caster::PREFIX_VIRTUAL.'period' => new ConstStub($period, implode("\n", $dates))]; - - return $filter & Caster::EXCLUDE_VERBOSE ? $p : $p + $a; - } - - private static function formatDateTime(\DateTimeInterface $d, string $extra = ''): string - { - return $d->format('Y-m-d H:i:'.self::formatSeconds($d->format('s'), $d->format('u')).$extra); - } - - private static function formatSeconds(string $s, string $us): string - { - return sprintf('%02d.%s', $s, 0 === ($len = \strlen($t = rtrim($us, '0'))) ? '0' : ($len <= 3 ? str_pad($t, 3, '0') : $us)); - } -} diff --git a/vendor/symfony/var-dumper/Caster/DoctrineCaster.php b/vendor/symfony/var-dumper/Caster/DoctrineCaster.php deleted file mode 100644 index 129b2cb..0000000 --- a/vendor/symfony/var-dumper/Caster/DoctrineCaster.php +++ /dev/null @@ -1,62 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Doctrine\Common\Proxy\Proxy as CommonProxy; -use Doctrine\ORM\PersistentCollection; -use Doctrine\ORM\Proxy\Proxy as OrmProxy; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Doctrine related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class DoctrineCaster -{ - public static function castCommonProxy(CommonProxy $proxy, array $a, Stub $stub, bool $isNested) - { - foreach (['__cloner__', '__initializer__'] as $k) { - if (\array_key_exists($k, $a)) { - unset($a[$k]); - ++$stub->cut; - } - } - - return $a; - } - - public static function castOrmProxy(OrmProxy $proxy, array $a, Stub $stub, bool $isNested) - { - foreach (['_entityPersister', '_identifier'] as $k) { - if (\array_key_exists($k = "\0Doctrine\\ORM\\Proxy\\Proxy\0".$k, $a)) { - unset($a[$k]); - ++$stub->cut; - } - } - - return $a; - } - - public static function castPersistentCollection(PersistentCollection $coll, array $a, Stub $stub, bool $isNested) - { - foreach (['snapshot', 'association', 'typeClass'] as $k) { - if (\array_key_exists($k = "\0Doctrine\\ORM\\PersistentCollection\0".$k, $a)) { - $a[$k] = new CutStub($a[$k]); - } - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/DsCaster.php b/vendor/symfony/var-dumper/Caster/DsCaster.php deleted file mode 100644 index b34b670..0000000 --- a/vendor/symfony/var-dumper/Caster/DsCaster.php +++ /dev/null @@ -1,70 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Ds\Collection; -use Ds\Map; -use Ds\Pair; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Ds extension classes to array representation. - * - * @author Jáchym Toušek - * - * @final - */ -class DsCaster -{ - public static function castCollection(Collection $c, array $a, Stub $stub, bool $isNested): array - { - $a[Caster::PREFIX_VIRTUAL.'count'] = $c->count(); - $a[Caster::PREFIX_VIRTUAL.'capacity'] = $c->capacity(); - - if (!$c instanceof Map) { - $a += $c->toArray(); - } - - return $a; - } - - public static function castMap(Map $c, array $a, Stub $stub, bool $isNested): array - { - foreach ($c as $k => $v) { - $a[] = new DsPairStub($k, $v); - } - - return $a; - } - - public static function castPair(Pair $c, array $a, Stub $stub, bool $isNested): array - { - foreach ($c->toArray() as $k => $v) { - $a[Caster::PREFIX_VIRTUAL.$k] = $v; - } - - return $a; - } - - public static function castPairStub(DsPairStub $c, array $a, Stub $stub, bool $isNested): array - { - if ($isNested) { - $stub->class = Pair::class; - $stub->value = null; - $stub->handle = 0; - - $a = $c->value; - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/DsPairStub.php b/vendor/symfony/var-dumper/Caster/DsPairStub.php deleted file mode 100644 index a1dcc15..0000000 --- a/vendor/symfony/var-dumper/Caster/DsPairStub.php +++ /dev/null @@ -1,28 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Nicolas Grekas - */ -class DsPairStub extends Stub -{ - public function __construct($key, $value) - { - $this->value = [ - Caster::PREFIX_VIRTUAL.'key' => $key, - Caster::PREFIX_VIRTUAL.'value' => $value, - ]; - } -} diff --git a/vendor/symfony/var-dumper/Caster/EnumStub.php b/vendor/symfony/var-dumper/Caster/EnumStub.php deleted file mode 100644 index 7a4e98a..0000000 --- a/vendor/symfony/var-dumper/Caster/EnumStub.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents an enumeration of values. - * - * @author Nicolas Grekas - */ -class EnumStub extends Stub -{ - public $dumpKeys = true; - - public function __construct(array $values, bool $dumpKeys = true) - { - $this->value = $values; - $this->dumpKeys = $dumpKeys; - } -} diff --git a/vendor/symfony/var-dumper/Caster/ExceptionCaster.php b/vendor/symfony/var-dumper/Caster/ExceptionCaster.php deleted file mode 100644 index 9229bdd..0000000 --- a/vendor/symfony/var-dumper/Caster/ExceptionCaster.php +++ /dev/null @@ -1,383 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\ErrorHandler\Exception\SilencedErrorContext; -use Symfony\Component\VarDumper\Cloner\Stub; -use Symfony\Component\VarDumper\Exception\ThrowingCasterException; - -/** - * Casts common Exception classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class ExceptionCaster -{ - public static $srcContext = 1; - public static $traceArgs = true; - public static $errorTypes = [ - E_DEPRECATED => 'E_DEPRECATED', - E_USER_DEPRECATED => 'E_USER_DEPRECATED', - E_RECOVERABLE_ERROR => 'E_RECOVERABLE_ERROR', - E_ERROR => 'E_ERROR', - E_WARNING => 'E_WARNING', - E_PARSE => 'E_PARSE', - E_NOTICE => 'E_NOTICE', - E_CORE_ERROR => 'E_CORE_ERROR', - E_CORE_WARNING => 'E_CORE_WARNING', - E_COMPILE_ERROR => 'E_COMPILE_ERROR', - E_COMPILE_WARNING => 'E_COMPILE_WARNING', - E_USER_ERROR => 'E_USER_ERROR', - E_USER_WARNING => 'E_USER_WARNING', - E_USER_NOTICE => 'E_USER_NOTICE', - E_STRICT => 'E_STRICT', - ]; - - private static $framesCache = []; - - public static function castError(\Error $e, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - return self::filterExceptionArray($stub->class, $a, "\0Error\0", $filter); - } - - public static function castException(\Exception $e, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - return self::filterExceptionArray($stub->class, $a, "\0Exception\0", $filter); - } - - public static function castErrorException(\ErrorException $e, array $a, Stub $stub, bool $isNested) - { - if (isset($a[$s = Caster::PREFIX_PROTECTED.'severity'], self::$errorTypes[$a[$s]])) { - $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); - } - - return $a; - } - - public static function castThrowingCasterException(ThrowingCasterException $e, array $a, Stub $stub, bool $isNested) - { - $trace = Caster::PREFIX_VIRTUAL.'trace'; - $prefix = Caster::PREFIX_PROTECTED; - $xPrefix = "\0Exception\0"; - - if (isset($a[$xPrefix.'previous'], $a[$trace]) && $a[$xPrefix.'previous'] instanceof \Exception) { - $b = (array) $a[$xPrefix.'previous']; - $class = \get_class($a[$xPrefix.'previous']); - $class = 'c' === $class[0] && 0 === strpos($class, "class@anonymous\0") ? get_parent_class($class).'@anonymous' : $class; - self::traceUnshift($b[$xPrefix.'trace'], $class, $b[$prefix.'file'], $b[$prefix.'line']); - $a[$trace] = new TraceStub($b[$xPrefix.'trace'], false, 0, -\count($a[$trace]->value)); - } - - unset($a[$xPrefix.'previous'], $a[$prefix.'code'], $a[$prefix.'file'], $a[$prefix.'line']); - - return $a; - } - - public static function castSilencedErrorContext(SilencedErrorContext $e, array $a, Stub $stub, bool $isNested) - { - $sPrefix = "\0".SilencedErrorContext::class."\0"; - - if (!isset($a[$s = $sPrefix.'severity'])) { - return $a; - } - - if (isset(self::$errorTypes[$a[$s]])) { - $a[$s] = new ConstStub(self::$errorTypes[$a[$s]], $a[$s]); - } - - $trace = [[ - 'file' => $a[$sPrefix.'file'], - 'line' => $a[$sPrefix.'line'], - ]]; - - if (isset($a[$sPrefix.'trace'])) { - $trace = array_merge($trace, $a[$sPrefix.'trace']); - } - - unset($a[$sPrefix.'file'], $a[$sPrefix.'line'], $a[$sPrefix.'trace']); - $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); - - return $a; - } - - public static function castTraceStub(TraceStub $trace, array $a, Stub $stub, bool $isNested) - { - if (!$isNested) { - return $a; - } - $stub->class = ''; - $stub->handle = 0; - $frames = $trace->value; - $prefix = Caster::PREFIX_VIRTUAL; - - $a = []; - $j = \count($frames); - if (0 > $i = $trace->sliceOffset) { - $i = max(0, $j + $i); - } - if (!isset($trace->value[$i])) { - return []; - } - $lastCall = isset($frames[$i]['function']) ? (isset($frames[$i]['class']) ? $frames[0]['class'].$frames[$i]['type'] : '').$frames[$i]['function'].'()' : ''; - $frames[] = ['function' => '']; - $collapse = false; - - for ($j += $trace->numberingOffset - $i++; isset($frames[$i]); ++$i, --$j) { - $f = $frames[$i]; - $call = isset($f['function']) ? (isset($f['class']) ? $f['class'].$f['type'] : '').$f['function'] : '???'; - - $frame = new FrameStub( - [ - 'object' => isset($f['object']) ? $f['object'] : null, - 'class' => isset($f['class']) ? $f['class'] : null, - 'type' => isset($f['type']) ? $f['type'] : null, - 'function' => isset($f['function']) ? $f['function'] : null, - ] + $frames[$i - 1], - false, - true - ); - $f = self::castFrameStub($frame, [], $frame, true); - if (isset($f[$prefix.'src'])) { - foreach ($f[$prefix.'src']->value as $label => $frame) { - if (0 === strpos($label, "\0~collapse=0")) { - if ($collapse) { - $label = substr_replace($label, '1', 11, 1); - } else { - $collapse = true; - } - } - $label = substr_replace($label, "title=Stack level $j.&", 2, 0); - } - $f = $frames[$i - 1]; - if ($trace->keepArgs && !empty($f['args']) && $frame instanceof EnumStub) { - $frame->value['arguments'] = new ArgsStub($f['args'], isset($f['function']) ? $f['function'] : null, isset($f['class']) ? $f['class'] : null); - } - } elseif ('???' !== $lastCall) { - $label = new ClassStub($lastCall); - if (isset($label->attr['ellipsis'])) { - $label->attr['ellipsis'] += 2; - $label = substr_replace($prefix, "ellipsis-type=class&ellipsis={$label->attr['ellipsis']}&ellipsis-tail=1&title=Stack level $j.", 2, 0).$label->value.'()'; - } else { - $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$label->value.'()'; - } - } else { - $label = substr_replace($prefix, "title=Stack level $j.", 2, 0).$lastCall; - } - $a[substr_replace($label, sprintf('separator=%s&', $frame instanceof EnumStub ? ' ' : ':'), 2, 0)] = $frame; - - $lastCall = $call; - } - if (null !== $trace->sliceLength) { - $a = \array_slice($a, 0, $trace->sliceLength, true); - } - - return $a; - } - - public static function castFrameStub(FrameStub $frame, array $a, Stub $stub, bool $isNested) - { - if (!$isNested) { - return $a; - } - $f = $frame->value; - $prefix = Caster::PREFIX_VIRTUAL; - - if (isset($f['file'], $f['line'])) { - $cacheKey = $f; - unset($cacheKey['object'], $cacheKey['args']); - $cacheKey[] = self::$srcContext; - $cacheKey = implode('-', $cacheKey); - - if (isset(self::$framesCache[$cacheKey])) { - $a[$prefix.'src'] = self::$framesCache[$cacheKey]; - } else { - if (preg_match('/\((\d+)\)(?:\([\da-f]{32}\))? : (?:eval\(\)\'d code|runtime-created function)$/', $f['file'], $match)) { - $f['file'] = substr($f['file'], 0, -\strlen($match[0])); - $f['line'] = (int) $match[1]; - } - $src = $f['line']; - $srcKey = $f['file']; - $ellipsis = new LinkStub($srcKey, 0); - $srcAttr = 'collapse='.(int) $ellipsis->inVendor; - $ellipsisTail = isset($ellipsis->attr['ellipsis-tail']) ? $ellipsis->attr['ellipsis-tail'] : 0; - $ellipsis = isset($ellipsis->attr['ellipsis']) ? $ellipsis->attr['ellipsis'] : 0; - - if (file_exists($f['file']) && 0 <= self::$srcContext) { - if (!empty($f['class']) && (is_subclass_of($f['class'], 'Twig\Template') || is_subclass_of($f['class'], 'Twig_Template')) && method_exists($f['class'], 'getDebugInfo')) { - $template = isset($f['object']) ? $f['object'] : unserialize(sprintf('O:%d:"%s":0:{}', \strlen($f['class']), $f['class'])); - - $ellipsis = 0; - $templateSrc = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : ''); - $templateInfo = $template->getDebugInfo(); - if (isset($templateInfo[$f['line']])) { - if (!method_exists($template, 'getSourceContext') || !file_exists($templatePath = $template->getSourceContext()->getPath())) { - $templatePath = null; - } - if ($templateSrc) { - $src = self::extractSource($templateSrc, $templateInfo[$f['line']], self::$srcContext, 'twig', $templatePath, $f); - $srcKey = ($templatePath ?: $template->getTemplateName()).':'.$templateInfo[$f['line']]; - } - } - } - if ($srcKey == $f['file']) { - $src = self::extractSource(file_get_contents($f['file']), $f['line'], self::$srcContext, 'php', $f['file'], $f); - $srcKey .= ':'.$f['line']; - if ($ellipsis) { - $ellipsis += 1 + \strlen($f['line']); - } - } - $srcAttr .= sprintf('&separator= &file=%s&line=%d', rawurlencode($f['file']), $f['line']); - } else { - $srcAttr .= '&separator=:'; - } - $srcAttr .= $ellipsis ? '&ellipsis-type=path&ellipsis='.$ellipsis.'&ellipsis-tail='.$ellipsisTail : ''; - self::$framesCache[$cacheKey] = $a[$prefix.'src'] = new EnumStub(["\0~$srcAttr\0$srcKey" => $src]); - } - } - - unset($a[$prefix.'args'], $a[$prefix.'line'], $a[$prefix.'file']); - if ($frame->inTraceStub) { - unset($a[$prefix.'class'], $a[$prefix.'type'], $a[$prefix.'function']); - } - foreach ($a as $k => $v) { - if (!$v) { - unset($a[$k]); - } - } - if ($frame->keepArgs && !empty($f['args'])) { - $a[$prefix.'arguments'] = new ArgsStub($f['args'], $f['function'], $f['class']); - } - - return $a; - } - - private static function filterExceptionArray(string $xClass, array $a, string $xPrefix, int $filter): array - { - if (isset($a[$xPrefix.'trace'])) { - $trace = $a[$xPrefix.'trace']; - unset($a[$xPrefix.'trace']); // Ensures the trace is always last - } else { - $trace = []; - } - - if (!($filter & Caster::EXCLUDE_VERBOSE) && $trace) { - if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { - self::traceUnshift($trace, $xClass, $a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); - } - $a[Caster::PREFIX_VIRTUAL.'trace'] = new TraceStub($trace, self::$traceArgs); - } - if (empty($a[$xPrefix.'previous'])) { - unset($a[$xPrefix.'previous']); - } - unset($a[$xPrefix.'string'], $a[Caster::PREFIX_DYNAMIC.'xdebug_message'], $a[Caster::PREFIX_DYNAMIC.'__destructorException']); - - if (isset($a[Caster::PREFIX_PROTECTED.'message']) && false !== strpos($a[Caster::PREFIX_PROTECTED.'message'], "class@anonymous\0")) { - $a[Caster::PREFIX_PROTECTED.'message'] = preg_replace_callback('/class@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++/', function ($m) { - return class_exists($m[0], false) ? get_parent_class($m[0]).'@anonymous' : $m[0]; - }, $a[Caster::PREFIX_PROTECTED.'message']); - } - - if (isset($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line'])) { - $a[Caster::PREFIX_PROTECTED.'file'] = new LinkStub($a[Caster::PREFIX_PROTECTED.'file'], $a[Caster::PREFIX_PROTECTED.'line']); - } - - return $a; - } - - private static function traceUnshift(array &$trace, ?string $class, string $file, int $line): void - { - if (isset($trace[0]['file'], $trace[0]['line']) && $trace[0]['file'] === $file && $trace[0]['line'] === $line) { - return; - } - array_unshift($trace, [ - 'function' => $class ? 'new '.$class : null, - 'file' => $file, - 'line' => $line, - ]); - } - - private static function extractSource(string $srcLines, int $line, int $srcContext, string $lang, ?string $file, array $frame): EnumStub - { - $srcLines = explode("\n", $srcLines); - $src = []; - - for ($i = $line - 1 - $srcContext; $i <= $line - 1 + $srcContext; ++$i) { - $src[] = (isset($srcLines[$i]) ? $srcLines[$i] : '')."\n"; - } - - if ($frame['function'] ?? false) { - $stub = new CutStub(new \stdClass()); - $stub->class = (isset($frame['class']) ? $frame['class'].$frame['type'] : '').$frame['function']; - $stub->type = Stub::TYPE_OBJECT; - $stub->attr['cut_hash'] = true; - $stub->attr['file'] = $frame['file']; - $stub->attr['line'] = $frame['line']; - - try { - $caller = isset($frame['class']) ? new \ReflectionMethod($frame['class'], $frame['function']) : new \ReflectionFunction($frame['function']); - $stub->class .= ReflectionCaster::getSignature(ReflectionCaster::castFunctionAbstract($caller, [], $stub, true, Caster::EXCLUDE_VERBOSE)); - - if ($f = $caller->getFileName()) { - $stub->attr['file'] = $f; - $stub->attr['line'] = $caller->getStartLine(); - } - } catch (\ReflectionException $e) { - // ignore fake class/function - } - - $srcLines = ["\0~separator=\0" => $stub]; - } else { - $stub = null; - $srcLines = []; - } - - $ltrim = 0; - do { - $pad = null; - for ($i = $srcContext << 1; $i >= 0; --$i) { - if (isset($src[$i][$ltrim]) && "\r" !== ($c = $src[$i][$ltrim]) && "\n" !== $c) { - if (null === $pad) { - $pad = $c; - } - if ((' ' !== $c && "\t" !== $c) || $pad !== $c) { - break; - } - } - } - ++$ltrim; - } while (0 > $i && null !== $pad); - - --$ltrim; - - foreach ($src as $i => $c) { - if ($ltrim) { - $c = isset($c[$ltrim]) && "\r" !== $c[$ltrim] ? substr($c, $ltrim) : ltrim($c, " \t"); - } - $c = substr($c, 0, -1); - if ($i !== $srcContext) { - $c = new ConstStub('default', $c); - } else { - $c = new ConstStub($c, $stub ? 'in '.$stub->class : ''); - if (null !== $file) { - $c->attr['file'] = $file; - $c->attr['line'] = $line; - } - } - $c->attr['lang'] = $lang; - $srcLines[sprintf("\0~separator=› &%d\0", $i + $line - $srcContext)] = $c; - } - - return new EnumStub($srcLines); - } -} diff --git a/vendor/symfony/var-dumper/Caster/FrameStub.php b/vendor/symfony/var-dumper/Caster/FrameStub.php deleted file mode 100644 index 8786755..0000000 --- a/vendor/symfony/var-dumper/Caster/FrameStub.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * Represents a single backtrace frame as returned by debug_backtrace() or Exception->getTrace(). - * - * @author Nicolas Grekas - */ -class FrameStub extends EnumStub -{ - public $keepArgs; - public $inTraceStub; - - public function __construct(array $frame, bool $keepArgs = true, bool $inTraceStub = false) - { - $this->value = $frame; - $this->keepArgs = $keepArgs; - $this->inTraceStub = $inTraceStub; - } -} diff --git a/vendor/symfony/var-dumper/Caster/GmpCaster.php b/vendor/symfony/var-dumper/Caster/GmpCaster.php deleted file mode 100644 index b018cc7..0000000 --- a/vendor/symfony/var-dumper/Caster/GmpCaster.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts GMP objects to array representation. - * - * @author Hamza Amrouche - * @author Nicolas Grekas - * - * @final - */ -class GmpCaster -{ - public static function castGmp(\GMP $gmp, array $a, Stub $stub, bool $isNested, int $filter): array - { - $a[Caster::PREFIX_VIRTUAL.'value'] = new ConstStub(gmp_strval($gmp), gmp_strval($gmp)); - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/ImagineCaster.php b/vendor/symfony/var-dumper/Caster/ImagineCaster.php deleted file mode 100644 index d1289da..0000000 --- a/vendor/symfony/var-dumper/Caster/ImagineCaster.php +++ /dev/null @@ -1,37 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Imagine\Image\ImageInterface; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Grégoire Pineau - */ -final class ImagineCaster -{ - public static function castImage(ImageInterface $c, array $a, Stub $stub, bool $isNested): array - { - $imgData = $c->get('png'); - if (\strlen($imgData) > 1 * 1000 * 1000) { - $a += [ - Caster::PREFIX_VIRTUAL.'image' => new ConstStub($c->getSize()), - ]; - } else { - $a += [ - Caster::PREFIX_VIRTUAL.'image' => new ImgStub($imgData, 'image/png', $c->getSize()), - ]; - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/ImgStub.php b/vendor/symfony/var-dumper/Caster/ImgStub.php deleted file mode 100644 index 05789fe..0000000 --- a/vendor/symfony/var-dumper/Caster/ImgStub.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * @author Grégoire Pineau - */ -class ImgStub extends ConstStub -{ - public function __construct(string $data, string $contentType, string $size) - { - $this->value = ''; - $this->attr['img-data'] = $data; - $this->attr['img-size'] = $size; - $this->attr['content-type'] = $contentType; - } -} diff --git a/vendor/symfony/var-dumper/Caster/IntlCaster.php b/vendor/symfony/var-dumper/Caster/IntlCaster.php deleted file mode 100644 index 23b9d5d..0000000 --- a/vendor/symfony/var-dumper/Caster/IntlCaster.php +++ /dev/null @@ -1,172 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Nicolas Grekas - * @author Jan Schädlich - * - * @final - */ -class IntlCaster -{ - public static function castMessageFormatter(\MessageFormatter $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), - Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), - ]; - - return self::castError($c, $a); - } - - public static function castNumberFormatter(\NumberFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), - Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), - ]; - - if ($filter & Caster::EXCLUDE_VERBOSE) { - $stub->cut += 3; - - return self::castError($c, $a); - } - - $a += [ - Caster::PREFIX_VIRTUAL.'attributes' => new EnumStub( - [ - 'PARSE_INT_ONLY' => $c->getAttribute(\NumberFormatter::PARSE_INT_ONLY), - 'GROUPING_USED' => $c->getAttribute(\NumberFormatter::GROUPING_USED), - 'DECIMAL_ALWAYS_SHOWN' => $c->getAttribute(\NumberFormatter::DECIMAL_ALWAYS_SHOWN), - 'MAX_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_INTEGER_DIGITS), - 'MIN_INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_INTEGER_DIGITS), - 'INTEGER_DIGITS' => $c->getAttribute(\NumberFormatter::INTEGER_DIGITS), - 'MAX_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_FRACTION_DIGITS), - 'MIN_FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_FRACTION_DIGITS), - 'FRACTION_DIGITS' => $c->getAttribute(\NumberFormatter::FRACTION_DIGITS), - 'MULTIPLIER' => $c->getAttribute(\NumberFormatter::MULTIPLIER), - 'GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::GROUPING_SIZE), - 'ROUNDING_MODE' => $c->getAttribute(\NumberFormatter::ROUNDING_MODE), - 'ROUNDING_INCREMENT' => $c->getAttribute(\NumberFormatter::ROUNDING_INCREMENT), - 'FORMAT_WIDTH' => $c->getAttribute(\NumberFormatter::FORMAT_WIDTH), - 'PADDING_POSITION' => $c->getAttribute(\NumberFormatter::PADDING_POSITION), - 'SECONDARY_GROUPING_SIZE' => $c->getAttribute(\NumberFormatter::SECONDARY_GROUPING_SIZE), - 'SIGNIFICANT_DIGITS_USED' => $c->getAttribute(\NumberFormatter::SIGNIFICANT_DIGITS_USED), - 'MIN_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MIN_SIGNIFICANT_DIGITS), - 'MAX_SIGNIFICANT_DIGITS' => $c->getAttribute(\NumberFormatter::MAX_SIGNIFICANT_DIGITS), - 'LENIENT_PARSE' => $c->getAttribute(\NumberFormatter::LENIENT_PARSE), - ] - ), - Caster::PREFIX_VIRTUAL.'text_attributes' => new EnumStub( - [ - 'POSITIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_PREFIX), - 'POSITIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::POSITIVE_SUFFIX), - 'NEGATIVE_PREFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_PREFIX), - 'NEGATIVE_SUFFIX' => $c->getTextAttribute(\NumberFormatter::NEGATIVE_SUFFIX), - 'PADDING_CHARACTER' => $c->getTextAttribute(\NumberFormatter::PADDING_CHARACTER), - 'CURRENCY_CODE' => $c->getTextAttribute(\NumberFormatter::CURRENCY_CODE), - 'DEFAULT_RULESET' => $c->getTextAttribute(\NumberFormatter::DEFAULT_RULESET), - 'PUBLIC_RULESETS' => $c->getTextAttribute(\NumberFormatter::PUBLIC_RULESETS), - ] - ), - Caster::PREFIX_VIRTUAL.'symbols' => new EnumStub( - [ - 'DECIMAL_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::DECIMAL_SEPARATOR_SYMBOL), - 'GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::GROUPING_SEPARATOR_SYMBOL), - 'PATTERN_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::PATTERN_SEPARATOR_SYMBOL), - 'PERCENT_SYMBOL' => $c->getSymbol(\NumberFormatter::PERCENT_SYMBOL), - 'ZERO_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::ZERO_DIGIT_SYMBOL), - 'DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::DIGIT_SYMBOL), - 'MINUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::MINUS_SIGN_SYMBOL), - 'PLUS_SIGN_SYMBOL' => $c->getSymbol(\NumberFormatter::PLUS_SIGN_SYMBOL), - 'CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::CURRENCY_SYMBOL), - 'INTL_CURRENCY_SYMBOL' => $c->getSymbol(\NumberFormatter::INTL_CURRENCY_SYMBOL), - 'MONETARY_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_SEPARATOR_SYMBOL), - 'EXPONENTIAL_SYMBOL' => $c->getSymbol(\NumberFormatter::EXPONENTIAL_SYMBOL), - 'PERMILL_SYMBOL' => $c->getSymbol(\NumberFormatter::PERMILL_SYMBOL), - 'PAD_ESCAPE_SYMBOL' => $c->getSymbol(\NumberFormatter::PAD_ESCAPE_SYMBOL), - 'INFINITY_SYMBOL' => $c->getSymbol(\NumberFormatter::INFINITY_SYMBOL), - 'NAN_SYMBOL' => $c->getSymbol(\NumberFormatter::NAN_SYMBOL), - 'SIGNIFICANT_DIGIT_SYMBOL' => $c->getSymbol(\NumberFormatter::SIGNIFICANT_DIGIT_SYMBOL), - 'MONETARY_GROUPING_SEPARATOR_SYMBOL' => $c->getSymbol(\NumberFormatter::MONETARY_GROUPING_SEPARATOR_SYMBOL), - ] - ), - ]; - - return self::castError($c, $a); - } - - public static function castIntlTimeZone(\IntlTimeZone $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'display_name' => $c->getDisplayName(), - Caster::PREFIX_VIRTUAL.'id' => $c->getID(), - Caster::PREFIX_VIRTUAL.'raw_offset' => $c->getRawOffset(), - ]; - - if ($c->useDaylightTime()) { - $a += [ - Caster::PREFIX_VIRTUAL.'dst_savings' => $c->getDSTSavings(), - ]; - } - - return self::castError($c, $a); - } - - public static function castIntlCalendar(\IntlCalendar $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - Caster::PREFIX_VIRTUAL.'type' => $c->getType(), - Caster::PREFIX_VIRTUAL.'first_day_of_week' => $c->getFirstDayOfWeek(), - Caster::PREFIX_VIRTUAL.'minimal_days_in_first_week' => $c->getMinimalDaysInFirstWeek(), - Caster::PREFIX_VIRTUAL.'repeated_wall_time_option' => $c->getRepeatedWallTimeOption(), - Caster::PREFIX_VIRTUAL.'skipped_wall_time_option' => $c->getSkippedWallTimeOption(), - Caster::PREFIX_VIRTUAL.'time' => $c->getTime(), - Caster::PREFIX_VIRTUAL.'in_daylight_time' => $c->inDaylightTime(), - Caster::PREFIX_VIRTUAL.'is_lenient' => $c->isLenient(), - Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), - ]; - - return self::castError($c, $a); - } - - public static function castIntlDateFormatter(\IntlDateFormatter $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $a += [ - Caster::PREFIX_VIRTUAL.'locale' => $c->getLocale(), - Caster::PREFIX_VIRTUAL.'pattern' => $c->getPattern(), - Caster::PREFIX_VIRTUAL.'calendar' => $c->getCalendar(), - Caster::PREFIX_VIRTUAL.'time_zone_id' => $c->getTimeZoneId(), - Caster::PREFIX_VIRTUAL.'time_type' => $c->getTimeType(), - Caster::PREFIX_VIRTUAL.'date_type' => $c->getDateType(), - Caster::PREFIX_VIRTUAL.'calendar_object' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getCalendarObject()) : $c->getCalendarObject(), - Caster::PREFIX_VIRTUAL.'time_zone' => ($filter & Caster::EXCLUDE_VERBOSE) ? new CutStub($c->getTimeZone()) : $c->getTimeZone(), - ]; - - return self::castError($c, $a); - } - - private static function castError(object $c, array $a): array - { - if ($errorCode = $c->getErrorCode()) { - $a += [ - Caster::PREFIX_VIRTUAL.'error_code' => $errorCode, - Caster::PREFIX_VIRTUAL.'error_message' => $c->getErrorMessage(), - ]; - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/LinkStub.php b/vendor/symfony/var-dumper/Caster/LinkStub.php deleted file mode 100644 index 6360716..0000000 --- a/vendor/symfony/var-dumper/Caster/LinkStub.php +++ /dev/null @@ -1,108 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -/** - * Represents a file or a URL. - * - * @author Nicolas Grekas - */ -class LinkStub extends ConstStub -{ - public $inVendor = false; - - private static $vendorRoots; - private static $composerRoots; - - public function __construct($label, int $line = 0, $href = null) - { - $this->value = $label; - - if (null === $href) { - $href = $label; - } - if (!\is_string($href)) { - return; - } - if (0 === strpos($href, 'file://')) { - if ($href === $label) { - $label = substr($label, 7); - } - $href = substr($href, 7); - } elseif (false !== strpos($href, '://')) { - $this->attr['href'] = $href; - - return; - } - if (!file_exists($href)) { - return; - } - if ($line) { - $this->attr['line'] = $line; - } - if ($label !== $this->attr['file'] = realpath($href) ?: $href) { - return; - } - if ($composerRoot = $this->getComposerRoot($href, $this->inVendor)) { - $this->attr['ellipsis'] = \strlen($href) - \strlen($composerRoot) + 1; - $this->attr['ellipsis-type'] = 'path'; - $this->attr['ellipsis-tail'] = 1 + ($this->inVendor ? 2 + \strlen(implode('', \array_slice(explode(\DIRECTORY_SEPARATOR, substr($href, 1 - $this->attr['ellipsis'])), 0, 2))) : 0); - } elseif (3 < \count($ellipsis = explode(\DIRECTORY_SEPARATOR, $href))) { - $this->attr['ellipsis'] = 2 + \strlen(implode('', \array_slice($ellipsis, -2))); - $this->attr['ellipsis-type'] = 'path'; - $this->attr['ellipsis-tail'] = 1; - } - } - - private function getComposerRoot(string $file, bool &$inVendor) - { - if (null === self::$vendorRoots) { - self::$vendorRoots = []; - - foreach (get_declared_classes() as $class) { - if ('C' === $class[0] && 0 === strpos($class, 'ComposerAutoloaderInit')) { - $r = new \ReflectionClass($class); - $v = \dirname($r->getFileName(), 2); - if (file_exists($v.'/composer/installed.json')) { - self::$vendorRoots[] = $v.\DIRECTORY_SEPARATOR; - } - } - } - } - $inVendor = false; - - if (isset(self::$composerRoots[$dir = \dirname($file)])) { - return self::$composerRoots[$dir]; - } - - foreach (self::$vendorRoots as $root) { - if ($inVendor = 0 === strpos($file, $root)) { - return $root; - } - } - - $parent = $dir; - while (!@file_exists($parent.'/composer.json')) { - if (!@file_exists($parent)) { - // open_basedir restriction in effect - break; - } - if ($parent === \dirname($parent)) { - return self::$composerRoots[$dir] = false; - } - - $parent = \dirname($parent); - } - - return self::$composerRoots[$dir] = $parent.\DIRECTORY_SEPARATOR; - } -} diff --git a/vendor/symfony/var-dumper/Caster/MemcachedCaster.php b/vendor/symfony/var-dumper/Caster/MemcachedCaster.php deleted file mode 100644 index 111b060..0000000 --- a/vendor/symfony/var-dumper/Caster/MemcachedCaster.php +++ /dev/null @@ -1,81 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Jan Schädlich - * - * @final - */ -class MemcachedCaster -{ - private static $optionConstants; - private static $defaultOptions; - - public static function castMemcached(\Memcached $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'servers' => $c->getServerList(), - Caster::PREFIX_VIRTUAL.'options' => new EnumStub( - self::getNonDefaultOptions($c) - ), - ]; - - return $a; - } - - private static function getNonDefaultOptions(\Memcached $c): array - { - self::$defaultOptions = self::$defaultOptions ?? self::discoverDefaultOptions(); - self::$optionConstants = self::$optionConstants ?? self::getOptionConstants(); - - $nonDefaultOptions = []; - foreach (self::$optionConstants as $constantKey => $value) { - if (self::$defaultOptions[$constantKey] !== $option = $c->getOption($value)) { - $nonDefaultOptions[$constantKey] = $option; - } - } - - return $nonDefaultOptions; - } - - private static function discoverDefaultOptions(): array - { - $defaultMemcached = new \Memcached(); - $defaultMemcached->addServer('127.0.0.1', 11211); - - $defaultOptions = []; - self::$optionConstants = self::$optionConstants ?? self::getOptionConstants(); - - foreach (self::$optionConstants as $constantKey => $value) { - $defaultOptions[$constantKey] = $defaultMemcached->getOption($value); - } - - return $defaultOptions; - } - - private static function getOptionConstants(): array - { - $reflectedMemcached = new \ReflectionClass(\Memcached::class); - - $optionConstants = []; - foreach ($reflectedMemcached->getConstants() as $constantKey => $value) { - if (0 === strpos($constantKey, 'OPT_')) { - $optionConstants[$constantKey] = $value; - } - } - - return $optionConstants; - } -} diff --git a/vendor/symfony/var-dumper/Caster/PdoCaster.php b/vendor/symfony/var-dumper/Caster/PdoCaster.php deleted file mode 100644 index 4ba302b..0000000 --- a/vendor/symfony/var-dumper/Caster/PdoCaster.php +++ /dev/null @@ -1,122 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts PDO related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class PdoCaster -{ - private static $pdoAttributes = [ - 'CASE' => [ - \PDO::CASE_LOWER => 'LOWER', - \PDO::CASE_NATURAL => 'NATURAL', - \PDO::CASE_UPPER => 'UPPER', - ], - 'ERRMODE' => [ - \PDO::ERRMODE_SILENT => 'SILENT', - \PDO::ERRMODE_WARNING => 'WARNING', - \PDO::ERRMODE_EXCEPTION => 'EXCEPTION', - ], - 'TIMEOUT', - 'PREFETCH', - 'AUTOCOMMIT', - 'PERSISTENT', - 'DRIVER_NAME', - 'SERVER_INFO', - 'ORACLE_NULLS' => [ - \PDO::NULL_NATURAL => 'NATURAL', - \PDO::NULL_EMPTY_STRING => 'EMPTY_STRING', - \PDO::NULL_TO_STRING => 'TO_STRING', - ], - 'CLIENT_VERSION', - 'SERVER_VERSION', - 'STATEMENT_CLASS', - 'EMULATE_PREPARES', - 'CONNECTION_STATUS', - 'STRINGIFY_FETCHES', - 'DEFAULT_FETCH_MODE' => [ - \PDO::FETCH_ASSOC => 'ASSOC', - \PDO::FETCH_BOTH => 'BOTH', - \PDO::FETCH_LAZY => 'LAZY', - \PDO::FETCH_NUM => 'NUM', - \PDO::FETCH_OBJ => 'OBJ', - ], - ]; - - public static function castPdo(\PDO $c, array $a, Stub $stub, bool $isNested) - { - $attr = []; - $errmode = $c->getAttribute(\PDO::ATTR_ERRMODE); - $c->setAttribute(\PDO::ATTR_ERRMODE, \PDO::ERRMODE_EXCEPTION); - - foreach (self::$pdoAttributes as $k => $v) { - if (!isset($k[0])) { - $k = $v; - $v = []; - } - - try { - $attr[$k] = 'ERRMODE' === $k ? $errmode : $c->getAttribute(\constant('PDO::ATTR_'.$k)); - if ($v && isset($v[$attr[$k]])) { - $attr[$k] = new ConstStub($v[$attr[$k]], $attr[$k]); - } - } catch (\Exception $e) { - } - } - if (isset($attr[$k = 'STATEMENT_CLASS'][1])) { - if ($attr[$k][1]) { - $attr[$k][1] = new ArgsStub($attr[$k][1], '__construct', $attr[$k][0]); - } - $attr[$k][0] = new ClassStub($attr[$k][0]); - } - - $prefix = Caster::PREFIX_VIRTUAL; - $a += [ - $prefix.'inTransaction' => method_exists($c, 'inTransaction'), - $prefix.'errorInfo' => $c->errorInfo(), - $prefix.'attributes' => new EnumStub($attr), - ]; - - if ($a[$prefix.'inTransaction']) { - $a[$prefix.'inTransaction'] = $c->inTransaction(); - } else { - unset($a[$prefix.'inTransaction']); - } - - if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { - unset($a[$prefix.'errorInfo']); - } - - $c->setAttribute(\PDO::ATTR_ERRMODE, $errmode); - - return $a; - } - - public static function castPdoStatement(\PDOStatement $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - $a[$prefix.'errorInfo'] = $c->errorInfo(); - - if (!isset($a[$prefix.'errorInfo'][1], $a[$prefix.'errorInfo'][2])) { - unset($a[$prefix.'errorInfo']); - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/PgSqlCaster.php b/vendor/symfony/var-dumper/Caster/PgSqlCaster.php deleted file mode 100644 index f9f3c6f..0000000 --- a/vendor/symfony/var-dumper/Caster/PgSqlCaster.php +++ /dev/null @@ -1,156 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts pqsql resources to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class PgSqlCaster -{ - private static $paramCodes = [ - 'server_encoding', - 'client_encoding', - 'is_superuser', - 'session_authorization', - 'DateStyle', - 'TimeZone', - 'IntervalStyle', - 'integer_datetimes', - 'application_name', - 'standard_conforming_strings', - ]; - - private static $transactionStatus = [ - PGSQL_TRANSACTION_IDLE => 'PGSQL_TRANSACTION_IDLE', - PGSQL_TRANSACTION_ACTIVE => 'PGSQL_TRANSACTION_ACTIVE', - PGSQL_TRANSACTION_INTRANS => 'PGSQL_TRANSACTION_INTRANS', - PGSQL_TRANSACTION_INERROR => 'PGSQL_TRANSACTION_INERROR', - PGSQL_TRANSACTION_UNKNOWN => 'PGSQL_TRANSACTION_UNKNOWN', - ]; - - private static $resultStatus = [ - PGSQL_EMPTY_QUERY => 'PGSQL_EMPTY_QUERY', - PGSQL_COMMAND_OK => 'PGSQL_COMMAND_OK', - PGSQL_TUPLES_OK => 'PGSQL_TUPLES_OK', - PGSQL_COPY_OUT => 'PGSQL_COPY_OUT', - PGSQL_COPY_IN => 'PGSQL_COPY_IN', - PGSQL_BAD_RESPONSE => 'PGSQL_BAD_RESPONSE', - PGSQL_NONFATAL_ERROR => 'PGSQL_NONFATAL_ERROR', - PGSQL_FATAL_ERROR => 'PGSQL_FATAL_ERROR', - ]; - - private static $diagCodes = [ - 'severity' => PGSQL_DIAG_SEVERITY, - 'sqlstate' => PGSQL_DIAG_SQLSTATE, - 'message' => PGSQL_DIAG_MESSAGE_PRIMARY, - 'detail' => PGSQL_DIAG_MESSAGE_DETAIL, - 'hint' => PGSQL_DIAG_MESSAGE_HINT, - 'statement position' => PGSQL_DIAG_STATEMENT_POSITION, - 'internal position' => PGSQL_DIAG_INTERNAL_POSITION, - 'internal query' => PGSQL_DIAG_INTERNAL_QUERY, - 'context' => PGSQL_DIAG_CONTEXT, - 'file' => PGSQL_DIAG_SOURCE_FILE, - 'line' => PGSQL_DIAG_SOURCE_LINE, - 'function' => PGSQL_DIAG_SOURCE_FUNCTION, - ]; - - public static function castLargeObject($lo, array $a, Stub $stub, bool $isNested) - { - $a['seek position'] = pg_lo_tell($lo); - - return $a; - } - - public static function castLink($link, array $a, Stub $stub, bool $isNested) - { - $a['status'] = pg_connection_status($link); - $a['status'] = new ConstStub(PGSQL_CONNECTION_OK === $a['status'] ? 'PGSQL_CONNECTION_OK' : 'PGSQL_CONNECTION_BAD', $a['status']); - $a['busy'] = pg_connection_busy($link); - - $a['transaction'] = pg_transaction_status($link); - if (isset(self::$transactionStatus[$a['transaction']])) { - $a['transaction'] = new ConstStub(self::$transactionStatus[$a['transaction']], $a['transaction']); - } - - $a['pid'] = pg_get_pid($link); - $a['last error'] = pg_last_error($link); - $a['last notice'] = pg_last_notice($link); - $a['host'] = pg_host($link); - $a['port'] = pg_port($link); - $a['dbname'] = pg_dbname($link); - $a['options'] = pg_options($link); - $a['version'] = pg_version($link); - - foreach (self::$paramCodes as $v) { - if (false !== $s = pg_parameter_status($link, $v)) { - $a['param'][$v] = $s; - } - } - - $a['param']['client_encoding'] = pg_client_encoding($link); - $a['param'] = new EnumStub($a['param']); - - return $a; - } - - public static function castResult($result, array $a, Stub $stub, bool $isNested) - { - $a['num rows'] = pg_num_rows($result); - $a['status'] = pg_result_status($result); - if (isset(self::$resultStatus[$a['status']])) { - $a['status'] = new ConstStub(self::$resultStatus[$a['status']], $a['status']); - } - $a['command-completion tag'] = pg_result_status($result, PGSQL_STATUS_STRING); - - if (-1 === $a['num rows']) { - foreach (self::$diagCodes as $k => $v) { - $a['error'][$k] = pg_result_error_field($result, $v); - } - } - - $a['affected rows'] = pg_affected_rows($result); - $a['last OID'] = pg_last_oid($result); - - $fields = pg_num_fields($result); - - for ($i = 0; $i < $fields; ++$i) { - $field = [ - 'name' => pg_field_name($result, $i), - 'table' => sprintf('%s (OID: %s)', pg_field_table($result, $i), pg_field_table($result, $i, true)), - 'type' => sprintf('%s (OID: %s)', pg_field_type($result, $i), pg_field_type_oid($result, $i)), - 'nullable' => (bool) pg_field_is_null($result, $i), - 'storage' => pg_field_size($result, $i).' bytes', - 'display' => pg_field_prtlen($result, $i).' chars', - ]; - if (' (OID: )' === $field['table']) { - $field['table'] = null; - } - if ('-1 bytes' === $field['storage']) { - $field['storage'] = 'variable size'; - } elseif ('1 bytes' === $field['storage']) { - $field['storage'] = '1 byte'; - } - if ('1 chars' === $field['display']) { - $field['display'] = '1 char'; - } - $a['fields'][] = new EnumStub($field); - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php b/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php deleted file mode 100644 index e712019..0000000 --- a/vendor/symfony/var-dumper/Caster/ProxyManagerCaster.php +++ /dev/null @@ -1,33 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use ProxyManager\Proxy\ProxyInterface; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Nicolas Grekas - * - * @final - */ -class ProxyManagerCaster -{ - public static function castProxy(ProxyInterface $c, array $a, Stub $stub, bool $isNested) - { - if ($parent = get_parent_class($c)) { - $stub->class .= ' - '.$parent; - } - $stub->class .= '@proxy'; - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/RedisCaster.php b/vendor/symfony/var-dumper/Caster/RedisCaster.php deleted file mode 100644 index a7ca8ec..0000000 --- a/vendor/symfony/var-dumper/Caster/RedisCaster.php +++ /dev/null @@ -1,152 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Redis class from ext-redis to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class RedisCaster -{ - private static $serializer = [ - \Redis::SERIALIZER_NONE => 'NONE', - \Redis::SERIALIZER_PHP => 'PHP', - 2 => 'IGBINARY', // Optional Redis::SERIALIZER_IGBINARY - ]; - - private static $mode = [ - \Redis::ATOMIC => 'ATOMIC', - \Redis::MULTI => 'MULTI', - \Redis::PIPELINE => 'PIPELINE', - ]; - - private static $compression = [ - 0 => 'NONE', // Redis::COMPRESSION_NONE - 1 => 'LZF', // Redis::COMPRESSION_LZF - ]; - - private static $failover = [ - \RedisCluster::FAILOVER_NONE => 'NONE', - \RedisCluster::FAILOVER_ERROR => 'ERROR', - \RedisCluster::FAILOVER_DISTRIBUTE => 'DISTRIBUTE', - \RedisCluster::FAILOVER_DISTRIBUTE_SLAVES => 'DISTRIBUTE_SLAVES', - ]; - - public static function castRedis(\Redis $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if (!$connected = $c->isConnected()) { - return $a + [ - $prefix.'isConnected' => $connected, - ]; - } - - $mode = $c->getMode(); - - return $a + [ - $prefix.'isConnected' => $connected, - $prefix.'host' => $c->getHost(), - $prefix.'port' => $c->getPort(), - $prefix.'auth' => $c->getAuth(), - $prefix.'mode' => isset(self::$mode[$mode]) ? new ConstStub(self::$mode[$mode], $mode) : $mode, - $prefix.'dbNum' => $c->getDbNum(), - $prefix.'timeout' => $c->getTimeout(), - $prefix.'lastError' => $c->getLastError(), - $prefix.'persistentId' => $c->getPersistentID(), - $prefix.'options' => self::getRedisOptions($c), - ]; - } - - public static function castRedisArray(\RedisArray $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - return $a + [ - $prefix.'hosts' => $c->_hosts(), - $prefix.'function' => ClassStub::wrapCallable($c->_function()), - $prefix.'lastError' => $c->getLastError(), - $prefix.'options' => self::getRedisOptions($c), - ]; - } - - public static function castRedisCluster(\RedisCluster $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - $failover = $c->getOption(\RedisCluster::OPT_SLAVE_FAILOVER); - - $a += [ - $prefix.'_masters' => $c->_masters(), - $prefix.'_redir' => $c->_redir(), - $prefix.'mode' => new ConstStub($c->getMode() ? 'MULTI' : 'ATOMIC', $c->getMode()), - $prefix.'lastError' => $c->getLastError(), - $prefix.'options' => self::getRedisOptions($c, [ - 'SLAVE_FAILOVER' => isset(self::$failover[$failover]) ? new ConstStub(self::$failover[$failover], $failover) : $failover, - ]), - ]; - - return $a; - } - - /** - * @param \Redis|\RedisArray|\RedisCluster $redis - */ - private static function getRedisOptions($redis, array $options = []): EnumStub - { - $serializer = $redis->getOption(\Redis::OPT_SERIALIZER); - if (\is_array($serializer)) { - foreach ($serializer as &$v) { - if (isset(self::$serializer[$v])) { - $v = new ConstStub(self::$serializer[$v], $v); - } - } - } elseif (isset(self::$serializer[$serializer])) { - $serializer = new ConstStub(self::$serializer[$serializer], $serializer); - } - - $compression = \defined('Redis::OPT_COMPRESSION') ? $redis->getOption(\Redis::OPT_COMPRESSION) : 0; - if (\is_array($compression)) { - foreach ($compression as &$v) { - if (isset(self::$compression[$v])) { - $v = new ConstStub(self::$compression[$v], $v); - } - } - } elseif (isset(self::$compression[$compression])) { - $compression = new ConstStub(self::$compression[$compression], $compression); - } - - $retry = \defined('Redis::OPT_SCAN') ? $redis->getOption(\Redis::OPT_SCAN) : 0; - if (\is_array($retry)) { - foreach ($retry as &$v) { - $v = new ConstStub($v ? 'RETRY' : 'NORETRY', $v); - } - } else { - $retry = new ConstStub($retry ? 'RETRY' : 'NORETRY', $retry); - } - - $options += [ - 'TCP_KEEPALIVE' => \defined('Redis::OPT_TCP_KEEPALIVE') ? $redis->getOption(\Redis::OPT_TCP_KEEPALIVE) : 0, - 'READ_TIMEOUT' => $redis->getOption(\Redis::OPT_READ_TIMEOUT), - 'COMPRESSION' => $compression, - 'SERIALIZER' => $serializer, - 'PREFIX' => $redis->getOption(\Redis::OPT_PREFIX), - 'SCAN' => $retry, - ]; - - return new EnumStub($options); - } -} diff --git a/vendor/symfony/var-dumper/Caster/ReflectionCaster.php b/vendor/symfony/var-dumper/Caster/ReflectionCaster.php deleted file mode 100644 index 94c2f3e..0000000 --- a/vendor/symfony/var-dumper/Caster/ReflectionCaster.php +++ /dev/null @@ -1,384 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts Reflector related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class ReflectionCaster -{ - const UNSET_CLOSURE_FILE_INFO = ['Closure' => __CLASS__.'::unsetClosureFileInfo']; - - private static $extraMap = [ - 'docComment' => 'getDocComment', - 'extension' => 'getExtensionName', - 'isDisabled' => 'isDisabled', - 'isDeprecated' => 'isDeprecated', - 'isInternal' => 'isInternal', - 'isUserDefined' => 'isUserDefined', - 'isGenerator' => 'isGenerator', - 'isVariadic' => 'isVariadic', - ]; - - public static function castClosure(\Closure $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - $c = new \ReflectionFunction($c); - - $a = static::castFunctionAbstract($c, $a, $stub, $isNested, $filter); - - if (false === strpos($c->name, '{closure}')) { - $stub->class = isset($a[$prefix.'class']) ? $a[$prefix.'class']->value.'::'.$c->name : $c->name; - unset($a[$prefix.'class']); - } - unset($a[$prefix.'extra']); - - $stub->class .= self::getSignature($a); - - if ($f = $c->getFileName()) { - $stub->attr['file'] = $f; - $stub->attr['line'] = $c->getStartLine(); - } - - unset($a[$prefix.'parameters']); - - if ($filter & Caster::EXCLUDE_VERBOSE) { - $stub->cut += ($c->getFileName() ? 2 : 0) + \count($a); - - return []; - } - - if ($f) { - $a[$prefix.'file'] = new LinkStub($f, $c->getStartLine()); - $a[$prefix.'line'] = $c->getStartLine().' to '.$c->getEndLine(); - } - - return $a; - } - - public static function unsetClosureFileInfo(\Closure $c, array $a) - { - unset($a[Caster::PREFIX_VIRTUAL.'file'], $a[Caster::PREFIX_VIRTUAL.'line']); - - return $a; - } - - public static function castGenerator(\Generator $c, array $a, Stub $stub, bool $isNested) - { - // Cannot create ReflectionGenerator based on a terminated Generator - try { - $reflectionGenerator = new \ReflectionGenerator($c); - } catch (\Exception $e) { - $a[Caster::PREFIX_VIRTUAL.'closed'] = true; - - return $a; - } - - return self::castReflectionGenerator($reflectionGenerator, $a, $stub, $isNested); - } - - public static function castType(\ReflectionType $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - $a += [ - $prefix.'name' => $c->getName(), - $prefix.'allowsNull' => $c->allowsNull(), - $prefix.'isBuiltin' => $c->isBuiltin(), - ]; - - return $a; - } - - public static function castReflectionGenerator(\ReflectionGenerator $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if ($c->getThis()) { - $a[$prefix.'this'] = new CutStub($c->getThis()); - } - $function = $c->getFunction(); - $frame = [ - 'class' => isset($function->class) ? $function->class : null, - 'type' => isset($function->class) ? ($function->isStatic() ? '::' : '->') : null, - 'function' => $function->name, - 'file' => $c->getExecutingFile(), - 'line' => $c->getExecutingLine(), - ]; - if ($trace = $c->getTrace(DEBUG_BACKTRACE_IGNORE_ARGS)) { - $function = new \ReflectionGenerator($c->getExecutingGenerator()); - array_unshift($trace, [ - 'function' => 'yield', - 'file' => $function->getExecutingFile(), - 'line' => $function->getExecutingLine() - 1, - ]); - $trace[] = $frame; - $a[$prefix.'trace'] = new TraceStub($trace, false, 0, -1, -1); - } else { - $function = new FrameStub($frame, false, true); - $function = ExceptionCaster::castFrameStub($function, [], $function, true); - $a[$prefix.'executing'] = $function[$prefix.'src']; - } - - $a[Caster::PREFIX_VIRTUAL.'closed'] = false; - - return $a; - } - - public static function castClass(\ReflectionClass $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - - if ($n = \Reflection::getModifierNames($c->getModifiers())) { - $a[$prefix.'modifiers'] = implode(' ', $n); - } - - self::addMap($a, $c, [ - 'extends' => 'getParentClass', - 'implements' => 'getInterfaceNames', - 'constants' => 'getConstants', - ]); - - foreach ($c->getProperties() as $n) { - $a[$prefix.'properties'][$n->name] = $n; - } - - foreach ($c->getMethods() as $n) { - $a[$prefix.'methods'][$n->name] = $n; - } - - if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { - self::addExtra($a, $c); - } - - return $a; - } - - public static function castFunctionAbstract(\ReflectionFunctionAbstract $c, array $a, Stub $stub, bool $isNested, int $filter = 0) - { - $prefix = Caster::PREFIX_VIRTUAL; - - self::addMap($a, $c, [ - 'returnsReference' => 'returnsReference', - 'returnType' => 'getReturnType', - 'class' => 'getClosureScopeClass', - 'this' => 'getClosureThis', - ]); - - if (isset($a[$prefix.'returnType'])) { - $v = $a[$prefix.'returnType']; - $v = $v->getName(); - $a[$prefix.'returnType'] = new ClassStub($a[$prefix.'returnType']->allowsNull() ? '?'.$v : $v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); - } - if (isset($a[$prefix.'class'])) { - $a[$prefix.'class'] = new ClassStub($a[$prefix.'class']); - } - if (isset($a[$prefix.'this'])) { - $a[$prefix.'this'] = new CutStub($a[$prefix.'this']); - } - - foreach ($c->getParameters() as $v) { - $k = '$'.$v->name; - if ($v->isVariadic()) { - $k = '...'.$k; - } - if ($v->isPassedByReference()) { - $k = '&'.$k; - } - $a[$prefix.'parameters'][$k] = $v; - } - if (isset($a[$prefix.'parameters'])) { - $a[$prefix.'parameters'] = new EnumStub($a[$prefix.'parameters']); - } - - if (!($filter & Caster::EXCLUDE_VERBOSE) && $v = $c->getStaticVariables()) { - foreach ($v as $k => &$v) { - if (\is_object($v)) { - $a[$prefix.'use']['$'.$k] = new CutStub($v); - } else { - $a[$prefix.'use']['$'.$k] = &$v; - } - } - unset($v); - $a[$prefix.'use'] = new EnumStub($a[$prefix.'use']); - } - - if (!($filter & Caster::EXCLUDE_VERBOSE) && !$isNested) { - self::addExtra($a, $c); - } - - return $a; - } - - public static function castMethod(\ReflectionMethod $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); - - return $a; - } - - public static function castParameter(\ReflectionParameter $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - - self::addMap($a, $c, [ - 'position' => 'getPosition', - 'isVariadic' => 'isVariadic', - 'byReference' => 'isPassedByReference', - 'allowsNull' => 'allowsNull', - ]); - - if ($v = $c->getType()) { - $a[$prefix.'typeHint'] = $v->getName(); - } - - if (isset($a[$prefix.'typeHint'])) { - $v = $a[$prefix.'typeHint']; - $a[$prefix.'typeHint'] = new ClassStub($v, [class_exists($v, false) || interface_exists($v, false) || trait_exists($v, false) ? $v : '', '']); - } else { - unset($a[$prefix.'allowsNull']); - } - - try { - $a[$prefix.'default'] = $v = $c->getDefaultValue(); - if ($c->isDefaultValueConstant()) { - $a[$prefix.'default'] = new ConstStub($c->getDefaultValueConstantName(), $v); - } - if (null === $v) { - unset($a[$prefix.'allowsNull']); - } - } catch (\ReflectionException $e) { - } - - return $a; - } - - public static function castProperty(\ReflectionProperty $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'modifiers'] = implode(' ', \Reflection::getModifierNames($c->getModifiers())); - self::addExtra($a, $c); - - return $a; - } - - public static function castReference(\ReflectionReference $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'id'] = $c->getId(); - - return $a; - } - - public static function castExtension(\ReflectionExtension $c, array $a, Stub $stub, bool $isNested) - { - self::addMap($a, $c, [ - 'version' => 'getVersion', - 'dependencies' => 'getDependencies', - 'iniEntries' => 'getIniEntries', - 'isPersistent' => 'isPersistent', - 'isTemporary' => 'isTemporary', - 'constants' => 'getConstants', - 'functions' => 'getFunctions', - 'classes' => 'getClasses', - ]); - - return $a; - } - - public static function castZendExtension(\ReflectionZendExtension $c, array $a, Stub $stub, bool $isNested) - { - self::addMap($a, $c, [ - 'version' => 'getVersion', - 'author' => 'getAuthor', - 'copyright' => 'getCopyright', - 'url' => 'getURL', - ]); - - return $a; - } - - public static function getSignature(array $a) - { - $prefix = Caster::PREFIX_VIRTUAL; - $signature = ''; - - if (isset($a[$prefix.'parameters'])) { - foreach ($a[$prefix.'parameters']->value as $k => $param) { - $signature .= ', '; - if ($type = $param->getType()) { - if (!$param->isOptional() && $param->allowsNull()) { - $signature .= '?'; - } - $signature .= substr(strrchr('\\'.$type->getName(), '\\'), 1).' '; - } - $signature .= $k; - - if (!$param->isDefaultValueAvailable()) { - continue; - } - $v = $param->getDefaultValue(); - $signature .= ' = '; - - if ($param->isDefaultValueConstant()) { - $signature .= substr(strrchr('\\'.$param->getDefaultValueConstantName(), '\\'), 1); - } elseif (null === $v) { - $signature .= 'null'; - } elseif (\is_array($v)) { - $signature .= $v ? '[…'.\count($v).']' : '[]'; - } elseif (\is_string($v)) { - $signature .= 10 > \strlen($v) && false === strpos($v, '\\') ? "'{$v}'" : "'…".\strlen($v)."'"; - } elseif (\is_bool($v)) { - $signature .= $v ? 'true' : 'false'; - } else { - $signature .= $v; - } - } - } - $signature = (empty($a[$prefix.'returnsReference']) ? '' : '&').'('.substr($signature, 2).')'; - - if (isset($a[$prefix.'returnType'])) { - $signature .= ': '.substr(strrchr('\\'.$a[$prefix.'returnType'], '\\'), 1); - } - - return $signature; - } - - private static function addExtra(array &$a, \Reflector $c) - { - $x = isset($a[Caster::PREFIX_VIRTUAL.'extra']) ? $a[Caster::PREFIX_VIRTUAL.'extra']->value : []; - - if (method_exists($c, 'getFileName') && $m = $c->getFileName()) { - $x['file'] = new LinkStub($m, $c->getStartLine()); - $x['line'] = $c->getStartLine().' to '.$c->getEndLine(); - } - - self::addMap($x, $c, self::$extraMap, ''); - - if ($x) { - $a[Caster::PREFIX_VIRTUAL.'extra'] = new EnumStub($x); - } - } - - private static function addMap(array &$a, \Reflector $c, array $map, string $prefix = Caster::PREFIX_VIRTUAL) - { - foreach ($map as $k => $m) { - if (method_exists($c, $m) && false !== ($m = $c->$m()) && null !== $m) { - $a[$prefix.$k] = $m instanceof \Reflector ? $m->name : $m; - } - } - } -} diff --git a/vendor/symfony/var-dumper/Caster/ResourceCaster.php b/vendor/symfony/var-dumper/Caster/ResourceCaster.php deleted file mode 100644 index 94ff5a9..0000000 --- a/vendor/symfony/var-dumper/Caster/ResourceCaster.php +++ /dev/null @@ -1,100 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts common resource types to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class ResourceCaster -{ - public static function castCurl($h, array $a, Stub $stub, bool $isNested) - { - return curl_getinfo($h); - } - - public static function castDba($dba, array $a, Stub $stub, bool $isNested) - { - $list = dba_list(); - $a['file'] = $list[(int) $dba]; - - return $a; - } - - public static function castProcess($process, array $a, Stub $stub, bool $isNested) - { - return proc_get_status($process); - } - - public static function castStream($stream, array $a, Stub $stub, bool $isNested) - { - $a = stream_get_meta_data($stream) + static::castStreamContext($stream, $a, $stub, $isNested); - if (isset($a['uri'])) { - $a['uri'] = new LinkStub($a['uri']); - } - - return $a; - } - - public static function castStreamContext($stream, array $a, Stub $stub, bool $isNested) - { - return @stream_context_get_params($stream) ?: $a; - } - - public static function castGd($gd, array $a, Stub $stub, $isNested) - { - $a['size'] = imagesx($gd).'x'.imagesy($gd); - $a['trueColor'] = imageistruecolor($gd); - - return $a; - } - - public static function castMysqlLink($h, array $a, Stub $stub, bool $isNested) - { - $a['host'] = mysql_get_host_info($h); - $a['protocol'] = mysql_get_proto_info($h); - $a['server'] = mysql_get_server_info($h); - - return $a; - } - - public static function castOpensslX509($h, array $a, Stub $stub, bool $isNested) - { - $stub->cut = -1; - $info = openssl_x509_parse($h, false); - - $pin = openssl_pkey_get_public($h); - $pin = openssl_pkey_get_details($pin)['key']; - $pin = \array_slice(explode("\n", $pin), 1, -2); - $pin = base64_decode(implode('', $pin)); - $pin = base64_encode(hash('sha256', $pin, true)); - - $a += [ - 'subject' => new EnumStub(array_intersect_key($info['subject'], ['organizationName' => true, 'commonName' => true])), - 'issuer' => new EnumStub(array_intersect_key($info['issuer'], ['organizationName' => true, 'commonName' => true])), - 'expiry' => new ConstStub(date(\DateTime::ISO8601, $info['validTo_time_t']), $info['validTo_time_t']), - 'fingerprint' => new EnumStub([ - 'md5' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'md5')), 2, ':', true)), - 'sha1' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha1')), 2, ':', true)), - 'sha256' => new ConstStub(wordwrap(strtoupper(openssl_x509_fingerprint($h, 'sha256')), 2, ':', true)), - 'pin-sha256' => new ConstStub($pin), - ]), - ]; - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/SplCaster.php b/vendor/symfony/var-dumper/Caster/SplCaster.php deleted file mode 100644 index e6c5b17..0000000 --- a/vendor/symfony/var-dumper/Caster/SplCaster.php +++ /dev/null @@ -1,228 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts SPL related classes to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class SplCaster -{ - private static $splFileObjectFlags = [ - \SplFileObject::DROP_NEW_LINE => 'DROP_NEW_LINE', - \SplFileObject::READ_AHEAD => 'READ_AHEAD', - \SplFileObject::SKIP_EMPTY => 'SKIP_EMPTY', - \SplFileObject::READ_CSV => 'READ_CSV', - ]; - - public static function castArrayObject(\ArrayObject $c, array $a, Stub $stub, bool $isNested) - { - return self::castSplArray($c, $a, $stub, $isNested); - } - - public static function castArrayIterator(\ArrayIterator $c, array $a, Stub $stub, bool $isNested) - { - return self::castSplArray($c, $a, $stub, $isNested); - } - - public static function castHeap(\Iterator $c, array $a, Stub $stub, $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'heap' => iterator_to_array(clone $c), - ]; - - return $a; - } - - public static function castDoublyLinkedList(\SplDoublyLinkedList $c, array $a, Stub $stub, bool $isNested) - { - $prefix = Caster::PREFIX_VIRTUAL; - $mode = $c->getIteratorMode(); - $c->setIteratorMode(\SplDoublyLinkedList::IT_MODE_KEEP | $mode & ~\SplDoublyLinkedList::IT_MODE_DELETE); - - $a += [ - $prefix.'mode' => new ConstStub((($mode & \SplDoublyLinkedList::IT_MODE_LIFO) ? 'IT_MODE_LIFO' : 'IT_MODE_FIFO').' | '.(($mode & \SplDoublyLinkedList::IT_MODE_DELETE) ? 'IT_MODE_DELETE' : 'IT_MODE_KEEP'), $mode), - $prefix.'dllist' => iterator_to_array($c), - ]; - $c->setIteratorMode($mode); - - return $a; - } - - public static function castFileInfo(\SplFileInfo $c, array $a, Stub $stub, bool $isNested) - { - static $map = [ - 'path' => 'getPath', - 'filename' => 'getFilename', - 'basename' => 'getBasename', - 'pathname' => 'getPathname', - 'extension' => 'getExtension', - 'realPath' => 'getRealPath', - 'aTime' => 'getATime', - 'mTime' => 'getMTime', - 'cTime' => 'getCTime', - 'inode' => 'getInode', - 'size' => 'getSize', - 'perms' => 'getPerms', - 'owner' => 'getOwner', - 'group' => 'getGroup', - 'type' => 'getType', - 'writable' => 'isWritable', - 'readable' => 'isReadable', - 'executable' => 'isExecutable', - 'file' => 'isFile', - 'dir' => 'isDir', - 'link' => 'isLink', - 'linkTarget' => 'getLinkTarget', - ]; - - $prefix = Caster::PREFIX_VIRTUAL; - - if (false === $c->getPathname()) { - $a[$prefix.'⚠'] = 'The parent constructor was not called: the object is in an invalid state'; - - return $a; - } - - foreach ($map as $key => $accessor) { - try { - $a[$prefix.$key] = $c->$accessor(); - } catch (\Exception $e) { - } - } - - if (isset($a[$prefix.'realPath'])) { - $a[$prefix.'realPath'] = new LinkStub($a[$prefix.'realPath']); - } - - if (isset($a[$prefix.'perms'])) { - $a[$prefix.'perms'] = new ConstStub(sprintf('0%o', $a[$prefix.'perms']), $a[$prefix.'perms']); - } - - static $mapDate = ['aTime', 'mTime', 'cTime']; - foreach ($mapDate as $key) { - if (isset($a[$prefix.$key])) { - $a[$prefix.$key] = new ConstStub(date('Y-m-d H:i:s', $a[$prefix.$key]), $a[$prefix.$key]); - } - } - - return $a; - } - - public static function castFileObject(\SplFileObject $c, array $a, Stub $stub, bool $isNested) - { - static $map = [ - 'csvControl' => 'getCsvControl', - 'flags' => 'getFlags', - 'maxLineLen' => 'getMaxLineLen', - 'fstat' => 'fstat', - 'eof' => 'eof', - 'key' => 'key', - ]; - - $prefix = Caster::PREFIX_VIRTUAL; - - foreach ($map as $key => $accessor) { - try { - $a[$prefix.$key] = $c->$accessor(); - } catch (\Exception $e) { - } - } - - if (isset($a[$prefix.'flags'])) { - $flagsArray = []; - foreach (self::$splFileObjectFlags as $value => $name) { - if ($a[$prefix.'flags'] & $value) { - $flagsArray[] = $name; - } - } - $a[$prefix.'flags'] = new ConstStub(implode('|', $flagsArray), $a[$prefix.'flags']); - } - - if (isset($a[$prefix.'fstat'])) { - $a[$prefix.'fstat'] = new CutArrayStub($a[$prefix.'fstat'], ['dev', 'ino', 'nlink', 'rdev', 'blksize', 'blocks']); - } - - return $a; - } - - public static function castFixedArray(\SplFixedArray $c, array $a, Stub $stub, bool $isNested) - { - $a += [ - Caster::PREFIX_VIRTUAL.'storage' => $c->toArray(), - ]; - - return $a; - } - - public static function castObjectStorage(\SplObjectStorage $c, array $a, Stub $stub, bool $isNested) - { - $storage = []; - unset($a[Caster::PREFIX_DYNAMIC."\0gcdata"]); // Don't hit https://bugs.php.net/65967 - - $clone = clone $c; - foreach ($clone as $obj) { - $storage[] = [ - 'object' => $obj, - 'info' => $clone->getInfo(), - ]; - } - - $a += [ - Caster::PREFIX_VIRTUAL.'storage' => $storage, - ]; - - return $a; - } - - public static function castOuterIterator(\OuterIterator $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'innerIterator'] = $c->getInnerIterator(); - - return $a; - } - - public static function castWeakReference(\WeakReference $c, array $a, Stub $stub, bool $isNested) - { - $a[Caster::PREFIX_VIRTUAL.'object'] = $c->get(); - - return $a; - } - - private static function castSplArray($c, array $a, Stub $stub, bool $isNested): array - { - $prefix = Caster::PREFIX_VIRTUAL; - $class = $stub->class; - $flags = $c->getFlags(); - - if (!($flags & \ArrayObject::STD_PROP_LIST)) { - $c->setFlags(\ArrayObject::STD_PROP_LIST); - $a = Caster::castObject($c, $class); - $c->setFlags($flags); - } - $a += [ - $prefix.'flag::STD_PROP_LIST' => (bool) ($flags & \ArrayObject::STD_PROP_LIST), - $prefix.'flag::ARRAY_AS_PROPS' => (bool) ($flags & \ArrayObject::ARRAY_AS_PROPS), - ]; - if ($c instanceof \ArrayObject) { - $a[$prefix.'iteratorClass'] = new ClassStub($c->getIteratorClass()); - } - $a[$prefix.'storage'] = $c->getArrayCopy(); - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/StubCaster.php b/vendor/symfony/var-dumper/Caster/StubCaster.php deleted file mode 100644 index 32ead7c..0000000 --- a/vendor/symfony/var-dumper/Caster/StubCaster.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts a caster's Stub. - * - * @author Nicolas Grekas - * - * @final - */ -class StubCaster -{ - public static function castStub(Stub $c, array $a, Stub $stub, bool $isNested) - { - if ($isNested) { - $stub->type = $c->type; - $stub->class = $c->class; - $stub->value = $c->value; - $stub->handle = $c->handle; - $stub->cut = $c->cut; - $stub->attr = $c->attr; - - if (Stub::TYPE_REF === $c->type && !$c->class && \is_string($c->value) && !preg_match('//u', $c->value)) { - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_BINARY; - } - - $a = []; - } - - return $a; - } - - public static function castCutArray(CutArrayStub $c, array $a, Stub $stub, bool $isNested) - { - return $isNested ? $c->preservedSubset : $a; - } - - public static function cutInternals($obj, array $a, Stub $stub, bool $isNested) - { - if ($isNested) { - $stub->cut += \count($a); - - return []; - } - - return $a; - } - - public static function castEnum(EnumStub $c, array $a, Stub $stub, bool $isNested) - { - if ($isNested) { - $stub->class = $c->dumpKeys ? '' : null; - $stub->handle = 0; - $stub->value = null; - $stub->cut = $c->cut; - $stub->attr = $c->attr; - - $a = []; - - if ($c->value) { - foreach (array_keys($c->value) as $k) { - $keys[] = !isset($k[0]) || "\0" !== $k[0] ? Caster::PREFIX_VIRTUAL.$k : $k; - } - // Preserve references with array_combine() - $a = array_combine($keys, $c->value); - } - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/SymfonyCaster.php b/vendor/symfony/var-dumper/Caster/SymfonyCaster.php deleted file mode 100644 index 6b87bde..0000000 --- a/vendor/symfony/var-dumper/Caster/SymfonyCaster.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\HttpFoundation\Request; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @final - */ -class SymfonyCaster -{ - private static $requestGetters = [ - 'pathInfo' => 'getPathInfo', - 'requestUri' => 'getRequestUri', - 'baseUrl' => 'getBaseUrl', - 'basePath' => 'getBasePath', - 'method' => 'getMethod', - 'format' => 'getRequestFormat', - ]; - - public static function castRequest(Request $request, array $a, Stub $stub, bool $isNested) - { - $clone = null; - - foreach (self::$requestGetters as $prop => $getter) { - $key = Caster::PREFIX_PROTECTED.$prop; - if (\array_key_exists($key, $a) && null === $a[$key]) { - if (null === $clone) { - $clone = clone $request; - } - $a[Caster::PREFIX_VIRTUAL.$prop] = $clone->{$getter}(); - } - } - - return $a; - } - - public static function castHttpClient($client, array $a, Stub $stub, bool $isNested) - { - $multiKey = sprintf("\0%s\0multi", \get_class($client)); - if (isset($a[$multiKey])) { - $a[$multiKey] = new CutStub($a[$multiKey]); - } - - return $a; - } - - public static function castHttpClientResponse($response, array $a, Stub $stub, bool $isNested) - { - $stub->cut += \count($a); - $a = []; - - foreach ($response->getInfo() as $k => $v) { - $a[Caster::PREFIX_VIRTUAL.$k] = $v; - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/TraceStub.php b/vendor/symfony/var-dumper/Caster/TraceStub.php deleted file mode 100644 index 5eea1c8..0000000 --- a/vendor/symfony/var-dumper/Caster/TraceStub.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Represents a backtrace as returned by debug_backtrace() or Exception->getTrace(). - * - * @author Nicolas Grekas - */ -class TraceStub extends Stub -{ - public $keepArgs; - public $sliceOffset; - public $sliceLength; - public $numberingOffset; - - public function __construct(array $trace, bool $keepArgs = true, int $sliceOffset = 0, int $sliceLength = null, int $numberingOffset = 0) - { - $this->value = $trace; - $this->keepArgs = $keepArgs; - $this->sliceOffset = $sliceOffset; - $this->sliceLength = $sliceLength; - $this->numberingOffset = $numberingOffset; - } -} diff --git a/vendor/symfony/var-dumper/Caster/UuidCaster.php b/vendor/symfony/var-dumper/Caster/UuidCaster.php deleted file mode 100644 index b102774..0000000 --- a/vendor/symfony/var-dumper/Caster/UuidCaster.php +++ /dev/null @@ -1,30 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Ramsey\Uuid\UuidInterface; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * @author Grégoire Pineau - */ -final class UuidCaster -{ - public static function castRamseyUuid(UuidInterface $c, array $a, Stub $stub, bool $isNested): array - { - $a += [ - Caster::PREFIX_VIRTUAL.'uuid' => (string) $c, - ]; - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php b/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php deleted file mode 100644 index 1bca357..0000000 --- a/vendor/symfony/var-dumper/Caster/XmlReaderCaster.php +++ /dev/null @@ -1,79 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts XmlReader class to array representation. - * - * @author Baptiste Clavié - * - * @final - */ -class XmlReaderCaster -{ - private static $nodeTypes = [ - \XMLReader::NONE => 'NONE', - \XMLReader::ELEMENT => 'ELEMENT', - \XMLReader::ATTRIBUTE => 'ATTRIBUTE', - \XMLReader::TEXT => 'TEXT', - \XMLReader::CDATA => 'CDATA', - \XMLReader::ENTITY_REF => 'ENTITY_REF', - \XMLReader::ENTITY => 'ENTITY', - \XMLReader::PI => 'PI (Processing Instruction)', - \XMLReader::COMMENT => 'COMMENT', - \XMLReader::DOC => 'DOC', - \XMLReader::DOC_TYPE => 'DOC_TYPE', - \XMLReader::DOC_FRAGMENT => 'DOC_FRAGMENT', - \XMLReader::NOTATION => 'NOTATION', - \XMLReader::WHITESPACE => 'WHITESPACE', - \XMLReader::SIGNIFICANT_WHITESPACE => 'SIGNIFICANT_WHITESPACE', - \XMLReader::END_ELEMENT => 'END_ELEMENT', - \XMLReader::END_ENTITY => 'END_ENTITY', - \XMLReader::XML_DECLARATION => 'XML_DECLARATION', - ]; - - public static function castXmlReader(\XMLReader $reader, array $a, Stub $stub, bool $isNested) - { - $props = Caster::PREFIX_VIRTUAL.'parserProperties'; - $info = [ - 'localName' => $reader->localName, - 'prefix' => $reader->prefix, - 'nodeType' => new ConstStub(self::$nodeTypes[$reader->nodeType], $reader->nodeType), - 'depth' => $reader->depth, - 'isDefault' => $reader->isDefault, - 'isEmptyElement' => \XMLReader::NONE === $reader->nodeType ? null : $reader->isEmptyElement, - 'xmlLang' => $reader->xmlLang, - 'attributeCount' => $reader->attributeCount, - 'value' => $reader->value, - 'namespaceURI' => $reader->namespaceURI, - 'baseURI' => $reader->baseURI ? new LinkStub($reader->baseURI) : $reader->baseURI, - $props => [ - 'LOADDTD' => $reader->getParserProperty(\XMLReader::LOADDTD), - 'DEFAULTATTRS' => $reader->getParserProperty(\XMLReader::DEFAULTATTRS), - 'VALIDATE' => $reader->getParserProperty(\XMLReader::VALIDATE), - 'SUBST_ENTITIES' => $reader->getParserProperty(\XMLReader::SUBST_ENTITIES), - ], - ]; - - if ($info[$props] = Caster::filter($info[$props], Caster::EXCLUDE_EMPTY, [], $count)) { - $info[$props] = new EnumStub($info[$props]); - $info[$props]->cut = $count; - } - - $info = Caster::filter($info, Caster::EXCLUDE_EMPTY, [], $count); - // +2 because hasValue and hasAttributes are always filtered - $stub->cut += $count + 2; - - return $a + $info; - } -} diff --git a/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php b/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php deleted file mode 100644 index edece30..0000000 --- a/vendor/symfony/var-dumper/Caster/XmlResourceCaster.php +++ /dev/null @@ -1,63 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Caster; - -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * Casts XML resources to array representation. - * - * @author Nicolas Grekas - * - * @final - */ -class XmlResourceCaster -{ - private static $xmlErrors = [ - XML_ERROR_NONE => 'XML_ERROR_NONE', - XML_ERROR_NO_MEMORY => 'XML_ERROR_NO_MEMORY', - XML_ERROR_SYNTAX => 'XML_ERROR_SYNTAX', - XML_ERROR_NO_ELEMENTS => 'XML_ERROR_NO_ELEMENTS', - XML_ERROR_INVALID_TOKEN => 'XML_ERROR_INVALID_TOKEN', - XML_ERROR_UNCLOSED_TOKEN => 'XML_ERROR_UNCLOSED_TOKEN', - XML_ERROR_PARTIAL_CHAR => 'XML_ERROR_PARTIAL_CHAR', - XML_ERROR_TAG_MISMATCH => 'XML_ERROR_TAG_MISMATCH', - XML_ERROR_DUPLICATE_ATTRIBUTE => 'XML_ERROR_DUPLICATE_ATTRIBUTE', - XML_ERROR_JUNK_AFTER_DOC_ELEMENT => 'XML_ERROR_JUNK_AFTER_DOC_ELEMENT', - XML_ERROR_PARAM_ENTITY_REF => 'XML_ERROR_PARAM_ENTITY_REF', - XML_ERROR_UNDEFINED_ENTITY => 'XML_ERROR_UNDEFINED_ENTITY', - XML_ERROR_RECURSIVE_ENTITY_REF => 'XML_ERROR_RECURSIVE_ENTITY_REF', - XML_ERROR_ASYNC_ENTITY => 'XML_ERROR_ASYNC_ENTITY', - XML_ERROR_BAD_CHAR_REF => 'XML_ERROR_BAD_CHAR_REF', - XML_ERROR_BINARY_ENTITY_REF => 'XML_ERROR_BINARY_ENTITY_REF', - XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF => 'XML_ERROR_ATTRIBUTE_EXTERNAL_ENTITY_REF', - XML_ERROR_MISPLACED_XML_PI => 'XML_ERROR_MISPLACED_XML_PI', - XML_ERROR_UNKNOWN_ENCODING => 'XML_ERROR_UNKNOWN_ENCODING', - XML_ERROR_INCORRECT_ENCODING => 'XML_ERROR_INCORRECT_ENCODING', - XML_ERROR_UNCLOSED_CDATA_SECTION => 'XML_ERROR_UNCLOSED_CDATA_SECTION', - XML_ERROR_EXTERNAL_ENTITY_HANDLING => 'XML_ERROR_EXTERNAL_ENTITY_HANDLING', - ]; - - public static function castXml($h, array $a, Stub $stub, bool $isNested) - { - $a['current_byte_index'] = xml_get_current_byte_index($h); - $a['current_column_number'] = xml_get_current_column_number($h); - $a['current_line_number'] = xml_get_current_line_number($h); - $a['error_code'] = xml_get_error_code($h); - - if (isset(self::$xmlErrors[$a['error_code']])) { - $a['error_code'] = new ConstStub(self::$xmlErrors[$a['error_code']], $a['error_code']); - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Cloner/AbstractCloner.php b/vendor/symfony/var-dumper/Cloner/AbstractCloner.php deleted file mode 100644 index 56b2384..0000000 --- a/vendor/symfony/var-dumper/Cloner/AbstractCloner.php +++ /dev/null @@ -1,360 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -use Symfony\Component\VarDumper\Caster\Caster; -use Symfony\Component\VarDumper\Exception\ThrowingCasterException; - -/** - * AbstractCloner implements a generic caster mechanism for objects and resources. - * - * @author Nicolas Grekas - */ -abstract class AbstractCloner implements ClonerInterface -{ - public static $defaultCasters = [ - '__PHP_Incomplete_Class' => ['Symfony\Component\VarDumper\Caster\Caster', 'castPhpIncompleteClass'], - - 'Symfony\Component\VarDumper\Caster\CutStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], - 'Symfony\Component\VarDumper\Caster\CutArrayStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castCutArray'], - 'Symfony\Component\VarDumper\Caster\ConstStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castStub'], - 'Symfony\Component\VarDumper\Caster\EnumStub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'castEnum'], - - 'Closure' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClosure'], - 'Generator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castGenerator'], - 'ReflectionType' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castType'], - 'ReflectionGenerator' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReflectionGenerator'], - 'ReflectionClass' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castClass'], - 'ReflectionFunctionAbstract' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castFunctionAbstract'], - 'ReflectionMethod' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castMethod'], - 'ReflectionParameter' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castParameter'], - 'ReflectionProperty' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castProperty'], - 'ReflectionReference' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castReference'], - 'ReflectionExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castExtension'], - 'ReflectionZendExtension' => ['Symfony\Component\VarDumper\Caster\ReflectionCaster', 'castZendExtension'], - - 'Doctrine\Common\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Doctrine\Common\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castCommonProxy'], - 'Doctrine\ORM\Proxy\Proxy' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castOrmProxy'], - 'Doctrine\ORM\PersistentCollection' => ['Symfony\Component\VarDumper\Caster\DoctrineCaster', 'castPersistentCollection'], - 'Doctrine\Persistence\ObjectManager' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - - 'DOMException' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castException'], - 'DOMStringList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMNameList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMImplementation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castImplementation'], - 'DOMImplementationList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNode'], - 'DOMNameSpaceNode' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNameSpaceNode'], - 'DOMDocument' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocument'], - 'DOMNodeList' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMNamedNodeMap' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLength'], - 'DOMCharacterData' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castCharacterData'], - 'DOMAttr' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castAttr'], - 'DOMElement' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castElement'], - 'DOMText' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castText'], - 'DOMTypeinfo' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castTypeinfo'], - 'DOMDomError' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDomError'], - 'DOMLocator' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castLocator'], - 'DOMDocumentType' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castDocumentType'], - 'DOMNotation' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castNotation'], - 'DOMEntity' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castEntity'], - 'DOMProcessingInstruction' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castProcessingInstruction'], - 'DOMXPath' => ['Symfony\Component\VarDumper\Caster\DOMCaster', 'castXPath'], - - 'XMLReader' => ['Symfony\Component\VarDumper\Caster\XmlReaderCaster', 'castXmlReader'], - - 'ErrorException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castErrorException'], - 'Exception' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castException'], - 'Error' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castError'], - 'Symfony\Component\DependencyInjection\ContainerInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Symfony\Component\EventDispatcher\EventDispatcherInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Symfony\Component\HttpClient\CurlHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], - 'Symfony\Component\HttpClient\NativeHttpClient' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClient'], - 'Symfony\Component\HttpClient\Response\CurlResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], - 'Symfony\Component\HttpClient\Response\NativeResponse' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castHttpClientResponse'], - 'Symfony\Component\HttpFoundation\Request' => ['Symfony\Component\VarDumper\Caster\SymfonyCaster', 'castRequest'], - 'Symfony\Component\VarDumper\Exception\ThrowingCasterException' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castThrowingCasterException'], - 'Symfony\Component\VarDumper\Caster\TraceStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castTraceStub'], - 'Symfony\Component\VarDumper\Caster\FrameStub' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castFrameStub'], - 'Symfony\Component\VarDumper\Cloner\AbstractCloner' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Symfony\Component\ErrorHandler\Exception\SilencedErrorContext' => ['Symfony\Component\VarDumper\Caster\ExceptionCaster', 'castSilencedErrorContext'], - - 'Imagine\Image\ImageInterface' => ['Symfony\Component\VarDumper\Caster\ImagineCaster', 'castImage'], - - 'Ramsey\Uuid\UuidInterface' => ['Symfony\Component\VarDumper\Caster\UuidCaster', 'castRamseyUuid'], - - 'ProxyManager\Proxy\ProxyInterface' => ['Symfony\Component\VarDumper\Caster\ProxyManagerCaster', 'castProxy'], - 'PHPUnit_Framework_MockObject_MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'PHPUnit\Framework\MockObject\MockObject' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'PHPUnit\Framework\MockObject\Stub' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Prophecy\Prophecy\ProphecySubjectInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - 'Mockery\MockInterface' => ['Symfony\Component\VarDumper\Caster\StubCaster', 'cutInternals'], - - 'PDO' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdo'], - 'PDOStatement' => ['Symfony\Component\VarDumper\Caster\PdoCaster', 'castPdoStatement'], - - 'AMQPConnection' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castConnection'], - 'AMQPChannel' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castChannel'], - 'AMQPQueue' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castQueue'], - 'AMQPExchange' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castExchange'], - 'AMQPEnvelope' => ['Symfony\Component\VarDumper\Caster\AmqpCaster', 'castEnvelope'], - - 'ArrayObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayObject'], - 'ArrayIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castArrayIterator'], - 'SplDoublyLinkedList' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castDoublyLinkedList'], - 'SplFileInfo' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileInfo'], - 'SplFileObject' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFileObject'], - 'SplFixedArray' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castFixedArray'], - 'SplHeap' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], - 'SplObjectStorage' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castObjectStorage'], - 'SplPriorityQueue' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castHeap'], - 'OuterIterator' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castOuterIterator'], - 'WeakReference' => ['Symfony\Component\VarDumper\Caster\SplCaster', 'castWeakReference'], - - 'Redis' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedis'], - 'RedisArray' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisArray'], - 'RedisCluster' => ['Symfony\Component\VarDumper\Caster\RedisCaster', 'castRedisCluster'], - - 'DateTimeInterface' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castDateTime'], - 'DateInterval' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castInterval'], - 'DateTimeZone' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castTimeZone'], - 'DatePeriod' => ['Symfony\Component\VarDumper\Caster\DateCaster', 'castPeriod'], - - 'GMP' => ['Symfony\Component\VarDumper\Caster\GmpCaster', 'castGmp'], - - 'MessageFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castMessageFormatter'], - 'NumberFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castNumberFormatter'], - 'IntlTimeZone' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlTimeZone'], - 'IntlCalendar' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlCalendar'], - 'IntlDateFormatter' => ['Symfony\Component\VarDumper\Caster\IntlCaster', 'castIntlDateFormatter'], - - 'Memcached' => ['Symfony\Component\VarDumper\Caster\MemcachedCaster', 'castMemcached'], - - 'Ds\Collection' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castCollection'], - 'Ds\Map' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castMap'], - 'Ds\Pair' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPair'], - 'Symfony\Component\VarDumper\Caster\DsPairStub' => ['Symfony\Component\VarDumper\Caster\DsCaster', 'castPairStub'], - - ':curl' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castCurl'], - ':dba' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], - ':dba persistent' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castDba'], - ':gd' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castGd'], - ':mysql link' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castMysqlLink'], - ':pgsql large object' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLargeObject'], - ':pgsql link' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], - ':pgsql link persistent' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castLink'], - ':pgsql result' => ['Symfony\Component\VarDumper\Caster\PgSqlCaster', 'castResult'], - ':process' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castProcess'], - ':stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], - ':OpenSSL X.509' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castOpensslX509'], - ':persistent stream' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStream'], - ':stream-context' => ['Symfony\Component\VarDumper\Caster\ResourceCaster', 'castStreamContext'], - ':xml' => ['Symfony\Component\VarDumper\Caster\XmlResourceCaster', 'castXml'], - ]; - - protected $maxItems = 2500; - protected $maxString = -1; - protected $minDepth = 1; - - private $casters = []; - private $prevErrorHandler; - private $classInfo = []; - private $filter = 0; - - /** - * @param callable[]|null $casters A map of casters - * - * @see addCasters - */ - public function __construct(array $casters = null) - { - if (null === $casters) { - $casters = static::$defaultCasters; - } - $this->addCasters($casters); - } - - /** - * Adds casters for resources and objects. - * - * Maps resources or objects types to a callback. - * Types are in the key, with a callable caster for value. - * Resource types are to be prefixed with a `:`, - * see e.g. static::$defaultCasters. - * - * @param callable[] $casters A map of casters - */ - public function addCasters(array $casters) - { - foreach ($casters as $type => $callback) { - $this->casters[$type][] = $callback; - } - } - - /** - * Sets the maximum number of items to clone past the minimum depth in nested structures. - */ - public function setMaxItems(int $maxItems) - { - $this->maxItems = $maxItems; - } - - /** - * Sets the maximum cloned length for strings. - */ - public function setMaxString(int $maxString) - { - $this->maxString = $maxString; - } - - /** - * Sets the minimum tree depth where we are guaranteed to clone all the items. After this - * depth is reached, only setMaxItems items will be cloned. - */ - public function setMinDepth(int $minDepth) - { - $this->minDepth = $minDepth; - } - - /** - * Clones a PHP variable. - * - * @param mixed $var Any PHP variable - * @param int $filter A bit field of Caster::EXCLUDE_* constants - * - * @return Data The cloned variable represented by a Data object - */ - public function cloneVar($var, int $filter = 0) - { - $this->prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) { - if (E_RECOVERABLE_ERROR === $type || E_USER_ERROR === $type) { - // Cloner never dies - throw new \ErrorException($msg, 0, $type, $file, $line); - } - - if ($this->prevErrorHandler) { - return ($this->prevErrorHandler)($type, $msg, $file, $line, $context); - } - - return false; - }); - $this->filter = $filter; - - if ($gc = gc_enabled()) { - gc_disable(); - } - try { - return new Data($this->doClone($var)); - } finally { - if ($gc) { - gc_enable(); - } - restore_error_handler(); - $this->prevErrorHandler = null; - } - } - - /** - * Effectively clones the PHP variable. - * - * @param mixed $var Any PHP variable - * - * @return array The cloned variable represented in an array - */ - abstract protected function doClone($var); - - /** - * Casts an object to an array representation. - * - * @param bool $isNested True if the object is nested in the dumped structure - * - * @return array The object casted as array - */ - protected function castObject(Stub $stub, bool $isNested) - { - $obj = $stub->value; - $class = $stub->class; - - if (isset($class[15]) && "\0" === $class[15] && 0 === strpos($class, "class@anonymous\x00")) { - $stub->class = get_parent_class($class).'@anonymous'; - } - if (isset($this->classInfo[$class])) { - list($i, $parents, $hasDebugInfo, $fileInfo) = $this->classInfo[$class]; - } else { - $i = 2; - $parents = [$class]; - $hasDebugInfo = method_exists($class, '__debugInfo'); - - foreach (class_parents($class) as $p) { - $parents[] = $p; - ++$i; - } - foreach (class_implements($class) as $p) { - $parents[] = $p; - ++$i; - } - $parents[] = '*'; - - $r = new \ReflectionClass($class); - $fileInfo = $r->isInternal() || $r->isSubclassOf(Stub::class) ? [] : [ - 'file' => $r->getFileName(), - 'line' => $r->getStartLine(), - ]; - - $this->classInfo[$class] = [$i, $parents, $hasDebugInfo, $fileInfo]; - } - - $stub->attr += $fileInfo; - $a = Caster::castObject($obj, $class, $hasDebugInfo); - - try { - while ($i--) { - if (!empty($this->casters[$p = $parents[$i]])) { - foreach ($this->casters[$p] as $callback) { - $a = $callback($obj, $a, $stub, $isNested, $this->filter); - } - } - } - } catch (\Exception $e) { - $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; - } - - return $a; - } - - /** - * Casts a resource to an array representation. - * - * @param bool $isNested True if the object is nested in the dumped structure - * - * @return array The resource casted as array - */ - protected function castResource(Stub $stub, bool $isNested) - { - $a = []; - $res = $stub->value; - $type = $stub->class; - - try { - if (!empty($this->casters[':'.$type])) { - foreach ($this->casters[':'.$type] as $callback) { - $a = $callback($res, $a, $stub, $isNested, $this->filter); - } - } - } catch (\Exception $e) { - $a = [(Stub::TYPE_OBJECT === $stub->type ? Caster::PREFIX_VIRTUAL : '').'⚠' => new ThrowingCasterException($e)] + $a; - } - - return $a; - } -} diff --git a/vendor/symfony/var-dumper/Cloner/ClonerInterface.php b/vendor/symfony/var-dumper/Cloner/ClonerInterface.php deleted file mode 100644 index 7ed287a..0000000 --- a/vendor/symfony/var-dumper/Cloner/ClonerInterface.php +++ /dev/null @@ -1,27 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * @author Nicolas Grekas - */ -interface ClonerInterface -{ - /** - * Clones a PHP variable. - * - * @param mixed $var Any PHP variable - * - * @return Data The cloned variable represented by a Data object - */ - public function cloneVar($var); -} diff --git a/vendor/symfony/var-dumper/Cloner/Cursor.php b/vendor/symfony/var-dumper/Cloner/Cursor.php deleted file mode 100644 index 5b0542f..0000000 --- a/vendor/symfony/var-dumper/Cloner/Cursor.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * Represents the current state of a dumper while dumping. - * - * @author Nicolas Grekas - */ -class Cursor -{ - const HASH_INDEXED = Stub::ARRAY_INDEXED; - const HASH_ASSOC = Stub::ARRAY_ASSOC; - const HASH_OBJECT = Stub::TYPE_OBJECT; - const HASH_RESOURCE = Stub::TYPE_RESOURCE; - - public $depth = 0; - public $refIndex = 0; - public $softRefTo = 0; - public $softRefCount = 0; - public $softRefHandle = 0; - public $hardRefTo = 0; - public $hardRefCount = 0; - public $hardRefHandle = 0; - public $hashType; - public $hashKey; - public $hashKeyIsBinary; - public $hashIndex = 0; - public $hashLength = 0; - public $hashCut = 0; - public $stop = false; - public $attr = []; - public $skipChildren = false; -} diff --git a/vendor/symfony/var-dumper/Cloner/Data.php b/vendor/symfony/var-dumper/Cloner/Data.php deleted file mode 100644 index 66f43c9..0000000 --- a/vendor/symfony/var-dumper/Cloner/Data.php +++ /dev/null @@ -1,451 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -use Symfony\Component\VarDumper\Caster\Caster; -use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; - -/** - * @author Nicolas Grekas - */ -class Data implements \ArrayAccess, \Countable, \IteratorAggregate -{ - private $data; - private $position = 0; - private $key = 0; - private $maxDepth = 20; - private $maxItemsPerDepth = -1; - private $useRefHandles = -1; - private $context = []; - - /** - * @param array $data An array as returned by ClonerInterface::cloneVar() - */ - public function __construct(array $data) - { - $this->data = $data; - } - - /** - * @return string|null The type of the value - */ - public function getType() - { - $item = $this->data[$this->position][$this->key]; - - if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { - $item = $item->value; - } - if (!$item instanceof Stub) { - return \gettype($item); - } - if (Stub::TYPE_STRING === $item->type) { - return 'string'; - } - if (Stub::TYPE_ARRAY === $item->type) { - return 'array'; - } - if (Stub::TYPE_OBJECT === $item->type) { - return $item->class; - } - if (Stub::TYPE_RESOURCE === $item->type) { - return $item->class.' resource'; - } - - return null; - } - - /** - * @param array|bool $recursive Whether values should be resolved recursively or not - * - * @return string|int|float|bool|array|Data[]|null A native representation of the original value - */ - public function getValue($recursive = false) - { - $item = $this->data[$this->position][$this->key]; - - if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { - $item = $item->value; - } - if (!($item = $this->getStub($item)) instanceof Stub) { - return $item; - } - if (Stub::TYPE_STRING === $item->type) { - return $item->value; - } - - $children = $item->position ? $this->data[$item->position] : []; - - foreach ($children as $k => $v) { - if ($recursive && !($v = $this->getStub($v)) instanceof Stub) { - continue; - } - $children[$k] = clone $this; - $children[$k]->key = $k; - $children[$k]->position = $item->position; - - if ($recursive) { - if (Stub::TYPE_REF === $v->type && ($v = $this->getStub($v->value)) instanceof Stub) { - $recursive = (array) $recursive; - if (isset($recursive[$v->position])) { - continue; - } - $recursive[$v->position] = true; - } - $children[$k] = $children[$k]->getValue($recursive); - } - } - - return $children; - } - - /** - * @return int - */ - public function count() - { - return \count($this->getValue()); - } - - /** - * @return \Traversable - */ - public function getIterator() - { - if (!\is_array($value = $this->getValue())) { - throw new \LogicException(sprintf('"%s" object holds non-iterable type "%s".', self::class, \gettype($value))); - } - - yield from $value; - } - - public function __get(string $key) - { - if (null !== $data = $this->seek($key)) { - $item = $this->getStub($data->data[$data->position][$data->key]); - - return $item instanceof Stub || [] === $item ? $data : $item; - } - - return null; - } - - /** - * @return bool - */ - public function __isset(string $key) - { - return null !== $this->seek($key); - } - - /** - * @return bool - */ - public function offsetExists($key) - { - return $this->__isset($key); - } - - public function offsetGet($key) - { - return $this->__get($key); - } - - public function offsetSet($key, $value) - { - throw new \BadMethodCallException(self::class.' objects are immutable.'); - } - - public function offsetUnset($key) - { - throw new \BadMethodCallException(self::class.' objects are immutable.'); - } - - /** - * @return string - */ - public function __toString() - { - $value = $this->getValue(); - - if (!\is_array($value)) { - return (string) $value; - } - - return sprintf('%s (count=%d)', $this->getType(), \count($value)); - } - - /** - * Returns a depth limited clone of $this. - * - * @return static - */ - public function withMaxDepth(int $maxDepth) - { - $data = clone $this; - $data->maxDepth = (int) $maxDepth; - - return $data; - } - - /** - * Limits the number of elements per depth level. - * - * @return static - */ - public function withMaxItemsPerDepth(int $maxItemsPerDepth) - { - $data = clone $this; - $data->maxItemsPerDepth = (int) $maxItemsPerDepth; - - return $data; - } - - /** - * Enables/disables objects' identifiers tracking. - * - * @param bool $useRefHandles False to hide global ref. handles - * - * @return static - */ - public function withRefHandles(bool $useRefHandles) - { - $data = clone $this; - $data->useRefHandles = $useRefHandles ? -1 : 0; - - return $data; - } - - /** - * @return static - */ - public function withContext(array $context) - { - $data = clone $this; - $data->context = $context; - - return $data; - } - - /** - * Seeks to a specific key in nested data structures. - * - * @param string|int $key The key to seek to - * - * @return static|null Null if the key is not set - */ - public function seek($key) - { - $item = $this->data[$this->position][$this->key]; - - if ($item instanceof Stub && Stub::TYPE_REF === $item->type && !$item->position) { - $item = $item->value; - } - if (!($item = $this->getStub($item)) instanceof Stub || !$item->position) { - return null; - } - $keys = [$key]; - - switch ($item->type) { - case Stub::TYPE_OBJECT: - $keys[] = Caster::PREFIX_DYNAMIC.$key; - $keys[] = Caster::PREFIX_PROTECTED.$key; - $keys[] = Caster::PREFIX_VIRTUAL.$key; - $keys[] = "\0$item->class\0$key"; - // no break - case Stub::TYPE_ARRAY: - case Stub::TYPE_RESOURCE: - break; - default: - return null; - } - - $data = null; - $children = $this->data[$item->position]; - - foreach ($keys as $key) { - if (isset($children[$key]) || \array_key_exists($key, $children)) { - $data = clone $this; - $data->key = $key; - $data->position = $item->position; - break; - } - } - - return $data; - } - - /** - * Dumps data with a DumperInterface dumper. - */ - public function dump(DumperInterface $dumper) - { - $refs = [0]; - $cursor = new Cursor(); - - if ($cursor->attr = $this->context[SourceContextProvider::class] ?? []) { - $cursor->attr['if_links'] = true; - $cursor->hashType = -1; - $dumper->dumpScalar($cursor, 'default', '^'); - $cursor->attr = ['if_links' => true]; - $dumper->dumpScalar($cursor, 'default', ' '); - $cursor->hashType = 0; - } - - $this->dumpItem($dumper, $cursor, $refs, $this->data[$this->position][$this->key]); - } - - /** - * Depth-first dumping of items. - * - * @param mixed $item A Stub object or the original value being dumped - */ - private function dumpItem(DumperInterface $dumper, Cursor $cursor, array &$refs, $item) - { - $cursor->refIndex = 0; - $cursor->softRefTo = $cursor->softRefHandle = $cursor->softRefCount = 0; - $cursor->hardRefTo = $cursor->hardRefHandle = $cursor->hardRefCount = 0; - $firstSeen = true; - - if (!$item instanceof Stub) { - $cursor->attr = []; - $type = \gettype($item); - if ($item && 'array' === $type) { - $item = $this->getStub($item); - } - } elseif (Stub::TYPE_REF === $item->type) { - if ($item->handle) { - if (!isset($refs[$r = $item->handle - (PHP_INT_MAX >> 1)])) { - $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; - } else { - $firstSeen = false; - } - $cursor->hardRefTo = $refs[$r]; - $cursor->hardRefHandle = $this->useRefHandles & $item->handle; - $cursor->hardRefCount = $item->refCount; - } - $cursor->attr = $item->attr; - $type = $item->class ?: \gettype($item->value); - $item = $this->getStub($item->value); - } - if ($item instanceof Stub) { - if ($item->refCount) { - if (!isset($refs[$r = $item->handle])) { - $cursor->refIndex = $refs[$r] = $cursor->refIndex ?: ++$refs[0]; - } else { - $firstSeen = false; - } - $cursor->softRefTo = $refs[$r]; - } - $cursor->softRefHandle = $this->useRefHandles & $item->handle; - $cursor->softRefCount = $item->refCount; - $cursor->attr = $item->attr; - $cut = $item->cut; - - if ($item->position && $firstSeen) { - $children = $this->data[$item->position]; - - if ($cursor->stop) { - if ($cut >= 0) { - $cut += \count($children); - } - $children = []; - } - } else { - $children = []; - } - switch ($item->type) { - case Stub::TYPE_STRING: - $dumper->dumpString($cursor, $item->value, Stub::STRING_BINARY === $item->class, $cut); - break; - - case Stub::TYPE_ARRAY: - $item = clone $item; - $item->type = $item->class; - $item->class = $item->value; - // no break - case Stub::TYPE_OBJECT: - case Stub::TYPE_RESOURCE: - $withChildren = $children && $cursor->depth !== $this->maxDepth && $this->maxItemsPerDepth; - $dumper->enterHash($cursor, $item->type, $item->class, $withChildren); - if ($withChildren) { - if ($cursor->skipChildren) { - $withChildren = false; - $cut = -1; - } else { - $cut = $this->dumpChildren($dumper, $cursor, $refs, $children, $cut, $item->type, null !== $item->class); - } - } elseif ($children && 0 <= $cut) { - $cut += \count($children); - } - $cursor->skipChildren = false; - $dumper->leaveHash($cursor, $item->type, $item->class, $withChildren, $cut); - break; - - default: - throw new \RuntimeException(sprintf('Unexpected Stub type: "%s".', $item->type)); - } - } elseif ('array' === $type) { - $dumper->enterHash($cursor, Cursor::HASH_INDEXED, 0, false); - $dumper->leaveHash($cursor, Cursor::HASH_INDEXED, 0, false, 0); - } elseif ('string' === $type) { - $dumper->dumpString($cursor, $item, false, 0); - } else { - $dumper->dumpScalar($cursor, $type, $item); - } - } - - /** - * Dumps children of hash structures. - * - * @return int The final number of removed items - */ - private function dumpChildren(DumperInterface $dumper, Cursor $parentCursor, array &$refs, array $children, int $hashCut, int $hashType, bool $dumpKeys): int - { - $cursor = clone $parentCursor; - ++$cursor->depth; - $cursor->hashType = $hashType; - $cursor->hashIndex = 0; - $cursor->hashLength = \count($children); - $cursor->hashCut = $hashCut; - foreach ($children as $key => $child) { - $cursor->hashKeyIsBinary = isset($key[0]) && !preg_match('//u', $key); - $cursor->hashKey = $dumpKeys ? $key : null; - $this->dumpItem($dumper, $cursor, $refs, $child); - if (++$cursor->hashIndex === $this->maxItemsPerDepth || $cursor->stop) { - $parentCursor->stop = true; - - return $hashCut >= 0 ? $hashCut + $cursor->hashLength - $cursor->hashIndex : $hashCut; - } - } - - return $hashCut; - } - - private function getStub($item) - { - if (!$item || !\is_array($item)) { - return $item; - } - - $stub = new Stub(); - $stub->type = Stub::TYPE_ARRAY; - foreach ($item as $stub->class => $stub->position) { - } - if (isset($item[0])) { - $stub->cut = $item[0]; - } - $stub->value = $stub->cut + ($stub->position ? \count($this->data[$stub->position]) : 0); - - return $stub; - } -} diff --git a/vendor/symfony/var-dumper/Cloner/DumperInterface.php b/vendor/symfony/var-dumper/Cloner/DumperInterface.php deleted file mode 100644 index 6d60b72..0000000 --- a/vendor/symfony/var-dumper/Cloner/DumperInterface.php +++ /dev/null @@ -1,56 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * DumperInterface used by Data objects. - * - * @author Nicolas Grekas - */ -interface DumperInterface -{ - /** - * Dumps a scalar value. - * - * @param string $type The PHP type of the value being dumped - * @param string|int|float|bool $value The scalar value being dumped - */ - public function dumpScalar(Cursor $cursor, string $type, $value); - - /** - * Dumps a string. - * - * @param string $str The string being dumped - * @param bool $bin Whether $str is UTF-8 or binary encoded - * @param int $cut The number of characters $str has been cut by - */ - public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut); - - /** - * Dumps while entering an hash. - * - * @param int $type A Cursor::HASH_* const for the type of hash - * @param string|int $class The object class, resource type or array count - * @param bool $hasChild When the dump of the hash has child item - */ - public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild); - - /** - * Dumps while leaving an hash. - * - * @param int $type A Cursor::HASH_* const for the type of hash - * @param string|int $class The object class, resource type or array count - * @param bool $hasChild When the dump of the hash has child item - * @param int $cut The number of items the hash has been cut by - */ - public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut); -} diff --git a/vendor/symfony/var-dumper/Cloner/Stub.php b/vendor/symfony/var-dumper/Cloner/Stub.php deleted file mode 100644 index 7f6d05d..0000000 --- a/vendor/symfony/var-dumper/Cloner/Stub.php +++ /dev/null @@ -1,67 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * Represents the main properties of a PHP variable. - * - * @author Nicolas Grekas - */ -class Stub -{ - const TYPE_REF = 1; - const TYPE_STRING = 2; - const TYPE_ARRAY = 3; - const TYPE_OBJECT = 4; - const TYPE_RESOURCE = 5; - - const STRING_BINARY = 1; - const STRING_UTF8 = 2; - - const ARRAY_ASSOC = 1; - const ARRAY_INDEXED = 2; - - public $type = self::TYPE_REF; - public $class = ''; - public $value; - public $cut = 0; - public $handle = 0; - public $refCount = 0; - public $position = 0; - public $attr = []; - - private static $defaultProperties = []; - - /** - * @internal - */ - public function __sleep(): array - { - $properties = []; - - if (!isset(self::$defaultProperties[$c = static::class])) { - self::$defaultProperties[$c] = get_class_vars($c); - - foreach ((new \ReflectionClass($c))->getStaticProperties() as $k => $v) { - unset(self::$defaultProperties[$c][$k]); - } - } - - foreach (self::$defaultProperties[$c] as $k => $v) { - if ($this->$k !== $v) { - $properties[] = $k; - } - } - - return $properties; - } -} diff --git a/vendor/symfony/var-dumper/Cloner/VarCloner.php b/vendor/symfony/var-dumper/Cloner/VarCloner.php deleted file mode 100644 index b5bd252..0000000 --- a/vendor/symfony/var-dumper/Cloner/VarCloner.php +++ /dev/null @@ -1,284 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Cloner; - -/** - * @author Nicolas Grekas - */ -class VarCloner extends AbstractCloner -{ - private static $gid; - private static $arrayCache = []; - - /** - * {@inheritdoc} - */ - protected function doClone($var) - { - $len = 1; // Length of $queue - $pos = 0; // Number of cloned items past the minimum depth - $refsCounter = 0; // Hard references counter - $queue = [[$var]]; // This breadth-first queue is the return value - $hardRefs = []; // Map of original zval ids to stub objects - $objRefs = []; // Map of original object handles to their stub object counterpart - $objects = []; // Keep a ref to objects to ensure their handle cannot be reused while cloning - $resRefs = []; // Map of original resource handles to their stub object counterpart - $values = []; // Map of stub objects' ids to original values - $maxItems = $this->maxItems; - $maxString = $this->maxString; - $minDepth = $this->minDepth; - $currentDepth = 0; // Current tree depth - $currentDepthFinalIndex = 0; // Final $queue index for current tree depth - $minimumDepthReached = 0 === $minDepth; // Becomes true when minimum tree depth has been reached - $cookie = (object) []; // Unique object used to detect hard references - $a = null; // Array cast for nested structures - $stub = null; // Stub capturing the main properties of an original item value - // or null if the original value is used directly - - if (!$gid = self::$gid) { - $gid = self::$gid = md5(random_bytes(6)); // Unique string used to detect the special $GLOBALS variable - } - $arrayStub = new Stub(); - $arrayStub->type = Stub::TYPE_ARRAY; - $fromObjCast = false; - - for ($i = 0; $i < $len; ++$i) { - // Detect when we move on to the next tree depth - if ($i > $currentDepthFinalIndex) { - ++$currentDepth; - $currentDepthFinalIndex = $len - 1; - if ($currentDepth >= $minDepth) { - $minimumDepthReached = true; - } - } - - $refs = $vals = $queue[$i]; - foreach ($vals as $k => $v) { - // $v is the original value or a stub object in case of hard references - - if (\PHP_VERSION_ID >= 70400) { - $zvalIsRef = null !== \ReflectionReference::fromArrayElement($vals, $k); - } else { - $refs[$k] = $cookie; - $zvalIsRef = $vals[$k] === $cookie; - } - - if ($zvalIsRef) { - $vals[$k] = &$stub; // Break hard references to make $queue completely - unset($stub); // independent from the original structure - if ($v instanceof Stub && isset($hardRefs[spl_object_id($v)])) { - $vals[$k] = $refs[$k] = $v; - if ($v->value instanceof Stub && (Stub::TYPE_OBJECT === $v->value->type || Stub::TYPE_RESOURCE === $v->value->type)) { - ++$v->value->refCount; - } - ++$v->refCount; - continue; - } - $refs[$k] = $vals[$k] = new Stub(); - $refs[$k]->value = $v; - $h = spl_object_id($refs[$k]); - $hardRefs[$h] = &$refs[$k]; - $values[$h] = $v; - $vals[$k]->handle = ++$refsCounter; - } - // Create $stub when the original value $v can not be used directly - // If $v is a nested structure, put that structure in array $a - switch (true) { - case null === $v: - case \is_bool($v): - case \is_int($v): - case \is_float($v): - continue 2; - - case \is_string($v): - if ('' === $v) { - continue 2; - } - if (!preg_match('//u', $v)) { - $stub = new Stub(); - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_BINARY; - if (0 <= $maxString && 0 < $cut = \strlen($v) - $maxString) { - $stub->cut = $cut; - $stub->value = substr($v, 0, -$cut); - } else { - $stub->value = $v; - } - } elseif (0 <= $maxString && isset($v[1 + ($maxString >> 2)]) && 0 < $cut = mb_strlen($v, 'UTF-8') - $maxString) { - $stub = new Stub(); - $stub->type = Stub::TYPE_STRING; - $stub->class = Stub::STRING_UTF8; - $stub->cut = $cut; - $stub->value = mb_substr($v, 0, $maxString, 'UTF-8'); - } else { - continue 2; - } - $a = null; - break; - - case \is_array($v): - if (!$v) { - continue 2; - } - $stub = $arrayStub; - $stub->class = Stub::ARRAY_INDEXED; - - $j = -1; - foreach ($v as $gk => $gv) { - if ($gk !== ++$j) { - $stub->class = Stub::ARRAY_ASSOC; - break; - } - } - $a = $v; - - if (Stub::ARRAY_ASSOC === $stub->class) { - // Copies of $GLOBALS have very strange behavior, - // let's detect them with some black magic - $a[$gid] = true; - - // Happens with copies of $GLOBALS - if (isset($v[$gid])) { - unset($v[$gid]); - $a = []; - foreach ($v as $gk => &$gv) { - $a[$gk] = &$gv; - } - unset($gv); - } else { - $a = $v; - } - } - break; - - case \is_object($v): - case $v instanceof \__PHP_Incomplete_Class: - if (empty($objRefs[$h = spl_object_id($v)])) { - $stub = new Stub(); - $stub->type = Stub::TYPE_OBJECT; - $stub->class = \get_class($v); - $stub->value = $v; - $stub->handle = $h; - $a = $this->castObject($stub, 0 < $i); - if ($v !== $stub->value) { - if (Stub::TYPE_OBJECT !== $stub->type || null === $stub->value) { - break; - } - $stub->handle = $h = spl_object_id($stub->value); - } - $stub->value = null; - if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { - $stub->cut = \count($a); - $a = null; - } - } - if (empty($objRefs[$h])) { - $objRefs[$h] = $stub; - $objects[] = $v; - } else { - $stub = $objRefs[$h]; - ++$stub->refCount; - $a = null; - } - break; - - default: // resource - if (empty($resRefs[$h = (int) $v])) { - $stub = new Stub(); - $stub->type = Stub::TYPE_RESOURCE; - if ('Unknown' === $stub->class = @get_resource_type($v)) { - $stub->class = 'Closed'; - } - $stub->value = $v; - $stub->handle = $h; - $a = $this->castResource($stub, 0 < $i); - $stub->value = null; - if (0 <= $maxItems && $maxItems <= $pos && $minimumDepthReached) { - $stub->cut = \count($a); - $a = null; - } - } - if (empty($resRefs[$h])) { - $resRefs[$h] = $stub; - } else { - $stub = $resRefs[$h]; - ++$stub->refCount; - $a = null; - } - break; - } - - if ($a) { - if (!$minimumDepthReached || 0 > $maxItems) { - $queue[$len] = $a; - $stub->position = $len++; - } elseif ($pos < $maxItems) { - if ($maxItems < $pos += \count($a)) { - $a = \array_slice($a, 0, $maxItems - $pos); - if ($stub->cut >= 0) { - $stub->cut += $pos - $maxItems; - } - } - $queue[$len] = $a; - $stub->position = $len++; - } elseif ($stub->cut >= 0) { - $stub->cut += \count($a); - $stub->position = 0; - } - } - - if ($arrayStub === $stub) { - if ($arrayStub->cut) { - $stub = [$arrayStub->cut, $arrayStub->class => $arrayStub->position]; - $arrayStub->cut = 0; - } elseif (isset(self::$arrayCache[$arrayStub->class][$arrayStub->position])) { - $stub = self::$arrayCache[$arrayStub->class][$arrayStub->position]; - } else { - self::$arrayCache[$arrayStub->class][$arrayStub->position] = $stub = [$arrayStub->class => $arrayStub->position]; - } - } - - if ($zvalIsRef) { - $refs[$k]->value = $stub; - } else { - $vals[$k] = $stub; - } - } - - if ($fromObjCast) { - $fromObjCast = false; - $refs = $vals; - $vals = []; - $j = -1; - foreach ($queue[$i] as $k => $v) { - foreach ([$k => true] as $gk => $gv) { - } - if ($gk !== $k) { - $vals = (object) $vals; - $vals->{$k} = $refs[++$j]; - $vals = (array) $vals; - } else { - $vals[$k] = $refs[++$j]; - } - } - } - - $queue[$i] = $vals; - } - - foreach ($values as $h => $v) { - $hardRefs[$h] = $v; - } - - return $queue; - } -} diff --git a/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php b/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php deleted file mode 100644 index dc77d03..0000000 --- a/vendor/symfony/var-dumper/Command/Descriptor/CliDescriptor.php +++ /dev/null @@ -1,88 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command\Descriptor; - -use Symfony\Component\Console\Formatter\OutputFormatterStyle; -use Symfony\Component\Console\Input\ArrayInput; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\CliDumper; - -/** - * Describe collected data clones for cli output. - * - * @author Maxime Steinhausser - * - * @final - */ -class CliDescriptor implements DumpDescriptorInterface -{ - private $dumper; - private $lastIdentifier; - private $supportsHref; - - public function __construct(CliDumper $dumper) - { - $this->dumper = $dumper; - $this->supportsHref = method_exists(OutputFormatterStyle::class, 'setHref'); - } - - public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void - { - $io = $output instanceof SymfonyStyle ? $output : new SymfonyStyle(new ArrayInput([]), $output); - $this->dumper->setColors($output->isDecorated()); - - $rows = [['date', date('r', $context['timestamp'])]]; - $lastIdentifier = $this->lastIdentifier; - $this->lastIdentifier = $clientId; - - $section = "Received from client #$clientId"; - if (isset($context['request'])) { - $request = $context['request']; - $this->lastIdentifier = $request['identifier']; - $section = sprintf('%s %s', $request['method'], $request['uri']); - if ($controller = $request['controller']) { - $rows[] = ['controller', rtrim($this->dumper->dump($controller, true), "\n")]; - } - } elseif (isset($context['cli'])) { - $this->lastIdentifier = $context['cli']['identifier']; - $section = '$ '.$context['cli']['command_line']; - } - - if ($this->lastIdentifier !== $lastIdentifier) { - $io->section($section); - } - - if (isset($context['source'])) { - $source = $context['source']; - $sourceInfo = sprintf('%s on line %d', $source['name'], $source['line']); - $fileLink = $source['file_link'] ?? null; - if ($this->supportsHref && $fileLink) { - $sourceInfo = sprintf('%s', $fileLink, $sourceInfo); - } - $rows[] = ['source', $sourceInfo]; - $file = $source['file_relative'] ?? $source['file']; - $rows[] = ['file', $file]; - } - - $io->table([], $rows); - - if (!$this->supportsHref && isset($fileLink)) { - $io->writeln(['Open source in your IDE/browser:', $fileLink]); - $io->newLine(); - } - - $this->dumper->dump($data); - $io->newLine(); - } -} diff --git a/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php b/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php deleted file mode 100644 index 267d27b..0000000 --- a/vendor/symfony/var-dumper/Command/Descriptor/DumpDescriptorInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command\Descriptor; - -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\VarDumper\Cloner\Data; - -/** - * @author Maxime Steinhausser - */ -interface DumpDescriptorInterface -{ - public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void; -} diff --git a/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php b/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php deleted file mode 100644 index 35a203b..0000000 --- a/vendor/symfony/var-dumper/Command/Descriptor/HtmlDescriptor.php +++ /dev/null @@ -1,119 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command\Descriptor; - -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; - -/** - * Describe collected data clones for html output. - * - * @author Maxime Steinhausser - * - * @final - */ -class HtmlDescriptor implements DumpDescriptorInterface -{ - private $dumper; - private $initialized = false; - - public function __construct(HtmlDumper $dumper) - { - $this->dumper = $dumper; - } - - public function describe(OutputInterface $output, Data $data, array $context, int $clientId): void - { - if (!$this->initialized) { - $styles = file_get_contents(__DIR__.'/../../Resources/css/htmlDescriptor.css'); - $scripts = file_get_contents(__DIR__.'/../../Resources/js/htmlDescriptor.js'); - $output->writeln(""); - $this->initialized = true; - } - - $title = '-'; - if (isset($context['request'])) { - $request = $context['request']; - $controller = "{$this->dumper->dump($request['controller'], true, ['maxDepth' => 0])}"; - $title = sprintf('%s %s', $request['method'], $uri = $request['uri'], $uri); - $dedupIdentifier = $request['identifier']; - } elseif (isset($context['cli'])) { - $title = '$ '.$context['cli']['command_line']; - $dedupIdentifier = $context['cli']['identifier']; - } else { - $dedupIdentifier = uniqid('', true); - } - - $sourceDescription = ''; - if (isset($context['source'])) { - $source = $context['source']; - $projectDir = $source['project_dir'] ?? null; - $sourceDescription = sprintf('%s on line %d', $source['name'], $source['line']); - if (isset($source['file_link'])) { - $sourceDescription = sprintf('%s', $source['file_link'], $sourceDescription); - } - } - - $isoDate = $this->extractDate($context, 'c'); - $tags = array_filter([ - 'controller' => $controller ?? null, - 'project dir' => $projectDir ?? null, - ]); - - $output->writeln(<< -
-
-

$title

- -
- {$this->renderTags($tags)} -
-
-

- $sourceDescription -

- {$this->dumper->dump($data, true)} -
- -HTML - ); - } - - private function extractDate(array $context, string $format = 'r'): string - { - return date($format, $context['timestamp']); - } - - private function renderTags(array $tags): string - { - if (!$tags) { - return ''; - } - - $renderedTags = ''; - foreach ($tags as $key => $value) { - $renderedTags .= sprintf('
  • %s%s
  • ', $key, $value); - } - - return << -
      - $renderedTags -
    - -HTML; - } -} diff --git a/vendor/symfony/var-dumper/Command/ServerDumpCommand.php b/vendor/symfony/var-dumper/Command/ServerDumpCommand.php deleted file mode 100644 index c8a61da..0000000 --- a/vendor/symfony/var-dumper/Command/ServerDumpCommand.php +++ /dev/null @@ -1,99 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Command; - -use Symfony\Component\Console\Command\Command; -use Symfony\Component\Console\Exception\InvalidArgumentException; -use Symfony\Component\Console\Input\InputInterface; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Output\OutputInterface; -use Symfony\Component\Console\Style\SymfonyStyle; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Command\Descriptor\CliDescriptor; -use Symfony\Component\VarDumper\Command\Descriptor\DumpDescriptorInterface; -use Symfony\Component\VarDumper\Command\Descriptor\HtmlDescriptor; -use Symfony\Component\VarDumper\Dumper\CliDumper; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; -use Symfony\Component\VarDumper\Server\DumpServer; - -/** - * Starts a dump server to collect and output dumps on a single place with multiple formats support. - * - * @author Maxime Steinhausser - * - * @final - */ -class ServerDumpCommand extends Command -{ - protected static $defaultName = 'server:dump'; - - private $server; - - /** @var DumpDescriptorInterface[] */ - private $descriptors; - - public function __construct(DumpServer $server, array $descriptors = []) - { - $this->server = $server; - $this->descriptors = $descriptors + [ - 'cli' => new CliDescriptor(new CliDumper()), - 'html' => new HtmlDescriptor(new HtmlDumper()), - ]; - - parent::__construct(); - } - - protected function configure() - { - $availableFormats = implode(', ', array_keys($this->descriptors)); - - $this - ->addOption('format', null, InputOption::VALUE_REQUIRED, sprintf('The output format (%s)', $availableFormats), 'cli') - ->setDescription('Starts a dump server that collects and displays dumps in a single place') - ->setHelp(<<<'EOF' -%command.name% starts a dump server that collects and displays -dumps in a single place for debugging you application: - - php %command.full_name% - -You can consult dumped data in HTML format in your browser by providing the --format=html option -and redirecting the output to a file: - - php %command.full_name% --format="html" > dump.html - -EOF - ) - ; - } - - protected function execute(InputInterface $input, OutputInterface $output): int - { - $io = new SymfonyStyle($input, $output); - $format = $input->getOption('format'); - - if (!$descriptor = $this->descriptors[$format] ?? null) { - throw new InvalidArgumentException(sprintf('Unsupported format "%s".', $format)); - } - - $errorIo = $io->getErrorStyle(); - $errorIo->title('Symfony Var Dumper Server'); - - $this->server->start(); - - $errorIo->success(sprintf('Server listening on %s', $this->server->getHost())); - $errorIo->comment('Quit the server with CONTROL-C.'); - - $this->server->listen(function (Data $data, array $context, int $clientId) use ($descriptor, $io) { - $descriptor->describe($io, $data, $context, $clientId); - }); - } -} diff --git a/vendor/symfony/var-dumper/Dumper/AbstractDumper.php b/vendor/symfony/var-dumper/Dumper/AbstractDumper.php deleted file mode 100644 index 1a00038..0000000 --- a/vendor/symfony/var-dumper/Dumper/AbstractDumper.php +++ /dev/null @@ -1,204 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Cloner\DumperInterface; - -/** - * Abstract mechanism for dumping a Data object. - * - * @author Nicolas Grekas - */ -abstract class AbstractDumper implements DataDumperInterface, DumperInterface -{ - const DUMP_LIGHT_ARRAY = 1; - const DUMP_STRING_LENGTH = 2; - const DUMP_COMMA_SEPARATOR = 4; - const DUMP_TRAILING_COMMA = 8; - - public static $defaultOutput = 'php://output'; - - protected $line = ''; - protected $lineDumper; - protected $outputStream; - protected $decimalPoint; // This is locale dependent - protected $indentPad = ' '; - protected $flags; - - private $charset = ''; - - /** - * @param callable|resource|string|null $output A line dumper callable, an opened stream or an output path, defaults to static::$defaultOutput - * @param string|null $charset The default character encoding to use for non-UTF8 strings - * @param int $flags A bit field of static::DUMP_* constants to fine tune dumps representation - */ - public function __construct($output = null, string $charset = null, int $flags = 0) - { - $this->flags = $flags; - $this->setCharset($charset ?: ini_get('php.output_encoding') ?: ini_get('default_charset') ?: 'UTF-8'); - $this->decimalPoint = localeconv(); - $this->decimalPoint = $this->decimalPoint['decimal_point']; - $this->setOutput($output ?: static::$defaultOutput); - if (!$output && \is_string(static::$defaultOutput)) { - static::$defaultOutput = $this->outputStream; - } - } - - /** - * Sets the output destination of the dumps. - * - * @param callable|resource|string $output A line dumper callable, an opened stream or an output path - * - * @return callable|resource|string The previous output destination - */ - public function setOutput($output) - { - $prev = null !== $this->outputStream ? $this->outputStream : $this->lineDumper; - - if (\is_callable($output)) { - $this->outputStream = null; - $this->lineDumper = $output; - } else { - if (\is_string($output)) { - $output = fopen($output, 'wb'); - } - $this->outputStream = $output; - $this->lineDumper = [$this, 'echoLine']; - } - - return $prev; - } - - /** - * Sets the default character encoding to use for non-UTF8 strings. - * - * @return string The previous charset - */ - public function setCharset(string $charset) - { - $prev = $this->charset; - - $charset = strtoupper($charset); - $charset = null === $charset || 'UTF-8' === $charset || 'UTF8' === $charset ? 'CP1252' : $charset; - - $this->charset = $charset; - - return $prev; - } - - /** - * Sets the indentation pad string. - * - * @param string $pad A string that will be prepended to dumped lines, repeated by nesting level - * - * @return string The previous indent pad - */ - public function setIndentPad(string $pad) - { - $prev = $this->indentPad; - $this->indentPad = $pad; - - return $prev; - } - - /** - * Dumps a Data object. - * - * @param callable|resource|string|true|null $output A line dumper callable, an opened stream, an output path or true to return the dump - * - * @return string|null The dump as string when $output is true - */ - public function dump(Data $data, $output = null) - { - $this->decimalPoint = localeconv(); - $this->decimalPoint = $this->decimalPoint['decimal_point']; - - if ($locale = $this->flags & (self::DUMP_COMMA_SEPARATOR | self::DUMP_TRAILING_COMMA) ? setlocale(LC_NUMERIC, 0) : null) { - setlocale(LC_NUMERIC, 'C'); - } - - if ($returnDump = true === $output) { - $output = fopen('php://memory', 'r+b'); - } - if ($output) { - $prevOutput = $this->setOutput($output); - } - try { - $data->dump($this); - $this->dumpLine(-1); - - if ($returnDump) { - $result = stream_get_contents($output, -1, 0); - fclose($output); - - return $result; - } - } finally { - if ($output) { - $this->setOutput($prevOutput); - } - if ($locale) { - setlocale(LC_NUMERIC, $locale); - } - } - - return null; - } - - /** - * Dumps the current line. - * - * @param int $depth The recursive depth in the dumped structure for the line being dumped, - * or -1 to signal the end-of-dump to the line dumper callable - */ - protected function dumpLine(int $depth) - { - ($this->lineDumper)($this->line, $depth, $this->indentPad); - $this->line = ''; - } - - /** - * Generic line dumper callback. - */ - protected function echoLine(string $line, int $depth, string $indentPad) - { - if (-1 !== $depth) { - fwrite($this->outputStream, str_repeat($indentPad, $depth).$line."\n"); - } - } - - /** - * Converts a non-UTF-8 string to UTF-8. - * - * @return string|null The string converted to UTF-8 - */ - protected function utf8Encode(?string $s) - { - if (null === $s || preg_match('//u', $s)) { - return $s; - } - - if (!\function_exists('iconv')) { - throw new \RuntimeException('Unable to convert a non-UTF-8 string to UTF-8: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); - } - - if (false !== $c = @iconv($this->charset, 'UTF-8', $s)) { - return $c; - } - if ('CP1252' !== $this->charset && false !== $c = @iconv('CP1252', 'UTF-8', $s)) { - return $c; - } - - return iconv('CP850', 'UTF-8', $s); - } -} diff --git a/vendor/symfony/var-dumper/Dumper/CliDumper.php b/vendor/symfony/var-dumper/Dumper/CliDumper.php deleted file mode 100644 index 326ce1d..0000000 --- a/vendor/symfony/var-dumper/Dumper/CliDumper.php +++ /dev/null @@ -1,636 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Cursor; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * CliDumper dumps variables for command line output. - * - * @author Nicolas Grekas - */ -class CliDumper extends AbstractDumper -{ - public static $defaultColors; - public static $defaultOutput = 'php://stdout'; - - protected $colors; - protected $maxStringWidth = 0; - protected $styles = [ - // See http://en.wikipedia.org/wiki/ANSI_escape_code#graphics - 'default' => '0;38;5;208', - 'num' => '1;38;5;38', - 'const' => '1;38;5;208', - 'str' => '1;38;5;113', - 'note' => '38;5;38', - 'ref' => '38;5;247', - 'public' => '', - 'protected' => '', - 'private' => '', - 'meta' => '38;5;170', - 'key' => '38;5;113', - 'index' => '38;5;38', - ]; - - protected static $controlCharsRx = '/[\x00-\x1F\x7F]+/'; - protected static $controlCharsMap = [ - "\t" => '\t', - "\n" => '\n', - "\v" => '\v', - "\f" => '\f', - "\r" => '\r', - "\033" => '\e', - ]; - - protected $collapseNextHash = false; - protected $expandNextHash = false; - - private $displayOptions = [ - 'fileLinkFormat' => null, - ]; - - private $handlesHrefGracefully; - - /** - * {@inheritdoc} - */ - public function __construct($output = null, string $charset = null, int $flags = 0) - { - parent::__construct($output, $charset, $flags); - - if ('\\' === \DIRECTORY_SEPARATOR && !$this->isWindowsTrueColor()) { - // Use only the base 16 xterm colors when using ANSICON or standard Windows 10 CLI - $this->setStyles([ - 'default' => '31', - 'num' => '1;34', - 'const' => '1;31', - 'str' => '1;32', - 'note' => '34', - 'ref' => '1;30', - 'meta' => '35', - 'key' => '32', - 'index' => '34', - ]); - } - - $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format') ?: 'file://%f#L%l'; - } - - /** - * Enables/disables colored output. - */ - public function setColors(bool $colors) - { - $this->colors = $colors; - } - - /** - * Sets the maximum number of characters per line for dumped strings. - */ - public function setMaxStringWidth(int $maxStringWidth) - { - $this->maxStringWidth = $maxStringWidth; - } - - /** - * Configures styles. - * - * @param array $styles A map of style names to style definitions - */ - public function setStyles(array $styles) - { - $this->styles = $styles + $this->styles; - } - - /** - * Configures display options. - * - * @param array $displayOptions A map of display options to customize the behavior - */ - public function setDisplayOptions(array $displayOptions) - { - $this->displayOptions = $displayOptions + $this->displayOptions; - } - - /** - * {@inheritdoc} - */ - public function dumpScalar(Cursor $cursor, string $type, $value) - { - $this->dumpKey($cursor); - - $style = 'const'; - $attr = $cursor->attr; - - switch ($type) { - case 'default': - $style = 'default'; - break; - - case 'integer': - $style = 'num'; - break; - - case 'double': - $style = 'num'; - - switch (true) { - case INF === $value: $value = 'INF'; break; - case -INF === $value: $value = '-INF'; break; - case is_nan($value): $value = 'NAN'; break; - default: - $value = (string) $value; - if (false === strpos($value, $this->decimalPoint)) { - $value .= $this->decimalPoint.'0'; - } - break; - } - break; - - case 'NULL': - $value = 'null'; - break; - - case 'boolean': - $value = $value ? 'true' : 'false'; - break; - - default: - $attr += ['value' => $this->utf8Encode($value)]; - $value = $this->utf8Encode($type); - break; - } - - $this->line .= $this->style($style, $value, $attr); - - $this->endValue($cursor); - } - - /** - * {@inheritdoc} - */ - public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) - { - $this->dumpKey($cursor); - $attr = $cursor->attr; - - if ($bin) { - $str = $this->utf8Encode($str); - } - if ('' === $str) { - $this->line .= '""'; - $this->endValue($cursor); - } else { - $attr += [ - 'length' => 0 <= $cut ? mb_strlen($str, 'UTF-8') + $cut : 0, - 'binary' => $bin, - ]; - $str = explode("\n", $str); - if (isset($str[1]) && !isset($str[2]) && !isset($str[1][0])) { - unset($str[1]); - $str[0] .= "\n"; - } - $m = \count($str) - 1; - $i = $lineCut = 0; - - if (self::DUMP_STRING_LENGTH & $this->flags) { - $this->line .= '('.$attr['length'].') '; - } - if ($bin) { - $this->line .= 'b'; - } - - if ($m) { - $this->line .= '"""'; - $this->dumpLine($cursor->depth); - } else { - $this->line .= '"'; - } - - foreach ($str as $str) { - if ($i < $m) { - $str .= "\n"; - } - if (0 < $this->maxStringWidth && $this->maxStringWidth < $len = mb_strlen($str, 'UTF-8')) { - $str = mb_substr($str, 0, $this->maxStringWidth, 'UTF-8'); - $lineCut = $len - $this->maxStringWidth; - } - if ($m && 0 < $cursor->depth) { - $this->line .= $this->indentPad; - } - if ('' !== $str) { - $this->line .= $this->style('str', $str, $attr); - } - if ($i++ == $m) { - if ($m) { - if ('' !== $str) { - $this->dumpLine($cursor->depth); - if (0 < $cursor->depth) { - $this->line .= $this->indentPad; - } - } - $this->line .= '"""'; - } else { - $this->line .= '"'; - } - if ($cut < 0) { - $this->line .= '…'; - $lineCut = 0; - } elseif ($cut) { - $lineCut += $cut; - } - } - if ($lineCut) { - $this->line .= '…'.$lineCut; - $lineCut = 0; - } - - if ($i > $m) { - $this->endValue($cursor); - } else { - $this->dumpLine($cursor->depth); - } - } - } - } - - /** - * {@inheritdoc} - */ - public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild) - { - $this->dumpKey($cursor); - $attr = $cursor->attr; - - if ($this->collapseNextHash) { - $cursor->skipChildren = true; - $this->collapseNextHash = $hasChild = false; - } - - $class = $this->utf8Encode($class); - if (Cursor::HASH_OBJECT === $type) { - $prefix = $class && 'stdClass' !== $class ? $this->style('note', $class, $attr).(empty($attr['cut_hash']) ? ' {' : '') : '{'; - } elseif (Cursor::HASH_RESOURCE === $type) { - $prefix = $this->style('note', $class.' resource', $attr).($hasChild ? ' {' : ' '); - } else { - $prefix = $class && !(self::DUMP_LIGHT_ARRAY & $this->flags) ? $this->style('note', 'array:'.$class, $attr).' [' : '['; - } - - if (($cursor->softRefCount || 0 < $cursor->softRefHandle) && empty($attr['cut_hash'])) { - $prefix .= $this->style('ref', (Cursor::HASH_RESOURCE === $type ? '@' : '#').(0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->softRefTo), ['count' => $cursor->softRefCount]); - } elseif ($cursor->hardRefTo && !$cursor->refIndex && $class) { - $prefix .= $this->style('ref', '&'.$cursor->hardRefTo, ['count' => $cursor->hardRefCount]); - } elseif (!$hasChild && Cursor::HASH_RESOURCE === $type) { - $prefix = substr($prefix, 0, -1); - } - - $this->line .= $prefix; - - if ($hasChild) { - $this->dumpLine($cursor->depth); - } - } - - /** - * {@inheritdoc} - */ - public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut) - { - if (empty($cursor->attr['cut_hash'])) { - $this->dumpEllipsis($cursor, $hasChild, $cut); - $this->line .= Cursor::HASH_OBJECT === $type ? '}' : (Cursor::HASH_RESOURCE !== $type ? ']' : ($hasChild ? '}' : '')); - } - - $this->endValue($cursor); - } - - /** - * Dumps an ellipsis for cut children. - * - * @param bool $hasChild When the dump of the hash has child item - * @param int $cut The number of items the hash has been cut by - */ - protected function dumpEllipsis(Cursor $cursor, $hasChild, $cut) - { - if ($cut) { - $this->line .= ' …'; - if (0 < $cut) { - $this->line .= $cut; - } - if ($hasChild) { - $this->dumpLine($cursor->depth + 1); - } - } - } - - /** - * Dumps a key in a hash structure. - */ - protected function dumpKey(Cursor $cursor) - { - if (null !== $key = $cursor->hashKey) { - if ($cursor->hashKeyIsBinary) { - $key = $this->utf8Encode($key); - } - $attr = ['binary' => $cursor->hashKeyIsBinary]; - $bin = $cursor->hashKeyIsBinary ? 'b' : ''; - $style = 'key'; - switch ($cursor->hashType) { - default: - case Cursor::HASH_INDEXED: - if (self::DUMP_LIGHT_ARRAY & $this->flags) { - break; - } - $style = 'index'; - // no break - case Cursor::HASH_ASSOC: - if (\is_int($key)) { - $this->line .= $this->style($style, $key).' => '; - } else { - $this->line .= $bin.'"'.$this->style($style, $key).'" => '; - } - break; - - case Cursor::HASH_RESOURCE: - $key = "\0~\0".$key; - // no break - case Cursor::HASH_OBJECT: - if (!isset($key[0]) || "\0" !== $key[0]) { - $this->line .= '+'.$bin.$this->style('public', $key).': '; - } elseif (0 < strpos($key, "\0", 1)) { - $key = explode("\0", substr($key, 1), 2); - - switch ($key[0][0]) { - case '+': // User inserted keys - $attr['dynamic'] = true; - $this->line .= '+'.$bin.'"'.$this->style('public', $key[1], $attr).'": '; - break 2; - case '~': - $style = 'meta'; - if (isset($key[0][1])) { - parse_str(substr($key[0], 1), $attr); - $attr += ['binary' => $cursor->hashKeyIsBinary]; - } - break; - case '*': - $style = 'protected'; - $bin = '#'.$bin; - break; - default: - $attr['class'] = $key[0]; - $style = 'private'; - $bin = '-'.$bin; - break; - } - - if (isset($attr['collapse'])) { - if ($attr['collapse']) { - $this->collapseNextHash = true; - } else { - $this->expandNextHash = true; - } - } - - $this->line .= $bin.$this->style($style, $key[1], $attr).(isset($attr['separator']) ? $attr['separator'] : ': '); - } else { - // This case should not happen - $this->line .= '-'.$bin.'"'.$this->style('private', $key, ['class' => '']).'": '; - } - break; - } - - if ($cursor->hardRefTo) { - $this->line .= $this->style('ref', '&'.($cursor->hardRefCount ? $cursor->hardRefTo : ''), ['count' => $cursor->hardRefCount]).' '; - } - } - } - - /** - * Decorates a value with some style. - * - * @param string $style The type of style being applied - * @param string $value The value being styled - * @param array $attr Optional context information - * - * @return string The value with style decoration - */ - protected function style($style, $value, $attr = []) - { - if (null === $this->colors) { - $this->colors = $this->supportsColors(); - } - - if (null === $this->handlesHrefGracefully) { - $this->handlesHrefGracefully = 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') && !getenv('KONSOLE_VERSION'); - } - - if (isset($attr['ellipsis'], $attr['ellipsis-type'])) { - $prefix = substr($value, 0, -$attr['ellipsis']); - if ('cli' === \PHP_SAPI && 'path' === $attr['ellipsis-type'] && isset($_SERVER[$pwd = '\\' === \DIRECTORY_SEPARATOR ? 'CD' : 'PWD']) && 0 === strpos($prefix, $_SERVER[$pwd])) { - $prefix = '.'.substr($prefix, \strlen($_SERVER[$pwd])); - } - if (!empty($attr['ellipsis-tail'])) { - $prefix .= substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']); - $value = substr($value, -$attr['ellipsis'] + $attr['ellipsis-tail']); - } else { - $value = substr($value, -$attr['ellipsis']); - } - - $value = $this->style('default', $prefix).$this->style($style, $value); - - goto href; - } - - $map = static::$controlCharsMap; - $startCchr = $this->colors ? "\033[m\033[{$this->styles['default']}m" : ''; - $endCchr = $this->colors ? "\033[m\033[{$this->styles[$style]}m" : ''; - $value = preg_replace_callback(static::$controlCharsRx, function ($c) use ($map, $startCchr, $endCchr) { - $s = $startCchr; - $c = $c[$i = 0]; - do { - $s .= isset($map[$c[$i]]) ? $map[$c[$i]] : sprintf('\x%02X', \ord($c[$i])); - } while (isset($c[++$i])); - - return $s.$endCchr; - }, $value, -1, $cchrCount); - - if ($this->colors) { - if ($cchrCount && "\033" === $value[0]) { - $value = substr($value, \strlen($startCchr)); - } else { - $value = "\033[{$this->styles[$style]}m".$value; - } - if ($cchrCount && $endCchr === substr($value, -\strlen($endCchr))) { - $value = substr($value, 0, -\strlen($endCchr)); - } else { - $value .= "\033[{$this->styles['default']}m"; - } - } - - href: - if ($this->colors && $this->handlesHrefGracefully) { - if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) { - if ('note' === $style) { - $value .= "\033]8;;{$href}\033\\^\033]8;;\033\\"; - } else { - $attr['href'] = $href; - } - } - if (isset($attr['href'])) { - $value = "\033]8;;{$attr['href']}\033\\{$value}\033]8;;\033\\"; - } - } elseif ($attr['if_links'] ?? false) { - return ''; - } - - return $value; - } - - /** - * @return bool Tells if the current output stream supports ANSI colors or not - */ - protected function supportsColors() - { - if ($this->outputStream !== static::$defaultOutput) { - return $this->hasColorSupport($this->outputStream); - } - if (null !== static::$defaultColors) { - return static::$defaultColors; - } - if (isset($_SERVER['argv'][1])) { - $colors = $_SERVER['argv']; - $i = \count($colors); - while (--$i > 0) { - if (isset($colors[$i][5])) { - switch ($colors[$i]) { - case '--ansi': - case '--color': - case '--color=yes': - case '--color=force': - case '--color=always': - return static::$defaultColors = true; - - case '--no-ansi': - case '--color=no': - case '--color=none': - case '--color=never': - return static::$defaultColors = false; - } - } - } - } - - $h = stream_get_meta_data($this->outputStream) + ['wrapper_type' => null]; - $h = 'Output' === $h['stream_type'] && 'PHP' === $h['wrapper_type'] ? fopen('php://stdout', 'wb') : $this->outputStream; - - return static::$defaultColors = $this->hasColorSupport($h); - } - - /** - * {@inheritdoc} - */ - protected function dumpLine(int $depth, bool $endOfValue = false) - { - if ($this->colors) { - $this->line = sprintf("\033[%sm%s\033[m", $this->styles['default'], $this->line); - } - parent::dumpLine($depth); - } - - protected function endValue(Cursor $cursor) - { - if (-1 === $cursor->hashType) { - return; - } - - if (Stub::ARRAY_INDEXED === $cursor->hashType || Stub::ARRAY_ASSOC === $cursor->hashType) { - if (self::DUMP_TRAILING_COMMA & $this->flags && 0 < $cursor->depth) { - $this->line .= ','; - } elseif (self::DUMP_COMMA_SEPARATOR & $this->flags && 1 < $cursor->hashLength - $cursor->hashIndex) { - $this->line .= ','; - } - } - - $this->dumpLine($cursor->depth, true); - } - - /** - * Returns true if the stream supports colorization. - * - * Reference: Composer\XdebugHandler\Process::supportsColor - * https://github.com/composer/xdebug-handler - * - * @param mixed $stream A CLI output stream - */ - private function hasColorSupport($stream): bool - { - if (!\is_resource($stream) || 'stream' !== get_resource_type($stream)) { - return false; - } - - // Follow https://no-color.org/ - if (isset($_SERVER['NO_COLOR']) || false !== getenv('NO_COLOR')) { - return false; - } - - if ('Hyper' === getenv('TERM_PROGRAM')) { - return true; - } - - if (\DIRECTORY_SEPARATOR === '\\') { - return (\function_exists('sapi_windows_vt100_support') - && @sapi_windows_vt100_support($stream)) - || false !== getenv('ANSICON') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM'); - } - - return stream_isatty($stream); - } - - /** - * Returns true if the Windows terminal supports true color. - * - * Note that this does not check an output stream, but relies on environment - * variables from known implementations, or a PHP and Windows version that - * supports true color. - */ - private function isWindowsTrueColor(): bool - { - $result = 183 <= getenv('ANSICON_VER') - || 'ON' === getenv('ConEmuANSI') - || 'xterm' === getenv('TERM') - || 'Hyper' === getenv('TERM_PROGRAM'); - - if (!$result) { - $version = sprintf( - '%s.%s.%s', - PHP_WINDOWS_VERSION_MAJOR, - PHP_WINDOWS_VERSION_MINOR, - PHP_WINDOWS_VERSION_BUILD - ); - $result = $version >= '10.0.15063'; - } - - return $result; - } - - private function getSourceLink(string $file, int $line) - { - if ($fmt = $this->displayOptions['fileLinkFormat']) { - return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : ($fmt->format($file, $line) ?: 'file://'.$file.'#L'.$line); - } - - return false; - } -} diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php deleted file mode 100644 index 38f8789..0000000 --- a/vendor/symfony/var-dumper/Dumper/ContextProvider/CliContextProvider.php +++ /dev/null @@ -1,32 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -/** - * Tries to provide context on CLI. - * - * @author Maxime Steinhausser - */ -final class CliContextProvider implements ContextProviderInterface -{ - public function getContext(): ?array - { - if ('cli' !== \PHP_SAPI) { - return null; - } - - return [ - 'command_line' => $commandLine = implode(' ', $_SERVER['argv'] ?? []), - 'identifier' => hash('crc32b', $commandLine.$_SERVER['REQUEST_TIME_FLOAT']), - ]; - } -} diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php deleted file mode 100644 index 38ef3b0..0000000 --- a/vendor/symfony/var-dumper/Dumper/ContextProvider/ContextProviderInterface.php +++ /dev/null @@ -1,25 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -/** - * Interface to provide contextual data about dump data clones sent to a server. - * - * @author Maxime Steinhausser - */ -interface ContextProviderInterface -{ - /** - * @return array|null Context data or null if unable to provide any context - */ - public function getContext(): ?array; -} diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php deleted file mode 100644 index 3684a47..0000000 --- a/vendor/symfony/var-dumper/Dumper/ContextProvider/RequestContextProvider.php +++ /dev/null @@ -1,51 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -use Symfony\Component\HttpFoundation\RequestStack; -use Symfony\Component\VarDumper\Caster\ReflectionCaster; -use Symfony\Component\VarDumper\Cloner\VarCloner; - -/** - * Tries to provide context from a request. - * - * @author Maxime Steinhausser - */ -final class RequestContextProvider implements ContextProviderInterface -{ - private $requestStack; - private $cloner; - - public function __construct(RequestStack $requestStack) - { - $this->requestStack = $requestStack; - $this->cloner = new VarCloner(); - $this->cloner->setMaxItems(0); - $this->cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); - } - - public function getContext(): ?array - { - if (null === $request = $this->requestStack->getCurrentRequest()) { - return null; - } - - $controller = $request->attributes->get('_controller'); - - return [ - 'uri' => $request->getUri(), - 'method' => $request->getMethod(), - 'controller' => $controller ? $this->cloner->cloneVar($controller) : $controller, - 'identifier' => spl_object_hash($request), - ]; - } -} diff --git a/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php b/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php deleted file mode 100644 index 6f4caba..0000000 --- a/vendor/symfony/var-dumper/Dumper/ContextProvider/SourceContextProvider.php +++ /dev/null @@ -1,126 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper\ContextProvider; - -use Symfony\Component\HttpKernel\Debug\FileLinkFormatter; -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; -use Symfony\Component\VarDumper\VarDumper; -use Twig\Template; - -/** - * Tries to provide context from sources (class name, file, line, code excerpt, ...). - * - * @author Nicolas Grekas - * @author Maxime Steinhausser - */ -final class SourceContextProvider implements ContextProviderInterface -{ - private $limit; - private $charset; - private $projectDir; - private $fileLinkFormatter; - - public function __construct(string $charset = null, string $projectDir = null, FileLinkFormatter $fileLinkFormatter = null, int $limit = 9) - { - $this->charset = $charset; - $this->projectDir = $projectDir; - $this->fileLinkFormatter = $fileLinkFormatter; - $this->limit = $limit; - } - - public function getContext(): ?array - { - $trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS, $this->limit); - - $file = $trace[1]['file']; - $line = $trace[1]['line']; - $name = false; - $fileExcerpt = false; - - for ($i = 2; $i < $this->limit; ++$i) { - if (isset($trace[$i]['class'], $trace[$i]['function']) - && 'dump' === $trace[$i]['function'] - && VarDumper::class === $trace[$i]['class'] - ) { - $file = $trace[$i]['file'] ?? $file; - $line = $trace[$i]['line'] ?? $line; - - while (++$i < $this->limit) { - if (isset($trace[$i]['function'], $trace[$i]['file']) && empty($trace[$i]['class']) && 0 !== strpos($trace[$i]['function'], 'call_user_func')) { - $file = $trace[$i]['file']; - $line = $trace[$i]['line']; - - break; - } elseif (isset($trace[$i]['object']) && $trace[$i]['object'] instanceof Template) { - $template = $trace[$i]['object']; - $name = $template->getTemplateName(); - $src = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getCode() : (method_exists($template, 'getSource') ? $template->getSource() : false); - $info = $template->getDebugInfo(); - if (isset($info[$trace[$i - 1]['line']])) { - $line = $info[$trace[$i - 1]['line']]; - $file = method_exists($template, 'getSourceContext') ? $template->getSourceContext()->getPath() : null; - - if ($src) { - $src = explode("\n", $src); - $fileExcerpt = []; - - for ($i = max($line - 3, 1), $max = min($line + 3, \count($src)); $i <= $max; ++$i) { - $fileExcerpt[] = ''.$this->htmlEncode($src[$i - 1]).''; - } - - $fileExcerpt = '
      '.implode("\n", $fileExcerpt).'
    '; - } - } - break; - } - } - break; - } - } - - if (false === $name) { - $name = str_replace('\\', '/', $file); - $name = substr($name, strrpos($name, '/') + 1); - } - - $context = ['name' => $name, 'file' => $file, 'line' => $line]; - $context['file_excerpt'] = $fileExcerpt; - - if (null !== $this->projectDir) { - $context['project_dir'] = $this->projectDir; - if (0 === strpos($file, $this->projectDir)) { - $context['file_relative'] = ltrim(substr($file, \strlen($this->projectDir)), \DIRECTORY_SEPARATOR); - } - } - - if ($this->fileLinkFormatter && $fileLink = $this->fileLinkFormatter->format($context['file'], $context['line'])) { - $context['file_link'] = $fileLink; - } - - return $context; - } - - private function htmlEncode(string $s): string - { - $html = ''; - - $dumper = new HtmlDumper(function ($line) use (&$html) { $html .= $line; }, $this->charset); - $dumper->setDumpHeader(''); - $dumper->setDumpBoundaries('', ''); - - $cloner = new VarCloner(); - $dumper->dump($cloner->cloneVar($s)); - - return substr(strip_tags($html), 1, -1); - } -} diff --git a/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php b/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php deleted file mode 100644 index 7638417..0000000 --- a/vendor/symfony/var-dumper/Dumper/ContextualizedDumper.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; - -/** - * @author Kévin Thérage - */ -class ContextualizedDumper implements DataDumperInterface -{ - private $wrappedDumper; - private $contextProviders; - - /** - * @param ContextProviderInterface[] $contextProviders - */ - public function __construct(DataDumperInterface $wrappedDumper, array $contextProviders) - { - $this->wrappedDumper = $wrappedDumper; - $this->contextProviders = $contextProviders; - } - - public function dump(Data $data) - { - $context = []; - foreach ($this->contextProviders as $contextProvider) { - $context[\get_class($contextProvider)] = $contextProvider->getContext(); - } - - $this->wrappedDumper->dump($data->withContext($context)); - } -} diff --git a/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php b/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php deleted file mode 100644 index b173bcc..0000000 --- a/vendor/symfony/var-dumper/Dumper/DataDumperInterface.php +++ /dev/null @@ -1,24 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; - -/** - * DataDumperInterface for dumping Data objects. - * - * @author Nicolas Grekas - */ -interface DataDumperInterface -{ - public function dump(Data $data); -} diff --git a/vendor/symfony/var-dumper/Dumper/HtmlDumper.php b/vendor/symfony/var-dumper/Dumper/HtmlDumper.php deleted file mode 100644 index d89fecf..0000000 --- a/vendor/symfony/var-dumper/Dumper/HtmlDumper.php +++ /dev/null @@ -1,1004 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Cursor; -use Symfony\Component\VarDumper\Cloner\Data; - -/** - * HtmlDumper dumps variables as HTML. - * - * @author Nicolas Grekas - */ -class HtmlDumper extends CliDumper -{ - public static $defaultOutput = 'php://output'; - - protected static $themes = [ - 'dark' => [ - 'default' => 'background-color:#18171B; color:#FF8400; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', - 'num' => 'font-weight:bold; color:#1299DA', - 'const' => 'font-weight:bold', - 'str' => 'font-weight:bold; color:#56DB3A', - 'note' => 'color:#1299DA', - 'ref' => 'color:#A0A0A0', - 'public' => 'color:#FFFFFF', - 'protected' => 'color:#FFFFFF', - 'private' => 'color:#FFFFFF', - 'meta' => 'color:#B729D9', - 'key' => 'color:#56DB3A', - 'index' => 'color:#1299DA', - 'ellipsis' => 'color:#FF8400', - 'ns' => 'user-select:none;', - ], - 'light' => [ - 'default' => 'background:none; color:#CC7832; line-height:1.2em; font:12px Menlo, Monaco, Consolas, monospace; word-wrap: break-word; white-space: pre-wrap; position:relative; z-index:99999; word-break: break-all', - 'num' => 'font-weight:bold; color:#1299DA', - 'const' => 'font-weight:bold', - 'str' => 'font-weight:bold; color:#629755;', - 'note' => 'color:#6897BB', - 'ref' => 'color:#6E6E6E', - 'public' => 'color:#262626', - 'protected' => 'color:#262626', - 'private' => 'color:#262626', - 'meta' => 'color:#B729D9', - 'key' => 'color:#789339', - 'index' => 'color:#1299DA', - 'ellipsis' => 'color:#CC7832', - 'ns' => 'user-select:none;', - ], - ]; - - protected $dumpHeader; - protected $dumpPrefix = '
    ';
    -    protected $dumpSuffix = '
    '; - protected $dumpId = 'sf-dump'; - protected $colors = true; - protected $headerIsDumped = false; - protected $lastDepth = -1; - protected $styles; - - private $displayOptions = [ - 'maxDepth' => 1, - 'maxStringLength' => 160, - 'fileLinkFormat' => null, - ]; - private $extraDisplayOptions = []; - - /** - * {@inheritdoc} - */ - public function __construct($output = null, string $charset = null, int $flags = 0) - { - AbstractDumper::__construct($output, $charset, $flags); - $this->dumpId = 'sf-dump-'.mt_rand(); - $this->displayOptions['fileLinkFormat'] = ini_get('xdebug.file_link_format') ?: get_cfg_var('xdebug.file_link_format'); - $this->styles = static::$themes['dark'] ?? self::$themes['dark']; - } - - /** - * {@inheritdoc} - */ - public function setStyles(array $styles) - { - $this->headerIsDumped = false; - $this->styles = $styles + $this->styles; - } - - public function setTheme(string $themeName) - { - if (!isset(static::$themes[$themeName])) { - throw new \InvalidArgumentException(sprintf('Theme "%s" does not exist in class "%s".', $themeName, static::class)); - } - - $this->setStyles(static::$themes[$themeName]); - } - - /** - * Configures display options. - * - * @param array $displayOptions A map of display options to customize the behavior - */ - public function setDisplayOptions(array $displayOptions) - { - $this->headerIsDumped = false; - $this->displayOptions = $displayOptions + $this->displayOptions; - } - - /** - * Sets an HTML header that will be dumped once in the output stream. - * - * @param string $header An HTML string - */ - public function setDumpHeader($header) - { - $this->dumpHeader = $header; - } - - /** - * Sets an HTML prefix and suffix that will encapse every single dump. - * - * @param string $prefix The prepended HTML string - * @param string $suffix The appended HTML string - */ - public function setDumpBoundaries($prefix, $suffix) - { - $this->dumpPrefix = $prefix; - $this->dumpSuffix = $suffix; - } - - /** - * {@inheritdoc} - */ - public function dump(Data $data, $output = null, array $extraDisplayOptions = []) - { - $this->extraDisplayOptions = $extraDisplayOptions; - $result = parent::dump($data, $output); - $this->dumpId = 'sf-dump-'.mt_rand(); - - return $result; - } - - /** - * Dumps the HTML header. - */ - protected function getDumpHeader() - { - $this->headerIsDumped = null !== $this->outputStream ? $this->outputStream : $this->lineDumper; - - if (null !== $this->dumpHeader) { - return $this->dumpHeader; - } - - $line = str_replace('{$options}', json_encode($this->displayOptions, JSON_FORCE_OBJECT), <<<'EOHTML' -'.$this->dumpHeader; - } - - /** - * {@inheritdoc} - */ - public function dumpString(Cursor $cursor, string $str, bool $bin, int $cut) - { - if ('' === $str && isset($cursor->attr['img-data'], $cursor->attr['content-type'])) { - $this->dumpKey($cursor); - $this->line .= $this->style('default', $cursor->attr['img-size'] ?? '', []).' '; - $this->endValue($cursor); - $this->line .= $this->indentPad; - $this->line .= sprintf('', $cursor->attr['content-type'], base64_encode($cursor->attr['img-data'])); - $this->endValue($cursor); - } else { - parent::dumpString($cursor, $str, $bin, $cut); - } - } - - /** - * {@inheritdoc} - */ - public function enterHash(Cursor $cursor, int $type, $class, bool $hasChild) - { - if (Cursor::HASH_OBJECT === $type) { - $cursor->attr['depth'] = $cursor->depth; - } - parent::enterHash($cursor, $type, $class, false); - - if ($cursor->skipChildren) { - $cursor->skipChildren = false; - $eol = ' class=sf-dump-compact>'; - } elseif ($this->expandNextHash) { - $this->expandNextHash = false; - $eol = ' class=sf-dump-expanded>'; - } else { - $eol = '>'; - } - - if ($hasChild) { - $this->line .= 'refIndex) { - $r = Cursor::HASH_OBJECT !== $type ? 1 - (Cursor::HASH_RESOURCE !== $type) : 2; - $r .= $r && 0 < $cursor->softRefHandle ? $cursor->softRefHandle : $cursor->refIndex; - - $this->line .= sprintf(' id=%s-ref%s', $this->dumpId, $r); - } - $this->line .= $eol; - $this->dumpLine($cursor->depth); - } - } - - /** - * {@inheritdoc} - */ - public function leaveHash(Cursor $cursor, int $type, $class, bool $hasChild, int $cut) - { - $this->dumpEllipsis($cursor, $hasChild, $cut); - if ($hasChild) { - $this->line .= ''; - } - parent::leaveHash($cursor, $type, $class, $hasChild, 0); - } - - /** - * {@inheritdoc} - */ - protected function style($style, $value, $attr = []) - { - if ('' === $value) { - return ''; - } - - $v = esc($value); - - if ('ref' === $style) { - if (empty($attr['count'])) { - return sprintf('%s', $v); - } - $r = ('#' !== $v[0] ? 1 - ('@' !== $v[0]) : 2).substr($value, 1); - - return sprintf('%s', $this->dumpId, $r, 1 + $attr['count'], $v); - } - - if ('const' === $style && isset($attr['value'])) { - $style .= sprintf(' title="%s"', esc(is_scalar($attr['value']) ? $attr['value'] : json_encode($attr['value']))); - } elseif ('public' === $style) { - $style .= sprintf(' title="%s"', empty($attr['dynamic']) ? 'Public property' : 'Runtime added dynamic property'); - } elseif ('str' === $style && 1 < $attr['length']) { - $style .= sprintf(' title="%d%s characters"', $attr['length'], $attr['binary'] ? ' binary or non-UTF-8' : ''); - } elseif ('note' === $style && 0 < ($attr['depth'] ?? 0) && false !== $c = strrpos($value, '\\')) { - $style .= ' title=""'; - $attr += [ - 'ellipsis' => \strlen($value) - $c, - 'ellipsis-type' => 'note', - 'ellipsis-tail' => 1, - ]; - } elseif ('protected' === $style) { - $style .= ' title="Protected property"'; - } elseif ('meta' === $style && isset($attr['title'])) { - $style .= sprintf(' title="%s"', esc($this->utf8Encode($attr['title']))); - } elseif ('private' === $style) { - $style .= sprintf(' title="Private property defined in class: `%s`"', esc($this->utf8Encode($attr['class']))); - } - $map = static::$controlCharsMap; - - if (isset($attr['ellipsis'])) { - $class = 'sf-dump-ellipsis'; - if (isset($attr['ellipsis-type'])) { - $class = sprintf('"%s sf-dump-ellipsis-%s"', $class, $attr['ellipsis-type']); - } - $label = esc(substr($value, -$attr['ellipsis'])); - $style = str_replace(' title="', " title=\"$v\n", $style); - $v = sprintf('%s', $class, substr($v, 0, -\strlen($label))); - - if (!empty($attr['ellipsis-tail'])) { - $tail = \strlen(esc(substr($value, -$attr['ellipsis'], $attr['ellipsis-tail']))); - $v .= sprintf('%s%s', $class, substr($label, 0, $tail), substr($label, $tail)); - } else { - $v .= $label; - } - } - - $v = "".preg_replace_callback(static::$controlCharsRx, function ($c) use ($map) { - $s = $b = ''; - }, $v).''; - - if (isset($attr['file']) && $href = $this->getSourceLink($attr['file'], isset($attr['line']) ? $attr['line'] : 0)) { - $attr['href'] = $href; - } - if (isset($attr['href'])) { - $target = isset($attr['file']) ? '' : ' target="_blank"'; - $v = sprintf('%s', esc($this->utf8Encode($attr['href'])), $target, $v); - } - if (isset($attr['lang'])) { - $v = sprintf('%s', esc($attr['lang']), $v); - } - - return $v; - } - - /** - * {@inheritdoc} - */ - protected function dumpLine(int $depth, bool $endOfValue = false) - { - if (-1 === $this->lastDepth) { - $this->line = sprintf($this->dumpPrefix, $this->dumpId, $this->indentPad).$this->line; - } - if ($this->headerIsDumped !== (null !== $this->outputStream ? $this->outputStream : $this->lineDumper)) { - $this->line = $this->getDumpHeader().$this->line; - } - - if (-1 === $depth) { - $args = ['"'.$this->dumpId.'"']; - if ($this->extraDisplayOptions) { - $args[] = json_encode($this->extraDisplayOptions, JSON_FORCE_OBJECT); - } - // Replace is for BC - $this->line .= sprintf(str_replace('"%s"', '%s', $this->dumpSuffix), implode(', ', $args)); - } - $this->lastDepth = $depth; - - $this->line = mb_convert_encoding($this->line, 'HTML-ENTITIES', 'UTF-8'); - - if (-1 === $depth) { - AbstractDumper::dumpLine(0); - } - AbstractDumper::dumpLine($depth); - } - - private function getSourceLink(string $file, int $line) - { - $options = $this->extraDisplayOptions + $this->displayOptions; - - if ($fmt = $options['fileLinkFormat']) { - return \is_string($fmt) ? strtr($fmt, ['%f' => $file, '%l' => $line]) : $fmt->format($file, $line); - } - - return false; - } -} - -function esc($str) -{ - return htmlspecialchars($str, ENT_QUOTES, 'UTF-8'); -} diff --git a/vendor/symfony/var-dumper/Dumper/ServerDumper.php b/vendor/symfony/var-dumper/Dumper/ServerDumper.php deleted file mode 100644 index 94795bf..0000000 --- a/vendor/symfony/var-dumper/Dumper/ServerDumper.php +++ /dev/null @@ -1,53 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Dumper; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; -use Symfony\Component\VarDumper\Server\Connection; - -/** - * ServerDumper forwards serialized Data clones to a server. - * - * @author Maxime Steinhausser - */ -class ServerDumper implements DataDumperInterface -{ - private $connection; - private $wrappedDumper; - - /** - * @param string $host The server host - * @param DataDumperInterface|null $wrappedDumper A wrapped instance used whenever we failed contacting the server - * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name - */ - public function __construct(string $host, DataDumperInterface $wrappedDumper = null, array $contextProviders = []) - { - $this->connection = new Connection($host, $contextProviders); - $this->wrappedDumper = $wrappedDumper; - } - - public function getContextProviders(): array - { - return $this->connection->getContextProviders(); - } - - /** - * {@inheritdoc} - */ - public function dump(Data $data) - { - if (!$this->connection->write($data) && $this->wrappedDumper) { - $this->wrappedDumper->dump($data); - } - } -} diff --git a/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php b/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php deleted file mode 100644 index 122f0d3..0000000 --- a/vendor/symfony/var-dumper/Exception/ThrowingCasterException.php +++ /dev/null @@ -1,26 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Exception; - -/** - * @author Nicolas Grekas - */ -class ThrowingCasterException extends \Exception -{ - /** - * @param \Throwable $prev The exception thrown from the caster - */ - public function __construct(\Throwable $prev) - { - parent::__construct('Unexpected '.\get_class($prev).' thrown from a caster: '.$prev->getMessage(), 0, $prev); - } -} diff --git a/vendor/symfony/var-dumper/README.md b/vendor/symfony/var-dumper/README.md deleted file mode 100644 index 339f73e..0000000 --- a/vendor/symfony/var-dumper/README.md +++ /dev/null @@ -1,15 +0,0 @@ -VarDumper Component -=================== - -The VarDumper component provides mechanisms for walking through any arbitrary -PHP variable. It provides a better `dump()` function that you can use instead -of `var_dump`. - -Resources ---------- - - * [Documentation](https://symfony.com/doc/current/components/var_dumper/introduction.html) - * [Contributing](https://symfony.com/doc/current/contributing/index.html) - * [Report issues](https://github.com/symfony/symfony/issues) and - [send Pull Requests](https://github.com/symfony/symfony/pulls) - in the [main Symfony repository](https://github.com/symfony/symfony) diff --git a/vendor/symfony/var-dumper/Resources/bin/var-dump-server b/vendor/symfony/var-dumper/Resources/bin/var-dump-server deleted file mode 100755 index 98c813a..0000000 --- a/vendor/symfony/var-dumper/Resources/bin/var-dump-server +++ /dev/null @@ -1,63 +0,0 @@ -#!/usr/bin/env php - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -/** - * Starts a dump server to collect and output dumps on a single place with multiple formats support. - * - * @author Maxime Steinhausser - */ - -use Psr\Log\LoggerInterface; -use Symfony\Component\Console\Application; -use Symfony\Component\Console\Input\ArgvInput; -use Symfony\Component\Console\Input\InputOption; -use Symfony\Component\Console\Logger\ConsoleLogger; -use Symfony\Component\Console\Output\ConsoleOutput; -use Symfony\Component\VarDumper\Command\ServerDumpCommand; -use Symfony\Component\VarDumper\Server\DumpServer; - -function includeIfExists(string $file): bool -{ - return file_exists($file) && include $file; -} - -if ( - !includeIfExists(__DIR__ . '/../../../../autoload.php') && - !includeIfExists(__DIR__ . '/../../vendor/autoload.php') && - !includeIfExists(__DIR__ . '/../../../../../../vendor/autoload.php') -) { - fwrite(STDERR, 'Install dependencies using Composer.'.PHP_EOL); - exit(1); -} - -if (!class_exists(Application::class)) { - fwrite(STDERR, 'You need the "symfony/console" component in order to run the VarDumper server.'.PHP_EOL); - exit(1); -} - -$input = new ArgvInput(); -$output = new ConsoleOutput(); -$defaultHost = '127.0.0.1:9912'; -$host = $input->getParameterOption(['--host'], $_SERVER['VAR_DUMPER_SERVER'] ?? $defaultHost, true); -$logger = interface_exists(LoggerInterface::class) ? new ConsoleLogger($output->getErrorOutput()) : null; - -$app = new Application(); - -$app->getDefinition()->addOption( - new InputOption('--host', null, InputOption::VALUE_REQUIRED, 'The address the server should listen to', $defaultHost) -); - -$app->add($command = new ServerDumpCommand(new DumpServer($host, $logger))) - ->getApplication() - ->setDefaultCommand($command->getName(), true) - ->run($input, $output) -; diff --git a/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css b/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css deleted file mode 100644 index 8f706d6..0000000 --- a/vendor/symfony/var-dumper/Resources/css/htmlDescriptor.css +++ /dev/null @@ -1,130 +0,0 @@ -body { - display: flex; - flex-direction: column-reverse; - justify-content: flex-end; - max-width: 1140px; - margin: auto; - padding: 15px; - word-wrap: break-word; - background-color: #F9F9F9; - color: #222; - font-family: Helvetica, Arial, sans-serif; - font-size: 14px; - line-height: 1.4; -} -p { - margin: 0; -} -a { - color: #218BC3; - text-decoration: none; -} -a:hover { - text-decoration: underline; -} -.text-small { - font-size: 12px !important; -} -article { - margin: 5px; - margin-bottom: 10px; -} -article > header > .row { - display: flex; - flex-direction: row; - align-items: baseline; - margin-bottom: 10px; -} -article > header > .row > .col { - flex: 1; - display: flex; - align-items: baseline; -} -article > header > .row > h2 { - font-size: 14px; - color: #222; - font-weight: normal; - font-family: "Lucida Console", monospace, sans-serif; - word-break: break-all; - margin: 20px 5px 0 0; - user-select: all; -} -article > header > .row > h2 > code { - white-space: nowrap; - user-select: none; - color: #cc2255; - background-color: #f7f7f9; - border: 1px solid #e1e1e8; - border-radius: 3px; - margin-right: 5px; - padding: 0 3px; -} -article > header > .row > time.col { - flex: 0; - text-align: right; - white-space: nowrap; - color: #999; - font-style: italic; -} -article > header ul.tags { - list-style: none; - padding: 0; - margin: 0; - font-size: 12px; -} -article > header ul.tags > li { - user-select: all; - margin-bottom: 2px; -} -article > header ul.tags > li > span.badge { - display: inline-block; - padding: .25em .4em; - margin-right: 5px; - border-radius: 4px; - background-color: #6c757d3b; - color: #524d4d; - font-size: 12px; - text-align: center; - font-weight: 700; - line-height: 1; - white-space: nowrap; - vertical-align: baseline; - user-select: none; -} -article > section.body { - border: 1px solid #d8d8d8; - background: #FFF; - padding: 10px; - border-radius: 3px; -} -pre.sf-dump { - border-radius: 3px; - margin-bottom: 0; -} -.hidden { - display: none !important; -} -.dumped-tag > .sf-dump { - display: inline-block; - margin: 0; - padding: 1px 5px; - line-height: 1.4; - vertical-align: top; - background-color: transparent; - user-select: auto; -} -.dumped-tag > pre.sf-dump, -.dumped-tag > .sf-dump-default { - color: #CC7832; - background: none; -} -.dumped-tag > .sf-dump .sf-dump-str { color: #629755; } -.dumped-tag > .sf-dump .sf-dump-private, -.dumped-tag > .sf-dump .sf-dump-protected, -.dumped-tag > .sf-dump .sf-dump-public { color: #262626; } -.dumped-tag > .sf-dump .sf-dump-note { color: #6897BB; } -.dumped-tag > .sf-dump .sf-dump-key { color: #789339; } -.dumped-tag > .sf-dump .sf-dump-ref { color: #6E6E6E; } -.dumped-tag > .sf-dump .sf-dump-ellipsis { color: #CC7832; max-width: 100em; } -.dumped-tag > .sf-dump .sf-dump-ellipsis-path { max-width: 5em; } -.dumped-tag > .sf-dump .sf-dump-ns { user-select: none; } diff --git a/vendor/symfony/var-dumper/Resources/functions/dump.php b/vendor/symfony/var-dumper/Resources/functions/dump.php deleted file mode 100644 index a485d57..0000000 --- a/vendor/symfony/var-dumper/Resources/functions/dump.php +++ /dev/null @@ -1,43 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -use Symfony\Component\VarDumper\VarDumper; - -if (!function_exists('dump')) { - /** - * @author Nicolas Grekas - */ - function dump($var, ...$moreVars) - { - VarDumper::dump($var); - - foreach ($moreVars as $v) { - VarDumper::dump($v); - } - - if (1 < func_num_args()) { - return func_get_args(); - } - - return $var; - } -} - -if (!function_exists('dd')) { - function dd(...$vars) - { - foreach ($vars as $v) { - VarDumper::dump($v); - } - - exit(1); - } -} diff --git a/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js b/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js deleted file mode 100644 index 63101e5..0000000 --- a/vendor/symfony/var-dumper/Resources/js/htmlDescriptor.js +++ /dev/null @@ -1,10 +0,0 @@ -document.addEventListener('DOMContentLoaded', function() { - let prev = null; - Array.from(document.getElementsByTagName('article')).reverse().forEach(function (article) { - const dedupId = article.dataset.dedupId; - if (dedupId === prev) { - article.getElementsByTagName('header')[0].classList.add('hidden'); - } - prev = dedupId; - }); -}); diff --git a/vendor/symfony/var-dumper/Server/Connection.php b/vendor/symfony/var-dumper/Server/Connection.php deleted file mode 100644 index 8b814cb..0000000 --- a/vendor/symfony/var-dumper/Server/Connection.php +++ /dev/null @@ -1,95 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Server; - -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Dumper\ContextProvider\ContextProviderInterface; - -/** - * Forwards serialized Data clones to a server. - * - * @author Maxime Steinhausser - */ -class Connection -{ - private $host; - private $contextProviders; - private $socket; - - /** - * @param string $host The server host - * @param ContextProviderInterface[] $contextProviders Context providers indexed by context name - */ - public function __construct(string $host, array $contextProviders = []) - { - if (false === strpos($host, '://')) { - $host = 'tcp://'.$host; - } - - $this->host = $host; - $this->contextProviders = $contextProviders; - } - - public function getContextProviders(): array - { - return $this->contextProviders; - } - - public function write(Data $data): bool - { - $socketIsFresh = !$this->socket; - if (!$this->socket = $this->socket ?: $this->createSocket()) { - return false; - } - - $context = ['timestamp' => microtime(true)]; - foreach ($this->contextProviders as $name => $provider) { - $context[$name] = $provider->getContext(); - } - $context = array_filter($context); - $encodedPayload = base64_encode(serialize([$data, $context]))."\n"; - - set_error_handler([self::class, 'nullErrorHandler']); - try { - if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { - return true; - } - if (!$socketIsFresh) { - stream_socket_shutdown($this->socket, STREAM_SHUT_RDWR); - fclose($this->socket); - $this->socket = $this->createSocket(); - } - if (-1 !== stream_socket_sendto($this->socket, $encodedPayload)) { - return true; - } - } finally { - restore_error_handler(); - } - - return false; - } - - private static function nullErrorHandler($t, $m) - { - // no-op - } - - private function createSocket() - { - set_error_handler([self::class, 'nullErrorHandler']); - try { - return stream_socket_client($this->host, $errno, $errstr, 3, STREAM_CLIENT_CONNECT | STREAM_CLIENT_ASYNC_CONNECT); - } finally { - restore_error_handler(); - } - } -} diff --git a/vendor/symfony/var-dumper/Server/DumpServer.php b/vendor/symfony/var-dumper/Server/DumpServer.php deleted file mode 100644 index ad920bd..0000000 --- a/vendor/symfony/var-dumper/Server/DumpServer.php +++ /dev/null @@ -1,107 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Server; - -use Psr\Log\LoggerInterface; -use Symfony\Component\VarDumper\Cloner\Data; -use Symfony\Component\VarDumper\Cloner\Stub; - -/** - * A server collecting Data clones sent by a ServerDumper. - * - * @author Maxime Steinhausser - * - * @final - */ -class DumpServer -{ - private $host; - private $socket; - private $logger; - - public function __construct(string $host, LoggerInterface $logger = null) - { - if (false === strpos($host, '://')) { - $host = 'tcp://'.$host; - } - - $this->host = $host; - $this->logger = $logger; - } - - public function start(): void - { - if (!$this->socket = stream_socket_server($this->host, $errno, $errstr)) { - throw new \RuntimeException(sprintf('Server start failed on "%s": %s %s.', $this->host, $errstr, $errno)); - } - } - - public function listen(callable $callback): void - { - if (null === $this->socket) { - $this->start(); - } - - foreach ($this->getMessages() as $clientId => $message) { - $payload = @unserialize(base64_decode($message), ['allowed_classes' => [Data::class, Stub::class]]); - - // Impossible to decode the message, give up. - if (false === $payload) { - if ($this->logger) { - $this->logger->warning('Unable to decode a message from {clientId} client.', ['clientId' => $clientId]); - } - - continue; - } - - if (!\is_array($payload) || \count($payload) < 2 || !$payload[0] instanceof Data || !\is_array($payload[1])) { - if ($this->logger) { - $this->logger->warning('Invalid payload from {clientId} client. Expected an array of two elements (Data $data, array $context)', ['clientId' => $clientId]); - } - - continue; - } - - list($data, $context) = $payload; - - $callback($data, $context, $clientId); - } - } - - public function getHost(): string - { - return $this->host; - } - - private function getMessages(): iterable - { - $sockets = [(int) $this->socket => $this->socket]; - $write = []; - - while (true) { - $read = $sockets; - stream_select($read, $write, $write, null); - - foreach ($read as $stream) { - if ($this->socket === $stream) { - $stream = stream_socket_accept($this->socket); - $sockets[(int) $stream] = $stream; - } elseif (feof($stream)) { - unset($sockets[(int) $stream]); - fclose($stream); - } else { - yield (int) $stream => fgets($stream); - } - } - } - } -} diff --git a/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php b/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php deleted file mode 100644 index 33d60c0..0000000 --- a/vendor/symfony/var-dumper/Test/VarDumperTestTrait.php +++ /dev/null @@ -1,84 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper\Test; - -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\CliDumper; - -/** - * @author Nicolas Grekas - */ -trait VarDumperTestTrait -{ - /** - * @internal - */ - private $varDumperConfig = [ - 'casters' => [], - 'flags' => null, - ]; - - protected function setUpVarDumper(array $casters, int $flags = null): void - { - $this->varDumperConfig['casters'] = $casters; - $this->varDumperConfig['flags'] = $flags; - } - - /** - * @after - */ - protected function tearDownVarDumper(): void - { - $this->varDumperConfig['casters'] = []; - $this->varDumperConfig['flags'] = null; - } - - public function assertDumpEquals($expected, $data, int $filter = 0, string $message = '') - { - $this->assertSame($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); - } - - public function assertDumpMatchesFormat($expected, $data, int $filter = 0, string $message = '') - { - $this->assertStringMatchesFormat($this->prepareExpectation($expected, $filter), $this->getDump($data, null, $filter), $message); - } - - protected function getDump($data, $key = null, int $filter = 0): ?string - { - if (null === $flags = $this->varDumperConfig['flags']) { - $flags = getenv('DUMP_LIGHT_ARRAY') ? CliDumper::DUMP_LIGHT_ARRAY : 0; - $flags |= getenv('DUMP_STRING_LENGTH') ? CliDumper::DUMP_STRING_LENGTH : 0; - $flags |= getenv('DUMP_COMMA_SEPARATOR') ? CliDumper::DUMP_COMMA_SEPARATOR : 0; - } - - $cloner = new VarCloner(); - $cloner->addCasters($this->varDumperConfig['casters']); - $cloner->setMaxItems(-1); - $dumper = new CliDumper(null, null, $flags); - $dumper->setColors(false); - $data = $cloner->cloneVar($data, $filter)->withRefHandles(false); - if (null !== $key && null === $data = $data->seek($key)) { - return null; - } - - return rtrim($dumper->dump($data, true)); - } - - private function prepareExpectation($expected, int $filter): string - { - if (!\is_string($expected)) { - $expected = $this->getDump($expected, null, $filter); - } - - return rtrim($expected); - } -} diff --git a/vendor/symfony/var-dumper/VarDumper.php b/vendor/symfony/var-dumper/VarDumper.php deleted file mode 100644 index d336d5d..0000000 --- a/vendor/symfony/var-dumper/VarDumper.php +++ /dev/null @@ -1,60 +0,0 @@ - - * - * For the full copyright and license information, please view the LICENSE - * file that was distributed with this source code. - */ - -namespace Symfony\Component\VarDumper; - -use Symfony\Component\VarDumper\Caster\ReflectionCaster; -use Symfony\Component\VarDumper\Cloner\VarCloner; -use Symfony\Component\VarDumper\Dumper\CliDumper; -use Symfony\Component\VarDumper\Dumper\ContextProvider\SourceContextProvider; -use Symfony\Component\VarDumper\Dumper\ContextualizedDumper; -use Symfony\Component\VarDumper\Dumper\HtmlDumper; - -// Load the global dump() function -require_once __DIR__.'/Resources/functions/dump.php'; - -/** - * @author Nicolas Grekas - */ -class VarDumper -{ - private static $handler; - - public static function dump($var) - { - if (null === self::$handler) { - $cloner = new VarCloner(); - $cloner->addCasters(ReflectionCaster::UNSET_CLOSURE_FILE_INFO); - - if (isset($_SERVER['VAR_DUMPER_FORMAT'])) { - $dumper = 'html' === $_SERVER['VAR_DUMPER_FORMAT'] ? new HtmlDumper() : new CliDumper(); - } else { - $dumper = \in_array(\PHP_SAPI, ['cli', 'phpdbg']) ? new CliDumper() : new HtmlDumper(); - } - - $dumper = new ContextualizedDumper($dumper, [new SourceContextProvider()]); - - self::$handler = function ($var) use ($cloner, $dumper) { - $dumper->dump($cloner->cloneVar($var)); - }; - } - - return (self::$handler)($var); - } - - public static function setHandler(callable $callable = null) - { - $prevHandler = self::$handler; - self::$handler = $callable; - - return $prevHandler; - } -} diff --git a/vendor/symfony/var-dumper/composer.json b/vendor/symfony/var-dumper/composer.json deleted file mode 100644 index c7e668c..0000000 --- a/vendor/symfony/var-dumper/composer.json +++ /dev/null @@ -1,53 +0,0 @@ -{ - "name": "symfony/var-dumper", - "type": "library", - "description": "Symfony mechanism for exploring and dumping PHP variables", - "keywords": ["dump", "debug"], - "homepage": "https://symfony.com", - "license": "MIT", - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "require": { - "php": "^7.2.5", - "symfony/polyfill-mbstring": "~1.0" - }, - "require-dev": { - "ext-iconv": "*", - "symfony/console": "^4.4|^5.0", - "symfony/process": "^4.4|^5.0", - "twig/twig": "^2.4|^3.0" - }, - "conflict": { - "phpunit/phpunit": "<5.4.3", - "symfony/console": "<4.4" - }, - "suggest": { - "ext-iconv": "To convert non-UTF-8 strings to UTF-8 (or symfony/polyfill-iconv in case ext-iconv cannot be used).", - "ext-intl": "To show region name in time zone dump", - "symfony/console": "To use the ServerDumpCommand and/or the bin/var-dump-server script" - }, - "autoload": { - "files": [ "Resources/functions/dump.php" ], - "psr-4": { "Symfony\\Component\\VarDumper\\": "" }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "minimum-stability": "dev", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - } -} diff --git a/vendor/tightenco/collect/composer.json b/vendor/tightenco/collect/composer.json deleted file mode 100644 index f9d28f7..0000000 --- a/vendor/tightenco/collect/composer.json +++ /dev/null @@ -1,38 +0,0 @@ -{ - "name": "tightenco/collect", - "description": "Collect - Illuminate Collections as a separate package.", - "keywords": ["laravel", "collection"], - "license": "MIT", - "authors": [ - { - "name": "Taylor Otwell", - "email": "taylorotwell@gmail.com" - } - ], - "require": { - "php": "^7.1.3", - "symfony/var-dumper": "^3.4 || ^4.0 || ^5.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.0", - "nesbot/carbon": "^2.23.0" - }, - "autoload": { - "files": [ - "src/Collect/Support/helpers.php", - "src/Collect/Support/alias.php" - ], - "psr-4": { - "Tightenco\\Collect\\": "src/Collect" - } - }, - "scripts": { - "test": [ - "@composer install", - "phpunit" - ] - }, - "minimum-stability": "dev", - "prefer-stable": true -} diff --git a/vendor/tightenco/collect/src/Collect/Contracts/Support/Arrayable.php b/vendor/tightenco/collect/src/Collect/Contracts/Support/Arrayable.php deleted file mode 100755 index a205804..0000000 --- a/vendor/tightenco/collect/src/Collect/Contracts/Support/Arrayable.php +++ /dev/null @@ -1,13 +0,0 @@ -all(); - } elseif (! is_array($values)) { - continue; - } - - $results[] = $values; - } - - return array_merge([], ...$results); - } - - /** - * Cross join the given arrays, returning all possible permutations. - * - * @param iterable ...$arrays - * @return array - */ - public static function crossJoin(...$arrays) - { - $results = [[]]; - - foreach ($arrays as $index => $array) { - $append = []; - - foreach ($results as $product) { - foreach ($array as $item) { - $product[$index] = $item; - - $append[] = $product; - } - } - - $results = $append; - } - - return $results; - } - - /** - * Divide an array into two arrays. One with keys and the other with values. - * - * @param array $array - * @return array - */ - public static function divide($array) - { - return [array_keys($array), array_values($array)]; - } - - /** - * Flatten a multi-dimensional associative array with dots. - * - * @param iterable $array - * @param string $prepend - * @return array - */ - public static function dot($array, $prepend = '') - { - $results = []; - - foreach ($array as $key => $value) { - if (is_array($value) && ! empty($value)) { - $results = array_merge($results, static::dot($value, $prepend.$key.'.')); - } else { - $results[$prepend.$key] = $value; - } - } - - return $results; - } - - /** - * Get all of the given array except for a specified array of keys. - * - * @param array $array - * @param array|string $keys - * @return array - */ - public static function except($array, $keys) - { - static::forget($array, $keys); - - return $array; - } - - /** - * Determine if the given key exists in the provided array. - * - * @param \ArrayAccess|array $array - * @param string|int $key - * @return bool - */ - public static function exists($array, $key) - { - if ($array instanceof ArrayAccess) { - return $array->offsetExists($key); - } - - return array_key_exists($key, $array); - } - - /** - * Return the first element in an array passing a given truth test. - * - * @param iterable $array - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public static function first($array, callable $callback = null, $default = null) - { - if (is_null($callback)) { - if (empty($array)) { - return value($default); - } - - foreach ($array as $item) { - return $item; - } - } - - foreach ($array as $key => $value) { - if ($callback($value, $key)) { - return $value; - } - } - - return value($default); - } - - /** - * Return the last element in an array passing a given truth test. - * - * @param array $array - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public static function last($array, callable $callback = null, $default = null) - { - if (is_null($callback)) { - return empty($array) ? value($default) : end($array); - } - - return static::first(array_reverse($array, true), $callback, $default); - } - - /** - * Flatten a multi-dimensional array into a single level. - * - * @param iterable $array - * @param int $depth - * @return array - */ - public static function flatten($array, $depth = INF) - { - $result = []; - - foreach ($array as $item) { - $item = $item instanceof Collection ? $item->all() : $item; - - if (! is_array($item)) { - $result[] = $item; - } else { - $values = $depth === 1 - ? array_values($item) - : static::flatten($item, $depth - 1); - - foreach ($values as $value) { - $result[] = $value; - } - } - } - - return $result; - } - - /** - * Remove one or many array items from a given array using "dot" notation. - * - * @param array $array - * @param array|string $keys - * @return void - */ - public static function forget(&$array, $keys) - { - $original = &$array; - - $keys = (array) $keys; - - if (count($keys) === 0) { - return; - } - - foreach ($keys as $key) { - // if the exact key exists in the top-level, remove it - if (static::exists($array, $key)) { - unset($array[$key]); - - continue; - } - - $parts = explode('.', $key); - - // clean up before each pass - $array = &$original; - - while (count($parts) > 1) { - $part = array_shift($parts); - - if (isset($array[$part]) && is_array($array[$part])) { - $array = &$array[$part]; - } else { - continue 2; - } - } - - unset($array[array_shift($parts)]); - } - } - - /** - * Get an item from an array using "dot" notation. - * - * @param \ArrayAccess|array $array - * @param string|int|null $key - * @param mixed $default - * @return mixed - */ - public static function get($array, $key, $default = null) - { - if (! static::accessible($array)) { - return value($default); - } - - if (is_null($key)) { - return $array; - } - - if (static::exists($array, $key)) { - return $array[$key]; - } - - if (strpos($key, '.') === false) { - return $array[$key] ?? value($default); - } - - foreach (explode('.', $key) as $segment) { - if (static::accessible($array) && static::exists($array, $segment)) { - $array = $array[$segment]; - } else { - return value($default); - } - } - - return $array; - } - - /** - * Check if an item or items exist in an array using "dot" notation. - * - * @param \ArrayAccess|array $array - * @param string|array $keys - * @return bool - */ - public static function has($array, $keys) - { - $keys = (array) $keys; - - if (! $array || $keys === []) { - return false; - } - - foreach ($keys as $key) { - $subKeyArray = $array; - - if (static::exists($array, $key)) { - continue; - } - - foreach (explode('.', $key) as $segment) { - if (static::accessible($subKeyArray) && static::exists($subKeyArray, $segment)) { - $subKeyArray = $subKeyArray[$segment]; - } else { - return false; - } - } - } - - return true; - } - - /** - * Determine if any of the keys exist in an array using "dot" notation. - * - * @param \ArrayAccess|array $array - * @param string|array $keys - * @return bool - */ - public static function hasAny($array, $keys) - { - if (is_null($keys)) { - return false; - } - - $keys = (array) $keys; - - if (! $array) { - return false; - } - - if ($keys === []) { - return false; - } - - foreach ($keys as $key) { - if (static::has($array, $key)) { - return true; - } - } - - return false; - } - - /** - * Determines if an array is associative. - * - * An array is "associative" if it doesn't have sequential numerical keys beginning with zero. - * - * @param array $array - * @return bool - */ - public static function isAssoc(array $array) - { - $keys = array_keys($array); - - return array_keys($keys) !== $keys; - } - - /** - * Get a subset of the items from the given array. - * - * @param array $array - * @param array|string $keys - * @return array - */ - public static function only($array, $keys) - { - return array_intersect_key($array, array_flip((array) $keys)); - } - - /** - * Pluck an array of values from an array. - * - * @param iterable $array - * @param string|array $value - * @param string|array|null $key - * @return array - */ - public static function pluck($array, $value, $key = null) - { - $results = []; - - [$value, $key] = static::explodePluckParameters($value, $key); - - foreach ($array as $item) { - $itemValue = data_get($item, $value); - - // If the key is "null", we will just append the value to the array and keep - // looping. Otherwise we will key the array using the value of the key we - // received from the developer. Then we'll return the final array form. - if (is_null($key)) { - $results[] = $itemValue; - } else { - $itemKey = data_get($item, $key); - - if (is_object($itemKey) && method_exists($itemKey, '__toString')) { - $itemKey = (string) $itemKey; - } - - $results[$itemKey] = $itemValue; - } - } - - return $results; - } - - /** - * Explode the "value" and "key" arguments passed to "pluck". - * - * @param string|array $value - * @param string|array|null $key - * @return array - */ - protected static function explodePluckParameters($value, $key) - { - $value = is_string($value) ? explode('.', $value) : $value; - - $key = is_null($key) || is_array($key) ? $key : explode('.', $key); - - return [$value, $key]; - } - - /** - * Push an item onto the beginning of an array. - * - * @param array $array - * @param mixed $value - * @param mixed $key - * @return array - */ - public static function prepend($array, $value, $key = null) - { - if (is_null($key)) { - array_unshift($array, $value); - } else { - $array = [$key => $value] + $array; - } - - return $array; - } - - /** - * Get a value from the array, and remove it. - * - * @param array $array - * @param string $key - * @param mixed $default - * @return mixed - */ - public static function pull(&$array, $key, $default = null) - { - $value = static::get($array, $key, $default); - - static::forget($array, $key); - - return $value; - } - - /** - * Get one or a specified number of random values from an array. - * - * @param array $array - * @param int|null $number - * @return mixed - * - * @throws \InvalidArgumentException - */ - public static function random($array, $number = null) - { - $requested = is_null($number) ? 1 : $number; - - $count = count($array); - - if ($requested > $count) { - throw new InvalidArgumentException( - "You requested {$requested} items, but there are only {$count} items available." - ); - } - - if (is_null($number)) { - return $array[array_rand($array)]; - } - - if ((int) $number === 0) { - return []; - } - - $keys = array_rand($array, $number); - - $results = []; - - foreach ((array) $keys as $key) { - $results[] = $array[$key]; - } - - return $results; - } - - /** - * Set an array item to a given value using "dot" notation. - * - * If no key is given to the method, the entire array will be replaced. - * - * @param array $array - * @param string|null $key - * @param mixed $value - * @return array - */ - public static function set(&$array, $key, $value) - { - if (is_null($key)) { - return $array = $value; - } - - $keys = explode('.', $key); - - while (count($keys) > 1) { - $key = array_shift($keys); - - // If the key doesn't exist at this depth, we will just create an empty array - // to hold the next value, allowing us to create the arrays to hold final - // values at the correct depth. Then we'll keep digging into the array. - if (! isset($array[$key]) || ! is_array($array[$key])) { - $array[$key] = []; - } - - $array = &$array[$key]; - } - - $array[array_shift($keys)] = $value; - - return $array; - } - - /** - * Shuffle the given array and return the result. - * - * @param array $array - * @param int|null $seed - * @return array - */ - public static function shuffle($array, $seed = null) - { - if (is_null($seed)) { - shuffle($array); - } else { - mt_srand($seed); - shuffle($array); - mt_srand(); - } - - return $array; - } - - /** - * Sort the array using the given callback or "dot" notation. - * - * @param array $array - * @param callable|string|null $callback - * @return array - */ - public static function sort($array, $callback = null) - { - return Collection::make($array)->sortBy($callback)->all(); - } - - /** - * Recursively sort an array by keys and values. - * - * @param array $array - * @return array - */ - public static function sortRecursive($array) - { - foreach ($array as &$value) { - if (is_array($value)) { - $value = static::sortRecursive($value); - } - } - - if (static::isAssoc($array)) { - ksort($array); - } else { - sort($array); - } - - return $array; - } - - /** - * Convert the array into a query string. - * - * @param array $array - * @return string - */ - public static function query($array) - { - return http_build_query($array, null, '&', PHP_QUERY_RFC3986); - } - - /** - * Filter the array using the given callback. - * - * @param array $array - * @param callable $callback - * @return array - */ - public static function where($array, callable $callback) - { - return array_filter($array, $callback, ARRAY_FILTER_USE_BOTH); - } - - /** - * If the given value is not an array and not null, wrap it in one. - * - * @param mixed $value - * @return array - */ - public static function wrap($value) - { - if (is_null($value)) { - return []; - } - - return is_array($value) ? $value : [$value]; - } -} diff --git a/vendor/tightenco/collect/src/Collect/Support/Collection.php b/vendor/tightenco/collect/src/Collect/Support/Collection.php deleted file mode 100644 index 09aaee7..0000000 --- a/vendor/tightenco/collect/src/Collect/Support/Collection.php +++ /dev/null @@ -1,1336 +0,0 @@ -items = $this->getArrayableItems($items); - } - - /** - * Create a new collection by invoking the callback a given amount of times. - * - * @param int $number - * @param callable|null $callback - * @return static - */ - public static function times($number, callable $callback = null) - { - if ($number < 1) { - return new static; - } - - if (is_null($callback)) { - return new static(range(1, $number)); - } - - return (new static(range(1, $number)))->map($callback); - } - - /** - * Get all of the items in the collection. - * - * @return array - */ - public function all() - { - return $this->items; - } - - /** - * Get a lazy collection for the items in this collection. - * - * @return \Tightenco\Collect\Support\LazyCollection - */ - public function lazy() - { - return new LazyCollection($this->items); - } - - /** - * Get the average value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function avg($callback = null) - { - $callback = $this->valueRetriever($callback); - - $items = $this->map(function ($value) use ($callback) { - return $callback($value); - })->filter(function ($value) { - return ! is_null($value); - }); - - if ($count = $items->count()) { - return $items->sum() / $count; - } - } - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return mixed - */ - public function median($key = null) - { - $values = (isset($key) ? $this->pluck($key) : $this) - ->filter(function ($item) { - return ! is_null($item); - })->sort()->values(); - - $count = $values->count(); - - if ($count === 0) { - return; - } - - $middle = (int) ($count / 2); - - if ($count % 2) { - return $values->get($middle); - } - - return (new static([ - $values->get($middle - 1), $values->get($middle), - ]))->average(); - } - - /** - * Get the mode of a given key. - * - * @param string|array|null $key - * @return array|null - */ - public function mode($key = null) - { - if ($this->count() === 0) { - return; - } - - $collection = isset($key) ? $this->pluck($key) : $this; - - $counts = new self; - - $collection->each(function ($value) use ($counts) { - $counts[$value] = isset($counts[$value]) ? $counts[$value] + 1 : 1; - }); - - $sorted = $counts->sort(); - - $highestValue = $sorted->last(); - - return $sorted->filter(function ($value) use ($highestValue) { - return $value == $highestValue; - })->sort()->keys()->all(); - } - - /** - * Collapse the collection of items into a single array. - * - * @return static - */ - public function collapse() - { - return new static(Arr::collapse($this->items)); - } - - /** - * Determine if an item exists in the collection. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function contains($key, $operator = null, $value = null) - { - if (func_num_args() === 1) { - if ($this->useAsCallable($key)) { - $placeholder = new stdClass; - - return $this->first($key, $placeholder) !== $placeholder; - } - - return in_array($key, $this->items); - } - - return $this->contains($this->operatorForWhere(...func_get_args())); - } - - /** - * Cross join with the given lists, returning all possible permutations. - * - * @param mixed ...$lists - * @return static - */ - public function crossJoin(...$lists) - { - return new static(Arr::crossJoin( - $this->items, ...array_map([$this, 'getArrayableItems'], $lists) - )); - } - - /** - * Get the items in the collection that are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diff($items) - { - return new static(array_diff($this->items, $this->getArrayableItems($items))); - } - - /** - * Get the items in the collection that are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffUsing($items, callable $callback) - { - return new static(array_udiff($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Get the items in the collection whose keys and values are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diffAssoc($items) - { - return new static(array_diff_assoc($this->items, $this->getArrayableItems($items))); - } - - /** - * Get the items in the collection whose keys and values are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffAssocUsing($items, callable $callback) - { - return new static(array_diff_uassoc($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Get the items in the collection whose keys are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diffKeys($items) - { - return new static(array_diff_key($this->items, $this->getArrayableItems($items))); - } - - /** - * Get the items in the collection whose keys are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffKeysUsing($items, callable $callback) - { - return new static(array_diff_ukey($this->items, $this->getArrayableItems($items), $callback)); - } - - /** - * Retrieve duplicate items from the collection. - * - * @param callable|null $callback - * @param bool $strict - * @return static - */ - public function duplicates($callback = null, $strict = false) - { - $items = $this->map($this->valueRetriever($callback)); - - $uniqueItems = $items->unique(null, $strict); - - $compare = $this->duplicateComparator($strict); - - $duplicates = new static; - - foreach ($items as $key => $value) { - if ($uniqueItems->isNotEmpty() && $compare($value, $uniqueItems->first())) { - $uniqueItems->shift(); - } else { - $duplicates[$key] = $value; - } - } - - return $duplicates; - } - - /** - * Retrieve duplicate items from the collection using strict comparison. - * - * @param callable|null $callback - * @return static - */ - public function duplicatesStrict($callback = null) - { - return $this->duplicates($callback, true); - } - - /** - * Get the comparison function to detect duplicates. - * - * @param bool $strict - * @return \Closure - */ - protected function duplicateComparator($strict) - { - if ($strict) { - return function ($a, $b) { - return $a === $b; - }; - } - - return function ($a, $b) { - return $a == $b; - }; - } - - /** - * Get all items except for those with the specified keys. - * - * @param \Tightenco\Collect\Support\Collection|mixed $keys - * @return static - */ - public function except($keys) - { - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_array($keys)) { - $keys = func_get_args(); - } - - return new static(Arr::except($this->items, $keys)); - } - - /** - * Run a filter over each of the items. - * - * @param callable|null $callback - * @return static - */ - public function filter(callable $callback = null) - { - if ($callback) { - return new static(Arr::where($this->items, $callback)); - } - - return new static(array_filter($this->items)); - } - - /** - * Get the first item from the collection passing the given truth test. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public function first(callable $callback = null, $default = null) - { - return Arr::first($this->items, $callback, $default); - } - - /** - * Get a flattened array of the items in the collection. - * - * @param int $depth - * @return static - */ - public function flatten($depth = INF) - { - return new static(Arr::flatten($this->items, $depth)); - } - - /** - * Flip the items in the collection. - * - * @return static - */ - public function flip() - { - return new static(array_flip($this->items)); - } - - /** - * Remove an item from the collection by key. - * - * @param string|array $keys - * @return $this - */ - public function forget($keys) - { - foreach ((array) $keys as $key) { - $this->offsetUnset($key); - } - - return $this; - } - - /** - * Get an item from the collection by key. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function get($key, $default = null) - { - if ($this->offsetExists($key)) { - return $this->items[$key]; - } - - return value($default); - } - - /** - * Group an associative array by a field or using a callback. - * - * @param array|callable|string $groupBy - * @param bool $preserveKeys - * @return static - */ - public function groupBy($groupBy, $preserveKeys = false) - { - if (is_array($groupBy)) { - $nextGroups = $groupBy; - - $groupBy = array_shift($nextGroups); - } - - $groupBy = $this->valueRetriever($groupBy); - - $results = []; - - foreach ($this->items as $key => $value) { - $groupKeys = $groupBy($value, $key); - - if (! is_array($groupKeys)) { - $groupKeys = [$groupKeys]; - } - - foreach ($groupKeys as $groupKey) { - $groupKey = is_bool($groupKey) ? (int) $groupKey : $groupKey; - - if (! array_key_exists($groupKey, $results)) { - $results[$groupKey] = new static; - } - - $results[$groupKey]->offsetSet($preserveKeys ? $key : null, $value); - } - } - - $result = new static($results); - - if (! empty($nextGroups)) { - return $result->map->groupBy($nextGroups, $preserveKeys); - } - - return $result; - } - - /** - * Key an associative array by a field or using a callback. - * - * @param callable|string $keyBy - * @return static - */ - public function keyBy($keyBy) - { - $keyBy = $this->valueRetriever($keyBy); - - $results = []; - - foreach ($this->items as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if (is_object($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - $results[$resolvedKey] = $item; - } - - return new static($results); - } - - /** - * Determine if an item exists in the collection by key. - * - * @param mixed $key - * @return bool - */ - public function has($key) - { - $keys = is_array($key) ? $key : func_get_args(); - - foreach ($keys as $value) { - if (! $this->offsetExists($value)) { - return false; - } - } - - return true; - } - - /** - * Concatenate values of a given key as a string. - * - * @param string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null) - { - $first = $this->first(); - - if (is_array($first) || is_object($first)) { - return implode($glue, $this->pluck($value)->all()); - } - - return implode($value, $this->items); - } - - /** - * Intersect the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function intersect($items) - { - return new static(array_intersect($this->items, $this->getArrayableItems($items))); - } - - /** - * Intersect the collection with the given items by key. - * - * @param mixed $items - * @return static - */ - public function intersectByKeys($items) - { - return new static(array_intersect_key( - $this->items, $this->getArrayableItems($items) - )); - } - - /** - * Determine if the collection is empty or not. - * - * @return bool - */ - public function isEmpty() - { - return empty($this->items); - } - - /** - * Join all items from the collection using a string. The final items can use a separate glue string. - * - * @param string $glue - * @param string $finalGlue - * @return string - */ - public function join($glue, $finalGlue = '') - { - if ($finalGlue === '') { - return $this->implode($glue); - } - - $count = $this->count(); - - if ($count === 0) { - return ''; - } - - if ($count === 1) { - return $this->last(); - } - - $collection = new static($this->items); - - $finalItem = $collection->pop(); - - return $collection->implode($glue).$finalGlue.$finalItem; - } - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys() - { - return new static(array_keys($this->items)); - } - - /** - * Get the last item from the collection. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public function last(callable $callback = null, $default = null) - { - return Arr::last($this->items, $callback, $default); - } - - /** - * Get the values of a given key. - * - * @param string|array $value - * @param string|null $key - * @return static - */ - public function pluck($value, $key = null) - { - return new static(Arr::pluck($this->items, $value, $key)); - } - - /** - * Run a map over each of the items. - * - * @param callable $callback - * @return static - */ - public function map(callable $callback) - { - $keys = array_keys($this->items); - - $items = array_map($callback, $this->items, $keys); - - return new static(array_combine($keys, $items)); - } - - /** - * Run a dictionary map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $callback - * @return static - */ - public function mapToDictionary(callable $callback) - { - $dictionary = []; - - foreach ($this->items as $key => $item) { - $pair = $callback($item, $key); - - $key = key($pair); - - $value = reset($pair); - - if (! isset($dictionary[$key])) { - $dictionary[$key] = []; - } - - $dictionary[$key][] = $value; - } - - return new static($dictionary); - } - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $callback - * @return static - */ - public function mapWithKeys(callable $callback) - { - $result = []; - - foreach ($this->items as $key => $value) { - $assoc = $callback($value, $key); - - foreach ($assoc as $mapKey => $mapValue) { - $result[$mapKey] = $mapValue; - } - } - - return new static($result); - } - - /** - * Merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function merge($items) - { - return new static(array_merge($this->items, $this->getArrayableItems($items))); - } - - /** - * Recursively merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function mergeRecursive($items) - { - return new static(array_merge_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @param mixed $values - * @return static - */ - public function combine($values) - { - return new static(array_combine($this->all(), $this->getArrayableItems($values))); - } - - /** - * Union the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function union($items) - { - return new static($this->items + $this->getArrayableItems($items)); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return static - */ - public function nth($step, $offset = 0) - { - $new = []; - - $position = 0; - - foreach ($this->items as $item) { - if ($position % $step === $offset) { - $new[] = $item; - } - - $position++; - } - - return new static($new); - } - - /** - * Get the items with the specified keys. - * - * @param mixed $keys - * @return static - */ - public function only($keys) - { - if (is_null($keys)) { - return new static($this->items); - } - - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } - - $keys = is_array($keys) ? $keys : func_get_args(); - - return new static(Arr::only($this->items, $keys)); - } - - /** - * Get and remove the last item from the collection. - * - * @return mixed - */ - public function pop() - { - return array_pop($this->items); - } - - /** - * Push an item onto the beginning of the collection. - * - * @param mixed $value - * @param mixed $key - * @return $this - */ - public function prepend($value, $key = null) - { - $this->items = Arr::prepend($this->items, $value, $key); - - return $this; - } - - /** - * Push one or more items onto the end of the collection. - * - * @param mixed $values [optional] - * @return $this - */ - public function push(...$values) - { - foreach ($values as $value) { - $this->items[] = $value; - } - - return $this; - } - - /** - * Push all of the given items onto the collection. - * - * @param iterable $source - * @return static - */ - public function concat($source) - { - $result = new static($this); - - foreach ($source as $item) { - $result->push($item); - } - - return $result; - } - - /** - * Get and remove an item from the collection. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function pull($key, $default = null) - { - return Arr::pull($this->items, $key, $default); - } - - /** - * Put an item in the collection by key. - * - * @param mixed $key - * @param mixed $value - * @return $this - */ - public function put($key, $value) - { - $this->offsetSet($key, $value); - - return $this; - } - - /** - * Get one or a specified number of items randomly from the collection. - * - * @param int|null $number - * @return static|mixed - * - * @throws \InvalidArgumentException - */ - public function random($number = null) - { - if (is_null($number)) { - return Arr::random($this->items); - } - - return new static(Arr::random($this->items, $number)); - } - - /** - * Reduce the collection to a single value. - * - * @param callable $callback - * @param mixed $initial - * @return mixed - */ - public function reduce(callable $callback, $initial = null) - { - return array_reduce($this->items, $callback, $initial); - } - - /** - * Replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replace($items) - { - return new static(array_replace($this->items, $this->getArrayableItems($items))); - } - - /** - * Recursively replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replaceRecursive($items) - { - return new static(array_replace_recursive($this->items, $this->getArrayableItems($items))); - } - - /** - * Reverse items order. - * - * @return static - */ - public function reverse() - { - return new static(array_reverse($this->items, true)); - } - - /** - * Search the collection for a given value and return the corresponding key if successful. - * - * @param mixed $value - * @param bool $strict - * @return mixed - */ - public function search($value, $strict = false) - { - if (! $this->useAsCallable($value)) { - return array_search($value, $this->items, $strict); - } - - foreach ($this->items as $key => $item) { - if ($value($item, $key)) { - return $key; - } - } - - return false; - } - - /** - * Get and remove the first item from the collection. - * - * @return mixed - */ - public function shift() - { - return array_shift($this->items); - } - - /** - * Shuffle the items in the collection. - * - * @param int|null $seed - * @return static - */ - public function shuffle($seed = null) - { - return new static(Arr::shuffle($this->items, $seed)); - } - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count) - { - return $this->slice($count); - } - - /** - * Slice the underlying collection array. - * - * @param int $offset - * @param int|null $length - * @return static - */ - public function slice($offset, $length = null) - { - return new static(array_slice($this->items, $offset, $length, true)); - } - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return static - */ - public function split($numberOfGroups) - { - if ($this->isEmpty()) { - return new static; - } - - $groups = new static; - - $groupSize = floor($this->count() / $numberOfGroups); - - $remain = $this->count() % $numberOfGroups; - - $start = 0; - - for ($i = 0; $i < $numberOfGroups; $i++) { - $size = $groupSize; - - if ($i < $remain) { - $size++; - } - - if ($size) { - $groups->push(new static(array_slice($this->items, $start, $size))); - - $start += $size; - } - } - - return $groups; - } - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @return static - */ - public function chunk($size) - { - if ($size <= 0) { - return new static; - } - - $chunks = []; - - foreach (array_chunk($this->items, $size, true) as $chunk) { - $chunks[] = new static($chunk); - } - - return new static($chunks); - } - - /** - * Sort through each item with a callback. - * - * @param callable|null $callback - * @return static - */ - public function sort($callback = null) - { - $items = $this->items; - - $callback && is_callable($callback) - ? uasort($items, $callback) - : asort($items, $callback); - - return new static($items); - } - - /** - * Sort items in descending order. - * - * @param int $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR) - { - $items = $this->items; - - arsort($items, $options); - - return new static($items); - } - - /** - * Sort the collection using the given callback. - * - * @param callable|string $callback - * @param int $options - * @param bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - $results = []; - - $callback = $this->valueRetriever($callback); - - // First we will loop through the items and get the comparator from a callback - // function which we were given. Then, we will sort the returned values and - // and grab the corresponding values for the sorted keys from this array. - foreach ($this->items as $key => $value) { - $results[$key] = $callback($value, $key); - } - - $descending ? arsort($results, $options) - : asort($results, $options); - - // Once we have sorted all of the keys in the array, we will loop through them - // and grab the corresponding model so we can set the underlying items list - // to the sorted version. Then we'll just return the collection instance. - foreach (array_keys($results) as $key) { - $results[$key] = $this->items[$key]; - } - - return new static($results); - } - - /** - * Sort the collection in descending order using the given callback. - * - * @param callable|string $callback - * @param int $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR) - { - return $this->sortBy($callback, $options, true); - } - - /** - * Sort the collection keys. - * - * @param int $options - * @param bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - $items = $this->items; - - $descending ? krsort($items, $options) : ksort($items, $options); - - return new static($items); - } - - /** - * Sort the collection keys in descending order. - * - * @param int $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->sortKeys($options, true); - } - - /** - * Splice a portion of the underlying collection array. - * - * @param int $offset - * @param int|null $length - * @param mixed $replacement - * @return static - */ - public function splice($offset, $length = null, $replacement = []) - { - if (func_num_args() === 1) { - return new static(array_splice($this->items, $offset)); - } - - return new static(array_splice($this->items, $offset, $length, $replacement)); - } - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit) - { - if ($limit < 0) { - return $this->slice($limit, abs($limit)); - } - - return $this->slice(0, $limit); - } - - /** - * Transform each item in the collection using a callback. - * - * @param callable $callback - * @return $this - */ - public function transform(callable $callback) - { - $this->items = $this->map($callback)->all(); - - return $this; - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return new static(array_values($this->items)); - } - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new Collection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @param mixed ...$items - * @return static - */ - public function zip($items) - { - $arrayableItems = array_map(function ($items) { - return $this->getArrayableItems($items); - }, func_get_args()); - - $params = array_merge([function () { - return new static(func_get_args()); - }, $this->items], $arrayableItems); - - return new static(call_user_func_array('array_map', $params)); - } - - /** - * Pad collection to the specified length with a value. - * - * @param int $size - * @param mixed $value - * @return static - */ - public function pad($size, $value) - { - return new static(array_pad($this->items, $size, $value)); - } - - /** - * Get an iterator for the items. - * - * @return \ArrayIterator - */ - public function getIterator() - { - return new ArrayIterator($this->items); - } - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count() - { - return count($this->items); - } - - /** - * Add an item to the collection. - * - * @param mixed $item - * @return $this - */ - public function add($item) - { - $this->items[] = $item; - - return $this; - } - - /** - * Get a base Support collection instance from this collection. - * - * @return \Tightenco\Collect\Support\Collection - */ - public function toBase() - { - return new self($this); - } - - /** - * Determine if an item exists at an offset. - * - * @param mixed $key - * @return bool - */ - public function offsetExists($key) - { - return array_key_exists($key, $this->items); - } - - /** - * Get an item at a given offset. - * - * @param mixed $key - * @return mixed - */ - public function offsetGet($key) - { - return $this->items[$key]; - } - - /** - * Set the item at a given offset. - * - * @param mixed $key - * @param mixed $value - * @return void - */ - public function offsetSet($key, $value) - { - if (is_null($key)) { - $this->items[] = $value; - } else { - $this->items[$key] = $value; - } - } - - /** - * Unset the item at a given offset. - * - * @param string $key - * @return void - */ - public function offsetUnset($key) - { - unset($this->items[$key]); - } -} diff --git a/vendor/tightenco/collect/src/Collect/Support/Enumerable.php b/vendor/tightenco/collect/src/Collect/Support/Enumerable.php deleted file mode 100644 index 507929e..0000000 --- a/vendor/tightenco/collect/src/Collect/Support/Enumerable.php +++ /dev/null @@ -1,928 +0,0 @@ -html = $html; - } - - /** - * Get the HTML string. - * - * @return string - */ - public function toHtml() - { - return $this->html; - } - - /** - * Get the HTML string. - * - * @return string - */ - public function __toString() - { - return $this->toHtml(); - } -} diff --git a/vendor/tightenco/collect/src/Collect/Support/LazyCollection.php b/vendor/tightenco/collect/src/Collect/Support/LazyCollection.php deleted file mode 100644 index 5dcf197..0000000 --- a/vendor/tightenco/collect/src/Collect/Support/LazyCollection.php +++ /dev/null @@ -1,1274 +0,0 @@ -source = $source; - } elseif (is_null($source)) { - $this->source = static::empty(); - } else { - $this->source = $this->getArrayableItems($source); - } - } - - /** - * Create a new instance with no items. - * - * @return static - */ - public static function empty() - { - return new static([]); - } - - /** - * Create a new instance by invoking the callback a given amount of times. - * - * @param int $number - * @param callable|null $callback - * @return static - */ - public static function times($number, callable $callback = null) - { - if ($number < 1) { - return new static; - } - - $instance = new static(function () use ($number) { - for ($current = 1; $current <= $number; $current++) { - yield $current; - } - }); - - return is_null($callback) ? $instance : $instance->map($callback); - } - - /** - * Create an enumerable with the given range. - * - * @param int $from - * @param int $to - * @return static - */ - public static function range($from, $to) - { - return new static(function () use ($from, $to) { - for (; $from <= $to; $from++) { - yield $from; - } - }); - } - - /** - * Get all items in the enumerable. - * - * @return array - */ - public function all() - { - if (is_array($this->source)) { - return $this->source; - } - - return iterator_to_array($this->getIterator()); - } - - /** - * Eager load all items into a new lazy collection backed by an array. - * - * @return static - */ - public function eager() - { - return new static($this->all()); - } - - /** - * Cache values as they're enumerated. - * - * @return static - */ - public function remember() - { - $iterator = $this->getIterator(); - - $iteratorIndex = 0; - - $cache = []; - - return new static(function () use ($iterator, &$iteratorIndex, &$cache) { - for ($index = 0; true; $index++) { - if (array_key_exists($index, $cache)) { - yield $cache[$index][0] => $cache[$index][1]; - - continue; - } - - if ($iteratorIndex < $index) { - $iterator->next(); - - $iteratorIndex++; - } - - if (! $iterator->valid()) { - break; - } - - $cache[$index] = [$iterator->key(), $iterator->current()]; - - yield $cache[$index][0] => $cache[$index][1]; - } - }); - } - - /** - * Get the average value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function avg($callback = null) - { - return $this->collect()->avg($callback); - } - - /** - * Get the median of a given key. - * - * @param string|array|null $key - * @return mixed - */ - public function median($key = null) - { - return $this->collect()->median($key); - } - - /** - * Get the mode of a given key. - * - * @param string|array|null $key - * @return array|null - */ - public function mode($key = null) - { - return $this->collect()->mode($key); - } - - /** - * Collapse the collection of items into a single array. - * - * @return static - */ - public function collapse() - { - return new static(function () { - foreach ($this as $values) { - if (is_array($values) || $values instanceof Enumerable) { - foreach ($values as $value) { - yield $value; - } - } - } - }); - } - - /** - * Determine if an item exists in the enumerable. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function contains($key, $operator = null, $value = null) - { - if (func_num_args() === 1 && $this->useAsCallable($key)) { - $placeholder = new stdClass; - - return $this->first($key, $placeholder) !== $placeholder; - } - - if (func_num_args() === 1) { - $needle = $key; - - foreach ($this as $value) { - if ($value == $needle) { - return true; - } - } - - return false; - } - - return $this->contains($this->operatorForWhere(...func_get_args())); - } - - /** - * Cross join the given iterables, returning all possible permutations. - * - * @param array ...$arrays - * @return static - */ - public function crossJoin(...$arrays) - { - return $this->passthru('crossJoin', func_get_args()); - } - - /** - * Get the items that are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diff($items) - { - return $this->passthru('diff', func_get_args()); - } - - /** - * Get the items that are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffUsing($items, callable $callback) - { - return $this->passthru('diffUsing', func_get_args()); - } - - /** - * Get the items whose keys and values are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diffAssoc($items) - { - return $this->passthru('diffAssoc', func_get_args()); - } - - /** - * Get the items whose keys and values are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffAssocUsing($items, callable $callback) - { - return $this->passthru('diffAssocUsing', func_get_args()); - } - - /** - * Get the items whose keys are not present in the given items. - * - * @param mixed $items - * @return static - */ - public function diffKeys($items) - { - return $this->passthru('diffKeys', func_get_args()); - } - - /** - * Get the items whose keys are not present in the given items, using the callback. - * - * @param mixed $items - * @param callable $callback - * @return static - */ - public function diffKeysUsing($items, callable $callback) - { - return $this->passthru('diffKeysUsing', func_get_args()); - } - - /** - * Retrieve duplicate items. - * - * @param callable|null $callback - * @param bool $strict - * @return static - */ - public function duplicates($callback = null, $strict = false) - { - return $this->passthru('duplicates', func_get_args()); - } - - /** - * Retrieve duplicate items using strict comparison. - * - * @param callable|null $callback - * @return static - */ - public function duplicatesStrict($callback = null) - { - return $this->passthru('duplicatesStrict', func_get_args()); - } - - /** - * Get all items except for those with the specified keys. - * - * @param mixed $keys - * @return static - */ - public function except($keys) - { - return $this->passthru('except', func_get_args()); - } - - /** - * Run a filter over each of the items. - * - * @param callable|null $callback - * @return static - */ - public function filter(callable $callback = null) - { - if (is_null($callback)) { - $callback = function ($value) { - return (bool) $value; - }; - } - - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - if ($callback($value, $key)) { - yield $key => $value; - } - } - }); - } - - /** - * Get the first item from the enumerable passing the given truth test. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public function first(callable $callback = null, $default = null) - { - $iterator = $this->getIterator(); - - if (is_null($callback)) { - if (! $iterator->valid()) { - return value($default); - } - - return $iterator->current(); - } - - foreach ($iterator as $key => $value) { - if ($callback($value, $key)) { - return $value; - } - } - - return value($default); - } - - /** - * Get a flattened list of the items in the collection. - * - * @param int $depth - * @return static - */ - public function flatten($depth = INF) - { - $instance = new static(function () use ($depth) { - foreach ($this as $item) { - if (! is_array($item) && ! $item instanceof Enumerable) { - yield $item; - } elseif ($depth === 1) { - yield from $item; - } else { - yield from (new static($item))->flatten($depth - 1); - } - } - }); - - return $instance->values(); - } - - /** - * Flip the items in the collection. - * - * @return static - */ - public function flip() - { - return new static(function () { - foreach ($this as $key => $value) { - yield $value => $key; - } - }); - } - - /** - * Get an item by key. - * - * @param mixed $key - * @param mixed $default - * @return mixed - */ - public function get($key, $default = null) - { - if (is_null($key)) { - return; - } - - foreach ($this as $outerKey => $outerValue) { - if ($outerKey == $key) { - return $outerValue; - } - } - - return value($default); - } - - /** - * Group an associative array by a field or using a callback. - * - * @param array|callable|string $groupBy - * @param bool $preserveKeys - * @return static - */ - public function groupBy($groupBy, $preserveKeys = false) - { - return $this->passthru('groupBy', func_get_args()); - } - - /** - * Key an associative array by a field or using a callback. - * - * @param callable|string $keyBy - * @return static - */ - public function keyBy($keyBy) - { - return new static(function () use ($keyBy) { - $keyBy = $this->valueRetriever($keyBy); - - foreach ($this as $key => $item) { - $resolvedKey = $keyBy($item, $key); - - if (is_object($resolvedKey)) { - $resolvedKey = (string) $resolvedKey; - } - - yield $resolvedKey => $item; - } - }); - } - - /** - * Determine if an item exists in the collection by key. - * - * @param mixed $key - * @return bool - */ - public function has($key) - { - $keys = array_flip(is_array($key) ? $key : func_get_args()); - $count = count($keys); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $keys) && --$count == 0) { - return true; - } - } - - return false; - } - - /** - * Concatenate values of a given key as a string. - * - * @param string $value - * @param string|null $glue - * @return string - */ - public function implode($value, $glue = null) - { - return $this->collect()->implode(...func_get_args()); - } - - /** - * Intersect the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function intersect($items) - { - return $this->passthru('intersect', func_get_args()); - } - - /** - * Intersect the collection with the given items by key. - * - * @param mixed $items - * @return static - */ - public function intersectByKeys($items) - { - return $this->passthru('intersectByKeys', func_get_args()); - } - - /** - * Determine if the items is empty or not. - * - * @return bool - */ - public function isEmpty() - { - return ! $this->getIterator()->valid(); - } - - /** - * Join all items from the collection using a string. The final items can use a separate glue string. - * - * @param string $glue - * @param string $finalGlue - * @return string - */ - public function join($glue, $finalGlue = '') - { - return $this->collect()->join(...func_get_args()); - } - - /** - * Get the keys of the collection items. - * - * @return static - */ - public function keys() - { - return new static(function () { - foreach ($this as $key => $value) { - yield $key; - } - }); - } - - /** - * Get the last item from the collection. - * - * @param callable|null $callback - * @param mixed $default - * @return mixed - */ - public function last(callable $callback = null, $default = null) - { - $needle = $placeholder = new stdClass; - - foreach ($this as $key => $value) { - if (is_null($callback) || $callback($value, $key)) { - $needle = $value; - } - } - - return $needle === $placeholder ? value($default) : $needle; - } - - /** - * Get the values of a given key. - * - * @param string|array $value - * @param string|null $key - * @return static - */ - public function pluck($value, $key = null) - { - return new static(function () use ($value, $key) { - [$value, $key] = $this->explodePluckParameters($value, $key); - - foreach ($this as $item) { - $itemValue = data_get($item, $value); - - if (is_null($key)) { - yield $itemValue; - } else { - $itemKey = data_get($item, $key); - - if (is_object($itemKey) && method_exists($itemKey, '__toString')) { - $itemKey = (string) $itemKey; - } - - yield $itemKey => $itemValue; - } - } - }); - } - - /** - * Run a map over each of the items. - * - * @param callable $callback - * @return static - */ - public function map(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - yield $key => $callback($value, $key); - } - }); - } - - /** - * Run a dictionary map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $callback - * @return static - */ - public function mapToDictionary(callable $callback) - { - return $this->passthru('mapToDictionary', func_get_args()); - } - - /** - * Run an associative map over each of the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $callback - * @return static - */ - public function mapWithKeys(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - yield from $callback($value, $key); - } - }); - } - - /** - * Merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function merge($items) - { - return $this->passthru('merge', func_get_args()); - } - - /** - * Recursively merge the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function mergeRecursive($items) - { - return $this->passthru('mergeRecursive', func_get_args()); - } - - /** - * Create a collection by using this collection for keys and another for its values. - * - * @param mixed $values - * @return static - */ - public function combine($values) - { - return new static(function () use ($values) { - $values = $this->makeIterator($values); - - $errorMessage = 'Both parameters should have an equal number of elements'; - - foreach ($this as $key) { - if (! $values->valid()) { - trigger_error($errorMessage, E_USER_WARNING); - - break; - } - - yield $key => $values->current(); - - $values->next(); - } - - if ($values->valid()) { - trigger_error($errorMessage, E_USER_WARNING); - } - }); - } - - /** - * Union the collection with the given items. - * - * @param mixed $items - * @return static - */ - public function union($items) - { - return $this->passthru('union', func_get_args()); - } - - /** - * Create a new collection consisting of every n-th element. - * - * @param int $step - * @param int $offset - * @return static - */ - public function nth($step, $offset = 0) - { - return new static(function () use ($step, $offset) { - $position = 0; - - foreach ($this as $item) { - if ($position % $step === $offset) { - yield $item; - } - - $position++; - } - }); - } - - /** - * Get the items with the specified keys. - * - * @param mixed $keys - * @return static - */ - public function only($keys) - { - if ($keys instanceof Enumerable) { - $keys = $keys->all(); - } elseif (! is_null($keys)) { - $keys = is_array($keys) ? $keys : func_get_args(); - } - - return new static(function () use ($keys) { - if (is_null($keys)) { - yield from $this; - } else { - $keys = array_flip($keys); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $keys)) { - yield $key => $value; - - unset($keys[$key]); - - if (empty($keys)) { - break; - } - } - } - } - }); - } - - /** - * Push all of the given items onto the collection. - * - * @param iterable $source - * @return static - */ - public function concat($source) - { - return (new static(function () use ($source) { - yield from $this; - yield from $source; - }))->values(); - } - - /** - * Get one or a specified number of items randomly from the collection. - * - * @param int|null $number - * @return static|mixed - * - * @throws \InvalidArgumentException - */ - public function random($number = null) - { - $result = $this->collect()->random(...func_get_args()); - - return is_null($number) ? $result : new static($result); - } - - /** - * Reduce the collection to a single value. - * - * @param callable $callback - * @param mixed $initial - * @return mixed - */ - public function reduce(callable $callback, $initial = null) - { - $result = $initial; - - foreach ($this as $value) { - $result = $callback($result, $value); - } - - return $result; - } - - /** - * Replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replace($items) - { - return new static(function () use ($items) { - $items = $this->getArrayableItems($items); - - foreach ($this as $key => $value) { - if (array_key_exists($key, $items)) { - yield $key => $items[$key]; - - unset($items[$key]); - } else { - yield $key => $value; - } - } - - foreach ($items as $key => $value) { - yield $key => $value; - } - }); - } - - /** - * Recursively replace the collection items with the given items. - * - * @param mixed $items - * @return static - */ - public function replaceRecursive($items) - { - return $this->passthru('replaceRecursive', func_get_args()); - } - - /** - * Reverse items order. - * - * @return static - */ - public function reverse() - { - return $this->passthru('reverse', func_get_args()); - } - - /** - * Search the collection for a given value and return the corresponding key if successful. - * - * @param mixed $value - * @param bool $strict - * @return mixed - */ - public function search($value, $strict = false) - { - $predicate = $this->useAsCallable($value) - ? $value - : function ($item) use ($value, $strict) { - return $strict ? $item === $value : $item == $value; - }; - - foreach ($this as $key => $item) { - if ($predicate($item, $key)) { - return $key; - } - } - - return false; - } - - /** - * Shuffle the items in the collection. - * - * @param int|null $seed - * @return static - */ - public function shuffle($seed = null) - { - return $this->passthru('shuffle', func_get_args()); - } - - /** - * Skip the first {$count} items. - * - * @param int $count - * @return static - */ - public function skip($count) - { - return new static(function () use ($count) { - $iterator = $this->getIterator(); - - while ($iterator->valid() && $count--) { - $iterator->next(); - } - - while ($iterator->valid()) { - yield $iterator->key() => $iterator->current(); - - $iterator->next(); - } - }); - } - - /** - * Get a slice of items from the enumerable. - * - * @param int $offset - * @param int|null $length - * @return static - */ - public function slice($offset, $length = null) - { - if ($offset < 0 || $length < 0) { - return $this->passthru('slice', func_get_args()); - } - - $instance = $this->skip($offset); - - return is_null($length) ? $instance : $instance->take($length); - } - - /** - * Split a collection into a certain number of groups. - * - * @param int $numberOfGroups - * @return static - */ - public function split($numberOfGroups) - { - return $this->passthru('split', func_get_args()); - } - - /** - * Chunk the collection into chunks of the given size. - * - * @param int $size - * @return static - */ - public function chunk($size) - { - if ($size <= 0) { - return static::empty(); - } - - return new static(function () use ($size) { - $iterator = $this->getIterator(); - - while ($iterator->valid()) { - $chunk = []; - - while (true) { - $chunk[$iterator->key()] = $iterator->current(); - - if (count($chunk) < $size) { - $iterator->next(); - - if (! $iterator->valid()) { - break; - } - } else { - break; - } - } - - yield new static($chunk); - - $iterator->next(); - } - }); - } - - /** - * Sort through each item with a callback. - * - * @param callable|null|int $callback - * @return static - */ - public function sort($callback = null) - { - return $this->passthru('sort', func_get_args()); - } - - /** - * Sort items in descending order. - * - * @param int $options - * @return static - */ - public function sortDesc($options = SORT_REGULAR) - { - return $this->passthru('sortDesc', func_get_args()); - } - - /** - * Sort the collection using the given callback. - * - * @param callable|string $callback - * @param int $options - * @param bool $descending - * @return static - */ - public function sortBy($callback, $options = SORT_REGULAR, $descending = false) - { - return $this->passthru('sortBy', func_get_args()); - } - - /** - * Sort the collection in descending order using the given callback. - * - * @param callable|string $callback - * @param int $options - * @return static - */ - public function sortByDesc($callback, $options = SORT_REGULAR) - { - return $this->passthru('sortByDesc', func_get_args()); - } - - /** - * Sort the collection keys. - * - * @param int $options - * @param bool $descending - * @return static - */ - public function sortKeys($options = SORT_REGULAR, $descending = false) - { - return $this->passthru('sortKeys', func_get_args()); - } - - /** - * Sort the collection keys in descending order. - * - * @param int $options - * @return static - */ - public function sortKeysDesc($options = SORT_REGULAR) - { - return $this->passthru('sortKeysDesc', func_get_args()); - } - - /** - * Take the first or last {$limit} items. - * - * @param int $limit - * @return static - */ - public function take($limit) - { - if ($limit < 0) { - return $this->passthru('take', func_get_args()); - } - - return new static(function () use ($limit) { - $iterator = $this->getIterator(); - - while ($limit--) { - if (! $iterator->valid()) { - break; - } - - yield $iterator->key() => $iterator->current(); - - if ($limit) { - $iterator->next(); - } - } - }); - } - - /** - * Pass each item in the collection to the given callback, lazily. - * - * @param callable $callback - * @return static - */ - public function tapEach(callable $callback) - { - return new static(function () use ($callback) { - foreach ($this as $key => $value) { - $callback($value, $key); - - yield $key => $value; - } - }); - } - - /** - * Reset the keys on the underlying array. - * - * @return static - */ - public function values() - { - return new static(function () { - foreach ($this as $item) { - yield $item; - } - }); - } - - /** - * Zip the collection together with one or more arrays. - * - * e.g. new LazyCollection([1, 2, 3])->zip([4, 5, 6]); - * => [[1, 4], [2, 5], [3, 6]] - * - * @param mixed ...$items - * @return static - */ - public function zip($items) - { - $iterables = func_get_args(); - - return new static(function () use ($iterables) { - $iterators = Collection::make($iterables)->map(function ($iterable) { - return $this->makeIterator($iterable); - })->prepend($this->getIterator()); - - while ($iterators->contains->valid()) { - yield new static($iterators->map->current()); - - $iterators->each->next(); - } - }); - } - - /** - * Pad collection to the specified length with a value. - * - * @param int $size - * @param mixed $value - * @return static - */ - public function pad($size, $value) - { - if ($size < 0) { - return $this->passthru('pad', func_get_args()); - } - - return new static(function () use ($size, $value) { - $yielded = 0; - - foreach ($this as $index => $item) { - yield $index => $item; - - $yielded++; - } - - while ($yielded++ < $size) { - yield $value; - } - }); - } - - /** - * Get the values iterator. - * - * @return \Traversable - */ - public function getIterator() - { - return $this->makeIterator($this->source); - } - - /** - * Count the number of items in the collection. - * - * @return int - */ - public function count() - { - if (is_array($this->source)) { - return count($this->source); - } - - return iterator_count($this->getIterator()); - } - - /** - * Make an iterator from the given source. - * - * @param mixed $source - * @return \Traversable - */ - protected function makeIterator($source) - { - if ($source instanceof IteratorAggregate) { - return $source->getIterator(); - } - - if (is_array($source)) { - return new ArrayIterator($source); - } - - return $source(); - } - - /** - * Explode the "value" and "key" arguments passed to "pluck". - * - * @param string|array $value - * @param string|array|null $key - * @return array - */ - protected function explodePluckParameters($value, $key) - { - $value = is_string($value) ? explode('.', $value) : $value; - - $key = is_null($key) || is_array($key) ? $key : explode('.', $key); - - return [$value, $key]; - } - - /** - * Pass this lazy collection through a method on the collection class. - * - * @param string $method - * @param array $params - * @return static - */ - protected function passthru($method, array $params) - { - return new static(function () use ($method, $params) { - yield from $this->collect()->$method(...$params); - }); - } -} diff --git a/vendor/tightenco/collect/src/Collect/Support/Traits/EnumeratesValues.php b/vendor/tightenco/collect/src/Collect/Support/Traits/EnumeratesValues.php deleted file mode 100644 index e8b0ee9..0000000 --- a/vendor/tightenco/collect/src/Collect/Support/Traits/EnumeratesValues.php +++ /dev/null @@ -1,928 +0,0 @@ -all() : $value; - } - - /** - * Alias for the "avg" method. - * - * @param callable|string|null $callback - * @return mixed - */ - public function average($callback = null) - { - return $this->avg($callback); - } - - /** - * Alias for the "contains" method. - * - * @param mixed $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function some($key, $operator = null, $value = null) - { - return $this->contains(...func_get_args()); - } - - /** - * Determine if an item exists, using strict comparison. - * - * @param mixed $key - * @param mixed $value - * @return bool - */ - public function containsStrict($key, $value = null) - { - if (func_num_args() === 2) { - return $this->contains(function ($item) use ($key, $value) { - return data_get($item, $key) === $value; - }); - } - - if ($this->useAsCallable($key)) { - return ! is_null($this->first($key)); - } - - foreach ($this as $item) { - if ($item === $key) { - return true; - } - } - - return false; - } - - /** - * Dump the items and end the script. - * - * @param mixed ...$args - * @return void - */ - public function dd(...$args) - { - call_user_func_array([$this, 'dump'], $args); - - die(1); - } - - /** - * Dump the items. - * - * @return $this - */ - public function dump() - { - (new static(func_get_args())) - ->push($this) - ->each(function ($item) { - VarDumper::dump($item); - }); - - return $this; - } - - /** - * Execute a callback over each item. - * - * @param callable $callback - * @return $this - */ - public function each(callable $callback) - { - foreach ($this as $key => $item) { - if ($callback($item, $key) === false) { - break; - } - } - - return $this; - } - - /** - * Execute a callback over each nested chunk of items. - * - * @param callable $callback - * @return static - */ - public function eachSpread(callable $callback) - { - return $this->each(function ($chunk, $key) use ($callback) { - $chunk[] = $key; - - return $callback(...$chunk); - }); - } - - /** - * Determine if all items pass the given truth test. - * - * @param string|callable $key - * @param mixed $operator - * @param mixed $value - * @return bool - */ - public function every($key, $operator = null, $value = null) - { - if (func_num_args() === 1) { - $callback = $this->valueRetriever($key); - - foreach ($this as $k => $v) { - if (! $callback($v, $k)) { - return false; - } - } - - return true; - } - - return $this->every($this->operatorForWhere(...func_get_args())); - } - - /** - * Get the first item by the given key value pair. - * - * @param string $key - * @param mixed $operator - * @param mixed $value - * @return mixed - */ - public function firstWhere($key, $operator = null, $value = null) - { - return $this->first($this->operatorForWhere(...func_get_args())); - } - - /** - * Determine if the collection is not empty. - * - * @return bool - */ - public function isNotEmpty() - { - return ! $this->isEmpty(); - } - - /** - * Run a map over each nested chunk of items. - * - * @param callable $callback - * @return static - */ - public function mapSpread(callable $callback) - { - return $this->map(function ($chunk, $key) use ($callback) { - $chunk[] = $key; - - return $callback(...$chunk); - }); - } - - /** - * Run a grouping map over the items. - * - * The callback should return an associative array with a single key/value pair. - * - * @param callable $callback - * @return static - */ - public function mapToGroups(callable $callback) - { - $groups = $this->mapToDictionary($callback); - - return $groups->map([$this, 'make']); - } - - /** - * Map a collection and flatten the result by a single level. - * - * @param callable $callback - * @return static - */ - public function flatMap(callable $callback) - { - return $this->map($callback)->collapse(); - } - - /** - * Map the values into a new class. - * - * @param string $class - * @return static - */ - public function mapInto($class) - { - return $this->map(function ($value, $key) use ($class) { - return new $class($value, $key); - }); - } - - /** - * Get the min value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function min($callback = null) - { - $callback = $this->valueRetriever($callback); - - return $this->map(function ($value) use ($callback) { - return $callback($value); - })->filter(function ($value) { - return ! is_null($value); - })->reduce(function ($result, $value) { - return is_null($result) || $value < $result ? $value : $result; - }); - } - - /** - * Get the max value of a given key. - * - * @param callable|string|null $callback - * @return mixed - */ - public function max($callback = null) - { - $callback = $this->valueRetriever($callback); - - return $this->filter(function ($value) { - return ! is_null($value); - })->reduce(function ($result, $item) use ($callback) { - $value = $callback($item); - - return is_null($result) || $value > $result ? $value : $result; - }); - } - - /** - * "Paginate" the collection by slicing it into a smaller collection. - * - * @param int $page - * @param int $perPage - * @return static - */ - public function forPage($page, $perPage) - { - $offset = max(0, ($page - 1) * $perPage); - - return $this->slice($offset, $perPage); - } - - /** - * Partition the collection into two arrays using the given callback or key. - * - * @param callable|string $key - * @param mixed $operator - * @param mixed $value - * @return static - */ - public function partition($key, $operator = null, $value = null) - { - $passed = []; - $failed = []; - - $callback = func_num_args() === 1 - ? $this->valueRetriever($key) - : $this->operatorForWhere(...func_get_args()); - - foreach ($this as $key => $item) { - if ($callback($item, $key)) { - $passed[$key] = $item; - } else { - $failed[$key] = $item; - } - } - - return new static([new static($passed), new static($failed)]); - } - - /** - * Get the sum of the given values. - * - * @param callable|string|null $callback - * @return mixed - */ - public function sum($callback = null) - { - if (is_null($callback)) { - $callback = function ($value) { - return $value; - }; - } else { - $callback = $this->valueRetriever($callback); - } - - return $this->reduce(function ($result, $item) use ($callback) { - return $result + $callback($item); - }, 0); - } - - /** - * Apply the callback if the value is truthy. - * - * @param bool|mixed $value - * @param callable|null $callback - * @param callable|null $default - * @return static|mixed - */ - public function when($value, callable $callback = null, callable $default = null) - { - if (! $callback) { - return new HigherOrderWhenProxy($this, $value); - } - - if ($value) { - return $callback($this, $value); - } elseif ($default) { - return $default($this, $value); - } - - return $this; - } - - /** - * Apply the callback if the collection is empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function whenEmpty(callable $callback, callable $default = null) - { - return $this->when($this->isEmpty(), $callback, $default); - } - - /** - * Apply the callback if the collection is not empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function whenNotEmpty(callable $callback, callable $default = null) - { - return $this->when($this->isNotEmpty(), $callback, $default); - } - - /** - * Apply the callback if the value is falsy. - * - * @param bool $value - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function unless($value, callable $callback, callable $default = null) - { - return $this->when(! $value, $callback, $default); - } - - /** - * Apply the callback unless the collection is empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function unlessEmpty(callable $callback, callable $default = null) - { - return $this->whenNotEmpty($callback, $default); - } - - /** - * Apply the callback unless the collection is not empty. - * - * @param callable $callback - * @param callable|null $default - * @return static|mixed - */ - public function unlessNotEmpty(callable $callback, callable $default = null) - { - return $this->whenEmpty($callback, $default); - } - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param mixed $operator - * @param mixed $value - * @return static - */ - public function where($key, $operator = null, $value = null) - { - return $this->filter($this->operatorForWhere(...func_get_args())); - } - - /** - * Filter items where the given key is not null. - * - * @param string|null $key - * @return static - */ - public function whereNull($key = null) - { - return $this->whereStrict($key, null); - } - - /** - * Filter items where the given key is null. - * - * @param string|null $key - * @return static - */ - public function whereNotNull($key = null) - { - return $this->where($key, '!==', null); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param mixed $value - * @return static - */ - public function whereStrict($key, $value) - { - return $this->where($key, '===', $value); - } - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param mixed $values - * @param bool $strict - * @return static - */ - public function whereIn($key, $values, $strict = false) - { - $values = $this->getArrayableItems($values); - - return $this->filter(function ($item) use ($key, $values, $strict) { - return in_array(data_get($item, $key), $values, $strict); - }); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param mixed $values - * @return static - */ - public function whereInStrict($key, $values) - { - return $this->whereIn($key, $values, true); - } - - /** - * Filter items such that the value of the given key is between the given values. - * - * @param string $key - * @param array $values - * @return static - */ - public function whereBetween($key, $values) - { - return $this->where($key, '>=', reset($values))->where($key, '<=', end($values)); - } - - /** - * Filter items such that the value of the given key is not between the given values. - * - * @param string $key - * @param array $values - * @return static - */ - public function whereNotBetween($key, $values) - { - return $this->filter(function ($item) use ($key, $values) { - return data_get($item, $key) < reset($values) || data_get($item, $key) > end($values); - }); - } - - /** - * Filter items by the given key value pair. - * - * @param string $key - * @param mixed $values - * @param bool $strict - * @return static - */ - public function whereNotIn($key, $values, $strict = false) - { - $values = $this->getArrayableItems($values); - - return $this->reject(function ($item) use ($key, $values, $strict) { - return in_array(data_get($item, $key), $values, $strict); - }); - } - - /** - * Filter items by the given key value pair using strict comparison. - * - * @param string $key - * @param mixed $values - * @return static - */ - public function whereNotInStrict($key, $values) - { - return $this->whereNotIn($key, $values, true); - } - - /** - * Filter the items, removing any items that don't match the given type. - * - * @param string $type - * @return static - */ - public function whereInstanceOf($type) - { - return $this->filter(function ($value) use ($type) { - return $value instanceof $type; - }); - } - - /** - * Pass the collection to the given callback and return the result. - * - * @param callable $callback - * @return mixed - */ - public function pipe(callable $callback) - { - return $callback($this); - } - - /** - * Pass the collection to the given callback and then return it. - * - * @param callable $callback - * @return $this - */ - public function tap(callable $callback) - { - $callback(clone $this); - - return $this; - } - - /** - * Create a collection of all elements that do not pass a given truth test. - * - * @param callable|mixed $callback - * @return static - */ - public function reject($callback = true) - { - $useAsCallable = $this->useAsCallable($callback); - - return $this->filter(function ($value, $key) use ($callback, $useAsCallable) { - return $useAsCallable - ? ! $callback($value, $key) - : $value != $callback; - }); - } - - /** - * Return only unique items from the collection array. - * - * @param string|callable|null $key - * @param bool $strict - * @return static - */ - public function unique($key = null, $strict = false) - { - $callback = $this->valueRetriever($key); - - $exists = []; - - return $this->reject(function ($item, $key) use ($callback, $strict, &$exists) { - if (in_array($id = $callback($item, $key), $exists, $strict)) { - return true; - } - - $exists[] = $id; - }); - } - - /** - * Return only unique items from the collection array using strict comparison. - * - * @param string|callable|null $key - * @return static - */ - public function uniqueStrict($key = null) - { - return $this->unique($key, true); - } - - /** - * Collect the values into a collection. - * - * @return \Tightenco\Collect\Support\Collection - */ - public function collect() - { - return new Collection($this->all()); - } - - /** - * Get the collection of items as a plain array. - * - * @return array - */ - public function toArray() - { - return $this->map(function ($value) { - return $value instanceof Arrayable ? $value->toArray() : $value; - })->all(); - } - - /** - * Convert the object into something JSON serializable. - * - * @return array - */ - public function jsonSerialize() - { - return array_map(function ($value) { - if ($value instanceof JsonSerializable) { - return $value->jsonSerialize(); - } elseif ($value instanceof Jsonable) { - return json_decode($value->toJson(), true); - } elseif ($value instanceof Arrayable) { - return $value->toArray(); - } - - return $value; - }, $this->all()); - } - - /** - * Get the collection of items as JSON. - * - * @param int $options - * @return string - */ - public function toJson($options = 0) - { - return json_encode($this->jsonSerialize(), $options); - } - - /** - * Get a CachingIterator instance. - * - * @param int $flags - * @return \CachingIterator - */ - public function getCachingIterator($flags = CachingIterator::CALL_TOSTRING) - { - return new CachingIterator($this->getIterator(), $flags); - } - - /** - * Count the number of items in the collection using a given truth test. - * - * @param callable|null $callback - * @return static - */ - public function countBy($callback = null) - { - if (is_null($callback)) { - $callback = function ($value) { - return $value; - }; - } - - return new static($this->groupBy($callback)->map(function ($value) { - return $value->count(); - })); - } - - /** - * Convert the collection to its string representation. - * - * @return string - */ - public function __toString() - { - return $this->toJson(); - } - - /** - * Add a method to the list of proxied methods. - * - * @param string $method - * @return void - */ - public static function proxy($method) - { - static::$proxies[] = $method; - } - - /** - * Dynamically access collection proxies. - * - * @param string $key - * @return mixed - * - * @throws \Exception - */ - public function __get($key) - { - if (! in_array($key, static::$proxies)) { - throw new Exception("Property [{$key}] does not exist on this collection instance."); - } - - return new HigherOrderCollectionProxy($this, $key); - } - - /** - * Results array of items from Collection or Arrayable. - * - * @param mixed $items - * @return array - */ - protected function getArrayableItems($items) - { - if (is_array($items)) { - return $items; - } elseif ($items instanceof Enumerable) { - return $items->all(); - } elseif ($items instanceof Arrayable) { - return $items->toArray(); - } elseif ($items instanceof Jsonable) { - return json_decode($items->toJson(), true); - } elseif ($items instanceof JsonSerializable) { - return (array) $items->jsonSerialize(); - } elseif ($items instanceof Traversable) { - return iterator_to_array($items); - } - - return (array) $items; - } - - /** - * Get an operator checker callback. - * - * @param string $key - * @param string|null $operator - * @param mixed $value - * @return \Closure - */ - protected function operatorForWhere($key, $operator = null, $value = null) - { - if (func_num_args() === 1) { - $value = true; - - $operator = '='; - } - - if (func_num_args() === 2) { - $value = $operator; - - $operator = '='; - } - - return function ($item) use ($key, $operator, $value) { - $retrieved = data_get($item, $key); - - $strings = array_filter([$retrieved, $value], function ($value) { - return is_string($value) || (is_object($value) && method_exists($value, '__toString')); - }); - - if (count($strings) < 2 && count(array_filter([$retrieved, $value], 'is_object')) == 1) { - return in_array($operator, ['!=', '<>', '!==']); - } - - switch ($operator) { - default: - case '=': - case '==': return $retrieved == $value; - case '!=': - case '<>': return $retrieved != $value; - case '<': return $retrieved < $value; - case '>': return $retrieved > $value; - case '<=': return $retrieved <= $value; - case '>=': return $retrieved >= $value; - case '===': return $retrieved === $value; - case '!==': return $retrieved !== $value; - } - }; - } - - /** - * Determine if the given value is callable, but not a string. - * - * @param mixed $value - * @return bool - */ - protected function useAsCallable($value) - { - return ! is_string($value) && is_callable($value); - } - - /** - * Get a value retrieving callback. - * - * @param callable|string|null $value - * @return callable - */ - protected function valueRetriever($value) - { - if ($this->useAsCallable($value)) { - return $value; - } - - return function ($item) use ($value) { - return data_get($item, $value); - }; - } -} diff --git a/vendor/tightenco/collect/src/Collect/Support/alias.php b/vendor/tightenco/collect/src/Collect/Support/alias.php deleted file mode 100644 index d31121c..0000000 --- a/vendor/tightenco/collect/src/Collect/Support/alias.php +++ /dev/null @@ -1,21 +0,0 @@ - Illuminate\Contracts\Support\Arrayable::class, - Tightenco\Collect\Contracts\Support\Jsonable::class => Illuminate\Contracts\Support\Jsonable::class, - Tightenco\Collect\Contracts\Support\Htmlable::class => Illuminate\Contracts\Support\Htmlable::class, - Tightenco\Collect\Support\Collection::class => Illuminate\Support\Collection::class, - Tightenco\Collect\Support\LazyCollection::class => Illuminate\Support\LazyCollection::class, - Tightenco\Collect\Support\Arr::class => Illuminate\Support\Arr::class, - Tightenco\Collect\Support\HigherOrderCollectionProxy::class => Illuminate\Support\HigherOrderCollectionProxy::class, - Tightenco\Collect\Support\HtmlString::class => Illuminate\Support\HtmlString::class, - Tightenco\Collect\Support\Enumerable::class => Illuminate\Support\Enumerable::class, - Tightenco\Collect\Support\Traits\Macroable::class => Illuminate\Support\Traits\Macroable::class, - Tightenco\Collect\Support\Traits\EnumeratesValues::class => Illuminate\Support\Traits\EnumeratesValues::class, -]; - -foreach ($aliases as $tighten => $illuminate) { - if (! class_exists($illuminate) && ! interface_exists($illuminate) && ! trait_exists($illuminate)) { - class_alias($tighten, $illuminate); - } -} diff --git a/vendor/tightenco/collect/src/Collect/Support/helpers.php b/vendor/tightenco/collect/src/Collect/Support/helpers.php deleted file mode 100755 index 893d1cd..0000000 --- a/vendor/tightenco/collect/src/Collect/Support/helpers.php +++ /dev/null @@ -1,118 +0,0 @@ -all(); - } elseif (! is_array($target)) { - return value($default); - } - - $result = Arr::pluck($target, $key); - - return in_array('*', $key) ? Arr::collapse($result) : $result; - } - - if (Arr::accessible($target) && Arr::exists($target, $segment)) { - $target = $target[$segment]; - } elseif (is_object($target) && isset($target->{$segment})) { - $target = $target->{$segment}; - } else { - return value($default); - } - } - - return $target; - } - } - - if (! function_exists('with')) { - /** - * Return the given object. Useful for chaining. - * - * @param mixed $object - * @return mixed - */ - function with($object) - { - return $object; - } - } - - if (! function_exists('dd')) { - /** - * Dump the passed variables and end the script. - * - * @param mixed - * @return void - */ - function dd(...$args) - { - foreach ($args as $x) { - VarDumper::dump($x); - } - die(1); - } - } -} diff --git a/vendor/timber/timber/CHANGELOG.md b/vendor/timber/timber/CHANGELOG.md new file mode 100644 index 0000000..e05d381 --- /dev/null +++ b/vendor/timber/timber/CHANGELOG.md @@ -0,0 +1,135 @@ +# Changelog + +## [2.3.2](https://github.com/timber/timber/compare/v2.3.1...v2.3.2) (2025-05-13) + + +### Bug Fixes + +* Fix MenuItem::is_external() returning false positives for relative URLs ([#3089](https://github.com/timber/timber/issues/3089)) ([2a14525](https://github.com/timber/timber/commit/2a145250d3ad2ea88f7fdabc20a649720e5e3cec)) +* Fix typos in source code([#3077](https://github.com/timber/timber/issues/3077)) ([d7b3b80](https://github.com/timber/timber/commit/d7b3b804c3244083f6ae60e9f760f86aa512b054)) +* **security:** Bump minimum required Twig version to fix security issue in Twig ([#3104](https://github.com/timber/timber/issues/3104)) ([9766a9c](https://github.com/timber/timber/commit/9766a9c1ac58b82dc2433536ab2a1a8442bc3ffa)) + + +### Miscellaneous Chores + +* **deps:** bump lycheeverse/lychee-action from 2.0.2 to 2.2.0 ([#3078](https://github.com/timber/timber/issues/3078)) ([11a74ba](https://github.com/timber/timber/commit/11a74ba68cd05a109eff14d6fcf19119743626d9)) +* **deps:** bump tj-actions/changed-files from 45 to 46 ([#3105](https://github.com/timber/timber/issues/3105)) ([d8535cf](https://github.com/timber/timber/commit/d8535cf693a5bbdae55b1396b2fa24471dad22d9)) + +## [2.3.1](https://github.com/timber/timber/compare/v2.3.0...v2.3.1) (2024-12-18) + + +### Bug Fixes + +* fix avatar test ([#3071](https://github.com/timber/timber/issues/3071)) ([0e65e54](https://github.com/timber/timber/commit/0e65e54897fead31d3ba5eb3065242e294dcf51b)) +* Fix bug with Attachment::path() method ([#3073](https://github.com/timber/timber/issues/3073)) ([5434dde](https://github.com/timber/timber/commit/5434dde5889f174bf1d36c0686b94b180d93fa5d)) +* fix get location by id in Timber::get_menu_location() ([#3066](https://github.com/timber/timber/issues/3066)) ([5b33ba8](https://github.com/timber/timber/commit/5b33ba8475361e1e31974ee42a7e9a27e34e8b65)) +* timber::get_menu(0) returns alphabetically first menu instead of nothing ([#3070](https://github.com/timber/timber/issues/3070)) ([d278f95](https://github.com/timber/timber/commit/d278f954f672c0f3bb56e0a40e5d0acf40fc0608)) +* update twig & twig/cache-extra dependency to version 3.17 to fix unit tests ([cbac2e0](https://github.com/timber/timber/commit/cbac2e0fcf0b01c3bc3eaaf7de01bc721003b926)) +* Use correct deprecation_info for Twig callables ([#3064](https://github.com/timber/timber/issues/3064)) ([72a013e](https://github.com/timber/timber/commit/72a013e604ea098cb2819906a7be3454f4a3802d)) + +## [2.3.0](https://github.com/timber/timber/compare/v2.2.0...v2.3.0) (2024-11-08) + + +### Features + +* Add support for avif image format [#3015](https://github.com/timber/timber/issues/3015) ([#3019](https://github.com/timber/timber/issues/3019)) ([92716c1](https://github.com/timber/timber/commit/92716c1b2a9ecee090df9bebfcfcf5acf3192fc5)) + + +### Bug Fixes + +* add more default arguments to PagesMenu::build method ([#3050](https://github.com/timber/timber/issues/3050)) ([c7aea5d](https://github.com/timber/timber/commit/c7aea5d9b800836bfa51ef11f2b7493d5a8ce91b)) +* Apply Rector code standard on MenuItem.php ([5d64d9a](https://github.com/timber/timber/commit/5d64d9a390664de0e32aa51a7c69c5c4964f9559)) +* Fix menu location compatibility with WPML ([#2733](https://github.com/timber/timber/issues/2733)) ([8603855](https://github.com/timber/timber/commit/86038557c683fa65e0564e078c600ea2fc3ea446)) +* Fix URI to FS parsing in ImageHelper ([#3027](https://github.com/timber/timber/issues/3027)) ([87d3ef4](https://github.com/timber/timber/commit/87d3ef4e81f55ddb783ad6eb7da4c96ca9c643aa)), closes [#3024](https://github.com/timber/timber/issues/3024) +* fixes an issue where in some cases images would not be rouned properly by image operations. This could lead to artifacts in the generated images. ([#3046](https://github.com/timber/timber/issues/3046)) ([10ab23d](https://github.com/timber/timber/commit/10ab23d5cfcd1b1e777a5f4a65f8e983e272b73d)) +* Run CS fixes on codebase ([#3047](https://github.com/timber/timber/issues/3047)) ([48dc3fc](https://github.com/timber/timber/commit/48dc3fc5a9104251f440af6b65f6a622660a91dc)) + + +### Miscellaneous Chores + +* add several files to export-ignore ([0cd0cdf](https://github.com/timber/timber/commit/0cd0cdf3e09438f54b8e65bc408b08a98e42cdd7)) +* **deps:** bump lycheeverse/lychee-action from 1.10.0 to 2.0.2 ([#3053](https://github.com/timber/timber/issues/3053)) ([480534f](https://github.com/timber/timber/commit/480534fc95cf7d0b92af0ffc1f64805a352406ea)) +* **deps:** bump tj-actions/changed-files from 44 to 45 ([#3031](https://github.com/timber/timber/issues/3031)) ([880c0ff](https://github.com/timber/timber/commit/880c0ff23df5e7952cc6499d0043996a4d2c89bf)) +* inherit Funding from .github repo ([5623a79](https://github.com/timber/timber/commit/5623a797483542f496df0c3002cc211d9838960e)) + +## [2.2.0](https://github.com/timber/timber/compare/v2.1.0...v2.2.0) (2024-05-15) + + +### Features + +* Introduce Rector to upgrade code for PHP 8.1 ([#2977](https://github.com/timber/timber/issues/2977)) ([9edf999](https://github.com/timber/timber/commit/9edf999a6d4a12f6a0e96ffaaa38b3e48dc3ea2f)) +* Upgrade Timber requirements and testing (PHP 8.1/WP 6.2/Twig 3.5) ([#2970](https://github.com/timber/timber/issues/2970)) ([a2f0f07](https://github.com/timber/timber/commit/a2f0f07e9423f66c1998b3aabccfc2d803512c33)) + + +### Bug Fixes + +* allow Timber\PostExcerpt::read_more to accept bool value ([#2937](https://github.com/timber/timber/issues/2937)) ([85e2a32](https://github.com/timber/timber/commit/85e2a32e79616f937a19f1521c1378755c0e5014)) +* Fix a bug with URL check for avatars ([#3002](https://github.com/timber/timber/issues/3002)) ([456c24e](https://github.com/timber/timber/commit/456c24e7a438569d9e7fefd351f4f68cd3f7394d)) +* Fix deprecation notice since twig 3.10 to now use EscaperRuntime instead of EscaperExtension ([#2997](https://github.com/timber/timber/issues/2997)) ([295349b](https://github.com/timber/timber/commit/295349b0316640014a0841acef0f185bbdb8bd2e)) +* Fix problem when an empty ACF taxonomy relationship field transform loads all terms instead of none. ([#2960](https://github.com/timber/timber/issues/2960)) ([f95b82a](https://github.com/timber/timber/commit/f95b82af7cc8fa79ef8e10a75dbf62477b073ada)) +* fix regression where crops with the default crop setting would s… ([#2998](https://github.com/timber/timber/issues/2998)) ([8090247](https://github.com/timber/timber/commit/809024798d720fc743fac807431144605bb1cea3)) +* Fix typos in codebase ([#2968](https://github.com/timber/timber/issues/2968)) ([e40ceb3](https://github.com/timber/timber/commit/e40ceb3a72c7decaa597f6e2cdb27b4d1f3f5420)) +* Improve doing_it_wrong messages for using deprecated parameters in Timber::get_attachment() and Timber::get_image() ([#2999](https://github.com/timber/timber/issues/2999)) ([e6cdf7e](https://github.com/timber/timber/commit/e6cdf7ef584f43de585d0b437cb250179d1a0045)) +* Remove security patch not needed in PHP 8 ([#2983](https://github.com/timber/timber/issues/2983)) ([8a30865](https://github.com/timber/timber/commit/8a30865b753b51771b524cf8745f5ee362a7de85)) +* Update admin notice for minimum required WordPress version ([#3001](https://github.com/timber/timber/issues/3001)) ([66e92a5](https://github.com/timber/timber/commit/66e92a526622afeb3eba3da52f47db2b8ae6735e)) + + +### Miscellaneous Chores + +* **deps:** bump lycheeverse/lychee-action from 1.9.3 to 1.10.0 ([#2980](https://github.com/timber/timber/issues/2980)) ([dd34720](https://github.com/timber/timber/commit/dd3472030a25ee59f760abe95c48c5fabcf54abb)) +* **deps:** bump tj-actions/changed-files from 42 to 44 ([#2959](https://github.com/timber/timber/issues/2959)) ([66eabe2](https://github.com/timber/timber/commit/66eabe28a32b40d9eadaae6864c6bf7c3f8144c4)) +* set proper return types on build methods ([#2976](https://github.com/timber/timber/issues/2976)) ([6b72908](https://github.com/timber/timber/commit/6b72908d473188aa756d0b8ebb6641fae747e0b4)) +* Update all links in the codebase and documentation to https ([#2947](https://github.com/timber/timber/issues/2947)) ([05af54f](https://github.com/timber/timber/commit/05af54f7f5463c737299fb9b0512f79b334d2e94)) + +## [2.1.0](https://github.com/timber/timber/compare/2.0.0...v2.1.0) (2024-04-10) + + +### Features + +* add filter to cache methods ([#2878](https://github.com/timber/timber/issues/2878)) ([b347677](https://github.com/timber/timber/commit/b34767750ba5e1e3dc67942d4f42bf0def3e28aa)) +* add filter for sideloaded images basename ([e4ff72f](https://github.com/timber/timber/commit/e4ff72f451e11b05887179086e4bb5a82d799184)) +* add filter to $output before it is cached ([#2910](https://github.com/timber/timber/issues/2910)) ([d1356fd](https://github.com/timber/timber/commit/d1356fd550ccb9b2f9679789e345e22283f8c33c)) +* add is_current and profile_link methods ([#2924](https://github.com/timber/timber/issues/2924)) ([b048da8](https://github.com/timber/timber/commit/b048da899df98ecdcfc8a04c25819fec489251a2)) +* Add WP escapers via Twig filters ([#2933](https://github.com/timber/timber/issues/2933)) ([a88aa00](https://github.com/timber/timber/commit/a88aa006fe18cc329170859707462c6a1927b500)) +* Allow pagination object to be generated using `$prefs` only ([99219a9](https://github.com/timber/timber/commit/99219a97f328ff5369510996c5cc0d15d551e42e)) +* allow pagination object to be generated using $prefs only ([2834fd4](https://github.com/timber/timber/commit/2834fd457375f4e8467839505cdd91fe5198c39c)) +* bump php-stubs/acf-pro-stubs to ^6.0 ([ac17052](https://github.com/timber/timber/commit/ac17052787d2d97eb0f37d477ea14e15b74b00f7)) +* update ECS config and apply standards ([#2893](https://github.com/timber/timber/issues/2893)) ([71111e1](https://github.com/timber/timber/commit/71111e1dc0eabc78b11f45b095c638fa45374044)) + + +### Bug Fixes + +* Add patch for PHAR deserialization vulnerability for Timber 2.x (security advisory GHSA-6363-v5m4-fvq3) ([13c6b0f](https://github.com/timber/timber/commit/13c6b0f60346304f2eed4da1e0bb51566518de4a)) +* adding classes in `MenuItem` ([#2905](https://github.com/timber/timber/issues/2905)) ([7e00eeb](https://github.com/timber/timber/commit/7e00eeba682e54f13a9064359306580e0e628f52)) +* Allow overwrite of default avatar in comments. ([#2786](https://github.com/timber/timber/issues/2786)) ([9c6e0e3](https://github.com/timber/timber/commit/9c6e0e3035b6312de63609c65a7d38b5735d8721)), closes [#2468](https://github.com/timber/timber/issues/2468) +* **docs:** Simplify an if-check in the ACF docs ([96d2874](https://github.com/timber/timber/commit/96d287470a16cab3cc4b14aa373c88423816b3cb)) +* file permissions ([#2842](https://github.com/timber/timber/issues/2842)) ([337d54d](https://github.com/timber/timber/commit/337d54d2727d3c1a511377e1b1a3c367a6ed006b)) +* fix minor codestyle issue in loader.php to make easy-coding-standard happy ([#2950](https://github.com/timber/timber/issues/2950)) ([6e8b6ab](https://github.com/timber/timber/commit/6e8b6ab375df317207ea658cccb12cfb4710e64b)) +* ignore acf_get_field_type void errors ([441ef9e](https://github.com/timber/timber/commit/441ef9e82478cb250373938972bc09c0c1acf154)) +* make PostIterator->last_post nullable ([#2918](https://github.com/timber/timber/issues/2918)) ([064dde7](https://github.com/timber/timber/commit/064dde77998288c10cd39c26914a7e5ea934e04b)) +* Prevent unneeded blog switching in multisite env. ([#2781](https://github.com/timber/timber/issues/2781)) ([d81f995](https://github.com/timber/timber/commit/d81f9951ae41b27e1134b8bf6ae7354a9bae0546)) +* split test running for integrations (plugins) ([#2904](https://github.com/timber/timber/issues/2904)) ([8d03809](https://github.com/timber/timber/commit/8d03809fe2ded38f497dab7c2347fa48a8de10b9)) +* tests failing since Twig 3.8.0 ([#2895](https://github.com/timber/timber/issues/2895)) ([f4a233e](https://github.com/timber/timber/commit/f4a233ec6b3afacee5db592725090d775d654de1)) +* **tests:** fix missing constants in static analysis test ([ae50ccd](https://github.com/timber/timber/commit/ae50ccd25db099d18a93c96b20ecfc82e86a5c58)) +* **test:** use new filter in tests ([c12e9af](https://github.com/timber/timber/commit/c12e9af6027f5bed6c418c2c933c3492e7d68d3e)) +* undefined property ([9e8409e](https://github.com/timber/timber/commit/9e8409e69985925e256d7d48bb855dd95708f84f)) +* unnecessary lowercasing parameters ([#2877](https://github.com/timber/timber/issues/2877)) ([664ea62](https://github.com/timber/timber/commit/664ea625504a0d781ac2efeb5e2b8a39c5ac3e70)) + + +### Reverts + +* revert changing property name ([a7b019b](https://github.com/timber/timber/commit/a7b019b75d5358c35b4237c39817d5a830e8dce2)) + + +### Miscellaneous Chores + +* Add script descriptions in composer file ([#2951](https://github.com/timber/timber/issues/2951)) ([5785128](https://github.com/timber/timber/commit/5785128c1fbb817e146bbf5fdecc270c1856bae8)) +* add Timber authors ([567475e](https://github.com/timber/timber/commit/567475eb396eec7d3c80715e7db7880d2875f338)) +* Create SECURITY.md ([#2939](https://github.com/timber/timber/issues/2939)) ([be36065](https://github.com/timber/timber/commit/be360651eedad4e99a59d185ecaf04d7ab6a3b11)) +* **deps:** bump lycheeverse/lychee-action from 1.8.0 to 1.9.1 ([1ca79af](https://github.com/timber/timber/commit/1ca79aff20b5ac821cded348a2e4ed151bb58777)) +* **deps:** bump lycheeverse/lychee-action from 1.9.1 to 1.9.3 ([#2907](https://github.com/timber/timber/issues/2907)) ([eecfb03](https://github.com/timber/timber/commit/eecfb039dd7fbf3020cdf0310f6f96b6306616b0)) +* **deps:** bump peter-evans/create-issue-from-file from 4 to 5 ([#2906](https://github.com/timber/timber/issues/2906)) ([64703f8](https://github.com/timber/timber/commit/64703f86ae16d68b5706cd3bfd001a34ec821153)) +* **deps:** bump ramsey/composer-install from 2 to 3 ([#2941](https://github.com/timber/timber/issues/2941)) ([97010c4](https://github.com/timber/timber/commit/97010c47a27788c262b214a62d69a530a802b6c0)) +* **deps:** bump tj-actions/changed-files from 39 to 42 ([964f11a](https://github.com/timber/timber/commit/964f11aa496f577179e03f1afadbd1da1e7a5d1b)) +* remove Lando config ([#2899](https://github.com/timber/timber/issues/2899)) ([6fa8ffc](https://github.com/timber/timber/commit/6fa8ffcdb51d286169b47e29ddf54f26568da95a)) +* update links in contributing.md ([3b2c855](https://github.com/timber/timber/commit/3b2c855495b7877a6967537c68054aaebf972eea)) diff --git a/vendor/timber/timber/LICENSE.txt b/vendor/timber/timber/LICENSE.txt new file mode 100644 index 0000000..54dedb3 --- /dev/null +++ b/vendor/timber/timber/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright (c) 2012-2018 Jared Novack + +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. \ No newline at end of file diff --git a/vendor/timber/timber/composer.json b/vendor/timber/timber/composer.json new file mode 100644 index 0000000..ad7fcde --- /dev/null +++ b/vendor/timber/timber/composer.json @@ -0,0 +1,161 @@ +{ + "name": "timber/timber", + "description": "Create WordPress themes with beautiful OOP code and the Twig Template Engine", + "license": "MIT", + "type": "library", + "keywords": [ + "timber", + "twig", + "themes", + "templating", + "wordpress" + ], + "authors": [ + { + "name": "Erik van der Bas", + "email": "erik@basedonline.nl", + "homepage": "https://basedonline.nl" + }, + { + "name": "Lukas Gächter", + "email": "lukas.gaechter@mind.ch", + "homepage": "https://www.mind.ch" + }, + { + "name": "Nicolas Lemoine", + "email": "nico@n5s.dev", + "homepage": "https://n5s.dev" + }, + { + "name": "Jared Novack", + "email": "jared@upstatement.com", + "homepage": "https://upstatement.com" + }, + { + "name": "Timber Community", + "homepage": "https://github.com/timber/timber" + } + ], + "homepage": "https://timber.upstatement.com", + "support": { + "issues": "https://github.com/timber/timber/issues", + "source": "https://github.com/timber/timber", + "docs": "https://timber.github.io/docs/" + }, + "require": { + "php": "^8.1", + "twig/twig": "^3.19" + }, + "require-dev": { + "ergebnis/composer-normalize": "^2.28", + "php-parallel-lint/php-parallel-lint": "^1.3", + "php-stubs/acf-pro-stubs": "^6.0", + "php-stubs/wp-cli-stubs": "^2.0", + "phpro/grumphp": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.7", + "phpunit/phpunit": "^9.0", + "rector/rector": "^1.0", + "squizlabs/php_codesniffer": "^3.0", + "symplify/easy-coding-standard": "^12.2", + "szepeviktor/phpstan-wordpress": "^1.1", + "twig/cache-extra": "^3.17", + "wpackagist-plugin/advanced-custom-fields": "^6.0", + "wpackagist-plugin/co-authors-plus": "^3.6", + "yoast/wp-test-utils": "^1.2" + }, + "suggest": { + "php-coveralls/php-coveralls": "^2.0 for code coverage", + "twig/cache-extra": "For using the cache tag in Twig" + }, + "repositories": [ + { + "type": "composer", + "url": "https://wpackagist.org", + "only": [ + "wpackagist-plugin/*", + "wpackagist-theme/*" + ] + } + ], + "autoload": { + "psr-4": { + "Timber\\": "src/" + } + }, + "autoload-dev": { + "psr-4": { + "TimberPHPStan\\": "tests/phpstan" + }, + "classmap": [ + "tests/" + ], + "exclude-from-classmap": [ + "tests/php", + "tests/phpstan" + ] + }, + "config": { + "allow-plugins": { + "composer/installers": true, + "ergebnis/composer-normalize": true, + "phpro/grumphp": true, + "phpstan/extension-installer": true + }, + "sort-packages": true + }, + "scripts": { + "analyze": "phpstan analyse --memory-limit=1G", + "cs": "ecs check", + "cs:docs": "ecs check-markdown docs/v2/**/*.md --fix", + "cs:docs:fix": "ecs check-markdown docs/v2/**/*.md", + "cs:fix": "ecs check --fix", + "grump": "grumphp run", + "grump:install": "grumphp git:init", + "lint": "parallel-lint --exclude .git --exclude vendor .", + "lint-composer": "@composer normalize --dry-run", + "lint-composer:fix": "@composer normalize", + "qa": [ + "@lint-composer", + "@lint", + "@test", + "@cs" + ], + "rector": "rector process --dry-run", + "rector:fix": "rector process", + "test": [ + "@test:integration:acf", + "@test:integration:coauthors-plus", + "@test:integration:wpml", + "@test:integration" + ], + "test:integration": "phpunit --no-coverage --exclude-group acf,coauthors-plus,wpml", + "test:integration:acf": "phpunit --no-coverage --group acf", + "test:integration:coauthors-plus": "phpunit --no-coverage --group coauthors-plus", + "test:integration:codecov": "phpunit --coverage-clover ./build/logs/clover.xml", + "test:integration:wpml": "phpunit --no-coverage --group wpml", + "test:make-pot": "wp i18n make-pot src tests/languages/timber.pot --domain= && wp i18n make-pot ./tests/assets/translations ./tests/languages/timber-test.pot --domain=timber-test" + }, + "scripts-descriptions": { + "analyze": "Run PHPStan code analysis", + "cs": "Check code style with EasyCodingStandard", + "cs:docs": "Check markdown files in the docs directory with EasyCodingStandard", + "cs:docs:fix": "Fix markdown files in the docs directory with EasyCodingStandard", + "cs:fix": "Fix code style with EasyCodingStandard", + "grump": "Run GrumPHP checks", + "grump:install": "Install GrumPHP configuration", + "lint": "Run PHP Parallel Lint", + "lint-composer": "Check composer.json for incorrect order and whitespace", + "lint-composer:fix": "Fix composer.json for incorrect order and whitespace", + "qa": "Run all quality assurance checks/tests", + "rector": "Check Rector rules without applying them", + "rector:fix": "Fix Rector rules", + "test": "Run all integration tests with PHPUnit", + "test:integration": "Run unit tests for Timber core", + "test:integration:acf": "Run unit tests related to the ACF integration for Timber", + "test:integration:coauthors-plus": "Run unit tests related to the Co-Authors Plus integration for Timber", + "test:integration:codecov": "Run unit tests for Timber with code coverage", + "test:integration:wpml": "Run unit tests related to the WPML integration for Timber", + "test:make-pot": "Generate .pot files if translatable strings have changed that are used in the translation unit test." + } +} diff --git a/vendor/timber/timber/src/AccessesPostsLazily.php b/vendor/timber/timber/src/AccessesPostsLazily.php new file mode 100644 index 0000000..5bacd44 --- /dev/null +++ b/vendor/timber/timber/src/AccessesPostsLazily.php @@ -0,0 +1,145 @@ + 'some_post_type', + * ]); + * }, HOUR_IN_SECONDS); + * + * foreach ($lazy_posts as $post) { + * // This will incur the performance cost of Post::setup(). + * } + * + * // Contrast with: + * + * $eager_posts = \Timber\Helper::transient('my_posts', function() { + * $query = \Timber\Timber::get_posts([ + * 'post_type' => 'some_post_type', + * ]); + * // Incur Post::setup() cost up front. + * return $query->realize(); + * }, HOUR_IN_SECONDS); + * + * foreach ($eager_posts as $post) { + * // No additional overhead here. + * } + * ``` + * @return PostCollectionInterface The realized PostQuery. + */ + public function realize(): self + { + if (!$this->realized) { + // offsetGet() is where lazy instantiation actually happens. + // Since arbitrary array index access may have happened previously, + // leverage that to ensure each Post is instantiated exactly once. + // We call parent::getArrayCopy() to avoid infinite mutual recursion. + foreach (\array_keys(parent::getArrayCopy()) as $k) { + $this->offsetGet($k); + } + $this->realized = true; + } + + return $this; + } + + /** + * @internal + */ + public function getArrayCopy(): array + { + // Force eager instantiation of Timber\Posts before returning them all in an array. + $this->realize(); + return parent::getArrayCopy(); + } + + /** + * @api + * @return array + */ + public function to_array(): array + { + return $this->getArrayCopy(); + } + + /** + * @deprecated 2.0.0 use PostCollectionInterface::to_array() instead + * @api + * @return array + */ + public function get_posts(): array + { + Helper::deprecated(\sprintf('%s::get_posts()', static::class), \sprintf('%s::to_array()', static::class), '2.0.0'); + return $this->getArrayCopy(); + } + + /** + * Lazily instantiates Timber\Post instances from WP_Post objects. + * + * @internal + */ + #[ReturnTypeWillChange] + public function offsetGet($offset) + { + $post = parent::offsetGet($offset); + if ($post instanceof WP_Post) { + $post = $this->factory()->from($post); + $this->offsetSet($offset, $post); + } + + return $post; + } + + /** + * @internal + */ + private function factory(): PostFactory + { + if (!$this->factory) { + $this->factory = new PostFactory(); + } + + return $this->factory; + } +} diff --git a/vendor/timber/timber/src/Admin.php b/vendor/timber/timber/src/Admin.php new file mode 100644 index 0000000..039a495 --- /dev/null +++ b/vendor/timber/timber/src/Admin.php @@ -0,0 +1,39 @@ +Timber 2.0 requires WordPress $minimum_version or greater, but you are running WordPress $wp_version. Please update WordPress in order to use Timber 2.0."); + } + } + + /** + * Display a message in the admin. + * + * @date 01/07/2020 + * + * @param string $text to display + * @param string $class of the notice 'error' (red) or 'warning' (yellow) + */ + protected static function show_notice($text, $class = 'error') + { + \add_action('admin_notices', function () use ($text, $class) { + echo '

    ' . $text . '

    '; + }, 1); + } +} diff --git a/vendor/timber/timber/src/Archives.php b/vendor/timber/timber/src/Archives.php new file mode 100644 index 0000000..3955a31 --- /dev/null +++ b/vendor/timber/timber/src/Archives.php @@ -0,0 +1,384 @@ + + * {% for item in archives.items %} + *
  • {{item.name}}
  • + * {% for child in item.children %} + *
  • {{child.name}}
  • + * {% endfor %} + * {% endfor %} + * + * ``` + * ```html + *
      + *
    • 2015
    • + *
    • May
    • + *
    • April
    • + *
    • March
    • + *
    • February
    • + *
    • January
    • + *
    • 2014
    • + *
    • December
    • + *
    • November
    • + *
    • October
    • + *
    + * ``` + */ +class Archives extends Core +{ + /** + * @var array Preserves arguments sent with the constructor for possible later use when + * displaying items. + */ + protected $args; + + /** + * URL prefix. + * + * @api + * @var string + */ + public $base = ''; + + /** + * @api + * @var array The items of the archives to iterate through and markup for your page. + */ + public $items; + + /** + * Build an Archives menu + * + * @api + * @param array $args { + * Optional. Array of arguments. + * + * @type bool $show_year => false + * @type string + * @type string $type => 'monthly-nested' + * @type int $limit => -1 + * @type bool $show_post_count => false + * @type string $order => 'DESC' + * @type string $post_type => 'post' + * @type bool $show_year => false + * @type bool $nested => false + * } + * @param string $base Any additional paths that need to be prepended to the URLs that are + * generated, for example: "tags". Default ''. + */ + public function __construct($args = null, $base = '') + { + $this->init($args, $base); + } + + /** + * Initialize the Archives + * + * @internal + * @param array|string $args + * @param string $base + */ + public function init($args = null, $base = '') + { + $this->base = $base; + $this->items = $this->items($args); + $this->args = $args; + } + + /** + * @internal + * @param string $url + * @param string $text + * @param int $post_count + * @return mixed + */ + protected function get_archives_link($url, $text, $post_count = 0) + { + $ret = []; + $ret['text'] = $ret['title'] = $ret['name'] = \wptexturize($text); + $ret['url'] = $ret['link'] = \esc_url(URLHelper::prepend_to_url($url, $this->base)); + if ($post_count) { + $ret['post_count'] = (int) $post_count; + } + return $ret; + } + + /** + * @internal + * @param array $args + * @param string $last_changed + * @param string $join + * @param string $where + * @param string $order + * @param string $limit + * @return array + */ + protected function get_items_yearly($args, $last_changed, $join, $where, $order, $limit) + { + global $wpdb; + $output = []; + $query = "SELECT YEAR(post_date) AS `year`, count(ID) as posts FROM {$wpdb->posts} $join $where GROUP BY YEAR(post_date) ORDER BY post_date $order $limit"; + $key = \md5($query); + $key = "wp_get_archives:$key:$last_changed"; + if (!$results = \wp_cache_get($key, 'posts')) { + $results = $wpdb->get_results($query); + \wp_cache_set($key, $results, 'posts'); + } + if ($results) { + foreach ((array) $results as $result) { + $url = \get_year_link($result->year); + $text = \sprintf('%d', $result->year); + $output[] = $this->get_archives_link($url, $text, $result->posts); + } + } + return $output; + } + + /** + * @internal + * @param array|string $args + * @param string $last_changed + * @param string $join + * @param string $where + * @param string $order + * @param string $limit + * @param bool $nested + * @return array + */ + protected function get_items_monthly($args, $last_changed, $join, $where, $order, $limit = '', $nested = true) + { + global $wpdb, $wp_locale; + $output = []; + $defaults = [ + 'show_year' => false, + ]; + $r = \wp_parse_args($args, $defaults); + + $show_year = $r['show_year']; + //will need to specify which year we're looking for + $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, count(ID) as posts " + . "FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date) " + . "ORDER BY post_date $order $limit"; + $key = \md5($query); + $key = "wp_get_archives:$key:$last_changed"; + if (!$results = \wp_cache_get($key, 'posts')) { + $results = $wpdb->get_results($query); + \wp_cache_set($key, $results, 'posts'); + } + if ($results) { + foreach ((array) $results as $result) { + $url = \get_month_link($result->year, $result->month); + if ($show_year && !$nested) { + $text = \sprintf(\__('%1$s %2$d'), $wp_locale->get_month($result->month), $result->year); + } else { + $text = $wp_locale->get_month($result->month); + } + if ($nested) { + $output[$result->year][] = $this->get_archives_link($url, $text, $result->posts); + } else { + $output[] = $this->get_archives_link($url, $text, $result->posts); + } + } + } + if ($nested) { + $out2 = []; + foreach ($output as $year => $months) { + $out2[] = [ + 'name' => $year, + 'children' => $months, + 'post_count' => \array_sum(\array_column($months, 'post_count')), + ]; + } + return $out2; + } + return $output; + } + + /** + * Gets archive items. + * + * @api + * @deprecated 2.0.0, use `{{ archives.items }}` instead. + * @see \Timber\Archives::items() + * @return array|string + */ + public function get_items($args = null) + { + Helper::warn('{{ archives.get_items }} is deprecated. Use {{ archives.items }} instead.'); + + return $this->items($args); + } + + /** + * @api + * @param array|string $args Optional. Array of arguments. + * @return array|string + */ + public function items($args = null) + { + global $wpdb; + + $defaults = [ + 'type' => 'monthly-nested', + 'limit' => '', + 'show_post_count' => false, + 'order' => 'DESC', + 'post_type' => 'post', + 'show_year' => false, + 'nested' => false, + ]; + + if ($args === null) { + $args = $this->args; + } + + $args = \wp_parse_args($args, $defaults); + $post_type = $args['post_type']; + $order = $args['order']; + $nested = $args['nested']; + $type = $args['type']; + $limit = ''; + if ($type == 'yearlymonthly' || $type == 'yearmonth') { + $type = 'monthly-nested'; + } + if ($type == 'monthly-nested') { + $nested = true; + } + + if (!empty($args['limit'])) { + $limit = \absint($args['limit']); + $limit = ' LIMIT ' . $limit; + } + $order = \strtoupper((string) $order); + if ($order !== 'ASC') { + $order = 'DESC'; + } + + // this is what will separate dates on weekly archive links + $archive_week_separator = '–'; + + // over-ride general date format ? 0 = no: use the date format set in Options, 1 = yes: over-ride + $archive_date_format_over_ride = 0; + + // options for daily archive (only if you over-ride the general date format) + $archive_day_date_format = 'Y/m/d'; + + // options for weekly archive (only if you over-ride the general date format) + $archive_week_start_date_format = 'Y/m/d'; + $archive_week_end_date_format = 'Y/m/d'; + + if (!$archive_date_format_over_ride) { + $archive_day_date_format = \get_option('date_format'); + $archive_week_start_date_format = \get_option('date_format'); + $archive_week_end_date_format = \get_option('date_format'); + } + + $where = $wpdb->prepare('WHERE post_type = "%s" AND post_status = "publish"', $post_type); + + /** + * @link https://developer.wordpress.org/reference/hooks/getarchives_where/ + */ + $where = \apply_filters('getarchives_where', $where, $args); + + /** + * @link https://developer.wordpress.org/reference/hooks/getarchives_join/ + */ + $join = \apply_filters('getarchives_join', '', $args); + + $output = []; + $last_changed = \wp_cache_get('last_changed', 'posts'); + if (!$last_changed) { + $last_changed = \microtime(); + \wp_cache_set('last_changed', $last_changed, 'posts'); + } + if ('monthly' == $type) { + $output = $this->get_items_monthly($args, $last_changed, $join, $where, $order, $limit, $nested); + } elseif ('yearly' == $type) { + $output = $this->get_items_yearly($args, $last_changed, $join, $where, $order, $limit); + } elseif ('monthly-nested' == $type) { + $output = $this->get_items_monthly($args, $last_changed, $join, $where, $order, $limit); + } elseif ('daily' == $type) { + $query = "SELECT YEAR(post_date) AS `year`, MONTH(post_date) AS `month`, DAYOFMONTH(post_date) AS `dayofmonth`, count(ID) as posts FROM $wpdb->posts $join $where GROUP BY YEAR(post_date), MONTH(post_date), DAYOFMONTH(post_date) ORDER BY post_date $order $limit"; + $key = \md5($query); + $key = "wp_get_archives:$key:$last_changed"; + if (!$results = \wp_cache_get($key, 'posts')) { + $results = $wpdb->get_results($query); + $cache = []; + $cache[$key] = $results; + \wp_cache_set($key, $results, 'posts'); + } + if ($results) { + foreach ((array) $results as $result) { + $url = \get_day_link($result->year, $result->month, $result->dayofmonth); + $date = \sprintf('%1$d-%2$02d-%3$02d 00:00:00', $result->year, $result->month, $result->dayofmonth); + $text = \mysql2date($archive_day_date_format, $date); + $output[] = $this->get_archives_link($url, $text, $result->posts); + } + } + } elseif ('weekly' == $type) { + $week = \_wp_mysql_week('`post_date`'); + $query = "SELECT DISTINCT $week AS `week`, YEAR( `post_date` ) AS `yr`, DATE_FORMAT( `post_date`, '%Y-%m-%d' ) AS `yyyymmdd`, " + . "count( `ID` ) AS `posts` FROM `$wpdb->posts` $join $where GROUP BY $week, YEAR( `post_date` ) ORDER BY `post_date` $order $limit"; + $key = \md5($query); + $key = "wp_get_archives:$key:$last_changed"; + if (!$results = \wp_cache_get($key, 'posts')) { + $results = $wpdb->get_results($query); + \wp_cache_set($key, $results, 'posts'); + } + $arc_w_last = ''; + if ($results) { + foreach ((array) $results as $result) { + if ($result->week != $arc_w_last) { + $arc_year = $result->yr; + $arc_w_last = $result->week; + $arc_week = \get_weekstartend($result->yyyymmdd, \get_option('start_of_week')); + $arc_week_start = \date_i18n($archive_week_start_date_format, $arc_week['start']); + $arc_week_end = \date_i18n($archive_week_end_date_format, $arc_week['end']); + $url = \sprintf('%1$s/%2$s%3$sm%4$s%5$s%6$sw%7$s%8$d', \home_url(), '', '?', '=', $arc_year, '&', '=', $result->week); + $text = $arc_week_start . $archive_week_separator . $arc_week_end; + $output[] = $this->get_archives_link($url, $text, $result->posts); + } + } + } + } elseif ('postbypost' == $type || 'alpha' == $type) { + $orderby = 'alpha' == $type ? 'post_title ASC ' : 'post_date DESC '; + $query = "SELECT * FROM $wpdb->posts $join $where ORDER BY $orderby $limit"; + $key = \md5($query); + $key = "wp_get_archives:$key:$last_changed"; + if (!$results = \wp_cache_get($key, 'posts')) { + $results = $wpdb->get_results($query); + \wp_cache_set($key, $results, 'posts'); + } + if ($results) { + foreach ((array) $results as $result) { + if ($result->post_date != '0000-00-00 00:00:00') { + $url = \get_permalink($result); + $text = $result->ID; + if ($result->post_title) { + /** This filter is documented in wp-includes/post-template.php */ + $text = \strip_tags((string) \apply_filters('the_title', $result->post_title, $result->ID)); + } + $output[] = $this->get_archives_link($url, $text); + } + } + } + } + return $output; + } +} diff --git a/vendor/timber/timber/src/Attachment.php b/vendor/timber/timber/src/Attachment.php new file mode 100644 index 0000000..b404389 --- /dev/null +++ b/vendor/timber/timber/src/Attachment.php @@ -0,0 +1,353 @@ +src(); + } + + /** + * Gets the link to an attachment. + * + * This returns a link to an attachment’s page, but not the link to the image src itself. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * + * + * ``` + * + * @return string The URL of the attachment. + */ + public function link() + { + if ($this->abs_url) { + return $this->abs_url; + } + + return \get_permalink($this->ID); + } + + /** + * Gets the relative path to an attachment. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return string The relative path to an attachment. + */ + public function path(): string + { + $src = $this->src(); + $src = URLHelper::url_to_file_system($src); + + return URLHelper::get_rel_path($src); + } + + /** + * Gets the relative path to the uploads folder of an attachment. + * + * @api + * + * @return string + */ + public function file(): string + { + return $this->file ?? ($this->file = (string) \get_post_meta($this->ID, '_wp_attached_file', true)); + } + + /** + * Gets the absolute path to an attachment. + * + * @api + * + * @return string + */ + public function file_loc(): string + { + return $this->file_loc ?? ($this->file_loc = (string) \get_attached_file($this->ID)); + } + + /** + * Gets the source URL for an attachment. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return string + */ + public function src(): string + { + return (string) \wp_get_attachment_url($this->ID); + } + + /** + * Gets the caption of an attachment. + * + * @api + * @since 2.0 + * @example + * ```twig + *
    + * + * + * {% if post.thumbnail is not empty %} + *
    {{ post.thumbnail.caption }}
    + * ``` + * + * @return string|null + */ + public function caption(): ?string + { + /** + * Filters the attachment caption. + * + * @since WordPress 4.6.0 + * @since 2.0.0 + * + * @param string $caption Caption for the given attachment. + * @param int $post_id Attachment ID. + */ + return \apply_filters('wp_get_attachment_caption', $this->post_excerpt, $this->ID); + } + + /** + * Gets the raw filesize in bytes. + * + * Use the `size_format` filter to format the raw size into a human readable size («1 MB» instead of «1048576») + * + * @api + * @since 2.0.0 + * @example + * @see https://developer.wordpress.org/reference/functions/size_format/ + * + * Use filesize information in a link that downloads a file: + * + * ```twig + *
    + * {{ attachment.title }} + * (Download, {{ attachment.size|size_format }}) + * + * ``` + * + * @return int|null The raw filesize or null if it could not be read. + */ + public function size(): ?int + { + if (isset($this->size)) { + return $this->size; + } + + /** + * Since 6.0.0, the filesize is stored in the attachment metadata. + * + * @see https://make.wordpress.org/core/2022/05/02/media-storing-file-size-as-part-of-metadata/ + */ + $size = $this->metadata('filesize'); + if ($size !== null && \is_numeric($size)) { + return $this->size = (int) $size; + } + + /** + * Filesize wasn't found in the metadata, so we'll try to get it from the file itself. + * + * We could have used `wp_filesize()` here, but it returns 0 when the file doesn't exist. Which is a perfectly valid filesize + * and prevents us from telling the difference between a file that doesn't exist and a file that has a filesize of 0. + * + * @see https://developer.wordpress.org/reference/functions/wp_filesize/ + */ + $size = \filesize($this->file_loc()); + return $this->size = $size === false ? null : (int) $size; + } + + /** + * Gets the extension of the attached file. + * + * @api + * @since 2.0.0 + * @example + * Use extension information in a link that downloads a file: + * + * ```twig + * + * {{ attachment.title }} + * + * (Download {{ attachment.extension|upper }}, {{ attachment.size }}) + * + * + * ``` + * + * @return string An uppercase extension string. + */ + public function extension(): string + { + return $this->file_extension ?? ($this->file_extension = \pathinfo($this->file(), PATHINFO_EXTENSION)); + } + + /** + * Gets the parent object. + * + * The parent object of an attachment is a post it is assigned to. + * + * @api + * @example + * ```twig + * This image is assigned to {{ image.parent.title }} + * ``` + * + * @return null|Post Parent object as a `Timber\Post`. Returns `false` if no parent + * object is defined. + */ + public function parent(): ?Post + { + if (!$this->post_parent) { + return null; + } + + $factory = new PostFactory(); + + return $factory->from($this->post_parent); + } + + /** + * Get a PHP array with pathinfo() info from the file + * + * @deprecated 2.0.0, use Attachment::pathinfo() instead + * @return array + */ + public function get_pathinfo() + { + Helper::deprecated( + "{{ image.get_pathinfo }}", + "{{ image.pathinfo }}", + '2.0.0' + ); + return PathHelper::pathinfo($this->file()); + } + + /** + * Get a PHP array with pathinfo() info from the file + * + * @return array + */ + public function pathinfo() + { + return PathHelper::pathinfo($this->file()); + } + + /** + * Get attachment metadata the lazy way. + * + * This method is used to retrieve the attachment metadata only when it's needed. + * + * @return array|string|int|null + */ + protected function metadata(?string $key = null) + { + // We haven't retrieved the metadata yet because it's wasn't needed until now. + if (!isset($this->metadata)) { + // Cache it so we don't have to retrieve it again. + $this->metadata = (array) \wp_get_attachment_metadata($this->ID); + } + + if ($key === null) { + return $this->metadata; + } + + return $this->metadata[$key] ?? null; + } +} diff --git a/vendor/timber/timber/src/Cache/Cleaner.php b/vendor/timber/timber/src/Cache/Cleaner.php new file mode 100644 index 0000000..6e61708 --- /dev/null +++ b/vendor/timber/timber/src/Cache/Cleaner.php @@ -0,0 +1,160 @@ +clear_cache_timber(); + } + + /** + * Clears Twig’s cache. + * + * @api + * @since 2.0.0 + * @example + * ```php + * Timber\Cache\Cleaner::clear_cache_twig(); + * ``` + * + * @return bool + */ + public static function clear_cache_twig() + { + $loader = new Loader(); + return $loader->clear_cache_twig(); + } + + protected static function delete_transients_single_site() + { + global $wpdb; + $sql = " + DELETE + a, b + FROM + {$wpdb->options} a, {$wpdb->options} b + WHERE + a.option_name LIKE '%_transient_%' AND + a.option_name NOT LIKE '%_transient_timeout_%' AND + b.option_name = CONCAT( + '_transient_timeout_', + SUBSTRING( + a.option_name, + CHAR_LENGTH('_transient_') + 1 + ) + ) + AND b.option_value < UNIX_TIMESTAMP() + "; + return $wpdb->query($sql); + } + + protected static function delete_transients_multisite() + { + global $wpdb; + $sql = " + DELETE + a, b + FROM + {$wpdb->sitemeta} a, {$wpdb->sitemeta} b + WHERE + a.meta_key LIKE '_site_transient_%' AND + a.meta_key NOT LIKE '_site_transient_timeout_%' AND + b.meta_key = CONCAT( + '_site_transient_timeout_', + SUBSTRING( + a.meta_key, + CHAR_LENGTH('_site_transient_') + 1 + ) + ) + AND b.meta_value < UNIX_TIMESTAMP() + "; + + $clean = $wpdb->query($sql); + return $clean; + } + + public static function delete_transients() + { + global $_wp_using_ext_object_cache; + + if ($_wp_using_ext_object_cache) { + return 0; + } + + global $wpdb; + $records = 0; + + // Delete transients from options table + $records .= self::delete_transients_single_site(); + + // Delete transients from multisite, if configured as such + + if (\is_multisite() && \is_main_network()) { + $records .= self::delete_transients_multisite(); + } + return $records; + } +} diff --git a/vendor/timber/timber/src/Cache/KeyGenerator.php b/vendor/timber/timber/src/Cache/KeyGenerator.php new file mode 100644 index 0000000..96b4235 --- /dev/null +++ b/vendor/timber/timber/src/Cache/KeyGenerator.php @@ -0,0 +1,30 @@ +_get_cache_key(); + } + + if (\is_array($value) && isset($value['_cache_key'])) { + return $value['_cache_key']; + } + + $key = \md5(\json_encode($value)); + if (\is_object($value)) { + $key = $value::class . ';' . $key; + } + + // Replace any of the reserved characters. + $key = \preg_replace('/[{}()\/\\\@:]/', ';', $key); + + return $key; + } +} diff --git a/vendor/timber/timber/src/Cache/TimberKeyGeneratorInterface.php b/vendor/timber/timber/src/Cache/TimberKeyGeneratorInterface.php new file mode 100644 index 0000000..4c87d47 --- /dev/null +++ b/vendor/timber/timber/src/Cache/TimberKeyGeneratorInterface.php @@ -0,0 +1,8 @@ +timberloader->get_cache($key, $this->cache_group, Loader::CACHE_USE_DEFAULT); + } + + public function save($key, $value, $expire = 0) + { + return $this->timberloader->set_cache($key, $value, $this->cache_group, $expire, Loader::CACHE_USE_DEFAULT); + } +} diff --git a/vendor/timber/timber/src/Comment.php b/vendor/timber/timber/src/Comment.php new file mode 100644 index 0000000..3e04e4a --- /dev/null +++ b/vendor/timber/timber/src/Comment.php @@ -0,0 +1,637 @@ + $comment + * ]; + * + * Timber::render('index.twig', $context); + * ``` + * + * ```twig + *

    {{comment_of_the_day.content}}

    + *

    - {{comment.author.name}}

    + * ``` + * + * ```html + *

    But, O Sarah! If the dead can come back to this earth and flit unseen around those they loved, I shall always be near you; in the garish day and in the darkest night -- amidst your happiest scenes and gloomiest hours - always, always; and if there be a soft breeze upon your cheek, it shall be my breath; or the cool air fans your throbbing temple, it shall be my spirit passing by.

    + *

    - Sullivan Ballou

    + * ``` + */ +class Comment extends CoreEntity implements Stringable +{ + /** + * The underlying WordPress Core object. + * + * @since 2.0.0 + * + * @var WP_Comment|null + */ + protected ?WP_Comment $wp_object = null; + + public $object_type = 'comment'; + + public static $representation = 'comment'; + + /** + * @api + * @var int + */ + public $ID; + + /** + * @api + * @var int + */ + public $id; + + /** + * @var int + */ + public $comment_approved; + + /** + * @api + * @var string + */ + public $comment_author_email; + + /** + * @api + * @var string + */ + public $comment_content; + + /** + * @api + * @var string + */ + public $comment_date; + + /** + * @api + * @var int + */ + public $comment_ID; + + /** + * @var int + */ + public $comment_parent; + + /** + * @api + * @var int + */ + public $user_id; + + /** + * @api + * @var int + */ + public $post_id; + + /** + * @api + * @var string + */ + public $comment_author; + + public $_depth = 0; + + protected $children = []; + + /** + * Construct a Timber\Comment. This is protected to prevent direct instantiation, + * which is no longer supported. Use `Timber::get_comment()` instead. + * + * @internal + */ + protected function __construct() + { + } + + /** + * Build a Timber\Comment. Do not call this directly. Use `Timber::get_comment()` instead. + * + * @internal + * @param WP_Comment $wp_comment a native WP_Comment instance + */ + public static function build(WP_Comment $wp_comment): static + { + $comment = new static(); + $comment->import($wp_comment); + $comment->ID = $wp_comment->comment_ID; + $comment->id = $wp_comment->comment_ID; + $comment->wp_object = $wp_comment; + + return $comment; + } + + /** + * Gets the content. + * + * @api + * @return string + */ + public function __toString() + { + return $this->content(); + } + + /** + * @internal + * @param integer $cid + */ + public function init($cid) + { + $comment_data = $cid; + if (\is_integer($cid)) { + $comment_data = \get_comment($cid); + } + $this->import($comment_data); + $this->ID = $this->comment_ID; + $this->id = $this->comment_ID; + } + + /** + * Gets the underlying WordPress Core object. + * + * @since 2.0.0 + * + * @return WP_Comment|null + */ + public function wp_object(): ?WP_Comment + { + return $this->wp_object; + } + + /** + * Gets the author. + * + * @api + * @example + * ```twig + *

    Comments by...

    + *
      + * {% for comment in post.comments %} + *
    1. {{comment.author.name}}, who is a {{comment.author.roles[0]}}
    2. + * {% endfor %} + *
    + * ``` + * ```html + *

    Comments by...

    + *
      + *
    1. Jared Novack, who is a contributor
    2. + *
    3. Katie Ricci, who is a subscriber
    4. + *
    5. Rebecca Pearl, who is a author
    6. + *
    + * ``` + * @return User + */ + public function author() + { + if ($this->user_id) { + return Timber::get_user($this->user_id); + } else { + // We can't (and shouldn't) construct a full-blown User object, + // so just return a stdclass inst with a name + return (object) [ + 'name' => $this->comment_author ?: 'Anonymous', + ]; + } + } + + /** + * Fetches the Gravatar. + * + * @api + * @example + * ```twig + * Image of {{comment.author.name}} + * ``` + * ```html + * Image of Katherine Rich + * ``` + * @param int|mixed $size Size of avatar. + * @param string $default Default avatar URL. + * @return bool|mixed|string + */ + public function avatar($size = 92, $default = '') + { + if (!\get_option('show_avatars')) { + return false; + } + if (!\is_numeric($size)) { + $size = '92'; + } + + $email = $this->avatar_email(); + + $args = [ + 'size' => $size, + 'default' => $default, + ]; + $args = \apply_filters('pre_get_avatar_data', $args, $email); + if (isset($args['url'])) { + return $args['url']; + } + + if (isset($args['default'])) { + $default = $args['default']; + } + + $email_hash = ''; + if (!empty($email)) { + $email_hash = \md5(\strtolower(\trim($email))); + } + $host = $this->avatar_host($email_hash); + $default = $this->avatar_default($default, $email, $size, $host); + if (!empty($email)) { + $avatar = $this->avatar_out($default, $host, $email_hash, $size); + } else { + $avatar = $default; + } + return $avatar; + } + + /** + * Gets the content. + * + * @api + * @return string + */ + public function content() + { + return \trim((string) \apply_filters('comment_text', $this->comment_content)); + } + + /** + * Gets the comment children. + * + * @api + * @return array Comments + */ + public function children() + { + return $this->children; + } + + /** + * Adds a child. + * + * @api + * @param Comment $child_comment Comment child to add. + * @return array Comment children. + */ + public function add_child(Comment $child_comment) + { + return $this->children[] = $child_comment; + } + + /** + * Updates the comment depth. + * + * @api + * @param int $depth Level of depth. + */ + public function update_depth($depth = 0) + { + $this->_depth = $depth; + $children = $this->children(); + foreach ($children as $comment) { + $child_depth = $depth + 1; + $comment->update_depth($child_depth); + } + } + + /** + * At what depth is this comment? + * + * @api + * @return int + */ + public function depth() + { + return $this->_depth; + } + + /** + * Is the comment approved? + * + * @api + * @example + * ```twig + * {% if comment.approved %} + * Your comment is good + * {% else %} + * Do you kiss your mother with that mouth? + * {% endif %} + * ``` + * @return boolean + */ + public function approved() + { + return Helper::is_true($this->comment_approved); + } + + /** + * The date for the comment. + * + * @api + * @example + * ```twig + * {% for comment in post.comments %} + *
    + *

    Posted on {{ comment.date }}:

    + *

    {{ comment.content }}

    + *
    + * {% endfor %} + * ``` + * ```html + *
    + *

    Posted on September 28, 2015:

    + *

    Happy Birthday!

    + *
    + * ``` + * @param string $date_format of desired PHP date format (eg "M j, Y"). + * @return string + */ + public function date($date_format = '') + { + $df = $date_format ?: \get_option('date_format'); + $the_date = (string) \mysql2date($df, $this->comment_date); + return \apply_filters('get_comment_date ', $the_date, $df); + } + + /** + * What time was the comment posted? + * + * @api + * @example + * ```twig + * {% for comment in post.comments %} + *
    + *

    Posted on {{ comment.date }} at {{comment.time}}:

    + *

    {{ comment.content }}

    + *
    + * {% endfor %} + * ``` + * ```html + *
    + *

    Posted on September 28, 2015 at 12:45 am:

    + *

    Happy Birthday!

    + *
    + * ``` + * @param string $time_format of desired PHP time format (eg "H:i:s"). + * @return string + */ + public function time($time_format = '') + { + $tf = $time_format ?: \get_option('time_format'); + $the_time = (string) \mysql2date($tf, $this->comment_date); + return \apply_filters('get_comment_time', $the_time, $tf); + } + + /** + * Gets a comment meta value. + * + * @api + * @deprecated 2.0.0, use `{{ comment.meta('field_name') }}` instead. + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_meta_field($field_name) + { + Helper::deprecated( + "{{ comment.get_meta_field('field_name') }}", + "{{ comment.meta('field_name') }}", + '2.0.0' + ); + + return $this->meta($field_name); + } + + /** + * Checks if the comment is a child. + * + * @api + * @return bool + */ + public function is_child() + { + return $this->comment_parent > 0; + } + + /** + * Gets a comment meta value. + * + * @api + * @deprecated 2.0.0, use `{{ comment.meta('field_name') }}` instead. + * @see \Timber\Comment::meta() + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_field($field_name = null) + { + Helper::deprecated( + "{{ comment.get_field('field_name') }}", + "{{ comment.meta('field_name') }}", + '2.0.0' + ); + + return $this->meta($field_name); + } + + /** + * Enqueue the WP threaded comments JavaScript, and fetch the reply link for various comments. + * + * @api + * @param string $reply_text Text of the reply link. + * @return string + */ + public function reply_link($reply_text = 'Reply') + { + if (\is_singular() && \comments_open() && \get_option('thread_comments')) { + \wp_enqueue_script('comment-reply'); + } + + // Get the comments depth option from the admin panel + $max_depth = \get_option('thread_comments_depth'); + + // Default args + $args = [ + 'add_below' => 'comment', + 'respond_id' => 'respond', + 'reply_text' => $reply_text, + 'depth' => $this->depth() + 1, + 'max_depth' => $max_depth, + ]; + + return \get_comment_reply_link($args, $this->ID, $this->post_id); + } + + /** + * Checks whether the current user can edit the comment. + * + * @api + * @example + * ```twig + * {% if comment.can_edit %} + * Edit + * {% endif %} + * ``` + * @return bool + */ + public function can_edit(): bool + { + return \current_user_can('edit_comment', $this->ID); + } + + /** + * Gets the edit link for a comment if the current user has the correct rights. + * + * @api + * @since 2.0.0 + * @example + * ```twig + * {% if comment.can_edit %} + * Edit + * {% endif %} + * ``` + * @return string|null The edit URL of a comment in the WordPress admin or null if the current user can’t edit the + * comment. + */ + public function edit_link(): ?string + { + if (!$this->can_edit()) { + return null; + } + + return \get_edit_comment_link($this->ID); + } + + /* AVATAR Stuff + ======================= */ + + /** + * @internal + * @return string + */ + protected function avatar_email() + { + $id = (int) $this->user_id; + $user = \get_userdata($id); + if ($user) { + $email = $user->user_email; + } else { + $email = $this->comment_author_email; + } + return $email; + } + + /** + * @internal + * @param string $email_hash + * @return string + */ + protected function avatar_host($email_hash) + { + if (\is_ssl()) { + $host = 'https://secure.gravatar.com'; + } else { + if (!empty($email_hash)) { + $host = \sprintf("https://%d.gravatar.com", (\hexdec($email_hash[0]) % 2)); + } else { + $host = 'https://0.gravatar.com'; + } + } + return $host; + } + + /** + * @internal + * @param string $default + * @param string $email + * @param string $size + * @param string $host + * @return string + */ + protected function avatar_default($default, $email, $size, $host) + { + if (\str_starts_with($default, '/')) { + $default = \home_url() . $default; + } + + if (empty($default)) { + $avatar_default = \get_option('avatar_default'); + if (empty($avatar_default)) { + $default = 'mystery'; + } else { + $default = $avatar_default; + } + } + + if ('mystery' == $default) { + $default = $host . '/avatar/ad516503a11cd5ca435acc9bb6523536?s=' . $size; + // ad516503a11cd5ca435acc9bb6523536 == md5('unknown@gravatar.com') + } elseif ('blank' == $default) { + $default = $email ? 'blank' : \includes_url('images/blank.gif'); + } elseif (!empty($email) && 'gravatar_default' == $default) { + $default = ''; + } elseif ('gravatar_default' == $default) { + $default = $host . '/avatar/?s=' . $size; + } elseif (empty($email) && !\preg_match('/^https?:\/\//', (string) $default)) { + $default = $host . '/avatar/?d=' . $default . '&s=' . $size; + } + return $default; + } + + /** + * @internal + * @param string $default + * @param string $host + * @param string $email_hash + * @param string $size + * @return mixed + */ + protected function avatar_out($default, $host, $email_hash, $size) + { + $out = $host . '/avatar/' . $email_hash; + $rating = \get_option('avatar_rating'); + + $url_args = [ + 's' => $size, + 'd' => $default, + ]; + + if (!empty($rating)) { + $url_args['r'] = $rating; + } + + $out = \add_query_arg( + \rawurlencode_deep(\array_filter($url_args)), + $out + ); + + return \str_replace('&', '&', (string) \esc_url($out)); + } +} diff --git a/vendor/timber/timber/src/CommentThread.php b/vendor/timber/timber/src/CommentThread.php new file mode 100644 index 0000000..d8ccc74 --- /dev/null +++ b/vendor/timber/timber/src/CommentThread.php @@ -0,0 +1,190 @@ + + *

    Comments on {{ post.title }}

    + *
      + * {% for comment in post.comments %} + * {% include 'comment.twig' %} + * {% endfor %} + *
    + *
    + * {{ function('comment_form') }} + *
    + * + * ``` + * + * ```twig + * {# comment.twig #} + *
  • + *
    {{ comment.content }}
    + *

    {{ comment.author.name }}

    + * {{ function('comment_form') }} + * + * {% if comment.children %} + *
    + * {% for child_comment in comment.children %} + * {% include 'comment.twig' with { comment:child_comment } %} + * {% endfor %} + *
    + * {% endif %} + *
  • + * ``` + */ +class CommentThread extends ArrayObject +{ + public $_orderby = ''; + + public $_order = 'ASC'; + + /** + * Creates a new `Timber\CommentThread` object. + * + * @param int $post_id The post ID. + * @param array|boolean $args Optional. An array of arguments or false if initialization + * should be skipped. + */ + public function __construct( + public $post_id, + $args = [] + ) { + parent::__construct(); + if ($args || \is_array($args)) { + $this->init($args); + } + } + + /** + * @internal + */ + protected function fetch_comments($args = []) + { + $args['post_id'] = $this->post_id; + $comments = \get_comments($args); + return $comments; + } + + /** + * Gets the number of comments on a post. + * + * @return int The number of comments on a post. + */ + public function mecount() + { + return \get_comments_number($this->post_id); + } + + protected function merge_args($args) + { + $base = [ + 'status' => 'approve', + 'order' => $this->_order, + ]; + return \array_merge($base, $args); + } + + /** + * @internal + */ + public function order($order = 'ASC') + { + $this->_order = $order; + $this->init(); + return $this; + } + + /** + * @internal + */ + public function orderby($orderby = 'wp') + { + $this->_orderby = $orderby; + $this->init(); + return $this; + } + + /** + * Inits the object. + * + * @internal + * @param array $args Optional. + */ + public function init($args = []) + { + global $overridden_cpage; + $args = self::merge_args($args); + $comments = $this->fetch_comments($args); + $tcs = []; + if ('' == \get_query_var('cpage') && \get_option('page_comments')) { + \set_query_var('cpage', 'newest' == \get_option('default_comments_page') ? \get_comment_pages_count() : 1); + $overridden_cpage = true; + } + foreach ($comments as $key => &$comment) { + $factory = new CommentFactory(); + $timber_comment = $factory->from($comment); + $tcs[$timber_comment->id] = $timber_comment; + } + + $parents = []; + $children = []; + + foreach ($tcs as $comment) { + if ($comment->is_child()) { + $children[$comment->ID] = $comment; + } else { + $parents[$comment->ID] = $comment; + } + } + + foreach ($children as &$comment) { + $parent_id = $comment->comment_parent; + if (isset($parents[$parent_id])) { + $parents[$parent_id]->add_child($comment); + } + if (isset($children[$parent_id])) { + $children[$parent_id]->add_child($comment); + } + } + //there's something in update_depth that breaks order? + + foreach ($parents as $comment) { + $comment->update_depth(); + } + $this->import_comments($parents); + } + + /** + * @internal + */ + protected function clear() + { + $this->exchangeArray([]); + } + + /** + * @internal + */ + protected function import_comments($arr) + { + $this->clear(); + $i = 0; + foreach ($arr as $comment) { + $this[$i] = $comment; + $i++; + } + } +} diff --git a/vendor/timber/timber/src/Core.php b/vendor/timber/timber/src/Core.php new file mode 100644 index 0000000..7b27467 --- /dev/null +++ b/vendor/timber/timber/src/Core.php @@ -0,0 +1,170 @@ +$field); + } + + /** + * Magic method dispatcher for meta fields, for convenience in Twig views. + * + * Called when explicitly invoking non-existent methods on a Core object. This method is not + * meant to be called directly. + * + * @example + * ```php + * $post = Timber\Post::get( get_the_ID() ); + * + * update_post_meta( $post->id, 'favorite_zep4_track', 'Black Dog' ); + * + * Timber::render( 'rock-n-roll.twig', array( 'post' => $post ) ); + * ``` + * ```twig + * {# Since this method does not exist explicitly on the Post class, + * it will dynamically dispatch the magic __call() method with an argument + * of "favorite_zep4_track" #} + * Favorite Zeppelin IV Track: {{ post.favorite_zep4_track() }} + * ``` + * @link https://secure.php.net/manual/en/language.oop5.overloading.php#object.call + * @link https://github.com/twigphp/Twig/issues/2 + * @api + * + * @param string $field The name of the method being called. + * @param array $arguments Enumerated array containing the parameters passed to the function. + * Not used. + * + * @return mixed The value of the meta field named `$field` if truthy, `false` otherwise. + */ + public function __call($field, $arguments) + { + if (\method_exists($this, 'meta') && $meta_value = $this->meta($field)) { + return $meta_value; + } + + return false; + } + + /** + * Magic getter for dynamic meta fields, for convenience in Twig views. + * + * This method is not meant to be called directly. + * + * @example + * ```php + * $post = Timber\Post::get( get_the_ID() ); + * + * update_post_meta( $post->id, 'favorite_darkside_track', 'Any Colour You Like' ); + * + * Timber::render('rock-n-roll.twig', array( 'post' => $post )); + * ``` + * ```twig + * {# Since this property does not exist explicitly on the Post class, + * it will dynamically dispatch the magic __get() method with an argument + * of "favorite_darkside_track" #} + * Favorite Dark Side of the Moon Track: {{ post.favorite_darkside_track }} + * ``` + * @link https://secure.php.net/manual/en/language.oop5.overloading.php#object.get + * @link https://twig.symfony.com/doc/2.x/recipes.html#using-dynamic-object-properties + * + * @param string $field The name of the property being accessed. + * + * @return mixed The value of the meta field, or the result of invoking `$field()` as a method + * with no arguments, or `false` if neither returns a truthy value. + */ + public function __get($field) + { + if (\method_exists($this, 'meta') && $meta_value = $this->meta($field)) { + return $this->$field = $meta_value; + } + if (\method_exists($this, $field)) { + return $this->$field = $this->$field(); + } + + if ('custom' === $field) { + Helper::deprecated( + "Accessing a meta value through {{ {$this->object_type}.custom }}", + "{{ {$this->object_type}.meta() }} or {{ {$this->object_type}.raw_meta() }}", + '2.0.0' + ); + } + + return $this->$field = false; + } + + /** + * Takes an array or object and adds the properties to the parent object. + * + * @example + * ```php + * $data = array( 'airplane' => '757-200', 'flight' => '5316' ); + * $post = Timber::get_post(); + * $post->import(data); + * + * echo $post->airplane; // 757-200 + * ``` + * @param array|object $info an object or array you want to grab data from to attach to the Timber object + */ + public function import($info, $force = false, $only_declared_properties = false) + { + if (\is_object($info)) { + $info = \get_object_vars($info); + } + if (\is_array($info)) { + foreach ($info as $key => $value) { + if ($key === '' || \ord($key[0]) === 0) { + continue; + } + if (!empty($key) && $force) { + $this->$key = $value; + } elseif (!empty($key) && !\method_exists($this, $key)) { + if ($only_declared_properties) { + if (\property_exists($this, $key)) { + $this->$key = $value; + } + } else { + $this->$key = $value; + } + } + } + } + } + + /** + * Updates metadata for the object. + * + * @deprecated 2.0.0 Use `update_metadata()` instead. + * + * @param string $key The key of the meta field to update. + * @param mixed $value The new value. + */ + public function update($key, mixed $value) + { + Helper::deprecated('Timber\Core::update()', 'update_metadata()', '2.0.0'); + \update_metadata($this->object_type, $this->ID, $key, $value); + } +} diff --git a/vendor/timber/timber/src/CoreEntity.php b/vendor/timber/timber/src/CoreEntity.php new file mode 100644 index 0000000..c9182fd --- /dev/null +++ b/vendor/timber/timber/src/CoreEntity.php @@ -0,0 +1,305 @@ +fetch_meta($field_name, $args); + } + + /** + * Gets an object meta value directly from the database. + * + * Returns a raw meta value or all raw meta values saved in the meta database table. In + * comparison to `meta()`, this function will return raw values that are not filtered by third- + * party plugins. + * + * Fetching raw values for all custom fields will not have a big performance impact, because + * WordPress gets all meta values, when the first meta value is accessed. + * + * @api + * @since 2.0.0 + * + * @param string $field_name Optional. The field name for which you want to get the value. If + * no field name is provided, this function will fetch values for all + * custom fields. Default empty string. + * + * @return null|mixed The meta field value(s). Null if no value could be found, an empty array + * if all fields were requested but no values could be found. + */ + public function raw_meta($field_name = '') + { + return $this->fetch_meta($field_name, [], false); + } + + /** + * Gets an object meta value. + * + * Returns a meta value or all meta values for all custom fields of an object saved in the object + * meta database table. + * + * Fetching all values is only advised during development, because it can have a big performance + * impact, when all filters are applied. + * + * @api + * + * @param string $field_name Optional. The field name for which you want to get the value. If + * no field name is provided, this function will fetch values for all + * custom fields. Default empty string. + * @param array $args { + * An array of arguments for getting the meta value. Third-party integrations can use this + * argument to make their API arguments available in Timber. Default empty array. + * } + * @param bool $apply_filters Whether to apply filtering of meta values. You can also use + * the `raw_meta()` method as a shortcut to apply this argument. + * Default true. + * + * @return mixed The custom field value or an array of custom field values. Null if no value + * could be found. + */ + protected function fetch_meta($field_name = '', $args = [], $apply_filters = true) + { + /** + * Filters whether to transform a meta value. + * + * If the filter returns `true`, all meta value will be transformed to Timber/standard PHP objects if possible. + * + * @api + * @since 2.0.0 + * @example + * ```php + * // Transforms all meta value. + * add_filter( 'timber/meta/transform_value', '__return_true' ); + * ``` + * + * @param bool $transform_value + */ + $transform_value = \apply_filters('timber/meta/transform_value', false); + + $args = \wp_parse_args($args, [ + 'transform_value' => $transform_value, + ]); + + $object_meta = null; + + $object_type = $this->get_object_type(); + + if ($apply_filters) { + /** + * Filters object meta data before it is fetched from the database. + * + * @since 2.0.0 + * + * @example + * ```php + * // Disable fetching meta values. + * add_filter( 'timber/post/pre_meta', '__return_false' ); + * + * // Add your own meta data. + * add_filter( 'timber/post/pre_meta', function( $post_meta, $post_id, $post ) { + * $post_meta = array_merge( $post_meta, [ + * 'custom_data_1' => 73, + * 'custom_data_2' => 274, + * ] ); + * + * return $post_meta; + * }, 10, 3 ); + * ``` + * + * @param string|null $object_meta The field value. Default null. Passing a non-null + * value will skip fetching the value from the database + * and will use the value from the filter instead. + * @param int $post_id The post ID. + * @param string $field_name The name of the meta field to get the value for. + * @param object $object The Timber object. + * @param array $args An array of arguments. + */ + $object_meta = \apply_filters( + "timber/{$object_type}/pre_meta", + $object_meta, + $this->ID, + $field_name, + $this, + $args + ); + + if ($object_type !== 'term') { + /** + * Filters the value for a post meta field before it is fetched from the database. + * + * @deprecated 2.0.0, use `timber/{object_type}/pre_meta` + */ + $object_meta = \apply_filters_deprecated( + "timber_{$object_type}_get_meta_field_pre", + [$object_meta, $this->ID, $field_name, $this], + '2.0.0', + "timber/{$object_type}/pre_meta" + ); + + /** + * Filters post meta data before it is fetched from the database. + * + * @deprecated 2.0.0, use `timber/{object_type}/pre_meta` + */ + \do_action_deprecated( + "timber_{$object_type}_get_meta_pre", + [$object_meta, $this->ID, $this], + '2.0.0', + "timber/{$object_type}/pre_meta" + ); + } + } + + if (null === $object_meta) { + // Fetch values. Auto-fetches all values if $field_name is empty. + $object_meta = \get_metadata($object_type, $this->ID, $field_name, true); + + // Mimic $single argument when fetching all meta values. + if (empty($field_name) && \is_array($object_meta)) { + $object_meta = \array_map(function ($meta) { + /** + * We use array_key_exists() instead of isset(), because when the meta value is null, isset() would + * return false, even though null is a valid value to return. + * + * @ticket #2519 + */ + if (1 === \count($meta) && \array_key_exists(0, $meta)) { + return $meta[0]; + } + + return $meta; + }, $object_meta); + } + } + + if ($apply_filters) { + /** + * Filters the value for a post meta field. + * + * This filter is used by the ACF Integration. + * + * @see \Timber\Post::meta() + * @since 2.0.0 + * + * @example + * ```php + * add_filter( 'timber/post/meta', function( $post_meta, $post_id, $field_name, $post ) { + * if ( 'event' === $post->post_type ) { + * // Do something special. + * $post_meta['foo'] = $post_meta['foo'] . ' bar'; + * } + * + * return $post_meta; + * }, 10, 4 ); + * ``` + * + * @param string $post_meta The field value. + * @param int $post_id The post ID. + * @param string $field_name The name of the meta field to get the value for. + * @param CoreEntity $post The post object. + * @param array $args An array of arguments. + */ + $object_meta = \apply_filters( + "timber/{$object_type}/meta", + $object_meta, + $this->ID, + $field_name, + $this, + $args + ); + + if ($object_type === 'term') { + /** + * Filters the value for a term meta field. + * + * @deprecated 2.0.0, use `timber/term/meta` + */ + $object_meta = \apply_filters_deprecated( + 'timber/term/meta/field', + [$object_meta, $this->ID, $field_name, $this], + '2.0.0', + 'timber/term/meta' + ); + } + + /** + * Filters the value for an object meta field. + * + * @deprecated 2.0.0, use `timber/{object_type}/meta` + */ + $object_meta = \apply_filters_deprecated( + "timber_{$object_type}_get_meta_field", + [$object_meta, $this->ID, $field_name, $this], + '2.0.0', + "timber/{$object_type}/meta" + ); + + /** + * Filters object meta data fetched from the database. + * + * @deprecated 2.0.0, use `timber/{object_type}/meta` + */ + $object_meta = \apply_filters_deprecated( + "timber_{$object_type}_get_meta", + [$object_meta, $this->ID, $this], + '2.0.0', + "timber/{$object_type}/meta" + ); + + // Maybe convert values to Timber objects. + if ($args['transform_value']) { + $object_meta = $this->convert($object_meta); + } + } + + return $object_meta; + } + + /** + * Finds any WP_Post objects and converts them to Timber\Posts + * + * @api + * @param array|CoreEntity $data + */ + public function convert($data) + { + if (\is_object($data)) { + $data = Helper::convert_wp_object($data); + } elseif (\is_array($data)) { + $data = \array_map([$this, 'convert'], $data); + } + return $data; + } + + /** + * Get the base object type + * + * @return string + */ + protected function get_object_type() + { + return $this->object_type; + } +} diff --git a/vendor/timber/timber/src/CoreEntityInterface.php b/vendor/timber/timber/src/CoreEntityInterface.php new file mode 100644 index 0000000..52030d3 --- /dev/null +++ b/vendor/timber/timber/src/CoreEntityInterface.php @@ -0,0 +1,25 @@ +getTimestamp(); + } elseif (\is_numeric($date) && (\strtotime($date) === false || \strlen($date) !== 8)) { + $timestamp = \intval($date); + } else { + $timestamp = \strtotime($date); + } + + if (\is_string($timezone)) { + $timezone = new DateTimeZone($timezone); + } + + return \wp_date($format, $timestamp, $timezone); + } + + /** + * Returns the difference between two times in a human readable format. + * + * Differentiates between past and future dates. + * + * @api + * @see \human_time_diff() + * @example + * ```twig + * {{ post.date('U')|time_ago }} + * {{ post.date('Y-m-d H:i:s')|time_ago }} + * {{ post.date(constant('DATE_ATOM'))|time_ago }} + * ``` + * + * @param int|string $from Base date as a timestamp or a date string. + * @param int|string $to Optional. Date to calculate difference to as a timestamp or + * a date string. Default current time. + * @param string $format_past Optional. String to use for past dates. To be used with + * `sprintf()`. Default `%s ago`. + * @param string $format_future Optional. String to use for future dates. To be used with + * `sprintf()`. Default `%s from now`. + * + * @return string + */ + public static function time_ago($from, $to = null, $format_past = null, $format_future = null) + { + if (null === $format_past) { + /* translators: %s: Human-readable time difference. */ + $format_past = \__('%s ago'); + } + + if (null === $format_future) { + /* translators: %s: Human-readable time difference. */ + $format_future = \__('%s from now'); + } + + $to ??= \time(); + $to = \is_numeric($to) + ? new DateTimeImmutable('@' . $to, \wp_timezone()) + : new DateTimeImmutable($to, \wp_timezone()); + $from = \is_numeric($from) + ? new DateTimeImmutable('@' . $from, \wp_timezone()) + : new DateTimeImmutable($from, \wp_timezone()); + + if ($from < $to) { + return \sprintf($format_past, \human_time_diff($from->getTimestamp(), $to->getTimestamp())); + } else { + return \sprintf($format_future, \human_time_diff($to->getTimestamp(), $from->getTimestamp())); + } + } +} diff --git a/vendor/timber/timber/src/DatedInterface.php b/vendor/timber/timber/src/DatedInterface.php new file mode 100644 index 0000000..03469ff --- /dev/null +++ b/vendor/timber/timber/src/DatedInterface.php @@ -0,0 +1,55 @@ + + * + *

    {{ post.title }}

    + *
    + * {{ post.content }} + *
    + * + * ``` + * + * ```html + *
    + * + *

    Now you've done it!

    + *
    + * Whatever whatever + *
    + *
    + * ``` + */ +class ExternalImage implements ImageInterface +{ + /** + * Alt text. + * + * @api + * @var string + */ + protected string $alt_text; + + /** + * Alt text. + * + * @api + * @var string + */ + protected string $caption; + + /** + * Representation. + * + * @var string What does this class represent in WordPress terms? + */ + public static $representation = 'image'; + + /** + * File location. + * + * @api + * @var string The absolute path to the attachmend file in the filesystem + * (Example: `/var/www/htdocs/wp-content/themes/my-theme/images/`) + */ + protected string $file_loc; + + /** + * File extension. + * + * @api + * @since 2.0.0 + * @var string A file extension. + */ + protected string $file_extension; + + /** + * Absolute URL. + * + * @var string The absolute URL to the attachment. + */ + public $abs_url; + + /** + * Size. + * + * @var integer|null + */ + protected ?int $size = null; + + /** + * File types. + * + * @var array An array of supported relative file types. + */ + private $image_file_types = [ + 'jpg', + 'jpeg', + 'png', + 'svg', + 'bmp', + 'ico', + 'gif', + 'tiff', + 'pdf', + ]; + + /** + * Image dimensions. + * + * @internal + * @var ImageDimensions|null stores Image Dimensions in a structured way. + */ + protected ?ImageDimensions $image_dimensions = null; + + protected function __construct() + { + } + + /** + * Inits the ExternalImage object. + * + * @internal + * @param $url string URL or path to load the image from. + * @param $args array An array of arguments for the image. + */ + public static function build($url, array $args = []): ?ExternalImage + { + if (!\is_string($url) || \is_numeric($url)) { + return null; + } + + $external_image = new static(); + + if (!empty($args['alt'])) { + $external_image->alt_text = (string) $args['alt']; + } + if (!empty($args['caption'])) { + $external_image->caption = (string) $args['caption']; + } + + if (\str_contains($url, '://')) { + // Assume URL. + $external_image->init_with_url($url); + + return $external_image; + } elseif (\str_contains($url, (string) ABSPATH)) { + // Assume absolute path. + $external_image->init_with_file_path($url); + + return $external_image; + } else { + // Check for image file types. + foreach ($external_image->image_file_types as $type) { + // Assume a relative path. + if (\str_contains(\strtolower($url), (string) $type)) { + $external_image->init_with_relative_path($url); + + return $external_image; + } + } + } + + return null; + } + + /** + * Gets the source URL for the image. + * + * @api + * @example + * ```twig + * + * + * ``` + * ```html + * + * + * ``` + * + * @param string $size Ignored. For compatibility with Timber\Image. + * + * @return string The src URL for the image. + */ + public function src($size = 'full'): string + { + return URLHelper::maybe_secure_url($this->abs_url); + } + + /** + * Gets the relative path to the file. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return string The relative path to the image file. + */ + public function path(): string + { + return URLHelper::get_rel_path($this->file_loc()); + } + + /** + * Gets the absolute path to the image. + * + * @api + * + * @return string + */ + public function file_loc(): string + { + return $this->file_loc ?? ''; + } + + /** + * Gets filesize in a human-readable format. + * + * This can be useful if you want to display the human-readable filesize for a file. It’s + * easier to read «16 KB» than «16555 bytes» or «1 MB» than «1048576 bytes». + * + * @api + * @since 2.0.0 + * @example + * Use filesize information in a link that downloads a file: + * + * ```twig + * + * {{ attachment.title }} + * (Download, {{ attachment.size }}) + * + * ``` + * + * @return null|int Filsize or null if the filesize couldn't be determined. + */ + public function size(): ?int + { + if (isset($this->size)) { + return $this->size; + } + + /** + * Filesize wasn't found in the metadata, so we'll try to get it from the file itself. + * + * We could have used `wp_filesize()` here, but it returns 0 when the file doesn't exist. Which is a perfectly valid filesize + * and prevents us from telling the difference between a file that doesn't exist and a file that has a filesize of 0. + * + * @see https://developer.wordpress.org/reference/functions/wp_filesize/ + */ + $size = \filesize($this->file_loc()); + return $this->size = $size === false ? null : (int) $size; + } + + /** + * Gets the src for an attachment. + * + * @api + * + * @return string The src of the attachment. + */ + public function __toString(): string + { + return $this->src(); + } + + /** + * Gets the extension of the attached file. + * + * @api + * @since 2.0.0 + * @example + * + * Use extension information in a link that downloads a file: + * + * ```twig + * + * {{ attachment.title }} + * + * (Download {{ attachment.extension|upper }}, {{ attachment.size }}) + * + * + * ``` + * + * @return string|null An uppercase extension string. + */ + public function extension(): ?string + { + return $this->file_extension ?? ($this->file_extension = \pathinfo($this->file_loc(), PATHINFO_EXTENSION)); + } + + /** + * Gets the width of the image in pixels. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return int|null The width of the image in pixels. Null if the width can’t be read, e.g. because the file doesn’t + * exist. + */ + public function width(): ?int + { + return $this->image_dimensions->width(); + } + + /** + * Gets the height of the image in pixels. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return int|null The height of the image in pixels. Null if the height can’t be read, e.g. because the file + * doesn’t exist. + */ + public function height(): ?int + { + return $this->image_dimensions->height(); + } + + /** + * Gets the aspect ratio of the image. + * + * @api + * @example + * ```twig + * {% if post.thumbnail.aspect < 1 %} + * {# handle vertical image #} + * A basketball player + * {% else %} + * A sumo wrestler + * {% endif %} + * ``` + * + * @return float The aspect ratio of the image. + */ + public function aspect() + { + return $this->image_dimensions->aspect(); + } + + /** + * Sets the relative alt text of the image. + * + * @param string $alt Alt text for the image. + */ + public function set_alt(string $alt) + { + $this->alt_text = $alt; + } + + /** + * Sets the relative alt text of the image. + * + * @param string $caption Caption text for the image + */ + public function set_caption(string $caption) + { + $this->caption = $caption; + } + + /** + * Inits the object with an absolute path. + * + * @internal + * + * @param string $file_path An absolute path to a file. + */ + protected function init_with_file_path($file_path) + { + $url = URLHelper::file_system_to_url($file_path); + + $this->abs_url = $url; + $this->file_loc = $file_path; + $this->image_dimensions = new ImageDimensions($file_path); + } + + /** + * Inits the object with a relative path. + * + * @internal + * + * @param string $relative_path A relative path to a file. + */ + protected function init_with_relative_path($relative_path) + { + $file_path = URLHelper::get_full_path($relative_path); + + $this->abs_url = \home_url($relative_path); + $this->file_loc = $file_path; + $this->image_dimensions = new ImageDimensions($file_path); + } + + /** + * Inits the object with an URL. + * + * @internal + * + * @param string $url An URL on the same host. + */ + protected function init_with_url($url) + { + if (!URLHelper::is_local($url)) { + $url = ImageHelper::sideload_image($url); + } + + $this->abs_url = $url; + + if (URLHelper::is_local($url)) { + $this->file_loc = URLHelper::remove_double_slashes( + ABSPATH . URLHelper::get_rel_url($url) + ); + $this->image_dimensions = new ImageDimensions($this->file_loc); + } + } + + /** + * Gets the alt text for an image. + * + * For better accessibility, you should always add an alt attribute to your images, even if it’s + * empty. + * + * @api + * @example + * ```twig + * {{ image.alt }} + * ``` + * ```html + * You should always add alt texts to your images for better accessibility + * ``` + * + * @return string Alt text stored in WordPress. + */ + public function alt(): ?string + { + return $this->alt_text ?? null; + } + + public function caption(): ?string + { + return $this->caption ?? null; + } +} diff --git a/vendor/timber/timber/src/Factory/CommentFactory.php b/vendor/timber/timber/src/Factory/CommentFactory.php new file mode 100644 index 0000000..144c9b1 --- /dev/null +++ b/vendor/timber/timber/src/Factory/CommentFactory.php @@ -0,0 +1,154 @@ +from_id((int) $params); + } + + if ($params instanceof WP_Comment_Query) { + return $this->from_wp_comment_query($params); + } + + if (\is_object($params)) { + return $this->from_comment_object($params); + } + + if ($this->is_numeric_array($params)) { + return \array_map([$this, 'from'], $params); + } + + if (\is_array($params)) { + return $this->from_wp_comment_query(new WP_Comment_Query($params)); + } + } + + protected function from_id(int $id) + { + $wp_comment = \get_comment($id); + + if (!$wp_comment) { + return null; + } + + return $this->build($wp_comment); + } + + protected function from_comment_object(object $comment): CoreInterface + { + if ($comment instanceof CoreInterface) { + // We already have some kind of Timber Core object + return $comment; + } + + if ($comment instanceof WP_Comment) { + return $this->build($comment); + } + + throw new InvalidArgumentException(\sprintf( + 'Expected an instance of Timber\CoreInterface or WP_Comment, got %s', + $comment::class + )); + } + + protected function from_wp_comment_query(WP_Comment_Query $query): iterable + { + return \array_map([$this, 'build'], $query->get_comments()); + } + + protected function get_comment_class(WP_Comment $comment): string + { + /** + * Filters the class(es) used for comments linked to different post types. + * + * The default class is Timber\Comment. You can use this filter to provide your own comment class for specific post types. + * + * Make sure to merge in your additional classes instead of overwriting the whole Class Map. + * + * @since 2.0.0 + * @example + * ``` + * use Book; + * + * add_filter( 'timber/post/classmap', function( $classmap ) { + * $custom_classmap = [ + * 'book' => BookComment::class, + * ]; + * + * return array_merge( $classmap, $custom_classmap ); + * } ); + * ``` + * + * @param array $classmap The post class(es) to use. An associative array where the key is + * the post type and the value the name of the class to use for the comments + * of this post type or a callback that determines the class to use. + */ + $map = \apply_filters('timber/comment/classmap', []); + + $type = \get_post_type($comment->comment_post_ID); + $class = $map[$type] ?? null; + + if (\is_callable($class)) { + $class = $class($comment); + } + + $class ??= Comment::class; + + /** + * Filters the comment class based on your custom criteria. + * + * Maybe you want to set a custom class based upon the comment type? + * This allows you to filter the PHP class, utilizing data from the WP_Comment object. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/comment/class', function( $class, $comment ) { + * if ( $comment->comment_type === 'pingback' ) { + * return PingBackComment::class; + * } + * return $class; + * }, 10, 2 ); + * ``` + * + * @param string $class The class to use. + * @param WP_Comment $comment The comment object. + */ + $class = \apply_filters('timber/comment/class', $class, $comment); + + return $class; + } + + protected function build(WP_Comment $comment): CoreInterface + { + $class = $this->get_comment_class($comment); + + return $class::build($comment); + } + + protected function is_numeric_array($arr) + { + if (!\is_array($arr)) { + return false; + } + foreach (\array_keys($arr) as $k) { + if (!\is_int($k)) { + return false; + } + } + return true; + } +} diff --git a/vendor/timber/timber/src/Factory/MenuFactory.php b/vendor/timber/timber/src/Factory/MenuFactory.php new file mode 100644 index 0000000..d12bea6 --- /dev/null +++ b/vendor/timber/timber/src/Factory/MenuFactory.php @@ -0,0 +1,266 @@ +from_nav_menu_terms($args); + } + + // If $params is a numeric slug, we might get the wrong menu + if (\is_numeric($params)) { + $menu = $this->from_id((int) $params, $args); + } + + if (\is_object($params)) { + $menu = $this->from_object($params, $args); + } + + if (!$menu && \is_string($params)) { + // If $location is the same than some menu slug, we might get the wrong menu + $menu = $this->from_location($params, $args); + if (!$menu) { + $menu = $this->from_slug($params, $args); + } + if (!$menu) { + $menu = $this->from_name($params, $args); + } + } + + return $menu; + } + + /** + * Get a Menu from its location + * + * @return Menu|null + */ + protected function from_nav_menu_terms(array $args = []): ?Menu + { + $menus = \wp_get_nav_menus(); + foreach ($menus as $menu_maybe) { + $menu_items = \wp_get_nav_menu_items($menu_maybe->term_id, [ + 'update_post_term_cache' => false, + ]); + if ($menu_items) { + $menu = $menu_maybe; + break; + } + } + return isset($menu) ? $this->from_object($menu, $args) : null; + } + + /** + * Get a Menu from its location + * + * @return Menu|null + */ + public function from_location(string $location, array $args = []): ?Menu + { + $locations = Timber::get_menu_locations(); + if (!isset($locations[$location])) { + return null; + } + + $term = \get_term_by('id', $locations[$location], 'nav_menu'); + if (!$term) { + return null; + } + + $args['location'] = $location; + + return $this->build($term, $args); + } + + /** + * Get a Menu by its ID + * + * @internal + */ + public function from_id(int $id, array $args = []): ?Menu + { + if (0 === $id) { + return null; + } + + $term = \get_term_by('id', $id, 'nav_menu'); + + if (!$term) { + return null; + } + + $args['menu'] = $id; + + return $this->build($term, $args); + } + + /** + * Get a Menu by its slug + * + * @internal + */ + public function from_slug(string $slug, array $args = []): ?Menu + { + $term = \get_term_by('slug', $slug, 'nav_menu'); + + if (!$term) { + return null; + } + + $args['menu'] = $slug; + + return $this->build($term, $args); + } + + /** + * Get a Menu by its name + * + * @internal + */ + public function from_name(string $name, array $args = []): ?Menu + { + $term = \get_term_by('name', $name, 'nav_menu'); + + if (!$term) { + return null; + } + + $args['menu'] = $name; + + return $this->build($term, $args); + } + + /** + * Get a menu from object + * + * @internal + */ + protected function from_object(object $obj, array $args = []): ?Menu + { + if ($obj instanceof Menu) { + // We already have a Timber Core object of some kind + return $obj; + } + + if ($obj instanceof WP_Term) { + $args['menu'] = $obj; + return $this->build($obj, $args); + } + + throw new InvalidArgumentException(\sprintf( + 'Expected an instance of Timber\CoreInterface or WP_Term, got %s', + $obj::class + )); + } + + /** + * Get a menu class + * + * @internal + */ + protected function get_menu_class($term, $args): string + { + /** + * Filters the class(es) used for different menus. + * + * Read more about this in the documentation for [Menu Class Maps](https://timber.github.io/docs/v2/guides/class-maps/#the-menu-class-map). + * + * The default Menu Class Map will contain class names for locations that map to `Timber\Menu`. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/menu/classmap', function( $classmap ) { + * $custom_classmap = [ + * 'primary' => MenuPrimary::class, + * 'secondary' => MenuSecondary::class, + * ]; + * + * return array_merge( $classmap, $custom_classmap ); + * } ); + * ``` + * + * @param array $classmap The menu class(es) to use. An associative array where the key is + * the location and the value the name of the class to use for this + * menu or a callback that determines the class to use. + */ + $classmap = \apply_filters('timber/menu/classmap', []); + + $location = Timber::get_menu_location($term); + + $class = $classmap[$location] ?? null; + + // If class is a callable, call it to get the actual class name + if (\is_callable($class)) { + $class = $class($term, $args); + } + + // Fallback on the default class + $class ??= Menu::class; + + /** + * Filters the menu class based on your custom criteria. + * + * Maybe the location is not appropriate in some cases. This filter will allow you to filter the class + * on whatever data is available. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/menu/class', function( $class, $term, $args ) { + * if ( $args['depth'] === 1 ) { + * return SingleLevelMenu::class; + * } + * + * return MultiLevelMenu::class; + * }, 10, 3 ); + * ``` + * + * @param string $class The class to use. + * @param WP_Term $term The menu term. + * @param array $args The arguments passed to the menu. + */ + $class = \apply_filters('timber/menu/class', $class, $term, $args); + + return $class; + } + + /** + * Build menu + * + * @param array $args + * @return CoreInterface + */ + protected function build(WP_Term $term, $args): CoreInterface + { + $class = $this->get_menu_class($term, $args); + + return $class::build($term, $args); + } +} diff --git a/vendor/timber/timber/src/Factory/MenuItemFactory.php b/vendor/timber/timber/src/Factory/MenuItemFactory.php new file mode 100644 index 0000000..65547f1 --- /dev/null +++ b/vendor/timber/timber/src/Factory/MenuItemFactory.php @@ -0,0 +1,101 @@ +build($item, $menu); + } + + return null; + } + + protected function build(WP_Post $item, Menu $menu): CoreInterface + { + $class = $this->get_menuitem_class($item, $menu); + + return $class::build($item, $menu); + } + + protected function get_menuitem_class(WP_Post $item, Menu $menu): string + { + /** + * Filters the class(es) used for different menu items. + * + * Read more about this in the documentation for [Menu Item Class Maps](https://timber.github.io/docs/v2/guides/class-maps/#the-menu-item-class-map). + * + * The default Menu Item Class Map will contain class names for locations that map to `Timber\MenuItem`. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/menuitem/classmap', function( $classmap ) { + * $custom_classmap = [ + * 'primary' => MenuItemFooter::class, + * 'secondary' => MenuItemHeader::class, + * ]; + * + * return array_merge( $classmap, $custom_classmap ); + * } ); + * ``` + * + * @param array $classmap The menu item class(es) to use. An associative array where the key is + * the location and the value the name of the class to use for this + * menu item or a callback that determines the class to use. + */ + $classmap = \apply_filters('timber/menuitem/classmap', []); + + $class = $classmap[$menu->theme_location] ?? null; + + // If class is a callable, call it to get the actual class name + if (\is_callable($class)) { + $class = $class($item, $menu); + } + + // Fallback on the default class + $class ??= MenuItem::class; + + /** + * Filters the menu item class + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/menuitem/class', function( $class, $item, $menu ) { + * if ( $item->post_parent ) { + * return SubMenuItem::class; + * } + * + * return MenuItem::class; + * }, 10, 3 ); + * ``` + * + * @param string $class The class to use. + * @param WP_Post $item The menu item. + * @param Menu $menu The menu object. + */ + $class = \apply_filters('timber/menuitem/class', $class, $item, $menu); + return $class; + } +} diff --git a/vendor/timber/timber/src/Factory/PagesMenuFactory.php b/vendor/timber/timber/src/Factory/PagesMenuFactory.php new file mode 100644 index 0000000..4586983 --- /dev/null +++ b/vendor/timber/timber/src/Factory/PagesMenuFactory.php @@ -0,0 +1,75 @@ +build($args); + } + + /** + * Gets the pages menu class. + * + * @internal + * + * @return string + */ + protected function get_menu_class($args): string + { + /** + * Filters the class used for different menus. + * + * Read more about this in the documentation for [Pages Menu Class filter](https://timber.github.io/docs/v2/guides/class-maps/#the-pages-menu-class-filter). + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/pages_menu/class', function( $class ) { + * return ExtendedPagesMenu::class; + * } ); + * ``` + * + * @param string $class The pages menu class to use. + * @param array $args The arguments passed to `Timber::get_pages_menu()`. + */ + $class = \apply_filters('timber/pages_menu/class', PagesMenu::class, $args); + + // If class is a callable, call it to get the actual class name + if (\is_callable($class)) { + $class = $class($args); + } + + // Fallback on the default class. + $class ??= PagesMenu::class; + + return $class; + } + + /** + * Build menu + * + * @param array $args Optional. Args for get_pages(). + * @return CoreInterface + */ + protected function build(array $args = []): CoreInterface + { + $class = $this->get_menu_class($args); + + return $class::build(null, $args); + } +} diff --git a/vendor/timber/timber/src/Factory/PostFactory.php b/vendor/timber/timber/src/Factory/PostFactory.php new file mode 100644 index 0000000..3bf0ae7 --- /dev/null +++ b/vendor/timber/timber/src/Factory/PostFactory.php @@ -0,0 +1,222 @@ +from_id((int) $params); + } + + if ($params instanceof WP_Query) { + return $this->from_wp_query($params); + } + + if (\is_object($params)) { + return $this->from_post_object($params); + } + + if ($this->is_numeric_array($params)) { + return new PostArrayObject(\array_map([$this, 'from'], $params)); + } + + if (\is_array($params) && !empty($params['ID'])) { + return $this->from_id($params['ID']); + } + + if (\is_array($params)) { + return $this->from_wp_query(new WP_Query($params)); + } + + return null; + } + + protected function from_id(int $id): ?Post + { + $wp_post = \get_post($id); + + if (!$wp_post) { + return null; + } + + return $this->build($wp_post); + } + + protected function from_post_object(object $obj): CoreInterface + { + if ($obj instanceof CoreInterface) { + return $obj; + } + + if ($obj instanceof WP_Post) { + return $this->build($obj); + } + + throw new InvalidArgumentException(\sprintf( + 'Expected an instance of Timber\CoreInterface or WP_Post, got %s', + $obj::class + )); + } + + protected function from_wp_query(WP_Query $query): iterable + { + return new PostQuery($query); + } + + protected function get_post_class(WP_Post $post): string + { + /** + * Pseudo filter that checks whether the non-usable filter was used. + * + * @deprecated 2.0.0, use `timber/post/classmap` + */ + if ('deprecated' !== \apply_filters('Timber\PostClassMap', 'deprecated')) { + Helper::doing_it_wrong( + 'The `Timber\PostClassMap` filter', + 'Use the `timber/post/classmap` filter instead.', + '2.0.0' + ); + } + + /** + * Filters the class(es) used for different post types. + * + * Read more about this in the documentation for [Post Class Maps](https://timber.github.io/docs/v2/guides/class-maps/#the-post-class-map). + * + * The default Post Class Map will contain class names for posts, pages that map to + * `Timber\Post` and a callback that will map attachments to `Timber\Attachment` and + * attachments that are images to `Timber\Image`. + * + * Make sure to merge in your additional classes instead of overwriting the whole Class Map. + * + * @since 2.0.0 + * @example + * ``` + * use Book; + * use Page; + * + * add_filter( 'timber/post/classmap', function( $classmap ) { + * $custom_classmap = [ + * 'page' => Page::class, + * 'book' => Book::class, + * ]; + * + * return array_merge( $classmap, $custom_classmap ); + * } ); + * ``` + * + * @param array $classmap The post class(es) to use. An associative array where the key is + * the post type and the value the name of the class to use for this + * post type or a callback that determines the class to use. + */ + $classmap = \apply_filters('timber/post/classmap', [ + 'post' => Post::class, + 'page' => Post::class, + // Apply special logic for attachments. + 'attachment' => fn (WP_Post $attachment) => $this->is_image($attachment) ? Image::class : Attachment::class, + ]); + + $class = $classmap[$post->post_type] ?? null; + + // If class is a callable, call it to get the actual class name + if (\is_callable($class)) { + $class = $class($post); + } + + $class ??= Post::class; + + /** + * Filters the post class based on your custom criteria. + * + * Maybe you want to set a custom class based upon how blocks are used? + * This allows you to filter the PHP class, utilizing data from the WP_Post object. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/post/class', function( $class, $post ) { + * if ( has_blocks($post) ) { + * return GutenbergPost::class; + * } + * + * return $class; + * }, 10, 2 ); + * ``` + * + * @param string $class The class to use. + * @param WP_Post $post The post object. + */ + $class = \apply_filters('timber/post/class', $class, $post); + + return $class; + } + + protected function is_image(WP_Post $post) + { + $src = \get_attached_file($post->ID); + $mimes = \wp_get_mime_types(); + // Add mime types that Timber recognizes as images, regardless of config + $mimes['svg'] = 'image/svg+xml'; + $mimes['webp'] = 'image/webp'; + $check = \wp_check_filetype(PathHelper::basename($src), $mimes); + + /** + * Filters the list of image extensions that will be used to determine if an attachment is an image. + * + * You can use this filter to add or remove image extensions to the list of extensions that will be + * used to determine if an attachment is an image. + * + * @param array $extensions An array of image extensions. + * @since 2.0.0 + */ + $extensions = \apply_filters('timber/post/image_extensions', [ + 'jpg', + 'jpeg', + 'jpe', + 'gif', + 'png', + 'svg', + 'webp', + 'avif', + ]); + + return \in_array($check['ext'], $extensions); + } + + protected function build(WP_Post $post): CoreInterface + { + $class = $this->get_post_class($post); + + return $class::build($post); + } + + protected function is_numeric_array($arr) + { + if (!\is_array($arr)) { + return false; + } + foreach (\array_keys($arr) as $k) { + if (!\is_int($k)) { + return false; + } + } + return true; + } +} diff --git a/vendor/timber/timber/src/Factory/TermFactory.php b/vendor/timber/timber/src/Factory/TermFactory.php new file mode 100644 index 0000000..01d567d --- /dev/null +++ b/vendor/timber/timber/src/Factory/TermFactory.php @@ -0,0 +1,240 @@ +from_id((int) $params); + } + + if (\is_string($params)) { + return $this->from_taxonomy_names([$params]); + } + + if ($params instanceof WP_Term_Query) { + return $this->from_wp_term_query($params); + } + + if (\is_object($params)) { + return $this->from_term_object($params); + } + + if ($this->is_numeric_array($params)) { + if ($this->is_array_of_strings($params)) { + return $this->from_taxonomy_names($params); + } + + return \array_map([$this, 'from'], $params); + } + + if (\is_array($params)) { + return $this->from_wp_term_query(new WP_Term_Query( + $this->filter_query_params($params) + )); + } + + return null; + } + + protected function from_id(int $id): ?Term + { + $wp_term = \get_term($id); + + if (!$wp_term) { + return null; + } + + return $this->build($wp_term); + } + + protected function from_wp_term_query(WP_Term_Query $query) + { + $terms = $query->get_terms(); + + $fields = $query->query_vars['fields']; + if ('all' === $fields || 'all_with_object_id' === $fields) { + return \array_map([$this, 'build'], $terms); + } + + return $terms; + } + + protected function from_term_object(object $obj): CoreInterface + { + if ($obj instanceof CoreInterface) { + // We already have a Timber Core object of some kind + return $obj; + } + + if ($obj instanceof WP_Term) { + return $this->build($obj); + } + + throw new InvalidArgumentException(\sprintf( + 'Expected an instance of Timber\CoreInterface or WP_Term, got %s', + $obj::class + )); + } + + protected function from_taxonomy_names(array $names) + { + return $this->from_wp_term_query(new WP_Term_Query( + $this->filter_query_params([ + 'taxonomy' => $names, + ]) + )); + } + + protected function get_term_class(WP_Term $term): string + { + /** + * Filters the class(es) used for terms of different taxonomies. + * + * The default Term Class Map will contain class names mapped to the build-in post_tag and category taxonomies. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/term/classmap', function( $classmap ) { + * $custom_classmap = [ + * 'expertise' => ExpertiseTerm::class, + * ]; + * + * return array_merge( $classmap, $custom_classmap ); + * } ); + * ``` + * + * @param array $classmap The term class(es) to use. An associative array where the key is + * the taxonomy name and the value the name of the class to use for this + * taxonomy or a callback that determines the class to use. + */ + $map = \apply_filters('timber/term/classmap', [ + 'post_tag' => Term::class, + 'category' => Term::class, + ]); + + $class = $map[$term->taxonomy] ?? null; + + if (\is_callable($class)) { + $class = $class($term); + } + + $class ??= Term::class; + + /** + * Filters the term class based on your custom criteria. + * + * Maybe you want to set a custom class based upon a certain category? + * This allows you to filter the PHP class, utilizing data from the WP_Term object. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/term/class', function( $class, $term ) { + * if ( get_term_meta($term->term_id, 'is_special_category', true) ) { + * return MyCustomTermClass::class; + * } + * + * return $class; + * }, 10, 2 ); + * ``` + * + * @param string $class The class to use. + * @param WP_Term $term The term object. + */ + $class = \apply_filters('timber/term/class', $class, $term); + + return $class; + } + + protected function build(WP_Term $term): CoreInterface + { + $class = $this->get_term_class($term); + + return $class::build($term); + } + + protected function correct_tax_key(array $params) + { + $corrections = [ + 'taxonomies' => 'taxonomy', + 'taxs' => 'taxonomy', + 'tax' => 'taxonomy', + ]; + + foreach ($corrections as $mistake => $correction) { + if (isset($params[$mistake])) { + $params[$correction] = $params[$mistake]; + } + } + + return $params; + } + + protected function correct_taxonomies($tax): array + { + $taxonomies = \is_array($tax) ? $tax : [$tax]; + + $corrections = [ + 'categories' => 'category', + 'tags' => 'post_tag', + 'tag' => 'post_tag', + ]; + + return \array_map(fn ($taxonomy) => $corrections[$taxonomy] ?? $taxonomy, $taxonomies); + } + + protected function filter_query_params(array $params) + { + $params = $this->correct_tax_key($params); + + if (isset($params['taxonomy'])) { + $params['taxonomy'] = $this->correct_taxonomies($params['taxonomy']); + } + + $include = $params['term_id'] ?? null; + if ($include) { + $params['include'] = \is_array($include) ? $include : [$include]; + } + + return $params; + } + + protected function is_numeric_array($arr) + { + if (!\is_array($arr)) { + return false; + } + foreach (\array_keys($arr) as $k) { + if (!\is_int($k)) { + return false; + } + } + return true; + } + + protected function is_array_of_strings($arr) + { + if (!\is_array($arr)) { + return false; + } + foreach ($arr as $v) { + if (!\is_string($v)) { + return false; + } + } + return true; + } +} diff --git a/vendor/timber/timber/src/Factory/UserFactory.php b/vendor/timber/timber/src/Factory/UserFactory.php new file mode 100644 index 0000000..f528171 --- /dev/null +++ b/vendor/timber/timber/src/Factory/UserFactory.php @@ -0,0 +1,141 @@ +from_id($params); + } + + if ($params instanceof WP_User_Query) { + return $this->from_wp_user_query($params); + } + + if (\is_object($params)) { + // assume we have some kind of WP user object, Timber or otherwise + return $this->from_user_object($params); + } + + if ($this->is_numeric_array($params)) { + // we have a numeric array of objects and/or IDs + return \array_map([$this, 'from'], $params); + } + + if (\is_array($params)) { + // we have a query array to be passed to WP_User_Query::__construct() + return $this->from_wp_user_query(new WP_User_Query($params)); + } + + return null; + } + + protected function from_id(int $id) + { + $wp_user = \get_user_by('id', $id); + + return $wp_user ? $this->build($wp_user) : null; + } + + protected function from_user_object($obj): CoreInterface + { + if ($obj instanceof CoreInterface) { + // we already have some kind of Timber Core object + return $obj; + } + + if ($obj instanceof WP_User) { + return $this->build($obj); + } + + throw new InvalidArgumentException(\sprintf( + 'Expected an instance of Timber\CoreInterface or WP_User, got %s', + $obj::class + )); + } + + protected function from_wp_user_query(WP_User_Query $query): iterable + { + return \array_map([$this, 'build'], $query->get_results()); + } + + protected function build(WP_User $user): CoreInterface + { + /** + * Filters the name of the PHP class used to instantiate `Timber\User` objects. + * + * The User Class Map receives the default `Timber\User` class and a `WP_User` object. You + * should be able to decide which class to use based on that user object. + * + * @api + * @since 2.0.0 + * @example + * ```php + * use Administrator; + * use Editor; + * + * add_filter( 'timber/user/class', function( $class, \WP_User $user ) { + * if ( in_array( 'editor', $user->roles, true ) ) { + * return Editor::class; + * } elseif ( in_array( 'author', $user->roles, true ) ) { + * return Author::class; + * } + * + * return $class; + * }, 10, 2 ); + * ``` + * + * @param string $class The name of the class. Default `Timber\User`. + * @param WP_User $user The `WP_User` object that is used as the base for the + * `Timber\User` object. + */ + $class = \apply_filters('timber/user/class', User::class, $user); + + return $class::build($user); + } + + protected function is_numeric_array($arr) + { + if (!\is_array($arr)) { + return false; + } + foreach (\array_keys($arr) as $k) { + if (!\is_int($k)) { + return false; + } + } + return true; + } +} diff --git a/vendor/timber/timber/src/FunctionWrapper.php b/vendor/timber/timber/src/FunctionWrapper.php new file mode 100644 index 0000000..02d730e --- /dev/null +++ b/vendor/timber/timber/src/FunctionWrapper.php @@ -0,0 +1,91 @@ +call(); + } catch (Exception $e) { + return 'Caught exception: ' . $e->getMessage() . "\n"; + } + } + + /** + * + * + * @param callable $function + * @param array $args + * @param bool $return_output_buffer + */ + public function __construct( + $function, + private $args = [], + private $return_output_buffer = false + ) { + if (\is_array($function)) { + if ((\is_string($function[0]) && \class_exists($function[0])) || \gettype($function[0]) === 'object') { + $this->_class = $function[0]; + } + + if (\is_string($function[1])) { + $this->_function = $function[1]; + } + } else { + $this->_function = $function; + } + } + + /** + * + * + * @return string + */ + public function call() + { + $args = $this->_parse_args(\func_get_args(), $this->args); + $callable = (isset($this->_class)) ? [$this->_class, $this->_function] : $this->_function; + + if ($this->return_output_buffer) { + return Helper::ob_function($callable, $args); + } else { + return \call_user_func_array($callable, $args); + } + } + + /** + * + * + * @param array $args + * @param array $defaults + * @return array + */ + private function _parse_args($args, $defaults) + { + $_arg = \reset($defaults); + + foreach ($args as $index => $arg) { + $defaults[$index] = \is_null($arg) ? $_arg : $arg; + $_arg = \next($defaults); + } + + return $defaults; + } +} diff --git a/vendor/timber/timber/src/Helper.php b/vendor/timber/timber/src/Helper.php new file mode 100644 index 0000000..8add161 --- /dev/null +++ b/vendor/timber/timber/src/Helper.php @@ -0,0 +1,727 @@ + Timber\Helper::transient( 'user-' . $uid . '-favorites' , function() use ( $uid ) { + * // Some expensive query here that’s doing something you want to store to a transient. + * return $favorites; + * }, 600 ), + * ] ); + * + * Timber::render('single.twig', $context); + * ``` + * + * @param string $slug Unique identifier for transient + * @param callable $callback Callback that generates the data that's to be cached + * @param integer $transient_time (optional) Expiration of transients in seconds + * @param integer $lock_timeout (optional) How long (in seconds) to lock the transient to prevent race conditions + * @param boolean $force (optional) Force callback to be executed when transient is locked + * + * @return mixed + */ + public static function transient($slug, $callback, $transient_time = 0, $lock_timeout = 5, $force = false) + { + /** + * Filters the transient slug. + * + * This might be useful if you are using a multilingual solution. + * + * @since 0.22.6 + * + * @param string $slug The slug for the transient. + */ + $slug = \apply_filters('timber/transient/slug', $slug); + + $enable_transients = ($transient_time === false || (\defined('WP_DISABLE_TRANSIENTS') && WP_DISABLE_TRANSIENTS)) ? false : true; + $data = $enable_transients ? \get_transient($slug) : false; + + if (false === $data) { + $data = self::handle_transient_locking($slug, $callback, $transient_time, $lock_timeout, $force, $enable_transients); + } + return $data; + } + + /** + * Does the dirty work of locking the transient, running the callback and unlocking. + * + * @internal + * + * @param string $slug Unique identifier for transient + * @param callable $callback Callback that generates the data that's to be cached + * @param integer $transient_time Expiration of transients in seconds + * @param integer $lock_timeout How long (in seconds) to lock the transient to prevent race conditions + * @param boolean $force Force callback to be executed when transient is locked + * @param boolean $enable_transients Force callback to be executed when transient is locked + */ + protected static function handle_transient_locking($slug, $callback, $transient_time, $lock_timeout, $force, $enable_transients) + { + if ($enable_transients && self::_is_transient_locked($slug)) { + /** + * Filters whether to force a locked transients to be regenerated. + * + * If a transient is locked, it means that another process is currently generating the data. + * If you want to force the transient to be regenerated, during that process, you can set this + * filter to true. + * + * @since 2.0.0 + * @param bool $force Whether to force a locked transient to be regenerated. + */ + $force = \apply_filters('timber/transient/force_transients', $force); + + /** + * Filters whether to force a locked transients to be regenerated. + * + * If a transient is locked, it means that another process is currently generating the data. + * If you want to force the transient to be regenerated, during that process, you can set this + * filter to true. + * + * @deprecated 2.0.0, use `timber/transient/force_transients` + */ + $force = \apply_filters_deprecated( + 'timber_force_transients', + [$force], + '2.0.0', + 'timber/transient/force_transients' + ); + + /** + * Filters whether to force a specific locked transients to be regenerated. + * + * If a transient is locked, it means that another process is currently generating the data. + * If you want to force the transient to be regenerated during that process, you can set this value to true. + * + * @example + * ```php + * + * add_filter( 'timber/transient/force_transient_mycustumslug', function($force) { + * if(false == something_special_has_occurred()){ + * return false; + * } + * + * return true; + * }, 10 ); + * ``` + * @since 2.0.0 + * + * @param bool $force Whether to force a locked transient to be regenerated. + */ + $force = \apply_filters("timber/transient/force_transient_{$slug}", $force); + + /** + * Filters whether to force a specific locked transients to be regenerated. + * + * If a transient is locked, it means that another process is currently generating the data. + * If you want to force the transient to be regenerated, during that process, you can set this value to true. + * `$slug` The transient slug. + * + * @param bool $force Whether to force a locked transient to be regenerated. + * @deprecated 2.0.0, use `timber/transient/force_transient_{$slug}` + */ + $force = \apply_filters_deprecated( + "timber_force_transient_{$slug}", + [$force], + '2.0.0', + "timber/transient/force_transient_{$slug}" + ); + + if (!$force) { + //the server is currently executing the process. + //We're just gonna dump these users. Sorry! + return false; + } + $enable_transients = false; + } + // lock timeout shouldn't be higher than 5 seconds, unless + // remote calls with high timeouts are made here + if ($enable_transients) { + self::_lock_transient($slug, $lock_timeout); + } + $data = $callback(); + if ($enable_transients) { + \set_transient($slug, $data, $transient_time); + self::_unlock_transient($slug); + } + return $data; + } + + /** + * @internal + * @param string $slug + * @param integer $lock_timeout + */ + public static function _lock_transient($slug, $lock_timeout) + { + \set_transient($slug . '_lock', true, $lock_timeout); + } + + /** + * @internal + * @param string $slug + */ + public static function _unlock_transient($slug) + { + \delete_transient($slug . '_lock'); + } + + /** + * @internal + * @param string $slug + */ + public static function _is_transient_locked($slug) + { + return (bool) \get_transient($slug . '_lock'); + } + + /* These are for measuring page render time */ + + /** + * For measuring time, this will start a timer. + * + * @api + * @return float + */ + public static function start_timer() + { + $time = \microtime(); + $time = \explode(' ', $time); + $time = (float) $time[1] + (float) $time[0]; + return $time; + } + + /** + * For stopping time and getting the data. + * + * @api + * @example + * ```php + * $start = Timber\Helper::start_timer(); + * // do some stuff that takes awhile + * echo Timber\Helper::stop_timer( $start ); + * ``` + * + * @param int $start + * @return string + */ + public static function stop_timer($start) + { + $time = \microtime(); + $time = \explode(' ', $time); + $time = (float) $time[1] + (float) $time[0]; + $finish = $time; + $total_time = \round(($finish - $start), 4); + return $total_time . ' seconds.'; + } + + /* Function Utilities + ======================== */ + + /** + * Calls a function with an output buffer. This is useful if you have a function that outputs + * text that you want to capture and use within a twig template. + * + * @api + * @example + * ```php + * function the_form() { + * echo '
    incorrectly. %2$s %3$s', + $function, + $message, + $version + ); + + // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error + // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped + \trigger_error('[ Timber ] ' . $error_message); + } + } + + /** + * Triggers a deprecation warning. + * + * If you want to catch errors like these in tests, then add the @expectedDeprecated tag to the + * DocBlock. E.g.: "@expectedDeprecated {{ TimberImage() }}". + * + * @api + * @see \_deprecated_function() + * + * @param string $function The name of the deprecated function/method. + * @param string $replacement The name of the function/method to use instead. + * @param string $version The version of Timber when the function was deprecated. + * + * @return void + */ + public static function deprecated($function, $replacement, $version) + { + /** + * Fires when a deprecated function is being used. + * + * @param string $function The function that was called. + * @param string $replacement The name of the function/method to use instead. + * @param string $version The version of Timber where the message was added. + */ + \do_action('deprecated_function_run', $function, $replacement, $version); + + if (!WP_DEBUG) { + return; + } + + /** + * Filters whether to trigger an error for deprecated functions. + * + * @since WordPress 2.5.0 + * + * @param bool $trigger Whether to trigger the error for deprecated functions. Default true. + */ + if (!\apply_filters('deprecated_function_trigger_error', true)) { + return; + } + + if (!\is_null($replacement)) { + $error_message = \sprintf( + '%1$s is deprecated since Timber version %2$s! Use %3$s instead.', + $function, + $version, + $replacement + ); + } else { + $error_message = \sprintf( + '%1$s is deprecated since Timber version %2$s with no alternative available.', + $function, + $version + ); + } + + // phpcs:disable WordPress.PHP.DevelopmentFunctions.error_log_trigger_error + // phpcs:disable WordPress.Security.EscapeOutput.OutputNotEscaped + \trigger_error('[ Timber ] ' . $error_message); + } + + /** + * @api + * + * @param string $separator + * @param string $seplocation + * @return string + */ + public static function get_wp_title($separator = ' ', $seplocation = 'left') + { + /** + * Filters the separator used for the page title. + * + * @since 2.0.0 + * + * @param string $separator The separator to use. Default `' '`. + */ + $separator = \apply_filters('timber/helper/wp_title_separator', $separator); + + /** + * Filters the separator used for the page title. + * + * @deprecated 2.0.0, use `timber/helper/wp_title_separator` + */ + $separator = \apply_filters_deprecated('timber_wp_title_seperator', [$separator], '2.0.0', 'timber/helper/wp_title_separator'); + + return \trim((string) \wp_title($separator, false, $seplocation)); + } + + /** + * Sorts object arrays by properties. + * + * @api + * + * @param array $array The array of objects to sort. + * @param string $prop The property to sort by. + * + * @return void + */ + public static function osort(&$array, $prop) + { + \usort($array, fn ($a, $b) => $a->$prop > $b->$prop ? 1 : -1); + } + + /** + * @api + * + * @param array $arr + * @return bool + */ + public static function is_array_assoc($arr) + { + if (!\is_array($arr)) { + return false; + } + return (bool) \count(\array_filter(\array_keys($arr), 'is_string')); + } + + /** + * @api + * + * @param array $array + * @return stdClass + */ + public static function array_to_object($array) + { + $obj = new stdClass(); + foreach ($array as $k => $v) { + if (\is_array($v)) { + $obj->{$k} = self::array_to_object($v); //RECURSION + } else { + $obj->{$k} = $v; + } + } + return $obj; + } + + /** + * @api + * + * @param array $array + * @param string $key + * @return bool|int + */ + public static function get_object_index_by_property($array, $key, mixed $value) + { + if (\is_array($array)) { + $i = 0; + foreach ($array as $arr) { + if (\is_array($arr)) { + if ($arr[$key] == $value) { + return $i; + } + } else { + if ($arr->$key == $value) { + return $i; + } + } + $i++; + } + } + return false; + } + + /** + * @api + * + * @param array $array + * @param string $key + * @return array|null + * @throws Exception + */ + public static function get_object_by_property($array, $key, mixed $value) + { + if (\is_array($array)) { + foreach ($array as $arr) { + if ($arr->$key == $value) { + return $arr; + } + } + return false; + } + throw new InvalidArgumentException('$array is not an array, got:'); + } + + /** + * @api + * + * @param array $array + * @param int $len + * @return array + */ + public static function array_truncate($array, $len) + { + if (\sizeof($array) > $len) { + $array = \array_splice($array, 0, $len); + } + return $array; + } + + /* Bool Utilities + ======================== */ + /** + * @api + * + * @return bool + */ + public static function is_true(mixed $value) + { + if (isset($value)) { + if (\is_string($value)) { + $value = \strtolower($value); + } + if (($value == 'true' || $value === 1 || $value === '1' || $value == true) && $value !== false && $value !== 'false') { + return true; + } + } + return false; + } + + /** + * Is the number even? Let's find out. + * + * @api + * + * @param int $i number to test. + * @return bool + */ + public static function iseven($i) + { + return ($i % 2) === 0; + } + + /** + * Is the number odd? Let's find out. + * + * @api + * + * @param int $i number to test. + * @return bool + */ + public static function isodd($i) + { + return ($i % 2) !== 0; + } + + /** + * Plucks the values of a certain key from an array of objects + * + * @api + * + * @param array $array + * @param string $key + * + * @return array + */ + public static function pluck($array, $key) + { + $return = []; + foreach ($array as $obj) { + if (\is_object($obj) && \method_exists($obj, $key)) { + $return[] = $obj->$key(); + } elseif (\is_object($obj) && \property_exists($obj, $key)) { + $return[] = $obj->$key; + } elseif (\is_array($obj) && isset($obj[$key])) { + $return[] = $obj[$key]; + } + } + return $return; + } + + /** + * Filters a list of objects, based on a set of key => value arguments. + * Uses WordPress WP_List_Util's filter. + * + * @api + * @since 1.5.3 + * @ticket #1594 + * + * @param array $list to filter. + * @param string|array $args to search for. + * @param string $operator to use (AND, NOT, OR). + * @return array + */ + public static function wp_list_filter($list, $args, $operator = 'AND') + { + if (!\is_array($args)) { + $args = [ + 'slug' => $args, + ]; + } + + if (!\is_array($list) && !\is_a($list, 'Traversable')) { + return []; + } + + $util = new WP_List_Util($list); + return $util->filter($args, $operator); + } + + /** + * Converts a WP object (WP_Post, WP_Term) into its + * equivalent Timber class (Timber\Post, Timber\Term). + * + * If no match is found the function will return the initial argument. + * + * @api + * @param mixed $obj WP Object to convert + * @return mixed Instance of equivalent Timber object, or the argument if no match is found + */ + public static function convert_wp_object(mixed $obj) + { + if ($obj instanceof WP_Post) { + static $postFactory; + $postFactory = $postFactory ?: new PostFactory(); + return $postFactory->from($obj->ID); + } elseif ($obj instanceof WP_Term) { + return Timber::get_term($obj->term_id); + } elseif ($obj instanceof WP_User) { + return Timber::get_user($obj->ID); + } + + return $obj; + } +} diff --git a/vendor/timber/timber/src/Image.php b/vendor/timber/timber/src/Image.php new file mode 100644 index 0000000..2d9ec7f --- /dev/null +++ b/vendor/timber/timber/src/Image.php @@ -0,0 +1,370 @@ +cover_image; + * + * $context['cover_image'] = Timber::get_post($cover_image_id); + * + * Timber::render('single.twig', $context); + * ``` + * + * ```twig + *
    + * + *

    {{post.title}}

    + *
    + * {{post.content}} + *
    + * + * Another way to initialize images as Timber\Image objects, but within Twig + *
    + * ``` + * + * ```html + *
    + * + *

    Now you've done it!

    + *
    + * Whatever whatever + *
    + * Another way to initialize images as Timber\Image objects, but within Twig + *
    + * ``` + */ +class Image extends Attachment implements ImageInterface +{ + /** + * Representation. + * + * @api + * @var string What does this class represent in WordPress terms? + */ + public static $representation = 'image'; + + /** + * Image sizes. + * + * @api + * @var array An array of available sizes for the image. + */ + protected array $sizes; + + /** + * Image dimensions. + * + * @internal + * @var ImageDimensions stores Image Dimensions in a structured way. + */ + protected ImageDimensions $image_dimensions; + + /** + * Gets the Image information. + * + * @internal + * + * @param array $data Data to update. + * @return array + */ + protected function get_info(array $data): array + { + $data = parent::get_info($data); + + if ($this->file_loc()) { + $data['image_dimensions'] = new ImageDimensions($this->file_loc()); + } + + return $data; + } + + /** + * Processes an image's dimensions. + * @deprecated 2.0.0, use `{{ image.width }}` or `{{ image.height }}` in Twig + * @internal + * @param string $dim + * @return array|int + */ + protected function get_dimensions($dim) + { + Helper::deprecated( + 'Image::get_dimensions()', + 'Image::get_width() | Image::get_height()', + '2.0.0' + ); + return [$this->image_dimensions->width(), $this->image_dimensions->height()]; + } + + /** + * @deprecated 2.0.0, use Image::get_dimension_loaded + * @internal + * @param string|null $dim + * @return array|int + */ + protected function get_dimensions_loaded($dim) + { + Helper::deprecated( + 'Image::get_dimensions()', + 'Image::get_width() or Image::get_height()', + '2.0.0' + ); + + return $this->image_dimensions->get_dimension($dim); + } + + /** + * @deprecated 2.0.0, use Image::meta to retrieve specific fields + * @return array + */ + protected function get_post_custom($iid) + { + Helper::deprecated( + '{{ image.get_post_custom( image.id ) }}', + "{{ image.meta('my_field') }}", + '2.0.0' + ); + $pc = \get_post_custom($iid); + if (\is_bool($pc)) { + return []; + } + return $pc; + } + + /** + * Gets the source URL for the image. + * + * You can use WordPress image sizes (including the ones you registered with your theme or + * plugin) by passing the name of the size to this function (like `medium` or `large`). If the + * WordPress size has not been generated, it will return an empty string. + * + * @api + * @example + * ```twig + * + * + * ``` + * ```html + * + * + * ``` + * + * @param string $size Optional. The requested image size. This can be a size that was in + * WordPress. Example: `medium` or `large`. Default `full`. + * + * @return string The src URL for the image. + */ + public function src($size = 'full'): string + { + if (isset($this->abs_url)) { + return URLHelper::maybe_secure_url($this->abs_url); + } + + $src = \wp_get_attachment_image_src($this->ID, $size); + $src = $src[0]; + + /** + * Filters the src URL for a `Timber\Image`. + * + * @see \Timber\Image::src() + * @since 0.21.7 + * + * @param string $src The image src. + * @param int $id The image ID. + */ + $src = \apply_filters('timber/image/src', $src, $this->ID); + + /** + * Filters the src URL for a `Timber\Image`. + * + * @deprecated 2.0.0, use `timber/image/src` + */ + $src = \apply_filters_deprecated( + 'timber_image_src', + [$src, $this->ID], + '2.0.0', + 'timber/image/src' + ); + + return $src; + } + + /** + * Get image sizes. + * + * @return array + */ + public function sizes(): array + { + return $this->sizes ?? ($this->sizes = (array) $this->metadata('sizes')); + } + + /** + * Gets the width of the image in pixels. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return int The width of the image in pixels. + */ + public function width() + { + return $this->image_dimensions->width(); + } + + /** + * Gets the height of the image in pixels. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return int The height of the image in pixels. + */ + public function height() + { + return $this->image_dimensions->height(); + } + + /** + * Gets the aspect ratio of the image. + * + * @api + * @example + * ```twig + * {% if post.thumbnail.aspect < 1 %} + * {# handle vertical image #} + * A basketball player + * {% else %} + * A sumo wrestler + * {% endif %} + * ``` + * + * @return float The aspect ratio of the image. + */ + public function aspect() + { + return $this->image_dimensions->aspect(); + } + + /** + * Gets the alt text for an image. + * + * For better accessibility, you should always add an alt attribute to your images, even if it’s + * empty. + * + * @api + * @example + * ```twig + * {{ image.alt }} + * ``` + * ```html + * You should always add alt texts to your images for better accessibility + * ``` + * + * @return string|null Alt text stored in WordPress. + */ + public function alt(): ?string + { + $alt = $this->meta('_wp_attachment_image_alt'); + return \trim((string) \wp_strip_all_tags($alt)); + } + + /** + * Gets dimension for an image. + * @deprecated 2.0.0, use `{{ image.width }}` or `{{ image.height }}` in Twig + * @internal + * + * @param string $dimension The requested dimension. Either `width` or `height`. + * @return int|null The requested dimension. Null if image file couldn’t be found. + */ + protected function get_dimension($dimension) + { + Helper::deprecated( + 'Image::get_dimension()', + 'Image::get_width() or Image::get_height()', + '2.0.0' + ); + return $this->image_dimensions->get_dimension($dimension); + } + + /** + * Gets already loaded dimension values. + * + * @internal + * + * @param string|null $dim Optional. The requested dimension. Either `width` or `height`. + * @return int The requested dimension in pixels. + */ + protected function get_dimension_loaded($dim = null) + { + return $this->image_dimensions->get_dimension($dim); + } + + /** + * Gets the srcset attribute for an image based on a WordPress image size. + * + * @api + * @example + * ```twig + *

    {{ post.title }}

    + * + * ``` + * ```html + * + * ``` + * @param string $size An image size known to WordPress (like "medium"). + * + * @return string|null + */ + public function srcset(string $size = 'full'): ?string + { + return \wp_get_attachment_image_srcset($this->ID, $size) ?: null; + } + + /** + * Gets the sizes attribute for an image based on a WordPress image size. + * + * @api + * @example + * ```twig + *

    {{ post.title }}

    + * + * ``` + * ```html + * + * ``` + * @param string $size An image size known to WordPress (like "medium"). + * @return string|null + */ + public function img_sizes(string $size = 'full'): ?string + { + return \wp_get_attachment_image_sizes($this->ID, $size) ?: null; + } +} diff --git a/vendor/timber/timber/src/Image/Operation.php b/vendor/timber/timber/src/Image/Operation.php new file mode 100644 index 0000000..d562bba --- /dev/null +++ b/vendor/timber/timber/src/Image/Operation.php @@ -0,0 +1,62 @@ + 255, 'green' => 20, 'blue' => 85); + */ + public static function hexrgb($hexstr) + { + $hexstr = \str_replace('#', '', $hexstr); + if (\strlen($hexstr) == 3) { + $hexstr = $hexstr[0] . $hexstr[0] . $hexstr[1] . $hexstr[1] . $hexstr[2] . $hexstr[2]; + } + $int = \hexdec($hexstr); + return [ + "red" => 0xFF & ($int >> 0x10), + "green" => 0xFF & ($int >> 0x8), + "blue" => 0xFF & $int, + ]; + } + + public static function rgbhex($r, $g, $b) + { + return '#' . \sprintf('%02x', $r) . \sprintf('%02x', $g) . \sprintf('%02x', $b); + } +} diff --git a/vendor/timber/timber/src/Image/Operation/Letterbox.php b/vendor/timber/timber/src/Image/Operation/Letterbox.php new file mode 100644 index 0000000..c1a5190 --- /dev/null +++ b/vendor/timber/timber/src/Image/Operation/Letterbox.php @@ -0,0 +1,135 @@ +color; + if (!$color) { + $color = 'trans'; + } + $color = \str_replace('#', '', $color); + $newbase = $src_filename . '-lbox-' . $this->w . 'x' . $this->h . '-' . $color; + $new_name = $newbase . '.' . $src_extension; + return $new_name; + } + + /** + * Performs the actual image manipulation, + * including saving the target file. + * + * @param string $load_filename filepath (not URL) to source file + * (ex: /src/var/www/wp-content/uploads/my-pic.jpg) + * @param string $save_filename filepath (not URL) where result file should be saved + * (ex: /src/var/www/wp-content/uploads/my-pic-lbox-300x200-FF3366.jpg) + * @return bool true if everything went fine, false otherwise + */ + public function run($load_filename, $save_filename) + { + // Attempt to check if SVG. + if (ImageHelper::is_svg($load_filename)) { + return false; + } + + $w = $this->w; + $h = $this->h; + + $bg = \imagecreatetruecolor($w, $h); + if (!$this->color) { + \imagesavealpha($bg, true); + $bgColor = \imagecolorallocatealpha($bg, 0, 0, 0, 127); + } else { + $c = self::hexrgb($this->color); + $bgColor = \imagecolorallocate($bg, $c['red'], $c['green'], $c['blue']); + } + + \imagefill($bg, 0, 0, $bgColor); + $image = \wp_get_image_editor($load_filename); + if (!\is_wp_error($image)) { + $current_size = $image->get_size(); + $quality = $image->get_quality(); + $ow = $current_size['width']; + $oh = $current_size['height']; + $new_aspect = $w / $h; + $old_aspect = $ow / $oh; + if ($new_aspect > $old_aspect) { + //taller than goal + $h_scale = $h / $oh; + $owt = $ow * $h_scale; + $y = 0; + $x = $w / 2 - $owt / 2; + $oht = $h; + } else { + $w_scale = $w / $ow; + $oht = $oh * $w_scale; + $x = 0; + $y = $h / 2 - $oht / 2; + $owt = $w; + } + + $image->crop(0, 0, \round($ow), \round($oh), \round($owt), \round($oht)); + + $result = $image->save($save_filename); + $func = 'imagecreatefromjpeg'; + $save_func = 'imagejpeg'; + $ext = PathHelper::pathinfo($save_filename, PATHINFO_EXTENSION); + if ($ext == 'gif') { + $func = 'imagecreatefromgif'; + $save_func = 'imagegif'; + } elseif ($ext == 'png') { + $func = 'imagecreatefrompng'; + $save_func = 'imagepng'; + if ($quality > 9) { + $quality = $quality / 10; + $quality = \round(10 - $quality); + } + } elseif ($ext == 'webp') { + $func = 'imagecreatefromwebp'; + $save_func = 'imagewebp'; + } + $image = $func($save_filename); + \imagecopy($bg, $image, \round($x), \round($y), 0, 0, \round($owt), \round($oht)); + if ($save_func === 'imagegif') { + return $save_func($bg, $save_filename); + } + return $save_func($bg, $save_filename, $quality); + } + Helper::error_log($image); + return false; + } +} diff --git a/vendor/timber/timber/src/Image/Operation/Resize.php b/vendor/timber/timber/src/Image/Operation/Resize.php new file mode 100644 index 0000000..31177ee --- /dev/null +++ b/vendor/timber/timber/src/Image/Operation/Resize.php @@ -0,0 +1,240 @@ +crop = $crop; + } + + /** + * @param string $src_filename the basename of the file (ex: my-awesome-pic) + * @param string $src_extension the extension (ex: .jpg) + * @return string the final filename to be used (ex: my-awesome-pic-300x200-c-default.jpg) + */ + public function filename($src_filename, $src_extension) + { + $w = 0; + $h = 0; + if ($this->w) { + $w = $this->w; + } + if ($this->h) { + $h = $this->h; + } + $result = $src_filename . '-' . $w . 'x' . $h . '-c-' . ($this->crop ?: 'f'); // Crop will be either user named or f (false) + if ($src_extension) { + $result .= '.' . $src_extension; + } + return $result; + } + + /** + * Run a resize as animated GIF (if the server supports it) + * + * @param string $load_filename the name of the file to resize. + * @param string $save_filename the desired name of the file to save. + * @param WP_Image_Editor $editor the image editor we're using. + * @return bool + */ + protected function run_animated_gif($load_filename, $save_filename, WP_Image_Editor $editor) + { + $w = $this->w; + $h = $this->h; + if (!\class_exists('Imagick') || (\defined('TEST_NO_IMAGICK') && TEST_NO_IMAGICK)) { + Helper::warn('Cannot resize GIF, Imagick is not installed'); + return false; + } + $image = new Imagick($load_filename); + $image = $image->coalesceImages(); + $crop = $this->get_target_sizes($editor); + foreach ($image as $frame) { + $frame->cropImage($crop['src_w'], $crop['src_h'], \round($crop['x']), \round($crop['y'])); + $frame->thumbnailImage($w, $h); + $frame->setImagePage($w, $h, 0, 0); + } + $image = $image->deconstructImages(); + return $image->writeImages($save_filename, true); + } + + protected function get_target_sizes(WP_Image_Editor $image) + { + $w = $this->w; + $h = $this->h; + $crop = $this->crop; + + $current_size = $image->get_size(); + $src_w = $current_size['width']; + $src_h = $current_size['height']; + $src_ratio = $src_w / $src_h; + if (!$h) { + $h = \round($w / $src_ratio); + } + if (!$w) { + //the user wants to resize based on constant height + $w = \round($h * $src_ratio); + } + + if (!$crop) { + return [ + 'x' => 0, + 'y' => 0, + 'src_w' => $src_w, + 'src_h' => $src_h, + 'target_w' => $w, + 'target_h' => $h, + ]; + } + // Get ratios + $dest_ratio = $w / $h; + $src_wt = $src_h * $dest_ratio; + $src_ht = $src_w / $dest_ratio; + $src_x = $src_w / 2 - $src_wt / 2; + $src_y = ($src_h - $src_ht) / 6; + //now specific overrides based on options: + switch ($crop) { + case 'center': + // Get source x and y + $src_x = \round(($src_w - $src_wt) / 2); + $src_y = \round(($src_h - $src_ht) / 2); + break; + + case 'top': + $src_y = 0; + break; + + case 'bottom': + $src_y = $src_h - $src_ht; + break; + + case 'top-center': + $src_y = \round(($src_h - $src_ht) / 4); + break; + + case 'bottom-center': + $src_y = $src_h - $src_ht - \round(($src_h - $src_ht) / 4); + break; + + case 'left': + $src_x = 0; + break; + + case 'right': + $src_x = $src_w - $src_wt; + break; + } + // Crop the image + return ($dest_ratio > $src_ratio) + ? [ + 'x' => 0, + 'y' => $src_y, + 'src_w' => $src_w, + 'src_h' => $src_ht, + 'target_w' => $w, + 'target_h' => $h, + ] + : [ + 'x' => $src_x, + 'y' => 0, + 'src_w' => $src_wt, + 'src_h' => $src_h, + 'target_w' => $w, + 'target_h' => $h, + ]; + } + + /** + * Performs the actual image manipulation, + * including saving the target file. + * + * @param string $load_filename filepath (not URL) to source file + * (ex: /src/var/www/wp-content/uploads/my-pic.jpg) + * @param string $save_filename filepath (not URL) where result file should be saved + * (ex: /src/var/www/wp-content/uploads/my-pic-300x200-c-default.jpg) + * @return boolean|null true if everything went fine, false otherwise + */ + public function run($load_filename, $save_filename) + { + // Attempt to check if SVG. + if (ImageHelper::is_svg($load_filename)) { + return false; + } + $image = \wp_get_image_editor($load_filename); + if (!\is_wp_error($image)) { + //should be resized by gif resizer + if (ImageHelper::is_animated_gif($load_filename)) { + //attempt to resize, return if successful proceed if not + $gif = $this->run_animated_gif($load_filename, $save_filename, $image); + if ($gif) { + return true; + } + } + + $crop = $this->get_target_sizes($image); + $image->crop( + (int) \round($crop['x']), + (int) \round($crop['y']), + (int) \round($crop['src_w']), + (int) \round($crop['src_h']), + (int) \round($crop['target_w']), + (int) \round($crop['target_h']) + ); + $quality = \apply_filters('wp_editor_set_quality', 82, 'image/jpeg'); + $image->set_quality($quality); + $result = $image->save($save_filename); + if (\is_wp_error($result)) { + // @codeCoverageIgnoreStart + Helper::error_log('Error resizing image'); + Helper::error_log($result); + return false; + // @codeCoverageIgnoreEnd + } else { + return true; + } + } elseif (isset($image->error_data['error_loading_image'])) { + // @codeCoverageIgnoreStart + Helper::error_log('Error loading ' . $image->error_data['error_loading_image']); + } else { + if (!\extension_loaded('gd')) { + Helper::error_log('Can not resize image, please install php-gd'); + } else { + Helper::error_log($image); + } + // @codeCoverageIgnoreEnd + } + + return false; + } +} diff --git a/vendor/timber/timber/src/Image/Operation/Retina.php b/vendor/timber/timber/src/Image/Operation/Retina.php new file mode 100644 index 0000000..ae7a3d9 --- /dev/null +++ b/vendor/timber/timber/src/Image/Operation/Retina.php @@ -0,0 +1,85 @@ +factor . 'x'; // add @2x, @3x, @1.5x, etc. + $new_name = $newbase . '.' . $src_extension; + return $new_name; + } + + /** + * Performs the actual image manipulation, + * including saving the target file. + * + * @param string $load_filename filepath (not URL) to source file + * (ex: /src/var/www/wp-content/uploads/my-pic.jpg) + * @param string $save_filename filepath (not URL) where result file should be saved + * (ex: /src/var/www/wp-content/uploads/my-pic@2x.jpg) + * @return bool true if everything went fine, false otherwise + */ + public function run($load_filename, $save_filename) + { + // Attempt to check if SVG. + if (ImageHelper::is_svg($load_filename)) { + return false; + } + $image = \wp_get_image_editor($load_filename); + if (!\is_wp_error($image)) { + $current_size = $image->get_size(); + $src_w = $current_size['width']; + $src_h = $current_size['height']; + // Get ratios + $w = \round($src_w * $this->factor); + $h = \round($src_h * $this->factor); + $image->crop(0, 0, $src_w, $src_h, $w, $h); + $result = $image->save($save_filename); + if (\is_wp_error($result)) { + // @codeCoverageIgnoreStart + Helper::error_log('Error resizing image'); + Helper::error_log($result); + return false; + // @codeCoverageIgnoreEnd + } + return true; + } elseif (isset($image->error_data['error_loading_image'])) { + Helper::error_log('Error loading ' . $image->error_data['error_loading_image']); + return false; + } + Helper::error_log($image); + return false; + } +} diff --git a/vendor/timber/timber/src/Image/Operation/ToJpg.php b/vendor/timber/timber/src/Image/Operation/ToJpg.php new file mode 100644 index 0000000..cf9b0c9 --- /dev/null +++ b/vendor/timber/timber/src/Image/Operation/ToJpg.php @@ -0,0 +1,81 @@ +color); + $color = \imagecolorallocate($output, $c['red'], $c['green'], $c['blue']); + \imagefilledrectangle($output, 0, 0, $width, $height, $color); + \imagecopy($output, $input, 0, 0, 0, 0, $width, $height); + \imagejpeg($output, $save_filename); + return true; + } +} diff --git a/vendor/timber/timber/src/Image/Operation/ToWebp.php b/vendor/timber/timber/src/Image/Operation/ToWebp.php new file mode 100644 index 0000000..4f13a46 --- /dev/null +++ b/vendor/timber/timber/src/Image/Operation/ToWebp.php @@ -0,0 +1,84 @@ +quality); + } +} diff --git a/vendor/timber/timber/src/ImageDimensions.php b/vendor/timber/timber/src/ImageDimensions.php new file mode 100644 index 0000000..6a42d37 --- /dev/null +++ b/vendor/timber/timber/src/ImageDimensions.php @@ -0,0 +1,188 @@ + + * ``` + * ```html + * + * ``` + * + * @return int|null The width of the image in pixels. Null if the width can’t be read, e.g. because the file doesn’t + * exist. + */ + public function width(): ?int + { + return $this->get_dimension('width'); + } + + /** + * Gets the height of the image in pixels. + * + * @api + * @example + * ```twig + * + * ``` + * ```html + * + * ``` + * + * @return int|null The height of the image in pixels. Null if the height can’t be read, e.g. because the file + * doesn’t exist. + */ + public function height(): ?int + { + return $this->get_dimension('height'); + } + + /** + * Gets the aspect ratio of the image. + * + * @api + * @example + * ```twig + * {% if post.thumbnail.aspect < 1 %} + * {# handle vertical image #} + * A basketball player + * {% else %} + * A sumo wrestler + * {% endif %} + * ``` + * + * @return float|null The aspect ratio of the image. Null if the aspect ratio can’t be calculated. + */ + public function aspect(): ?float + { + $w = \intval($this->width()); + $h = \intval($this->height()); + + if ($w and $h > 0) { + return $w / $h; + } + + return null; + } + + /** + * Gets dimension for an image. + * + * @internal + * @param string $dimension The requested dimension. Either `width` or `height`. + * @return int|null The requested dimension. Null if image file couldn’t be found. + */ + public function get_dimension($dimension): ?int + { + // Load from internal cache. + if (isset($this->dimensions)) { + return $this->get_dimension_loaded($dimension); + } + + // Load dimensions. + if (\file_exists($this->file_loc) && \filesize($this->file_loc)) { + if (ImageHelper::is_svg($this->file_loc)) { + $svg_size = $this->get_dimensions_svg($this->file_loc); + $this->dimensions = [(int) \round($svg_size->width), (int) \round($svg_size->height)]; + } else { + [$width, $height] = \getimagesize($this->file_loc); + + $this->dimensions = []; + $this->dimensions[0] = (int) $width; + $this->dimensions[1] = (int) $height; + } + + return $this->get_dimension_loaded($dimension); + } + + return null; + } + + /** + * Gets already loaded dimension values. + * + * @internal + * @param string|null $dim Optional. The requested dimension. Either `width` or `height`. + * @return int The requested dimension in pixels. + */ + protected function get_dimension_loaded($dim = null): int + { + $dim = \strtolower((string) $dim); + + if ('h' === $dim || 'height' === $dim) { + return $this->dimensions[1]; + } + + return $this->dimensions[0]; + } + + /** + * Retrieve dimensions from SVG file. + * + * @internal + * @param string $svg SVG Path + * @return object + */ + protected function get_dimensions_svg($svg) + { + $svg = \simplexml_load_file($svg); + $width = 0; + $height = 0; + + if (false !== $svg) { + $attributes = $svg->attributes(); + if (isset($attributes->viewBox)) { + $viewbox = \explode(' ', $attributes->viewBox); + $width = $viewbox[2]; + $height = $viewbox[3]; + } elseif ($attributes->width && $attributes->height) { + $width = $attributes->width; + $height = $attributes->height; + } + } + + return (object) [ + 'width' => (float) $width, + 'height' => (float) $height, + ]; + } +} diff --git a/vendor/timber/timber/src/ImageHelper.php b/vendor/timber/timber/src/ImageHelper.php new file mode 100644 index 0000000..f827b1e --- /dev/null +++ b/vendor/timber/timber/src/ImageHelper.php @@ -0,0 +1,960 @@ + + * ``` + * ```html + * + * ``` + * + * @param string $src A URL (absolute or relative) to the original image. + * @param int|string $w Target width (int) or WordPress image size (WP-set or + * user-defined). + * @param int $h Optional. Target height (ignored if `$w` is WP image size). If not + * set, will ignore and resize based on `$w` only. Default `0`. + * @param string $crop Optional. Your choices are `default`, `center`, `top`, `bottom`, + * `left`, `right`. Default `default`. + * @param bool $force Optional. Whether to remove any already existing result file and + * force file generation. Default `false`. + * @return string The URL of the resized image. + */ + public static function resize($src, $w, $h = 0, $crop = 'default', $force = false) + { + if (!\is_numeric($w) && \is_string($w)) { + if ($sizes = self::find_wp_dimensions($w)) { + $w = $sizes['w']; + $h = $sizes['h']; + } else { + return $src; + } + } + $op = new Operation\Resize($w, $h, $crop); + return self::_operate($src, $op, $force); + } + + /** + * Finds the sizes of an image based on a defined image size. + * + * @internal + * @param string $size The image size to search for can be WordPress-defined ('medium') or + * user-defined ('my-awesome-size'). + * @return false|array An array with `w` and `h` height key, corresponding to the width and the + * height of the image. + */ + private static function find_wp_dimensions($size) + { + global $_wp_additional_image_sizes; + if (isset($_wp_additional_image_sizes[$size])) { + $w = $_wp_additional_image_sizes[$size]['width']; + $h = $_wp_additional_image_sizes[$size]['height']; + } elseif (\in_array($size, ['thumbnail', 'medium', 'large'])) { + $w = \get_option($size . '_size_w'); + $h = \get_option($size . '_size_h'); + } + if (isset($w) && isset($h) && ($w || $h)) { + return [ + 'w' => $w, + 'h' => $h, + ]; + } + return false; + } + + /** + * Generates a new image with increased size, for display on Retina screens. + * + * @api + * + * @param string $src URL of the file to read from. + * @param float $multiplier Optional. Factor the original dimensions should be multiplied + * with. Default `2`. + * @param boolean $force Optional. Whether to remove any already existing result file and + * force file generation. Default `false`. + * @return string URL to the new image. + */ + public static function retina_resize($src, $multiplier = 2, $force = false) + { + $op = new Operation\Retina($multiplier); + return self::_operate($src, $op, $force); + } + + /** + * Checks to see if the given file is an animated GIF. + * + * @api + * + * @param string $file Local filepath to a file, not a URL. + * @return boolean True if it’s an animated GIF, false if not. + */ + public static function is_animated_gif($file) + { + if (!\str_contains(\strtolower($file), '.gif')) { + //doesn't have .gif, bail + return false; + } + // Its a gif so test + if (!($fh = @\fopen($file, 'rb'))) { + return false; + } + $count = 0; + // An animated gif contains multiple "frames", with each frame having a + // header made up of: + // * a static 4-byte sequence (\x00\x21\xF9\x04). + // * 4 variable bytes. + // * a static 2-byte sequence (\x00\x2C). + // We read through the file til we reach the end of the file, or we've found. + // at least 2 frame headers. + while (!\feof($fh) && $count < 2) { + $chunk = \fread($fh, 1024 * 100); //read 100kb at a time + $count += \preg_match_all('#\x00\x21\xF9\x04.{4}\x00[\x2C\x21]#s', $chunk, $matches); + } + + \fclose($fh); + return $count > 1; + } + + /** + * Checks if file is an SVG. + * + * @param string $file_path File path to check. + * @return bool True if SVG, false if not SVG or file doesn't exist. + */ + public static function is_svg($file_path) + { + if ('' === $file_path || !\file_exists($file_path)) { + return false; + } + + if (\str_ends_with(\strtolower($file_path), '.svg')) { + return true; + } + + /** + * Try reading mime type. + * + * SVG images are not allowed by default in WordPress, so we have to pass a default mime + * type for SVG images. + */ + $mime = \wp_check_filetype_and_ext($file_path, PathHelper::basename($file_path), [ + 'svg' => 'image/svg+xml', + ]); + + return \in_array($mime['type'], [ + 'image/svg+xml', + 'text/html', + 'text/plain', + 'image/svg', + ]); + } + + /** + * Generate a new image with the specified dimensions. + * + * New dimensions are achieved by adding colored bands to maintain ratio. + * + * @api + * + * @param string $src + * @param int $w + * @param int $h + * @param string|bool $color + * @param bool $force + * @return string + */ + public static function letterbox($src, $w, $h, $color = false, $force = false) + { + $op = new Operation\Letterbox($w, $h, $color); + return self::_operate($src, $op, $force); + } + + /** + * Generates a new image by converting the source GIF or PNG into JPG. + * + * @api + * + * @param string $src A URL or path to the image + * (https://example.org/wp-content/uploads/2014/image.jpg) or + * (/wp-content/uploads/2014/image.jpg). + * @param string $bghex The hex color to use for transparent zones. + * @return string The URL of the processed image. + */ + public static function img_to_jpg($src, $bghex = '#FFFFFF', $force = false) + { + $op = new Operation\ToJpg($bghex); + return self::_operate($src, $op, $force); + } + + /** + * Generates a new image by converting the source into WEBP if supported by the server. + * + * @param string $src A URL or path to the image + * (https://example.org/wp-content/uploads/2014/image.webp) or + * (/wp-content/uploads/2014/image.webp). + * @param int $quality Range from `0` (worst quality, smaller file) to `100` (best quality, + * biggest file). + * @param bool $force Optional. Whether to remove any already existing result file and + * force file generation. Default `false`. + * @return string The URL of the processed image. If webp is not supported, a jpeg image will be + * generated. + */ + public static function img_to_webp($src, $quality = 80, $force = false) + { + $op = new Operation\ToWebp($quality); + return self::_operate($src, $op, $force); + } + + //-- end of public methods --// + + /** + * Deletes all resized versions of an image when the source is deleted. + * + * @since 1.5.0 + * @param int $post_id An attachment ID. + */ + public static function delete_attachment($post_id) + { + self::_delete_generated_if_image($post_id); + } + + /** + * Delete all resized version of an image when its meta data is regenerated. + * + * @since 1.5.0 + * @param array $metadata Existing metadata. + * @param int $post_id An attachment ID. + * @return array + */ + public static function generate_attachment_metadata($metadata, $post_id) + { + self::_delete_generated_if_image($post_id); + return $metadata; + } + + /** + * Adds a 'relative' key to wp_upload_dir() result. + * + * It will contain the relative url to upload dir. + * + * @since 1.5.0 + * @param array $arr + * @return array + */ + public static function add_relative_upload_dir_key($arr) + { + $arr['relative'] = \str_replace(self::$home_url, '', (string) $arr['baseurl']); + return $arr; + } + + /** + * Checks if attachment is an image before deleting generated files. + * + * @param int $post_id An attachment ID. + */ + public static function _delete_generated_if_image($post_id) + { + if (\wp_attachment_is_image($post_id)) { + $attachment = Timber::get_post($post_id); + /** @var Attachment $attachment */ + if ($file_loc = $attachment->file_loc()) { + ImageHelper::delete_generated_files($file_loc); + } + } + } + + /** + * Deletes the auto-generated files for resize and letterboxing created by Timber. + * + * @param string $local_file ex: /var/www/wp-content/uploads/2015/my-pic.jpg + * or: https://example.org/wp-content/uploads/2015/my-pic.jpg + */ + public static function delete_generated_files($local_file) + { + if (URLHelper::is_absolute($local_file)) { + $local_file = URLHelper::url_to_file_system($local_file); + } + $info = PathHelper::pathinfo($local_file); + $dir = $info['dirname']; + $ext = $info['extension']; + $filename = $info['filename']; + self::process_delete_generated_files($filename, $ext, $dir, '-[0-9999999]*', '-[0-9]*x[0-9]*-c-[a-z]*.'); + self::process_delete_generated_files($filename, $ext, $dir, '-lbox-[0-9999999]*', '-lbox-[0-9]*x[0-9]*-[a-zA-Z0-9]*.'); + self::process_delete_generated_files($filename, 'jpg', $dir, '-tojpg.*'); + self::process_delete_generated_files($filename, 'jpg', $dir, '-tojpg-[0-9999999]*'); + } + + /** + * Deletes resized versions of the supplied file name. + * + * If passed a value like my-pic.jpg, this function will delete my-pic-500x200-c-left.jpg, my-pic-400x400-c-default.jpg, etc. + * + * Keeping these here so I know what the hell we’re matching + * $match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/$filename-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $found_file); + * $match = preg_match("/\/srv\/www\/wordpress-develop\/src\/wp-content\/uploads\/2014\/05\/arch-[0-9]*x[0-9]*-c-[a-z]*.jpg/", $filename); + * + * @param string $filename ex: my-pic. + * @param string $ext ex: jpg. + * @param string $dir var/www/wp-content/uploads/2015/. + * @param string $search_pattern Pattern of files to pluck from. + * @param string $match_pattern Pattern of files to go forth and delete. + */ + protected static function process_delete_generated_files($filename, $ext, $dir, $search_pattern, $match_pattern = null) + { + $searcher = '/' . $filename . $search_pattern; + $files = \glob($dir . $searcher); + if ($files === false || empty($files)) { + return; + } + foreach ($files as $found_file) { + $pattern = '/' . \preg_quote($dir, '/') . '\/' . \preg_quote($filename, '/') . $match_pattern . \preg_quote($ext, '/') . '/'; + $match = \preg_match($pattern, $found_file); + if (!$match_pattern || $match) { + \unlink($found_file); + } + } + } + + /** + * Determines the filepath corresponding to a given URL. + * + * @param string $url + * @return string + */ + public static function get_server_location($url) + { + // if we're already an absolute dir, just return. + if (\str_starts_with($url, (string) ABSPATH)) { + return $url; + } + // otherwise, analyze URL then build mapping path + $au = self::analyze_url($url); + $result = self::_get_file_path($au['base'], $au['subdir'], $au['basename']); + return $result; + } + + /** + * Determines the filepath where a given external file will be stored. + * + * @param string $file + * @return string + */ + public static function get_sideloaded_file_loc($file) + { + $upload = \wp_upload_dir(); + $dir = $upload['path']; + $filename = $file; + $file = \parse_url($file); + $path_parts = PathHelper::pathinfo($file['path']); + $basename = \md5($filename); + + /** + * Filters basename for sideloaded files. + * @since 2.1.0 + * @example + * ```php + * // Change the basename used for sideloaded images. + * add_filter( 'timber/sideload_image/basename', function ($basename, $path_parts) { + * return $path_parts['filename'] . '-' . substr($basename, 0, 6); + * }, 10, 2) + * ``` + * + * @param string $basename Current basename for the sideloaded file. + * @param array $path_parts Array with path info for the sideloaded file. + */ + $basename = \apply_filters('timber/sideload_image/basename', $basename, $path_parts); + + $ext = 'jpg'; + if (isset($path_parts['extension'])) { + $ext = $path_parts['extension']; + } + return $dir . '/' . $basename . '.' . $ext; + } + + /** + * Downloads an external image to the server and stores it on the server. + * + * External/sideloaded images are saved in a folder named **external** in the uploads folder. If you want to change + * the folder that is used for your sideloaded images, you can use the + * [`timber/sideload_image/subdir`](https://timber.github.io/docs/v2/hooks/filters/#timber/sideload_image/subdir) + * filter. You can disable this behavior using the same filter. + * + * @param string $file The URL to the original file. + * + * @return string The URL to the downloaded file. + */ + public static function sideload_image($file) + { + /** + * Adds a filter to change the upload folder temporarily. + * + * This is necessary so that external images are not downloaded every month in case + * year-month-based folders are used. We need to use the `upload_dir` filter, because we use + * functions like `wp_upload_bits()` which uses `wp_upload_dir()` under the hood. + * + * @ticket 1098 + * @link https://github.com/timber/timber/issues/1098 + */ + \add_filter('upload_dir', [self::class, 'set_sideload_image_upload_dir']); + + $loc = self::get_sideloaded_file_loc($file); + if (\file_exists($loc)) { + $url = URLHelper::file_system_to_url($loc); + + \remove_filter('upload_dir', [self::class, 'set_sideload_image_upload_dir']); + + return $url; + } + // Download file to temp location + if (!\function_exists('download_url')) { + require_once ABSPATH . '/wp-admin/includes/file.php'; + } + $tmp = \download_url($file); + \preg_match('/[^\?]+\.(jpe?g|jpe|gif|png)\b/i', $file, $matches); + + $file_array = []; + $file_array['tmp_name'] = $tmp; + // If error storing temporarily, do not use + if (\is_wp_error($tmp)) { + $file_array['tmp_name'] = ''; + } + // do the validation and storage stuff + $locinfo = PathHelper::pathinfo($loc); + $file = \wp_upload_bits($locinfo['basename'], null, \file_get_contents($file_array['tmp_name'])); + // delete tmp file + @\unlink($file_array['tmp_name']); + + \remove_filter('upload_dir', [self::class, 'set_sideload_image_upload_dir']); + + return $file['url']; + } + + /** + * Gets upload folder definition for sideloaded images. + * + * Used by ImageHelper::sideload_image(). + * + * @internal + * @since 2.0.0 + * @see \Timber\ImageHelper::sideload_image() + * + * @param array $upload Array of information about the upload directory. + * + * @return array Array of information about the upload directory, modified by this + * function. + */ + public static function set_sideload_image_upload_dir(array $upload) + { + $subdir = 'external'; + + /** + * Filters to directory that should be used for sideloaded images. + * + * @since 2.0.0 + * @example + * ```php + * // Change the subdirectory used for sideloaded images. + * add_filter( 'timber/sideload_image/subdir', function( $subdir ) { + * return 'sideloaded'; + * } ); + * + * // Disable subdirectory used for sideloaded images. + * add_filter( 'timber/sideload_image/subdir', '__return_false' ); + * ``` + * + * @param string $subdir The subdir name to use for sideloaded images. Return an empty + * string or a falsey value in order to not use a subfolder. Default + * `external`. + */ + $subdir = \apply_filters('timber/sideload_image/subdir', $subdir); + + if (!empty($subdir)) { + // Remove slashes before or after. + $subdir = \trim((string) $subdir, '/'); + + $upload['subdir'] = '/' . $subdir; + $upload['path'] = $upload['basedir'] . $upload['subdir']; + $upload['url'] = $upload['baseurl'] . $upload['subdir']; + } + + return $upload; + } + + /** + * Takes a URL and breaks it into components. + * + * The components can then be used in the different steps of image processing. + * The image is expected to be either part of a theme, plugin, or an upload. + * + * @param string $url A URL (absolute or relative) pointing to an image. + * @return array An array (see keys in code below). + */ + public static function analyze_url(string $url): array + { + /** + * Filters whether to short-circuit the ImageHelper::analyze_url() + * file path of a URL located in a theme directory. + * + * Returning a non-null value from the filter will short-circuit + * ImageHelper::analyze_url(), returning that value. + * + * @since 2.0.0 + * + * @param array|null $info The URL components array to short-circuit with. Default null. + * @param string $url The URL pointing to an image. + */ + $result = \apply_filters('timber/image_helper/pre_analyze_url', null, $url); + if (null === $result) { + $result = self::get_url_components($url); + } + + /** + * Filters the array of analyzed URL components. + * + * @since 2.0.0 + * + * @param array $info The URL components. + * @param string $url The URL pointing to an image. + */ + return \apply_filters('timber/image_helper/analyze_url', $result, $url); + } + + /** + * Returns information about a URL. + * + * @param string $url A URL (absolute or relative) pointing to an image. + * @return array An array (see keys in code below). + */ + private static function get_url_components(string $url): array + { + $result = [ + // the initial url + 'url' => $url, + // is the url absolute or relative (to home_url) + 'absolute' => URLHelper::is_absolute($url), + // is the image in uploads dir, or in content dir (theme or plugin) + 'base' => 0, + // the path between base (uploads or content) and file + 'subdir' => '', + // the filename, without extension + 'filename' => '', + // the file extension + 'extension' => '', + // full file name + 'basename' => '', + ]; + + $upload_dir = \wp_upload_dir(); + $tmp = $url; + if (\str_starts_with($tmp, (string) ABSPATH) || \str_starts_with($tmp, '/srv/www/')) { + // we've been given a dir, not an url + $result['absolute'] = true; + if (\str_starts_with($tmp, (string) $upload_dir['basedir'])) { + $result['base'] = self::BASE_UPLOADS; // upload based + $tmp = URLHelper::remove_url_component($tmp, $upload_dir['basedir']); + } + if (\str_starts_with($tmp, (string) WP_CONTENT_DIR)) { + $result['base'] = self::BASE_CONTENT; // content based + $tmp = URLHelper::remove_url_component($tmp, WP_CONTENT_DIR); + } + } else { + if (!$result['absolute']) { + $tmp = \untrailingslashit(\network_home_url()) . $tmp; + } + if (URLHelper::starts_with($tmp, $upload_dir['baseurl'])) { + $result['base'] = self::BASE_UPLOADS; // upload based + $tmp = URLHelper::remove_url_component($tmp, $upload_dir['baseurl']); + } elseif (URLHelper::starts_with($tmp, \content_url())) { + $result['base'] = self::BASE_CONTENT; // content-based + $tmp = self::theme_url_to_dir($tmp); + $tmp = URLHelper::remove_url_component($tmp, WP_CONTENT_DIR); + } + } + + // Remove query and fragment from URL. + if (($i = \strpos($tmp, '?')) !== false) { + $tmp = \substr($tmp, 0, $i); + } elseif (($i = \strpos($tmp, '#')) !== false) { + $tmp = \substr($tmp, 0, $i); + } + + $parts = PathHelper::pathinfo($tmp); + $result['subdir'] = ($parts['dirname'] === '/') ? '' : $parts['dirname']; + $result['filename'] = $parts['filename']; + $result['extension'] = (isset($parts['extension']) ? \strtolower((string) $parts['extension']) : ''); + $result['basename'] = $parts['basename']; + + return $result; + } + + /** + * Converts a URL located in a theme directory into the raw file path. + * + * @param string $src A URL (https://example.org/wp-content/themes/twentysixteen/images/home.jpg). + * @return string Full path to the file in question. + */ + public static function theme_url_to_dir(string $src): string + { + /** + * Filters whether to short-circuit the ImageHelper::theme_url_to_dir() + * file path of a URL located in a theme directory. + * + * Returning a non-null value from the filter will short-circuit + * ImageHelper::theme_url_to_dir(), returning that value. + * + * @since 2.0.0 + * + * @param string|null $path Full path to short-circuit with. Default null. + * @param string $src The URL to be converted. + */ + $path = \apply_filters('timber/image_helper/pre_theme_url_to_dir', null, $src); + if (null === $path) { + $path = self::get_dir_from_theme_url($src); + } + + /** + * Filters the raw file path of a URL located in a theme directory. + * + * @since 2.0.0 + * + * @param string $path The resolved full path to $src. + * @param string $src The URL that was converted. + */ + return \apply_filters('timber/image_helper/theme_url_to_dir', $path, $src); + } + + /** + * Converts a URL located in a theme directory into the raw file path. + * + * @param string $src A URL (https://example.org/wp-content/themes/twentysixteen/images/home.jpg). + * @return string Full path to the file in question. + */ + private static function get_dir_from_theme_url(string $src): string + { + $site_root = \trailingslashit(\get_theme_root_uri()) . \get_stylesheet(); + $path = \str_replace($site_root, '', $src); + //$path = \trailingslashit(\get_theme_root()).\get_stylesheet().$path; + $path = \get_stylesheet_directory() . $path; + if ($_path = \realpath($path)) { + return $_path; + } + return $path; + } + + /** + * Checks if uploaded image is located in theme. + * + * @param string $path image path. + * @return bool If the image is located in the theme directory it returns true. + * If not or $path doesn't exits it returns false. + */ + protected static function is_in_theme_dir($path) + { + $root = \realpath(\get_stylesheet_directory()); + + if (false === $root) { + return false; + } + + if (\str_starts_with($path, (string) $root)) { + return true; + } else { + return false; + } + } + + /** + * Builds the public URL of a file based on its different components. + * + * @param int $base One of `self::BASE_UPLOADS`, `self::BASE_CONTENT` to indicate if + * file is an upload or a content (theme or plugin). + * @param string $subdir Subdirectory in which file is stored, relative to $base root + * folder. + * @param string $filename File name, including extension (but no path). + * @param bool $absolute Should the returned URL be absolute (include protocol+host), or + * relative. + * @return string The URL. + */ + private static function _get_file_url($base, $subdir, $filename, $absolute) + { + $url = ''; + if (self::BASE_UPLOADS == $base) { + $upload_dir = \wp_upload_dir(); + $url = $upload_dir['baseurl']; + } + if (self::BASE_CONTENT == $base) { + $url = \content_url(); + } + if (!empty($subdir)) { + $url .= $subdir; + } + $url = \untrailingslashit($url) . '/' . $filename; + if (!$absolute) { + $home = \home_url(); + $home = \apply_filters('timber/image_helper/_get_file_url/home_url', $home); + $url = \str_replace($home, '', $url); + } + return $url; + } + + /** + * Runs realpath to resolve symbolic links (../, etc). But only if it’s a path and not a URL. + * + * @param string $path + * @return string The resolved path. + */ + protected static function maybe_realpath($path) + { + if (\str_contains($path, '../')) { + return \realpath($path); + } + return $path; + } + + /** + * Builds the absolute file system location of a file based on its different components. + * + * @param int $base One of `self::BASE_UPLOADS`, `self::BASE_CONTENT` to indicate if + * file is an upload or a content (theme or plugin). + * @param string $subdir Subdirectory in which file is stored, relative to $base root + * folder. + * @param string $filename File name, including extension (but no path). + * @return string The file location. + */ + private static function _get_file_path($base, $subdir, $filename) + { + if (URLHelper::is_url($subdir)) { + $subdir = URLHelper::url_to_file_system($subdir); + } + $subdir = self::maybe_realpath($subdir); + + $path = ''; + if (self::BASE_UPLOADS == $base) { + //it is in the Uploads directory + $upload_dir = \wp_upload_dir(); + $path = $upload_dir['basedir']; + } elseif (self::BASE_CONTENT == $base) { + //it is in the content directory, somewhere else ... + $path = WP_CONTENT_DIR; + } + if (self::is_in_theme_dir(\trailingslashit($subdir) . $filename)) { + //this is for weird installs when the theme folder is outside of /wp-content + return \trailingslashit($subdir) . $filename; + } + if (!empty($subdir)) { + $path = \trailingslashit($path) . $subdir; + } + $path = \trailingslashit($path) . $filename; + + return URLHelper::remove_double_slashes($path); + } + + /** + * Main method that applies operation to src image: + * 1. break down supplied URL into components + * 2. use components to determine result file and URL + * 3. check if a result file already exists + * 4. otherwise, delegate to supplied TimberImageOperation + * + * @param string $src A URL (absolute or relative) to an image. + * @param object $op Object of class TimberImageOperation. + * @param boolean $force Optional. Whether to remove any already existing result file and + * force file generation. Default `false`. + * @return string URL to the new image - or the source one if error. + */ + private static function _operate($src, $op, $force = false) + { + if (empty($src)) { + return ''; + } + + $allow_fs_write = \apply_filters('timber/allow_fs_write', true); + + if ($allow_fs_write === false) { + return $src; + } + + $external = false; + // if external image, load it first + if (URLHelper::is_external_content($src)) { + $src = self::sideload_image($src); + $external = true; + } + + // break down URL into components + $au = self::analyze_url($src); + + // build URL and filenames + $new_url = self::_get_file_url( + $au['base'], + $au['subdir'], + $op->filename($au['filename'], $au['extension']), + $au['absolute'] + ); + $destination_path = self::_get_file_path( + $au['base'], + $au['subdir'], + $op->filename($au['filename'], $au['extension']) + ); + $source_path = self::_get_file_path( + $au['base'], + $au['subdir'], + $au['basename'] + ); + + /** + * Filters the URL for the resized version of a `Timber\Image`. + * + * You’ll probably need to use this in combination with `timber/image/new_path`. + * + * @since 1.0.0 + * + * @param string $new_url The URL to the resized version of an image. + */ + $new_url = \apply_filters('timber/image/new_url', $new_url); + + /** + * Filters the destination path for the resized version of a `Timber\Image`. + * + * A possible use case for this would be to store all images generated by Timber in a + * separate directory. You’ll probably need to use this in combination with + * `timber/image/new_url`. + * + * @since 1.0.0 + * + * @param string $destination_path Full path to the destination of a resized image. + */ + $destination_path = \apply_filters('timber/image/new_path', $destination_path); + + // if already exists... + if (\file_exists($source_path) && \file_exists($destination_path)) { + if ($force || \filemtime($source_path) > \filemtime($destination_path)) { + // Force operation - warning: will regenerate the image on every pageload, use for testing purposes only! + \unlink($destination_path); + } else { + // return existing file (caching) + return $new_url; + } + } + // otherwise generate result file + if ($op->run($source_path, $destination_path)) { + if ($op::class === Operation\Resize::class && $external) { + $new_url = \strtolower((string) $new_url); + } + return $new_url; + } else { + // in case of error, we return source file itself + return $src; + } + } + + //-- the below methods are just used for + // unit testing the URL generation code --// + /** + * @internal + */ + public static function get_letterbox_file_url($url, $w, $h, $color) + { + $au = self::analyze_url($url); + $op = new Operation\Letterbox($w, $h, $color); + $new_url = self::_get_file_url( + $au['base'], + $au['subdir'], + $op->filename($au['filename'], $au['extension']), + $au['absolute'] + ); + return $new_url; + } + + /** + * @internal + */ + public static function get_letterbox_file_path($url, $w, $h, $color) + { + $au = self::analyze_url($url); + $op = new Operation\Letterbox($w, $h, $color); + $new_path = self::_get_file_path( + $au['base'], + $au['subdir'], + $op->filename($au['filename'], $au['extension']) + ); + return $new_path; + } + + /** + * @internal + */ + public static function get_resize_file_url($url, $w, $h, $crop) + { + $au = self::analyze_url($url); + $op = new Operation\Resize($w, $h, $crop); + $new_url = self::_get_file_url( + $au['base'], + $au['subdir'], + $op->filename($au['filename'], $au['extension']), + $au['absolute'] + ); + return $new_url; + } + + /** + * @internal + */ + public static function get_resize_file_path($url, $w, $h, $crop) + { + $au = self::analyze_url($url); + $op = new Operation\Resize($w, $h, $crop); + $new_path = self::_get_file_path( + $au['base'], + $au['subdir'], + $op->filename($au['filename'], $au['extension']) + ); + return $new_path; + } +} diff --git a/vendor/timber/timber/src/ImageInterface.php b/vendor/timber/timber/src/ImageInterface.php new file mode 100644 index 0000000..89288ce --- /dev/null +++ b/vendor/timber/timber/src/ImageInterface.php @@ -0,0 +1,101 @@ +taxonomy . '_' . $term_id, $field_name, $args); + } + + /** + * @deprecated 2.0.0, with no replacement + * + * @return mixed + */ + public static function term_set_meta($value, $field, $term_id, $term) + { + $searcher = $term->taxonomy . '_' . $term->ID; + \update_field($field, $value, $searcher); + return $value; + } + + /** + * Gets meta value for a user through ACF’s API. + * + * @param string $value The field value. Default null. + * @param int $user_id The user ID. + * @param string $field_name The name of the meta field to get the value for. + * @param \Timber\User $user The user object. + * @param array $args An array of arguments. + * @return mixed|false + */ + public static function user_get_meta_field($value, $user_id, $field_name, $user, $args) + { + return self::get_meta($value, 'user_' . $user_id, $field_name, $args); + } + + /** + * Transform ACF file field + * + * @param string $value + * @param int $id + * @param array $field + */ + public static function transform_file($value, $id, $field) + { + if (empty($value)) { + return false; + } + return Timber::get_attachment($value); + } + + /** + * Transform ACF image field + * + * @param string $value + * @param int $id + * @param array $field + */ + public static function transform_image($value, $id, $field) + { + if (empty($value)) { + return false; + } + return Timber::get_image($value); + } + + /** + * Transform ACF gallery field + * + * @param array $value + * @param int $id + * @param array $field + */ + public static function transform_gallery($value, $id, $field) + { + if (empty($value)) { + return false; + } + return Timber::get_posts($value); + } + + /** + * Transform ACF date picker field + * + * @param string $value + * @param int $id + * @param array $field + */ + public static function transform_date_picker($value, $id, $field) + { + if (!$value) { + return $value; + } + return new DateTimeImmutable(\acf_format_date($value, 'Y-m-d H:i:s')); + } + + /** + * Transform ACF post object field + * + * @param string $value + * @param int $id + * @param array $field + */ + public static function transform_post_object($value, $id, $field) + { + if (empty($value)) { + return false; + } + if (!$field['multiple']) { + return Timber::get_post($value); + } + return Timber::get_posts($value); + } + + /** + * Transform ACF relationship field + * + * @param string $value + * @param int $id + * @param array $field + */ + public static function transform_relationship($value, $id, $field) + { + if (empty($value)) { + return false; + } + return Timber::get_posts($value); + } + + /** + * Transform ACF taxonomy field + * + * @param string $value + * @param int $id + * @param array $field + */ + public static function transform_taxonomy($value, $id, $field) + { + if (empty($value)) { + return false; + } + if ($field['field_type'] === 'select' || $field['field_type'] === 'radio') { + return Timber::get_term((int) $value); + } + return Timber::get_terms((array) $value); + } + + /** + * Transform ACF user field + * + * @param string $value + * @param int $id + * @param array $field + */ + public static function transform_user($value, $id, $field) + { + if (empty($value)) { + return false; + } + if (!$field['multiple']) { + return Timber::get_user((int) $value); + } + return Timber::get_users((array) $value); + } + + /** + * Gets meta value through ACF’s API. + * + * @param string $value + * @param int|string $id + * @param string $field_name + * @param array $args + * @return mixed|false + */ + private static function get_meta($value, $id, $field_name, $args) + { + $args = \wp_parse_args($args, [ + 'format_value' => true, + 'transform_value' => false, + ]); + + if (!$args['transform_value']) { + return \get_field($field_name, $id, $args['format_value']); + } + + /** + * We use acf()->fields->get_field_type() instead of acf_get_field_type(), because of some function stub issues + * in the php-stubs/acf-pro-stubs package. The ACF plugin doesn’t use the right parameter and return values for + * some functions in the DocBlocks. + * + * @ticket https://github.com/timber/timber/pull/2630 + */ + $file_field_type = \acf_get_field_type('file'); + $image_field_type = \acf_get_field_type('image'); + $gallery_field_type = \acf_get_field_type('gallery'); + $date_picker_field_type = \acf_get_field_type('date_picker'); + $date_time_picker_field_type = \acf_get_field_type('date_time_picker'); + $post_object_field_type = \acf_get_field_type('post_object'); + $relationship_field_type = \acf_get_field_type('relationship'); + $taxonomy_field_type = \acf_get_field_type('taxonomy'); + $user_field_type = \acf_get_field_type('user'); + + \remove_filter('acf/format_value/type=file', [$file_field_type, 'format_value']); + \remove_filter('acf/format_value/type=image', [$image_field_type, 'format_value']); + \remove_filter('acf/format_value/type=gallery', [$gallery_field_type, 'format_value']); + \remove_filter('acf/format_value/type=date_picker', [$date_picker_field_type, 'format_value']); + \remove_filter('acf/format_value/type=date_time_picker', [$date_time_picker_field_type, 'format_value']); + \remove_filter('acf/format_value/type=post_object', [$post_object_field_type, 'format_value']); + \remove_filter('acf/format_value/type=relationship', [$relationship_field_type, 'format_value']); + \remove_filter('acf/format_value/type=taxonomy', [$taxonomy_field_type, 'format_value']); + \remove_filter('acf/format_value/type=user', [$user_field_type, 'format_value']); + + \add_filter('acf/format_value/type=file', [self::class, 'transform_file'], 10, 3); + \add_filter('acf/format_value/type=image', [self::class, 'transform_image'], 10, 3); + \add_filter('acf/format_value/type=gallery', [self::class, 'transform_gallery'], 10, 3); + \add_filter('acf/format_value/type=date_picker', [self::class, 'transform_date_picker'], 10, 3); + \add_filter('acf/format_value/type=date_time_picker', [self::class, 'transform_date_picker'], 10, 3); + \add_filter('acf/format_value/type=post_object', [self::class, 'transform_post_object'], 10, 3); + \add_filter('acf/format_value/type=relationship', [self::class, 'transform_relationship'], 10, 3); + \add_filter('acf/format_value/type=taxonomy', [self::class, 'transform_taxonomy'], 10, 3); + \add_filter('acf/format_value/type=user', [self::class, 'transform_user'], 10, 3); + + $value = \get_field($field_name, $id, true); + + \add_filter('acf/format_value/type=file', [$file_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=image', [$image_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=gallery', [$gallery_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=date_picker', [$date_picker_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=date_time_picker', [$date_time_picker_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=post_object', [$post_object_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=relationship', [$relationship_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=taxonomy', [$taxonomy_field_type, 'format_value'], 10, 3); + \add_filter('acf/format_value/type=user', [$taxonomy_field_type, 'format_value'], 10, 3); + + \remove_filter('acf/format_value/type=file', [self::class, 'transform_file']); + \remove_filter('acf/format_value/type=image', [self::class, 'transform_image']); + \remove_filter('acf/format_value/type=gallery', [self::class, 'transform_gallery']); + \remove_filter('acf/format_value/type=date_picker', [self::class, 'transform_date_picker']); + \remove_filter('acf/format_value/type=date_time_picker', [self::class, 'transform_date_picker']); + \remove_filter('acf/format_value/type=post_object', [self::class, 'transform_post_object']); + \remove_filter('acf/format_value/type=relationship', [self::class, 'transform_relationship']); + \remove_filter('acf/format_value/type=taxonomy', [self::class, 'transform_taxonomy']); + \remove_filter('acf/format_value/type=user', [self::class, 'transform_user']); + + return $value; + } +} diff --git a/vendor/timber/timber/src/Integration/CLI/TimberCommand.php b/vendor/timber/timber/src/Integration/CLI/TimberCommand.php new file mode 100644 index 0000000..e95f848 --- /dev/null +++ b/vendor/timber/timber/src/Integration/CLI/TimberCommand.php @@ -0,0 +1,55 @@ +] + * : Optional. The type of cache to clear. Accepts 'timber' or 'twig'. If not provided, the command will clear all caches. + * + * ## EXAMPLES + * + * # Clear all caches. + * wp timber clear-cache + * + * # Clear Timber caches. + * wp timber clear-cache timber + * + * # Clear Twig caches. + * wp timber clear-cache twig + * + * @subcommand clear-cache + * @alias clear_cache + */ + public function clear_cache($args = []): void + { + $mode = $args[0] ?? 'all'; + $mode_string = 'all' !== $mode ? \ucfirst((string) $mode) : $mode; + + WP_CLI::log("Clearing {$mode_string} caches …"); + + if (Cleaner::clear_cache($mode)) { + WP_CLI::success("Cleared {$mode_string} caches."); + } else { + WP_CLI::warning("Failed to clear {$mode_string} cached contents."); + } + } +} diff --git a/vendor/timber/timber/src/Integration/CoAuthorsPlus/CoAuthorsPlusUser.php b/vendor/timber/timber/src/Integration/CoAuthorsPlus/CoAuthorsPlusUser.php new file mode 100644 index 0000000..5c63a8b --- /dev/null +++ b/vendor/timber/timber/src/Integration/CoAuthorsPlus/CoAuthorsPlusUser.php @@ -0,0 +1,64 @@ +init($coauthor); + + return $user; + } + + /** + * @internal + * @param false|object $coauthor co-author object + */ + protected function init($coauthor = false) + { + /** + * @var stdclass $coauthor + */ + parent::init($coauthor); + + $this->id = $this->ID = (int) $coauthor->ID; + $this->first_name = $coauthor->first_name; + $this->last_name = $coauthor->last_name; + $this->user_nicename = $coauthor->user_nicename; + $this->description = $coauthor->description; + $this->display_name = $coauthor->display_name; + $this->_link = \get_author_posts_url(null, $coauthor->user_nicename); + } + + /** + * Get the user's avatar or Gravatar URL. + * + * @param array $args optional array arg to `get_avatar_url()` + * @return string + */ + public function avatar($args = null) + { + $prefer_gravatar = \apply_filters( + 'timber/co_authors_plus/prefer_gravatar', + false + ); + if ($prefer_gravatar) { + return \get_avatar_url($this->user_email, $args); + } else { + // 96 is the default wordpress avatar size + return \get_the_post_thumbnail_url($this->id, 96); + } + } +} diff --git a/vendor/timber/timber/src/Integration/CoAuthorsPlusIntegration.php b/vendor/timber/timber/src/Integration/CoAuthorsPlusIntegration.php new file mode 100644 index 0000000..8f6830f --- /dev/null +++ b/vendor/timber/timber/src/Integration/CoAuthorsPlusIntegration.php @@ -0,0 +1,77 @@ + CoAuthorsPlusUser::class, 10, 2); + } + + /** + * Filters {{ post.authors }} to return authors stored from Co-Authors Plus + * @since 1.1.4 + * @param array $author + * @param \Timber\Post $post + * @return array of User objects + */ + public function authors($author, $post) + { + $authors = []; + $cauthors = \get_coauthors($post->ID); + foreach ($cauthors as $author) { + $uid = $this->get_user_uid($author); + if ($uid) { + $authors[] = \Timber\Timber::get_user($uid); + } else { + $wp_user = new WP_User($author); + $user = \Timber\Timber::get_user($wp_user); + $user->import($wp_user->data); + unset($user->user_pass); + $user->id = $user->ID = (int) $wp_user->ID; + $authors[] = $user; + } + } + return $authors; + } + + /** + * return the user id for normal authors + * the user login for guest authors if it exists and self::prefer_users == true + * or null + * @internal + * @param object $cauthor + * @return int|null + */ + protected function get_user_uid($cauthor) + { + // if is guest author + if (\is_object($cauthor) && isset($cauthor->type) && $cauthor->type == 'guest-author') { + // if have have a linked user account + global $coauthors_plus; + if (!$coauthors_plus->force_guest_authors && isset($cauthor->linked_account) && !empty($cauthor->linked_account)) { + $wp_user = \get_user_by('slug', $cauthor->linked_account); + return $wp_user->ID; + } else { + return null; + } + } else { + return $cauthor->ID; + } + } +} diff --git a/vendor/timber/timber/src/Integration/IntegrationInterface.php b/vendor/timber/timber/src/Integration/IntegrationInterface.php new file mode 100644 index 0000000..95f08c6 --- /dev/null +++ b/vendor/timber/timber/src/Integration/IntegrationInterface.php @@ -0,0 +1,15 @@ + ($item instanceof WPML_LS_Menu_Item ? new WP_Post($item) : $item), + $items + ); + } + + public function theme_mod_nav_menu_locations($locations) + { + if (!\is_array($locations)) { + return $locations; + } + + return \array_map( + fn ($id) => \wpml_object_id_filter($id, 'nav_menu'), + $locations + ); + } +} diff --git a/vendor/timber/timber/src/Loader.php b/vendor/timber/timber/src/Loader.php new file mode 100644 index 0000000..afd4c60 --- /dev/null +++ b/vendor/timber/timber/src/Loader.php @@ -0,0 +1,774 @@ +locations = LocationManager::get_locations($caller); + + /** + * Filters the cache mode. + * + * You can read more about Caching in the + * [Performance/Caching]({{}}) guide. + * + * @since 0.20.10 + * + * @param string $cache_mode The cache mode. Can be one of the following: + * `Timber\Loader::CACHE_NONE`, + * `Timber\Loader::CACHE_OBJECT`, + * `Timber\Loader::CACHE_TRANSIENT`, + * `Timber\Loader::CACHE_SITE_TRANSIENT`, + * `Timber\Loader::CACHE_USE_DEFAULT`. + * Default `Timber\Loader::CACHE_TRANSIENT`. + */ + $this->cache_mode = \apply_filters('timber/cache/mode', $this->cache_mode); + + /** + * Filters the cache mode. + * + * @deprecated 2.0.0, use `timber/cache/mode` + */ + $this->cache_mode = \apply_filters_deprecated( + 'timber_cache_mode', + [$this->cache_mode], + '2.0.0', + 'timber/cache/mode' + ); + } + + /** + * @param string $file + * @param array $data + * @param array|boolean $expires (array for options, false for none, integer for # of seconds) + * @param string $cache_mode + * @return bool|string + */ + public function render($file, $data = null, $expires = false, $cache_mode = self::CACHE_USE_DEFAULT) + { + // Different $expires if user is anonymous or logged in + if (\is_array($expires)) { + /** @var array $expires */ + if (\is_user_logged_in() && isset($expires[1])) { + $expires = $expires[1]; + } else { + $expires = $expires[0]; + } + } + + if ($expires === 0) { + $expires = false; + } + + $key = null; + $output = false; + if (false !== $expires) { + \ksort($data); + $encoded = \json_encode($data); + + /** + * The encoding might fail, e.g. when an object has a recursion. In that case, we’d rather not cache the + * data instead of possibly returning the wrong data. + */ + if (false !== $encoded) { + $key = \md5($file . $encoded); + $output = $this->get_cache($key, self::CACHEGROUP, $cache_mode); + } + } + + if (false === $output || null === $output) { + $twig = $this->get_twig(); + if (\strlen($file)) { + $loader = $this->get_loader(); + $result = $loader->getCacheKey($file); + + /** + * Fires after … + * + * @todo Add summary, description parameter description + * + * @param string $result + */ + \do_action('timber/loader/render_file', $result); + + /** + * Fires after … + * + * This action is used by the Timber Debug Bar extension. + * + * @todo Add summary + * + * @deprecated 2.0.0, use `timber/loader/render_file` + */ + \do_action_deprecated( + 'timber_loader_render_file', + [$result], + '2.0.0', + 'timber/loader/render_file' + ); + } + + /** + * Filters … + * + * @todo Add summary, description, example, parameter descriptions + * + * @since 0.20.10 + * + * @param array $data + * @param string $file + */ + $data = \apply_filters('timber/loader/render_data', $data, $file); + + /** + * Filters … + * + * @todo Add summary + * + * @deprecated 2.0.0, use `timber/loader/render_data` + */ + $data = \apply_filters_deprecated( + 'timber_loader_render_data', + [$data], + '2.0.0', + 'timber/loader/render_data' + ); + + $template = $twig->load($file); + $output = $template->render($data); + + /** + * Filters $output before it is cached. + * + * @since 2.1.0 + * + * @param string $output + * @param array $data + * @param string $file + */ + $output = \apply_filters('timber/output/pre-cache', $output, $data, $file); + } + + if (false !== $output && false !== $expires && null !== $key) { + $this->delete_cache(); + $this->set_cache($key, $output, self::CACHEGROUP, $expires, $cache_mode); + } + + /** + * Filters … + * + * @todo Add summary, description, example, parameter descriptions + * + * @since 0.20.10 + * + * @param string $output + * @param array $data + * @param string $file + */ + $output = \apply_filters('timber/output', $output, $data, $file); + + /** + * Filters … + * + * @todo Add summary + * + * @deprecated 2.0.0, use `timber/output` + */ + $output = \apply_filters_deprecated('timber_output', [$output], '2.0.0', 'timber/output'); + + return $output; + } + + protected function delete_cache() + { + Cleaner::delete_transients(); + } + + /** + * Get first existing template. + * + * @param array|string $templates Name(s) of the Twig template(s) to choose from. + * @return string|bool Name of chosen template, otherwise false. + */ + public function choose_template($templates) + { + // Change $templates into array, if needed + if (!\is_array($templates)) { + $templates = (array) $templates; + } + + // Get Twig loader + $loader = $this->get_loader(); + + // Run through template array + foreach ($templates as $template) { + // Remove any whitespace around the template name + $template = \trim((string) $template); + // Use the Twig loader to test for existence + if ($loader->exists($template)) { + // Return name of existing template + return $template; + } + } + + // No existing template was found + return false; + } + + /** + * @return FilesystemLoader + */ + public function get_loader() + { + $paths = $this->locations; + + /** + * Filters the template paths used by the Loader. + * + * @since 0.20.10 + * + * @deprecated 2.0.0, use `timber/locations` + * + * @param array $paths + */ + $paths = \apply_filters_deprecated( + 'timber/loader/paths', + [$paths], + '2.0.0', + 'timber/locations' + ); + + $open_basedir = \ini_get('open_basedir'); + $rootPath = '/'; + if ($open_basedir) { + $rootPath = null; + } + + $fs = new FilesystemLoader([], $rootPath); + + foreach ($paths as $namespace => $path_locations) { + if (\is_array($path_locations)) { + \array_map(function ($path) use ($fs, $namespace) { + if (\is_string($namespace)) { + $fs->addPath($path, $namespace); + } else { + $fs->addPath($path, Loader::MAIN_NAMESPACE); + } + }, $path_locations); + } else { + Helper::deprecated( + 'add_filter( \'timber/loader/paths\', [\'path/to/my/templates\'] ) in a non-associative array', + 'add_filter( \'timber/loader/paths\', [ 0 => [ \'path/to/my/templates\' ] ] )', + '2.0.0' + ); + $fs->addPath($path_locations, self::MAIN_NAMESPACE); + } + } + + /** + * Filters … + * + * @todo Add summary, description, example, parameter description + * + * @link https://github.com/timber/timber/pull/1254 + * @since 1.1.11 + */ + $fs = \apply_filters('timber/loader/loader', $fs); + + return $fs; + } + + /** + * @return Environment + */ + public function get_twig() + { + // Default options. + $environment_options = [ + 'debug' => WP_DEBUG, + 'autoescape' => false, + 'cache' => false, + ]; + + /** + * Filters the environment options that are used when creating a Twig Environment instance. + * + * By default, Timber sets the following values: + * + * - `'debug' => WP_DEBUG` + * - `'autoescape' => false` + * - `'cache' => false` + * + * @api + * @since 1.9.5 + * @link https://twig.symfony.com/doc/2.x/api.html#environment-options + * @example + * ```php + * add_filter( 'timber/twig/environment/options', 'update_twig_environment_options' ); + * + * /** + * * Updates Twig environment options. + * * + * * @link https://twig.symfony.com/doc/2.x/api.html#environment-options + * * + * * \@param array $options An array of environment options. + * * + * * @return array + * *\/ + * function update_twig_environment_options( $options ) { + * $options['cache'] = true; + * $options['auto_reload'] = true; + * + * return $options; + * } + * ``` + * + * @param array $environment_options An array of Twig environment options. + */ + $environment_options = \apply_filters( + 'timber/twig/environment/options', + $environment_options + ); + + /** + * @deprecated 2.0.0 + */ + if (isset(Timber::$autoescape) && false !== Timber::$autoescape) { + Helper::deprecated( + 'Timber::$autoescape', + 'the \'timber/twig/environment/options filter\'', + '2.0.0' + ); + + $environment_options['autoescape'] = Timber::$autoescape; + } + + /** + * Backwards compatibility fix. + * + * The value `true` doesn’t exist anymore for the `autoescape` option. You need to define + * an auto-escaping fallback strategy. This fallback uses the `html` strategy. + */ + if (true === $environment_options['autoescape']) { + $environment_options['autoescape'] = 'html'; + } + + /** + * Alias Timber::$cache can be used for Timber::$twig_cache. + * + * @deprecated 2.0.0 + */ + if (isset(Timber::$cache) && true === Timber::$cache) { + Timber::$twig_cache = true; + } + + /** + * @deprecated 2.0.0 + */ + if (isset(Timber::$twig_cache) && false !== Timber::$twig_cache) { + Helper::deprecated( + 'Timber::$cache and Timber::$twig_cache', + 'the \'timber/twig/environment/options filter\'', + '2.0.0' + ); + + $environment_options['cache'] = Timber::$twig_cache; + } + + if (true === $environment_options['cache']) { + $twig_cache_loc = TIMBER_LOC . '/cache/twig'; + + /** + * Filters the cache location used for Twig. + * + * Allows you to set a new cache location for Twig. If the folder doesn’t exist yet, it + * will be created automatically. + * + * @since 0.20.10 + * @deprecated 2.0.0 + * + * @param string $twig_cache_loc Full path to the cache location. Default `/cache/twig` + * in the Timber root folder. + */ + $twig_cache_loc = \apply_filters_deprecated( + 'timber/cache/location', + [$twig_cache_loc], + '2.0.0', + 'timber/twig/environment/options' + ); + + if (!\file_exists($twig_cache_loc)) { + \mkdir($twig_cache_loc, 0777, true); + } + + $environment_options['cache'] = $twig_cache_loc; + } + $twig = new Environment($this->get_loader(), $environment_options); + + if (WP_DEBUG) { + $twig->addExtension(new \Twig\Extension\DebugExtension()); + } else { + $twig->addFunction(new TwigFunction('dump', fn () => null)); + } + + /** + * Filters the cache extension activation. + * + * Allows users to disable the cache extension and use their own + * + * @since 2.0.0 + * @param bool $enable_cache_extension Whether to enable the cache extension. + */ + $enable_cache_extension = \apply_filters('timber/cache/enable_extension', true); + + if ($enable_cache_extension && \class_exists('\Twig\CacheExtension\Extension')) { + $twig->addExtension($this->_get_cache_extension()); + } + + /** + * Filters … + * + * @todo Add summary, description, example + * + * @since 0.20.10 + * + * @param Environment $twig The Twig environment you can add functionality to. + */ + $twig = \apply_filters('timber/loader/twig', $twig); + + /** + * Filters … + * + * @deprecated 2.0.0, use `timber/twig` + */ + $twig = \apply_filters_deprecated('twig_apply_filters', [$twig], '2.0.0', 'timber/twig'); + + /** + * Filters the Twig environment used in the global context. + * + * You can use this filter if you want to add additional functionality to Twig, like global + * variables, filters or functions. + * + * @since 0.21.9 + * @example + * ```php + * /** + * * Adds Twig functionality. + * * + * * \@param \Twig\Environment $twig The Twig Environment to which you can add additional functionality. + * *\/ + * add_filter( 'timber/twig', function( $twig ) { + * // Make get_theme_file_uri() usable as {{ theme_file() }} in Twig. + * $twig->addFunction( new Twig\TwigFunction( 'theme_file', 'get_theme_file_uri' ) ); + * + * return $twig; + * } ); + * ``` + * + * ```twig + * + * Logo {{ site.title }} + * + * ``` + * + * @param Environment $twig The Twig environment. + */ + $twig = \apply_filters('timber/twig', $twig); + + /** + * Filters the Twig environment used in the global context. + * + * @deprecated 2.0.0 + */ + $twig = \apply_filters_deprecated('get_twig', [$twig], '2.0.0', 'timber/twig'); + + return $twig; + } + + /** + * Clears Timber’s cache. + * + * @param string $cache_mode + * @return bool Whether Timber’s cache was cleared + */ + public function clear_cache_timber($cache_mode = self::CACHE_USE_DEFAULT) + { + //_transient_timberloader + $cache_mode = $this->_get_cache_mode($cache_mode); + + if (self::CACHE_TRANSIENT === $cache_mode || self::CACHE_SITE_TRANSIENT === $cache_mode) { + // $wpdb->query() might return 0 affected rows, but that means it’s still successful. + return false !== self::clear_cache_timber_database(); + } elseif (self::CACHE_OBJECT === $cache_mode && $this->is_object_cache()) { + return false !== self::clear_cache_timber_object(); + } + + return false; + } + + /** + * Clears Timber cache in database. + * + * @return bool|int Number of deleted rows or false on error. + */ + protected static function clear_cache_timber_database() + { + global $wpdb; + + return $wpdb->query($wpdb->prepare( + "DELETE FROM $wpdb->options WHERE option_name LIKE '%s'", + '_transient%timberloader_%' + )); + } + + /** + * @return bool True when no cache was found or all cache was deleted, false when there was an + * error deleting the cache. + */ + protected static function clear_cache_timber_object() + { + global $wp_object_cache; + + $result = true; + + // Return true if no object cache is set. + if (!isset($wp_object_cache->cache[self::CACHEGROUP])) { + return $result; + } + + $items = $wp_object_cache->cache[self::CACHEGROUP]; + + foreach ($items as $key => $value) { + if (\is_multisite()) { + $key = \preg_replace('/^(.*?):/', '', (string) $key); + } + + // If any cache couldn’t be deleted, the result will be false. + if (!\wp_cache_delete($key, self::CACHEGROUP)) { + $result = false; + } + } + + return $result; + } + + public function clear_cache_twig() + { + $twig = $this->get_twig(); + + // Get the configured cache location. + $cache_location = $twig->getCache(true); + + // Cache not activated. + if (!$cache_location) { + return true; + } + + if (\is_string($cache_location) && \is_dir($cache_location)) { + self::rrmdir($cache_location); + return true; + } + + return false; + } + + /** + * Remove a directory and everything inside + * + * @param string|false $dirPath + */ + public static function rrmdir($dirPath) + { + if (!\is_dir($dirPath)) { + throw new InvalidArgumentException("$dirPath must be a directory"); + } + if (\substr($dirPath, \strlen($dirPath) - 1, 1) != '/') { + $dirPath .= '/'; + } + $files = \glob($dirPath . '*', GLOB_MARK); + foreach ($files as $file) { + if (\is_dir($file)) { + self::rrmdir($file); + } else { + \unlink($file); + } + } + \rmdir($dirPath); + } + + /** + * @return CacheExtension\Extension + */ + private function _get_cache_extension() + { + $key_generator = new Cache\KeyGenerator(); + $cache_provider = new Cache\WPObjectCacheAdapter($this); + $cache_lifetime = \apply_filters('timber/cache/extension/lifetime', 0); + $cache_strategy = new CacheExtension\CacheStrategy\GenerationalCacheStrategy( + $cache_provider, + $key_generator, + $cache_lifetime + ); + $cache_extension = new CacheExtension\Extension($cache_strategy); + + return $cache_extension; + } + + /** + * @param string $key + * @param string $group + * @param string $cache_mode + * @return bool + */ + public function get_cache($key, $group = self::CACHEGROUP, $cache_mode = self::CACHE_USE_DEFAULT) + { + $cache_mode = $this->_get_cache_mode($cache_mode); + $value = false; + $trans_key = \substr($group . '_' . $key, 0, self::TRANS_KEY_LEN); + + /** + * Filters the transient key used for caching. + * + * @api + * @since 2.1.0 + * @example + * ``` + * add_filter( 'timber/cache/transient_key', function( $trans_key, $key, $group, $cache_mode ) { + * return $trans_key . '_my_suffix'; + * }, 10, 4 ); + * ``` + * + * @param string $trans_key The transient key. + * @param string $key The cache key. + * @param string $group The cache group. + * @param string $cache_mode The cache mode. + */ + $trans_key = \apply_filters('timber/cache/transient_key', $trans_key, $key, $group, $cache_mode); + + if (self::CACHE_TRANSIENT === $cache_mode) { + $value = \get_transient($trans_key); + } elseif (self::CACHE_SITE_TRANSIENT === $cache_mode) { + $value = \get_site_transient($trans_key); + } elseif (self::CACHE_OBJECT === $cache_mode && $this->is_object_cache()) { + $value = \wp_cache_get($key, $group); + } + + return $value; + } + + /** + * @param string $key + * @param string|boolean $value + * @param string $group + * @param integer $expires + * @param string $cache_mode + * @return string|boolean + */ + public function set_cache($key, $value, $group = self::CACHEGROUP, $expires = 0, $cache_mode = self::CACHE_USE_DEFAULT) + { + if ((int) $expires < 1) { + $expires = 0; + } + + $cache_mode = $this->_get_cache_mode($cache_mode); + $trans_key = \substr($group . '_' . $key, 0, self::TRANS_KEY_LEN); + + /** + * Filters the transient key used for caching. + * + * @api + * @since 2.1.0 + * @example + * ``` + * add_filter( 'timber/cache/transient_key', function( $trans_key, $key, $group, $cache_mode ) { + * return $trans_key . '_my_suffix'; + * }, 10, 4 ); + * ``` + * + * @param string $trans_key The transient key. + * @param string $key The cache key. + * @param string $group The cache group. + * @param string $cache_mode The cache mode. + */ + $trans_key = \apply_filters('timber/cache/transient_key', $trans_key, $key, $group, $cache_mode); + + if (self::CACHE_TRANSIENT === $cache_mode) { + \set_transient($trans_key, $value, $expires); + } elseif (self::CACHE_SITE_TRANSIENT === $cache_mode) { + \set_site_transient($trans_key, $value, $expires); + } elseif (self::CACHE_OBJECT === $cache_mode && $this->is_object_cache()) { + \wp_cache_set($key, $value, $group, $expires); + } + + return $value; + } + + /** + * @param string $cache_mode + * @return string + */ + private function _get_cache_mode($cache_mode) + { + if (empty($cache_mode) || self::CACHE_USE_DEFAULT === $cache_mode) { + $cache_mode = $this->cache_mode; + } + + // Fallback if self::$cache_mode did not get a valid value + if (!\in_array($cache_mode, self::$cache_modes)) { + $cache_mode = self::CACHE_OBJECT; + } + + return $cache_mode; + } + + /** + * Checks whether WordPress object cache is activated. + * + * @since 2.0.0 + * @return bool + */ + protected function is_object_cache() + { + return isset($GLOBALS['wp_object_cache']) && \is_object($GLOBALS['wp_object_cache']); + } +} diff --git a/vendor/timber/timber/src/LocationManager.php b/vendor/timber/timber/src/LocationManager.php new file mode 100644 index 0000000..8a555c3 --- /dev/null +++ b/vendor/timber/timber/src/LocationManager.php @@ -0,0 +1,236 @@ + \array_map('trailingslashit', $loc), $locs); + + /** + * Filters the filesystem paths to search for Twig templates. + * + * @example + * ``` + * add_filter( 'timber/locations', function( $locs ) { + * $locs = \array_map(function ($loc) { + * \array_unshift($loc, \dirname(__DIR__) . '/my-custom-dir'); + * return $loc; + * }, $locs); + * + * return $locs; + * } ); + * ``` + * + * @since 0.20.10 + * + * @param array $locs An array of filesystem paths to search for Twig templates. + */ + $locs = \apply_filters('timber/locations', $locs); + + /** + * Filters the filesystem paths to search for Twig templates. + * + * @deprecated 2.0.0, use `timber/locations` + */ + $locs = \apply_filters_deprecated('timber_locations', [$locs], '2.0.0', 'timber/locations'); + + return $locs; + } + + /** + * @return array + */ + protected static function get_locations_theme() + { + $theme_locs = []; + $theme_dirs = LocationManager::get_locations_theme_dir(); + $roots = [\get_stylesheet_directory(), \get_template_directory()]; + $roots = \array_map('realpath', $roots); + $roots = \array_unique($roots); + foreach ($roots as $root) { + if (!\is_dir($root)) { + continue; + } + + $theme_locs[Loader::MAIN_NAMESPACE][] = $root; + $root = \trailingslashit($root); + foreach ($theme_dirs as $namespace => $dirnames) { + $dirnames = self::convert_to_array($dirnames); + \array_map(function ($dirname) use ($root, $namespace, &$theme_locs) { + $tloc = \realpath($root . $dirname); + if (\is_dir($tloc)) { + $theme_locs[$namespace][] = $tloc; + } + }, $dirnames); + } + } + + return $theme_locs; + } + + /** + * Get calling script file. + * @api + * @param int $offset + * @return string|null + */ + public static function get_calling_script_file($offset = 0) + { + $callers = []; + $backtrace = \debug_backtrace(); + foreach ($backtrace as $trace) { + if (\array_key_exists('file', $trace) && $trace['file'] != __FILE__) { + $callers[] = $trace['file']; + } + } + $callers = \array_unique($callers); + $callers = \array_values($callers); + return $callers[$offset]; + } + + /** + * Get calling script dir. + * @api + * @return string|null + */ + public static function get_calling_script_dir($offset = 0) + { + $caller = self::get_calling_script_file($offset); + if (!\is_null($caller)) { + $pathinfo = PathHelper::pathinfo($caller); + $dir = $pathinfo['dirname']; + return $dir; + } + + return null; + } + + /** + * returns an array of the directory inside themes that holds twig files + * @return array the names of directories, ie: array('__MAIN__' => ['templates', 'views']); + */ + public static function get_locations_theme_dir() + { + if (\is_string(Timber::$dirname)) { + return [ + Loader::MAIN_NAMESPACE => [Timber::$dirname], + ]; + } + return Timber::$dirname; + } + + /** + * @deprecated since 2.0.0 Use `add_filter('timber/locations', $locations)` instead. + * @return array + */ + protected static function get_locations_user() + { + $locs = []; + if (isset(Timber::$locations)) { + if (\is_string(Timber::$locations)) { + Timber::$locations = [Timber::$locations]; + } + foreach (Timber::$locations as $tloc => $namespace_or_tloc) { + if (\is_string($tloc)) { + $namespace = $namespace_or_tloc; + } else { + $tloc = $namespace_or_tloc; + $namespace = null; + } + + $tloc = \realpath($tloc); + if (\is_dir($tloc)) { + if (!\is_string($namespace)) { + $locs[Loader::MAIN_NAMESPACE][] = $tloc; + } else { + $locs[$namespace][] = $tloc; + } + } + } + } + + return $locs; + } + + /** + * + * Converts the variable to an array with the var as the sole element. Ignores if it's already an array + * + * @param mixed $var the variable to test and maybe convert + * @return array + */ + protected static function convert_to_array(mixed $var) + { + if (\is_string($var)) { + $var = [$var]; + } + return $var; + } + + /** + * @param bool|string $caller the calling directory + * @param bool $skip_parent whether to skip the parent theme + * @return array + */ + protected static function get_locations_caller($caller = false, bool $skip_parent = false) + { + $locs = []; + if ($caller && \is_string($caller)) { + $caller = \realpath($caller); + $parent_theme = \get_template_directory(); + $parent_slug = \basename((string) $parent_theme); + + if ($skip_parent && \str_contains($caller, $parent_slug)) { + return $locs; + } + + if (\is_dir($caller)) { + $locs[Loader::MAIN_NAMESPACE][] = $caller; + } + $caller = \trailingslashit($caller); + foreach (LocationManager::get_locations_theme_dir() as $namespace => $dirnames) { + $dirnames = self::convert_to_array($dirnames); + \array_map(function ($dirname) use ($caller, $namespace, &$locs) { + $caller_sub = \realpath($caller . $dirname); + if (\is_dir($caller_sub)) { + $locs[$namespace][] = $caller_sub; + } + }, $dirnames); + } + } + + return $locs; + } + + /** + * returns an array of the directory set with "open_basedir" + * see : https://www.php.net/manual/en/ini.core.php#ini.open-basedir + * @return array + */ + protected static function get_locations_open_basedir() + { + $open_basedir = \ini_get('open_basedir'); + + return [ + Loader::MAIN_NAMESPACE => [ + $open_basedir ? ABSPATH : '/', + ], + ]; + } +} diff --git a/vendor/timber/timber/src/Menu.php b/vendor/timber/timber/src/Menu.php new file mode 100644 index 0000000..9a4112d --- /dev/null +++ b/vendor/timber/timber/src/Menu.php @@ -0,0 +1,624 @@ + '', + 'container' => 'div', + 'container_class' => '', + 'container_id' => '', + 'container_aria_label' => '', + 'menu_class' => 'menu', + 'menu_id' => '', + 'echo' => true, + 'fallback_cb' => 'wp_page_menu', + 'before' => '', + 'after' => '', + 'link_before' => '', + 'link_after' => '', + 'items_wrap' => '
      %3$s
    ', + 'item_spacing' => 'preserve', + 'depth' => 0, + 'walker' => '', + 'theme_location' => '', + ]; + + $args = \wp_parse_args($args, $defaults); + + if (!\in_array($args['item_spacing'], ['preserve', 'discard'], true)) { + // Invalid value, fall back to default. + $args['item_spacing'] = $defaults['item_spacing']; + } + + /** + * @see wp_nav_menu() + */ + $args = \apply_filters('wp_nav_menu_args', $args); + $args = (object) $args; + + /** + * Since Timber doesn't use HTML here, try to unserialize the maybe cached menu object + * + * @see wp_nav_menu() + */ + $nav_menu = \apply_filters('pre_wp_nav_menu', null, $args); + if (null !== $nav_menu) { + try { + $nav_menu = \unserialize($nav_menu); + if ($nav_menu instanceof Menu) { + return $nav_menu; + } + } catch (Throwable) { + } + } + + /** + * No valid menu term provided. + * + * In earlier versions, Timber returned a pages menu if no menu was found. Now, it returns + * null. If you still need the pages menu, you can use Timber\Timber::get_pages_menu(). + * + * @see \Timber\Timber::get_pages_menu() + */ + if (!$menu) { + return null; + } + + // Skip the menu term guessing part, we already have our menu term + + $menu_items = \wp_get_nav_menu_items($menu->term_id, [ + 'update_post_term_cache' => false, + ]); + + \_wp_menu_item_classes_by_context($menu_items); + + $sorted_menu_items = []; + $menu_items_with_children = []; + foreach ((array) $menu_items as $menu_item) { + $sorted_menu_items[$menu_item->menu_order] = $menu_item; + if ($menu_item->menu_item_parent) { + $menu_items_with_children[$menu_item->menu_item_parent] = true; + } + } + + // Add the menu-item-has-children class where applicable. + if ($menu_items_with_children) { + foreach ($sorted_menu_items as &$menu_item) { + if (isset($menu_items_with_children[$menu_item->ID])) { + $menu_item->classes[] = 'menu-item-has-children'; + } + } + } + + unset($menu_items, $menu_item); + + /** + * @see wp_nav_menu() + */ + $sorted_menu_items = \apply_filters('wp_nav_menu_objects', $sorted_menu_items, $args); + + /** + * Filters the sorted list of menu item objects before creating the Menu object. + * + * @since 2.0.0 + * @example + * ``` + * add_filter( 'timber/menu/item_objects', function ( $items ) { + * return array_map(function ($item) { + * if ( is_object( $item ) && ! ( $item instanceof \WP_Post ) ) { + * return new \WP_Post( get_object_vars( $item ) ); + * } + * + * return $item; + * }, $items); + * } ); + * ``` + * + * @param array $item + * @param WP_Term $menu + */ + $sorted_menu_items = \apply_filters('timber/menu/item_objects', $sorted_menu_items, $menu); + + // Create Menu object + $nav_menu = new static($menu, (array) $args); + $nav_menu->sorted_menu_items = $sorted_menu_items; + + // Convert items into MenuItem objects + $sorted_menu_items = $nav_menu->convert_menu_items($sorted_menu_items); + $sorted_menu_items = $nav_menu->order_children($sorted_menu_items); + $sorted_menu_items = $nav_menu->strip_to_depth_limit($sorted_menu_items); + + $nav_menu->items = $sorted_menu_items; + unset($sorted_menu_items); + + /** + * Since Timber doesn't use HTML, serialize the menu object to provide a cacheable string + * + * @see wp_nav_menu() + */ + $_nav_menu = \apply_filters('wp_nav_menu', \serialize($nav_menu), $args); + + return $nav_menu; + } + + /** + * Initialize a menu. + * + * @api + * + * @param WP_Term|null $term A menu slug, the term ID of the menu, the full name from the admin + * menu, the slug of the registered location or nothing. Passing + * nothing is good if you only have one menu. Timber will grab what + * it finds. + * @param array $args Optional. Right now, only the `depth` is supported which says how + * many levels of hierarchy should be included in the menu. Default + * `0`, which is all levels. + */ + protected function __construct(?WP_Term $term, array $args = []) + { + // For future enhancements? + $this->raw_args = $args; + $this->args = (object) $args; + + if (isset($this->args->depth)) { + $this->depth = (int) $this->args->depth; + } + + if (!$term) { + return; + } + + // Set theme location if available + $this->theme_location = Timber::get_menu_location($term); + if ($this->theme_location) { + $this->args->theme_location = $this->theme_location; + } + + $this->import($term); + $this->ID = $this->term_id; + $this->id = $this->term_id; + $this->wp_object = $term; + $this->title = $this->name; + } + + /** + * Gets the underlying WordPress Core object. + * + * @since 2.0.0 + * + * @return WP_Term|null + */ + public function wp_object(): ?WP_Term + { + return $this->wp_object; + } + + /** + * Convert menu items into MenuItem objects + * + * @return MenuItem[] + */ + protected function convert_menu_items(array $menu_items): array + { + $menu_item_factory = new MenuItemFactory(); + return \array_map(fn ($item): MenuItem => $menu_item_factory->from($item, $this), $menu_items); + } + + /** + * Find a parent menu item in a set of menu items. + * + * @api + * @param array $menu_items An array of menu items. + * @param int $parent_id The parent ID to look for. + * @return MenuItem|null A menu item. False if no parent was found. + */ + public function find_parent_item_in_menu(array $menu_items, int $parent_id): ?MenuItem + { + foreach ($menu_items as $item) { + if ($item->ID === $parent_id) { + return $item; + } + } + return null; + } + + /** + * @internal + * @return MenuItem[] + */ + protected function order_children(array $items): array + { + $items_by_id = []; + $menu_items = []; + + foreach ($items as $item) { + // Index each item by its ID + $items_by_id[$item->ID] = $item; + } + + // Walk through our indexed items and assign them to their parents as applicable + foreach ($items_by_id as $item) { + if (!empty($item->menu_item_parent) && isset($items_by_id[$item->menu_item_parent])) { + // This one is a child item, add it to its parent + $items_by_id[$item->menu_item_parent]->add_child($item); + } else { + // This is a top-level item, add it as such + $menu_items[] = $item; + } + } + return $menu_items; + } + + /** + * @internal + */ + protected function strip_to_depth_limit(array $menu_items, int $current = 1): array + { + $depth = (int) $this->depth; // Confirms still int. + if ($depth <= 0) { + return $menu_items; + } + + foreach ($menu_items as &$current_item) { + if ($current === $depth) { + $current_item->remove_class('menu-item-has-children'); + $current_item->children = false; + continue; + } + + $current_item->children = $this->strip_to_depth_limit($current_item->children, $current + 1); + } + + return $menu_items; + } + + /** + * Gets a menu meta value. + * + * @api + * @deprecated 2.0.0, use `{{ menu.meta('field_name') }}` instead. + * @see \Timber\Menu::meta() + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_field($field_name = null) + { + Helper::deprecated( + "{{ menu.get_field('field_name') }}", + "{{ menu.meta('field_name') }}", + '2.0.0' + ); + + return $this->meta($field_name); + } + + /** + * Get menu items. + * + * Instead of using this function, you can use the `$items` property directly to get the items + * for a menu. + * + * @api + * @example + * ```twig + * {% for item in menu.get_items %} + * {{ item.title }} + * {% endfor %} + * ``` + * @return array Array of `Timber\MenuItem` objects. Empty array if no items could be found. + */ + public function get_items() + { + if (\is_array($this->items)) { + return $this->items; + } + + return []; + } + + /** + * Get the current MenuItem based on the WP context + * + * @see _wp_menu_item_classes_by_context() + * @example + * Say you want to render the sub-tree of the main menu that corresponds + * to the menu item for the current page, such as in a context-aware sidebar: + * ```twig + * + * ``` + * @param int $depth the maximum depth to traverse the menu tree to find the + * current item. Defaults to null, meaning no maximum. 1-based, meaning the + * top level is 1. + * @return MenuItem the current `Timber\MenuItem` object, i.e. the menu item + * corresponding to the current post. + */ + public function current_item($depth = null) + { + if (false === $this->_current_item) { + // I TOLD YOU BEFORE. + return false; + } + + if (empty($this->items)) { + $this->_current_item = false; + return $this->_current_item; + } + + if (!isset($this->_current_item)) { + $current = $this->traverse_items_for_current( + $this->items, + $depth + ); + + if (\is_null($depth)) { + $this->_current_item = $current; + } else { + return $current; + } + } + + return $this->_current_item; + } + + /** + * Alias for current_top_level_item(1). + * + * @return MenuItem the current top-level `Timber\MenuItem` object. + */ + public function current_top_level_item() + { + return $this->current_item(1); + } + + /** + * Traverse an array of MenuItems in search of the current item. + * + * @internal + * @param array $items the items to traverse. + */ + private function traverse_items_for_current($items, $depth) + { + $current = false; + $currentDepth = 1; + $i = 0; + + while (isset($items[$i])) { + $item = $items[$i]; + + if ($item->current) { + // cache this item for subsequent calls. + $current = $item; + // stop looking. + break; + } elseif ($item->current_item_ancestor) { + // we found an ancestor, + // but keep looking for a more precise match. + $current = $item; + + if ($currentDepth === $depth) { + // we're at max traversal depth. + return $current; + } + + // we're in the right subtree, so go deeper. + if ($item->children()) { + // reset the counter, since we're at a new level. + $items = $item->children(); + $i = 0; + $currentDepth++; + continue; + } + } + + $i++; + } + + return $current; + } + + public function __toString() + { + static $menu_id_slugs = []; + + $args = $this->args; + + $items = ''; + $nav_menu = ''; + $show_container = false; + + if ($args->container) { + /** + * Filters the list of HTML tags that are valid for use as menu containers. + * + * @since 3.0.0 + * + * @param string[] $tags The acceptable HTML tags for use as menu containers. + * Default is array containing 'div' and 'nav'. + */ + $allowed_tags = \apply_filters('wp_nav_menu_container_allowedtags', ['div', 'nav']); + + if (\is_string($args->container) && \in_array($args->container, $allowed_tags, true)) { + $show_container = true; + $class = $args->container_class ? ' class="' . \esc_attr($args->container_class) . '"' : ' class="menu-' . $this->slug . '-container"'; + $id = $args->container_id ? ' id="' . \esc_attr($args->container_id) . '"' : ''; + $aria_label = ('nav' === $args->container && $args->container_aria_label) ? ' aria-label="' . \esc_attr($args->container_aria_label) . '"' : ''; + $nav_menu .= '<' . $args->container . $id . $class . $aria_label . '>'; + } + } + + $items .= \walk_nav_menu_tree($this->sorted_menu_items, $args->depth, $args); + + // Attributes. + if (!empty($args->menu_id)) { + $wrap_id = $args->menu_id; + } else { + $wrap_id = 'menu-' . $this->slug; + + while (\in_array($wrap_id, $menu_id_slugs, true)) { + if (\preg_match('#-(\d+)$#', (string) $wrap_id, $matches)) { + $wrap_id = \preg_replace('#-(\d+)$#', '-' . ++$matches[1], (string) $wrap_id); + } else { + $wrap_id = $wrap_id . '-1'; + } + } + } + $menu_id_slugs[] = $wrap_id; + + $wrap_class = $args->menu_class ?: ''; + + $nav_menu .= \sprintf($args->items_wrap, \esc_attr($wrap_id), \esc_attr($wrap_class), $items); + if ($show_container) { + $nav_menu .= 'container . '>'; + } + + return $nav_menu; + } + + /** + * Checks whether the current user can edit the menu. + * + * @api + * @since 2.0.0 + * @return bool + */ + public function can_edit(): bool + { + return \current_user_can('edit_theme_options'); + } +} diff --git a/vendor/timber/timber/src/MenuItem.php b/vendor/timber/timber/src/MenuItem.php new file mode 100644 index 0000000..dabd104 --- /dev/null +++ b/vendor/timber/timber/src/MenuItem.php @@ -0,0 +1,606 @@ +title = $this->wp_object->title; + + $this->import($this->wp_object); + $this->import_classes($this->wp_object); + $this->id = $this->wp_object->ID; + $this->ID = $this->wp_object->ID; + + $this->_name = $this->wp_object->name ?? ''; + $this->add_class('menu-item-' . $this->ID); + + /** + * Because init_as_page_menu already set it to simulate the master object + * + * @see Menu::init_as_page_menu + */ + if (!isset($this->object_id)) { + $this->object_id = (int) \get_post_meta($this->ID, '_menu_item_object_id', true); + } + } + + /** + * Gets the underlying WordPress Core object. + * + * @since 2.0.0 + * + * @return WP_Post|null + */ + public function wp_object(): ?WP_Post + { + return $this->wp_object; + } + + /** + * Add a CSS class the menu item should have. + * + * @param string $class_name CSS class name to be added. + */ + public function add_class(string $class_name) + { + // Class name is already there + if (\in_array($class_name, $this->classes, true)) { + return; + } + $this->classes[] = $class_name; + $this->update_class(); + } + + /** + * Add a CSS class the menu item should have. + * + * @param string $class_name CSS class name to be added. + */ + public function remove_class(string $class_name) + { + // Class name is already there + if (!\in_array($class_name, $this->classes, true)) { + return; + } + $class_key = \array_search($class_name, $this->classes, true); + unset($this->classes[$class_key]); + $this->update_class(); + } + + /** + * Update class string + */ + protected function update_class() + { + $this->class = \trim(\implode(' ', $this->classes)); + } + + /** + * Get the label for the menu item. + * + * @api + * @return string The label for the menu item. + */ + public function name() + { + return $this->title(); + } + + /** + * Magic method to get the label for the menu item. + * + * @api + * @example + * ```twig + * {{ item }} + * ``` + * @see \Timber\MenuItem::name() + * @return string The label for the menu item. + */ + public function __toString(): string + { + return $this->name(); + } + + /** + * Get the slug for the menu item. + * + * @api + * @example + * ```twig + *
      + * {% for item in menu.items %} + *
    • + * {{ item.name }} + *
    • + * {% endfor %} + *
    + * ``` + * @return string The URL-safe slug of the menu item. + */ + public function slug() + { + $mo = $this->master_object(); + if ($mo && $mo->post_name) { + return $mo->post_name; + } + return $this->post_name; + } + + /** + * Allows dev to access the "master object" (ex: post, page, category, post type object) the menu item represents + * + * @api + * @example + * ```twig + *
    + * {% for item in menu.items %} + * + * {% endfor %} + *
    + * ``` + * @return mixed|null Whatever object (Timber\Post, Timber\Term, etc.) the menu item represents. + */ + public function master_object() + { + switch ($this->type) { + case 'post_type': + $factory = new PostFactory(); + break; + case 'taxonomy': + $factory = new TermFactory(); + break; + case 'post_type_archive': + return \get_post_type_object($this->object); + default: + $factory = null; + break; + } + + return $factory && $this->object_id ? $factory->from($this->object_id) : null; + } + + /** + * Add a new `Timber\MenuItem` object as a child of this menu item. + * + * @api + * + * @param MenuItem $item The menu item to add. + */ + public function add_child(MenuItem $item) + { + $this->children[] = $item; + $item->level = $this->level + 1; + if (\count($this->children)) { + $this->update_child_levels(); + } + } + + /** + * Update the level data associated with $this. + * + * @internal + * @return bool|null + */ + public function update_child_levels() + { + if (\is_array($this->children)) { + foreach ($this->children as $child) { + $child->level = $this->level + 1; + $child->update_child_levels(); + } + return true; + } + } + + /** + * Imports the classes to be used in CSS. + * + * @internal + * + * @param array|object $data to import. + */ + public function import_classes($data) + { + if (\is_array($data)) { + $data = (object) $data; + } + $this->classes = \array_unique(\array_merge($this->classes, $data->classes ?? [])); + $this->classes = \array_values(\array_filter($this->classes)); + + $args = new stdClass(); + if (isset($this->menu->args)) { + // The args need to be an object. + $args = $this->menu->args; + } + + /** + * @see Walker_Nav_Menu + */ + $this->classes = \apply_filters( + 'nav_menu_css_class', + $this->classes, + $this->wp_object, + $args, + 0 // TODO: find the right depth + ); + + $this->update_class(); + } + + /** + * Get children of a menu item. + * + * You can also directly access the children through the `$children` property (`item.children` + * in Twig). + * + * @internal + * @deprecated 2.0.0, use `item.children` instead. + * @example + * ```twig + * {% for child in item.get_children %} + * + * {% endfor %} + * ``` + * @return array|bool Array of children of a menu item. Empty if there are no child menu items. + */ + public function get_children() + { + Helper::deprecated( + "{{ item.get_children }}", + "{{ item.children }}", + '2.0.0' + ); + return $this->children(); + } + + /** + * Checks to see if the menu item is an external link. + * + * If your site is `example.org`, then `google.com/whatever` is an external link. This is + * helpful when you want to style external links differently or create rules for the target of a + * link. + * + * @api + * @example + * ```twig + * + * ``` + * + * Or when you only want to add a target attribute if it is really needed: + * + * ```twig + * + * ``` + * + * In combination with `is_target_blank()`: + * + * ```twig + * + * ``` + * + * @return bool Whether the link is external or not. + */ + public function is_external() + { + if ($this->type !== 'custom') { + return false; + } + + // Additional check for relative/non-URLs + if (false === URLHelper::is_url($this->link())) { + return false; + } + + return URLHelper::is_external($this->link()); + } + + /** + * Checks whether the «Open in new tab» option checked in the menu item options. + * + * @example + * ```twig + * + * ``` + * + * In combination with `is_external()` + * + * ```twig + * + * ``` + * + * @return bool Whether the menu item has the «Open in new tab» option checked in the menu item + * options. + */ + public function is_target_blank() + { + return '_blank' === $this->meta('_menu_item_target'); + } + + /** + * Gets the target of a menu item according to the «Open in new tab» option in the menu item + * options. + * + * This function return `_blank` when the option to open a menu item in a new tab is checked in + * the WordPress backend, and `_self` if the option is not checked. Beware `_self` is the + * default value for the target attribute, which means you could leave it out. You can use + * `item.is_target_blank` if you want to use a conditional. + * + * @example + * ```twig + * + * ``` + * + * @return string + */ + public function target() + { + $target = $this->meta('_menu_item_target'); + if (!$target) { + return '_self'; + } + return $target; + } + + /** + * Timber Menu. + * + * @api + * @since 1.12.0 + * @return Menu The `Menu` object the menu item is associated with. + */ + public function menu() + { + return $this->menu; + } + + /** + * Gets a menu item meta value. + * + * @api + * @deprecated 2.0.0, use `{{ item.meta('field_name') }}` instead. + * @see \Timber\MenuItem::meta() + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_field($field_name = null) + { + Helper::deprecated( + "{{ item.get_field('field_name') }}", + "{{ item.meta('field_name') }}", + '2.0.0' + ); + return $this->meta($field_name); + } + + /** + * Get the child menu items of a `Timber\MenuItem`. + * + * @api + * @example + * ```twig + * {% for child in item.children %} + * + * {% endfor %} + * ``` + * @return array|bool Array of children of a menu item. Empty if there are no child menu items. + */ + public function children() + { + return $this->children; + } + + /** + * Checks to see if the menu item is an external link. + * + * @api + * @deprecated 2.0.0, use `{{ item.is_external }}` + * @see \Timber\MenuItem::is_external() + * + * @return bool Whether the link is external or not. + */ + public function external() + { + Helper::warn('{{ item.external }} is deprecated. Use {{ item.is_external }} instead.'); + return $this->is_external(); + } + + /** + * Get the full link to a menu item. + * + * @api + * @example + * ```twig + * {% for item in menu.items %} + *
  • {{ item.title }}
  • + * {% endfor %} + * ``` + * @return string A full URL, like `https://mysite.com/thing/`. + */ + public function link() + { + return $this->url; + } + + /** + * Get the relative path of the menu item’s link. + * + * @api + * @example + * ```twig + * {% for item in menu.items %} + *
  • {{ item.title }}
  • + * {% endfor %} + * ``` + * @return string The path of a URL, like `/foo`. + */ + public function path() + { + return URLHelper::get_rel_url($this->link()); + } + + /** + * Get the public label for the menu item. + * + * @api + * @example + * ```twig + * {% for item in menu.items %} + *
  • {{ item.title }}
  • + * {% endfor %} + * ``` + * @return string The public label, like "Foo". + */ + public function title() + { + /** + * @see Walker_Nav_Menu::start_el() + */ + $title = \apply_filters('nav_menu_item_title', $this->title, $this->wp_object, $this->menu->args ?: new stdClass(), $this->level); + return $title; + } + + /** + * Checks whether the current user can edit the menu item. + * + * @api + * @since 2.0.0 + * @return bool + */ + public function can_edit(): bool + { + return \current_user_can('edit_theme_options'); + } +} diff --git a/vendor/timber/timber/src/MetaInterface.php b/vendor/timber/timber/src/MetaInterface.php new file mode 100644 index 0000000..3dfea2a --- /dev/null +++ b/vendor/timber/timber/src/MetaInterface.php @@ -0,0 +1,48 @@ + 'menu_order, post_title', + 'echo' => true, + 'link_before' => '', + 'link_after' => '', + 'before' => '
      ', + 'after' => '
    ', + 'item_spacing' => 'discard', + 'walker' => '', + 'menu' => '', + 'theme_location' => '', + 'menu_id' => '', + 'menu_class' => 'menu', + 'container' => 'div', + ]; + + $args = \wp_parse_args($args, $defaults); + + if (!\in_array($args['item_spacing'], ['preserve', 'discard'], true)) { + // Invalid value, fall back to default. + $args['item_spacing'] = $defaults['item_spacing']; + } + + /** + * Filters the arguments used to generate a page-based menu. + * + * @see wp_page_menu() + * + * @param array $args An array of page menu arguments. See wp_page_menu() for information on + * accepted arguments. + */ + $args = \apply_filters('wp_page_menu_args', $args); + + /** + * Default arguments from wp_list_pages() function. + * + * @see wp_list_pages() + */ + $defaults = [ + 'depth' => 0, + 'show_date' => '', + 'date_format' => \get_option('date_format'), + 'child_of' => 0, + 'exclude' => '', + 'title_li' => \__('Pages'), + 'echo' => 1, + 'authors' => '', + 'sort_column' => 'menu_order, post_title', + 'link_before' => '', + 'link_after' => '', + 'item_spacing' => 'preserve', + 'walker' => '', + ]; + + $args = \wp_parse_args($args, $defaults); + + if (!\in_array($args['item_spacing'], ['preserve', 'discard'], true)) { + // Invalid value, fall back to default. + $args['item_spacing'] = $defaults['item_spacing']; + } + + // Sanitize, mostly to keep spaces out. + $args['exclude'] = \preg_replace('/[^0-9,]/', '', (string) $args['exclude']); + + // Allow plugins to filter an array of excluded pages (but don't put a nullstring into the array). + $exclude_array = ($args['exclude']) + ? \explode(',', $args['exclude']) + : []; + + /** + * Filters the array of pages to exclude from the pages list. + * + * @param string[] $exclude_array An array of page IDs to exclude. + */ + $args['exclude'] = \implode(',', \apply_filters('wp_list_pages_excludes', $exclude_array)); + + $args['hierarchical'] = 0; + + $pages_menu = new static(null, $args); + + // Query pages. + $menu_items = \get_pages($pages_menu->args); + + if (!empty($menu_items)) { + $menu_items = \array_map([$pages_menu, 'pre_setup_nav_menu_item'], $menu_items); + $menu_items = \array_map('wp_setup_nav_menu_item', $menu_items); + + /** + * Can’t really apply the "wp_get_nav_menu_items" filter here, because we don’t have a + * $menu object to pass in. + */ + + \_wp_menu_item_classes_by_context($menu_items); + + $menu_items_with_children = []; + + foreach ((array) $menu_items as $menu_item) { + if ($menu_item->menu_item_parent) { + $menu_items_with_children[$menu_item->menu_item_parent] = true; + } + } + + // Add the menu-item-has-children class where applicable. + if (!empty($menu_items_with_children)) { + foreach ($menu_items as &$menu_item) { + if (isset($menu_items_with_children[$menu_item->ID])) { + $menu_item->classes[] = 'menu-item-has-children'; + } + } + } + + unset($menu_item); + + if (\is_array($menu_items)) { + /** + * Filters the arguments used to display a navigation menu. + * + * @see wp_nav_menu() + * + * @param array $args Array of wp_nav_menu() arguments. + */ + $args = \apply_filters('wp_nav_menu_args', $args); + $args = (object) $args; + + /** + * Filters the sorted list of menu item objects before generating the menu's HTML. + * + * @param array $menu_items The menu items, sorted by each menu item's menu order. + * @param stdClass $args An object containing wp_nav_menu() arguments. + */ + $menu_items = \apply_filters('wp_nav_menu_objects', $menu_items, $args); + + $menu_items = $pages_menu->convert_menu_items($menu_items); + $menu_items = $pages_menu->order_children($menu_items); + $menu_items = $pages_menu->strip_to_depth_limit($menu_items); + } + + $pages_menu->items = $menu_items; + } + + /** + * Since Timber doesn’t use HTML, serialize the menu object to provide a cacheable string. + * + * Certain caching plugins will use this filter to cache a menu and return it early in the + * `pre_wp_nav_menu` filter. + * + * We can’t use the result of this filter, because it would return a string. That’s why we + * don’t assign the result of the filter to a variable. + * + * @see wp_nav_menu() + */ + \apply_filters('wp_nav_menu', \serialize($pages_menu), $args); + + return $pages_menu; + } + + /** + * Sets up properties needed for mocking nav menu items. + * + * We need to set some properties so that we can use `wp_setup_nav_menu_item()` on the menu + * items and a proper menu item hierarchy can be built. + * + * @param WP_Post $post A post object. + * + * @return WP_Post Updated post object. + */ + protected function pre_setup_nav_menu_item($post) + { + $post->object_id = $post->ID; + $post->menu_item_parent = $post->post_parent; + $post->object = $post->post_type; + $post->post_type = 'nav_menu_item'; + $post->type = 'post_type'; + + return $post; + } +} diff --git a/vendor/timber/timber/src/Pagination.php b/vendor/timber/timber/src/Pagination.php new file mode 100644 index 0000000..719fe23 --- /dev/null +++ b/vendor/timber/timber/src/Pagination.php @@ -0,0 +1,269 @@ +init($prefs, $wp_query); + } + + /** + * Get pagination. + * + * @api + * @param array $prefs + * @return array mixed + */ + public static function get_pagination($prefs = []) + { + $pagination = new self($prefs); + $pagination = \get_object_vars($pagination); + return $pagination; + } + + protected function init($prefs = [], $wp_query = null) + { + if (!$wp_query) { + global $wp_query; + } + + // use the current page from the provided query if available; else fall back to the global + $paged = $wp_query->query_vars['paged'] ?? \get_query_var('paged'); + + global $wp_rewrite; + $args = []; + // calculate the total number of pages based on found posts and posts per page + $ppp = $wp_query->query_vars['posts_per_page'] ?? 10; + + $args['total'] = \ceil($wp_query->found_posts / $ppp); + if ($wp_rewrite->using_permalinks()) { + $url = \explode('?', (string) \get_pagenum_link(0, false)); + if (isset($url[1])) { + $query = []; + \wp_parse_str($url[1], $query); + $args['add_args'] = $query; + } + $args['format'] = $wp_rewrite->pagination_base . '/%#%'; + $args['base'] = \trailingslashit($url[0]) . '%_%'; + } else { + $big = 999999999; + $pagination_link = \get_pagenum_link($big, false); + $args['base'] = \str_replace('paged=' . $big, '', (string) $pagination_link); + $args['format'] = '?paged=%#%'; + } + + $args['type'] = 'array'; + $args['current'] = \max(1, $paged); + $args['mid_size'] = \max(9 - $args['current'], 3); + if (\is_int($prefs)) { + $args['mid_size'] = $prefs - 2; + } else { + $args = \array_merge($args, $prefs); + } + $this->current = $args['current']; + $this->total = $args['total']; + $this->pages = self::paginate_links($args); + if ($this->total <= \count($this->pages)) { + // decrement current so that it matches up with the 0 based index used by the pages array + $current = $this->current - 1; + } else { + // $data['current'] can't be used b/c there are more than 10 pages and we are condensing with dots + foreach ($this->pages as $key => $page) { + if (!empty($page['current'])) { + $current = $key; + break; + } + } + } + + // set next and prev using pages array generated by paginate links + if (isset($current) && isset($this->pages[$current + 1]) && isset($this->pages[$current + 1]['link'])) { + $this->next = [ + 'link' => $this->pages[$current + 1]['link'], + 'class' => 'page-numbers next', + ]; + } + if (isset($current) && isset($this->pages[$current - 1]) && isset($this->pages[$current - 1]['link'])) { + $this->prev = [ + 'link' => $this->pages[$current - 1]['link'], + 'class' => 'page-numbers prev', + ]; + } + if ($this->current < 2) { + $this->prev = ''; + } + if ($this->total === (float) 0) { + $this->next = ''; + } + } + + /** + * + * + * @param array $args + * @return array + */ + public static function paginate_links($args = []) + { + $defaults = [ + 'base' => '%_%', + // https://example.com/all_posts.php%_% : %_% is replaced by format (below) + 'format' => '?page=%#%', + // ?page=%#% : %#% is replaced by the page number + 'total' => 1, + 'current' => 0, + 'show_all' => false, + 'prev_next' => false, + 'prev_text' => \__('« Previous'), + 'next_text' => \__('Next »'), + 'start_size' => -1, + 'end_size' => 1, + 'mid_size' => 2, + 'type' => 'array', + 'add_args' => [], + // array of query args to add + 'add_fragment' => '', + ]; + $args = \wp_parse_args($args, $defaults); + + $args = self::sanitize_args($args); + + // Who knows what else people pass in $args + $args['total'] = \intval((int) $args['total']); + if ($args['total'] < 2) { + return []; + } + $args['current'] = (int) $args['current']; + $args['end_size'] = 0 <= (int) $args['end_size'] ? (int) $args['end_size'] : 1; // Out of bounds? Make it the default. + $args['start_size'] = 0 <= (int) $args['start_size'] ? (int) $args['start_size'] : $args['end_size']; // Default to end_size for backwards compat + $args['mid_size'] = 0 <= (int) $args['mid_size'] ? (int) $args['mid_size'] : 2; + $args['add_args'] = \is_array($args['add_args']) ? $args['add_args'] : false; + $page_links = []; + $dots = true; + for ($n = 1; $n <= $args['total']; $n++) { + $n_display = \number_format_i18n($n); + if ($n == $args['current']) { + $page_links[] = [ + 'class' => 'page-number page-numbers current', + 'title' => $n_display, + 'text' => $n_display, + 'name' => $n_display, + 'current' => true, + ]; + $dots = true; + } else { + if ( + $args['show_all'] + || ( + $n <= (int) $args['start_size'] + || ( + $args['current'] + && $n >= (int) $args['current'] - (int) $args['mid_size'] + && $n <= (int) $args['current'] + (int) $args['mid_size'] + ) + || $n > (int) $args['total'] - (int) $args['end_size'] + ) + ) { + $link = \str_replace('%_%', 1 == $n ? '' : $args['format'], (string) $args['base']); + $link = \str_replace('%#%', $n, $link); + + // we first follow the user trailing slash configuration + $link = URLHelper::user_trailingslashit($link); + + // then we add all required querystring parameters + if ($args['add_args']) { + $link = \add_query_arg($args['add_args'], $link); + } + + // last, we add fragment if needed + $link .= $args['add_fragment']; + + $link = \apply_filters('paginate_links', $link); + + $page_links[] = [ + 'class' => 'page-number page-numbers', + 'link' => \esc_url($link), + 'title' => $n_display, + 'name' => $n_display, + 'current' => $args['current'] == $n, + ]; + $dots = true; + } elseif ($dots && !$args['show_all']) { + $page_links[] = [ + 'class' => 'dots', + 'title' => \__('…'), + ]; + $dots = false; + } + } + } + + return $page_links; + } + + protected static function sanitize_url_params($add_args) + { + foreach ($add_args as $key => $value) { + $add_args[$key] = \urlencode_deep($value); + } + return $add_args; + } + + protected static function sanitize_args($args) + { + $format_args = []; + + $format = \explode('?', \str_replace('%_%', $args['format'], (string) $args['base'])); + $format_query = $format[1] ?? ''; + + \wp_parse_str($format_query, $format_args); + + // Remove the format argument from the array of query arguments, to avoid overwriting custom format. + foreach ($format_args as $format_arg => $format_arg_value) { + unset($args['add_args'][\urlencode_deep($format_arg)]); + } + + $url_parts = \explode('?', (string) $args['base']); + + if (isset($url_parts[1])) { + // Find the query args of the requested URL. + $url_query_args = []; + \wp_parse_str($url_parts[1], $url_query_args); + + $args['add_args'] = \array_merge($args['add_args'], \urlencode_deep($url_query_args)); + $args['base'] = $url_parts[0] . '%_%'; + } + + if (isset($args['add_args'])) { + $args['add_args'] = self::sanitize_url_params($args['add_args']); + } + return $args; + } +} diff --git a/vendor/timber/timber/src/PathHelper.php b/vendor/timber/timber/src/PathHelper.php new file mode 100644 index 0000000..80ef7a2 --- /dev/null +++ b/vendor/timber/timber/src/PathHelper.php @@ -0,0 +1,80 @@ + + *

    {{ post.title }}

    + *
    + * {{ post.content }} + *
    + * + * ``` + * + * ```html + *
    + *

    The Empire Strikes Back

    + *
    + * It is a dark time for the Rebellion. Although the Death Star has been + * destroyed, Imperial troops have driven the Rebel forces from their + * hidden base and pursued them across the galaxy. + *
    + *
    + * ``` + */ +class Post extends CoreEntity implements DatedInterface, Setupable, Stringable +{ + /** + * The underlying WordPress Core object. + * + * @since 2.0.0 + * + * @var WP_Post|null + */ + protected ?WP_Post $wp_object = null; + + /** + * @var string What does this class represent in WordPress terms? + */ + public $object_type = 'post'; + + /** + * @var string What does this class represent in WordPress terms? + */ + public static $representation = 'post'; + + /** + * @internal + * @var string stores the processed content internally + */ + protected $___content; + + /** + * @var string|boolean The returned permalink from WP's get_permalink function + */ + protected $_permalink; + + /** + * @var array Stores the results of the next Timber\Post in a set inside an array (in order to manage by-taxonomy) + */ + protected $_next = []; + + /** + * @var array Stores the results of the previous Timber\Post in a set inside an array (in order to manage by-taxonomy) + */ + protected $_prev = []; + + /** + * @var string Stores the CSS classes for the post (ex: "post post-type-book post-123") + */ + protected $_css_class; + + /** + * @api + * @var int The numeric WordPress id of a post. + */ + public $id; + + /** + * @api + * @var int The numeric WordPress id of a post, capitalized to match WordPress usage. + */ + public $ID; + + /** + * @api + * @var int The numeric ID of the a post's author corresponding to the wp_user database table + */ + public $post_author; + + /** + * @api + * @var string The raw text of a WP post as stored in the database + */ + public $post_content; + + /** + * @api + * @var string The raw date string as stored in the WP database, ex: 2014-07-05 18:01:39 + */ + public $post_date; + + /** + * @api + * @var string The raw text of a manual post excerpt as stored in the database + */ + public $post_excerpt; + + /** + * @api + * @var int The numeric ID of a post's parent post + */ + public $post_parent; + + /** + * @api + * @var string The status of a post ("draft", "publish", etc.) + */ + public $post_status; + + /** + * @api + * @var string The raw text of a post's title as stored in the database + */ + public $post_title; + + /** + * @api + * @var string The name of the post type, this is the machine name (so "my_custom_post_type" as + * opposed to "My Custom Post Type") + */ + public $post_type; + + /** + * @api + * @var string The URL-safe slug, this corresponds to the poorly-named "post_name" in the WP + * database, ex: "hello-world" + */ + public $slug; + + /** + * @var string Stores the PostType object for the post. + */ + protected $__type; + + /** + * Create and initialize a new instance of the called Post class + * (i.e. Timber\Post or a subclass). + * + * @internal + * @return Post + */ + public static function build(WP_Post $wp_post): static + { + $post = new static(); + + $post->id = $wp_post->ID; + $post->ID = $wp_post->ID; + $post->wp_object = $wp_post; + + $data = \get_object_vars($wp_post); + $data = $post->get_info($data); + + /** + * Filters the imported post data. + * + * Used internally for previews. + * + * @since 2.0.0 + * @see Timber::init() + * @param array $data An array of post data to import. + * @param Post $post The Timber post instance. + */ + $data = \apply_filters('timber/post/import_data', $data, $post); + + $post->import($data); + + return $post; + } + + /** + * If you send the constructor nothing it will try to figure out the current post id based on + * being inside The_Loop. + * + * @internal + */ + protected function __construct() + { + } + + /** + * This is helpful for twig to return properties and methods see: + * https://github.com/fabpot/Twig/issues/2 + * + * This is also here to ensure that {{ post.class }} remains usable. + * + * @api + * + * @return mixed + */ + public function __get($field) + { + if ('class' === $field) { + return $this->css_class(); + } + + if ('_thumbnail_id' === $field) { + Helper::doing_it_wrong( + "Accessing the thumbnail ID through {{ {$this->object_type}._thumbnail_id }}", + "You can retrieve the thumbnail ID via the thumbnail object {{ {$this->object_type}.thumbnail.id }}. If you need the id as stored on this post's postmeta you can use {{ {$this->object_type}.meta('_thumbnail_id') }}", + '2.0.0' + ); + } + + return parent::__get($field); + } + + /** + * This is helpful for twig to return properties and methods see: + * https://github.com/fabpot/Twig/issues/2 + * + * This is also here to ensure that {{ post.class }} remains usable + * + * @api + * + * @return mixed + */ + public function __call($field, $args) + { + if ('class' === $field) { + $class = $args[0] ?? ''; + return $this->css_class($class); + } + + return parent::__call($field, $args); + } + + /** + * Gets the underlying WordPress Core object. + * + * @since 2.0.0 + * + * @return WP_Post|null + */ + public function wp_object(): ?WP_Post + { + return $this->wp_object; + } + + /** + * Sets up a post. + * + * Sets up the `$post` global, and other global variables as well as variables in the + * `$wp_query` global that makes Timber more compatible with WordPress. + * + * This function will be called automatically when you loop over Timber posts as well as in + * `Timber::context()`. + * + * @api + * @since 2.0.0 + * + * @return Post The post instance. + */ + public function setup() + { + global $post; + global $wp_query; + + // Mimic WordPress behavior to improve compatibility with third party plugins. + $wp_query->in_the_loop = true; + + if (!$this->wp_object) { + return $this; + } + + /** + * Maybe set or overwrite post global. + * + * We have to overwrite the post global to be compatible with a couple of WordPress plugins + * that work with the post global in certain conditions. + */ + // phpcs:ignore WordPress.WP.GlobalVariablesOverride.OverrideProhibited + if (!$post || isset($post->ID) && $post->ID !== $this->ID) { + $post = $this->wp_object; + } + + // The setup_postdata() function will call the 'the_post' action. + $wp_query->setup_postdata($this->wp_object); + + return $this; + } + + /** + * Resets variables after post has been used. + * + * This function will be called automatically when you loop over Timber posts. + * + * @api + * @since 2.0.0 + * + * @return Post The post instance. + */ + public function teardown() + { + global $wp_query; + + $wp_query->in_the_loop = false; + + return $this; + } + + /** + * Determine whether or not an admin/editor is looking at the post in "preview mode" via the + * WordPress admin + * @internal + * @return bool + */ + protected static function is_previewing() + { + global $wp_query; + return isset($_GET['preview']) && isset($_GET['preview_nonce']) && \wp_verify_nonce($_GET['preview_nonce'], 'post_preview_' . $wp_query->queried_object_id); + } + + /** + * Outputs the title of the post if you do something like `

    {{post}}

    ` + * + * @api + * @return string + */ + public function __toString() + { + return $this->title(); + } + + protected function get_post_preview_object() + { + global $wp_query; + if (static::is_previewing()) { + $revision_id = $this->get_post_preview_id($wp_query); + return Timber::get_post($revision_id); + } + } + + protected function get_post_preview_id($query) + { + $can = [ + \get_post_type_object($query->queried_object->post_type)->cap->edit_post, + ]; + + if ($query->queried_object->author_id !== \get_current_user_id()) { + $can[] = \get_post_type_object($query->queried_object->post_type)->cap->edit_others_posts; + } + + $can_preview = []; + + foreach ($can as $type) { + if (\current_user_can($type, $query->queried_object_id)) { + $can_preview[] = true; + } + } + + if (\count($can_preview) !== \count($can)) { + return; + } + + $revisions = \wp_get_post_revisions($query->queried_object_id); + + if (!empty($revisions)) { + $revision = \reset($revisions); + return $revision->ID; + } + + return false; + } + + /** + * Updates post_meta of the current object with the given value. + * + * @deprecated 2.0.0 Use `update_post_meta()` instead. + * + * @param string $field The key of the meta field to update. + * @param mixed $value The new value. + */ + public function update($field, $value) + { + Helper::deprecated('Timber\Post::update()', 'update_post_meta()', '2.0.0'); + + if (isset($this->ID)) { + \update_post_meta($this->ID, $field, $value); + $this->$field = $value; + } + } + + /** + * Gets a excerpt of your post. + * + * If you have an excerpt is set on the post, the excerpt will be used. Otherwise it will try to + * pull from an excerpt from `post_content`. If there’s a `` tag in the post + * content, it will use that to mark where to pull through. + * + * @api + * @see PostExcerpt + * + * @param array $options { + * An array of configuration options for generating the excerpt. Default empty. + * + * @type int $words Number of words in the excerpt. Default `50`. + * @type int|bool $chars Number of characters in the excerpt. Default `false` (no + * character limit). + * @type string $end String to append to the end of the excerpt. Default '…' + * (HTML ellipsis character). + * @type bool $force Whether to shorten the excerpt to the length/word count + * specified, if the editor wrote a manual excerpt longer than the + * set length. Default `false`. + * @type bool $strip Whether to strip HTML tags. Default `true`. + * @type string $read_more String for what the "Read More" text should be. Default + * 'Read More'. + * } + * @example + * ```twig + *

    {{ post.title }}

    + *
    {{ post.excerpt({ words: 100, read_more: 'Keep reading' }) }}
    + * ``` + * @return PostExcerpt + */ + public function excerpt(array $options = []) + { + return new PostExcerpt($this, $options); + } + + /** + * Gets an excerpt of your post. + * + * If you have an excerpt is set on the post, the excerpt will be used. Otherwise it will try to + * pull from an excerpt from `post_content`. If there’s a `` tag in the post + * content, it will use that to mark where to pull through. + * + * This method returns a `Timber\PostExcerpt` object, which is a **chainable object**. This + * means that you can change the output of the excerpt by **adding more methods**. Refer to the + * [documentation of the `Timber\PostExcerpt` class](https://timber.github.io/docs/v2/reference/timber-postexcerpt/) + * to get an overview of all the available methods. + * + * @api + * @deprecated 2.0.0, use `{{ post.excerpt }}` instead. + * @see PostExcerpt + * @example + * ```twig + * {# Use default excerpt #} + *

    {{ post.excerpt }}

    + * + * {# Change the post excerpt text #} + *

    {{ post.excerpt.read_more('Continue Reading') }}

    + * + * {# Additionally restrict the length to 50 words #} + *

    {{ post.excerpt.length(50).read_more('Continue Reading') }}

    + * ``` + * @return PostExcerpt + */ + public function preview() + { + Helper::deprecated('{{ post.preview }}', '{{ post.excerpt }}', '2.0.0'); + return new PostExcerpt($this); + } + + /** + * Gets the link to a page number. + * + * @internal + * @param int $i + * @return string|null Link to page number or `null` if link could not be read. + */ + protected static function get_wp_link_page($i) + { + $link = \_wp_link_page($i); + $link = new SimpleXMLElement($link . ''); + + return $link['href'] ?? null; + } + + /** + * Gets info to import on Timber post object. + * + * Used internally by init, etc. to build Timber\Post object. + * + * @internal + * + * @param array $data Data to update. + * @return array + */ + protected function get_info(array $data): array + { + $data = \array_merge($data, [ + 'slug' => $this->wp_object->post_name, + 'status' => $this->wp_object->post_status, + ]); + + return $data; + } + + /** + * Gets the comment form for use on a single article page + * + * @api + * @param array $args see [WordPress docs on comment_form](https://codex.wordpress.org/Function_Reference/comment_form) + * for reference on acceptable parameters + * @return string of HTML for the form + */ + public function comment_form($args = []) + { + return \trim(Helper::ob_function('comment_form', [$args, $this->ID])); + } + + /** + * Gets the terms associated with the post. + * + * @api + * @example + * ```twig + *
    + * {% if jobs is not empty %} + * {% for post in jobs %} + *
    + *

    {{ post.title }}

    + *

    {{ post.terms({ + * taxonomy: 'category', + * orderby: 'name', + * order: 'ASC' + * })|join(', ') }}

    + *
    + * {% endfor %} + * {% endif %} + *
    + * ``` + * ```html + *
    + *
    + *

    Cheese Maker

    + *

    Cheese, Food, Fromage

    + *
    + *
    + *

    Mime

    + *

    Performance, Silence

    + *
    + *
    + * ``` + * ```php + * // Get all terms of a taxonomy. + * $terms = $post->terms( 'category' ); + * + * // Get terms of multiple taxonomies. + * $terms = $post->terms( array( 'books', 'movies' ) ); + * + * // Use custom arguments for taxonomy query and options. + * $terms = $post->terms( [ + * 'taxonomy' => 'custom_tax', + * 'orderby' => 'count' + * ], [ + * 'merge' => false + * ] ); + * ``` + * + * @param string|array $query_args Any array of term query parameters for getting the terms. + * See `WP_Term_Query::__construct()` for supported arguments. + * Use the `taxonomy` argument to choose which taxonomies to + * get. Defaults to querying all registered taxonomies for the + * post type. You can use custom or built-in WordPress + * taxonomies (category, tag). Timber plays nice and figures + * out that `tag`, `tags` or `post_tag` are all the same + * (also for `categories` or `category`). For custom + * taxonomies you need to define the proper name. + * @param array $options { + * Optional. An array of options for the function. + * + * @type bool $merge Whether the resulting array should be one big one (`true`) or whether + * it should be an array of sub-arrays for each taxonomy (`false`). + * Default `true`. + * } + * @return array An array of taxonomies. + */ + public function terms($query_args = [], $options = []) + { + // Make it possible to use a taxonomy or an array of taxonomies as a shorthand. + if (!\is_array($query_args) || isset($query_args[0])) { + $query_args = [ + 'taxonomy' => $query_args, + ]; + } + + /** + * Handles backwards compatibility for users who use an array with a query property. + * + * @deprecated 2.0.0 use Post::terms( $query_args, $options ) + */ + if (\is_array($query_args) && isset($query_args['query'])) { + if (isset($query_args['merge']) && !isset($options['merge'])) { + $options['merge'] = $query_args['merge']; + } + $query_args = $query_args['query']; + } + + // Defaults. + $query_args = \wp_parse_args($query_args, [ + 'taxonomy' => 'all', + ]); + + $options = \wp_parse_args($options, [ + 'merge' => true, + ]); + + $taxonomies = $query_args['taxonomy']; + $merge = $options['merge']; + + if (\in_array($taxonomies, ['all', 'any', ''])) { + $taxonomies = \get_object_taxonomies($this->post_type); + } + + if (!\is_array($taxonomies)) { + $taxonomies = [$taxonomies]; + } + + $query = \array_merge($query_args, [ + 'object_ids' => [$this->ID], + 'taxonomy' => $taxonomies, + ]); + + if (!$merge) { + // get results segmented out per taxonomy + $queries = $this->partition_tax_queries($query, $taxonomies); + $termGroups = Timber::get_terms($queries); + + // zip 'em up with the right keys + return \array_combine($taxonomies, $termGroups); + } + + return Timber::get_terms($query, $options); + } + + /** + * @api + * @param string|int $term_name_or_id + * @param string $taxonomy + * @return bool + */ + public function has_term($term_name_or_id, $taxonomy = 'all') + { + if ($taxonomy == 'all' || $taxonomy == 'any') { + $taxes = \get_object_taxonomies($this->post_type, 'names'); + $ret = false; + foreach ($taxes as $tax) { + if (\has_term($term_name_or_id, $tax, $this->ID)) { + $ret = true; + break; + } + } + return $ret; + } + return \has_term($term_name_or_id, $taxonomy, $this->ID); + } + + /** + * Gets the number of comments on a post. + * + * @api + * @return int The number of comments on a post + */ + public function comment_count(): int + { + return (int) \get_comments_number($this->ID); + } + + /** + * @api + * @param string $field_name + * @return boolean + */ + public function has_field($field_name) + { + return (!$this->meta($field_name)) ? false : true; + } + + /** + * Gets the field object data from Advanced Custom Fields. + * This includes metadata on the field like whether it's conditional or not. + * + * @api + * @since 1.6.0 + * @param string $field_name of the field you want to lookup. + * @return mixed + */ + public function field_object($field_name) + { + /** + * Filters field object data from Advanced Custom Fields. + * + * This filter is used by the ACF Integration. + * + * @see \Timber\Post::field_object() + * @since 1.6.0 + * + * @param mixed $value The value. + * @param int|null $post_id The post ID. + * @param string $field_name The ACF field name. + * @param Post $post The post object. + */ + $value = \apply_filters('timber/post/meta_object_field', null, $this->ID, $field_name, $this); + $value = $this->convert($value); + return $value; + } + + /** + * @inheritDoc + */ + protected function fetch_meta($field_name = '', $args = [], $apply_filters = true) + { + $revised_data = $this->get_revised_data_from_method('meta', $field_name); + + if ($revised_data) { + return $revised_data; + } + + return parent::fetch_meta($field_name, $args, $apply_filters); + } + + /** + * Gets a post meta value. + * + * @api + * @deprecated 2.0.0, use `{{ post.meta('field_name') }}` instead. + * @see \Timber\Post::meta() + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_field($field_name = null) + { + Helper::deprecated( + "{{ post.get_field('field_name') }}", + "{{ post.meta('field_name') }}", + '2.0.0' + ); + + if ($field_name === null) { + // On the off-chance the field is actually named meta. + $field_name = 'meta'; + } + + return $this->meta($field_name); + } + + /** + * Import field data onto this object + * + * @api + * @deprecated since 2.0.0 + * @param string $field_name + */ + public function import_field($field_name) + { + Helper::deprecated( + "Importing field data onto an object", + "{{ post.meta('field_name') }}", + '2.0.0' + ); + + $this->$field_name = $this->meta($field_name); + } + + /** + * Get the CSS classes for a post without cache. + * For usage you should use `{{post.class}}` + * + * @internal + * @param string $class additional classes you want to add. + * @example + * ```twig + *
    + * {# Some stuff here #} + *
    + * ``` + * + * ```html + *
    + * {# Some stuff here #} + *
    + * ``` + * @return string a space-separated list of classes + */ + public function post_class($class = '') + { + global $post; + $old_global_post = $post; + $post = $this; + + $class_array = \get_post_class($class, $this->ID); + if (static::is_previewing()) { + $class_array = \get_post_class($class, $this->post_parent); + } + $class_array = \implode(' ', $class_array); + + $post = $old_global_post; + return $class_array; + } + + /** + * Get the CSS classes for a post, but with caching css post classes. For usage you should use `{{ post.class }}` instead of `{{post.css_class}}` or `{{post.post_class}}` + * + * @internal + * @param string $class additional classes you want to add. + * @see \Timber\Post::$_css_class + * @example + * ```twig + *
    + * {# Some stuff here #} + *
    + * ``` + * + * @return string a space-separated list of classes + */ + public function css_class($class = '') + { + if (!$this->_css_class) { + $this->_css_class = $this->post_class(); + } + + return \trim(\sprintf('%s %s', $this->_css_class, $class)); + } + + /** + * @return array + * @codeCoverageIgnore + */ + public function get_method_values(): array + { + $ret['author'] = $this->author(); + $ret['categories'] = $this->categories(); + $ret['category'] = $this->category(); + $ret['children'] = $this->children(); + $ret['comments'] = $this->comments(); + $ret['content'] = $this->content(); + $ret['edit_link'] = $this->edit_link(); + $ret['format'] = $this->format(); + $ret['link'] = $this->link(); + $ret['next'] = $this->next(); + $ret['pagination'] = $this->pagination(); + $ret['parent'] = $this->parent(); + $ret['path'] = $this->path(); + $ret['prev'] = $this->prev(); + $ret['terms'] = $this->terms(); + $ret['tags'] = $this->tags(); + $ret['thumbnail'] = $this->thumbnail(); + $ret['title'] = $this->title(); + return $ret; + } + + /** + * Return the author of a post + * + * @api + * @example + * ```twig + *

    {{post.title}}

    + * + * ``` + * @return User|null A User object if found, false if not + */ + public function author() + { + if (isset($this->post_author)) { + $factory = new UserFactory(); + return $factory->from((int) $this->post_author); + } + } + + /** + * Got more than one author? That's cool, but you'll need Co-Authors plus or another plugin to access any data + * + * @api + * @return array + */ + public function authors() + { + /** + * Filters authors for a post. + * + * This filter is used by the CoAuthorsPlus integration. + * + * @example + * ``` + * add_filter( 'timber/post/authors', function( $author, $post ) { + * foreach ($cauthors as $author) { + * // do something with $author + * } + * + * return $authors; + * } ); + * ``` + * + * @see \Timber\Post::authors() + * @since 1.1.4 + * + * @param array $authors An array of User objects. Default: User object for `post_author`. + * @param Post $post The post object. + */ + return \apply_filters('timber/post/authors', [$this->author()], $this); + } + + /** + * Get the author (WordPress user) who last modified the post + * + * @api + * @example + * ```twig + * Last updated by {{ post.modified_author.name }} + * ``` + * ```html + * Last updated by Harper Lee + * ``` + * @return User|null A User object if found, false if not + */ + public function modified_author() + { + $user_id = \get_post_meta($this->ID, '_edit_last', true); + return ($user_id ? Timber::get_user($user_id) : $this->author()); + } + + /** + * Get the categories on a particular post + * + * @api + * @return array of Timber\Term objects + */ + public function categories() + { + return $this->terms('category'); + } + + /** + * Gets a category attached to a post. + * + * If multiple categories are set, it will return just the first one. + * + * @api + * @return Term|null + */ + public function category() + { + $cats = $this->categories(); + if (\count($cats) && isset($cats[0])) { + return $cats[0]; + } + + return null; + } + + /** + * Returns an array of children on the post as Timber\Posts + * (or other claass as you define). + * + * @api + * @example + * ```twig + * {% if post.children is not empty %} + * Here are the child pages: + * {% for child in post.children %} + * {{ child.title }} + * {% endfor %} + * {% endif %} + * ``` + * @param string|array $args _optional_ An array of arguments for the `get_children` function or a string/non-indexed array to use as the post type(s). + * @return PostCollectionInterface + */ + public function children($args = 'any') + { + if (\is_string($args) || \array_values($args) === $args) { + $args = [ + 'post_type' => 'parent' === $args ? $this->post_type : $args, + ]; + } + + $args = \wp_parse_args($args, [ + 'post_parent' => $this->ID, + 'post_type' => 'any', + 'posts_per_page' => -1, + 'orderby' => 'menu_order title', + 'order' => 'ASC', + 'post_status' => 'publish' === $this->post_status ? ['publish', 'inherit'] : 'publish', + ]); + + /** + * Filters the arguments for the query used to get the children of a post. + * + * This filter is used by the `Timber\Post::children()` method. It allows you to modify the + * arguments for the `get_children` function. This way you can change the query to get the + * children of a post. + * + * @example + * ``` + * add_filter( 'timber/post/children_args', function( $args, $post ) { + * + * if ( $post->post_type === 'custom_post_type' ) { + * $args['post_status'] = 'private'; + * } + * + * return $args; + * } ); + * ``` + * + * @see \Timber\Post::children() + * @since 2.1.0 + * + * @param array $arguments An array of arguments for the `get_children` function. + * @param Post $post The post object. + */ + $args = \apply_filters('timber/post/children_args', $args, $this); + + return $this->factory()->from(\get_children($args)); + } + + /** + * Gets the comments on a Timber\Post and returns them as an array of `Timber\Comment` objects (or whatever comment class you set). + * + * @api + * Gets the comments on a `Timber\Post` and returns them as a `Timber\CommentThread`: a PHP + * ArrayObject of [`Timber\Comment`](https://timber.github.io/docs/v2/reference/timber-comment/) + * (or whatever comment class you set). + * @api + * + * @param int $count Set the number of comments you want to get. `0` is analogous to + * "all". + * @param string $order Use ordering set in WordPress admin, or a different scheme. + * @param string $type For when other plugins use the comments table for their own + * special purposes. Might be set to 'liveblog' or other, depending + * on what’s stored in your comments table. + * @param string $status Could be 'pending', etc. + * @see CommentThread for an example with nested comments + * @return bool|CommentThread + * + * @example + * + * **single.twig** + * + * ```twig + *
    + *

    Comments on {{ post.title }}

    + *
      + * {% for comment in post.comments() %} + * {% include 'comment.twig' %} + * {% endfor %} + *
    + *
    + * {{ function('comment_form') }} + *
    + *
    + * ``` + * + * **comment.twig** + * + * ```twig + * {# comment.twig #} + *
  • + *

    {{ comment.author.name }} says:

    + *
    {{ comment.content }}
    + *
  • + * ``` + */ + public function comments($count = null, $order = 'wp', $type = 'comment', $status = 'approve') + { + global $overridden_cpage, $user_ID; + $overridden_cpage = false; + + $commenter = \wp_get_current_commenter(); + $comment_author_email = $commenter['comment_author_email']; + + $args = [ + 'status' => $status, + 'order' => $order, + 'type' => $type, + ]; + if ($count > 0) { + $args['number'] = $count; + } + if (\strtolower($order) == 'wp' || \strtolower($order) == 'wordpress') { + $args['order'] = \get_option('comment_order'); + } + if ($user_ID) { + $args['include_unapproved'] = [$user_ID]; + } elseif (!empty($comment_author_email)) { + $args['include_unapproved'] = [$comment_author_email]; + } elseif (\function_exists('wp_get_unapproved_comment_author_email')) { + $unapproved_email = \wp_get_unapproved_comment_author_email(); + if ($unapproved_email) { + $args['include_unapproved'] = [$unapproved_email]; + } + } + $ct = new CommentThread($this->ID, false); + $ct->init($args); + return $ct; + } + + /** + * If the Password form is to be shown, show it! + * @return string|void + */ + protected function maybe_show_password_form() + { + if ($this->password_required()) { + $show_pw = false; + + /** + * Filters whether the password form should be shown for password protected posts. + * + * This filter runs only when you call `{{ post.content }}` for a password protected + * post. When this filter returns `true`, a password form will be shown instead of the + * post content. If you want to modify the form itself, you can use the + * `timber/post/content/password_form` filter. + * + * @since 1.1.4 + * @example + * ```php + * // Always show password form for password protected posts. + * add_filter( 'timber/post/content/show_password_form_for_protected', '__return_true' ); + * ``` + * + * @param bool $show_pw Whether the password form should be shown. Default `false`. + */ + $show_pw = \apply_filters('timber/post/content/show_password_form_for_protected', $show_pw); + + if ($show_pw) { + /** + * Filters the password form output. + * + * As an alternative to this filter, you could also use WordPress’s `the_password_form` filter. + * The difference to this filter is, that you’ll also have the post object available as a second + * parameter, in case you need that. + * + * @since 1.1.4 + * + * @example + * ```php + * // Modify the password form. + * add_filter( 'timber/post/content/password_form', function( $form, $post ) { + * return Timber::compile( 'assets/password-form.twig', array( 'post' => $post ) ); + * }, 10, 2 ); + * ``` + * + * @param string $form Form output. Default WordPress password form output generated by `get_the_password_form()`. + * @param Post $post The post object. + */ + return \apply_filters('timber/post/content/password_form', \get_the_password_form($this->ID), $this); + } + } + } + + /** + * + */ + protected function get_revised_data_from_method($method, $args = false) + { + if (!\is_array($args)) { + $args = [$args]; + } + $rev = $this->get_post_preview_object(); + if ($rev && $this->ID == $rev->post_parent && $this->ID != $rev->ID) { + return \call_user_func_array([$rev, $method], $args); + } + } + + /** + * Gets the actual content of a WordPress post. + * + * As opposed to using `{{ post.post_content }}`, this will run the hooks/filters attached to + * the `the_content` filter. It will return your post’s content with WordPress filters run on it + * – which means it will parse blocks, convert shortcodes or run `wpautop()` on the content. + * + * If you use page breaks in your content to split your post content into multiple pages, + * use `{{ post.paged_content }}` to display only the content for the current page. + * + * @api + * @example + * ```twig + *
    + *

    {{ post.title }}

    + * + *
    {{ post.content }}
    + *
    + * ``` + * + * @param int $page Optional. The page to show if the content of the post is split into multiple + * pages. Read more about this in the [Pagination Guide](https://timber.github.io/docs/v2/guides/pagination/#paged-content-within-a-post). Default `0`. + * @param int $len Optional. The number of words to show. Default `-1` (show all). + * @param bool $remove_blocks Optional. Whether to remove blocks. Defaults to false. True when called from the $post->excerpt() method. + * @return string The content of the post. + */ + public function content($page = 0, $len = -1, $remove_blocks = false) + { + if ($rd = $this->get_revised_data_from_method('content', [$page, $len])) { + return $rd; + } + if ($form = $this->maybe_show_password_form()) { + return $form; + } + if ($len == -1 && $page == 0 && $this->___content) { + return $this->___content; + } + + $content = $this->post_content; + + if ($len > 0) { + $content = \wp_trim_words($content, $len); + } + + /** + * Page content split by . + * + * @see WP_Query::generate_postdata() + */ + if ($page && \str_contains((string) $content, '')) { + $content = \str_replace("\n\n", '', (string) $content); + $content = \str_replace("\n", '', $content); + $content = \str_replace("\n", '', $content); + + // Remove the nextpage block delimiters, to avoid invalid block structures in the split content. + $content = \str_replace('', '', $content); + $content = \str_replace('', '', $content); + + // Ignore nextpage at the beginning of the content. + if (\str_starts_with($content, '')) { + $content = \substr($content, 15); + } + + $pages = \explode('', $content); + $page--; + + if (\count($pages) > $page) { + $content = $pages[$page]; + } + } + + /** + * Filters whether the content produced by block editor blocks should be removed or not from the content. + * + * If truthy then block whose content does not belong in the excerpt, will be removed. + * This removal is done using WordPress Core `excerpt_remove_blocks` function. + * + * @since 2.1.1 + * + * @param bool $remove_blocks Whether blocks whose content should not be part of the excerpt should be removed + * or not from the excerpt. + * + * @see excerpt_remove_blocks() The WordPress Core function that will handle the block removal from the excerpt. + */ + $remove_blocks = (bool) \apply_filters('timber/post/content/remove_blocks', $remove_blocks); + + if ($remove_blocks) { + $content = \excerpt_remove_blocks($content); + } + + $content = $this->content_handle_no_teaser_block($content); + $content = \apply_filters('the_content', ($content)); + + if ($len == -1 && $page == 0) { + $this->___content = $content; + } + + return $content; + } + + /** + * Handles for an circumstance with the Block editor where a "more" block has an option to + * "Hide the excerpt on the full content page" which hides everything prior to the inserted + * "more" block + * @ticket #2218 + * @param string $content + * @return string + */ + protected function content_handle_no_teaser_block($content) + { + if ((\str_contains($content, 'noTeaser:true') || \str_contains($content, '"noTeaser":true')) && \str_contains($content, '')) { + $arr = \explode('', $content); + return \trim($arr[1]); + } + return $content; + } + + /** + * Gets the paged content for a post. + * + * You will use this, if you use `` in your post content or the Page Break block + * in the Block Editor. Use `{{ post.pagination }}` to create a pagination for your paged + * content. Learn more about this in the [Pagination Guide](https://timber.github.io/docs/v2/guides/pagination/#paged-content-within-a-post). + * + * @example + * ```twig + * {{ post.paged_content }} + * ``` + * + * @return string The content for the current page. If there’s no page break found in the + * content, the whole content is returned. + */ + public function paged_content() + { + global $page; + return $this->content($page, -1); + } + + /** + * Gets the timestamp when the post was published. + * + * @api + * @since 2.0.0 + * + * @return false|int Unix timestamp on success, false on failure. + */ + public function timestamp() + { + return \get_post_timestamp($this->ID); + } + + /** + * Gets the timestamp when the post was last modified. + * + * @api + * @since 2.0.0 + * + * @return false|int Unix timestamp on success, false on failure. + */ + public function modified_timestamp() + { + return \get_post_timestamp($this->ID, 'modified'); + } + + /** + * Gets the publishing date of the post. + * + * This function will also apply the + * [`get_the_date`](https://developer.wordpress.org/reference/hooks/get_the_date/) filter to the + * output. + * + * If you use {{ post.date }} with the |time_ago filter, then make sure that you use a time + * format including the full time and not just the date. + * + * @api + * @example + * ```twig + * {# Uses date format set in Settings → General #} + * Published on {{ post.date }} + * OR + * Published on {{ post.date('F jS') }} + * which was + * {{ post.date('U')|time_ago }} + * {{ post.date('Y-m-d H:i:s')|time_ago }} + * {{ post.date(constant('DATE_ATOM'))|time_ago }} + * ``` + * + * ```html + * Published on January 12, 2015 + * OR + * Published on Jan 12th + * which was + * 8 years ago + * ``` + * + * @param string|null $date_format Optional. PHP date format. Will use the `date_format` option + * as a default. + * + * @return string + */ + public function date($date_format = null) + { + $format = $date_format ?: \get_option('date_format'); + $date = \wp_date($format, $this->timestamp()); + + /** + * Filters the date a post was published. + * + * @see get_the_date() + * + * @param string $date The formatted date. + * @param string $date_format PHP date format. Defaults to 'date_format' option if not + * specified. + * @param int|WP_Post $id The post object or ID. + */ + $date = \apply_filters('get_the_date', $date, $date_format, $this->ID); + + return $date; + } + + /** + * Gets the date the post was last modified. + * + * This function will also apply the + * [`get_the_modified_date`](https://developer.wordpress.org/reference/hooks/get_the_modified_date/) + * filter to the output. + * + * @api + * @example + * ```twig + * {# Uses date format set in Settings → General #} + * Last modified on {{ post.modified_date }} + * OR + * Last modified on {{ post.modified_date('F jS') }} + * ``` + * + * ```html + * Last modified on January 12, 2015 + * OR + * Last modified on Jan 12th + * ``` + * + * @param string|null $date_format Optional. PHP date format. Will use the `date_format` option + * as a default. + * + * @return string + */ + public function modified_date($date_format = null) + { + $format = $date_format ?: \get_option('date_format'); + $date = \wp_date($format, $this->modified_timestamp()); + + /** + * Filters the date a post was last modified. + * + * This filter expects a `WP_Post` object as the last parameter. We only have a + * `Timber\Post` object available, that wouldn’t match the expected argument. That’s why we + * need to get the post object with get_post(). This is fairly inexpensive, because the post + * will already be in the cache. + * + * @see get_the_modified_date() + * + * @param string|bool $date The formatted date or false if no post is found. + * @param string $date_format PHP date format. Defaults to value specified in + * 'date_format' option. + * @param WP_Post|null $post WP_Post object or null if no post is found. + */ + $date = \apply_filters('get_the_modified_date', $date, $date_format, \get_post($this->ID)); + + return $date; + } + + /** + * Gets the time the post was published to use in your template. + * + * This function will also apply the + * [`get_the_time`](https://developer.wordpress.org/reference/hooks/get_the_time/) filter to the + * output. + * + * @api + * @example + * ```twig + * {# Uses time format set in Settings → General #} + * Published at {{ post.time }} + * OR + * Published at {{ post.time('G:i') }} + * ``` + * + * ```html + * Published at 1:25 pm + * OR + * Published at 13:25 + * ``` + * + * @param string|null $time_format Optional. PHP date format. Will use the `time_format` option + * as a default. + * + * @return string + */ + public function time($time_format = null) + { + $format = $time_format ?: \get_option('time_format'); + $time = \wp_date($format, $this->timestamp()); + + /** + * Filters the time a post was written. + * + * @see get_the_time() + * + * @param string $time The formatted time. + * @param string $time_format Format to use for retrieving the time the post was + * written. Accepts 'G', 'U', or php date format value + * specified in `time_format` option. Default empty. + * @param int|WP_Post $id WP_Post object or ID. + */ + $time = \apply_filters('get_the_time', $time, $time_format, $this->ID); + + return $time; + } + + /** + * Gets the time of the last modification of the post to use in your template. + * + * This function will also apply the + * [`get_the_time`](https://developer.wordpress.org/reference/hooks/get_the_modified_time/) + * filter to the output. + * + * @api + * @example + * ```twig + * {# Uses time format set in Settings → General #} + * Published at {{ post.modified_time }} + * OR + * Published at {{ post.modified_time('G:i') }} + * ``` + * + * ```html + * Published at 1:25 pm + * OR + * Published at 13:25 + * ``` + * + * @param string|null $time_format Optional. PHP date format. Will use the `time_format` option + * as a default. + * + * @return string + */ + public function modified_time($time_format = null) + { + $format = $time_format ?: \get_option('time_format'); + $time = \wp_date($format, $this->modified_timestamp()); + + /** + * Filters the localized time a post was last modified. + * + * This filter expects a `WP_Post` object as the last parameter. We only have a + * `Timber\Post` object available, that wouldn’t match the expected argument. That’s why we + * need to get the post object with get_post(). This is fairly inexpensive, because the post + * will already be in the cache. + * + * @see get_the_modified_time() + * + * @param string|bool $time The formatted time or false if no post is found. + * @param string $time_format Format to use for retrieving the time the post was + * written. Accepts 'G', 'U', or php date format. Defaults + * to value specified in 'time_format' option. + * @param WP_Post|null $post WP_Post object or null if no post is found. + */ + $time = \apply_filters('get_the_modified_time', $time, $time_format, \get_post($this->ID)); + + return $time; + } + + /** + * Returns the PostType object for a post’s post type with labels and other info. + * + * @api + * @since 1.0.4 + * @example + * ```twig + * This post is from {{ post.type.labels.name }} + * ``` + * + * ```html + * This post is from Recipes + * ``` + * @return PostType + */ + public function type() + { + if (!$this->__type instanceof PostType) { + $this->__type = new PostType($this->post_type); + } + return $this->__type; + } + + /** + * Checks whether the current user can edit the post. + * + * @api + * @example + * ```twig + * {% if post.can_edit %} + * Edit + * {% endif %} + * ``` + * @return bool + */ + public function can_edit(): bool + { + return \current_user_can('edit_post', $this->ID); + } + + /** + * Gets the edit link for a post if the current user has the correct rights. + * + * @api + * @example + * ```twig + * {% if post.can_edit %} + * Edit + * {% endif %} + * ``` + * @return string|null The edit URL of a post in the WordPress admin or null if the current user can’t edit the + * post. + */ + public function edit_link(): ?string + { + if (!$this->can_edit()) { + return null; + } + + return \get_edit_post_link($this->ID); + } + + /** + * @api + * @return mixed + */ + public function format() + { + return \get_post_format($this->ID); + } + + /** + * whether post requires password and correct password has been provided + * @api + * @return boolean + */ + public function password_required() + { + return \post_password_required($this->ID); + } + + /** + * get the permalink for a post object + * @api + * @example + * ```twig + * Read my post + * ``` + * @return string ex: https://example.org/2015/07/my-awesome-post + */ + public function link() + { + if (isset($this->_permalink)) { + return $this->_permalink; + } + $this->_permalink = \get_permalink($this->ID); + return $this->_permalink; + } + + /** + * @api + * @return string + */ + public function name() + { + return $this->title(); + } + + /** + * Gets the next post that is adjacent to the current post in a collection. + * + * Works pretty much the same as + * [`get_next_post()`](https://developer.wordpress.org/reference/functions/get_next_post/). + * + * @api + * @example + * ```twig + * {% if post.next %} + * {{ post.next.title }} + * {% endif %} + * ``` + * @param bool|string $in_same_term Whether the post should be in a same taxonomy term. Default + * `false`. + * + * @return mixed + */ + public function next($in_same_term = false) + { + if (!isset($this->_next) || !isset($this->_next[$in_same_term])) { + global $post; + $this->_next = []; + $old_global = $post; + $post = $this; + if (\is_string($in_same_term) && \strlen($in_same_term)) { + $adjacent = \get_adjacent_post(true, '', false, $in_same_term); + } else { + $adjacent = \get_adjacent_post(false, '', false); + } + + if ($adjacent) { + $this->_next[$in_same_term] = $this->factory()->from($adjacent); + } else { + $this->_next[$in_same_term] = false; + } + $post = $old_global; + } + return $this->_next[$in_same_term]; + } + + /** + * Gets a data array to display a pagination for your paginated post. + * + * Use this in combination with `{{ post.paged_content }}`. + * + * @api + * @example + * Using simple links to the next an previous page. + * ```twig + * {% if post.pagination.next is not empty %} + * Go to next page + * {% endif %} + * + * {% if post.pagination.prev is not empty %} + * Go to previous page + * {% endif %} + * ``` + * Using a pagination for all pages. + * ```twig + * {% if post.pagination.pages is not empty %} + * + * {% endif %} + * ``` + * + * @return array An array with data to build your paginated content. + */ + public function pagination() + { + global $post, $page, $numpages, $multipage; + $post = $this; + $ret = []; + if ($multipage) { + for ($i = 1; $i <= $numpages; $i++) { + $link = self::get_wp_link_page($i); + $data = [ + 'name' => $i, + 'title' => $i, + 'text' => $i, + 'link' => $link, + ]; + if ($i == $page) { + $data['current'] = true; + } + $ret['pages'][] = $data; + } + $i = $page - 1; + if ($i) { + $link = self::get_wp_link_page($i); + $ret['prev'] = [ + 'link' => $link, + ]; + } + $i = $page + 1; + if ($i <= $numpages) { + $link = self::get_wp_link_page($i); + $ret['next'] = [ + 'link' => $link, + ]; + } + } + return $ret; + } + + /** + * Finds any WP_Post objects and converts them to Timber\Post objects. + * + * @api + * @param array|WP_Post $data + */ + public function convert($data) + { + if (\is_object($data)) { + $data = Helper::convert_wp_object($data); + } elseif (\is_array($data)) { + $data = \array_map([$this, 'convert'], $data); + } + return $data; + } + + /** + * Gets the parent (if one exists) from a post as a Timber\Post object. + * Honors Class Maps. + * + * @api + * @example + * ```twig + * Parent page: {{ post.parent.title }} + * ``` + * @return bool|Post + */ + public function parent() + { + if (!$this->post_parent) { + return false; + } + + return $this->factory()->from($this->post_parent); + } + + /** + * Gets the relative path of a WP Post, so while link() will return https://example.org/2015/07/my-cool-post + * this will return just /2015/07/my-cool-post + * + * @api + * @example + * ```twig + * {{post.title}} + * ``` + * @return string + */ + public function path() + { + return URLHelper::get_rel_url($this->link()); + } + + /** + * Get the previous post that is adjacent to the current post in a collection. + * + * Works pretty much the same as + * [`get_previous_post()`](https://developer.wordpress.org/reference/functions/get_previous_post/). + * + * @api + * @example + * ```twig + * {% if post.prev %} + * {{ post.prev.title }} + * {% endif %} + * ``` + * @param bool|string $in_same_term Whether the post should be in a same taxonomy term. Default + * `false`. + * @return mixed + */ + public function prev($in_same_term = false) + { + if (isset($this->_prev) && isset($this->_prev[$in_same_term])) { + return $this->_prev[$in_same_term]; + } + global $post; + $old_global = $post; + $post = $this; + $within_taxonomy = $in_same_term ?: 'category'; + $adjacent = \get_adjacent_post(($in_same_term), '', true, $within_taxonomy); + $prev_in_taxonomy = false; + if ($adjacent) { + $prev_in_taxonomy = $this->factory()->from($adjacent); + } + $this->_prev[$in_same_term] = $prev_in_taxonomy; + $post = $old_global; + return $this->_prev[$in_same_term]; + } + + /** + * Gets the tags on a post, uses WP's post_tag taxonomy + * + * @api + * @return array + */ + public function tags() + { + return $this->terms('post_tag'); + } + + /** + * Gets the post’s thumbnail ID. + * + * @api + * @since 2.0.0 + * + * @return false|int The default post’s ID. False if no thumbnail was defined. + */ + public function thumbnail_id() + { + return (int) \get_post_meta($this->ID, '_thumbnail_id', true); + } + + /** + * get the featured image as a Timber/Image + * + * @api + * @example + * ```twig + * + * ``` + * @return Image|null of your thumbnail + */ + public function thumbnail() + { + $tid = $this->thumbnail_id(); + + if ($tid) { + return $this->factory()->from($tid); + } + + return null; + } + + /** + * Returns the processed title to be used in templates. This returns the title of the post after WP's filters have run. This is analogous to `the_title()` in standard WP template tags. + * + * @api + * @example + * ```twig + *

    {{ post.title }}

    + * ``` + * @return string + */ + public function title() + { + if ($rd = $this->get_revised_data_from_method('title')) { + return $rd; + } + return \apply_filters('the_title', $this->post_title, $this->ID); + } + + /** + * Returns galleries from the post’s content. + * + * @api + * @example + * ```twig + * {{ post.gallery }} + * ``` + * @return array A list of arrays, each containing gallery data and srcs parsed from the + * expanded shortcode. + */ + public function gallery($html = true) + { + $galleries = \get_post_galleries($this->ID, $html); + $gallery = \reset($galleries); + + return \apply_filters('get_post_gallery', $gallery, $this->ID, $galleries); + } + + protected function get_entity_name() + { + return 'post'; + } + + /** + * Given a base query and a list of taxonomies, return a list of queries + * each of which queries for one of the taxonomies. + * @example + * ``` + * $this->partition_tax_queries(["object_ids" => [123]], ["a", "b"]); + * + * // result: + * // [ + * // ["object_ids" => [123], "taxonomy" => ["a"]], + * // ["object_ids" => [123], "taxonomy" => ["b"]], + * // ] + * ``` + * @internal + */ + private function partition_tax_queries(array $query, array $taxonomies): array + { + return \array_map(fn (string $tax): array => \array_merge($query, [ + 'taxonomy' => [$tax], + ]), $taxonomies); + } + + /** + * Get a PostFactory instance for internal usage + * + * @internal + * @return PostFactory + */ + private function factory() + { + static $factory; + $factory = $factory ?: new PostFactory(); + return $factory; + } +} diff --git a/vendor/timber/timber/src/PostArrayObject.php b/vendor/timber/timber/src/PostArrayObject.php new file mode 100644 index 0000000..7cc776f --- /dev/null +++ b/vendor/timber/timber/src/PostArrayObject.php @@ -0,0 +1,81 @@ += 7.4. + * + * @see https://bugs.php.net/bug.php?id=69264 + * @internal + */ + public function __debugInfo(): array + { + return [ + 'info' => \sprintf( + ' +******************************************************************************** + + This output is generated by %s(). + + The properties you see here are not actual properties, but only debug + output. If you want to access the actual instances of Timber\Posts, loop + over the collection or get all posts through $query->to_array(). + + More info: https://timber.github.io/docs/v2/guides/posts/#debugging-post-collections + +********************************************************************************', + __METHOD__ + ), + 'posts' => $this->getArrayCopy(), + 'factory' => $this->factory, + 'iterator' => $this->getIterator(), + ]; + } + + /** + * Returns realized (eagerly instantiated) Timber\Post data to serialize to JSON. + * + * @internal + */ + #[ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/vendor/timber/timber/src/PostCollectionInterface.php b/vendor/timber/timber/src/PostCollectionInterface.php new file mode 100644 index 0000000..beb97a7 --- /dev/null +++ b/vendor/timber/timber/src/PostCollectionInterface.php @@ -0,0 +1,34 @@ +` tag inside your post content. + * You can also change the text used for the read more link by adding your desired text to the + * `` tag. Here’s an example: ``. + * + * You can change the defaults that are used for excerpts through the + * [`timber/post/excerpt/defaults`](https://timber.github.io/docs/v2/hooks/filters/#timber/post/excerpts/defaults) + * filter. + * + * @api + * @since 1.0.4 + * @see \Timber\Post::excerpt() + * @example + * ```twig + * {# Use default excerpt #} + *

    {{ post.excerpt }}

    + * + * {# Preferred method: Use hash notation to pass arguments. #} + *
    {{ post.excerpt({ words: 100, read_more: 'Keep reading' }) }}
    + * + * {# Change the post excerpt text only #} + *

    {{ post.excerpt.read_more('Continue Reading') }}

    + * + * {# Additionally restrict the length to 50 words #} + *

    {{ post.excerpt.length(50).read_more('Continue Reading') }}

    + * ``` + */ +class PostExcerpt implements Stringable +{ + /** + * Excerpt end. + * + * @var string + */ + protected $end = '…'; + + /** + * Force length. + * + * @var bool + */ + protected $force = false; + + /** + * Length in words. + * + * @var int + */ + protected $length = 50; + + /** + * Length in characters. + * + * @var int|bool + */ + protected $char_length = false; + + /** + * Read more text. + * + * @var string|bool + */ + protected $read_more = 'Read More'; + + /** + * HTML tag stripping behavior. + * + * @var string|bool + */ + protected $strip = true; + + /** + * Whether a read more link should be added even if the excerpt isn’t trimmed (when the excerpt + * isn’t shorter than the post’s content). + * + * @since 2.0.0 + * @var bool + */ + protected $always_add_read_more = false; + + /** + * Whether the end string should be added even if the excerpt isn’t trimmed (when the excerpt + * isn’t shorter than the post’s content). + * + * @since 2.0.0 + * @var bool + */ + protected $always_add_end = false; + + /** + * Destroy tags. + * + * @var array List of tags that should always be destroyed. + */ + protected $destroy_tags = ['script', 'style']; + + /** + * PostExcerpt constructor. + * + * @api + * + * @param Post $post The post to pull the excerpt from. + * @param array $options { + * An array of configuration options for generating the excerpt. Default empty. + * + * @type int $words Number of words in the excerpt. Default `50`. + * @type int|bool $chars Number of characters in the excerpt. Default `false` (no + * character limit). + * @type string $end String to append to the end of the excerpt. Default '…' + * (HTML ellipsis character). + * @type bool $force Whether to shorten the excerpt to the length/word count + * specified, even if an editor wrote a manual excerpt longer + * than the set length. Default `false`. + * @type bool $strip Whether to strip HTML tags. Default `true`. + * @type string $read_more String for what the "Read More" text should be. Default + * 'Read More'. + * @type bool $always_add_read_more Whether a read more link should be added even if the + * excerpt isn’t trimmed (when the excerpt isn’t + * shorter than the post’s content). Default `false`. + * @type bool $always_add_end Whether the end string should be added even if the + * excerpt isn’t trimmed (when the excerpt isn’t + * shorter than the post’s content). Default `false`. + * } + */ + public function __construct( + /** + * Post. + */ + protected $post, + array $options = [] + ) { + $defaults = [ + 'words' => 50, + 'chars' => false, + 'end' => '…', + 'force' => false, + 'strip' => true, + 'read_more' => 'Read More', + 'always_add_read_more' => false, + 'always_add_end' => false, + ]; + + /** + * Filters the default options used for post excerpts. + * + * @since 2.0.0 + * @example + * ```php + * add_filter( 'timber/post/excerpt/defaults', function( $defaults ) { + * // Only add a read more link if the post content isn’t longer than the excerpt. + * $defaults['always_add_read_more'] = false; + * + * // Set a default character limit. + * $defaults['words'] = 240; + * + * return $defaults; + * } ); + * ``` + * + * @param array $defaults An array of default options. You can see which options you can use + * when you look at the `$options` parameter for + * [PostExcerpt::__construct()](https://timber.github.io/docs/v2/reference/timber-postexcerpt/#__construct). + */ + $defaults = \apply_filters('timber/post/excerpt/defaults', $defaults); + + // Set up excerpt defaults. + $options = \wp_parse_args($options, $defaults); + + // Set excerpt properties + $this->length = $options['words']; + $this->char_length = $options['chars']; + $this->end = $options['end']; + $this->force = $options['force']; + $this->strip = $options['strip']; + $this->read_more = $options['read_more']; + $this->always_add_read_more = $options['always_add_read_more']; + $this->always_add_end = $options['always_add_end']; + } + + /** + * Returns the resulting excerpt. + * + * @api + * @return string + */ + public function __toString() + { + return (string) $this->run(); + } + + /** + * Restricts the length of the excerpt to a certain amount of words. + * + * @api + * @example + * ```twig + *

    {{ post.excerpt.length(50) }}

    + * ``` + * @param int $length The maximum amount of words (not letters) for the excerpt. Default `50`. + * @return PostExcerpt + */ + public function length($length = 50) + { + $this->length = $length; + return $this; + } + + /** + * Restricts the length of the excerpt to a certain amount of characters. + * + * @api + * @example + * ```twig + *

    {{ post.excerpt.chars(180) }}

    + * ``` + * @param int|bool $char_length The maximum amount of characters for the excerpt. Default + * `false`. + * @return PostExcerpt + */ + public function chars($char_length = false) + { + $this->char_length = $char_length; + return $this; + } + + /** + * Defines the text to end the excerpt with. + * + * @api + * @example + * ```twig + *

    {{ post.excerpt.end('… and much more!') }}

    + * ``` + * @param string $end The text for the end of the excerpt. Default `…`. + * @return PostExcerpt + */ + public function end($end = '…') + { + $this->end = $end; + return $this; + } + + /** + * Forces excerpt lengths. + * + * What happens if your custom post excerpt is longer than the length requested? By default, it + * will use the full `post_excerpt`. However, you can set this to `true` to *force* your excerpt + * to be of the desired length. + * + * @api + * @example + * ```twig + *

    {{ post.excerpt.length(20).force }}

    + * ``` + * @param bool $force Whether the length of the excerpt should be forced to the requested + * length, even if an editor wrote a manual excerpt that is longer than the + * set length. Default `true`. + * @return PostExcerpt + */ + public function force($force = true) + { + $this->force = $force; + return $this; + } + + /** + * Defines the text to be used for the "Read More" link. + * + * Set this to `false` to not add a "Read More" link. + * + * @api + * ```twig + *

    {{ post.excerpt.read_more('Learn more') }}

    + * ``` + * + * @param string|bool $text Text for the link. Default 'Read More'. + * + * @return PostExcerpt + */ + public function read_more($text = 'Read More') + { + $this->read_more = $text; + return $this; + } + + /** + * Defines how HTML tags should be stripped from the excerpt. + * + * @api + * ```twig + * {# Strips all HTML tags, except for bold or emphasized text #} + *

    {{ post.excerpt.length('50').strip('') }}

    + * ``` + * @param bool|string $strip Whether or how HTML tags in the excerpt should be stripped. Use + * `true` to strip all tags, `false` for no stripping, or a string for + * a list of allowed tags (e.g. '

    '). Default `true`. + * @return PostExcerpt + */ + public function strip($strip = true) + { + $this->strip = $strip; + return $this; + } + + /** + * Assembles excerpt. + * + * @internal + * + * @param string $text The text to use for the excerpt. + * @param array $args An array of arguments for the assembly. + */ + protected function assemble($text, $args = []) + { + $text = \trim($text); + $last = $text[\strlen($text) - 1]; + $last_p_tag = null; + if ($last != '.' && ($this->always_add_end || $args['add_end'])) { + $text .= $this->end; + } + if (!$this->strip) { + $last_p_tag = \strrpos($text, '

    '); + if ($last_p_tag !== false) { + $text = \substr($text, 0, $last_p_tag); + } + if ($last != '.' && ($this->always_add_end || $args['add_end'])) { + $text .= $this->end . ' '; + } + } + + // Maybe add read more link. + if ($this->read_more && ($this->always_add_read_more || $args['add_read_more'])) { + /** + * Filters the CSS class used for excerpt links. + * + * @since 2.0.0 + * @example + * ```php + * // Change the CSS class for excerpt links. + * add_filter( 'timber/post/excerpt/read_more_class', function( $class ) { + * return 'read-more__link'; + * } ); + * ``` + * + * @param string $class The CSS class to use for the excerpt link. Default `read-more`. + */ + $read_more_class = \apply_filters('timber/post/excerpt/read_more_class', 'read-more'); + + /** + * Filters the CSS class used for excerpt links. + * + * @deprecated 2.0.0 + * @since 1.0.4 + */ + $read_more_class = \apply_filters_deprecated( + 'timber/post/preview/read_more_class', + [$read_more_class], + '2.0.0', + 'timber/post/excerpt/read_more_class' + ); + + $linktext = \trim($this->read_more); + + $link = \sprintf( + ' %3$s', + $this->post->link(), + $read_more_class, + $linktext + ); + + /** + * Filters the link used for a read more text in an excerpt. + * + * @since 2.0.0 + * @param string $link The HTML link. + * @param Post $post Post instance. + * @param string $linktext The link text. + * @param string $read_more_class The CSS class name. + */ + $link = \apply_filters( + 'timber/post/excerpt/read_more_link', + $link, + $this->post, + $linktext, + $read_more_class + ); + + /** + * Filters the link used for a read more text in an excerpt. + * + * @deprecated 2.0.0 + * @since 1.1.3 + * @ticket #1142 + */ + $link = \apply_filters_deprecated( + 'timber/post/get_preview/read_more_link', + [$link], + '2.0.0', + 'timber/post/excerpt/read_more_link' + ); + + $text .= $link; + } + + if (!$this->strip && $last_p_tag && (\strpos($text, '

    ') > -1 || \strpos($text, '

    strip && \is_string($this->strip)) ? $this->strip : false; + $readmore_matches = []; + $text = ''; + $add_read_more = false; + $add_end = false; + + // A user-specified excerpt is authoritative, so check that first. + if (isset($this->post->post_excerpt) && \strlen($this->post->post_excerpt)) { + $text = $this->post->post_excerpt; + if ($this->force) { + if ($allowable_tags) { + $text = TextHelper::trim_words($text, $this->length, false, \strtr($allowable_tags, '<>', ' ')); + } else { + $text = TextHelper::trim_words($text, $this->length, false); + } + if ($this->char_length !== false) { + $text = TextHelper::trim_characters($text, $this->char_length, false); + } + + $add_end = true; + } + + $add_read_more = true; + } + + // Check for tag in post content. + if (empty($text) && \preg_match('//', $this->post->post_content, $readmore_matches)) { + $pieces = \explode($readmore_matches[0], $this->post->post_content); + $text = $pieces[0]; + + $add_read_more = true; + + /** + * Custom read more text. + * + * The following post content example will result in the read more text to become "But + * what is Elaina?": Eric is a polar bear Lauren is + * not a duck. + */ + if (!empty($readmore_matches[1])) { + $this->read_more = \trim($readmore_matches[1]); + } + + if ($this->force) { + if ($allowable_tags) { + $text = TextHelper::trim_words($text, $this->length, false, \strtr($allowable_tags, '<>', ' ')); + } else { + $text = TextHelper::trim_words($text, $this->length, false); + } + if ($this->char_length !== false) { + $text = TextHelper::trim_characters($text, $this->char_length, false); + } + + $add_end = true; + } + + $text = \do_shortcode($text); + } + + // Build an excerpt text from the post’s content. + if (empty($text)) { + $text = $this->post->content(0, -1, true); + $text = TextHelper::remove_tags($text, $this->destroy_tags); + $text_before_trim = \trim($text); + $text_before_char_trim = ''; + + if ($allowable_tags) { + $text = TextHelper::trim_words($text, $this->length, false, \strtr($allowable_tags, '<>', ' ')); + } else { + $text = TextHelper::trim_words($text, $this->length, false); + } + + if ($this->char_length !== false) { + $text_before_char_trim = \trim($text); + $text = TextHelper::trim_characters($text, $this->char_length, false); + } + + $has_trimmed_words = \strlen($text) < \strlen($text_before_trim); + $has_trimmed_chars = !empty($text_before_char_trim) + && \strlen($text) < \strlen($text_before_char_trim); + + if ($has_trimmed_words || $has_trimmed_chars) { + $add_end = true; + $add_read_more = true; + } + } + if (empty(\trim((string) $text))) { + return \trim((string) $text); + } + if ($this->strip) { + $text = \trim(\strip_tags((string) $text, $allowable_tags)); + } + if (!empty($text)) { + return $this->assemble($text, [ + 'add_end' => $add_end, + 'add_read_more' => $add_read_more, + ]); + } + + return \trim($text); + } +} diff --git a/vendor/timber/timber/src/PostQuery.php b/vendor/timber/timber/src/PostQuery.php new file mode 100644 index 0000000..2782b83 --- /dev/null +++ b/vendor/timber/timber/src/PostQuery.php @@ -0,0 +1,199 @@ + 'article', + * 'category_name' => 'sports', + * ] ); + * + * // Passing a WP_Query instance. + * $posts = Timber::get_posts( new WP_Query( [ 'post_type' => 'any' ) ); + * ``` + * + * @param WP_Query $query The WP_Query object to wrap. + */ + public function __construct(WP_Query $query) + { + $this->wp_query = $query; + $this->found_posts = (int) $this->wp_query->found_posts; + + $posts = $this->wp_query->posts ?: []; + + parent::__construct($posts, 0, PostsIterator::class); + } + + /** + * Get pagination for a post collection. + * + * Refer to the [Pagination Guide]({{< relref "../guides/pagination.md" >}}) for a detailed usage example. + * + * Optionally could be used to get pagination with custom preferences. + * + * @api + * @example + * ```twig + * {% if posts.pagination.prev %} + * Prev + * {% endif %} + * + *

      + * {% for page in posts.pagination.pages %} + *
    • + * {{ page.title }} + *
    • + * {% endfor %} + *
    + * + * {% if posts.pagination.next %} + * Next + * {% endif %} + * ``` + * + * @param array $prefs Optional. Custom preferences. Default `array()`. + * + * @return Pagination object + */ + public function pagination($prefs = []) + { + if (!$this->pagination && $this->wp_query instanceof WP_Query) { + $this->pagination = new Pagination($prefs, $this->wp_query); + } + + return $this->pagination; + } + + /** + * Gets the original query used to get a collection of Timber posts. + * + * @since 2.0 + * @return WP_Query|null + */ + public function query(): ?WP_Query + { + return $this->wp_query; + } + + /** + * Gets the original query used to get a collection of Timber posts. + * + * @deprecated 2.0.0, use PostQuery::query() instead. + * @return WP_Query|null + */ + public function get_query(): ?WP_Query + { + Helper::deprecated('Timber\PostQuery::get_query()', 'Timber\PostQuery::query()', '2.0.0'); + + return $this->wp_query; + } + + /** + * Override data printed by var_dump() and similar. Realizes the collection before + * returning. Due to a PHP bug, this only works in PHP >= 7.4. + * + * @see https://bugs.php.net/bug.php?id=69264 + * @internal + */ + public function __debugInfo(): array + { + return [ + 'info' => \sprintf( + ' +******************************************************************************** + + This output is generated by %s(). + + The properties you see here are not actual properties, but only debug + output. If you want to access the actual instances of Timber\Posts, loop + over the collection or get all posts through $query->to_array(). + + More info: https://timber.github.io/docs/v2/guides/posts/#debugging-post-collections + +********************************************************************************', + __METHOD__ + ), + 'posts' => $this->getArrayCopy(), + 'wp_query' => $this->wp_query, + 'found_posts' => $this->found_posts, + 'pagination' => $this->pagination, + 'factory' => $this->factory, + 'iterator' => $this->getIterator(), + ]; + } + + /** + * Returns realized (eagerly instantiated) Timber\Post data to serialize to JSON. + * + * @internal + */ + #[ReturnTypeWillChange] + public function jsonSerialize() + { + return $this->getArrayCopy(); + } +} diff --git a/vendor/timber/timber/src/PostType.php b/vendor/timber/timber/src/PostType.php new file mode 100644 index 0000000..2ac22d7 --- /dev/null +++ b/vendor/timber/timber/src/PostType.php @@ -0,0 +1,41 @@ +init($this->slug); + } + + public function __toString() + { + return $this->slug; + } + + protected function init($post_type) + { + $obj = \get_post_type_object($post_type); + if (!empty($obj)) { + foreach (\get_object_vars($obj) as $key => $value) { + if ($key === '' || \ord($key[0]) === 0) { + continue; + } + $this->$key = $value; + } + } + } +} diff --git a/vendor/timber/timber/src/PostsIterator.php b/vendor/timber/timber/src/PostsIterator.php new file mode 100644 index 0000000..14721cc --- /dev/null +++ b/vendor/timber/timber/src/PostsIterator.php @@ -0,0 +1,93 @@ +key()) { + /** + * The `loop_start` action is not the only thing we do to improve compatibility with + * WordPress. There’s more going on in the Timber\Post::setup() function. The + * compatibility improvements live there, because they also need to work for singular + * templates, where there’s no loop. + */ + \do_action_ref_array('loop_start', [&$GLOBALS['wp_query']]); + } + + $wp_post = parent::current(); + + // Lazily instantiate a Timber\Post instance exactly once. + $post = $factory->from($wp_post); + + if ($post instanceof Post) { + $post->setup(); + } + + $this->last_post = $post; + + return $post; + } + + /** + * Cleans up state before advancing to the next post. + * + * Calls the `teardown()` function of the current post. In the last run of a loop through posts, + * it will call the 'loop_end' hook to improve compatibility with WordPress. + * + * @since 2.0.0 + */ + public function next(): void + { + /** + * Load from $last_post instead of $this->current(), because $this->current() would call + * $post->setup() again. + */ + $post = $this->last_post; + + if ($post instanceof Post) { + $post->teardown(); + } + + // Fire action when the loop has ended. + if ($this->key() === $this->count() - 1) { + /** + * The `loop_end` action is not the only thing we do to improve compatibility with + * WordPress. There’s more going on in the Timber\Post::teardown() function. The + * compatibility improvements live there, because they also need to work for singular + * templates, where there’s no loop. + */ + \do_action_ref_array('loop_end', [&$GLOBALS['wp_query']]); + \wp_reset_postdata(); + } + + parent::next(); + } +} diff --git a/vendor/timber/timber/src/Setupable.php b/vendor/timber/timber/src/Setupable.php new file mode 100644 index 0000000..6d22815 --- /dev/null +++ b/vendor/timber/timber/src/Setupable.php @@ -0,0 +1,27 @@ + new Timber\Site( $other_site_id ), + * ] ); + * + * Timber::render('index.twig', $context); + * ``` + * ```twig + * My site is called {{site.name}}, another site on my network is {{other_site.name}} + * ``` + * ```html + * My site is called Jared's blog, another site on my network is Upstatement.com + * ``` + */ +class Site extends Core implements CoreInterface +{ + /** + * The underlying WordPress Core object. + * + * @since 2.0.0 + * + * @var WP_Site|null Will only be filled in multisite environments. Otherwise `null`. + */ + protected ?WP_Site $wp_object = null; + + /** + * @api + * @var string The admin email address set in the WP admin panel + */ + public $admin_email; + + /** + * @api + * @var string + */ + public $blogname; + + /** + * @api + * @var string + */ + public $charset; + + /** + * @api + * @var string + */ + public $description; + + /** + * @api + * @var int the ID of a site in multisite + */ + public $id; + + /** + * @api + * @var string the language setting ex: en-US + */ + public $language; + + /** + * @api + * @var bool true if multisite, false if plain ole' WordPress + */ + public $multisite; + + /** + * @api + * @var string + */ + public $name; + + /** + * @deprecated 2.0.0, use $pingback_url + * @var string for people who like trackback spam + */ + public $pingback; + + /** + * @api + * @var string for people who like trackback spam + */ + public $pingback_url; + + /** + * @api + * @var string + */ + public $siteurl; + + /** + * @api + * @var Theme + */ + public $theme; + + /** + * @api + * @var string + */ + public $title; + + /** + * @api + * @var string + */ + public $url; + + /** + * @api + * @var string + */ + public $home_url; + + /** + * @api + * @var string + */ + public $site_url; + + /** + * @api + * @var string + */ + public $rdf; + + public $rss; + + public $rss2; + + public $atom; + + /** + * Constructs a Timber\Site object + * @api + * @example + * ```php + * //multisite setup + * $site = new Timber\Site(1); + * $site_two = new Timber\Site("My Cool Site"); + * //non-multisite + * $site = new Timber\Site(); + * ``` + * @param string|int $site_name_or_id + */ + public function __construct($site_name_or_id = null) + { + if (\is_multisite()) { + $blog_id = self::switch_to_blog($site_name_or_id); + $this->init(); + $this->init_as_multisite($blog_id); + \restore_current_blog(); + } else { + $this->init(); + $this->init_as_singlesite(); + } + } + + /** + * Magic method dispatcher for site option fields, for convenience in Twig views. + * + * Called when explicitly invoking non-existent methods on the Site object. This method is not + * meant to be called directly. + * + * @example + * The following example will dynamically dispatch the magic __call() method with an argument + * of "users_can_register" #} + * + * ```twig + * {% if site.users_can_register %} + * {# Show a notification and link to the register form #} + * {% endif %} + * @link https://secure.php.net/manual/en/language.oop5.overloading.php#object.call + * @link https://github.com/twigphp/Twig/issues/2 + * @api + * + * @param string $option The name of the method being called. + * @param array $arguments Enumerated array containing the parameters passed to the function. + * Not used. + * + * @return mixed The value of the option field named `$field` if truthy, `false` otherwise. + */ + public function __call($option, $arguments) + { + return $this->option($option); + } + + /** + * Gets the underlying WordPress Core object. + * + * @since 2.0.0 + * + * @return WP_Site|null Will only return a `WP_Site` object in multisite environments. Otherwise `null`. + */ + public function wp_object(): ?WP_Site + { + return $this->wp_object; + } + + /** + * Switches to the blog requested in the request + * + * @param string|integer|null $blog_identifier The name or ID of the blog to switch to. If `null`, the current blog. + * @return integer with the ID of the new blog + */ + protected static function switch_to_blog($blog_identifier = null): int + { + $current_id = \get_current_blog_id(); + + if ($blog_identifier === null) { + $blog_identifier = $current_id; + } + + $info = \get_blog_details($blog_identifier, false); + + if (false === $info) { + return $current_id; + } + + $blog_identifier = $info->blog_id; + + if ((int) $current_id !== (int) $blog_identifier) { + \switch_to_blog($blog_identifier); + } + + return (int) $blog_identifier; + } + + /** + * @internal + * @param integer $site_id + */ + protected function init_as_multisite($site_id) + { + $wp_site = \get_blog_details($site_id); + $this->import($wp_site); + $this->ID = $wp_site->blog_id; + $this->id = $this->ID; + // Site might be false, but $wp_object needs to be null if it can’t be set. + $this->wp_object = $wp_site ?: null; + $this->name = $this->blogname; + $this->title = $this->blogname; + $theme_slug = \get_blog_option($wp_site->blog_id, 'stylesheet'); + $this->theme = new Theme($theme_slug); + $this->description = \get_blog_option($wp_site->blog_id, 'blogdescription'); + $this->admin_email = \get_blog_option($wp_site->blog_id, 'admin_email'); + $this->multisite = true; + } + + /** + * Executed for single-blog sites + * @internal + */ + protected function init_as_singlesite() + { + // No WP_Site object available in single site environments. + $this->wp_object = null; + + $this->admin_email = \get_bloginfo('admin_email'); + $this->name = \get_bloginfo('name'); + $this->title = $this->name; + $this->description = \get_bloginfo('description'); + $this->theme = new Theme(); + $this->multisite = false; + } + + /** + * Executed for all types of sites: both multisite and "regular" + * @internal + */ + protected function init() + { + $this->url = \home_url(); + $this->home_url = $this->url; + $this->site_url = \site_url(); + $this->rdf = \get_bloginfo('rdf_url'); + $this->rss = \get_bloginfo('rss_url'); + $this->rss2 = \get_bloginfo('rss2_url'); + $this->atom = \get_bloginfo('atom_url'); + $this->language = \get_locale(); + $this->charset = \get_bloginfo('charset'); + $this->pingback = $this->pingback_url = \get_bloginfo('pingback_url'); + } + + /** + * Returns the language attributes that you're looking for + * @return string + */ + public function language_attributes() + { + return \get_language_attributes(); + } + + /** + * Get the value for a site option. + * + * @api + * @example + * ```twig + * Published on: {{ post.date|date(site.date_format) }} + * ``` + * + * @param string $option The name of the option to get the value for. + * + * @return mixed The option value. + */ + public function __get($option) + { + if (!isset($this->$option)) { + if (\is_multisite()) { + $this->$option = \get_blog_option($this->ID, $option); + } else { + $this->$option = \get_option($option); + } + } + + return $this->$option; + } + + /** + * Get the value for a site option. + * + * @api + * @example + * ```twig + * Published on: {{ post.date|date(site.option('date_format')) }} + * ``` + * + * @param string $option The name of the option to get the value for. + * + * @return mixed The option value. + */ + public function option($option) + { + return $this->__get($option); + } + + /** + * Get the value for a site option. + * + * @api + * @deprecated 2.0.0, use `{{ site.option }}` instead + */ + public function meta($option) + { + Helper::deprecated('{{ site.meta() }}', '{{ site.option() }}', '2.0.0'); + + return $this->__get($option); + } + + /** + * @api + * @return null|Image + */ + public function icon() + { + if (\is_multisite()) { + return $this->icon_multisite($this->ID); + } + $iid = \get_option('site_icon'); + if ($iid) { + return Timber::get_post($iid); + } + + return null; + } + + protected function icon_multisite($site_id) + { + $image = null; + $blog_id = self::switch_to_blog($site_id); + $iid = \get_blog_option($blog_id, 'site_icon'); + if ($iid) { + $image = Timber::get_post($iid); + } + \restore_current_blog(); + return $image; + } + + /** + * Returns the link to the site's home. + * + * @api + * @example + * ```twig + * + * Logo for some stupid thing + * + * ``` + * ```html + * + * Logo for some stupid thing + * + * ``` + * + * @return string + */ + public function link() + { + return $this->url; + } + + /** + * Updates a site option. + * + * @deprecated 2.0.0 Use `update_option()` or `update_blog_option()` instead. + * + * @param string $key The key of the site option to update. + * @param mixed $value The new value. + */ + public function update($key, $value) + { + Helper::deprecated('Timber\Site::update()', 'update_option()', '2.0.0'); + + /** + * Filters a value before it is updated in the site options. + * + * @since 2.0.0 + * + * @param mixed $value The new value. + * @param string $key The option key. + * @param int $site_id The site ID. + * @param Site $site The site object. + */ + $value = \apply_filters('timber/site/update_option', $value, $key, $this->ID, $this); + + /** + * Filters a value before it is updated in the site options. + * + * @deprecated 2.0.0, use `timber/site/update_option` + * @since 0.20.0 + */ + $value = \apply_filters_deprecated( + 'timber_site_set_meta', + [$value, $key, $this->ID, $this], + '2.0.0', + 'timber/site/update_option' + ); + + if (\is_multisite()) { + \update_blog_option($this->ID, $key, $value); + } else { + \update_option($key, $value); + } + $this->$key = $value; + } +} diff --git a/vendor/timber/timber/src/Term.php b/vendor/timber/timber/src/Term.php new file mode 100644 index 0000000..c295fa6 --- /dev/null +++ b/vendor/timber/timber/src/Term.php @@ -0,0 +1,641 @@ +{{ term_page.name }} Archives + *

    Teams

    + *
      + *
    • {{ st_louis.name}} - {{ st_louis.description }}
    • + *
    • {{ team.name}} - {{ team.description }}
    • + *
    + * ``` + * ```html + *

    Team Archives

    + *

    Teams

    + *
      + *
    • St. Louis Cardinals - Winner of 11 World Series
    • + *
    • New England Patriots - Winner of 6 Super Bowls
    • + *
    + * ``` + */ +class Term extends CoreEntity implements Stringable +{ + /** + * The underlying WordPress Core object. + * + * @since 2.0.0 + * + * @var WP_Term|null + */ + protected ?WP_Term $wp_object = null; + + public $object_type = 'term'; + + public static $representation = 'term'; + + public $_children; + + /** + * @api + * @var string the human-friendly name of the term (ex: French Cuisine) + */ + public $name; + + /** + * @api + * @var string the WordPress taxonomy slug (ex: `post_tag` or `actors`) + */ + public $taxonomy; + + /** + * @internal + */ + protected function __construct() + { + } + + /** + * @internal + * + * @param WP_Term $wp_term The vanilla WordPress term object to build from. + * @return Term + */ + public static function build(WP_Term $wp_term): static + { + $term = new static(); + $term->init($wp_term); + return $term; + } + + /** + * The string the term will render as by default + * + * @api + * @return string + */ + public function __toString() + { + return $this->name; + } + + /** + * + * @deprecated 2.0.0, use TermFactory::from instead. + * + * @param $tid + * @param $taxonomy + * + * @return static + */ + public static function from($tid, $taxonomy = null) + { + Helper::deprecated( + "Term::from()", + "Timber\Factory\TermFactory->from()", + '2.0.0' + ); + + $termFactory = new TermFactory(); + return $termFactory->from($tid); + } + + /* Setup + ===================== */ + /** + * @internal + */ + protected function init(WP_Term $term) + { + $this->ID = $term->term_id; + $this->id = $term->term_id; + $this->wp_object = $term; + $this->import($term); + } + + /** + * @internal + * @param int|object|array $tid + * @return mixed + */ + protected function get_term($tid) + { + if (\is_object($tid) || \is_array($tid)) { + return $tid; + } + $tid = self::get_tid($tid); + + if (\is_array($tid)) { + //there's more than one matching $term_id, let's figure out which is correct + if (isset($this->taxonomy) && \strlen($this->taxonomy)) { + foreach ($tid as $term_id) { + $maybe_term = \get_term($term_id, $this->taxonomy); + if ($maybe_term) { + return $maybe_term; + } + } + } + $tid = $tid[0]; + } + + if (isset($this->taxonomy) && \strlen($this->taxonomy)) { + return \get_term($tid, $this->taxonomy); + } else { + global $wpdb; + $query = $wpdb->prepare("SELECT taxonomy FROM $wpdb->term_taxonomy WHERE term_id = %d LIMIT 1", $tid); + $tax = $wpdb->get_var($query); + if (isset($tax) && \strlen((string) $tax)) { + $this->taxonomy = $tax; + return \get_term($tid, $tax); + } + } + return null; + } + + /** + * @internal + * @return int|array + */ + protected static function get_tid(mixed $tid) + { + global $wpdb; + if (\is_numeric($tid)) { + return $tid; + } + if (\gettype($tid) === 'object') { + $tid = $tid->term_id; + } + if (\is_numeric($tid)) { + $query = $wpdb->prepare("SELECT term_id FROM $wpdb->terms WHERE term_id = %d", $tid); + } else { + $query = $wpdb->prepare("SELECT term_id FROM $wpdb->terms WHERE slug = %s", $tid); + } + $result = $wpdb->get_col($query); + if ($result) { + if (\count($result) == 1) { + return $result[0]; + } + return $result; + } + return false; + } + + /* Public methods + ===================== */ + + /** + * Gets the underlying WordPress Core object. + * + * @since 2.0.0 + * + * @return WP_Term|null + */ + public function wp_object(): ?WP_Term + { + return $this->wp_object; + } + + /** + * @deprecated 2.0.0, use `{{ term.edit_link }}` instead. + * @return string + */ + public function get_edit_url() + { + Helper::deprecated('{{ term.get_edit_url }}', '{{ term.edit_link }}', '2.0.0'); + return $this->edit_link(); + } + + /** + * Gets a term meta value. + * @deprecated 2.0.0, use `{{ term.meta('field_name') }}` instead. + * + * @param string $field_name The field name for which you want to get the value. + * @return string The meta field value. + */ + public function get_meta_field($field_name) + { + Helper::deprecated( + "{{ term.get_meta_field('field_name') }}", + "{{ term.meta('field_name') }}", + '2.0.0' + ); + return $this->meta($field_name); + } + + /** + * @internal + * @return array + */ + public function children() + { + if (!isset($this->_children)) { + $children = \get_term_children($this->ID, $this->taxonomy); + foreach ($children as &$child) { + $child = Timber::get_term($child); + } + $this->_children = $children; + } + return $this->_children; + } + + /** + * Return the description of the term + * + * @api + * @return string + */ + public function description() + { + $prefix = '

    '; + $desc = \term_description($this->ID, $this->taxonomy); + if (\str_starts_with((string) $desc, $prefix)) { + $desc = \substr((string) $desc, \strlen($prefix)); + } + $desc = \preg_replace('/' . \preg_quote('

    ', '/') . '$/', '', (string) $desc); + return \trim((string) $desc); + } + + /** + * Checks whether the current user can edit the term. + * + * @api + * @example + * ```twig + * {% if term.can_edit %} + * Edit + * {% endif %} + * ``` + * @return bool + */ + public function can_edit(): bool + { + return \current_user_can('edit_term', $this->ID); + } + + /** + * Gets the edit link for a term if the current user has the correct rights. + * + * @api + * @example + * ```twig + * {% if term.can_edit %} + * Edit + * {% endif %} + * ``` + * @return string|null The edit URL of a term in the WordPress admin or null if the current user can’t edit the + * term. + */ + public function edit_link(): ?string + { + if (!$this->can_edit()) { + return null; + } + + return \get_edit_term_link($this->ID, $this->taxonomy); + } + + /** + * Returns a full link to the term archive page like `https://example.com/category/news` + * + * @api + * @example + * ```twig + * See all posts in: {{ term.name }} + * ``` + * + * @return string + */ + public function link() + { + $link = \get_term_link($this->wp_object); + + /** + * Filters the link to the term archive page. + * + * @see \Timber\Term::link() + * @since 0.21.9 + * + * @param string $link The link. + * @param Term $term The term object. + */ + $link = \apply_filters('timber/term/link', $link, $this); + + /** + * Filters the link to the term archive page. + * + * @deprecated 0.21.9, use `timber/term/link` + */ + $link = \apply_filters_deprecated( + 'timber_term_link', + [$link, $this], + '2.0.0', + 'timber/term/link' + ); + + return $link; + } + + /** + * Gets a term meta value. + * + * @api + * @deprecated 2.0.0, use `{{ term.meta('field_name') }}` instead. + * @see \Timber\Term::meta() + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_field($field_name = null) + { + Helper::deprecated( + "{{ term.get_field('field_name') }}", + "{{ term.meta('field_name') }}", + '2.0.0' + ); + + return $this->meta($field_name); + } + + /** + * Returns a relative link (path) to the term archive page like `/category/news` + * + * @api + * @example + * ```twig + * See all posts in: {{ term.name }} + * ``` + * @return string + */ + public function path() + { + $link = $this->link(); + $rel = URLHelper::get_rel_url($link, true); + + /** + * Filters the relative link (path) to a term archive page. + * + * ``` + * add_filter( 'timber/term/path', function( $rel, $term ) { + * if ( $term->slug === 'news' ) { + * return '/category/modified-url'; + * } + * + * return $rel; + * }, 10, 2 ); + * ``` + * + * @see \Timber\Term::path() + * @since 0.21.9 + * + * @param string $rel The relative link. + * @param Term $term The term object. + */ + $rel = \apply_filters('timber/term/path', $rel, $this); + + /** + * Filters the relative link (path) to a term archive page. + * + * @deprecated 2.0.0, use `timber/term/path` + */ + $rel = \apply_filters_deprecated( + 'timber_term_path', + [$rel, $this], + '2.0.0', + 'timber/term/path' + ); + + return $rel; + } + + /** + * Gets posts that have the current term assigned. + * + * @api + * @example + * Query the default posts_per_page for this Term: + * + * ```twig + *

    Recent posts in {{ term.name }}

    + * + *
      + * {% for post in term.posts() %} + *
    • + * {{ post.title }} + *
    • + * {% endfor %} + *
    + * ``` + * + * Query exactly 3 Posts from this Term: + * + * ```twig + *

    Recent posts in {{ term.name }}

    + * + *
      + * {% for post in term.posts(3) %} + *
    • + * {{ post.title }} + *
    • + * {% endfor %} + *
    + * ``` + * + * If you need more control over the query that is going to be performed, you can pass your + * custom query arguments in the first parameter. + * + * ```twig + *

    Our branches in {{ region.name }}

    + * + *
      + * {% for branch in region.posts({ + * post_type: 'branch', + * posts_per_page: -1, + * orderby: 'menu_order' + * }) %} + *
    • + * {{ branch.title }} + *
    • + * {% endfor %} + *
    + * ``` + * + * @param int|array $query Optional. Either the number of posts or an array of + * arguments for the post query to be performed. + * Default is an empty array, the equivalent of: + * ```php + * [ + * 'posts_per_page' => get_option('posts_per_page'), + * 'post_type' => 'any', + * 'tax_query' => [ ...tax query for this Term... ] + * ] + * ``` + * @param string $post_type_or_class Deprecated. Before Timber 2.x this was a post_type to be + * used for querying posts OR the Timber\Post subclass to + * instantiate for each post returned. As of Timber 2.0.0, + * specify `post_type` in the `$query` array argument. To + * specify the class, use Class Maps. + * @see https://timber.github.io/docs/v2/guides/posts/ + * @see https://timber.github.io/docs/v2/guides/class-maps/ + * @return PostQuery + */ + public function posts($query = [], $post_type_or_class = null) + { + if (\is_string($query)) { + Helper::doing_it_wrong( + 'Passing a query string to Term::posts()', + 'Pass a query array instead: e.g. `"posts_per_page=3"` should be replaced with `["posts_per_page" => 3]`', + '2.0.0' + ); + + return false; + } + + if (\is_int($query)) { + $query = [ + 'posts_per_page' => $query, + 'post_type' => 'any', + ]; + } + + if (isset($post_type_or_class)) { + Helper::deprecated( + 'Passing post_type_or_class', + 'Pass post_type as part of the $query argument. For specifying class, use Class Maps: https://timber.github.io/docs/v2/guides/class-maps/', + '2.0.0' + ); + + // Honor the non-deprecated posts_per_page param over the deprecated second arg. + $query['post_type'] ??= $post_type_or_class; + } + + if (\func_num_args() > 2) { + Helper::doing_it_wrong( + 'Passing a post class', + 'Use Class Maps instead: https://timber.github.io/docs/v2/guides/class-maps/', + '2.0.0' + ); + } + + $tax_query = [ + // Force a tax_query constraint on this term. + 'relation' => 'AND', + [ + 'field' => 'id', + 'terms' => $this->ID, + 'taxonomy' => $this->taxonomy, + ], + ]; + + // Merge a clause for this Term into any user-specified tax_query clauses. + $query['tax_query'] = \array_merge($query['tax_query'] ?? [], $tax_query); + + return Timber::get_posts($query); + } + + /** + * @api + * @return string + */ + public function title() + { + return $this->name; + } + + /** DEPRECATED DOWN HERE + * ====================== + **/ + + /** + * Get Posts that have been "tagged" with the particular term + * + * @api + * @deprecated 2.0.0 use `{{ term.posts }}` instead + * + * @param int $numberposts + * @return array|bool|null + */ + public function get_posts($numberposts = 10) + { + Helper::deprecated('{{ term.get_posts }}', '{{ term.posts }}', '2.0.0'); + return $this->posts($numberposts); + } + + /** + * @api + * @deprecated 2.0.0, use `{{ term.children }}` instead. + * + * @return array + */ + public function get_children() + { + Helper::deprecated('{{ term.get_children }}', '{{ term.children }}', '2.0.0'); + + return $this->children(); + } + + /** + * Updates term_meta of the current object with the given value. + * + * @deprecated 2.0.0 Use `update_term_meta()` instead. + * + * @param string $key The key of the meta field to update. + * @param mixed $value The new value. + */ + public function update($key, $value) + { + Helper::deprecated('Timber\Term::update()', 'update_term_meta()', '2.0.0'); + + /** + * Filters term meta value that is going to be updated. + * + * @deprecated 2.0.0 with no replacement + */ + $value = \apply_filters_deprecated( + 'timber_term_set_meta', + [$value, $key, $this->ID, $this], + '2.0.0', + false, + 'This filter will be removed in a future version of Timber. There is no replacement.' + ); + + /** + * Filters term meta value that is going to be updated. + * + * This filter is used by the ACF Integration. + * + * @deprecated 2.0.0, with no replacement + */ + $value = \apply_filters_deprecated( + 'timber/term/meta/set', + [$value, $key, $this->ID, $this], + '2.0.0', + false, + 'This filter will be removed in a future version of Timber. There is no replacement.' + ); + + $this->$key = $value; + } +} diff --git a/vendor/timber/timber/src/TextHelper.php b/vendor/timber/timber/src/TextHelper.php new file mode 100644 index 0000000..2b3bb9f --- /dev/null +++ b/vendor/timber/timber/src/TextHelper.php @@ -0,0 +1,139 @@ + $value !== ''); + $allowed_tag_string = '<' . \implode('><', $allowed_tags_array) . '>'; + + $text = \strip_tags($text, $allowed_tag_string); + /* + * translators: If your word count is based on single characters (e.g. East Asian characters), + * enter 'characters_excluding_spaces' or 'characters_including_spaces'. Otherwise, enter 'words'. + * Do not translate into your own language. + */ + if ('characters' == \_x('words', 'Word count type. Do not translate!') && \preg_match('/^utf\-?8$/i', (string) \get_option('blog_charset'))) { + $text = \trim((string) \preg_replace("/[\n\r\t ]+/", ' ', $text), ' '); + \preg_match_all('/./u', $text, $words_array); + $words_array = \array_slice($words_array[0], 0, $num_words + 1); + $sep = ''; + } else { + $words_array = \preg_split("/[\n\r\t ]+/", $text, $num_words + 1, PREG_SPLIT_NO_EMPTY); + $sep = ' '; + } + if (\count($words_array) > $num_words) { + \array_pop($words_array); + $text = \implode($sep, $words_array); + $text = $text . $more; + } else { + $text = \implode($sep, $words_array); + } + $text = self::close_tags($text); + return \apply_filters('wp_trim_words', $text, $num_words, $more, $original_text); + } + + /** + * @api + * + * @param $string + * @param array $tags + * + * @return null|string|string[] + */ + public static function remove_tags($string, $tags = []) + { + return \preg_replace('#<(' . \implode('|', $tags) . ')(?:[^>]+)?>.*?#s', '', (string) $string); + } + + /** + * + * + * @param string $html + * @return string + */ + public static function close_tags($html) + { + //put all opened tags into an array + \preg_match_all('#<([a-z]+)(?: .*)?(?#iU', $html, $result); + $openedtags = $result[1]; + //put all closed tags into an array + \preg_match_all('##iU', $html, $result); + $closedtags = $result[1]; + $len_opened = \count($openedtags); + // all tags are closed + if (\count($closedtags) == $len_opened) { + return $html; + } + $openedtags = \array_reverse($openedtags); + // close tags + for ($i = 0; $i < $len_opened; $i++) { + if (!\in_array($openedtags[$i], $closedtags)) { + $html .= ''; + } else { + unset($closedtags[\array_search($openedtags[$i], $closedtags)]); + } + } + $html = \str_replace(['
    ', '', ''], '', $html); + $html = \str_replace(['
    ', '
    ', ''], ['
    ', '
    ', ''], $html); + return $html; + } +} diff --git a/vendor/timber/timber/src/Theme.php b/vendor/timber/timber/src/Theme.php new file mode 100644 index 0000000..16cd09d --- /dev/null +++ b/vendor/timber/timber/src/Theme.php @@ -0,0 +1,243 @@ + + * ``` + * ```twig + * + * ``` + * ```html + * + * ``` + */ +class Theme extends Core implements JsonSerializable +{ + /** + * The human-friendly name of the theme (ex: `My Timber Starter Theme`) + * + * @api + * @var string the human-friendly name of the theme (ex: `My Timber Starter Theme`) + */ + public $name; + + /** + * The version of the theme (ex: `1.2.3`) + * + * @api + * @var string the version of the theme (ex: `1.2.3`) + */ + public $version; + + /** + * Timber\Theme object for the parent theme. + * + * Always returns the top-most theme. If the current theme is also the parent theme, it will + * return itself. + * + * @api + * @var Theme the Timber\Theme object for the parent theme + */ + public $parent; + + /** + * Slug of the parent theme (ex: `_s`) + * + * @api + * @var string the slug of the parent theme (ex: `_s`) + */ + public $parent_slug; + + /** + * @api + * @var string the slug of the theme (ex: `my-timber-theme`) + */ + public $slug; + + /** + * @api + * @var string Retrieves template directory URI for the active (parent) theme. (ex: `https://example.org/wp-content/themes/my-timber-theme`). + */ + public $uri; + + /** + * @var WP_Theme the underlying WordPress native Theme object + */ + private $theme; + + /** + * Constructs a new `Timber\Theme` object. + * + * The `Timber\Theme` object of the current theme comes in the default `Timber::context()` + * call. You can access this in your twig template via `{{ site.theme }}`. + * + * @api + * @example + * ```php + * init($slug); + } + + /** + * Initializes the Theme object + * + * @internal + * @param string $slug of theme (eg 'my-timber-theme'). + */ + protected function init($slug = null) + { + $this->theme = \wp_get_theme($slug); + $this->name = $this->theme->get('Name'); + $this->version = $this->theme->get('Version'); + $this->slug = $this->theme->get_stylesheet(); + + $this->uri = $this->theme->get_template_directory_uri(); + + $this->parent = $this; + $this->parent_slug = $this->theme->get_stylesheet(); + if ($this->theme->parent()) { + $this->parent_slug = $this->theme->parent()->get_stylesheet(); + $this->parent = new Theme($this->parent_slug); + } + } + + /** + * @api + * @return string Retrieves template directory URI for the active (child) theme. (ex: `https://example.org/wp-content/themes/my-timber-theme`). + */ + public function link() + { + return $this->theme->get_stylesheet_directory_uri(); + } + + /** + * @api + * @return string The relative path to the theme (ex: `/wp-content/themes/my-timber-theme`). + */ + public function path() + { + // force = true to work with specifying the port + // @see https://github.com/timber/timber/issues/1739 + return URLHelper::get_rel_url($this->link(), true); + } + + /** + * @api + * @param string $name + * @param bool $default + * @return string + */ + public function theme_mod($name, $default = false) + { + return \get_theme_mod($name, $default); + } + + /** + * @api + * @return array + */ + public function theme_mods() + { + return \get_theme_mods(); + } + + /** + * Gets a raw, unformatted theme header. + * + * @api + * @see \WP_Theme::get() + * @example + * ```twig + * {{ theme.get('Version') }} + * ``` + * + * @param string $header Name of the theme header. Name, Description, Author, Version, + * ThemeURI, AuthorURI, Status, Tags. + * + * @return false|string String on success, false on failure. + */ + public function get($header) + { + return $this->theme->get($header); + } + + /** + * Gets a theme header, formatted and translated for display. + * + * @api + * @see \WP_Theme::display() + * @example + * ```twig + * {{ theme.display('Description') }} + * ``` + * + * @param string $header Name of the theme header. Name, Description, Author, Version, + * ThemeURI, AuthorURI, Status, Tags. + * + * @return false|string + */ + public function display($header) + { + return $this->theme->display($header); + } + + /** + * Returns serialized theme data. + * + * This data will e.g. be used when a `Timber\Theme` object is used to generate a key. We need to serialize the data + * because the $parent property is a reference to itself. This recursion would cause json_encode() to fail. + * + * @internal + * @return array + */ + public function jsonSerialize(): array + { + return [ + 'name' => $this->name, + 'parent' => [ + 'name' => $this->parent->name, + 'parent' => null, + 'parent_slug' => null, + 'slug' => $this->parent->slug, + 'uri' => $this->parent->uri, + 'version' => $this->parent->version, + ], + 'parent_slug' => $this->parent_slug, + 'slug' => $this->slug, + 'uri' => $this->uri, + 'version' => $this->version, + ]; + } +} diff --git a/vendor/timber/timber/src/Timber.php b/vendor/timber/timber/src/Timber.php new file mode 100644 index 0000000..16e7cef --- /dev/null +++ b/vendor/timber/timber/src/Timber.php @@ -0,0 +1,1722 @@ + 'article', + * 'category_name' => 'sports', + * ] ); + * + * $context = Timber::context( [ + * 'posts' => $posts, + * ] ); + * + * Timber::render( 'index.twig', $context ); + * ``` + */ +class Timber +{ + public static $version = '2.3.2'; // x-release-please-version + + public static $locations; + + public static $dirname = 'views'; + + public static $auto_meta = true; + + /** + * Global context cache. + * + * @var array An array containing global context variables. + */ + public static $context_cache = []; + + /** + * Caching option for Twig. + * + * @deprecated 2.0.0 + * @var bool + */ + public static $twig_cache = false; + + /** + * Caching option for Twig. + * + * Alias for `Timber::$twig_cache`. + * + * @deprecated 2.0.0 + * @var bool + */ + public static $cache = false; + + /** + * Autoescaping option for Twig. + * + * @deprecated 2.0.0 + * @var bool + */ + public static $autoescape = false; + + /** + * Timber should be loaded with Timber\Timber::init() and not new Timber\Timber(); + * + * @codeCoverageIgnore + */ + protected function __construct() + { + } + + protected function init_constants() + { + \defined("TIMBER_LOC") or \define("TIMBER_LOC", \realpath(\dirname(__DIR__))); + } + + /** + * @codeCoverageIgnore + */ + public static function init(): void + { + if ( + !\defined('ABSPATH') + || !\class_exists('\WP') + || \defined('TIMBER_LOADED') + ) { + return; + } + + $self = new self(); + $self->init_constants(); + + Twig::init(); + ImageHelper::init(); + + \add_action('init', [self::class, 'init_integrations']); + \add_action('admin_init', [Admin::class, 'init']); + + \add_filter('timber/post/import_data', [self::class, 'handle_preview'], 10, 2); + + /** + * Make an alias for the Timber class. + * + * This way, developers can use Timber::render() instead of Timber\Timber::render, which + * is more user-friendly. + */ + \class_alias(Timber::class, 'Timber'); + + \define('TIMBER_LOADED', true); + } + + /** + * Initializes Timber's integrations. + * + * @return void + */ + public static function init_integrations(): void + { + $integrations = [ + new Integration\AcfIntegration(), + new Integration\CoAuthorsPlusIntegration(), + new Integration\WpCliIntegration(), + new Integration\WpmlIntegration(), + ]; + + /** + * Filters the integrations that should be initialized by Timber. + * + * @since 2.0.0 + * + * @param IntegrationInterface[] $integrations An array of PHP class names. Default: array of + * integrations that Timber initializes by default. + */ + $integrations = \apply_filters('timber/integrations', $integrations); + + // Integration classes must implement the IntegrationInterface. + $integrations = \array_filter($integrations, static fn ($integration) => $integration instanceof IntegrationInterface); + + foreach ($integrations as $integration) { + if (!$integration->should_init()) { + continue; + } + $integration->init(); + } + } + + /** + * Handles previewing posts. + * + * @param array $data + * @param Post $post + * @return array + */ + public static function handle_preview($data, $post) + { + if (!isset($_GET['preview']) || !isset($_GET['preview_id'])) { + return $data; + } + + $preview_post_id = (int) $_GET['preview_id']; + $current_post_id = $post->ID ?? null; + // ⚠️ Don't filter imported data if the current post ID is not the preview post ID. + // You might alter every `Timber::get_post()`! + if ($current_post_id !== $preview_post_id) { + return $data; + } + + $preview = \wp_get_post_autosave($preview_post_id); + + if (\is_object($preview)) { + $preview = \sanitize_post($preview); + + $data['post_content'] = $preview->post_content; + $data['post_title'] = $preview->post_title; + $data['post_excerpt'] = $preview->post_excerpt; + + \add_filter('get_the_terms', '_wp_preview_terms_filter', 10, 3); + } + + return $data; + } + + /* Post Retrieval Routine + ================================ */ + + /** + * Gets a Timber Post from a post ID, WP_Post object, a WP_Query object, or an associative + * array of arguments for WP_Query::__construct(). + * + * By default, Timber will use the `Timber\Post` class to create a new post object. To control + * which class is instantiated for your Post object, use [Class Maps](https://timber.github.io/docs/v2/guides/class-maps/) + * + * @api + * @example + * ```php + * // Using a post ID. + * $post = Timber::get_post( 75 ); + * + * // Using a WP_Post object. + * $wp_post = get_post( 123 ); + * $post = Timber::get_post( $wp_post ); + * + * // Using a WP_Query argument array + * $post = Timber::get_post( [ + * 'post_type' => 'page', + * ] ); + * + * // Use currently queried post. Same as using get_the_ID() as a parameter. + * $post = Timber::get_post(); + * + * // From an associative array with an `ID` key. For ACF compatibility. Using this + * // approach directly is not recommended. If you can, configure the return type of your + * // ACF field to just the ID. + * $post = Timber::get_post( get_field('associated_post_array') ); // Just OK. + * $post = Timber::get_post( get_field('associated_post_id') ); // Better! + * ``` + * @see https://developer.wordpress.org/reference/classes/wp_query/__construct/ + * + * @param mixed $query Optional. Post ID or query (as an array of arguments for WP_Query). + * If a query is provided, only the first post of the result will be + * returned. Default false. + * @param array $options Optional associative array of options. Defaults to an empty array. + * + * @return Post|null Timber\Post object if a post was found, null if no post was + * found. + */ + public static function get_post(mixed $query = false, $options = []) + { + self::check_post_api_deprecations($query, $options, 'Timber::get_post()'); + + if (\is_string($options)) { + $options = []; + } + + $factory = new PostFactory(); + + global $wp_query; + + $options = \wp_parse_args($options, [ + 'merge_default' => false, + ]); + + // Has WP already queried and found a post? + if ($query === false && ($wp_query->queried_object instanceof WP_Post)) { + $query = $wp_query->queried_object; + } elseif (\is_array($query) && $options['merge_default']) { + $query = \wp_parse_args($wp_query->query_vars); + } + + // Default to the global query. + $result = $factory->from($query ?: $wp_query); + + // If we got a Collection, return the first Post. + if ($result instanceof PostCollectionInterface) { + return $result[0] ?? null; + } + + return $result; + } + + /** + * Gets an attachment. + * + * Behaves just like Timber::get_post(), except that it returns null if it finds a Timber\Post + * that is not an Attachment. Honors Class Maps and falsifies return value *after* Class Map for + * the found Timber\Post has been resolved. + * + * @api + * @since 2.0.0 + * @see Timber::get_post() + * @see https://timber.github.io/docs/v2/guides/class-maps/ + * + * @param mixed $query Optional. Query or post identifier. Default false. + * @param array $options Optional. Options for Timber\Timber::get_post(). + * + * @return Attachment|null Timber\Attachment object if an attachment was found, null if no + * attachment was found. + */ + public static function get_attachment(mixed $query = false, $options = []) + { + self::check_post_api_deprecations($query, $options, 'Timber::get_attachment()'); + + $post = static::get_post($query, $options); + + // No need to instantiate a Post we're not going to use. + return ($post instanceof Attachment) ? $post : null; + } + + /** + * Gets an image. + * + * Behaves just like Timber::get_post(), except that it returns null if it finds a Timber\Post + * that is not an Image. Honors Class Maps and falsifies return value *after* Class Map for the + * found Timber\Post has been resolved. + * + * @api + * @since 2.0.0 + * @see Timber::get_post() + * @see https://timber.github.io/docs/v2/guides/class-maps/ + * + * @param mixed $query Optional. Query or post identifier. Default false. + * @param array $options Optional. Options for Timber\Timber::get_post(). + * + * @return Image|null + */ + public static function get_image(mixed $query = false, $options = []) + { + self::check_post_api_deprecations($query, $options, 'Timber::get_image()'); + + $post = static::get_post($query, $options); + + // No need to instantiate a Post we're not going to use. + return ($post instanceof Image) ? $post : null; + } + + /** + * Gets an external image. + * + * Behaves just like Timber::get_image(), except that you can use an absolute or relative path or a URL to load an + * image. You can also pass in an external URL. In that case, Timber will sideload the image and store it in the + * uploads folder of your WordPress installation. The next time the image is accessed, it will be loaded from there. + * + * @api + * @since 2.0.0 + * @see Timber::get_image() + * @see ImageHelper::sideload_image() + * + * @param bool $url Image path or URL. The path can be absolute or relative to the WordPress installation. + * @param array $args { + * An associative array with additional arguments for the image. + * + * @type string $alt Alt text for the image. + * @type string $caption Caption text for the image. + * } + * + * @return ExternalImage|null + */ + public static function get_external_image($url = false, array $args = []): ?ExternalImage + { + $args = \wp_parse_args($args, [ + 'alt' => '', + 'caption' => '', + ]); + + return ExternalImage::build($url, $args); + } + + /** + * Checks for deprecated Timber::get_post() API usage. + * + * @param $query + * @param $options + * @param $function_name + */ + private static function check_post_api_deprecations($query = false, $options = [], string $function_name = 'Timber::get_post()') + { + if (\is_string($query) && !\is_numeric($query)) { + Helper::doing_it_wrong( + $function_name, + 'Getting a post by post slug or post name was removed from Timber::get_post() in Timber 2.0. Use Timber::get_post_by() instead.', + '2.0.0' + ); + } + + if (\is_string($options)) { + Helper::doing_it_wrong( + $function_name, + 'The $PostClass parameter for passing in the post class to use in Timber::get_posts() was replaced with an $options array in Timber 2.0. To customize which class to instantiate for your post, use Class Maps instead: https://timber.github.io/docs/v2/guides/class-maps/', + '2.0.0' + ); + } + } + + /** + * Gets a collection of posts. + * + * Refer to the official documentation for + * [WP_Query](https://developer.wordpress.org/reference/classes/wp_query/) for a list of all + * the arguments that can be used for the `$query` parameter. + * + * @api + * @example + * ```php + * // Use the global query. + * $posts = Timber::get_posts(); + * + * // Using the WP_Query argument format. + * $posts = Timber::get_posts( [ + * 'post_type' => 'article', + * 'category_name' => 'sports', + * ] ); + * + * // Using a WP_Query instance. + * $posts = Timber::get_posts( new WP_Query( [ 'post_type' => 'any' ) ); + * + * // Using an array of post IDs. + * $posts = Timber::get_posts( [ 47, 543, 3220 ] ); + * ``` + * + * @param mixed $query Optional. Query args. Default `false`, which means that Timber will use + * the global query. Accepts an array of `WP_Query` arguments, a `WP_Query` + * instance or a list of post IDs. + * @param array $options { + * Optional. Options for the query. + * + * @type bool $merge_default Merge query parameters with the default query parameters of + * the current template. Default false. + * } + * + * @return PostCollectionInterface|null Null if no query could be run with the used + * query parameters. + */ + public static function get_posts(mixed $query = false, $options = []) + { + if (\is_string($query)) { + Helper::doing_it_wrong( + 'Timber::get_posts()', + "Querying posts by using a query string was removed in Timber 2.0. Pass in the query string as an options array instead. For example, change Timber::get_posts( 'post_type=portfolio&posts_per_page=3') to Timber::get_posts( [ 'post_type' => 'portfolio', 'posts_per_page' => 3 ] ). Learn more: https://timber.github.io/docs/v2/reference/timber-timber/#get_posts", + '2.0.0' + ); + + $query = new WP_Query($query); + } + + if (\is_string($options)) { + Helper::doing_it_wrong( + 'Timber::get_posts()', + 'The $PostClass parameter for passing in the post class to use in Timber::get_posts() was replaced with an $options array in Timber 2.0. To customize which class to instantiate for your post, use Class Maps instead: https://timber.github.io/docs/v2/guides/class-maps/', + '2.0.0' + ); + $options = []; + } + + if (3 === \func_num_args()) { + Helper::doing_it_wrong( + 'Timber::get_posts()', + 'The $return_collection parameter to control whether a post collection is returned in Timber::get_posts() was removed in Timber 2.0.', + '2.0.0' + ); + } + + if (\is_array($query) && isset($query['numberposts'])) { + Helper::doing_it_wrong( + 'Timber::get_posts()', + 'Using `numberposts` only works when using `get_posts()`, but not for Timber::get_posts(). Use `posts_per_page` instead.', + '2.0.0' + ); + } + + /** + * @todo Are there any more default options to support? + */ + $options = \wp_parse_args($options, [ + 'merge_default' => false, + ]); + + global $wp_query; + + if (\is_array($query) && $options['merge_default']) { + $query = \wp_parse_args($query, $wp_query->query_vars); + } + + $factory = new PostFactory(); + + // Default to the global query. + return $factory->from($query ?: $wp_query); + } + + /** + * Gets a post by title or slug. + * + * @api + * @since 2.0.0 + * @example + * ```php + * // By slug + * $post = Timber::get_post_by( 'slug', 'about-us' ); + * + * // By title + * $post = Timber::get_post_by( 'title', 'About us' ); + * ``` + * + * @param string $type The type to look for. One of `slug` or `title`. + * @param string $search_value The post slug or post title to search for. When searching + * for `title`, this parameter doesn’t need to be + * case-sensitive, because the `=` comparison is used in + * MySQL. + * @param array $args { + * Optional. An array of arguments to configure what is returned. + * + * @type string|array $post_type Optional. What WordPress post type to limit the + * results to. Defaults to 'any' + * @type string $order_by Optional. The field to sort by. Defaults to + * 'post_date' + * @type string $order Optional. The sort to apply. Defaults to ASC + * + * } + * + * @return Post|null A Timber post or `null` if no post could be found. If multiple + * posts with the same slug or title were found, it will select the + * post with the oldest date. + */ + public static function get_post_by($type, $search_value, $args = []) + { + $post_id = false; + $args = \wp_parse_args($args, [ + 'post_type' => 'any', + 'order_by' => 'post_date', + 'order' => 'ASC', + ]); + if ('slug' === $type) { + $args = \wp_parse_args($args, [ + 'name' => $search_value, + 'fields' => 'ids', + ]); + $query = new WP_Query($args); + + if ($query->post_count < 1) { + return null; + } + + $posts = $query->get_posts(); + $post_id = \array_shift($posts); + } elseif ('title' === $type) { + /** + * The following section is inspired by post_exists() as well as get_page_by_title(). + * + * These two functions always return the post with lowest ID. However, we want the post + * with oldest post date. + * + * @see \post_exists() + * @see \get_page_by_title() + */ + global $wpdb; + + $sql = "SELECT ID FROM $wpdb->posts WHERE post_title = %s"; + $query_args = [$search_value]; + if (\is_array($args['post_type'])) { + $post_type = \esc_sql($args['post_type']); + $post_type_in_string = "'" . \implode("','", $args['post_type']) . "'"; + + $sql .= " AND post_type IN ($post_type_in_string)"; + } elseif ('any' !== $args['post_type']) { + $sql .= ' AND post_type = %s'; + $query_args[] = $args['post_type']; + } + + // Always return the oldest post first. + $sql .= ' ORDER BY post_date ASC'; + + $post_id = $wpdb->get_var($wpdb->prepare($sql, $query_args)); + } + + if (!$post_id) { + return null; + } + + return self::get_post($post_id); + } + + /** + * Query post. + * + * @api + * @deprecated since 2.0.0 Use `Timber::get_post()` instead. + * + * + * @return Post|array|bool|null + */ + public static function query_post(mixed $query = false, array $options = []) + { + Helper::deprecated('Timber::query_post()', 'Timber::get_post()', '2.0.0'); + + return self::get_post($query, $options); + } + + /** + * Query posts. + * + * @api + * @deprecated since 2.0.0 Use `Timber::get_posts()` instead. + * + * + * @return PostCollectionInterface + */ + public static function query_posts(mixed $query = false, array $options = []) + { + Helper::deprecated('Timber::query_posts()', 'Timber::get_posts()', '2.0.0'); + + return self::get_posts($query, $options); + } + + /** + * Gets an attachment by its URL or absolute file path. + * + * Honors the `timber/post/image_extensions` filter, returning a Timber\Image if the found + * attachment is identified as an image. Also honors Class Maps. + * + * @api + * @since 2.0.0 + * @example + * ```php + * // Get attachment by URL. + * $attachment = Timber::get_attachment_by( 'url', 'https://example.com/uploads/2020/09/cat.gif' ); + * + * // Get attachment by filepath. + * $attachment = Timber::get_attachment_by( 'path', '/path/to/wp-content/uploads/2020/09/cat.gif' ); + * + * // Try to handle either case. + * $mystery_string = some_function(); + * $attachment = Timber::get_attachment_by( $mystery_string ); + * ``` + * + * @param string $field_or_ident Can be "url", "path", an attachment URL, or the absolute + * path of an attachment file. If "url" or "path" is given, a + * second arg is required. + * @param string $ident Optional. An attachment URL or absolute path. Default empty + * string. + * + * @return Attachment|null + */ + public static function get_attachment_by(string $field_or_ident, string $ident = '') + { + if ($field_or_ident === 'url') { + if (empty($ident)) { + Helper::doing_it_wrong( + 'Timber::get_attachment_by()', + 'Passing "url" as the first arg requires passing a URL as the second arg.', + '2.0.0' + ); + + return null; + } + + $id = \attachment_url_to_postid($ident); + + return $id ? (new PostFactory())->from($id) : null; + } + + if ($field_or_ident === 'path') { + if (empty($ident)) { + Helper::doing_it_wrong( + 'Timber::get_attachment_by()', + 'Passing "path" as the first arg requires passing an absolute path as the second arg.', + '2.0.0' + ); + + return null; + } + + if (!\file_exists($ident)) { + // Deal with a relative path. + $ident = URLHelper::get_full_path($ident); + } + + return self::get_attachment_by('url', URLHelper::file_system_to_url($ident)); + } + + if (empty($ident)) { + $field = URLHelper::starts_with($field_or_ident, ABSPATH) ? 'path' : 'url'; + + return self::get_attachment_by($field, $field_or_ident); + } + + return null; + } + + /* Term Retrieval + ================================ */ + + /** + * Gets terms. + * + * @api + * @see https://developer.wordpress.org/reference/classes/wp_term_query/__construct/ + * @example + * ```php + * // Get all tags. + * $tags = Timber::get_terms( 'post_tag' ); + * // Note that this is equivalent to: + * $tags = Timber::get_terms( 'tag' ); + * $tags = Timber::get_terms( 'tags' ); + * + * // Get all categories. + * $cats = Timber::get_terms( 'category' ); + * + * // Get all terms in a custom taxonomy. + * $cats = Timber::get_terms('my_taxonomy'); + * + * // Perform a custom Term query. + * $cats = Timber::get_terms( [ + * 'taxonomy' => 'my_taxonomy', + * 'orderby' => 'slug', + * 'order' => 'DESC', + * ] ); + * ``` + * + * @param string|array $args A string or array identifying the taxonomy or + * `WP_Term_Query` args. Numeric strings are treated as term IDs; + * non-numeric strings are treated as taxonomy names. Numeric + * arrays are treated as a list a of term identifiers; associative + * arrays are treated as args for `WP_Term_Query::__construct()` + * and accept any valid parameters to that constructor. + * Default `null`, which will get terms from all queryable + * taxonomies. + * @param array $options Optional. None are currently supported. Default empty array. + * + * @return iterable + */ + public static function get_terms($args = null, array $options = []): iterable + { + // default to all queryable taxonomies + $args ??= [ + 'taxonomy' => \get_taxonomies(), + ]; + + $factory = new TermFactory(); + + return $factory->from($args); + } + + /** + * Gets a term. + * + * @api + * @param int|WP_Term $term A WP_Term or term_id + * @return Term|null + * @example + * ```php + * // Get a Term. + * $tag = Timber::get_term( 123 ); + * ``` + */ + public static function get_term($term = null) + { + if (null === $term) { + // get the fallback term_id from the current query + global $wp_query; + $term = $wp_query->queried_object->term_id ?? null; + } + + if (null === $term) { + // not able to get term_id from the current query; bail + return null; + } + + $factory = new TermFactory(); + $terms = $factory->from($term); + + if (\is_array($terms)) { + $terms = $terms[0]; + } + + return $terms; + } + + /** + * Gets a term by field. + * + * This function works like + * [`get_term_by()`](https://developer.wordpress.org/reference/functions/get_term_by/), but + * returns a `Timber\Term` object. + * + * @api + * @since 2.0.0 + * @example + * ```php + * // Get a term by slug. + * $term = Timber::get_term_by( 'slug', 'security' ); + * + * // Get a term by name. + * $term = Timber::get_term_by( 'name', 'Security' ); + * + * // Get a term by slug from a specific taxonomy. + * $term = Timber::get_term_by( 'slug', 'security', 'category' ); + * ``` + * + * @param string $field The name of the field to retrieve the term with. One of: `id`, + * `ID`, `slug`, `name` or `term_taxonomy_id`. + * @param int|string $value The value to search for by `$field`. + * @param string $taxonomy The taxonomy you want to retrieve from. Empty string will search + * from all. + * + * @return Term|null + */ + public static function get_term_by(string $field, $value, string $taxonomy = '') + { + $wp_term = \get_term_by($field, $value, $taxonomy); + + if ($wp_term === false) { + if (empty($taxonomy) && $field != 'term_taxonomy_id') { + $search = [ + $field => $value, + $taxonomy => 'any', + 'hide_empty' => false, + ]; + return static::get_term($search); + } + + return null; + } + + return static::get_term($wp_term); + } + + /* User Retrieval + ================================ */ + + /** + * Gets one or more users as an array. + * + * By default, Timber will use the `Timber\User` class to create a your post objects. To + * control which class is used for your post objects, use [Class Maps](). + * + * @api + * @since 2.0.0 + * @example + * ```php + * // Get users with on an array of user IDs. + * $users = Timber::get_users( [ 24, 81, 325 ] ); + * + * // Get all users that only have a subscriber role. + * $subscribers = Timber::get_users( [ + * 'role' => 'subscriber', + * ] ); + * + * // Get all users that have published posts. + * $post_authors = Timber::get_users( [ + * 'has_published_posts' => [ 'post' ], + * ] ); + * ``` + * + * @todo Add links to Class Maps documentation in function summary. + * + * @param array $query Optional. A WordPress-style query or an array of user IDs. Use an + * array in the same way you would use the `$args` parameter in + * [WP_User_Query](https://developer.wordpress.org/reference/classes/wp_user_query/). + * See + * [WP_User_Query::prepare_query()](https://developer.wordpress.org/reference/classes/WP_User_Query/prepare_query/) + * for a list of all available parameters. Passing an empty parameter + * will return an empty array. Default empty array + * `[]`. + * @param array $options Optional. An array of options. None are currently supported. This + * parameter exists to prevent future breaking changes. Default empty + * array `[]`. + * + * @return iterable An array of users objects. Will be empty if no users were found. + */ + public static function get_users(array $query = [], array $options = []): iterable + { + $factory = new UserFactory(); + + return $factory->from($query); + } + + /** + * Gets a single user. + * + * By default, Timber will use the `Timber\User` class to create a your post objects. To + * control which class is used for your post objects, use [Class Maps](). + * + * @api + * @since 2.0.0 + * @example + * ```php + * $current_user = Timber::get_user(); + * + * // Get user by ID. + * $user = Timber::get_user( $user_id ); + * + * // Convert a WP_User object to a Timber\User object. + * $user = Timber::get_user( $wp_user_object ); + * + * // Check if a user is logged in. + * + * $user = Timber::get_user(); + * + * if ( $user ) { + * // Yay, user is logged in. + * } + * ``` + * + * @todo Add links to Class Maps documentation in function summary. + * + * @param int|WP_User $user A WP_User object or a WordPress user ID. Defaults to the ID of the + * currently logged-in user. + * + * @return User|null + */ + public static function get_user($user = null) + { + /* + * TODO in the interest of time, I'm implementing this logic here. If there's + * a better place to do this or something that already implements this, let me know + * and I'll switch over to that. + */ + $user = $user ?: \get_current_user_id(); + + $factory = new UserFactory(); + return $factory->from($user); + } + + /** + * Gets a user by field. + * + * This function works like + * [`get_user_by()`](https://developer.wordpress.org/reference/functions/get_user_by/), but + * returns a `Timber\User` object. + * + * @api + * @since 2.0.0 + * @example + * ```php + * // Get a user by email. + * $user = Timber::get_user_by( 'email', 'user@example.com' ); + * + * // Get a user by login. + * $user = Timber::get_user_by( 'login', 'keanu-reeves' ); + * ``` + * + * @param string $field The name of the field to retrieve the user with. One of: `id`, + * `ID`, `slug`, `email` or `login`. + * @param int|string $value The value to search for by `$field`. + * + * @return User|null + */ + public static function get_user_by(string $field, $value) + { + $wp_user = \get_user_by($field, $value); + + if ($wp_user === false) { + return null; + } + + return static::get_user($wp_user); + } + + /* Menu Retrieval + ================================ */ + + /** + * Gets a nav menu object. + * + * @api + * @since 2.0.0 + * @example + * ```php + * // Get a menu by location + * $menu = Timber::get_menu( 'primary-menu' ); + * + * // Get a menu by slug + * $menu = Timber::get_menu( 'my-menu' ); + * + * // Get a menu by name + * $menu = Timber::get_menu( 'Main Menu' ); + * + * // Get a menu by ID (term_id) + * $menu = Timber::get_menu( 123 ); + * ``` + * + * @param int|string $identifier A menu identifier: a term_id, slug, menu name, or menu location name + * @param array $args An associative array of options. Currently only one option is + * supported: + * - `depth`: How deep down the tree of menu items to query. Useful if you only want + * the first N levels of items in the menu. + * + * @return Menu|null + */ + public static function get_menu($identifier = null, array $args = []): ?Menu + { + $factory = new MenuFactory(); + return $factory->from($identifier, $args); + } + + /** + * Gets a menu by field. + * + * @api + * @since 2.0.0 + * @example + * ```php + * // Get a menu by location. + * $menu = Timber::get_menu_by( 'location', 'primary' ); + * + * // Get a menu by slug. + * $menu = Timber::get_menu_by( 'slug', 'primary-menu' ); + * ``` + * + * @param string $field The name of the field to retrieve the menu with. One of: `id`, + * `ID`, `term_id`, `slug`, `name` or `location`. + * @param int|string $value The value to search for by `$field`. + * + * @return Menu|null + */ + public static function get_menu_by(string $field, $value, array $args = []): ?Menu + { + $factory = new MenuFactory(); + $menu = null; + + $menu = match ($field) { + 'id', 'term_id', 'ID' => $factory->from_id($value, $args), + 'slug' => $factory->from_slug($value, $args), + 'name' => $factory->from_name($value, $args), + 'location' => $factory->from_location($value, $args), + default => $menu, + }; + + return $menu; + } + + /** + * Gets a menu from the existing pages. + * + * @api + * @since 2.0.0 + * + * @example + * ```php + * $menu = Timber::get_pages_menu(); + * ``` + * + * @param array $args Optional. Arguments for `wp_list_pages()`. Timber doesn’t use that + * function under the hood, but supports all arguments for that function. + * It will use `get_pages()` to get the pages that will be used for the Pages + * Menu. + */ + public static function get_pages_menu(array $args = []) + { + $factory = new PagesMenuFactory(); + + $menu = $factory->from_pages($args); + + return $menu; + } + + /** + * Get the navigation menu location assigned to the given menu. + * + * @api + * @since 2.3.0 + * + * @param WP_Term|int $term The menu to find; either a WP_Term object or a Term ID. + * @return string|null + */ + public static function get_menu_location($term): ?string + { + if ($term instanceof WP_Term) { + $term_id = $term->term_id; + } elseif (\is_int($term)) { + $term_id = $term; + } else { + return null; + } + + $locations = \array_flip(static::get_menu_locations()); + return $locations[$term_id] ?? null; + } + + /** + * Get the navigation menu locations with assigned menus. + * + * @api + * @since 2.3.0 + * + * @return array + */ + public static function get_menu_locations(): array + { + return \array_filter( + \get_nav_menu_locations(), + fn ($location) => \is_string($location) || \is_int($location) + ); + } + + /* Comment Retrieval + ================================ */ + + /** + * Get comments. + * + * @api + * @since 2.0.0 + * + * @param array|WP_Comment_Query $query + * @param array $options Optional. None are currently supported. + * @return array + */ + public static function get_comments($query = [], array $options = []): iterable + { + $factory = new CommentFactory(); + + return $factory->from($query); + } + + /** + * Gets comment. + * + * @api + * @since 2.0.0 + * @param int|WP_Comment $comment + * @return Comment|null + */ + public static function get_comment($comment) + { + $factory = new CommentFactory(); + return $factory->from($comment); + } + + /* Site Retrieval + ================================ */ + + /** + * Get sites. + * @api + * @param array|bool $blog_ids + * @return array + */ + public static function get_sites($blog_ids = false) + { + if (!\is_array($blog_ids)) { + global $wpdb; + $blog_ids = $wpdb->get_col("SELECT blog_id FROM $wpdb->blogs ORDER BY blog_id ASC"); + } + $return = []; + foreach ($blog_ids as $blog_id) { + $return[] = new Site($blog_id); + } + return $return; + } + + /* Template Setup and Display + ================================ */ + + /** + * Get context. + * @api + * @deprecated 2.0.0, use `Timber::context()` instead. + * + * @return array + */ + public static function get_context() + { + Helper::deprecated('get_context', 'context', '2.0.0'); + + return self::context(); + } + + /** + * Gets the global context. + * + * The context always contains the global context with the following variables: + * + * - `site` – An instance of `Timber\Site`. + * - `request` - An instance of `Timber\Request`. + * - `theme` - An instance of `Timber\Theme`. + * - `user` - An instance of `Timber\User`. + * - `http_host` - The HTTP host. + * - `wp_title` - Title retrieved for the currently displayed page, retrieved through + * `wp_title()`. + * - `body_class` - The body class retrieved through `get_body_class()`. + * + * The global context will be cached, which means that you can call this function again without + * losing performance. + * + * In addition to that, the context will contain template contexts depending on which template + * is being displayed. For archive templates, a `posts` variable will be present that will + * contain a collection of `Timber\Post` objects for the default query. For singular templates, + * a `post` variable will be present that that contains a `Timber\Post` object of the `$post` + * global. + * + * @api + * @since 2.0.0 + * + * @param array $extra Any extra data to merge in. Overrides whatever is already there for this + * call only. In other words, the underlying context data is immutable and + * unaffected by passing this param. + * + * @return array An array of context variables that is used to pass into Twig templates through + * a render or compile function. + */ + public static function context(array $extra = []) + { + $context = self::context_global(); + + if (\is_singular()) { + // NOTE: this also handles the is_front_page() case. + $context['post'] = Timber::get_post()->setup(); + } elseif (\is_home()) { + $post = Timber::get_post(); + + // When no page_on_front is set, there’s no post we can set up. + if ($post) { + $post->setup(); + } + + $context['post'] = $post; + $context['posts'] = Timber::get_posts(); + } elseif (\is_category() || \is_tag() || \is_tax()) { + $context['term'] = Timber::get_term(); + $context['posts'] = Timber::get_posts(); + } elseif (\is_search()) { + $context['posts'] = Timber::get_posts(); + $context['search_query'] = \get_search_query(); + } elseif (\is_author()) { + $context['author'] = Timber::get_user(\get_query_var('author')); + $context['posts'] = Timber::get_posts(); + } elseif (\is_archive()) { + $context['posts'] = Timber::get_posts(); + } + + return \array_merge($context, $extra); + } + + /** + * Gets the global context. + * + * This function is used by `Timber::context()` to get the global context. Usually, you don’t + * call this function directly, except when you need the global context in a partial view. + * + * The global context will be cached, which means that you can call this function again without + * losing performance. + * + * @api + * @since 2.0.0 + * @example + * ```php + * add_shortcode( 'global_address', function() { + * return Timber::compile( + * 'global_address.twig', + * Timber::context_global() + * ); + * } ); + * ``` + * + * @return array An array of global context variables. + */ + public static function context_global() + { + if (empty(self::$context_cache)) { + self::$context_cache['site'] = new Site(); + self::$context_cache['theme'] = self::$context_cache['site']->theme; + self::$context_cache['user'] = \is_user_logged_in() ? static::get_user() : false; + + self::$context_cache['http_host'] = URLHelper::get_scheme() . '://' . URLHelper::get_host(); + self::$context_cache['wp_title'] = Helper::get_wp_title(); + self::$context_cache['body_class'] = \implode(' ', \get_body_class()); + + /** + * Filters the global Timber context. + * + * By using this filter, you can add custom data to the global Timber context, which + * means that this data will be available on every page that is initialized with + * `Timber::context()`. + * + * Be aware that data will be cached as soon as you call `Timber::context()` for the + * first time. That’s why you should add this filter before you call + * `Timber::context()`. + * + * @see \Timber\Timber::context() + * @since 0.21.7 + * @example + * ```php + * add_filter( 'timber/context', function( $context ) { + * // Example: A custom value + * $context['custom_site_value'] = 'Hooray!'; + * + * // Example: Add a menu to the global context. + * $context['menu'] = new \Timber\Menu( 'primary-menu' ); + * + * // Example: Add all ACF options to global context. + * $context['options'] = get_fields( 'options' ); + * + * return $context; + * } ); + * ``` + * ```twig + *

    {{ custom_site_value|e }}

    + * + * {% for item in menu.items %} + * {# Display menu item #} + * {% endfor %} + * + *
    + * {% if options.footer_text is not empty %} + * {{ options.footer_text|e }} + * {% endif %} + *
    + * ``` + * + * @param array $context The global context. + */ + self::$context_cache = \apply_filters('timber/context', self::$context_cache); + + /** + * Filters the global Timber context. + * + * @deprecated 2.0.0, use `timber/context` + */ + self::$context_cache = \apply_filters_deprecated( + 'timber_context', + [self::$context_cache], + '2.0.0', + 'timber/context' + ); + } + + return self::$context_cache; + } + + /** + * Compiles a Twig file. + * + * Passes data to a Twig file and returns the output. If the template file doesn't exist it will throw a warning + * when WP_DEBUG is enabled. + * + * @api + * @example + * ```php + * $data = array( + * 'firstname' => 'Jane', + * 'lastname' => 'Doe', + * 'email' => 'jane.doe@example.org', + * ); + * + * $team_member = Timber::compile( 'team-member.twig', $data ); + * ``` + * @param array|string $filenames Name or full path of the Twig file to compile. If this is an array of file + * names or paths, Timber will compile the first file that exists. + * @param array $data Optional. An array of data to use in Twig template. + * @param bool|int|array $expires Optional. In seconds. Use false to disable cache altogether. When passed an + * array, the first value is used for non-logged in visitors, the second for users. + * Default false. + * @param string $cache_mode Optional. Any of the cache mode constants defined in Timber\Loader. + * @param bool $via_render Optional. Whether to apply optional render or compile filters. Default false. + * @return bool|string The returned output. + */ + public static function compile($filenames, $data = [], $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT, $via_render = false) + { + if (!\defined('TIMBER_LOADED')) { + self::init(); + } + $caller = LocationManager::get_calling_script_dir(1); + $loader = new Loader($caller); + $file = $loader->choose_template($filenames); + + $caller_file = LocationManager::get_calling_script_file(1); + + /** + * Fires after the calling PHP file was determined in Timber’s compile + * function. + * + * This action is used by the Timber Debug Bar extension. + * + * @since 1.1.2 + * @since 2.0.0 Switched from filter to action. + * + * @param string|null $caller_file The calling script file. + */ + \do_action('timber/calling_php_file', $caller_file); + + if ($via_render) { + /** + * Filters the Twig template that should be rendered. + * + * @since 2.0.0 + * + * @param string $file The chosen Twig template name to render. + */ + $file = \apply_filters('timber/render/file', $file); + + /** + * Filters the Twig file that should be rendered. + * + * @codeCoverageIgnore + * @deprecated 2.0.0, use `timber/render/file` + */ + $file = \apply_filters_deprecated( + 'timber_render_file', + [$file], + '2.0.0', + 'timber/render/file' + ); + } else { + /** + * Filters the Twig template that should be compiled. + * + * @since 2.0.0 + * + * @param string $file The chosen Twig template name to compile. + */ + $file = \apply_filters('timber/compile/file', $file); + + /** + * Filters the Twig template that should be compiled. + * + * @deprecated 2.0.0 + */ + $file = \apply_filters_deprecated( + 'timber_compile_file', + [$file], + '2.0.0', + 'timber/compile/file' + ); + } + $output = false; + + if ($file !== false) { + if (\is_null($data)) { + $data = []; + } + + if ($via_render) { + /** + * Filters the data that should be passed for rendering a Twig template. + * + * @since 2.0.0 + * + * @param array $data The data that is used to render the Twig template. + * @param string $file The name of the Twig template to render. + */ + $data = \apply_filters('timber/render/data', $data, $file); + /** + * Filters the data that should be passed for rendering a Twig template. + * + * @codeCoverageIgnore + * @deprecated 2.0.0 + */ + $data = \apply_filters_deprecated( + 'timber_render_data', + [$data], + '2.0.0', + 'timber/render/data' + ); + } else { + /** + * Filters the data that should be passed for compiling a Twig template. + * + * @since 2.0.0 + * + * @param array $data The data that is used to compile the Twig template. + * @param string $file The name of the Twig template to compile. + */ + $data = \apply_filters('timber/compile/data', $data, $file); + + /** + * Filters the data that should be passed for compiling a Twig template. + * + * @deprecated 2.0.0, use `timber/compile/data` + */ + $data = \apply_filters_deprecated( + 'timber_compile_data', + [$data], + '2.0.0', + 'timber/compile/data' + ); + } + + $output = $loader->render($file, $data, $expires, $cache_mode); + } else { + if (\is_array($filenames)) { + $filenames = \implode(", ", $filenames); + } + Helper::error_log('Error loading your template files: ' . $filenames . '. Make sure one of these files exists.'); + } + + /** + * Filters the compiled result before it is returned in `Timber::compile()`. + * + * It adds the possibility to filter the output ready for render. + * + * @since 2.0.0 + * + * @param string|bool $output the compiled output. + */ + $output = \apply_filters('timber/compile/result', $output); + + /** + * Fires after a Twig template was compiled and before the compiled data + * is returned. + * + * This action can be helpful if you need to debug Twig template + * compilation. + * + * @since 2.0.0 + * + * @param string $output The compiled output. + * @param string $file The name of the Twig template that was compiled. + * @param array $data The data that was used to compile the Twig template. + * @param bool|int|array $expires The expiration time of the cache in seconds, or false to disable cache. + * @param string $cache_mode Any of the cache mode constants defined in Timber\Loader. + */ + \do_action('timber/compile/done', $output, $file, $data, $expires, $cache_mode); + + /** + * Fires after a Twig template was compiled and before the compiled data + * is returned. + * + * @deprecated 2.0.0, use `timber/compile/done` + */ + \do_action_deprecated('timber_compile_done', [], '2.0.0', 'timber/compile/done'); + + return $output; + } + + /** + * Compile a string. + * + * @api + * @example + * ```php + * $data = array( + * 'username' => 'Jane Doe', + * ); + * + * $welcome = Timber::compile_string( 'Hi {{ username }}, I’m a string with a custom Twig variable', $data ); + * ``` + * @param string $string A string with Twig variables. + * @param array $data Optional. An array of data to use in Twig template. + * @return bool|string + */ + public static function compile_string($string, $data = []) + { + $dummy_loader = new Loader(); + $twig = $dummy_loader->get_twig(); + $template = $twig->createTemplate($string); + return $template->render($data); + } + + /** + * Fetch function. + * + * @api + * @deprecated 2.0.0 use Timber::compile() + * @param array|string $filenames Name of the Twig file to render. If this is an array of files, Timber will + * render the first file that exists. + * @param array $data Optional. An array of data to use in Twig template. + * @param bool|int $expires Optional. In seconds. Use false to disable cache altogether. When passed an + * array, the first value is used for non-logged in visitors, the second for users. + * Default false. + * @param string $cache_mode Optional. Any of the cache mode constants defined in Timber\Loader. + * @return bool|string The returned output. + */ + public static function fetch($filenames, $data = [], $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT) + { + Helper::deprecated( + 'fetch', + 'Timber::compile() (see https://timber.github.io/docs/v2/reference/timber/#compile for more information)', + '2.0.0' + ); + $output = self::compile($filenames, $data, $expires, $cache_mode, true); + + /** + * Filters the compiled result before it is returned. + * + * @see \Timber\Timber::fetch() + * @since 0.16.7 + * @deprecated 2.0.0 use timber/compile/result + * + * @param string $output The compiled output. + */ + $output = \apply_filters_deprecated( + 'timber_compile_result', + [$output], + '2.0.0', + 'timber/compile/result' + ); + + return $output; + } + + /** + * Renders a Twig file. + * + * Passes data to a Twig file and echoes the output. + * + * @api + * @example + * ```php + * $context = Timber::context(); + * + * Timber::render( 'index.twig', $context ); + * ``` + * @param array|string $filenames Name or full path of the Twig file to render. If this is an array of file + * names or paths, Timber will render the first file that exists. + * @param array $data Optional. An array of data to use in Twig template. + * @param bool|int|array $expires Optional. In seconds. Use false to disable cache altogether. When passed an + * array, the first value is used for non-logged in visitors, the second for users. + * Default false. + * @param string $cache_mode Optional. Any of the cache mode constants defined in Timber\Loader. + */ + public static function render($filenames, $data = [], $expires = false, $cache_mode = Loader::CACHE_USE_DEFAULT): void + { + $output = self::compile($filenames, $data, $expires, $cache_mode, true); + echo $output; + } + + /** + * Render a string with Twig variables. + * + * @api + * @example + * ```php + * $data = array( + * 'username' => 'Jane Doe', + * ); + * + * Timber::render_string( 'Hi {{ username }}, I’m a string with a custom Twig variable', $data ); + * ``` + * @param string $string A string with Twig variables. + * @param array $data An array of data to use in Twig template. + */ + public static function render_string($string, $data = []) + { + $compiled = self::compile_string($string, $data); + echo $compiled; + } + + /* Sidebar + ================================ */ + + /** + * Get sidebar. + * @api + * @param string $sidebar + * @param array $data + * @return bool|string + */ + public static function get_sidebar($sidebar = 'sidebar.php', $data = []) + { + if (\strstr(\strtolower($sidebar), '.php')) { + return self::get_sidebar_from_php($sidebar, $data); + } + return self::compile($sidebar, $data); + } + + /** + * Get sidebar from PHP + * @api + * @param string $sidebar + * @param array $data + * @return string + */ + public static function get_sidebar_from_php($sidebar = '', $data = []) + { + $caller = LocationManager::get_calling_script_dir(1); + $uris = LocationManager::get_locations($caller); + \ob_start(); + $found = false; + foreach ($uris as $namespace => $uri_locations) { + if (\is_array($uri_locations)) { + foreach ($uri_locations as $uri) { + if (\file_exists(\trailingslashit($uri) . $sidebar)) { + include \trailingslashit($uri) . $sidebar; + $found = true; + } + } + } + } + if (!$found) { + Helper::error_log('error loading your sidebar, check to make sure the file exists'); + } + $ret = \ob_get_contents(); + \ob_end_clean(); + + return $ret; + } + + /** + * Get widgets. + * + * @api + * @param int|string $widget_id Optional. Index, name or ID of dynamic sidebar. Default 1. + * @return string + */ + public static function get_widgets($widget_id) + { + return \trim(Helper::ob_function('dynamic_sidebar', [$widget_id])); + } + + /** + * Get pagination. + * + * @api + * @deprecated 2.0.0 + * @link https://timber.github.io/docs/v2/guides/pagination/ + * @param array $prefs an array of preference data. + * @return array|mixed + */ + public static function get_pagination($prefs = []) + { + Helper::deprecated( + 'get_pagination', + '{{ posts.pagination }} (see https://timber.github.io/docs/v2/guides/pagination/ for more information)', + '2.0.0' + ); + + return Pagination::get_pagination($prefs); + } +} diff --git a/vendor/timber/timber/src/Twig.php b/vendor/timber/timber/src/Twig.php new file mode 100644 index 0000000..b333176 --- /dev/null +++ b/vendor/timber/timber/src/Twig.php @@ -0,0 +1,830 @@ + [ + 'callable' => function ($action_name, ...$args) { + \do_action_ref_array($action_name, $args); + }, + ], + 'function' => [ + 'callable' => [$this, 'exec_function'], + ], + 'fn' => [ + 'callable' => [$this, 'exec_function'], + ], + 'get_post' => [ + 'callable' => [Timber::class, 'get_post'], + ], + 'get_image' => [ + 'callable' => [Timber::class, 'get_image'], + ], + 'get_external_image' => [ + 'callable' => [Timber::class, 'get_external_image'], + ], + 'get_attachment' => [ + 'callable' => [Timber::class, 'get_attachment'], + ], + 'get_posts' => [ + 'callable' => [Timber::class, 'get_posts'], + ], + 'get_attachment_by' => [ + 'callable' => [Timber::class, 'get_attachment_by'], + ], + 'get_term' => [ + 'callable' => [Timber::class, 'get_term'], + ], + 'get_terms' => [ + 'callable' => [Timber::class, 'get_terms'], + ], + 'get_user' => [ + 'callable' => [Timber::class, 'get_user'], + ], + 'get_users' => [ + 'callable' => [Timber::class, 'get_users'], + ], + 'get_comment' => [ + 'callable' => [Timber::class, 'get_comment'], + ], + 'get_comments' => [ + 'callable' => [Timber::class, 'get_comments'], + ], + 'Post' => [ + 'callable' => function ($post_id) use ($post_factory) { + Helper::deprecated('{{ Post() }}', '{{ get_post() }} or {{ get_posts() }}', '2.0.0'); + return $post_factory->from($post_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_post() }} or {{ get_posts() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'TimberPost' => [ + 'callable' => function ($post_id) use ($post_factory) { + Helper::deprecated('{{ TimberPost() }}', '{{ get_post() }} or {{ get_posts() }}', '2.0.0'); + return $post_factory->from($post_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_post() }} or {{ get_posts() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'Image' => [ + 'callable' => function ($post_id) use ($post_factory) { + Helper::deprecated('{{ Image() }}', '{{ get_image() }}, {{ get_post() }}, {{ get_posts() }}, {{ get_attachment() }} or {{ get_attachment_by() }}', '2.0.0'); + return $post_factory->from($post_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_image() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'TimberImage' => [ + 'callable' => function ($post_id) use ($post_factory) { + Helper::deprecated('{{ TimberImage() }}', '{{ get_image() }}, {{ get_post() }}, {{ get_posts() }}, {{ get_attachment() }} or {{ get_attachment_by() }}', '2.0.0'); + return $post_factory->from($post_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_image() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'Term' => [ + 'callable' => function ($term_id) use ($termFactory) { + Helper::deprecated('{{ Term() }}', '{{ get_term() }} or {{ get_terms() }}', '2.0.0'); + return $termFactory->from($term_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_term() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'TimberTerm' => [ + 'callable' => function ($term_id) use ($termFactory) { + Helper::deprecated('{{ TimberTerm() }}', '{{ get_term() }} or {{ get_terms() }}', '2.0.0'); + return $termFactory->from($term_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_term() }} or {{ get_terms() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'User' => [ + 'callable' => function ($user_id) { + Helper::deprecated('{{ User() }}', '{{ get_user() }} or {{ get_users() }}', '2.0.0'); + return Timber::get_user($user_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_user() }} or {{ get_users() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'TimberUser' => [ + 'callable' => function ($user_id) { + Helper::deprecated('{{ TimberUser() }}', '{{ get_user() }} or {{ get_users() }}', '2.0.0'); + return Timber::get_user($user_id); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ get_user() }} or {{ get_users() }}', + altPackage: null, + altVersion: null + ), + ], + ], + 'shortcode' => [ + 'callable' => 'do_shortcode', + ], + 'bloginfo' => [ + 'callable' => 'bloginfo', + ], + + // Translation functions. + '__' => [ + 'callable' => '__', + ], + 'translate' => [ + 'callable' => 'translate', + ], + '_e' => [ + 'callable' => '_e', + ], + '_n' => [ + 'callable' => '_n', + ], + '_x' => [ + 'callable' => '_x', + ], + '_ex' => [ + 'callable' => '_ex', + ], + '_nx' => [ + 'callable' => '_nx', + ], + '_n_noop' => [ + 'callable' => '_n_noop', + ], + '_nx_noop' => [ + 'callable' => '_nx_noop', + ], + 'translate_nooped_plural' => [ + 'callable' => 'translate_nooped_plural', + ], + ]; + + /** + * Filters the functions that are added to Twig. + * + * The `$functions` array is an associative array with the filter name as a key and an + * arguments array as the value. In the arguments array, you pass the function to call with + * a `callable` entry. + * + * This is an alternative filter that you can use instead of adding your function in the + * `timber/twig` filter. + * + * @api + * @since 2.0.0 + * @example + * ```php + * add_filter( 'timber/twig/functions', function( $functions ) { + * // Add your own function. + * $functions['url_to_domain'] = [ + * 'callable' => 'url_to_domain', + * ]; + * + * // Replace a function. + * $functions['get_image'] = [ + * 'callable' => 'custom_image_get', + * ]; + * + * // Remove a function. + * unset( $functions['bloginfo'] ); + * + * return $functions; + * } ); + * ``` + * + * @param array $functions + */ + $functions = \apply_filters('timber/twig/functions', $functions); + + return $functions; + } + + /** + * Adds Timber-specific functions to Twig. + * + * @param Environment $twig The Twig Environment. + * + * @return Environment + */ + public function add_timber_functions($twig) + { + foreach ($this->get_timber_functions() as $name => $function) { + $twig->addFunction( + new TwigFunction( + $name, + $function['callable'], + $function['options'] ?? [] + ) + ); + } + + return $twig; + } + + /** + * Get Timber default filters + * + * @return array Default Timber filters + */ + public function get_timber_filters() + { + $filters = [ + /* image filters */ + 'resize' => [ + 'callable' => [ImageHelper::class, 'resize'], + ], + 'retina' => [ + 'callable' => [ImageHelper::class, 'retina_resize'], + ], + 'letterbox' => [ + 'callable' => [ImageHelper::class, 'letterbox'], + ], + 'tojpg' => [ + 'callable' => [ImageHelper::class, 'img_to_jpg'], + ], + 'towebp' => [ + 'callable' => [ImageHelper::class, 'img_to_webp'], + ], + + // Debugging filters. + 'get_class' => [ + 'callable' => function ($obj) { + Helper::deprecated('{{ my_object | get_class }}', "{{ function('get_class', my_object) }}", '2.0.0'); + return $obj::class; + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: "{{ function('get_class', my_object) }}", + altPackage: null, + altVersion: null + ), + ], + ], + 'print_r' => [ + 'callable' => function ($arr) { + Helper::deprecated('{{ my_object | print_r }}', '{{ dump(my_object) }}', '2.0.0'); + return \print_r($arr, true); + }, + 'options' => [ + 'deprecation_info' => new DeprecatedCallableInfo( + package: 'timber/timber', + version: '2.0.0', + altName: '{{ dump(my_object) }}', + altPackage: null, + altVersion: null + ), + ], + ], + + // Other filters. + 'stripshortcodes' => [ + 'callable' => 'strip_shortcodes', + ], + 'array' => [ + 'callable' => [$this, 'to_array'], + ], + 'excerpt' => [ + 'callable' => 'wp_trim_words', + ], + 'excerpt_chars' => [ + 'callable' => [TextHelper::class, 'trim_characters'], + ], + 'function' => [ + 'callable' => [$this, 'exec_function'], + ], + 'pretags' => [ + 'callable' => [$this, 'twig_pretags'], + ], + 'sanitize' => [ + 'callable' => 'sanitize_title', + ], + 'shortcodes' => [ + 'callable' => 'do_shortcode', + ], + 'wpautop' => [ + 'callable' => 'wpautop', + ], + 'list' => [ + 'callable' => [$this, 'add_list_separators'], + ], + 'pluck' => [ + 'callable' => [Helper::class, 'pluck'], + ], + 'wp_list_filter' => [ + 'callable' => [Helper::class, 'wp_list_filter'], + ], + + 'relative' => [ + 'callable' => fn ($link) => URLHelper::get_rel_url($link, true), + ], + + /** + * Date and Time filters. + */ + 'date' => [ + 'callable' => [$this, 'twig_date_format_filter'], + 'options' => [ + 'needs_environment' => true, + ], + ], + 'time_ago' => [ + 'callable' => [DateTimeHelper::class, 'time_ago'], + ], + 'truncate' => [ + 'callable' => fn ($text, $len) => TextHelper::trim_words($text, $len), + ], + + // Numbers filters + 'size_format' => [ + 'callable' => 'size_format', + ], + + // Actions and filters. + 'apply_filters' => [ + 'callable' => function () { + $args = \func_get_args(); + $tag = \current(\array_splice($args, 1, 1)); + + return \apply_filters_ref_array($tag, $args); + }, + ], + ]; + + /** + * Filters the filters that are added to Twig. + * + * The `$filters` array is an associative array with the filter name as a key and an + * arguments array as the value. In the arguments array, you pass the function to call with + * a `callable` entry. + * + * This is an alternative filter that you can use instead of adding your filter in the + * `timber/twig` filter. + * + * @api + * @since 2.0.0 + * @example + * ```php + * add_filter( 'timber/twig/default_filters', function( $filters ) { + * // Add your own filter. + * $filters['price'] = [ + * 'callable' => 'format_price', + * ]; + * + * // Replace a filter. + * $filters['list'] = [ + * 'callable' => 'custom_list_filter', + * ]; + * + * // Remove a filter. + * unset( $filters['list'] ); + * + * return $filters; + * } ); + * ``` + * + * @param array $filters + */ + $filters = \apply_filters('timber/twig/filters', $filters); + + return $filters; + } + + /** + * Get Timber default filters + * + * @return array Default Timber filters + */ + public function get_timber_escaper_filters() + { + $escaper_filters = [ + 'esc_url' => [ + 'callable' => 'esc_url', + ], + 'wp_kses' => [ + 'callable' => 'wp_kses', + ], + 'wp_kses_post' => [ + 'callable' => 'wp_kses_post', + ], + 'esc_attr' => [ + 'callable' => 'esc_attr', + ], + 'esc_html' => [ + 'callable' => 'esc_html', + ], + 'esc_js' => [ + 'callable' => 'esc_js', + ], + ]; + + /** + * Filters the escaping filters that are added to Twig. + * + * The `$escaper_filters` array is an associative array with the filter name as a key and an + * arguments array as the value. In the arguments array, you pass the function to call with + * a `callable` entry. + * + * + * @api + * @since 2.1.0 + * @example + * ```php + * add_filter( 'timber/twig/escapers', function( $escaper_filters ) { + * // Add your own filter. + * $filters['esc_xml'] = [ + * 'callable' => 'esc_xml', + * 'options' => [ + * 'is_safe' => ['html'], + * ], + * ]; + * + * // Remove a filter. + * unset( $filters['esc_js'] ); + * + * return $filters; + * } ); + * ``` + * + * @param array $escaper_filters + */ + $escaper_filters = \apply_filters('timber/twig/escapers', $escaper_filters); + + return $escaper_filters; + } + + /** + * Adds filters to Twig. + * + * @param Environment $twig The Twig Environment. + * + * @return Environment + */ + public function add_timber_filters($twig) + { + foreach ($this->get_timber_filters() as $name => $function) { + $twig->addFilter( + new TwigFilter( + $name, + $function['callable'], + $function['options'] ?? [] + ) + ); + } + + return $twig; + } + + public function add_timber_escaper_filters($twig) + { + foreach ($this->get_timber_escaper_filters() as $name => $function) { + $twig->addFilter( + new TwigFilter( + $name, + $function['callable'], + $function['options'] ?? [ + 'is_safe' => ['html'], + ] + ) + ); + } + + return $twig; + } + + /** + * Adds escapers. + * + * @param Environment $twig The Twig Environment. + * @return Environment + */ + public function add_timber_escapers($twig) + { + $esc_url = fn (Environment $env, $string) => \esc_url($string); + + $wp_kses_post = fn (Environment $env, $string) => \wp_kses_post($string); + + $esc_html = fn (Environment $env, $string) => \esc_html($string); + + $esc_js = fn (Environment $env, $string) => \esc_js($string); + + if (\class_exists(EscaperRuntime::class)) { + $escaper_extension = $twig->getRuntime(EscaperRuntime::class); + $escaper_extension->setEscaper('esc_url', '\esc_url'); + $escaper_extension->setEscaper('wp_kses_post', '\wp_kses_post'); + $escaper_extension->setEscaper('esc_html', '\esc_html'); + $escaper_extension->setEscaper('esc_js', '\esc_js'); + } elseif ($twig->hasExtension(EscaperExtension::class)) { + $escaper_extension = $twig->getExtension(EscaperExtension::class); + $escaper_extension->setEscaper('esc_url', $esc_url); + $escaper_extension->setEscaper('wp_kses_post', $wp_kses_post); + $escaper_extension->setEscaper('esc_html', $esc_html); + $escaper_extension->setEscaper('esc_js', $esc_js); + } + + return $twig; + } + + /** + * Overwrite Twig defaults. + * + * Makes Twig compatible with how WordPress handles dates, timezones, numbers and perhaps other items in + * the future + * + * @since 2.0.0 + * + * @throws \Twig\Error\RuntimeError + * @param Environment $twig Twig Environment. + * + * @return Environment + */ + public function set_defaults(Environment $twig) + { + $twig->getExtension(CoreExtension::class)->setDateFormat(\get_option('date_format'), '%d days'); + $twig->getExtension(CoreExtension::class)->setTimezone(\wp_timezone_string()); + + /** @see https://developer.wordpress.org/reference/functions/number_format_i18n/ */ + global $wp_locale; + if (isset($wp_locale)) { + $twig->getExtension(CoreExtension::class)->setNumberFormat(0, $wp_locale->number_format['decimal_point'], $wp_locale->number_format['thousands_sep']); + } + + return $twig; + } + + /** + * Converts a date to the given format. + * + * @internal + * @since 2.0.0 + * @see twig_date_format_filter() + * @link https://twig.symfony.com/doc/2.x/filters/date.html + * + * @throws Exception + * + * @param Environment $env Twig Environment. + * @param null|string|int|DateTime $date A date. + * @param null|string $format Optional. PHP date format. Will return the + * current date as a DateTimeImmutable object by + * default. + * @param null $timezone Optional. The target timezone. Use `null` to use + * the default or + * `false` to leave the timezone unchanged. + * + * @return false|string A formatted date. + */ + public function twig_date_format_filter(Environment $env, $date = null, $format = null, $timezone = null) + { + // Support for DateInterval. + if ($date instanceof DateInterval) { + if (null === $format) { + $format = $env->getExtension(CoreExtension::class)->getDateFormat()[1]; + } + + return $date->format($format); + } + + if (null === $date || 'now' === $date) { + return DateTimeHelper::wp_date($format, null); + } + + /** + * If a string is given and it’s not a timestamp (e.g. "2010-01-28T15:00:00+04:00", try creating a DateTime + * object and read the timezone from that string. + */ + if (\is_string($date) && !\ctype_digit($date)) { + $date_obj = \date_create($date); + + if ($date_obj) { + $date = $date_obj; + } + } + + /** + * Check for `false` parameter in |date filter in Twig + * + * @link https://twig.symfony.com/doc/2.x/filters/date.html#timezone + */ + if (false === $timezone && $date instanceof DateTimeInterface) { + $timezone = $date->getTimezone(); + } + + return DateTimeHelper::wp_date($format, $date, $timezone); + } + + /** + * + * + * @return array + */ + public function to_array(mixed $arr) + { + if (\is_array($arr)) { + return $arr; + } + $arr = [$arr]; + return $arr; + } + + /** + * + * + * @param string $function_name + * @return mixed + */ + public function exec_function($function_name) + { + $args = \func_get_args(); + \array_shift($args); + if (\is_string($function_name)) { + $function_name = \trim($function_name); + } + return \call_user_func_array($function_name, ($args)); + } + + /** + * + * + * @param string $content + * @return string + */ + public function twig_pretags($content) + { + return \preg_replace_callback('|(.*) $item) { + if ($index < $length - 2) { + $delimiter = $first_delimiter . ' '; + } elseif ($index == $length - 2) { + $delimiter = $second_delimiter . ' '; + } else { + $delimiter = ''; + } + $list = $list . $item . $delimiter; + } + return $list; + } +} diff --git a/vendor/timber/timber/src/URLHelper.php b/vendor/timber/timber/src/URLHelper.php new file mode 100644 index 0000000..b4a0913 --- /dev/null +++ b/vendor/timber/timber/src/URLHelper.php @@ -0,0 +1,614 @@ + ["blog", "post", "news", "2014", "whatever"] + * + * $third = URLHelper::get_params(2); + * // => "news" + * + * // get_params() supports negative indices: + * $last = URLHelper::get_params(-1); + * // => "whatever" + * + * $nada = URLHelper::get_params(99); + * // => false + * ``` + * + * @api + * @param boolean|int $i the position of the parameter to grab. + * @return array|string|false + */ + public static function get_params($i = false) + { + $uri = \trim((string) $_SERVER['REQUEST_URI']); + $params = \array_values(\array_filter(\explode('/', $uri))); + + if (false === $i) { + return $params; + } + + // Support negative indices. + if ($i < 0) { + $i = \count($params) + $i; + } + + return $params[$i] ?? false; + } + + /** + * Secures an URL based on the current environment. + * + * @param string $url The URL to evaluate. + * + * @return string An URL with or without http/https, depending on what’s appropriate for server. + */ + public static function maybe_secure_url($url) + { + if (\is_ssl() && !\str_starts_with($url, 'https') && \str_starts_with($url, 'http')) { + $url = 'https' . \substr($url, \strlen('http')); + } + + return $url; + } +} diff --git a/vendor/timber/timber/src/User.php b/vendor/timber/timber/src/User.php new file mode 100644 index 0000000..12dd115 --- /dev/null +++ b/vendor/timber/timber/src/User.php @@ -0,0 +1,512 @@ +Your name is {{ user.name }}

    + * + * ``` + * ```html + * + * + * ``` + */ +class User extends CoreEntity implements Stringable +{ + /** + * The underlying WordPress Core object. + * + * @since 2.0.0 + * + * @var WP_User|null + */ + protected ?WP_User $wp_object = null; + + public $object_type = 'user'; + + public static $representation = 'user'; + + public $_link; + + /** + * @api + * @var string A URL to an avatar that overrides anything from Gravatar, etc. + */ + public $avatar_override; + + /** + * @api + * @var int The ID from WordPress + */ + public $id; + + /** + * @api + * @var string + */ + public $user_nicename; + + /** + * User email address. + * + * @api + * @var string + */ + public $user_email; + + /** + * The roles the user is part of. + * + * @api + * @since 1.8.5 + * + * @var array + */ + protected $roles; + + /** + * Construct a User object. For internal use only: Do not call directly. + * Call `Timber::get_user()` instead. + * + * @internal + */ + protected function __construct() + { + } + + /** + * Build a new User object. + */ + public static function build(WP_User $wp_user): static + { + $user = new static(); + $user->init($wp_user); + + return $user; + } + + /** + * @api + * @example + * ```twig + * This post is by {{ post.author }} + * ``` + * ```html + * This post is by Jared Novack + * ``` + * + * @return string a fallback for Timber\User::name() + */ + public function __toString() + { + return $this->name(); + } + + /** + * @internal + */ + protected function init($wp_user) + { + $this->wp_object = $wp_user; + + $data = \get_userdata($wp_user->ID); + if (!isset($data->data)) { + return; + } + $this->import($data->data); + + if (isset($data->roles)) { + $this->roles = $this->get_roles($data->roles); + } + + // Never leak password data + unset($this->user_pass); + $this->id = $this->ID = (int) $wp_user->ID; + } + + /** + * Gets the underlying WordPress Core object. + * + * @since 2.0.0 + * + * @return WP_User|null + */ + public function wp_object(): ?WP_User + { + return $this->wp_object; + } + + /** + * Get the URL of the user's profile + * + * @api + * @return string https://example.org/author/lincoln + */ + public function link() + { + if (!$this->_link) { + $this->_link = \user_trailingslashit(\get_author_posts_url($this->ID)); + } + return $this->_link; + } + + /** + * Gets a user meta value. + * + * @api + * @deprecated 2.0.0, use `{{ user.meta('field_name') }}` instead. + * @see \Timber\User::meta() + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_field($field_name = null) + { + Helper::deprecated( + "{{ user.get_field('field_name') }}", + "{{ user.meta('field_name') }}", + '2.0.0' + ); + + return $this->meta($field_name); + } + + /** + * Check if the user object is the current user + * + * @api + * + * @return bool true if the user is the current user + */ + public function is_current(): bool + { + return \get_current_user_id() === $this->ID; + } + + /** + * Get the name of the User + * + * @api + * @return string the human-friendly name of the user (ex: "Buster Bluth") + */ + public function name() + { + /** + * Filters the name of a user. + * + * @since 1.1.4 + * + * @param string $name The name of the user. Default `display_name`. + * @param User $user The user object. + */ + return \apply_filters('timber/user/name', $this->display_name, $this); + } + + /** + * Get the relative path to the user's profile + * + * @api + * @return string ex: /author/lincoln + */ + public function path() + { + return URLHelper::get_rel_url($this->link()); + } + + /** + * @api + * @return string ex baberaham-lincoln + */ + public function slug() + { + return $this->user_nicename; + } + + /** + * Gets a user meta value. + * + * @api + * @deprecated 2.0.0, use `{{ user.meta('field_name') }}` instead. + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_meta_field($field_name) + { + Helper::deprecated( + "{{ user.get_meta_field('field_name') }}", + "{{ user.meta('field_name') }}", + '2.0.0' + ); + + return $this->meta($field_name); + } + + /** + * Gets a user meta value. + * + * @api + * @deprecated 2.0.0, use `{{ user.meta('field_name') }}` instead. + * + * @param string $field_name The field name for which you want to get the value. + * @return mixed The meta field value. + */ + public function get_meta($field_name) + { + Helper::deprecated( + "{{ user.get_meta('field_name') }}", + "{{ user.meta('field_name') }}", + '2.0.0' + ); + return $this->meta($field_name); + } + + /** + * Creates an associative array with user role slugs and their translated names. + * + * @internal + * @since 1.8.5 + * @param array $roles user roles. + * @return array|null + */ + protected function get_roles($roles) + { + if (empty($roles)) { + // @codeCoverageIgnoreStart + return null; + // @codeCoverageIgnoreEnd + } + + $wp_roles = \wp_roles(); + $names = $wp_roles->get_names(); + + $values = []; + + foreach ($roles as $role) { + $name = $role; + if (isset($names[$role])) { + $name = \translate_user_role($names[$role]); + } + $values[$role] = $name; + } + + return $values; + } + + /** + * Gets the user roles. + * Roles shouldn’t be used to check whether a user has a capability. Use roles only for + * displaying purposes. For example, if you want to display the name of the subscription a user + * has on the site behind a paywall. + * + * If you want to check for capabilities, use `{{ user.can('capability') }}`. If you only want + * to check whether a user is logged in, you can use `{% if user %}`. + * + * @api + * @since 1.8.5 + * @example + * ```twig + *

    Role name

    + * {% for role in post.author.roles %} + * {{ role }} + * {% endfor %} + * ``` + * ```twig + *

    Role name

    + * {{ post.author.roles|join(', ') }} + * ``` + * ```twig + * {% for slug, name in post.author.roles %} + * {{ slug }} + * {% endfor %} + * ``` + * + * @return array|null + */ + public function roles() + { + return $this->roles; + } + + /** + * Gets the profile link to the user’s profile in the WordPress admin if the ID in the user object + * is the same as the current user’s ID. + * + * @api + * @since 2.1.0 + * @example + * + * Get the profile URL for the current user: + * + * ```twig + * {% if user.profile_link %} + * My profile + * {% endif %} + * ``` + * @return string|null The profile link for the current user. + */ + public function profile_link(): ?string + { + if (!$this->is_current()) { + return null; + } + + return \get_edit_profile_url($this->ID); + } + + /** + * Checks whether a user has a capability. + * + * Don’t use role slugs for capability checks. While checking against a role in place of a + * capability is supported in part, this practice is discouraged as it may produce unreliable + * results. This includes cases where you want to check whether a user is registered. If you + * want to check whether a user is a Subscriber, use `{{ user.can('read') }}`. If you only want + * to check whether a user is logged in, you can use `{% if user %}`. + * + * @api + * @since 1.8.5 + * + * @param string $capability The capability to check. + * @param mixed ...$args Additional arguments to pass to the user_can function + * + * @example + * Give moderation users another CSS class to style them differently. + * + * ```twig + * + * {{ comment.author.name }} + * + * ``` + * + * @example + * ```twig + * {# Show edit link for posts that a user can edit. #} + * {% if user.can('edit_post', post.id) %} + * Edit Post + * {% endif %} + * + * {% if user.can('edit_term', term.id) %} + * {# do something with privileges #} + * {% endif %} + * + * {% if user.can('edit_user', user.id) %} + * {# do something with privileges #} + * {% endif %} + * + * {% if user.can('edit_comment', comment.id) %} + * {# do something with privileges #} + * {% endif %} + * ``` + * + * @return bool Whether the user has the capability. + */ + public function can($capability, mixed ...$args) + { + return \user_can($this->wp_object, $capability, ...$args); + } + + /** + * Checks whether the current user can edit the post. + * + * @api + * @example + * ```twig + * {% if user.can_edit %} + * Edit + * {% endif %} + * ``` + * @return bool + */ + public function can_edit(): bool + { + return \current_user_can('edit_user', $this->ID); + } + + /** + * Gets the edit link for a user if the current user has the correct rights or the profile link for the current + * user. + * + * @api + * @since 2.0.0 + * @example + * ```twig + * {% if user.can_edit %} + * Edit + * {% endif %} + * ``` + * + * Get the profile URL for the current user: + * + * ```twig + * {# Assuming user is the current user. #} + * {% if user %} + * My profile + * {% endif %} + * ``` + * @return string|null The edit URL of a user in the WordPress admin or the profile link if the user object is for + * the current user. Null if the current user can’t edit the user. + */ + public function edit_link(): ?string + { + if (!$this->can_edit()) { + return null; + } + + return \get_edit_user_link($this->ID); + } + + /** + * Gets a user’s avatar URL. + * + * @api + * @since 1.9.1 + * @example + * Get a user avatar with a width and height of 150px: + * + * ```twig + * + * ``` + * + * @param null|array $args Parameters for + * [`get_avatar_url()`](https://developer.wordpress.org/reference/functions/get_avatar_url/). + * @return string The avatar URL. + */ + public function avatar($args = null) + { + if ($this->avatar_override) { + return $this->avatar_override; + } + + return \get_avatar_url($this->id, $args); + } +} diff --git a/vendor/twig/twig/.editorconfig b/vendor/twig/twig/.editorconfig deleted file mode 100644 index 270f1d1..0000000 --- a/vendor/twig/twig/.editorconfig +++ /dev/null @@ -1,18 +0,0 @@ -; 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/vendor/twig/twig/.gitattributes b/vendor/twig/twig/.gitattributes deleted file mode 100644 index 75e18f8..0000000 --- a/vendor/twig/twig/.gitattributes +++ /dev/null @@ -1,3 +0,0 @@ -/extra/** export-ignore -/tests export-ignore -/phpunit.xml.dist export-ignore diff --git a/vendor/twig/twig/.gitignore b/vendor/twig/twig/.gitignore deleted file mode 100644 index cd52aea..0000000 --- a/vendor/twig/twig/.gitignore +++ /dev/null @@ -1,4 +0,0 @@ -/composer.lock -/phpunit.xml -/vendor -.phpunit.result.cache diff --git a/vendor/twig/twig/.php_cs.dist b/vendor/twig/twig/.php_cs.dist deleted file mode 100644 index b81882f..0000000 --- a/vendor/twig/twig/.php_cs.dist +++ /dev/null @@ -1,20 +0,0 @@ -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'], - 'native_function_invocation' => ['include' => ['@compiler_optimized'], 'scope' => 'all'], - ]) - ->setRiskyAllowed(true) - ->setFinder(PhpCsFixer\Finder::create()->in(__DIR__)) -; diff --git a/vendor/twig/twig/.travis.yml b/vendor/twig/twig/.travis.yml deleted file mode 100644 index 3e7bb81..0000000 --- a/vendor/twig/twig/.travis.yml +++ /dev/null @@ -1,45 +0,0 @@ -language: php - -cache: - directories: - - vendor - - extra/*/vendor - - $HOME/.composer/cache/files - -env: - global: - - SYMFONY_PHPUNIT_REMOVE_RETURN_TYPEHINT=1 - - SYMFONY_PHPUNIT_DISABLE_RESULT_CACHE=1 - -before_install: - - phpenv config-rm xdebug.ini || return 0 - -install: - - travis_retry composer install - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/cssinliner-extra && travis_retry composer install) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/html-extra && travis_retry composer install) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/inky-extra && travis_retry composer install) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/intl-extra && travis_retry composer install) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/markdown-extra && travis_retry composer install) - - ([[ $TRAVIS_PHP_VERSION < 7.2 ]] || cd extra/string-extra && travis_retry composer install) - -script: - - ./vendor/bin/simple-phpunit - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/cssinliner-extra && ./vendor/bin/simple-phpunit) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/html-extra && ./vendor/bin/simple-phpunit) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/inky-extra && ./vendor/bin/simple-phpunit) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/intl-extra && ./vendor/bin/simple-phpunit) - - ([[ $TRAVIS_PHP_VERSION = 7.0 ]] || cd extra/markdown-extra && ./vendor/bin/simple-phpunit) - - ([[ $TRAVIS_PHP_VERSION < 7.2 ]] || cd extra/string-extra && ./vendor/bin/simple-phpunit) - -jobs: - fast_finish: true - include: - - php: 7.0 - - php: 7.1 - - php: 7.2 - - php: 7.3 - - php: 7.4 - - stage: integration tests - php: 7.3 - script: ./drupal_test.sh diff --git a/vendor/twig/twig/CHANGELOG b/vendor/twig/twig/CHANGELOG index 1c89d24..cf202b0 100644 --- a/vendor/twig/twig/CHANGELOG +++ b/vendor/twig/twig/CHANGELOG @@ -1,1444 +1,456 @@ -* 2.12.5 (2020-02-11) +# 3.21.1 (2025-05-03) + + * Fix ExtensionSet usage of BinaryOperatorExpressionParser + +# 3.21.0 (2025-05-02) + + * Fix wrong array index + * Deprecate `Template::loadTemplate()` + * Fix testing and expression when it evaluates to an instance of `Markup` + * Add `ReturnPrimitiveTypeInterface` (and sub-interfaces for number, boolean, string, and array) + * Add `SupportDefinedTestInterface` for expression nodes supporting the `defined` test + * Deprecate using the `|` operator in an expression with `+` or `-` without using parentheses to clarify precedence + * Deprecate operator precedence outside of the [0, 512] range + * Introduce expression parser classes to describe operators and operands provided by extensions + instead of arrays (it comes with many deprecations that are documented in + the ``deprecated`` documentation chapter) + * Deprecate the `Twig\ExpressionParser`, and `Twig\OperatorPrecedenceChange` classes + * Add attributes `AsTwigFilter`, `AsTwigFunction`, and `AsTwigTest` to ease extension development + +# 3.20.0 (2025-02-13) + + * Fix support for ignoring syntax errors in an undefined handler in guard + * Add configuration for Commonmark + * Fix wrong array index + * Bump minimum PHP version to 8.1 + * Add support for registering callbacks for undefined functions, filters or token parsers in the IntegrationTestCase + * Use correct line number for `ForElseNode` + * Fix timezone conversion on strings + +# 3.19.0 (2025-01-28) + + * Fix a security issue where escaping was missing when using `??` + * Deprecate `Token::getType()`, use `Token::test()` instead + * Add `Token::toEnglish()` + * Add `ForElseNode` + * Deprecate `Twig\ExpressionParser::parseOnlyArguments()` and + `Twig\ExpressionParser::parseArguments()` (use + `Twig\ExpressionParser::parseNamedArguments()` instead) + * Fix `constant()` behavior when used with `??` + * Add the `invoke` filter + * Make `{}` optional for the `types` tag + * Add `LastModifiedExtensionInterface` and implementation in `AbstractExtension` to track modification of runtime classes + * Ignore static properties when using the dot operator + +# 3.18.0 (2024-12-29) + + * Fix unary operator precedence change + * Ignore `SyntaxError` exceptions from undefined handlers when using the `guard` tag + * Add a way to stream template rendering (`TemplateWrapper::stream()` and `TemplateWrapper::streamBlock()`) + +# 3.17.1 (2024-12-12) + + * Fix the null coalescing operator when the test returns null + * Fix the Elvis operator when used as '? :' instead of '?:' + * Support for invoking closures + +# 3.17.0 (2024-12-10) + + * Fix ArrayAccess with objects as keys + * Support underscores in number literals + * Deprecate `ConditionalExpression` and `NullCoalesceExpression` (use `ConditionalTernary` and `NullCoalesceBinary` instead) + +# 3.16.0 (2024-11-29) + + * Deprecate `InlinePrint` + * Fix having macro variables starting with an underscore + * Deprecate not passing a `Source` instance to `TokenStream` + * Deprecate returning `null` from `TwigFilter::getSafe()` and `TwigFunction::getSafe()`, return `[]` instead + +# 3.15.0 (2024-11-17) + + * [BC BREAK] Add support for accessing class constants with the dot operator; + this can be a BC break if you don't use UPPERCASE constant names + * Add Spanish inflector support for the `plural` and `singular` filters in the String extension + * Deprecate `TempNameExpression` in favor of `LocalVariable` + * Deprecate `NameExpression` in favor of `ContextVariable` + * Deprecate `AssignNameExpression` in favor of `AssignContextVariable` + * Remove `MacroAutoImportNodeVisitor` + * Deprecate `MethodCallExpression` in favor of `MacroReferenceExpression` + * Fix support for the "is defined" test on `_self.xxx` (auto-imported) macros + * Fix support for the "is defined" test on inherited macros + * Add named arguments support for the dot operator arguments (`foo.bar(some: arg)`) + * Add named arguments support for macros + * Add a new `guard` tag that allows to test if some Twig callables are available at compilation time + * Allow arrow functions everywhere + * Deprecate passing a string or an array to Twig callable arguments accepting arrow functions (pass a `\Closure`) + * Add support for triggering deprecations for future operator precedence changes + * Deprecate using the `not` unary operator in an expression with ``*``, ``/``, ``//``, or ``%`` without using explicit parentheses to clarify precedence + * Deprecate using the `??` binary operator without explicit parentheses + * Deprecate using the `~` binary operator in an expression with `+` or `-` without using parentheses to clarify precedence + * Deprecate not passing `AbstractExpression` args to most constructor arguments for classes extending `AbstractExpression` + * Fix `power` expressions with a negative number in parenthesis (`(-1) ** 2`) + * Deprecate instantiating `Node` directly. Use `EmptyNode` or `Nodes` instead. + * Add support for inline comments + * Add `Profile::getStartTime()` and `Profile::getEndTime()` + * Fix "ignore missing" when used on an "embed" tag + * Fix the possibility to override an aliased block (via use) + * Add template cache hot reload + * Allow Twig callable argument names to be free-form (snake-case or camelCase) independently of the PHP callable signature + They were automatically converted to snake-cased before + * Deprecate the `attribute` function; use the `.` notation and wrap the name with parenthesis instead + * Add support for argument unpackaging + * Add JSON support for the file extension escaping strategy + * Support Markup instances (and any other \Stringable) as dynamic mapping keys + * Deprecate the `sandbox` tag + * Improve the way one can deprecate a Twig callable (use `deprecation_info` instead of the other callable options) + * Add the `enum` function + * Add support for logical `xor` operator + +# 3.14.2 (2024-11-07) + + * Fix an infinite recursion in the sandbox code + +# 3.14.1 (2024-11-06) + + * [BC BREAK] Fix a security issue in the sandbox mode allowing an attacker to call attributes on Array-like objects + They are now checked via the property policy + * Fix a security issue in the sandbox mode allowing an attacker to be able to call `toString()` + under some circumstances on an object even if the `__toString()` method is not allowed by the security policy + +# 3.14.0 (2024-09-09) + + * Fix a security issue when an included sandboxed template has been loaded before without the sandbox context + * Add the possibility to reset globals via `Environment::resetGlobals()` + * Deprecate `Environment::mergeGlobals()` + +# 3.13.0 (2024-09-07) + + * Add the `types` tag (experimental) + * Deprecate the `Twig\Test\NodeTestCase::getTests()` data provider, override `provideTests()` instead. + * Mark `Twig\Test\NodeTestCase::getEnvironment()` as final, override `createEnvironment()` instead. + * Deprecate `Twig\Test\NodeTestCase::getVariableGetter()`, call `createVariableGetter()` instead. + * Deprecate `Twig\Test\NodeTestCase::getAttributeGetter()`, call `createAttributeGetter()` instead. + * Deprecate not overriding `Twig\Test\IntegrationTestCase::getFixturesDirectory()`, this method will be abstract in 4.0 + * Marked `Twig\Test\IntegrationTestCase::getTests()` and `getLegacyTests()` as final + +# 3.12.0 (2024-08-29) + + * Deprecate the fact that the `extends` and `use` tags are always allowed in a sandboxed template. + This behavior will change in 4.0 where these tags will need to be explicitly allowed like any other tag. + * Deprecate the "tag" constructor argument of the "Twig\Node\Node" class as the tag is now automatically set by the Parser when needed + * Fix precedence of two-word tests when the first word is a valid test + * Deprecate the `spaceless` filter + * Deprecate some internal methods from `Parser`: `getBlockStack()`, `hasBlock()`, `getBlock()`, `hasMacro()`, `hasTraits()`, `getParent()` + * Deprecate passing `null` to `Twig\Parser::setParent()` + * Update `Node::__toString()` to include the node tag if set + * Add support for integers in methods of `Twig\Node\Node` that take a Node name + * Deprecate not passing a `BodyNode` instance as the body of a `ModuleNode` or `MacroNode` constructor + * Deprecate returning "null" from "TokenParserInterface::parse()". + * Deprecate `OptimizerNodeVisitor::OPTIMIZE_TEXT_NODES` + * Fix performance regression when `use_yield` is `false` (which is the default) + * Improve compatibility when `use_yield` is `false` (as extensions still using `echo` will work as is) + * Accept colons (`:`) in addition to equals (`=`) to separate argument names and values in named arguments + * Add the `html_cva` function (in the HTML extra package) + * Add support for named arguments to the `block` and `attribute` functions + * Throw a SyntaxError exception at compile time when a Twig callable has not the minimum number of required arguments + * Add a `CallableArgumentsExtractor` class + * Deprecate passing a name to `FunctionExpression`, `FilterExpression`, and `TestExpression`; + pass a `TwigFunction`, `TwigFilter`, or `TestFilter` instead + * Deprecate all Twig callable attributes on `FunctionExpression`, `FilterExpression`, and `TestExpression` + * Deprecate the `filter` node of `FilterExpression` + * Add the notion of Twig callables (functions, filters, and tests) + * Bump minimum PHP version to 8.0 + * Fix integration tests when a test has more than one data/expect section and deprecations + * Add the `enum_cases` function + +# 3.11.2 (2024-11-06) + + * [BC BREAK] Fix a security issue in the sandbox mode allowing an attacker to call attributes on Array-like objects + They are now checked via the property policy + * Fix a security issue in the sandbox mode allowing an attacker to be able to call `toString()` + under some circumstances on an object even if the `__toString()` method is not allowed by the security policy + +# 3.11.1 (2024-09-10) + + * Fix a security issue when an included sandboxed template has been loaded before without the sandbox context + +# 3.11.0 (2024-08-08) + + * Deprecate `OptimizerNodeVisitor::OPTIMIZE_RAW_FILTER` + * Add `Twig\Cache\ChainCache` and `Twig\Cache\ReadOnlyFilesystemCache` + * Add the possibility to deprecate attributes and nodes on `Node` + * Add the possibility to add a package and a version to the `deprecated` tag + * Add the possibility to add a package for filter/function/test deprecations + * Mark `ConstantExpression` as being `@final` + * Add the `find` filter + * Fix optimizer mode validation in `OptimizerNodeVisitor` + * Add the possibility to yield from a generator in `PrintNode` + * Add the `shuffle` filter + * Add the `singular` and `plural` filters in `StringExtension` + * Deprecate the second argument of `Twig\Node\Expression\CallExpression::compileArguments()` + * Deprecate `Twig\ExpressionParser\parseHashExpression()` in favor of + `Twig\ExpressionParser::parseMappingExpression()` + * Deprecate `Twig\ExpressionParser\parseArrayExpression()` in favor of + `Twig\ExpressionParser::parseSequenceExpression()` + * Add `sequence` and `mapping` tests + * Deprecate `Twig\Node\Expression\NameExpression::isSimple()` and + `Twig\Node\Expression\NameExpression::isSpecial()` + +# 3.10.3 (2024-05-16) + + * Fix missing ; in generated code + +# 3.10.2 (2024-05-14) + + * Fix support for the deprecated escaper signature + +# 3.10.1 (2024-05-12) + + * Fix BC break on escaper extension + * Fix constant return type + +# 3.10.0 (2024-05-11) + + * Make `CoreExtension::formatDate`, `CoreExtension::convertDate`, and + `CoreExtension::formatNumber` part of the public API + * Add `needs_charset` option for filters and functions + * Extract the escaping logic from the `EscaperExtension` class to a new + `EscaperRuntime` class. + + The following methods from ``Twig\\Extension\\EscaperExtension`` are + deprecated: ``setEscaper()``, ``getEscapers()``, ``setSafeClasses``, + ``addSafeClasses()``. Use the same methods on the + ``Twig\\Runtime\\EscaperRuntime`` class instead. + * Fix capturing output from extensions that still use echo + * Fix a PHP warning in the Lexer on malformed templates + * Fix blocks not available under some circumstances + * Synchronize source context in templates when setting a Node on a Node + +# 3.9.3 (2024-04-18) + + * Add missing `twig_escape_filter_is_safe` deprecated function + * Fix yield usage with CaptureNode + * Add missing unwrap call when using a TemplateWrapper instance internally + * Ensure Lexer is initialized early on + +# 3.9.2 (2024-04-17) + + * Fix usage of display_end hook + +# 3.9.1 (2024-04-17) + + * Fix missing `$blocks` variable in `CaptureNode` + +# 3.9.0 (2024-04-16) + + * Add support for PHP 8.4 + * Deprecate AbstractNodeVisitor + * Deprecate passing Template to Environment::resolveTemplate(), Environment::load(), and Template::loadTemplate() + * Add a new "yield" mode for output generation; + Node implementations that use "echo" or "print" should use "yield" instead; + all Node implementations should be flagged with `#[YieldReady]` once they've been made ready for "yield"; + the "use_yield" Environment option can be turned on when all nodes have been made `#[YieldReady]`; + "yield" will be the only strategy supported in the next major version + * Add return type for Symfony 7 compatibility + * Fix premature loop exit in Security Policy lookup of allowed methods/properties + * Deprecate all internal extension functions in favor of methods on the extension classes + * Mark all extension functions as @internal + * Add SourcePolicyInterface to selectively enable the Sandbox based on a template's Source + * Throw a proper Twig exception when using cycle on an empty array + +# 3.8.0 (2023-11-21) + + * Catch errors thrown during template rendering + * Fix IntlExtension::formatDateTime use of date formatter prototype + * Fix premature loop exit in Security Policy lookup of allowed methods/properties + * Remove NumberFormatter::TYPE_CURRENCY (deprecated in PHP 8.3) + * Restore return type annotations + * Allow Symfony 7 packages to be installed + * Deprecate `twig_test_iterable` function. Use the native `is_iterable` instead. + +# 3.7.1 (2023-08-28) + + * Fix some phpdocs + +# 3.7.0 (2023-07-26) + + * Add support for the ...spread operator on arrays and hashes + +# 3.6.1 (2023-06-08) + + * Suppress some native return type deprecation messages + +# 3.6.0 (2023-05-03) + + * Allow psr/container 2.0 + * Add the new PHP 8.0 IntlDateFormatter::RELATIVE_* constants for date formatting + * Make the Lexer initialize itself lazily + +# 3.5.1 (2023-02-08) + + * Arrow functions passed to the "reduce" filter now accept the current key as a third argument + * Restores the leniency of the matches twig comparison + * Fix error messages in sandboxed mode for "has some" and "has every" + +# 3.5.0 (2022-12-27) + + * Make Twig\ExpressionParser non-internal + * Add "has some" and "has every" operators + * Add Compile::reset() + * Throw a better runtime error when the "matches" regexp is not valid + * Add "twig *_names" intl functions + * Fix optimizing closures callbacks + * Add a better exception when getting an undefined constant via `constant` + * Fix `if` nodes when outside of a block and with an empty body + +# 3.4.3 (2022-09-28) + + * Fix a security issue on filesystem loader (possibility to load a template outside a configured directory) + +# 3.4.2 (2022-08-12) + + * Allow inherited magic method to still run with calling class + * Fix CallExpression::reflectCallable() throwing TypeError + * Fix typo in naming (currency_code) + +# 3.4.1 (2022-05-17) + +* Fix optimizing non-public named closures + +# 3.4.0 (2022-05-22) + + * Add support for named closures + +# 3.3.10 (2022-04-06) + + * Enable bytecode invalidation when auto_reload is enabled + +# 3.3.9 (2022-03-25) + + * Fix custom escapers when using multiple Twig environments + * Add support for "constant('class', object)" + * Do not reuse internally generated variable names during parsing + +# 3.3.8 (2022-02-04) + + * Fix a security issue when in a sandbox: the `sort` filter must require a Closure for the `arrow` parameter + * Fix deprecation notice on `round` + * Fix call to deprecated `convertToHtml` method + +# 3.3.7 (2022-01-03) + +* Allow more null support when Twig expects a string (for better 8.1 support) +* Only use Commonmark extensions if markdown enabled + +# 3.3.6 (2022-01-03) + +* Only use Commonmark extensions if markdown enabled + +# 3.3.5 (2022-01-03) + +* Allow CommonMark extensions to easily be added +* Allow null when Twig expects a string (for better 8.1 support) +* Make some performance optimizations +* Allow Symfony translation contract v3+ + +# 3.3.4 (2021-11-25) + + * Bump minimum supported Symfony component versions + * Fix a deprecated message + +# 3.3.3 (2021-09-17) + + * Allow Symfony 6 + * Improve compatibility with PHP 8.1 + * Explicitly specify the encoding for mb_ord in JS escaper + +# 3.3.2 (2021-05-16) + + * Revert "Throw a proper exception when a template name is an absolute path (as it has never been supported)" + +# 3.3.1 (2021-05-12) + + * Fix PHP 8.1 compatibility + * Throw a proper exception when a template name is an absolute path (as it has never been supported) + +# 3.3.0 (2021-02-08) + + * Fix macro calls in a "cache" tag + * Add the slug filter + * Allow extra bundle to be compatible with Twig 2 + +# 3.2.1 (2021-01-05) + + * Fix extra bundle compat with older versions of Symfony + +# 3.2.0 (2021-01-05) + + * Add the Cache extension in the "extra" repositories: "cache" tag + * Add "registerUndefinedTokenParserCallback" + * Mark built-in node visitors as @internal + * Fix "odd" not working for negative numbers + +# 3.1.1 (2020-10-27) + + * Fix "include(template_from_string())" + +# 3.1.0 (2020-10-21) + + * Fix sandbox support when using "include(template_from_string())" + * Make round brackets optional for one argument tests like "same as" or "divisible by" + * Add support for ES2015 style object initialisation shortcut { a } is the same as { 'a': a } + +# 3.0.5 (2020-08-05) + + * Fix twig_compare w.r.t. whitespace trimming + * Fix sandbox not disabled if syntax error occurs within {% sandbox %} tag + * Fix a regression when not using a space before an operator + * Restrict callables to closures in filters + * Allow trailing commas in argument lists (in calls as well as definitions) + +# 3.0.4 (2020-07-05) + + * Fix comparison operators + * Fix options not taken into account when using "Michelf\MarkdownExtra" + * Fix "Twig\Extra\Intl\IntlExtension::getCountryName()" to accept "null" as a first argument + * Throw exception in case non-Traversable data is passed to "filter" + * Fix context optimization on PHP 7.4 + * Fix PHP 8 compatibility + * Fix ambiguous syntax parsing + +# 3.0.3 (2020-02-11) * Add a check to ensure that iconv() is defined -* 2.12.4 (2020-02-11) +# 3.0.2 (2020-02-11) * Avoid exceptions when an intl resource is not found * Fix implementation of case-insensitivity for method names -* 2.12.3 (2019-12-28) +# 3.0.1 (2019-12-28) * fixed Symfony 5.0 support for the HTML extra extension + +# 3.0.0 (2019-11-15) + * fixed number formatter in Intl extra extension when using a formatter prototype -* 2.12.2 (2019-11-11) - - * added supported for exponential numbers - -* 2.12.1 (2019-10-17) - - * added the String extension in the "extra" repositories: "u" filter - -* 2.12.0 (2019-10-05) - - * added the spaceship operator ("<=>"), useful when using an arrow function in the "sort" filter - * added support for an "arrow" function on the "sort" filter - * added the CssInliner extension in the "extra" repositories: "inline_css" - filter - * added the Inky extension in the "extra" repositories: "inky_to_html" filter - * added Intl extension in the "extra" repositories: "country_name", - "currency_name", "currency_symbol", "language_name", "locale_name", - "timezone_name", "format_currency", "format_number", - "format_*_number", "format_datetime", "format_date", and "format_time" - filters, and the "country_timezones" function - * added the Markdown extension in the "extra" repositories: "markdown_to_html", - and "html_to_markdown" filters - * added the HtmlExtension extension in the "extra" repositories: "date_uri" - filter, and "html_classes" function - * optimized "block('foo') ?? 'bar'" - * fixed the empty test on Traversable instances - * fixed array_key_exists() on objects - * fixed cache when opcache is installed but disabled - * fixed using macros in arrow functions - * fixed split filter on edge case - -* 2.11.3 (2019-06-18) - - * display partial output (PHP buffer) when an error occurs in debug mode - * fixed the filter filter (allow the result to be used several times) - * fixed macro auto-import when a template contains only macros - -* 2.11.2 (2019-06-05) - - * fixed macro auto-import - -* 2.11.1 (2019-06-04) - - * added support for "Twig\Markup" instances in the "in" test (again) - * allowed string operators as variables names in assignments - * fixed support for macros defined in parent templates - -* 2.11.0 (2019-05-31) - - * added the possibility to register classes/interfaces as being safe for the escaper ("EscaperExtension::addSafeClass()") - * deprecated CoreExtension::setEscaper() and CoreExtension::getEscapers() in favor of the same methods on EscaperExtension - * macros are now auto-imported in the template they are defined (under the ``_self`` variable) - * added support for macros on "is defined" tests - * fixed macros "import" when using the same name in the parent and child templates - * fixed recursive macros - * macros imported "globally" in a template are now available in macros without re-importing them - * fixed the "filter" filter when the argument is \Traversable but does not implement \Iterator (\SimpleXmlElement for instance) - * fixed a PHP fatal error when calling a macro imported in a block in a nested block - * fixed a PHP fatal error when calling a macro imported in the template in another macro - * fixed wrong error message on "import" and "from" - -* 2.10.0 (2019-05-14) - - * deprecated "if" conditions on "for" tags - * added "filter", "map", and "reduce" filters (and support for arrow functions) - * fixed partial output leak when a PHP fatal error occurs - * optimized context access on PHP 7.4 - -* 2.9.0 (2019-04-28) - - * deprecated returning "false" to remove a Node from NodeVisitorInterface::leaveNode() - * allowed Twig\NodeVisitor\NodeVisitorInterface::leaveNode() to return "null" instead of "false" (same meaning) - * deprecated the "filter" tag (use the "apply" tag instead) - * added the "apply" tag as a replacement for the "filter" tag - * allowed Twig\Loader\FilesystemLoader::findTemplate() to return "null" instead of "false" (same meaning) - * added support for "Twig\Markup" instances in the "in" test - * fixed "import" when macros are stored in a template string - * fixed Lexer when using custom options containing the # char - * added template line number to twig_get_attribute() - -* 2.8.1 (2019-04-16) - - * fixed EscaperNodeVisitor - * deprecated passing a 3rd, 4th, and 5th arguments to the Sandbox exception classes - * deprecated Node::setTemplateName() in favor of Node::setSourceContext() - -* 2.8.0 (2019-04-16) - - * added Traversable support for the length filter - * fixed some wrong location in error messages - * made exception creation faster - * made escaping on ternary expressions (?: and ??) more fine-grained - * added the possibility to give a nice name to string templates (template_from_string function) - * fixed the "with" behavior to always include the globals (for consistency with the "include" and "embed" tags) - * fixed "include" with "ignore missing" when an error loading occurs in the included template - * added support for a new whitespace trimming option ({%~ ~%}, {{~ ~}}, {#~ ~#}) - * added the "column" filter - -* 2.7.4 (2019-03-23) - - * fixed variadic support - * fixed CheckToStringNode implementation (broken when a function/filter is variadic) - -* 2.7.3 (2019-03-21) - - * fixed the spaceless filter so that it behaves like the spaceless tag - * fixed BC break on Environment::resolveTemplate() - * allowed Traversable objects to be used in the "with" tag - * allowed Traversable objects to be used in the "with" tag - * allowed Traversable objects to be used in the "with" argument of the "include" and "embed" tags - -* 2.7.2 (2019-03-12) - - * added TemplateWrapper::getTemplateName() - -* 2.7.1 (2019-03-12) - - * fixed class aliases - -* 2.7.0 (2019-03-12) - - * fixed sandbox security issue (under some circumstances, calling the - __toString() method on an object was possible even if not allowed by the - security policy) - * fixed batch filter clobbers array keys when fill parameter is used - * added preserveKeys support for the batch filter - * fixed "embed" support when used from "template_from_string" - * deprecated passing a Twig\Template to Twig\Environment::load()/Twig\Environment::resolveTemplate() - * added the possibility to pass a TemplateWrapper to Twig\Environment::load() - * marked Twig\Environment::getTemplateClass() as internal (implementation detail) - * improved the performance of the sandbox - * deprecated the spaceless tag - * added a spaceless filter - * added max value to the "random" function - * deprecated Twig\Extension\InitRuntimeInterface - * deprecated Twig\Loader\ExistsLoaderInterface - * deprecated PSR-0 classes in favor of namespaced ones - * made namespace classes the default classes (PSR-0 ones are aliases now) - * added Twig\Loader\ChainLoader::getLoaders() - * removed duplicated directory separator in FilesystemLoader - * deprecated the "base_template_class" option on Twig\Environment - * deprecated the Twig\Environment::getBaseTemplateClass() and - Twig\Environment::setBaseTemplateClass() methods - * changed internal code to use the namespaced classes as much as possible - * deprecated Twig_Parser::isReservedMacroName() - -* 2.6.2 (2019-01-14) - - * fixed regression (key exists check for non ArrayObject objects) - -* 2.6.1 (2019-01-14) - - * fixed ArrayObject access with a null value - * fixed embedded templates starting with a BOM - * fixed using a Twig_TemplateWrapper instance as an argument to extends - * fixed error location when calling an undefined block - * deprecated passing a string as a source on Twig_Error - * switched generated code to use the PHP short array notation - * fixed float representation in compiled templates - * added a second argument to the join filter (last separator configuration) - -* 2.6.0 (2018-12-16) - - * made sure twig_include returns a string - * fixed multi-byte UFT-8 in escape('html_attr') - * added the "deprecated" tag - * added support for dynamically named tests - * fixed GlobalsInterface extended class - * fixed filesystem loader throwing an exception instead of returning false - -* 2.5.0 (2018-07-13) - - * deprecated using the spaceless tag at the root level of a child template (noop anyway) - * deprecated the possibility to define a block in a non-capturing block in a child template - * added the Symfony ctype polyfill as a dependency - * fixed reporting the proper location for errors compiled in templates - * fixed the error handling for the optimized extension-based function calls - * ensured that syntax errors are triggered with the right line - * "js" filter now produces valid JSON - -* 2.4.8 (2018-04-02) - - * fixed a regression when using the "default" filter or the "defined" test on non-existing arrays - -* 2.4.7 (2018-03-20) - - * optimized runtime performance - * optimized parser performance by inlining the constant values - * fixed block names unicity - * fixed counting children of SimpleXMLElement objects - * added missing else clause to avoid infinite loops - * fixed .. (range operator) in sandbox policy - -* 2.4.6 (2018-03-03) - - * fixed a regression in the way the profiler is registered in templates - -* 2.4.5 (2018-03-02) - - * optimized the performance of calling an extension method at runtime - * optimized the performance of the dot operator for array and method calls - * added an exception when using "===" instead of "same as" - * fixed possible array to string conversion concealing actual error - * made variable names deterministic in compiled templates - * fixed length filter when passing an instance of IteratorAggregate - * fixed Environment::resolveTemplate to accept instances of TemplateWrapper - -* 2.4.4 (2017-09-27) - - * added Twig_Profiler_Profile::reset() - * fixed use TokenParser to return an empty Node - * added RuntimeExtensionInterface - * added circular reference detection when loading templates - * added support for runtime loaders in IntegrationTestCase - * fixed deprecation when using Twig_Profiler_Dumper_Html - * removed @final from Twig_Profiler_Dumper_Text - -* 2.4.3 (2017-06-07) - - * fixed namespaces introduction - -* 2.4.2 (2017-06-05) - - * fixed namespaces introduction - -* 2.4.1 (2017-06-05) - - * fixed namespaces introduction - -* 2.4.0 (2017-06-05) - - * added support for PHPUnit 6 when testing extensions - * fixed PHP 7.2 compatibility - * fixed template name generation in Twig_Environment::createTemplate() - * removed final tag on Twig_TokenParser_Include - * dropped HHVM support - * added namespaced aliases for all (non-deprecated) classes and interfaces - * marked Twig_Filter, Twig_Function, Twig_Test, Twig_Node_Module and Twig_Profiler_Profile as final via the @final annotation - -* 2.3.2 (2017-04-20) - - * fixed edge case in the method cache for Twig attributes - -* 2.3.1 (2017-04-18) - - * fixed the empty() test - -* 2.3.0 (2017-03-22) - - * fixed a race condition handling when writing cache files - * "length" filter now returns string length when applied to an object that does - not implement \Countable but provides __toString() - * "empty" test will now consider the return value of the __toString() method for - objects implement __toString() but not \Countable - * fixed JS escaping for unicode characters with higher code points - * added error message when calling `parent()` in a block that doesn't exist in the parent template - -* 2.2.0 (2017-02-26) - - * added a PSR-11 compatible runtime loader - * added `side` argument to `trim` to allow left or right trimming only. - -* 2.1.0 (2017-01-11) - - * fixed twig_get_attribute() - * added Twig_NodeCaptureInterface for nodes that capture all output - -* 2.0.0 (2017-01-05) - - * removed the C extension - * moved Twig_Environment::getAttribute() to twig_get_attribute() - * removed Twig_Environment::getLexer(), Twig_Environment::getParser(), Twig_Environment::getCompiler() - * removed Twig_Compiler::getFilename() - * added hasser support in Twig_Template::getAttribute() - * sped up the json_encode filter - * removed reserved macro names; all names can be used as macro - * removed Twig_Template::getEnvironment() - * changed _self variable to return the current template name - * made the loader a required argument of Twig_Environment constructor - * removed Twig_Environment::clearTemplateCache() - * removed Twig_Autoloader (use Composer instead) - * removed `true` as an equivalent to `html` for the auto-escaping strategy - * removed pre-1.8 autoescape tag syntax - * dropped support for PHP 5.x - * removed the ability to register a global variable after the runtime or the extensions have been initialized - * improved the performance of the filesystem loader - * removed features that were deprecated in 1.x - -* 1.42.6 (2020-XX-XX) - - * n/a - -* 1.42.5 (2020-02-11) - - * Fix implementation of case-insensitivity for method names - -* 1.42.4 (2019-11-11) - - * optimized "block('foo') ?? 'bar" - * added supported for exponential numbers - -* 1.42.3 (2019-08-24) - - * fixed the "split" filter when the delimiter is "0" - * fixed the "empty" test on Traversable instances - * fixed cache when opcache is installed but disabled - * fixed PHP 7.4 compatibility - * bumped the minimal PHP version to 5.5 - -* 1.42.2 (2019-06-18) - - * Display partial output (PHP buffer) when an error occurs in debug mode - -* 1.42.1 (2019-06-04) - - * added support for "Twig\Markup" instances in the "in" test (again) - * allowed string operators as variables names in assignments - -* 1.42.0 (2019-05-31) - - * fixed the "filter" filter when the argument is \Traversable but does not implement \Iterator (\SimpleXmlElement for instance) - * fixed a PHP fatal error when calling a macro imported in a block in a nested block - * fixed a PHP fatal error when calling a macro imported in the template in another macro - * fixed wrong error message on "import" and "from" - -* 1.41.0 (2019-05-14) - - * fixed support for PHP 7.4 - * added "filter", "map", and "reduce" filters (and support for arrow functions) - * fixed partial output leak when a PHP fatal error occurs - * optimized context access on PHP 7.4 - -* 1.40.1 (2019-04-29) - -* fixed regression in NodeTraverser - -* 1.40.0 (2019-04-28) - - * allowed Twig\NodeVisitor\NodeVisitorInterface::leaveNode() to return "null" instead of "false" (same meaning) - * added the "apply" tag as a replacement for the "filter" tag - * allowed Twig\Loader\FilesystemLoader::findTemplate() to return "null" instead of "false" (same meaning) - * added support for "Twig\Markup" instances in the "in" test - * fixed Lexer when using custom options containing the # char - * fixed "import" when macros are stored in a template string - -* 1.39.1 (2019-04-16) - - * fixed EscaperNodeVisitor - -* 1.39.0 (2019-04-16) - - * added Traversable support for the length filter - * fixed some wrong location in error messages - * made exception creation faster - * made escaping on ternary expressions (?: and ??) more fine-grained - * added the possibility to give a nice name to string templates (template_from_string function) - * fixed the "with" behavior to always include the globals (for consistency with the "include" and "embed" tags) - * fixed "include" with "ignore missing" when an error loading occurs in the included template - * added support for a new whitespace trimming option ({%~ ~%}, {{~ ~}}, {#~ ~#}) - -* 1.38.4 (2019-03-23) - - * fixed CheckToStringNode implementation (broken when a function/filter is variadic) - -* 1.38.3 (2019-03-21) - - * fixed the spaceless filter so that it behaves like the spaceless tag - * fixed BC break on Environment::resolveTemplate() - * fixed the bundled Autoloader to also load namespaced classes - * allowed Traversable objects to be used in the "with" tag - * allowed Traversable objects to be used in the "with" argument of the "include" and "embed" tags - -* 1.38.2 (2019-03-12) - - * added TemplateWrapper::getTemplateName() - -* 1.38.1 (2019-03-12) - - * fixed class aliases - -* 1.38.0 (2019-03-12) - - * fixed sandbox security issue (under some circumstances, calling the - __toString() method on an object was possible even if not allowed by the - security policy) - * fixed batch filter clobbers array keys when fill parameter is used - * added preserveKeys support for the batch filter - * fixed "embed" support when used from "template_from_string" - * added the possibility to pass a TemplateWrapper to Twig\Environment::load() - * improved the performance of the sandbox - * added a spaceless filter - * added max value to the "random" function - * made namespace classes the default classes (PSR-0 ones are aliases now) - * removed duplicated directory separator in FilesystemLoader - * added Twig\Loader\ChainLoader::getLoaders() - * changed internal code to use the namespaced classes as much as possible - -* 1.37.1 (2019-01-14) - - * fixed regression (key exists check for non ArrayObject objects) - * fixed logic in TemplateWrapper - -* 1.37.0 (2019-01-14) - - * fixed ArrayObject access with a null value - * fixed embedded templates starting with a BOM - * fixed using a Twig_TemplateWrapper instance as an argument to extends - * switched generated code to use the PHP short array notation - * dropped PHP 5.3 support - * fixed float representation in compiled templates - * added a second argument to the join filter (last separator configuration) - -* 1.36.0 (2018-12-16) - - * made sure twig_include returns a string - * fixed multi-byte UFT-8 in escape('html_attr') - * added the "deprecated" tag - * added support for dynamically named tests - * fixed GlobalsInterface extended class - * fixed filesystem loader throwing an exception instead of returning false - -* 1.35.4 (2018-07-13) - - * ensured that syntax errors are triggered with the right line - * added the Symfony ctype polyfill as a dependency - * "js" filter now produces valid JSON - -* 1.35.3 (2018-03-20) - - * fixed block names unicity - * fixed counting children of SimpleXMLElement objects - * added missing else clause to avoid infinite loops - * fixed .. (range operator) in sandbox policy - -* 1.35.2 (2018-03-03) - - * fixed a regression in the way the profiler is registered in templates - -* 1.35.1 (2018-03-02) - - * added an exception when using "===" instead of "same as" - * fixed possible array to string conversion concealing actual error - * made variable names deterministic in compiled templates - * fixed length filter when passing an instance of IteratorAggregate - * fixed Environment::resolveTemplate to accept instances of TemplateWrapper - -* 1.35.0 (2017-09-27) - - * added Twig_Profiler_Profile::reset() - * fixed use TokenParser to return an empty Node - * added RuntimeExtensionInterface - * added circular reference detection when loading templates - -* 1.34.4 (2017-07-04) - - * added support for runtime loaders in IntegrationTestCase - * fixed deprecation when using Twig_Profiler_Dumper_Html - -* 1.34.3 (2017-06-07) - - * fixed namespaces introduction - -* 1.34.2 (2017-06-05) - - * fixed namespaces introduction - -* 1.34.1 (2017-06-05) - - * fixed namespaces introduction - -* 1.34.0 (2017-06-05) - - * added support for PHPUnit 6 when testing extensions - * fixed PHP 7.2 compatibility - * fixed template name generation in Twig_Environment::createTemplate() - * removed final tag on Twig_TokenParser_Include - * added namespaced aliases for all (non-deprecated) classes and interfaces - * dropped HHVM support - * dropped PHP 5.2 support - -* 1.33.2 (2017-04-20) - - * fixed edge case in the method cache for Twig attributes - -* 1.33.1 (2017-04-18) - - * fixed the empty() test - -* 1.33.0 (2017-03-22) - - * fixed a race condition handling when writing cache files - * "length" filter now returns string length when applied to an object that does - not implement \Countable but provides __toString() - * "empty" test will now consider the return value of the __toString() method for - objects implement __toString() but not \Countable - * fixed JS escaping for unicode characters with higher code points - -* 1.32.0 (2017-02-26) - - * fixed deprecation notice in Twig_Util_DeprecationCollector - * added a PSR-11 compatible runtime loader - * added `side` argument to `trim` to allow left or right trimming only. - -* 1.31.0 (2017-01-11) - - * added Twig_NodeCaptureInterface for nodes that capture all output - * fixed marking the environment as initialized too early - * fixed C89 compat for the C extension - * turned fatal error into exception when a previously generated cache is corrupted - * fixed offline cache warm-ups for embedded templates - -* 1.30.0 (2016-12-23) - - * added Twig_FactoryRuntimeLoader - * deprecated function/test/filter/tag overriding - * deprecated the "disable_c_ext" attribute on Twig_Node_Expression_GetAttr - -* 1.29.0 (2016-12-13) - - * fixed sandbox being left enabled if an exception is thrown while rendering - * marked some classes as being final (via @final) - * made Twig_Error report real source path when possible - * added support for {{ _self }} to provide an upgrade path from 1.x to 2.0 (replaces {{ _self.templateName }}) - * deprecated silent display of undefined blocks - * deprecated support for mbstring.func_overload != 0 - -* 1.28.2 (2016-11-23) - - * fixed precedence between getFoo() and isFoo() in Twig_Template::getAttribute() - * improved a deprecation message - -* 1.28.1 (2016-11-18) - - * fixed block() function when used with a template argument - -* 1.28.0 (2016-11-17) - - * added support for the PHP 7 null coalescing operator for the ?? Twig implementation - * exposed a way to access template data and methods in a portable way - * changed context access to use the PHP 7 null coalescing operator when available - * added the "with" tag - * added support for a custom template on the block() function - * added "is defined" support for block() and constant() - * optimized the way attributes are fetched - -* 1.27.0 (2016-10-25) - - * deprecated Twig_Parser::getEnvironment() - * deprecated Twig_Parser::addHandler() and Twig_Parser::addNodeVisitor() - * deprecated Twig_Compiler::addIndentation() - * fixed regression when registering two extensions having the same class name - * deprecated Twig_LoaderInterface::getSource() (implement Twig_SourceContextLoaderInterface instead) - * fixed the filesystem loader with relative paths - * deprecated Twig_Node::getLine() in favor of Twig_Node::getTemplateLine() - * deprecated Twig_Template::getSource() in favor of Twig_Template::getSourceContext() - * deprecated Twig_Node::getFilename() in favor of Twig_Node::getTemplateName() - * deprecated the "filename" escaping strategy (use "name" instead) - * added Twig_Source to hold information about the original template - * deprecated Twig_Error::getTemplateFile() and Twig_Error::setTemplateFile() in favor of Twig_Error::getTemplateName() and Twig_Error::setTemplateName() - * deprecated Parser::getFilename() - * fixed template paths when a template name contains a protocol like vfs:// - * improved debugging with Twig_Sandbox_SecurityError exceptions for disallowed methods and properties - -* 1.26.1 (2016-10-05) - - * removed template source code from generated template classes when debug is disabled - * fixed default implementation of Twig_Template::getDebugInfo() for better BC - * fixed regression on static calls for functions/filters/tests - -* 1.26.0 (2016-10-02) - - * added template cache invalidation based on more environment options - * added a missing deprecation notice - * fixed template paths when a template is stored in a PHAR file - * allowed filters/functions/tests implementation to use a different class than the extension they belong to - * deprecated Twig_ExtensionInterface::getName() - -* 1.25.0 (2016-09-21) - - * changed the way we store template source in template classes - * removed usage of realpath in cache keys - * fixed Twig cache sharing when used with different versions of PHP - * removed embed parent workaround for simple use cases - * deprecated the ability to store non Node instances in Node::$nodes - * deprecated Twig_Environment::getLexer(), Twig_Environment::getParser(), Twig_Environment::getCompiler() - * deprecated Twig_Compiler::getFilename() - -* 1.24.2 (2016-09-01) - - * fixed static callables - * fixed a potential PHP warning when loading the cache - * fixed a case where the autoescaping does not work as expected - -* 1.24.1 (2016-05-30) - - * fixed reserved keywords (forbids true, false, null and none keywords for variables names) - * fixed support for PHP7 (Throwable support) - * marked the following methods as being internals on Twig_Environment: - getFunctions(), getFilters(), getTests(), getFunction(), getFilter(), getTest(), - getTokenParsers(), getTags(), getNodeVisitors(), getUnaryOperators(), getBinaryOperators(), - getFunctions(), getFilters(), getGlobals(), initGlobals(), initExtensions(), and initExtension() - -* 1.24.0 (2016-01-25) - - * adding support for the ?? operator - * fixed the defined test when used on a constant, a map, or a sequence - * undeprecated _self (should only be used to get the template name, not the template instance) - * fixed parsing on PHP7 - -* 1.23.3 (2016-01-11) - - * fixed typo - -* 1.23.2 (2015-01-11) - - * added versions in deprecated messages - * made file cache tolerant for trailing (back)slashes on directory configuration - * deprecated unused Twig_Node_Expression_ExtensionReference class - -* 1.23.1 (2015-11-05) - - * fixed some exception messages which triggered PHP warnings - * fixed BC on Twig_Test_NodeTestCase - -* 1.23.0 (2015-10-29) - - * deprecated the possibility to override an extension by registering another one with the same name - * deprecated Twig_ExtensionInterface::getGlobals() (added Twig_Extension_GlobalsInterface for BC) - * deprecated Twig_ExtensionInterface::initRuntime() (added Twig_Extension_InitRuntimeInterface for BC) - * deprecated Twig_Environment::computeAlternatives() - -* 1.22.3 (2015-10-13) - - * fixed regression when using null as a cache strategy - * improved performance when checking template freshness - * fixed warnings when loaded templates do not exist - * fixed template class name generation to prevent possible collisions - * fixed logic for custom escapers to call them even on integers and null values - * changed template cache names to take into account the Twig C extension - -* 1.22.2 (2015-09-22) - - * fixed a race condition in template loading - -* 1.22.1 (2015-09-15) - - * fixed regression in template_from_string - -* 1.22.0 (2015-09-13) - - * made Twig_Test_IntegrationTestCase more flexible - * added an option to force PHP bytecode invalidation when writing a compiled template into the cache - * fixed the profiler duration for the root node - * changed template cache names to take into account enabled extensions - * deprecated Twig_Environment::clearCacheFiles(), Twig_Environment::getCacheFilename(), - Twig_Environment::writeCacheFile(), and Twig_Environment::getTemplateClassPrefix() - * added a way to override the filesystem template cache system - * added a way to get the original template source from Twig_Template - -* 1.21.2 (2015-09-09) - - * fixed variable names for the deprecation triggering code - * fixed escaping strategy detection based on filename - * added Traversable support for replace, merge, and sort - * deprecated support for character by character replacement for the "replace" filter - -* 1.21.1 (2015-08-26) - - * fixed regression when using the deprecated Twig_Test_* classes - -* 1.21.0 (2015-08-24) - - * added deprecation notices for deprecated features - * added a deprecation "framework" for filters/functions/tests and test fixtures - -* 1.20.0 (2015-08-12) - - * forbid access to the Twig environment from templates and internal parts of Twig_Template - * fixed limited RCEs when in sandbox mode - * deprecated Twig_Template::getEnvironment() - * deprecated the _self variable for usage outside of the from and import tags - * added Twig_BaseNodeVisitor to ease the compatibility of node visitors - between 1.x and 2.x - -* 1.19.0 (2015-07-31) - - * fixed wrong error message when including an undefined template in a child template - * added support for variadic filters, functions, and tests - * added support for extra positional arguments in macros - * added ignore_missing flag to the source function - * fixed batch filter with zero items - * deprecated Twig_Environment::clearTemplateCache() - * fixed sandbox disabling when using the include function - -* 1.18.2 (2015-06-06) - - * fixed template/line guessing in exceptions for nested templates - * optimized the number of inodes and the size of realpath cache when using the cache - -* 1.18.1 (2015-04-19) - - * fixed memory leaks in the C extension - * deprecated Twig_Loader_String - * fixed the slice filter when used with a SimpleXMLElement object - * fixed filesystem loader when trying to load non-files (like directories) - -* 1.18.0 (2015-01-25) - - * fixed some error messages where the line was wrong (unknown variables or argument names) - * added a new way to customize the main Module node (via empty nodes) - * added Twig_Environment::createTemplate() to create a template from a string - * added a profiler - * fixed filesystem loader cache when different file paths are used for the same template - -* 1.17.0 (2015-01-14) - - * added a 'filename' autoescaping strategy, which dynamically chooses the - autoescaping strategy for a template based on template file extension. - -* 1.16.3 (2014-12-25) - - * fixed regression for dynamic parent templates - * fixed cache management with statcache - * fixed a regression in the slice filter - -* 1.16.2 (2014-10-17) - - * fixed timezone on dates as strings - * fixed 2-words test names when a custom node class is not used - * fixed macros when using an argument named like a PHP super global (like GET or POST) - * fixed date_modify when working with DateTimeImmutable - * optimized for loops - * fixed multi-byte characters handling in the split filter - * fixed a regression in the in operator - * fixed a regression in the slice filter - -* 1.16.1 (2014-10-10) - - * improved error reporting in a sandboxed template - * fixed missing error file/line information under certain circumstances - * fixed wrong error line number in some error messages - * fixed the in operator to use strict comparisons - * sped up the slice filter - * fixed for mb function overload mb_substr acting different - * fixed the attribute() function when passing a variable for the arguments - -* 1.16.0 (2014-07-05) - - * changed url_encode to always encode according to RFC 3986 - * fixed inheritance in a 'use'-hierarchy - * removed the __toString policy check when the sandbox is disabled - * fixed recursively calling blocks in templates with inheritance - -* 1.15.1 (2014-02-13) - - * fixed the conversion of the special '0000-00-00 00:00' date - * added an error message when trying to import an undefined block from a trait - * fixed a C extension crash when accessing defined but uninitialized property. - -* 1.15.0 (2013-12-06) - - * made ignoreStrictCheck in Template::getAttribute() works with __call() methods throwing BadMethodCallException - * added min and max functions - * added the round filter - * fixed a bug that prevented the optimizers to be enabled/disabled selectively - * fixed first and last filters for UTF-8 strings - * added a source function to include the content of a template without rendering it - * fixed the C extension sandbox behavior when get or set is prepend to method name - -* 1.14.2 (2013-10-30) - - * fixed error filename/line when an error occurs in an included file - * allowed operators that contain whitespaces to have more than one whitespace - * allowed tests to be made of 1 or 2 words (like "same as" or "divisible by") - -* 1.14.1 (2013-10-15) - - * made it possible to use named operators as variables - * fixed the possibility to have a variable named 'matches' - * added support for PHP 5.5 DateTimeInterface - -* 1.14.0 (2013-10-03) - - * fixed usage of the html_attr escaping strategy to avoid double-escaping with the html strategy - * added new operators: ends with, starts with, and matches - * fixed some compatibility issues with HHVM - * added a way to add custom escaping strategies - * fixed the C extension compilation on Windows - * fixed the batch filter when using a fill argument with an exact match of elements to batch - * fixed the filesystem loader cache when a template name exists in several namespaces - * fixed template_from_string when the template includes or extends other ones - * fixed a crash of the C extension on an edge case - -* 1.13.2 (2013-08-03) - - * fixed the error line number for an error occurs in and embedded template - * fixed crashes of the C extension on some edge cases - -* 1.13.1 (2013-06-06) - - * added the possibility to ignore the filesystem constructor argument in Twig_Loader_Filesystem - * fixed Twig_Loader_Chain::exists() for a loader which implements Twig_ExistsLoaderInterface - * adjusted backtrace call to reduce memory usage when an error occurs - * added support for object instances as the second argument of the constant test - * fixed the include function when used in an assignment - -* 1.13.0 (2013-05-10) - - * fixed getting a numeric-like item on a variable ('09' for instance) - * fixed getting a boolean or float key on an array, so it is consistent with PHP's array access: - `{{ array[false] }}` behaves the same as `echo $array[false];` (equals `$array[0]`) - * made the escape filter 20% faster for happy path (escaping string for html with UTF-8) - * changed ☃ to § in tests - * enforced usage of named arguments after positional ones - -* 1.12.3 (2013-04-08) - - * fixed a security issue in the filesystem loader where it was possible to include a template one - level above the configured path - * fixed fatal error that should be an exception when adding a filter/function/test too late - * added a batch filter - * added support for encoding an array as query string in the url_encode filter - -* 1.12.2 (2013-02-09) - - * fixed the timezone used by the date filter and function when the given date contains a timezone (like 2010-01-28T15:00:00+02:00) - * fixed globals when getGlobals is called early on - * added the first and last filter - -* 1.12.1 (2013-01-15) - - * added support for object instances as the second argument of the constant function - * relaxed globals management to avoid a BC break - * added support for {{ some_string[:2] }} - -* 1.12.0 (2013-01-08) - - * added verbatim as an alias for the raw tag to avoid confusion with the raw filter - * fixed registration of tests and functions as anonymous functions - * fixed globals management - -* 1.12.0-RC1 (2012-12-29) - - * added an include function (does the same as the include tag but in a more flexible way) - * added the ability to use any PHP callable to define filters, functions, and tests - * added a syntax error when using a loop variable that is not defined - * added the ability to set default values for macro arguments - * added support for named arguments for filters, tests, and functions - * moved filters/functions/tests syntax errors to the parser - * added support for extended ternary operator syntaxes - -* 1.11.1 (2012-11-11) - - * fixed debug info line numbering (was off by 2) - * fixed escaping when calling a macro inside another one (regression introduced in 1.9.1) - * optimized variable access on PHP 5.4 - * fixed a crash of the C extension when an exception was thrown from a macro called without being imported (using _self.XXX) - -* 1.11.0 (2012-11-07) - - * fixed macro compilation when a variable name is a PHP reserved keyword - * changed the date filter behavior to always apply the default timezone, except if false is passed as the timezone - * fixed bitwise operator precedences - * added the template_from_string function - * fixed default timezone usage for the date function - * optimized the way Twig exceptions are managed (to make them faster) - * added Twig_ExistsLoaderInterface (implementing this interface in your loader make the chain loader much faster) - -* 1.10.3 (2012-10-19) - - * fixed wrong template location in some error messages - * reverted a BC break introduced in 1.10.2 - * added a split filter - -* 1.10.2 (2012-10-15) - - * fixed macro calls on PHP 5.4 - -* 1.10.1 (2012-10-15) - - * made a speed optimization to macro calls when imported via the "import" tag - * fixed C extension compilation on Windows - * fixed a segfault in the C extension when using DateTime objects - -* 1.10.0 (2012-09-28) - - * extracted functional tests framework to make it reusable for third-party extensions - * added namespaced templates support in Twig_Loader_Filesystem - * added Twig_Loader_Filesystem::prependPath() - * fixed an error when a token parser pass a closure as a test to the subparse() method - -* 1.9.2 (2012-08-25) - - * fixed the in operator for objects that contain circular references - * fixed the C extension when accessing a public property of an object implementing the \ArrayAccess interface - -* 1.9.1 (2012-07-22) - - * optimized macro calls when auto-escaping is on - * fixed wrong parent class for Twig_Function_Node - * made Twig_Loader_Chain more explicit about problems - -* 1.9.0 (2012-07-13) - - * made the parsing independent of the template loaders - * fixed exception trace when an error occurs when rendering a child template - * added escaping strategies for CSS, URL, and HTML attributes - * fixed nested embed tag calls - * added the date_modify filter - -* 1.8.3 (2012-06-17) - - * fixed paths in the filesystem loader when passing a path that ends with a slash or a backslash - * fixed escaping when a project defines a function named html or js - * fixed chmod mode to apply the umask correctly - -* 1.8.2 (2012-05-30) - - * added the abs filter - * fixed a regression when using a number in template attributes - * fixed compiler when mbstring.func_overload is set to 2 - * fixed DateTimeZone support in date filter - -* 1.8.1 (2012-05-17) - - * fixed a regression when dealing with SimpleXMLElement instances in templates - * fixed "is_safe" value for the "dump" function when "html_errors" is not defined in php.ini - * switched to use mbstring whenever possible instead of iconv (you might need to update your encoding as mbstring and iconv encoding names sometimes differ) - -* 1.8.0 (2012-05-08) - - * enforced interface when adding tests, filters, functions, and node visitors from extensions - * fixed a side-effect of the date filter where the timezone might be changed - * simplified usage of the autoescape tag; the only (optional) argument is now the escaping strategy or false (with a BC layer) - * added a way to dynamically change the auto-escaping strategy according to the template "filename" - * changed the autoescape option to also accept a supported escaping strategy (for BC, true is equivalent to html) - * added an embed tag - -* 1.7.0 (2012-04-24) - - * fixed a PHP warning when using CIFS - * fixed template line number in some exceptions - * added an iterable test - * added an error when defining two blocks with the same name in a template - * added the preserves_safety option for filters - * fixed a PHP notice when trying to access a key on a non-object/array variable - * enhanced error reporting when the template file is an instance of SplFileInfo - * added Twig_Environment::mergeGlobals() - * added compilation checks to avoid misuses of the sandbox tag - * fixed filesystem loader freshness logic for high traffic websites - * fixed random function when charset is null - -* 1.6.5 (2012-04-11) - - * fixed a regression when a template only extends another one without defining any blocks - -* 1.6.4 (2012-04-02) - - * fixed PHP notice in Twig_Error::guessTemplateLine() introduced in 1.6.3 - * fixed performance when compiling large files - * optimized parent template creation when the template does not use dynamic inheritance - -* 1.6.3 (2012-03-22) - - * fixed usage of Z_ADDREF_P for PHP 5.2 in the C extension - * fixed compilation of numeric values used in templates when using a locale where the decimal separator is not a dot - * made the strategy used to guess the real template file name and line number in exception messages much faster and more accurate - -* 1.6.2 (2012-03-18) - - * fixed sandbox mode when used with inheritance - * added preserveKeys support for the slice filter - * fixed the date filter when a DateTime instance is passed with a specific timezone - * added a trim filter - -* 1.6.1 (2012-02-29) - - * fixed Twig C extension - * removed the creation of Twig_Markup instances when not needed - * added a way to set the default global timezone for dates - * fixed the slice filter on strings when the length is not specified - * fixed the creation of the cache directory in case of a race condition - -* 1.6.0 (2012-02-04) - - * fixed raw blocks when used with the whitespace trim option - * made a speed optimization to macro calls when imported via the "from" tag - * fixed globals, parsers, visitors, filters, tests, and functions management in Twig_Environment when a new one or new extension is added - * fixed the attribute function when passing arguments - * added slice notation support for the [] operator (syntactic sugar for the slice operator) - * added a slice filter - * added string support for the reverse filter - * fixed the empty test and the length filter for Twig_Markup instances - * added a date function to ease date comparison - * fixed unary operators precedence - * added recursive parsing support in the parser - * added string and integer handling for the random function - -* 1.5.1 (2012-01-05) - - * fixed a regression when parsing strings - -* 1.5.0 (2012-01-04) - - * added Traversable objects support for the join filter - -* 1.5.0-RC2 (2011-12-30) - - * added a way to set the default global date interval format - * fixed the date filter for DateInterval instances (setTimezone() does not exist for them) - * refactored Twig_Template::display() to ease its extension - * added a number_format filter - -* 1.5.0-RC1 (2011-12-26) - - * removed the need to quote hash keys - * allowed hash keys to be any expression - * added a do tag - * added a flush tag - * added support for dynamically named filters and functions - * added a dump function to help debugging templates - * added a nl2br filter - * added a random function - * added a way to change the default format for the date filter - * fixed the lexer when an operator ending with a letter ends a line - * added string interpolation support - * enhanced exceptions for unknown filters, functions, tests, and tags - -* 1.4.0 (2011-12-07) - - * fixed lexer when using big numbers (> PHP_INT_MAX) - * added missing preserveKeys argument to the reverse filter - * fixed macros containing filter tag calls - -* 1.4.0-RC2 (2011-11-27) - - * removed usage of Reflection in Twig_Template::getAttribute() - * added a C extension that can optionally replace Twig_Template::getAttribute() - * added negative timestamp support to the date filter - -* 1.4.0-RC1 (2011-11-20) - - * optimized variable access when using PHP 5.4 - * changed the precedence of the .. operator to be more consistent with languages that implements such a feature like Ruby - * added an Exception to Twig_Loader_Array::isFresh() method when the template does not exist to be consistent with other loaders - * added Twig_Function_Node to allow more complex functions to have their own Node class - * added Twig_Filter_Node to allow more complex filters to have their own Node class - * added Twig_Test_Node to allow more complex tests to have their own Node class - * added a better error message when a template is empty but contain a BOM - * fixed "in" operator for empty strings - * fixed the "defined" test and the "default" filter (now works with more than one call (foo.bar.foo) and for both values of the strict_variables option) - * changed the way extensions are loaded (addFilter/addFunction/addGlobal/addTest/addNodeVisitor/addTokenParser/addExtension can now be called in any order) - * added Twig_Environment::display() - * made the escape filter smarter when the encoding is not supported by PHP - * added a convert_encoding filter - * moved all node manipulations outside the compile() Node method - * made several speed optimizations - -* 1.3.0 (2011-10-08) - -no changes - -* 1.3.0-RC1 (2011-10-04) - - * added an optimization for the parent() function - * added cache reloading when auto_reload is true and an extension has been modified - * added the possibility to force the escaping of a string already marked as safe (instance of Twig_Markup) - * allowed empty templates to be used as traits - * added traits support for the "parent" function - -* 1.2.0 (2011-09-13) - -no changes - -* 1.2.0-RC1 (2011-09-10) - - * enhanced the exception when a tag remains unclosed - * added support for empty Countable objects for the "empty" test - * fixed algorithm that determines if a template using inheritance is valid (no output between block definitions) - * added better support for encoding problems when escaping a string (available as of PHP 5.4) - * added a way to ignore a missing template when using the "include" tag ({% include "foo" ignore missing %}) - * added support for an array of templates to the "include" and "extends" tags ({% include ['foo', 'bar'] %}) - * added support for bitwise operators in expressions - * added the "attribute" function to allow getting dynamic attributes on variables - * added Twig_Loader_Chain - * added Twig_Loader_Array::setTemplate() - * added an optimization for the set tag when used to capture a large chunk of static text - * changed name regex to match PHP one "[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*" (works for blocks, tags, functions, filters, and macros) - * removed the possibility to use the "extends" tag from a block - * added "if" modifier support to "for" loops - -* 1.1.2 (2011-07-30) - - * fixed json_encode filter on PHP 5.2 - * fixed regression introduced in 1.1.1 ({{ block(foo|lower) }}) - * fixed inheritance when using conditional parents - * fixed compilation of templates when the body of a child template is not empty - * fixed output when a macro throws an exception - * fixed a parsing problem when a large chunk of text is enclosed in a comment tag - * added PHPDoc for all Token parsers and Core extension functions - -* 1.1.1 (2011-07-17) - - * added a performance optimization in the Optimizer (also helps to lower the number of nested level calls) - * made some performance improvement for some edge cases - -* 1.1.0 (2011-06-28) - - * fixed json_encode filter - -* 1.1.0-RC3 (2011-06-24) - - * fixed method case-sensitivity when using the sandbox mode - * added timezone support for the date filter - * fixed possible security problems with NUL bytes - -* 1.1.0-RC2 (2011-06-16) - - * added an exception when the template passed to "use" is not a string - * made 'a.b is defined' not throw an exception if a is not defined (in strict mode) - * added {% line \d+ %} directive - -* 1.1.0-RC1 (2011-05-28) - -Flush your cache after upgrading. - - * fixed date filter when using a timestamp - * fixed the defined test for some cases - * fixed a parsing problem when a large chunk of text is enclosed in a raw tag - * added support for horizontal reuse of template blocks (see docs for more information) - * added whitespace control modifier to all tags (see docs for more information) - * added null as an alias for none (the null test is also an alias for the none test now) - * made TRUE, FALSE, NONE equivalent to their lowercase counterparts - * wrapped all compilation and runtime exceptions with Twig_Error_Runtime and added logic to guess the template name and line - * moved display() method to Twig_Template (generated templates should now use doDisplay() instead) - -* 1.0.0 (2011-03-27) - - * fixed output when using mbstring - * fixed duplicate call of methods when using the sandbox - * made the charset configurable for the escape filter - -* 1.0.0-RC2 (2011-02-21) - - * changed the way {% set %} works when capturing (the content is now marked as safe) - * added support for macro name in the endmacro tag - * make Twig_Error compatible with PHP 5.3.0 > - * fixed an infinite loop on some Windows configurations - * fixed the "length" filter for numbers - * fixed Template::getAttribute() as properties in PHP are case sensitive - * removed coupling between Twig_Node and Twig_Template - * fixed the ternary operator precedence rule - -* 1.0.0-RC1 (2011-01-09) - -Backward incompatibilities: - - * the "items" filter, which has been deprecated for quite a long time now, has been removed - * the "range" filter has been converted to a function: 0|range(10) -> range(0, 10) - * the "constant" filter has been converted to a function: {{ some_date|date('DATE_W3C'|constant) }} -> {{ some_date|date(constant('DATE_W3C')) }} - * the "cycle" filter has been converted to a function: {{ ['odd', 'even']|cycle(i) }} -> {{ cycle(['odd', 'even'], i) }} - * the "for" tag does not support "joined by" anymore - * the "autoescape" first argument is now "true"/"false" (instead of "on"/"off") - * the "parent" tag has been replaced by a "parent" function ({{ parent() }} instead of {% parent %}) - * the "display" tag has been replaced by a "block" function ({{ block('title') }} instead of {% display title %}) - * removed the grammar and simple token parser (moved to the Twig Extensions repository) - -Changes: - - * added "needs_context" option for filters and functions (the context is then passed as a first argument) - * added global variables support - * made macros return their value instead of echoing directly (fixes calling a macro in sandbox mode) - * added the "from" tag to import macros as functions - * added support for functions (a function is just syntactic sugar for a getAttribute() call) - * made macros callable when sandbox mode is enabled - * added an exception when a macro uses a reserved name - * the "default" filter now uses the "empty" test instead of just checking for null - * added the "empty" test - -* 0.9.10 (2010-12-16) - -Backward incompatibilities: - - * The Escaper extension is enabled by default, which means that all displayed - variables are now automatically escaped. You can revert to the previous - behavior by removing the extension via $env->removeExtension('escaper') - or just set the 'autoescape' option to 'false'. - * removed the "without loop" attribute for the "for" tag (not needed anymore - as the Optimizer take care of that for most cases) - * arrays and hashes have now a different syntax - * arrays keep the same syntax with square brackets: [1, 2] - * hashes now use curly braces (["a": "b"] should now be written as {"a": "b"}) - * support for "arrays with keys" and "hashes without keys" is not supported anymore ([1, "foo": "bar"] or {"foo": "bar", 1}) - * the i18n extension is now part of the Twig Extensions repository - -Changes: - - * added the merge filter - * removed 'is_escaper' option for filters (a left over from the previous version) -- you must use 'is_safe' now instead - * fixed usage of operators as method names (like is, in, and not) - * changed the order of execution for node visitors - * fixed default() filter behavior when used with strict_variables set to on - * fixed filesystem loader compatibility with PHAR files - * enhanced error messages when an unexpected token is parsed in an expression - * fixed filename not being added to syntax error messages - * added the autoescape option to enable/disable autoescaping - * removed the newline after a comment (mimics PHP behavior) - * added a syntax error exception when parent block is used on a template that does not extend another one - * made the Escaper extension enabled by default - * fixed sandbox extension when used with auto output escaping - * fixed escaper when wrapping a Twig_Node_Print (the original class must be preserved) - * added an Optimizer extension (enabled by default; optimizes "for" loops and "raw" filters) - * added priority to node visitors - -* 0.9.9 (2010-11-28) - -Backward incompatibilities: - * the self special variable has been renamed to _self - * the odd and even filters are now tests: - {{ foo|odd }} must now be written {{ foo is odd }} - * the "safe" filter has been renamed to "raw" - * in Node classes, - sub-nodes are now accessed via getNode() (instead of property access) - attributes via getAttribute() (instead of array access) - * the urlencode filter had been renamed to url_encode - * the include tag now merges the passed variables with the current context by default - (the old behavior is still possible by adding the "only" keyword) - * moved Exceptions to Twig_Error_* (Twig_SyntaxError/Twig_RuntimeError are now Twig_Error_Syntax/Twig_Error_Runtime) - * removed support for {{ 1 < i < 3 }} (use {{ i > 1 and i < 3 }} instead) - * the "in" filter has been removed ({{ a|in(b) }} should now be written {{ a in b }}) - -Changes: - * added file and line to Twig_Error_Runtime exceptions thrown from Twig_Template - * changed trans tag to accept any variable for the plural count - * fixed sandbox mode (__toString() method check was not enforced if called implicitly from complex statements) - * added the ** (power) operator - * changed the algorithm used for parsing expressions - * added the spaceless tag - * removed trim_blocks option - * added support for is*() methods for attributes (foo.bar now looks for foo->getBar() or foo->isBar()) - * changed all exceptions to extend Twig_Error - * fixed unary expressions ({{ not(1 or 0) }}) - * fixed child templates (with an extend tag) that uses one or more imports - * added support for {{ 1 not in [2, 3] }} (more readable than the current {{ not (1 in [2, 3]) }}) - * escaping has been rewritten - * the implementation of template inheritance has been rewritten - (blocks can now be called individually and still work with inheritance) - * fixed error handling for if tag when a syntax error occurs within a subparse process - * added a way to implement custom logic for resolving token parsers given a tag name - * fixed js escaper to be stricter (now uses a whilelist-based js escaper) - * added the following filers: "constant", "trans", "replace", "json_encode" - * added a "constant" test - * fixed objects with __toString() not being autoescaped - * fixed subscript expressions when calling __call() (methods now keep the case) - * added "test" feature (accessible via the "is" operator) - * removed the debug tag (should be done in an extension) - * fixed trans tag when no vars are used in plural form - * fixed race condition when writing template cache - * added the special _charset variable to reference the current charset - * added the special _context variable to reference the current context - * renamed self to _self (to avoid conflict) - * fixed Twig_Template::getAttribute() for protected properties - -* 0.9.8 (2010-06-28) - -Backward incompatibilities: - * the trans tag plural count is now attached to the plural tag: - old: `{% trans count %}...{% plural %}...{% endtrans %}` - new: `{% trans %}...{% plural count %}...{% endtrans %}` - - * added a way to translate strings coming from a variable ({% trans var %}) - * fixed trans tag when used with the Escaper extension - * fixed default cache umask - * removed Twig_Template instances from the debug tag output - * fixed objects with __isset() defined - * fixed set tag when used with a capture - * fixed type hinting for Twig_Environment::addFilter() method - -* 0.9.7 (2010-06-12) - -Backward incompatibilities: - * changed 'as' to '=' for the set tag ({% set title as "Title" %} must now be {% set title = "Title" %}) - * removed the sandboxed attribute of the include tag (use the new sandbox tag instead) - * refactored the Node system (if you have custom nodes, you will have to update them to use the new API) - - * added self as a special variable that refers to the current template (useful for importing macros from the current template) - * added Twig_Template instance support to the include tag - * added support for dynamic and conditional inheritance ({% extends some_var %} and {% extends standalone ? "minimum" : "base" %}) - * added a grammar sub-framework to ease the creation of custom tags - * fixed the for tag for large arrays (some loop variables are now only available for arrays and objects that implement the Countable interface) - * removed the Twig_Resource::resolveMissingFilter() method - * fixed the filter tag which did not apply filtering to included files - * added a bunch of unit tests - * added a bunch of phpdoc - * added a sandbox tag in the sandbox extension - * changed the date filter to support any date format supported by DateTime - * added strict_variable setting to throw an exception when an invalid variable is used in a template (disabled by default) - * added the lexer, parser, and compiler as arguments to the Twig_Environment constructor - * changed the cache option to only accepts an explicit path to a cache directory or false - * added a way to add token parsers, filters, and visitors without creating an extension - * added three interfaces: Twig_NodeInterface, Twig_TokenParserInterface, and Twig_FilterInterface - * changed the generated code to match the new coding standards - * fixed sandbox mode (__toString() method check was not enforced if called implicitly from a simple statement like {{ article }}) - * added an exception when a child template has a non-empty body (as it is always ignored when rendering) - -* 0.9.6 (2010-05-12) - - * fixed variables defined outside a loop and for which the value changes in a for loop - * fixed the test suite for PHP 5.2 and older versions of PHPUnit - * added support for __call() in expression resolution - * fixed node visiting for macros (macros are now visited by visitors as any other node) - * fixed nested block definitions with a parent call (rarely useful but nonetheless supported now) - * added the cycle filter - * fixed the Lexer when mbstring.func_overload is used with an mbstring.internal_encoding different from ASCII - * added a long-syntax for the set tag ({% set foo %}...{% endset %}) - * unit tests are now powered by PHPUnit - * added support for gettext via the `i18n` extension - * fixed twig_capitalize_string_filter() and fixed twig_length_filter() when used with UTF-8 values - * added a more useful exception if an if tag is not closed properly - * added support for escaping strategy in the autoescape tag - * fixed lexer when a template has a big chunk of text between/in a block - -* 0.9.5 (2010-01-20) - -As for any new release, don't forget to remove all cached templates after -upgrading. - -If you have defined custom filters, you MUST upgrade them for this release. To -upgrade, replace "array" with "new Twig_Filter_Function", and replace the -environment constant by the "needs_environment" option: - - // before - 'even' => array('twig_is_even_filter', false), - 'escape' => array('twig_escape_filter', true), - - // after - 'even' => new Twig_Filter_Function('twig_is_even_filter'), - 'escape' => new Twig_Filter_Function('twig_escape_filter', array('needs_environment' => true)), - -If you have created NodeTransformer classes, you will need to upgrade them to -the new interface (please note that the interface is not yet considered -stable). - - * fixed list nodes that did not extend the Twig_NodeListInterface - * added the "without loop" option to the for tag (it disables the generation of the loop variable) - * refactored node transformers to node visitors - * fixed automatic-escaping for blocks - * added a way to specify variables to pass to an included template - * changed the automatic-escaping rules to be more sensible and more configurable in custom filters (the documentation lists all the rules) - * improved the filter system to allow object methods to be used as filters - * changed the Array and String loaders to actually make use of the cache mechanism - * included the default filter function definitions in the extension class files directly (Core, Escaper) - * added the // operator (like the floor() PHP function) - * added the .. operator (as a syntactic sugar for the range filter when the step is 1) - * added the in operator (as a syntactic sugar for the in filter) - * added the following filters in the Core extension: in, range - * added support for arrays (same behavior as in PHP, a mix between lists and dictionaries, arrays and hashes) - * enhanced some error messages to provide better feedback in case of parsing errors - -* 0.9.4 (2009-12-02) - -If you have custom loaders, you MUST upgrade them for this release: The -Twig_Loader base class has been removed, and the Twig_LoaderInterface has also -been changed (see the source code for more information or the documentation). - - * added support for DateTime instances for the date filter - * fixed loop.last when the array only has one item - * made it possible to insert newlines in tag and variable blocks - * fixed a bug when a literal '\n' were present in a template text - * fixed bug when the filename of a template contains */ - * refactored loaders - -* 0.9.3 (2009-11-11) - -This release is NOT backward compatible with the previous releases. - - The loaders do not take the cache and autoReload arguments anymore. Instead, - the Twig_Environment class has two new options: cache and auto_reload. - Upgrading your code means changing this kind of code: - - $loader = new Twig_Loader_Filesystem('/path/to/templates', '/path/to/compilation_cache', true); - $twig = new Twig_Environment($loader); - - to something like this: - - $loader = new Twig_Loader_Filesystem('/path/to/templates'); - $twig = new Twig_Environment($loader, array( - 'cache' => '/path/to/compilation_cache', - 'auto_reload' => true, - )); - - * deprecated the "items" filter as it is not needed anymore - * made cache and auto_reload options of Twig_Environment instead of arguments of Twig_Loader - * optimized template loading speed - * removed output when an error occurs in a template and render() is used - * made major speed improvements for loops (up to 300% on even the smallest loops) - * added properties as part of the sandbox mode - * added public properties support (obj.item can now be the item property on the obj object) - * extended set tag to support expression as value ({% set foo as 'foo' ~ 'bar' %} ) - * fixed bug when \ was used in HTML - -* 0.9.2 (2009-10-29) - - * made some speed optimizations - * changed the cache extension to .php - * added a js escaping strategy - * added support for short block tag - * changed the filter tag to allow chained filters - * made lexer more flexible as you can now change the default delimiters - * added set tag - * changed default directory permission when cache dir does not exist (more secure) - * added macro support - * changed filters first optional argument to be a Twig_Environment instance instead of a Twig_Template instance - * made Twig_Autoloader::autoload() a static method - * avoid writing template file if an error occurs - * added $ escaping when outputting raw strings - * enhanced some error messages to ease debugging - * fixed empty cache files when the template contains an error - -* 0.9.1 (2009-10-14) - - * fixed a bug in PHP 5.2.6 - * fixed numbers with one than one decimal - * added support for method calls with arguments ({{ foo.bar('a', 43) }}) - * made small speed optimizations - * made minor tweaks to allow better extensibility and flexibility - -* 0.9.0 (2009-10-12) - - * Initial release +# 3.0.0-BETA1 (2019-11-11) + + * removed the "if" condition support on the "for" tag + * made the in, <, >, <=, >=, ==, and != operators more strict when comparing strings and integers/floats + * removed the "filter" tag + * added type hints everywhere + * changed Environment::resolveTemplate() to always return a TemplateWrapper instance + * removed Template::__toString() + * removed Parser::isReservedMacroName() + * removed SanboxedPrintNode + * removed Node::setTemplateName() + * made classes marked as "@final" final + * removed InitRuntimeInterface, ExistsLoaderInterface, and SourceContextLoaderInterface + * removed the "spaceless" tag + * removed Twig\Environment::getBaseTemplateClass() and Twig\Environment::setBaseTemplateClass() + * removed the "base_template_class" option on Twig\Environment + * bumped minimum PHP version to 7.2 + * removed PSR-0 classes diff --git a/vendor/twig/twig/LICENSE b/vendor/twig/twig/LICENSE index 5e8a0b8..fd8234e 100644 --- a/vendor/twig/twig/LICENSE +++ b/vendor/twig/twig/LICENSE @@ -1,29 +1,27 @@ -Copyright (c) 2009-2020 by the Twig Team. +Copyright (c) 2009-present by the Twig Team. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions are -met: +All rights reserved. - * Redistributions of source code must retain the above copyright - notice, this list of conditions and the following disclaimer. +Redistribution and use in source and binary forms, with or without modification, +are permitted provided that the following conditions are met: - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials provided - with the distribution. - - * The names of the contributors may not be used to endorse or - promote products derived from this software without specific - prior written permission. + * Redistributions of source code must retain the above copyright notice, + this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above copyright notice, + this list of conditions and the following disclaimer in the documentation + and/or other materials provided with the distribution. + * Neither the name of Twig nor the names of its contributors + may be used to endorse or promote products derived from this software + without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR -A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT -OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, -SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT -LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, -DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY -THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT -(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE -OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR +CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, +EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, +PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR +PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF +LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING +NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS +SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/vendor/twig/twig/README.rst b/vendor/twig/twig/README.rst index d896ff5..7bf8c67 100644 --- a/vendor/twig/twig/README.rst +++ b/vendor/twig/twig/README.rst @@ -1,8 +1,7 @@ Twig, the flexible, fast, and secure template language for PHP ============================================================== -Twig is a template language for PHP, released under the new BSD license (code -and documentation). +Twig is a template language for PHP. Twig uses a syntax similar to the Django and Jinja template languages which inspired the Twig runtime environment. @@ -12,7 +11,7 @@ Sponsors .. raw:: html - + Blackfire.io diff --git a/vendor/twig/twig/composer.json b/vendor/twig/twig/composer.json index 0d1c0ad..3662366 100644 --- a/vendor/twig/twig/composer.json +++ b/vendor/twig/twig/composer.json @@ -5,6 +5,7 @@ "keywords": ["templating"], "homepage": "https://twig.symfony.com", "license": "BSD-3-Clause", + "minimum-stability": "dev", "authors": [ { "name": "Fabien Potencier", @@ -23,18 +24,23 @@ } ], "require": { - "php": "^7.0", + "php": ">=8.1.0", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "^1.3", "symfony/polyfill-ctype": "^1.8" }, "require-dev": { - "symfony/phpunit-bridge": "^4.4|^5.0", - "psr/container": "^1.0" + "symfony/phpunit-bridge": "^5.4.9|^6.4|^7.0", + "psr/container": "^1.0|^2.0", + "phpstan/phpstan": "^2.0" }, "autoload": { - "psr-0" : { - "Twig_" : "lib/" - }, + "files": [ + "src/Resources/core.php", + "src/Resources/debug.php", + "src/Resources/escaper.php", + "src/Resources/string_loader.php" + ], "psr-4" : { "Twig\\" : "src/" } @@ -43,10 +49,5 @@ "psr-4" : { "Twig\\Tests\\" : "tests/" } - }, - "extra": { - "branch-alias": { - "dev-master": "2.12-dev" - } } } diff --git a/vendor/twig/twig/doc/advanced.rst b/vendor/twig/twig/doc/advanced.rst deleted file mode 100644 index bd9f5a4..0000000 --- a/vendor/twig/twig/doc/advanced.rst +++ /dev/null @@ -1,912 +0,0 @@ -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); - -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']); - -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:: - - $twig = new \Twig\Environment($loader); - $test = new \Twig\TwigTest( - 'odd', - null, - ['node_class' => \Twig\Node\Expression\Test\OddTest::class]); - $twig->addTest($test); - - class Twig_Node_Expression_Test_Odd extends \Twig\Node\Expression\TestExpression - { - public function compile(\Twig\Compiler $compiler) - { - $compiler - ->raw('(') - ->subcompile($this->getNode('node')) - ->raw(' % 2 == 1') - ->raw(')') - ; - } - } - -The above example shows how you can create tests that use a node class. The node -class has access to one sub-node called ``node``. This sub-node contains the -value that is being tested. When the ``odd`` filter is used in code such as: - -.. code-block:: 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. - -.. versionadded:: 2.6 - Dynamic tests support was added in Twig 2.6. - -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 %} - - .. note:: - - The ``apply`` tag was introduced in Twig 2.9; use the ``filter`` tag with - previous versions. - -* 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:`filter ` 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 Project_Set_TokenParser()); - -Defining a Token Parser -~~~~~~~~~~~~~~~~~~~~~~~ - -Now, let's see the actual code of this class:: - - class Project_Set_TokenParser 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 Project_Set_Node($name, $value, $token->getLine(), $this->getTag()); - } - - public function getTag() - { - return 'set'; - } - } - -The ``getTag()`` method must return the tag we want to parse, here ``set``. - -The ``parse()`` method is invoked whenever the parser encounters a ``set`` -tag. It should return a ``\Twig\Node\Node`` instance that represents the node (the -``Project_Set_Node`` calls creating is explained in the next section). - -The parsing process is simplified thanks to a bunch of methods you can call -from the token stream (``$this->parser->getStream()``): - -* ``getCurrent()``: Gets the current token in the stream. - -* ``next()``: Moves to the next token in the stream, *but returns the old one*. - -* ``test($type)``, ``test($value)`` or ``test($type, $value)``: Determines whether - the current token is of a particular type or value (or both). The value may be an - array of several possible values. - -* ``expect($type[, $value[, $message]])``: If the current token isn't of the given - type/value a syntax error is thrown. Otherwise, if the type and value are correct, - the token is returned and the stream moves to the next token. - -* ``look()``: Looks 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 ``Project_Set_Node`` class itself is quite short:: - - class Project_Set_Node extends \Twig\Node\Node - { - public function __construct($name, \Twig\Node\Expression\AbstractExpression $value, $line, $tag = null) - { - parent::__construct(['value' => $value], ['name' => $name], $line, $tag); - } - - public function compile(\Twig\Compiler $compiler) - { - $compiler - ->addDebugInfo($this) - ->write('$context[\''.$this->getAttribute('name').'\'] = ') - ->subcompile($this->getNode('value')) - ->raw(";\n") - ; - } - } - -The compiler implements a fluid interface and provides methods that helps the -developer generate beautiful and readable PHP code: - -* ``subcompile()``: Compiles a node. - -* ``raw()``: Writes the given string as is. - -* ``write()``: Writes the given string by adding indentation at the beginning - of each line. - -* ``string()``: Writes a quoted string. - -* ``repr()``: Writes a PHP representation of a given value (see - ``\Twig\Node\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 Project_Twig_Extension 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 Project_Twig_Extension()); - -.. 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 Project_Twig_Extension extends \Twig\Extension\AbstractExtension implements \Twig\Extension\GlobalsInterface - { - public function getGlobals() - { - return [ - 'text' => new Text(), - ]; - } - - // ... - } - -Functions -~~~~~~~~~ - -Functions can be registered in an extension via the ``getFunctions()`` -method:: - - class Project_Twig_Extension 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 Project_Twig_Extension 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 Project_Twig_Extension extends \Twig\Extension\AbstractExtension - { - public function getTokenParsers() - { - return [new Project_Set_TokenParser()]; - } - - // ... - } - -In the above code, we have added a single new tag, defined by the -``Project_Set_TokenParser`` class. The ``Project_Set_TokenParser`` class is -responsible for parsing the tag and compiling it to PHP. - -Operators -~~~~~~~~~ - -The ``getOperators()`` methods lets you add new operators. Here is how to add -the ``!``, ``||``, and ``&&`` operators:: - - class Project_Twig_Extension 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 Project_Twig_Extension 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 Project_Twig_Extension 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 ('Project_Twig_RuntimeExtension' === $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 -``Project_Twig_RuntimeExtension`` class and use it directly in the extension:: - - class Project_Twig_RuntimeExtension - { - private $rot13Provider; - - public function __construct($rot13Provider) - { - $this->rot13Provider = $rot13Provider; - } - - public function rot13($value) - { - return $this->rot13Provider->rot13($value); - } - } - - class Project_Twig_Extension extends \Twig\Extension\AbstractExtension - { - public function getFunctions() - { - return [ - new \Twig\TwigFunction('rot13', ['Project_Twig_RuntimeExtension', 'rot13']), - // or - new \Twig\TwigFunction('rot13', 'Project_Twig_RuntimeExtension::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:: - - class Project_Tests_IntegrationTest extends \Twig\Test\IntegrationTestCase - { - public function getExtensions() - { - return [ - new Project_Twig_Extension1(), - new Project_Twig_Extension2(), - ]; - } - - 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. - -.. _`rot13`: https://secure.php.net/manual/en/function.str-rot13.php -.. _`tests/Twig/Fixtures`: https://github.com/twigphp/Twig/tree/2.x/tests/Fixtures -.. _`tests/Twig/Node`: https://github.com/twigphp/Twig/tree/2.x/tests/Node diff --git a/vendor/twig/twig/doc/api.rst b/vendor/twig/twig/doc/api.rst deleted file mode 100644 index 983ef19..0000000 --- a/vendor/twig/twig/doc/api.rst +++ /dev/null @@ -1,591 +0,0 @@ -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. - -* ``base_template_class`` *string* (defaults to ``\Twig\Template``) - - The base template class to use for generated - 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. - -* ``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; and -the performance boost is even larger if you use a PHP accelerator such as -OPCache. See the ``cache`` and ``auto_reload`` options of ``\Twig\Environment`` -above for more information. - -Built-in Loaders -~~~~~~~~~~~~~~~~ - -Here is a list of the built-in loaders: - -``\Twig\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`` support 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*: Enabled the built-in Twig profiler. - -* *Twig\Extension\OptimizerExtension*: Optimizes the node tree before - compilation. - -* *Twig\Extension\StringLoaderExtension*: Defined 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:: 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:: 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, 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. - -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. - -* ``\Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_RAW_FILTER``, removes the ``raw`` - filter whenever possible. - -* ``\Twig\NodeVisitor\OptimizerNodeVisitor::OPTIMIZE_VAR_ACCESS``, simplifies the creation - and access of variables in the compiled templates 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/vendor/twig/twig/doc/coding_standards.rst b/vendor/twig/twig/doc/coding_standards.rst deleted file mode 100644 index 721b0f1..0000000 --- a/vendor/twig/twig/doc/coding_standards.rst +++ /dev/null @@ -1,101 +0,0 @@ -Coding Standards -================ - -When writing Twig templates, we recommend you to follow these official coding -standards: - -* Put one (and only one) space after the start of a delimiter (``{{``, ``{%``, - and ``{#``) and before the end of a delimiter (``}}``, ``%}``, and ``#}``): - - .. code-block:: 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 one (and only one) space before and after the following operators: - comparison operators (``==``, ``!=``, ``<``, ``>``, ``>=``, ``<=``), math - operators (``+``, ``-``, ``/``, ``*``, ``%``, ``//``, ``**``), logic - operators (``not``, ``and``, ``or``), ``~``, ``is``, ``in``, and the ternary - operator (``?:``): - - .. code-block:: twig - - {{ 1 + 2 }} - {{ foo ~ bar }} - {{ true ? true : false }} - -* Put one (and only one) space after the ``:`` sign in hashes and ``,`` in - arrays and hashes: - - .. code-block:: 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 arrays - and hashes: - - .. 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 %} diff --git a/vendor/twig/twig/doc/deprecated.rst b/vendor/twig/twig/doc/deprecated.rst deleted file mode 100644 index d7a6145..0000000 --- a/vendor/twig/twig/doc/deprecated.rst +++ /dev/null @@ -1,115 +0,0 @@ -Deprecated Features -=================== - -This document lists deprecated features in Twig 2.x. Deprecated features are -kept for backward compatibility and removed in the next major release (a -feature that was deprecated in Twig 2.x is removed in Twig 3.0). - -PSR-0 ------ - -* PSR-0 classes are deprecated in favor of namespaced ones since Twig 2.7. - -Inheritance ------------ - -* Defining a "block" definition in a non-capturing block in a child template is - deprecated since Twig 2.5.0. In Twig 3.0, it will throw a - ``Twig\Error\SyntaxError`` exception. It does not work anyway, so most - projects won't need to do anything to upgrade. - -Errors ------- - - * Passing a string as the ``$source`` argument on ``\Twig\Error\Error`` / - ``Twig\Error\Error`` constructor is deprecated since Twig 2.6.1. Pass an - instance of ``Twig\Source`` instead. - -Tags ----- - -* The ``spaceless`` tag is deprecated in Twig 2.7. Use the ``spaceless`` filter - instead or ``{% apply spaceless %}`` (the ``Twig\Node\SpacelessNode`` and - ``Twig\TokenParser\SpacelessTokenParser`` classes are also deprecated). - -* Using the ``spaceless`` tag at the root level of a child template is - deprecated in Twig 2.5.0. This does not work as one would expect it to work - anyway. In Twig 3.0, it will throw a ``Twig\Error\SyntaxError`` exception. - -* The ``filter`` tag is deprecated in Twig 2.9. Use the ``apply`` tag instead - (the ``Twig\TokenParser\FilterTokenParser`` classes is also deprecated). - -* Adding an ``if`` condition on a ``for`` tag is deprecated in Twig 2.10. Use a - ``filter`` filter or an "if" condition inside the "for" body instead (if your condition - depends on a variable updated inside the loop) - -Final Classes -------------- - -The following classes are marked as ``@final`` in Twig 2 and will be final in -3.0: - -* ``Twig\Node\ModuleNode`` -* ``Twig\TwigFilter`` -* ``Twig\TwigFunction`` -* ``Twig\TwigTest`` -* ``Twig\Profiler\Profile`` - -Parser ------- - -* As of Twig 2.7, the ``\Twig\Parser::isReservedMacroName()`` / ``Twig\Parser`` - function is deprecated and will be removed in Twig 3.0. It always returns - ``false`` anyway as Twig 2 does not have any reserved macro names. - -Environment ------------ - -* As of Twig 2.7, the ``base_template_class`` option on ``Twig\Environment`` is - deprecated and will be removed in Twig 3.0. - -* As of Twig 2.7, the ``Twig\Environment::getBaseTemplateClass()`` and - ``Twig\Environment::setBaseTemplateClass()`` methods are deprecated and will - be removed in Twig 3.0. - -* As of Twig 2.7, the ``Twig\Environment::getTemplateClass()`` is marked as - being internal and should not be used. - -* As of Twig 2.7, passing a ``Twig\Template`` instance to the - ``Twig\Environment::load()`` and ``Twig\Environment::resolveTemplate()`` is - deprecated. - -* Depending on the input, ``Twig\Environment::resolveTemplate()`` can return - a ``Twig\Template`` or a ``Twig\TemplateWrapper`` instance. In Twig 3.0, this - method will **always** return a ``Twig\TemplateWrapper`` instance. You should - only rely on the methods of this class if you want to be forward-compatible. - -Interfaces ----------- - -* As of Twig 2.7, the empty ``Twig\Loader\ExistsLoaderInterface`` interface is - deprecated and will be removed in Twig 3.0. - -* As of Twig 2.7, the ``Twig\Extension\InitRuntimeInterface`` interface is - deprecated and will be removed in Twig 3.0. - -Extensions ----------- - -* As of Twig 2.11, the ``Twig\Extension\CoreExtension::setEscaper()`` and - ``Twig\Extension\CoreExtension::getEscapers()`` are deprecated. Use the same - methods on ``Twig\Extension\EscaperExtension`` instead. - -Miscellaneous -------------- - -* As of Twig 2.7, the ``Twig_SimpleFilter``, ``Twig_SimpleFunction``, and - ``Twig_SimpleTest`` empty classes are deprecated and will be removed in Twig - 3.0. Use ``Twig\TwigFilter``, ``Twig\TwigFunction``, and ``Twig\TwigTest`` - respectively. - -* As of Twig 2.8.2, all usage of - ``Twig\Loader\FilesystemLoader::findTemplate()`` check for a ``null`` return - value (same meaning as returning ``false``). If you are overidding - ``Twig\Loader\FilesystemLoader::findTemplate()``, you must return ``null`` instead of ``false`` - to be compatible with Twig 3.0. diff --git a/vendor/twig/twig/doc/filters/abs.rst b/vendor/twig/twig/doc/filters/abs.rst deleted file mode 100644 index 77d5cf0..0000000 --- a/vendor/twig/twig/doc/filters/abs.rst +++ /dev/null @@ -1,18 +0,0 @@ -``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://secure.php.net/abs diff --git a/vendor/twig/twig/doc/filters/batch.rst b/vendor/twig/twig/doc/filters/batch.rst deleted file mode 100644 index 7d33149..0000000 --- a/vendor/twig/twig/doc/filters/batch.rst +++ /dev/null @@ -1,49 +0,0 @@ -``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:: twig - - {% set items = ['a', 'b', 'c', 'd', 'e', 'f', 'g'] %} - - - {% for row in items|batch(3, 'No item') %} - - {% for column in row %} - - {% endfor %} - - {% endfor %} -
    {{ column }}
    - -The above example will be rendered as: - -.. code-block:: twig - - - - - - - - - - - - - - - - - -
    abc
    def
    gNo itemNo 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 diff --git a/vendor/twig/twig/doc/filters/capitalize.rst b/vendor/twig/twig/doc/filters/capitalize.rst deleted file mode 100644 index 2353658..0000000 --- a/vendor/twig/twig/doc/filters/capitalize.rst +++ /dev/null @@ -1,11 +0,0 @@ -``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/vendor/twig/twig/doc/filters/column.rst b/vendor/twig/twig/doc/filters/column.rst deleted file mode 100644 index 97a3037..0000000 --- a/vendor/twig/twig/doc/filters/column.rst +++ /dev/null @@ -1,27 +0,0 @@ -``column`` -========== - -.. versionadded:: 2.8 - The ``column`` filter was added in Twig 2.8. - -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://secure.php.net/array_column diff --git a/vendor/twig/twig/doc/filters/convert_encoding.rst b/vendor/twig/twig/doc/filters/convert_encoding.rst deleted file mode 100644 index d977c75..0000000 --- a/vendor/twig/twig/doc/filters/convert_encoding.rst +++ /dev/null @@ -1,22 +0,0 @@ -``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://secure.php.net/iconv diff --git a/vendor/twig/twig/doc/filters/country_name.rst b/vendor/twig/twig/doc/filters/country_name.rst deleted file mode 100644 index 7e267f1..0000000 --- a/vendor/twig/twig/doc/filters/country_name.rst +++ /dev/null @@ -1,42 +0,0 @@ -``country_name`` -================ - -.. versionadded:: 2.12 - The ``country_name`` filter was added in Twig 2.12. - -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 req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly on the Twig environment:: - - use Twig\Extra\Intl\IntlExtension; - - $twig = new \Twig\Environment(...); - $twig->addExtension(new IntlExtension()); - -Arguments ---------- - -* ``locale``: The locale diff --git a/vendor/twig/twig/doc/filters/currency_name.rst b/vendor/twig/twig/doc/filters/currency_name.rst deleted file mode 100644 index a665e4b..0000000 --- a/vendor/twig/twig/doc/filters/currency_name.rst +++ /dev/null @@ -1,45 +0,0 @@ -``currency_name`` -================= - -.. versionadded:: 2.12 - The ``currency_name`` filter was added in Twig 2.12. - -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 req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly on the Twig environment:: - - use Twig\Extra\Intl\IntlExtension; - - $twig = new \Twig\Environment(...); - $twig->addExtension(new IntlExtension()); - -Arguments ---------- - -* ``locale``: The locale diff --git a/vendor/twig/twig/doc/filters/currency_symbol.rst b/vendor/twig/twig/doc/filters/currency_symbol.rst deleted file mode 100644 index 8d533c9..0000000 --- a/vendor/twig/twig/doc/filters/currency_symbol.rst +++ /dev/null @@ -1,45 +0,0 @@ -``currency_symbol`` -=================== - -.. versionadded:: 2.12 - The ``currency_symbol`` filter was added in Twig 2.12. - -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 req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly on the Twig environment:: - - use Twig\Extra\Intl\IntlExtension; - - $twig = new \Twig\Environment(...); - $twig->addExtension(new IntlExtension()); - -Arguments ---------- - -* ``locale``: The locale diff --git a/vendor/twig/twig/doc/filters/data_uri.rst b/vendor/twig/twig/doc/filters/data_uri.rst deleted file mode 100644 index fa5a81e..0000000 --- a/vendor/twig/twig/doc/filters/data_uri.rst +++ /dev/null @@ -1,51 +0,0 @@ -``data_uri`` -============ - -.. versionadded:: 2.12 - The ``data_uri`` filter was added in Twig 2.12. - -The ``data_uri`` filter generates a URL using the data scheme as defined in RFC -2397: - -.. code-block:: 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 req twig/html-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly 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 (limits depends - on the usage context), validation should be done before calling this filter. - -Arguments ---------- - -* ``mime``: The mime type -* ``parameters``: An array of parameters diff --git a/vendor/twig/twig/doc/filters/date.rst b/vendor/twig/twig/doc/filters/date.rst deleted file mode 100644 index 95a1cbf..0000000 --- a/vendor/twig/twig/doc/filters/date.rst +++ /dev/null @@ -1,82 +0,0 @@ -``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: - -.. code-block:: php - - $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 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()``: - -.. code-block:: php - - $twig = new \Twig\Environment($loader); - $twig->getExtension(\Twig\Extension\CoreExtension::class)->setTimezone('Europe/Paris'); - -Arguments ---------- - -* ``format``: The date format -* ``timezone``: The date timezone - -.. _`strtotime`: https://secure.php.net/strtotime -.. _`DateTime`: https://secure.php.net/DateTime -.. _`DateInterval`: https://secure.php.net/DateInterval -.. _`date`: https://secure.php.net/date -.. _`DateInterval::format`: https://secure.php.net/DateInterval.format diff --git a/vendor/twig/twig/doc/filters/date_modify.rst b/vendor/twig/twig/doc/filters/date_modify.rst deleted file mode 100644 index cc5d6df..0000000 --- a/vendor/twig/twig/doc/filters/date_modify.rst +++ /dev/null @@ -1,20 +0,0 @@ -``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://secure.php.net/strtotime -.. _`DateTime`: https://secure.php.net/DateTime diff --git a/vendor/twig/twig/doc/filters/default.rst b/vendor/twig/twig/doc/filters/default.rst deleted file mode 100644 index c4ccb56..0000000 --- a/vendor/twig/twig/doc/filters/default.rst +++ /dev/null @@ -1,33 +0,0 @@ -``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') }} - -.. 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/vendor/twig/twig/doc/filters/escape.rst b/vendor/twig/twig/doc/filters/escape.rst deleted file mode 100644 index 7c654ab..0000000 --- a/vendor/twig/twig/doc/filters/escape.rst +++ /dev/null @@ -1,119 +0,0 @@ -``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 ---------------- - -You can define custom escapers by calling the ``setEscaper()`` method on the -``core`` extension instance. The first argument is the escaper name (to be -used in the ``escape`` call) and the second one must be a valid PHP callable: - -.. code-block:: php - - $twig = new \Twig\Environment($loader); - $twig->getExtension(\Twig\Extension\CoreExtension::class)->setEscaper('csv', 'csv_escaper'); - -When called by Twig, the callable receives the Twig environment instance, the -string to escape, and the charset. - -.. note:: - - Built-in escapers cannot be overridden mainly 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://secure.php.net/htmlspecialchars diff --git a/vendor/twig/twig/doc/filters/filter.rst b/vendor/twig/twig/doc/filters/filter.rst deleted file mode 100644 index e9f9682..0000000 --- a/vendor/twig/twig/doc/filters/filter.rst +++ /dev/null @@ -1,58 +0,0 @@ -``filter`` -========== - -.. versionadded:: 1.41 - The ``filter`` filter was added in Twig 1.41 and 2.10. - -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/vendor/twig/twig/doc/filters/first.rst b/vendor/twig/twig/doc/filters/first.rst deleted file mode 100644 index 8d7081a..0000000 --- a/vendor/twig/twig/doc/filters/first.rst +++ /dev/null @@ -1,22 +0,0 @@ -``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://secure.php.net/manual/en/class.traversable.php diff --git a/vendor/twig/twig/doc/filters/format.rst b/vendor/twig/twig/doc/filters/format.rst deleted file mode 100644 index c0c96ee..0000000 --- a/vendor/twig/twig/doc/filters/format.rst +++ /dev/null @@ -1,16 +0,0 @@ -``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. #} - -.. _`sprintf`: https://secure.php.net/sprintf - -.. seealso:: :doc:`replace` diff --git a/vendor/twig/twig/doc/filters/format_currency.rst b/vendor/twig/twig/doc/filters/format_currency.rst deleted file mode 100644 index 8be71ac..0000000 --- a/vendor/twig/twig/doc/filters/format_currency.rst +++ /dev/null @@ -1,75 +0,0 @@ -``format_currency`` -=================== - -.. versionadded:: 2.12 - The ``format_currency`` filter was added in Twig 2.12. - -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 req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly 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/vendor/twig/twig/doc/filters/format_date.rst b/vendor/twig/twig/doc/filters/format_date.rst deleted file mode 100644 index 39c8094..0000000 --- a/vendor/twig/twig/doc/filters/format_date.rst +++ /dev/null @@ -1,32 +0,0 @@ -``format_date`` -=============== - -.. versionadded:: 2.12 - The ``format_date`` filter was added in Twig 2.12. - -The ``format_date`` filter formats a date. It behaves in the exact same way as -the ``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 req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly 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 diff --git a/vendor/twig/twig/doc/filters/format_datetime.rst b/vendor/twig/twig/doc/filters/format_datetime.rst deleted file mode 100644 index aba69e7..0000000 --- a/vendor/twig/twig/doc/filters/format_datetime.rst +++ /dev/null @@ -1,68 +0,0 @@ -``format_datetime`` -=================== - -.. versionadded:: 2.12 - The ``format_datetime`` filter was added in Twig 2.12. - -The ``format_datetime`` filter formats a date time: - - public function formatDateTime(Environment $env, $date, ?string $dateFormat = 'medium', ?string $timeFormat = 'medium', string $pattern = '', $timezone = null, string $calendar = 'gregorian', string $locale = null): string - -.. code-block:: twig - - {# Aug 7, 2019, 11:39:12 PM #} - {{ '2019-08-07 23:39:12'|format_datetime() }} - -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``. - -For greater flexiblity, you can even define your own pattern: - -.. code-block:: twig - - {# 11 oclock PM, GMT #} - {{ '2019-08-07 23:39:12'|format_datetime(pattern="hh 'oclock' a, zzzz") }} - -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') }} - -.. note:: - - The ``format_datetime`` filter is part of the ``IntlExtension`` which is not - installed by default. Install it first: - - .. code-block:: bash - - $ composer req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly 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 diff --git a/vendor/twig/twig/doc/filters/format_number.rst b/vendor/twig/twig/doc/filters/format_number.rst deleted file mode 100644 index b8a9a0f..0000000 --- a/vendor/twig/twig/doc/filters/format_number.rst +++ /dev/null @@ -1,118 +0,0 @@ -``format_number`` -================= - -.. versionadded:: 2.12 - The ``format_number`` filter was added in Twig 2.12. - -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.34 #} - {{ '12.345'|format_number('EUR', {rounding_mode: 'floor'}) }} - - {# €1,000,000.0000 #} - {{ '1000000'|format_number('EUR', {fraction_digit: 4}) }} - -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 req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly 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/vendor/twig/twig/doc/filters/format_time.rst b/vendor/twig/twig/doc/filters/format_time.rst deleted file mode 100644 index e46e246..0000000 --- a/vendor/twig/twig/doc/filters/format_time.rst +++ /dev/null @@ -1,32 +0,0 @@ -``format_time`` -=============== - -.. versionadded:: 2.12 - The ``format_time`` filter was added in Twig 2.12. - -The ``format_time`` filter formats a time. It behaves in the exact same way as -the ``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 req twig/intl-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly 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 diff --git a/vendor/twig/twig/doc/filters/html_to_markdown.rst b/vendor/twig/twig/doc/filters/html_to_markdown.rst deleted file mode 100644 index b603271..0000000 --- a/vendor/twig/twig/doc/filters/html_to_markdown.rst +++ /dev/null @@ -1,66 +0,0 @@ -``html_to_markdown`` -==================== - -.. versionadded:: 2.12 - The ``html_to_markdown`` filter was added in Twig 2.12. - -The ``html_to_markdown`` filter converts a block of HTML to Markdown: - -.. code-block:: twig - - {% apply html_to_markdown %} - -

    Hello!

    - - {% endapply %} - -You can also add some options by passing them as an argument to the filter: - -.. code-block:: twig - - {% apply html_to_markdown({hard_break: false}) %} - -

    Hello!

    - - {% endapply %} - -.. note:: - - The options are the ones provided by the ``league/html-to-markdown`` package. - -You can also use the filter on an included file: - -.. 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 req twig/markdown-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly on the Twig environment:: - - use Twig\Extra\Markdown\MarkdownMarkdownExtension; - - $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()); - } - } - }); diff --git a/vendor/twig/twig/doc/filters/index.rst b/vendor/twig/twig/doc/filters/index.rst deleted file mode 100644 index a35c3d7..0000000 --- a/vendor/twig/twig/doc/filters/index.rst +++ /dev/null @@ -1,59 +0,0 @@ -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 - raw - reduce - replace - reverse - round - slice - sort - spaceless - split - striptags - timezone_name - title - trim - u - upper - url_encode diff --git a/vendor/twig/twig/doc/filters/inky_to_html.rst b/vendor/twig/twig/doc/filters/inky_to_html.rst deleted file mode 100644 index 5462b57..0000000 --- a/vendor/twig/twig/doc/filters/inky_to_html.rst +++ /dev/null @@ -1,40 +0,0 @@ -``inky_to_html`` -================ - -.. versionadded:: 2.12 - The ``inky_to_html`` filter was added in Twig 2.12. - -The ``inky_to_html`` filter processes an `inky email template -`_: - -.. code-block:: 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 req twig/inky-extra - - Then, use the ``twig/extra-bundle`` on Symfony projects or add the extension - explictly on the Twig environment:: - - use Twig\Extra\Inky\InkyExtension; - - $twig = new \Twig\Environment(...); - $twig->addExtension(new InkyExtension()); diff --git a/vendor/twig/twig/doc/filters/inline_css.rst b/vendor/twig/twig/doc/filters/inline_css.rst deleted file mode 100644 index d0f75c9..0000000 --- a/vendor/twig/twig/doc/filters/inline_css.rst +++ /dev/null @@ -1,64 +0,0 @@ -``inline_css`` -============== - -.. versionadded:: 2.12 - The ``inline_css`` filter was added in Twig 2.12. - -The ``inline_css`` filter inline CSS styles in HTML documents: - -.. code-block:: twig - - {% apply inline_css %} - - - - - -

    Hello CSS!

    - - - {% endapply %} - -You can also add some stylesheets by passing them as arguments to the filter: - -.. code-block:: 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:: 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:: 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:: 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:: 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:: 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:: twig - - {% for post in posts %} -
    -
    {{ post.title }}
    -
    {{ post.text }}
    -
    - {% endfor %} - -Let's take another example: a block included within an ``if`` statement: - -.. code-block:: 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:: 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/vendor/twig/twig/doc/tags/filter.rst b/vendor/twig/twig/doc/tags/filter.rst deleted file mode 100644 index f225fb2..0000000 --- a/vendor/twig/twig/doc/tags/filter.rst +++ /dev/null @@ -1,26 +0,0 @@ -``filter`` -========== - -.. note:: - - As of Twig 2.9, you should use the ``apply`` tag instead which does the - same thing except that the wrapped template data is not scoped. - -Filter sections allow you to apply regular Twig filters on a block of template -data. Just wrap the code in the special ``filter`` section: - -.. code-block:: twig - - {% filter upper %} - This text becomes uppercase - {% endfilter %} - -You can also chain filters and pass arguments to them: - -.. code-block:: twig - - {% filter lower|escape('html') %} - SOME TEXT - {% endfilter %} - - {# outputs "<strong>some text</strong>" #} diff --git a/vendor/twig/twig/doc/tags/flush.rst b/vendor/twig/twig/doc/tags/flush.rst deleted file mode 100644 index 332e982..0000000 --- a/vendor/twig/twig/doc/tags/flush.rst +++ /dev/null @@ -1,14 +0,0 @@ -``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://secure.php.net/flush diff --git a/vendor/twig/twig/doc/tags/for.rst b/vendor/twig/twig/doc/tags/for.rst deleted file mode 100644 index 8e9b4e5..0000000 --- a/vendor/twig/twig/doc/tags/for.rst +++ /dev/null @@ -1,176 +0,0 @@ -``for`` -======= - -Loop over each item in a sequence. For example, to display a list of users -provided in a variable called ``users``: - -.. code-block:: twig - -

    Members

    -
      - {% for user in users %} -
    • {{ user.username|e }}
    • - {% endfor %} -
    - -.. note:: - - A sequence can be either an array or an object implementing the - ``Traversable`` interface. - -If you do need to iterate over a sequence of numbers, you can use the ``..`` -operator: - -.. code-block:: 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. They are also not available when - looping with a condition. - -Adding a condition ------------------- - -.. tip:: - - As of Twig 2.10, use the :doc:`filter <../filters/filter>` filter instead, - or an ``if`` condition inside the ``for`` body (if your condition depends on - a variable updated inside the loop and you are not using the ``loop`` - variable). - -Unlike in PHP, it's not possible to ``break`` or ``continue`` in a loop. You -can however filter the sequence during iteration which allows you to skip -items. The following example skips all the users which are not active: - -.. code-block:: twig - -
      - {% for user in users if user.active %} -
    • {{ user.username|e }}
    • - {% endfor %} -
    - -The advantage is that the special loop variable will count correctly thus not -counting the users not iterated over. Keep in mind that properties like -``loop.last`` will not be defined when using loop conditions. - -.. note:: - - Using the ``loop`` variable within the condition is not recommended as it - will probably not be doing what you expect it to. For instance, adding a - condition like ``loop.index > 4`` won't work as the index is only - incremented when the condition is true (so the condition will never - match). - -The `else` Clause ------------------ - -If no iteration took place because the sequence was empty, you can render a -replacement block by using ``else``: - -.. code-block:: 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:: twig - -

    Members

    -
      - {% for key in users|keys %} -
    • {{ key }}
    • - {% endfor %} -
    - -Iterating over Keys and Values ------------------------------- - -You can also access both keys and values: - -.. code-block:: 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:: twig - -

    Top Ten Members

    -
      - {% for user in users|slice(0, 10) %} -
    • {{ user.username|e }}
    • - {% endfor %} -
    diff --git a/vendor/twig/twig/doc/tags/from.rst b/vendor/twig/twig/doc/tags/from.rst deleted file mode 100644 index 96c439a..0000000 --- a/vendor/twig/twig/doc/tags/from.rst +++ /dev/null @@ -1,6 +0,0 @@ -``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/vendor/twig/twig/doc/tags/if.rst b/vendor/twig/twig/doc/tags/if.rst deleted file mode 100644 index 2a1610c..0000000 --- a/vendor/twig/twig/doc/tags/if.rst +++ /dev/null @@ -1,79 +0,0 @@ -``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:: twig - - {% if online == false %} -

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

    - {% endif %} - -You can also test if an array is not empty: - -.. code-block:: 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:: 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:: 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 array false - null false - non-empty array true - object true - ====================== ==================== diff --git a/vendor/twig/twig/doc/tags/import.rst b/vendor/twig/twig/doc/tags/import.rst deleted file mode 100644 index f217479..0000000 --- a/vendor/twig/twig/doc/tags/import.rst +++ /dev/null @@ -1,6 +0,0 @@ -``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/vendor/twig/twig/doc/tags/include.rst b/vendor/twig/twig/doc/tags/include.rst deleted file mode 100644 index 93ec894..0000000 --- a/vendor/twig/twig/doc/tags/include.rst +++ /dev/null @@ -1,111 +0,0 @@ -``include`` -=========== - -The ``include`` statement includes a template and returns the rendered content -of that file: - -.. code-block:: twig - - {% include 'header.html' %} - Body - {% include 'footer.html' %} - -.. note:: - - As of Twig 1.12, 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') %} - - {# Filter a rendered template #} - {% filter upper %} - {% include 'template.html' %} - {% endfilter %} - {# 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/vendor/twig/twig/doc/tags/index.rst b/vendor/twig/twig/doc/tags/index.rst deleted file mode 100644 index 530f95f..0000000 --- a/vendor/twig/twig/doc/tags/index.rst +++ /dev/null @@ -1,27 +0,0 @@ -Tags -==== - -.. toctree:: - :maxdepth: 1 - - apply - autoescape - block - deprecated - do - embed - extends - filter - flush - for - from - if - import - include - macro - sandbox - set - spaceless - use - verbatim - with diff --git a/vendor/twig/twig/doc/tags/macro.rst b/vendor/twig/twig/doc/tags/macro.rst deleted file mode 100644 index aa40acb..0000000 --- a/vendor/twig/twig/doc/tags/macro.rst +++ /dev/null @@ -1,180 +0,0 @@ -``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.html``): - -.. code-block:: 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.html" as forms %} - -The above ``import`` call imports the ``forms.html`` file (which can contain -only macros, or a template and some macros), and import the macros as items of -the ``forms`` local variable. - -The macros can then be called at will in the *current* template: - -.. code-block:: 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:: twig - - {% from 'forms.html' import input as input_field, textarea %} - -

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

    -

    {{ textarea('comment') }}

    - -.. 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:: twig - -

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

    - - {% macro input(name, value, type = "text", size = 20) %} - - {% endmacro %} - - Auto-import is only available as of Twig 2.11. For older versions, import - macros using the special ``_self`` variable for the template name: - - .. code-block:: twig - - {% import _self as forms %} - -

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

    - -.. note:: - - Before Twig 2.11, when you want to use a macro in another macro from the - same file, you need to import it locally: - - .. code-block:: twig - - {% macro input(name, value, type, size) %} - - {% endmacro %} - - {% macro wrapped_input(name, value, type, size) %} - {% import _self as forms %} - -
    - {{ forms.input(name, value, type, size) }} -
    - {% endmacro %} - -Macros Scoping --------------- - -.. versionadded:: 2.11 - - The scoping rules described in this paragraph are implemented as of Twig - 2.11. - -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 explicitely re-import macros in each template. - -When calling ``import`` or ``from`` from a ``block`` tag, the imported macros -are only defined in the current block and they override macros defined at the -template level with the same names. - -When calling ``import`` or ``from`` from a ``macro`` tag, the imported macros -are only defined in the current macro and they override macros defined at the -template level with the same names. - -.. note:: - - Before Twig 2.11, it was possible to use macros imported in a block in a - "sub-block". When upgrading to 2.11, you need to either move the import in - the global scope or reimport the macros explicitly in the "sub-blocks". - -Checking if a Macro is defined ------------------------------- - -.. versionadded:: 2.11 - - Support for the ``defined`` test on macros was added in Twig 2.11. - -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/vendor/twig/twig/doc/tags/sandbox.rst b/vendor/twig/twig/doc/tags/sandbox.rst deleted file mode 100644 index b331fdb..0000000 --- a/vendor/twig/twig/doc/tags/sandbox.rst +++ /dev/null @@ -1,30 +0,0 @@ -``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/vendor/twig/twig/doc/tags/set.rst b/vendor/twig/twig/doc/tags/set.rst deleted file mode 100644 index f752fdd..0000000 --- a/vendor/twig/twig/doc/tags/set.rst +++ /dev/null @@ -1,78 +0,0 @@ -``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:: 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/vendor/twig/twig/doc/tags/spaceless.rst b/vendor/twig/twig/doc/tags/spaceless.rst deleted file mode 100644 index 2608538..0000000 --- a/vendor/twig/twig/doc/tags/spaceless.rst +++ /dev/null @@ -1,41 +0,0 @@ -``spaceless`` -============= - -.. tip:: - - As of Twig 1.38, use the :doc:`spaceless <../filters/spaceless>` filter instead. - -Use the ``spaceless`` tag to remove whitespace *between HTML tags*, not -whitespace within HTML tags or whitespace in plain text: - -.. code-block:: twig - - {% spaceless %} -
    - foo -
    - {% endspaceless %} - - {# output will be
    foo
    #} - -This tag is not meant to "optimize" the size of the generated HTML content but -merely to avoid extra whitespace between HTML tags to avoid browser rendering -quirks under some circumstances. - -.. tip:: - - If you want to optimize the size of the generated HTML content, gzip - compress the output instead. - -.. tip:: - - If you want to create a tag that actually removes all extra whitespace in - an HTML string, be warned that this is not as easy as it seems to be - (think of ``textarea`` or ``pre`` tags for instance). Using a third-party - library like Tidy is probably a better idea. - -.. tip:: - - For more information on whitespace control, read the - :ref:`dedicated section ` of the documentation and learn how - you can also use the whitespace control modifier on your tags. diff --git a/vendor/twig/twig/doc/tags/use.rst b/vendor/twig/twig/doc/tags/use.rst deleted file mode 100644 index 2aca6a0..0000000 --- a/vendor/twig/twig/doc/tags/use.rst +++ /dev/null @@ -1,117 +0,0 @@ -``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/vendor/twig/twig/doc/tags/verbatim.rst b/vendor/twig/twig/doc/tags/verbatim.rst deleted file mode 100644 index 88c6152..0000000 --- a/vendor/twig/twig/doc/tags/verbatim.rst +++ /dev/null @@ -1,16 +0,0 @@ -``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:: twig - - {% verbatim %} -
      - {% for item in seq %} -
    • {{ item }}
    • - {% endfor %} -
    - {% endverbatim %} diff --git a/vendor/twig/twig/doc/tags/with.rst b/vendor/twig/twig/doc/tags/with.rst deleted file mode 100644 index 107432f..0000000 --- a/vendor/twig/twig/doc/tags/with.rst +++ /dev/null @@ -1,41 +0,0 @@ -``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 -hash 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 hash #} - {% 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/vendor/twig/twig/doc/templates.rst b/vendor/twig/twig/doc/templates.rst deleted file mode 100644 index b83e96a..0000000 --- a/vendor/twig/twig/doc/templates.rst +++ /dev/null @@ -1,865 +0,0 @@ -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 `Jinja syntax plugin`_ or the `vim-twig plugin`_ -* *Netbeans* via the `Twig syntax plugin`_ (until 7.1, native as of 7.2) -* *PhpStorm* (native as of 2.1) -* *Eclipse* via the `Twig plugin`_ -* *Sublime Text* via the `Twig bundle`_ -* *GtkSourceView* via the `Twig language definition`_ (used by gedit and other projects) -* *Coda* and *SubEthaEdit* via the `Twig syntax mode`_ -* *Coda 2* via the `other Twig syntax mode`_ -* *Komodo* and *Komodo Edit* via the Twig highlight/syntax check mode -* *Notepad++* via the `Notepad++ Twig Highlighter`_ -* *Emacs* via `web-mode.el`_ -* *Atom* via the `PHP-twig for atom`_ -* *Visual Studio Code* via the `Twig pack`_ - -Also, `TwigFiddle`_ is an online service that allows you to execute Twig templates -from a browser; it supports all versions of Twig. - -Variables ---------- - -The application passes variables to the templates for manipulation in the -template. Variables may have attributes or elements you can access, too. The -visual representation of a variable depends heavily on the application providing -it. - -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. - -.. sidebar:: Implementation - - For convenience's sake ``foo.bar`` does the following things on the PHP - layer: - - * check if ``foo`` is an array and ``bar`` a valid element; - * if not, and if ``foo`` is an object, check that ``bar`` is a valid property; - * if not, and if ``foo`` is an object, check that ``bar`` is a valid method - (even if ``bar`` is the constructor - use ``__construct()`` instead); - * if not, and if ``foo`` is an object, check that ``getBar`` is a valid method; - * if not, and if ``foo`` is an object, check that ``isBar`` is a valid method; - * if not, and if ``foo`` is an object, check that ``hasBar`` is a valid method; - * if not, return a ``null`` value. - - Twig also supports a specific syntax for accessing items on PHP arrays, - ``foo['bar']``: - - * check if ``foo`` is an array and ``bar`` a valid element; - * if not, return a ``null`` value. - -If a variable or attribute does not exist, you will receive a ``null`` value -when the ``strict_variables`` option is set to ``false``; alternatively, if ``strict_variables`` -is set, Twig will throw an error (see :ref:`environment options`). - -.. 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. - -.. note:: - - The ``apply`` tag was introduced in Twig 2.9; use the ``filter`` tag with - previous versions. - -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 ---------------- - -.. 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 and filter documentation page has a section where the names - of all arguments are listed when supported. - -Control Structure ------------------ - -A control structure refers to all those things that control the flow of a -program - conditionals (i.e. ``if``/``elseif``/``else``), ``for``-loops, as -well as things like blocks. Control structures appear inside ``{% ... %}`` -blocks. - -For example, to display a list of users provided in a variable called -``users``, use the :doc:`for` tag: - -.. code-block:: twig - -

    Members

    -
      - {% for user in users %} -
    • {{ user.username|e }}
    • - {% endfor %} -
    - -The :doc:`if` tag can be used to test an expression: - -.. code-block:: 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:: 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:: 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 an other 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. - -.. note:: - - The operator precedence is as follows, with the lowest-precedence operators - listed first: ``?:`` (ternary operator), ``b-and``, ``b-xor``, ``b-or``, - ``or``, ``and``, ``==``, ``!=``, ``<=>``, ``<``, ``>``, ``>=``, ``<=``, - ``in``, ``matches``, ``starts with``, ``ends with``, ``..``, ``+``, ``-``, - ``~``, ``*``, ``/``, ``//``, ``%``, ``is`` (tests), ``**``, ``??``, ``|`` - (filters), ``[]``, and ``.``: - - .. code-block:: twig - - {% set greeting = 'Hello ' %} - {% set name = 'Fabien' %} - - {{ greeting ~ name|lower }} {# Hello fabien #} - - {# use parenthesis to change precedence #} - {{ (greeting ~ name)|lower }} {# hello fabien #} - -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). A 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"]``: Arrays are defined by a sequence of expressions - separated by a comma (``,``) and wrapped with squared brackets (``[]``). - -* ``{"foo": "bar"}``: Hashes are defined by a list of keys and values - separated by a comma (``,``) and wrapped with curly braces (``{}``): - - .. code-block:: twig - - {# keys as string #} - { 'foo': 'foo', 'bar': 'bar' } - - {# keys as names (equivalent to the previous hash) #} - { foo: 'foo', bar: 'bar' } - - {# keys as integer #} - { 2: 'foo', 4: 'bar' } - - {# 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``. - -Arrays and hashes 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. - -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 ``<=``. - -You can also 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 %} - -.. note:: - - For complex string comparisons, the ``matches`` operator allows you to use - `regular expressions`_: - - .. code-block:: twig - - {% if phone matches '/^[\\d\\.]+$/' %} - {% endif %} - -Containment Operator -~~~~~~~~~~~~~~~~~~~~ - -The ``in`` operator performs containment test. It returns ``true`` if the left -operand is contained in the right: - -.. code-block:: twig - - {# returns true #} - - {{ 1 in [1, 2, 3] }} - - {{ 'cd' in 'abcde' }} - -.. tip:: - - You can use this filter to perform a containment test on strings, arrays, - or objects implementing the ``Traversable`` interface. - -To perform a negative test, use the ``not in`` operator: - -.. code-block:: 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 - - {{ 1..5 }} - - {# equivalent to #} - {{ range(1, 5) }} - - Note that you must use parentheses when combining it with the filter operator - due to the :ref:`operator precedence rules `: - - .. code-block:: 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' }} - -.. _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" }} - -.. _templates-whitespace-control: - -Whitespace Control ------------------- - -.. versionadded:: 2.8 - Tag level Line whitespace control was added in Twig 2.8. - -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:: 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:: twig - - {% apply spaceless %} -
    - foo bar -
    - {% endapply %} - - {# output will be
    foo bar
    #} - - The ``apply`` tag was introduced in Twig 2.9; use the ``filter`` tag with - previous versions. - -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/Anomareh/PHP-Twig.tmbundle -.. _`Jinja syntax plugin`: http://jinja.pocoo.org/docs/integration/#vim -.. _`vim-twig plugin`: https://github.com/lumiliet/vim-twig -.. _`Twig syntax plugin`: http://plugins.netbeans.org/plugin/37069/php-twig -.. _`Twig plugin`: https://github.com/pulse00/Twig-Eclipse-Plugin -.. _`Twig language definition`: https://github.com/gabrielcorpse/gedit-twig-template-language -.. _`Twig syntax mode`: https://github.com/bobthecow/Twig-HTML.mode -.. _`other Twig syntax mode`: https://github.com/muxx/Twig-HTML.mode -.. _`Notepad++ Twig Highlighter`: https://github.com/Banane9/notepadplusplus-twig -.. _`web-mode.el`: http://web-mode.org/ -.. _`regular expressions`: https://secure.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 diff --git a/vendor/twig/twig/doc/tests/constant.rst b/vendor/twig/twig/doc/tests/constant.rst deleted file mode 100644 index 448c238..0000000 --- a/vendor/twig/twig/doc/tests/constant.rst +++ /dev/null @@ -1,19 +0,0 @@ -``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/vendor/twig/twig/doc/tests/defined.rst b/vendor/twig/twig/doc/tests/defined.rst deleted file mode 100644 index 234a289..0000000 --- a/vendor/twig/twig/doc/tests/defined.rst +++ /dev/null @@ -1,30 +0,0 @@ -``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/vendor/twig/twig/doc/tests/divisibleby.rst b/vendor/twig/twig/doc/tests/divisibleby.rst deleted file mode 100644 index 8032d34..0000000 --- a/vendor/twig/twig/doc/tests/divisibleby.rst +++ /dev/null @@ -1,10 +0,0 @@ -``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/vendor/twig/twig/doc/tests/empty.rst b/vendor/twig/twig/doc/tests/empty.rst deleted file mode 100644 index 2cd2806..0000000 --- a/vendor/twig/twig/doc/tests/empty.rst +++ /dev/null @@ -1,22 +0,0 @@ -``empty`` -========= - -.. versionadded:: 2.3 - - Support for the ``__toString()`` magic method has been added in Twig 2.3. - -``empty`` checks if a variable is an empty string, an empty array, an empty -hash, 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/vendor/twig/twig/doc/tests/even.rst b/vendor/twig/twig/doc/tests/even.rst deleted file mode 100644 index 5d9c876..0000000 --- a/vendor/twig/twig/doc/tests/even.rst +++ /dev/null @@ -1,10 +0,0 @@ -``even`` -======== - -``even`` returns ``true`` if the given number is even: - -.. code-block:: twig - - {{ var is even }} - -.. seealso:: :doc:`odd<../tests/odd>` diff --git a/vendor/twig/twig/doc/tests/index.rst b/vendor/twig/twig/doc/tests/index.rst deleted file mode 100644 index c63208e..0000000 --- a/vendor/twig/twig/doc/tests/index.rst +++ /dev/null @@ -1,15 +0,0 @@ -Tests -===== - -.. toctree:: - :maxdepth: 1 - - constant - defined - divisibleby - empty - even - iterable - null - odd - sameas diff --git a/vendor/twig/twig/doc/tests/iterable.rst b/vendor/twig/twig/doc/tests/iterable.rst deleted file mode 100644 index 4ebfe9d..0000000 --- a/vendor/twig/twig/doc/tests/iterable.rst +++ /dev/null @@ -1,16 +0,0 @@ -``iterable`` -============ - -``iterable`` checks if a variable is an array or a traversable object: - -.. code-block:: twig - - {# evaluates to true if the foo variable is iterable #} - {% if users is iterable %} - {% for user in users %} - Hello {{ user }}! - {% endfor %} - {% else %} - {# users is probably a string #} - Hello {{ users }}! - {% endif %} diff --git a/vendor/twig/twig/doc/tests/null.rst b/vendor/twig/twig/doc/tests/null.rst deleted file mode 100644 index 9ed93f6..0000000 --- a/vendor/twig/twig/doc/tests/null.rst +++ /dev/null @@ -1,12 +0,0 @@ -``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/vendor/twig/twig/doc/tests/odd.rst b/vendor/twig/twig/doc/tests/odd.rst deleted file mode 100644 index 0546f83..0000000 --- a/vendor/twig/twig/doc/tests/odd.rst +++ /dev/null @@ -1,10 +0,0 @@ -``odd`` -======= - -``odd`` returns ``true`` if the given number is odd: - -.. code-block:: twig - - {{ var is odd }} - -.. seealso:: :doc:`even<../tests/even>` diff --git a/vendor/twig/twig/doc/tests/sameas.rst b/vendor/twig/twig/doc/tests/sameas.rst deleted file mode 100644 index c092971..0000000 --- a/vendor/twig/twig/doc/tests/sameas.rst +++ /dev/null @@ -1,11 +0,0 @@ -``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/vendor/twig/twig/drupal_test.sh b/vendor/twig/twig/drupal_test.sh deleted file mode 100755 index 75d5f02..0000000 --- a/vendor/twig/twig/drupal_test.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/bin/bash - -set -x -set -e - -REPO=`pwd` -cd /tmp -rm -rf drupal-twig-test -composer create-project --no-interaction drupal-composer/drupal-project:8.x-dev drupal-twig-test -cd drupal-twig-test -(cd vendor/twig && rm -rf twig && ln -sf $REPO twig) -echo '$config["system.logging"]["error_level"] = "verbose";' >> web/sites/default/settings.php -composer require drupal/core:8.7.x-dev webflo/drupal-core-require-dev:8.7.x-dev "egulias/email-validator:^2.0" -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 - -wget https://get.symfony.com/cli/installer -O - | bash -export PATH="$HOME/.symfony/bin:$PATH" -symfony server:start -d --no-tls - -curl -OLsS https://get.blackfire.io/blackfire-player.phar -chmod +x blackfire-player.phar -cat > drupal-tests.bkf < + */ +abstract class AbstractTwigCallable implements TwigCallableInterface +{ + protected $options; + + private $name; + private $dynamicName; + private $callable; + private $arguments; + + public function __construct(string $name, $callable = null, array $options = []) + { + $this->name = $this->dynamicName = $name; + $this->callable = $callable; + $this->arguments = []; + $this->options = array_merge([ + 'needs_environment' => false, + 'needs_context' => false, + 'needs_charset' => false, + 'is_variadic' => false, + 'deprecation_info' => null, + 'deprecated' => false, + 'deprecating_package' => '', + 'alternative' => null, + ], $options); + + if ($this->options['deprecation_info'] && !$this->options['deprecation_info'] instanceof DeprecatedCallableInfo) { + throw new \LogicException(\sprintf('The "deprecation_info" option must be an instance of "%s".', DeprecatedCallableInfo::class)); + } + + if ($this->options['deprecated']) { + if ($this->options['deprecation_info']) { + throw new \LogicException('When setting the "deprecation_info" option, you need to remove the obsolete deprecated options.'); + } + + trigger_deprecation('twig/twig', '3.15', 'Using the "deprecated", "deprecating_package", and "alternative" options is deprecated, pass a "deprecation_info" one instead.'); + + $this->options['deprecation_info'] = new DeprecatedCallableInfo( + $this->options['deprecating_package'], + $this->options['deprecated'], + null, + $this->options['alternative'], + ); + } + + if ($this->options['deprecation_info']) { + $this->options['deprecation_info']->setName($name); + $this->options['deprecation_info']->setType($this->getType()); + } + } + + public function __toString(): string + { + return \sprintf('%s(%s)', static::class, $this->name); + } + + public function getName(): string + { + return $this->name; + } + + public function getDynamicName(): string + { + return $this->dynamicName; + } + + /** + * @return callable|array{class-string, string}|null + */ + public function getCallable() + { + return $this->callable; + } + + public function getNodeClass(): string + { + return $this->options['node_class']; + } + + public function needsCharset(): bool + { + return $this->options['needs_charset']; + } + + public function needsEnvironment(): bool + { + return $this->options['needs_environment']; + } + + public function needsContext(): bool + { + return $this->options['needs_context']; + } + + /** + * @return static + */ + public function withDynamicArguments(string $name, string $dynamicName, array $arguments): self + { + $new = clone $this; + $new->name = $name; + $new->dynamicName = $dynamicName; + $new->arguments = $arguments; + + return $new; + } + + /** + * @deprecated since Twig 3.12, use withDynamicArguments() instead + */ + public function setArguments(array $arguments): void + { + trigger_deprecation('twig/twig', '3.12', 'The "%s::setArguments()" method is deprecated, use "%s::withDynamicArguments()" instead.', static::class, static::class); + + $this->arguments = $arguments; + } + + public function getArguments(): array + { + return $this->arguments; + } + + public function isVariadic(): bool + { + return $this->options['is_variadic']; + } + + public function isDeprecated(): bool + { + return (bool) $this->options['deprecation_info']; + } + + public function triggerDeprecation(?string $file = null, ?int $line = null): void + { + $this->options['deprecation_info']->triggerDeprecation($file, $line); + } + + /** + * @deprecated since Twig 3.15 + */ + public function getDeprecatingPackage(): string + { + trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class); + + return $this->options['deprecating_package']; + } + + /** + * @deprecated since Twig 3.15 + */ + public function getDeprecatedVersion(): string + { + trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class); + + return \is_bool($this->options['deprecated']) ? '' : $this->options['deprecated']; + } + + /** + * @deprecated since Twig 3.15 + */ + public function getAlternative(): ?string + { + trigger_deprecation('twig/twig', '3.15', 'The "%s" method is deprecated, use "%s::triggerDeprecation()" instead.', __METHOD__, static::class); + + return $this->options['alternative']; + } + + public function getMinimalNumberOfRequiredArguments(): int + { + return ($this->options['needs_charset'] ? 1 : 0) + ($this->options['needs_environment'] ? 1 : 0) + ($this->options['needs_context'] ? 1 : 0) + \count($this->arguments); + } +} diff --git a/vendor/twig/twig/src/Attribute/AsTwigFilter.php b/vendor/twig/twig/src/Attribute/AsTwigFilter.php new file mode 100644 index 0000000..395531d --- /dev/null +++ b/vendor/twig/twig/src/Attribute/AsTwigFilter.php @@ -0,0 +1,56 @@ + + */ +final class ChainCache implements CacheInterface, RemovableCacheInterface +{ + /** + * @param iterable $caches The ordered list of caches used to store and fetch cached items + */ + public function __construct( + private iterable $caches, + ) { + } + + public function generateKey(string $name, string $className): string + { + return $className.'#'.$name; + } + + public function write(string $key, string $content): void + { + $splitKey = $this->splitKey($key); + + foreach ($this->caches as $cache) { + $cache->write($cache->generateKey(...$splitKey), $content); + } + } + + public function load(string $key): void + { + [$name, $className] = $this->splitKey($key); + + foreach ($this->caches as $cache) { + $cache->load($cache->generateKey($name, $className)); + + if (class_exists($className, false)) { + break; + } + } + } + + public function getTimestamp(string $key): int + { + $splitKey = $this->splitKey($key); + + foreach ($this->caches as $cache) { + if (0 < $timestamp = $cache->getTimestamp($cache->generateKey(...$splitKey))) { + return $timestamp; + } + } + + return 0; + } + + public function remove(string $name, string $cls): void + { + foreach ($this->caches as $cache) { + if ($cache instanceof RemovableCacheInterface) { + $cache->remove($name, $cls); + } + } + } + + /** + * @return string[] + */ + private function splitKey(string $key): array + { + return array_reverse(explode('#', $key, 2)); + } +} diff --git a/vendor/twig/twig/src/Cache/FilesystemCache.php b/vendor/twig/twig/src/Cache/FilesystemCache.php index b7c1e43..5840585 100644 --- a/vendor/twig/twig/src/Cache/FilesystemCache.php +++ b/vendor/twig/twig/src/Cache/FilesystemCache.php @@ -16,49 +16,45 @@ namespace Twig\Cache; * * @author Andrew Tch */ -class FilesystemCache implements CacheInterface +class FilesystemCache implements CacheInterface, RemovableCacheInterface { - const FORCE_BYTECODE_INVALIDATION = 1; + public const FORCE_BYTECODE_INVALIDATION = 1; private $directory; private $options; - /** - * @param string $directory The root cache directory - * @param int $options A set of options - */ - public function __construct($directory, $options = 0) + public function __construct(string $directory, int $options = 0) { $this->directory = rtrim($directory, '\/').'/'; $this->options = $options; } - public function generateKey($name, $className) + public function generateKey(string $name, string $className): string { - $hash = hash('sha256', $className); + $hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $className); return $this->directory.$hash[0].$hash[1].'/'.$hash.'.php'; } - public function load($key) + public function load(string $key): void { - if (file_exists($key)) { + if (is_file($key)) { @include_once $key; } } - public function write($key, $content) + public function write(string $key, string $content): void { $dir = \dirname($key); if (!is_dir($dir)) { if (false === @mkdir($dir, 0777, true)) { clearstatcache(true, $dir); if (!is_dir($dir)) { - throw new \RuntimeException(sprintf('Unable to create the cache directory (%s).', $dir)); + throw new \RuntimeException(\sprintf('Unable to create the cache directory (%s).', $dir)); } } } elseif (!is_writable($dir)) { - throw new \RuntimeException(sprintf('Unable to write in the cache directory (%s).', $dir)); + throw new \RuntimeException(\sprintf('Unable to write in the cache directory (%s).', $dir)); } $tmpFile = tempnam($dir, basename($key)); @@ -67,7 +63,7 @@ class FilesystemCache implements CacheInterface if (self::FORCE_BYTECODE_INVALIDATION == ($this->options & self::FORCE_BYTECODE_INVALIDATION)) { // Compile cached file into bytecode cache - if (\function_exists('opcache_invalidate') && filter_var(ini_get('opcache.enable'), FILTER_VALIDATE_BOOLEAN)) { + if (\function_exists('opcache_invalidate') && filter_var(\ini_get('opcache.enable'), \FILTER_VALIDATE_BOOLEAN)) { @opcache_invalidate($key, true); } elseif (\function_exists('apc_compile_file')) { apc_compile_file($key); @@ -77,17 +73,23 @@ class FilesystemCache implements CacheInterface return; } - throw new \RuntimeException(sprintf('Failed to write cache file "%s".', $key)); + throw new \RuntimeException(\sprintf('Failed to write cache file "%s".', $key)); } - public function getTimestamp($key) + public function remove(string $name, string $cls): void { - if (!file_exists($key)) { + $key = $this->generateKey($name, $cls); + if (!@unlink($key) && file_exists($key)) { + throw new \RuntimeException(\sprintf('Failed to delete cache file "%s".', $key)); + } + } + + public function getTimestamp(string $key): int + { + if (!is_file($key)) { return 0; } return (int) @filemtime($key); } } - -class_alias('Twig\Cache\FilesystemCache', 'Twig_Cache_Filesystem'); diff --git a/vendor/twig/twig/src/Cache/NullCache.php b/vendor/twig/twig/src/Cache/NullCache.php index 02c868c..1ae2169 100644 --- a/vendor/twig/twig/src/Cache/NullCache.php +++ b/vendor/twig/twig/src/Cache/NullCache.php @@ -16,25 +16,27 @@ namespace Twig\Cache; * * @author Fabien Potencier */ -final class NullCache implements CacheInterface +final class NullCache implements CacheInterface, RemovableCacheInterface { - public function generateKey($name, $className) + public function generateKey(string $name, string $className): string { return ''; } - public function write($key, $content) + public function write(string $key, string $content): void { } - public function load($key) + public function load(string $key): void { } - public function getTimestamp($key) + public function getTimestamp(string $key): int { return 0; } -} -class_alias('Twig\Cache\NullCache', 'Twig_Cache_Null'); + public function remove(string $name, string $cls): void + { + } +} diff --git a/vendor/twig/twig/src/Cache/ReadOnlyFilesystemCache.php b/vendor/twig/twig/src/Cache/ReadOnlyFilesystemCache.php new file mode 100644 index 0000000..3ba6514 --- /dev/null +++ b/vendor/twig/twig/src/Cache/ReadOnlyFilesystemCache.php @@ -0,0 +1,25 @@ + + */ +class ReadOnlyFilesystemCache extends FilesystemCache +{ + public function write(string $key, string $content): void + { + // Do nothing with the content, it's a read-only filesystem. + } +} diff --git a/vendor/twig/twig/src/Cache/RemovableCacheInterface.php b/vendor/twig/twig/src/Cache/RemovableCacheInterface.php new file mode 100644 index 0000000..05da569 --- /dev/null +++ b/vendor/twig/twig/src/Cache/RemovableCacheInterface.php @@ -0,0 +1,20 @@ + + */ +interface RemovableCacheInterface +{ + public function remove(string $name, string $cls): void; +} diff --git a/vendor/twig/twig/src/Compiler.php b/vendor/twig/twig/src/Compiler.php index 56933e2..6f62c09 100644 --- a/vendor/twig/twig/src/Compiler.php +++ b/vendor/twig/twig/src/Compiler.php @@ -15,8 +15,6 @@ namespace Twig; use Twig\Node\Node; /** - * Compiles a node to PHP code. - * * @author Fabien Potencier */ class Compiler @@ -24,45 +22,32 @@ class Compiler private $lastLine; private $source; private $indentation; - private $env; private $debugInfo = []; private $sourceOffset; private $sourceLine; private $varNameSalt = 0; + private $didUseEcho = false; + private $didUseEchoStack = []; - public function __construct(Environment $env) - { - $this->env = $env; + public function __construct( + private Environment $env, + ) { } - /** - * Returns the environment instance related to this compiler. - * - * @return Environment - */ - public function getEnvironment() + public function getEnvironment(): Environment { return $this->env; } - /** - * Gets the current PHP code after compilation. - * - * @return string The PHP code - */ - public function getSource() + public function getSource(): string { return $this->source; } /** - * Compiles a node. - * - * @param int $indentation The current indentation - * * @return $this */ - public function compile(Node $node, $indentation = 0) + public function reset(int $indentation = 0) { $this->lastLine = null; $this->source = ''; @@ -73,31 +58,64 @@ class Compiler $this->indentation = $indentation; $this->varNameSalt = 0; - $node->compile($this); - return $this; } - public function subcompile(Node $node, $raw = true) + /** + * @return $this + */ + public function compile(Node $node, int $indentation = 0) { - if (false === $raw) { + $this->reset($indentation); + $this->didUseEchoStack[] = $this->didUseEcho; + + try { + $this->didUseEcho = false; + $node->compile($this); + + if ($this->didUseEcho) { + trigger_deprecation('twig/twig', '3.9', 'Using "%s" is deprecated, use "yield" instead in "%s", then flag the class with #[\Twig\Attribute\YieldReady].', $this->didUseEcho, $node::class); + } + + return $this; + } finally { + $this->didUseEcho = array_pop($this->didUseEchoStack); + } + } + + /** + * @return $this + */ + public function subcompile(Node $node, bool $raw = true) + { + if (!$raw) { $this->source .= str_repeat(' ', $this->indentation * 4); } - $node->compile($this); + $this->didUseEchoStack[] = $this->didUseEcho; - return $this; + try { + $this->didUseEcho = false; + $node->compile($this); + + if ($this->didUseEcho) { + trigger_deprecation('twig/twig', '3.9', 'Using "%s" is deprecated, use "yield" instead in "%s", then flag the class with #[\Twig\Attribute\YieldReady].', $this->didUseEcho, $node::class); + } + + return $this; + } finally { + $this->didUseEcho = array_pop($this->didUseEchoStack); + } } /** * Adds a raw string to the compiled code. * - * @param string $string The string - * * @return $this */ - public function raw($string) + public function raw(string $string) { + $this->checkForEcho($string); $this->source .= $string; return $this; @@ -111,6 +129,7 @@ class Compiler public function write(...$strings) { foreach ($strings as $string) { + $this->checkForEcho($string); $this->source .= str_repeat(' ', $this->indentation * 4).$string; } @@ -120,13 +139,11 @@ class Compiler /** * Adds a quoted string to the compiled code. * - * @param string $value The string - * * @return $this */ - public function string($value) + public function string(string $value) { - $this->source .= sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); + $this->source .= \sprintf('"%s"', addcslashes($value, "\0\t\"\$\\")); return $this; } @@ -134,28 +151,26 @@ class Compiler /** * Returns a PHP representation of a given value. * - * @param mixed $value The value to convert - * * @return $this */ public function repr($value) { if (\is_int($value) || \is_float($value)) { - if (false !== $locale = setlocale(LC_NUMERIC, '0')) { - setlocale(LC_NUMERIC, 'C'); + if (false !== $locale = setlocale(\LC_NUMERIC, '0')) { + setlocale(\LC_NUMERIC, 'C'); } $this->raw(var_export($value, true)); if (false !== $locale) { - setlocale(LC_NUMERIC, $locale); + setlocale(\LC_NUMERIC, $locale); } } elseif (null === $value) { $this->raw('null'); } elseif (\is_bool($value)) { $this->raw($value ? 'true' : 'false'); } elseif (\is_array($value)) { - $this->raw('array('); + $this->raw('['); $first = true; foreach ($value as $key => $v) { if (!$first) { @@ -166,7 +181,7 @@ class Compiler $this->raw(' => '); $this->repr($v); } - $this->raw(')'); + $this->raw(']'); } else { $this->string($value); } @@ -175,14 +190,12 @@ class Compiler } /** - * Adds debugging information. - * * @return $this */ public function addDebugInfo(Node $node) { if ($node->getTemplateLine() != $this->lastLine) { - $this->write(sprintf("// line %d\n", $node->getTemplateLine())); + $this->write(\sprintf("// line %d\n", $node->getTemplateLine())); $this->sourceLine += substr_count($this->source, "\n", $this->sourceOffset); $this->sourceOffset = \strlen($this->source); @@ -194,7 +207,7 @@ class Compiler return $this; } - public function getDebugInfo() + public function getDebugInfo(): array { ksort($this->debugInfo); @@ -202,13 +215,9 @@ class Compiler } /** - * Indents the generated code. - * - * @param int $step The number of indentation to add - * * @return $this */ - public function indent($step = 1) + public function indent(int $step = 1) { $this->indentation += $step; @@ -216,15 +225,11 @@ class Compiler } /** - * Outdents the generated code. - * - * @param int $step The number of indentation to remove - * * @return $this * * @throws \LogicException When trying to outdent too much so the indentation would become negative */ - public function outdent($step = 1) + public function outdent(int $step = 1) { // can't outdent by more steps than the current indentation level if ($this->indentation < $step) { @@ -236,10 +241,17 @@ class Compiler return $this; } - public function getVarName() + public function getVarName(): string { - return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->varNameSalt++)); + return \sprintf('_v%d', $this->varNameSalt++); + } + + private function checkForEcho(string $string): void + { + if ($this->didUseEcho) { + return; + } + + $this->didUseEcho = preg_match('/^\s*+(echo|print)\b/', $string, $m) ? $m[1] : false; } } - -class_alias('Twig\Compiler', 'Twig_Compiler'); diff --git a/vendor/twig/twig/src/DeprecatedCallableInfo.php b/vendor/twig/twig/src/DeprecatedCallableInfo.php new file mode 100644 index 0000000..2db9f3d --- /dev/null +++ b/vendor/twig/twig/src/DeprecatedCallableInfo.php @@ -0,0 +1,67 @@ + + */ +final class DeprecatedCallableInfo +{ + private string $type; + private string $name; + + public function __construct( + private string $package, + private string $version, + private ?string $altName = null, + private ?string $altPackage = null, + private ?string $altVersion = null, + ) { + } + + public function setType(string $type): void + { + $this->type = $type; + } + + public function setName(string $name): void + { + $this->name = $name; + } + + public function triggerDeprecation(?string $file = null, ?int $line = null): void + { + $message = \sprintf('Twig %s "%s" is deprecated', ucfirst($this->type), $this->name); + + if ($this->altName) { + $message .= \sprintf('; use "%s"', $this->altName); + if ($this->altPackage) { + $message .= \sprintf(' from the "%s" package', $this->altPackage); + } + if ($this->altVersion) { + $message .= \sprintf(' (available since version %s)', $this->altVersion); + } + $message .= ' instead'; + } + + if ($file) { + $message .= \sprintf(' in %s', $file); + if ($line) { + $message .= \sprintf(' at line %d', $line); + } + } + + $message .= '.'; + + trigger_deprecation($this->package, $this->version, $message); + } +} diff --git a/vendor/twig/twig/src/Environment.php b/vendor/twig/twig/src/Environment.php index d0419fb..ff3f0c5 100644 --- a/vendor/twig/twig/src/Environment.php +++ b/vendor/twig/twig/src/Environment.php @@ -14,36 +14,41 @@ namespace Twig; use Twig\Cache\CacheInterface; use Twig\Cache\FilesystemCache; use Twig\Cache\NullCache; +use Twig\Cache\RemovableCacheInterface; use Twig\Error\Error; use Twig\Error\LoaderError; use Twig\Error\RuntimeError; use Twig\Error\SyntaxError; +use Twig\ExpressionParser\ExpressionParsers; use Twig\Extension\CoreExtension; use Twig\Extension\EscaperExtension; use Twig\Extension\ExtensionInterface; use Twig\Extension\OptimizerExtension; +use Twig\Extension\YieldNotReadyExtension; use Twig\Loader\ArrayLoader; use Twig\Loader\ChainLoader; use Twig\Loader\LoaderInterface; use Twig\Node\ModuleNode; use Twig\Node\Node; use Twig\NodeVisitor\NodeVisitorInterface; +use Twig\Runtime\EscaperRuntime; +use Twig\RuntimeLoader\FactoryRuntimeLoader; use Twig\RuntimeLoader\RuntimeLoaderInterface; use Twig\TokenParser\TokenParserInterface; /** - * Stores the Twig configuration. + * Stores the Twig configuration and renders templates. * * @author Fabien Potencier */ class Environment { - const VERSION = '2.12.5'; - const VERSION_ID = 21205; - const MAJOR_VERSION = 2; - const MINOR_VERSION = 12; - const RELEASE_VERSION = 5; - const EXTRA_VERSION = ''; + public const VERSION = '3.21.1'; + public const VERSION_ID = 32101; + public const MAJOR_VERSION = 3; + public const MINOR_VERSION = 21; + public const RELEASE_VERSION = 1; + public const EXTRA_VERSION = ''; private $charset; private $loader; @@ -53,17 +58,20 @@ class Environment private $lexer; private $parser; private $compiler; - private $baseTemplateClass; + /** @var array */ private $globals = []; private $resolvedGlobals; private $loadedTemplates; private $strictVariables; - private $templateClassPrefix = '__TwigTemplate_'; private $originalCache; private $extensionSet; private $runtimeLoaders = []; private $runtimes = []; private $optionsHash; + /** @var bool */ + private $useYield; + private $defaultRuntimeLoader; + private array $hotCache = []; /** * Constructor. @@ -75,9 +83,6 @@ class Environment * * * charset: The charset used by the templates (default to UTF-8). * - * * base_template_class: The base template class to use for generated - * templates (default to \Twig\Template). - * * * cache: An absolute path where to store the compiled templates, * a \Twig\Cache\CacheInterface implementation, * or false to disable compilation cache (default). @@ -98,67 +103,59 @@ class Environment * * optimizations: A flag that indicates which optimizations to apply * (default to -1 which means that all optimizations are enabled; * set it to 0 to disable). + * + * * use_yield: true: forces templates to exclusively use "yield" instead of "echo" (all extensions must be yield ready) + * false (default): allows templates to use a mix of "yield" and "echo" calls to allow for a progressive migration + * Switch to "true" when possible as this will be the only supported mode in Twig 4.0 */ - public function __construct(LoaderInterface $loader, $options = []) + public function __construct(LoaderInterface $loader, array $options = []) { $this->setLoader($loader); $options = array_merge([ 'debug' => false, 'charset' => 'UTF-8', - 'base_template_class' => Template::class, 'strict_variables' => false, 'autoescape' => 'html', 'cache' => false, 'auto_reload' => null, 'optimizations' => -1, + 'use_yield' => false, ], $options); + $this->useYield = (bool) $options['use_yield']; $this->debug = (bool) $options['debug']; - $this->setCharset($options['charset']); - $this->baseTemplateClass = '\\'.ltrim($options['base_template_class'], '\\'); - if ('\\'.Template::class !== $this->baseTemplateClass && '\Twig_Template' !== $this->baseTemplateClass) { - @trigger_error('The "base_template_class" option on '.__CLASS__.' is deprecated since Twig 2.7.0.', E_USER_DEPRECATED); - } + $this->setCharset($options['charset'] ?? 'UTF-8'); $this->autoReload = null === $options['auto_reload'] ? $this->debug : (bool) $options['auto_reload']; $this->strictVariables = (bool) $options['strict_variables']; $this->setCache($options['cache']); $this->extensionSet = new ExtensionSet(); + $this->defaultRuntimeLoader = new FactoryRuntimeLoader([ + EscaperRuntime::class => function () { return new EscaperRuntime($this->charset); }, + ]); $this->addExtension(new CoreExtension()); - $this->addExtension(new EscaperExtension($options['autoescape'])); + $escaperExt = new EscaperExtension($options['autoescape']); + $escaperExt->setEnvironment($this, false); + $this->addExtension($escaperExt); + if (\PHP_VERSION_ID >= 80000) { + $this->addExtension(new YieldNotReadyExtension($this->useYield)); + } $this->addExtension(new OptimizerExtension($options['optimizations'])); } /** - * Gets the base template class for compiled templates. - * - * @return string The base template class name + * @internal */ - public function getBaseTemplateClass() + public function useYield(): bool { - if (1 > \func_num_args() || \func_get_arg(0)) { - @trigger_error('The '.__METHOD__.' is deprecated since Twig 2.7.0.', E_USER_DEPRECATED); - } - - return $this->baseTemplateClass; - } - - /** - * Sets the base template class for compiled templates. - * - * @param string $class The base template class name - */ - public function setBaseTemplateClass($class) - { - @trigger_error('The '.__METHOD__.' is deprecated since Twig 2.7.0.', E_USER_DEPRECATED); - - $this->baseTemplateClass = $class; - $this->updateOptionsHash(); + return $this->useYield; } /** * Enables debugging mode. + * + * @return void */ public function enableDebug() { @@ -168,6 +165,8 @@ class Environment /** * Disables debugging mode. + * + * @return void */ public function disableDebug() { @@ -187,6 +186,8 @@ class Environment /** * Enables the auto_reload option. + * + * @return void */ public function enableAutoReload() { @@ -195,6 +196,8 @@ class Environment /** * Disables the auto_reload option. + * + * @return void */ public function disableAutoReload() { @@ -213,6 +216,8 @@ class Environment /** * Enables the strict_variables option. + * + * @return void */ public function enableStrictVariables() { @@ -222,6 +227,8 @@ class Environment /** * Disables the strict_variables option. + * + * @return void */ public function disableStrictVariables() { @@ -239,6 +246,18 @@ class Environment return $this->strictVariables; } + public function removeCache(string $name): void + { + $cls = $this->getTemplateClass($name); + $this->hotCache[$name] = $cls.'_'.bin2hex(random_bytes(16)); + + if ($this->cache instanceof RemovableCacheInterface) { + $this->cache->remove($name, $cls); + } else { + throw new \LogicException(\sprintf('The "%s" cache class does not support removing template cache as it does not implement the "RemovableCacheInterface" interface.', \get_class($this->cache))); + } + } + /** * Gets the current cache implementation. * @@ -259,19 +278,21 @@ class Environment * @param CacheInterface|string|false $cache A Twig\Cache\CacheInterface implementation, * an absolute path to the compiled templates, * or false to disable cache + * + * @return void */ public function setCache($cache) { if (\is_string($cache)) { $this->originalCache = $cache; - $this->cache = new FilesystemCache($cache); + $this->cache = new FilesystemCache($cache, $this->autoReload ? FilesystemCache::FORCE_BYTECODE_INVALIDATION : 0); } elseif (false === $cache) { $this->originalCache = $cache; $this->cache = new NullCache(); } elseif ($cache instanceof CacheInterface) { $this->originalCache = $this->cache = $cache; } else { - throw new \LogicException(sprintf('Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.')); + throw new \LogicException('Cache can only be a string, false, or a \Twig\Cache\CacheInterface implementation.'); } } @@ -282,7 +303,6 @@ class Environment * * * The cache key for the given template; * * The currently enabled extensions; - * * Whether the Twig C extension is available or not; * * PHP version; * * Twig version; * * Options with what environment was created. @@ -290,30 +310,25 @@ class Environment * @param string $name The name for which to calculate the template class name * @param int|null $index The index if it is an embedded template * - * @return string The template class name - * * @internal */ - public function getTemplateClass($name, $index = null) + public function getTemplateClass(string $name, ?int $index = null): string { - $key = $this->getLoader()->getCacheKey($name).$this->optionsHash; + $key = ($this->hotCache[$name] ?? $this->getLoader()->getCacheKey($name)).$this->optionsHash; - return $this->templateClassPrefix.hash('sha256', $key).(null === $index ? '' : '___'.$index); + return '__TwigTemplate_'.hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $key).(null === $index ? '' : '___'.$index); } /** * Renders a template. * - * @param string|TemplateWrapper $name The template name - * @param array $context An array of parameters to pass to the template - * - * @return string The rendered template + * @param string|TemplateWrapper $name The template name * * @throws LoaderError When the template cannot be found * @throws SyntaxError When an error occurred during compilation * @throws RuntimeError When an error occurred during rendering */ - public function render($name, array $context = []) + public function render($name, array $context = []): string { return $this->load($name)->render($context); } @@ -321,14 +336,13 @@ class Environment /** * Displays a template. * - * @param string|TemplateWrapper $name The template name - * @param array $context An array of parameters to pass to the template + * @param string|TemplateWrapper $name The template name * * @throws LoaderError When the template cannot be found * @throws SyntaxError When an error occurred during compilation * @throws RuntimeError When an error occurred during rendering */ - public function display($name, array $context = []) + public function display($name, array $context = []): void { $this->load($name)->display($context); } @@ -341,22 +355,19 @@ class Environment * @throws LoaderError When the template cannot be found * @throws RuntimeError When a previously generated cache is corrupted * @throws SyntaxError When an error occurred during compilation - * - * @return TemplateWrapper */ - public function load($name) + public function load($name): TemplateWrapper { if ($name instanceof TemplateWrapper) { return $name; } - if ($name instanceof Template) { - @trigger_error('Passing a \Twig\Template instance to '.__METHOD__.' is deprecated since Twig 2.7.0, use \Twig\TemplateWrapper instead.', E_USER_DEPRECATED); + trigger_deprecation('twig/twig', '3.9', 'Passing a "%s" instance to "%s" is deprecated.', self::class, __METHOD__); - return new TemplateWrapper($this, $name); + return $name; } - return new TemplateWrapper($this, $this->loadTemplate($name)); + return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name)); } /** @@ -365,10 +376,8 @@ class Environment * This method is for internal use only and should never be called * directly. * - * @param string $name The template name - * @param int $index The index if it is an embedded template - * - * @return Template A template instance representing the given template name + * @param string $name The template name + * @param int|null $index The index if it is an embedded template * * @throws LoaderError When the template cannot be found * @throws RuntimeError When a previously generated cache is corrupted @@ -376,15 +385,7 @@ class Environment * * @internal */ - public function loadTemplate($name, $index = null) - { - return $this->loadClass($this->getTemplateClass($name), $name, $index); - } - - /** - * @internal - */ - public function loadClass($cls, $name, $index = null) + public function loadTemplate(string $cls, string $name, ?int $index = null): Template { $mainCls = $cls; if (null !== $index) { @@ -402,12 +403,13 @@ class Environment $this->cache->load($key); } - $source = null; if (!class_exists($cls, false)) { $source = $this->getLoader()->getSourceContext($name); $content = $this->compileSource($source); - $this->cache->write($key, $content); - $this->cache->load($key); + if (!isset($this->hotCache[$name])) { + $this->cache->write($key, $content); + $this->cache->load($key); + } if (!class_exists($mainCls, false)) { /* Last line of defense if either $this->bcWriteCacheFile was used, @@ -419,13 +421,12 @@ class Environment } if (!class_exists($cls, false)) { - throw new RuntimeError(sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.', $name, $index), -1, $source); + throw new RuntimeError(\sprintf('Failed to load Twig template "%s", index "%s": cache might be corrupted.', $name, $index), -1, $source); } } } - // to be removed in 3.0 - $this->extensionSet->initRuntime($this); + $this->extensionSet->initRuntime(); return $this->loadedTemplates[$cls] = new $cls($this); } @@ -435,21 +436,19 @@ class Environment * * This method should not be used as a generic way to load templates. * - * @param string $template The template source - * @param string $name An optional name of the template to be used in error messages - * - * @return TemplateWrapper A template instance representing the given template name + * @param string $template The template source + * @param string|null $name An optional name of the template to be used in error messages * * @throws LoaderError When the template cannot be found * @throws SyntaxError When an error occurred during compilation */ - public function createTemplate($template, string $name = null) + public function createTemplate(string $template, ?string $name = null): TemplateWrapper { - $hash = hash('sha256', $template, false); + $hash = hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $template, false); if (null !== $name) { - $name = sprintf('%s (string template %s)', $name, $hash); + $name = \sprintf('%s (string template %s)', $name, $hash); } else { - $name = sprintf('__string_template__%s', $hash); + $name = \sprintf('__string_template__%s', $hash); } $loader = new ChainLoader([ @@ -459,7 +458,7 @@ class Environment $this->setLoader($loader); try { - return new TemplateWrapper($this, $this->loadTemplate($name)); + return new TemplateWrapper($this, $this->loadTemplate($this->getTemplateClass($name), $name)); } finally { $this->setLoader($current); } @@ -472,12 +471,9 @@ class Environment * this method also checks if the enabled extensions have * not changed. * - * @param string $name The template name - * @param int $time The last modification time of the cached template - * - * @return bool true if the template is fresh, false otherwise + * @param int $time The last modification time of the cached template */ - public function isTemplateFresh($name, $time) + public function isTemplateFresh(string $name, int $time): bool { return $this->extensionSet->getLastModified() <= $time && $this->getLoader()->isFresh($name, $time); } @@ -485,55 +481,53 @@ class Environment /** * Tries to load a template consecutively from an array. * - * Similar to load() but it also accepts instances of \Twig\Template and - * \Twig\TemplateWrapper, and an array of templates where each is tried to be loaded. + * Similar to load() but it also accepts instances of \Twig\TemplateWrapper + * and an array of templates where each is tried to be loaded. * - * @param string|TemplateWrapper|array $names A template or an array of templates to try consecutively - * - * @return TemplateWrapper|Template + * @param string|TemplateWrapper|array $names A template or an array of templates to try consecutively * * @throws LoaderError When none of the templates can be found * @throws SyntaxError When an error occurred during compilation */ - public function resolveTemplate($names) + public function resolveTemplate($names): TemplateWrapper { if (!\is_array($names)) { - $names = [$names]; + return $this->load($names); } + $count = \count($names); foreach ($names as $name) { if ($name instanceof Template) { - return $name; + trigger_deprecation('twig/twig', '3.9', 'Passing a "%s" instance to "%s" is deprecated.', Template::class, __METHOD__); + + return new TemplateWrapper($this, $name); } if ($name instanceof TemplateWrapper) { return $name; } - try { - return $this->loadTemplate($name); - } catch (LoaderError $e) { - if (1 === \count($names)) { - throw $e; - } + if (1 !== $count && !$this->getLoader()->exists($name)) { + continue; } + + return $this->load($name); } - throw new LoaderError(sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names))); + throw new LoaderError(\sprintf('Unable to find one of the following templates: "%s".', implode('", "', $names))); } + /** + * @return void + */ public function setLexer(Lexer $lexer) { $this->lexer = $lexer; } /** - * Tokenizes a source code. - * - * @return TokenStream - * * @throws SyntaxError When the code is syntactically wrong */ - public function tokenize(Source $source) + public function tokenize(Source $source): TokenStream { if (null === $this->lexer) { $this->lexer = new Lexer($this); @@ -542,6 +536,9 @@ class Environment return $this->lexer->tokenize($source); } + /** + * @return void + */ public function setParser(Parser $parser) { $this->parser = $parser; @@ -550,11 +547,9 @@ class Environment /** * Converts a token stream to a node tree. * - * @return ModuleNode - * * @throws SyntaxError When the token stream is syntactically or semantically wrong */ - public function parse(TokenStream $stream) + public function parse(TokenStream $stream): ModuleNode { if (null === $this->parser) { $this->parser = new Parser($this); @@ -563,6 +558,9 @@ class Environment return $this->parser->parse($stream); } + /** + * @return void + */ public function setCompiler(Compiler $compiler) { $this->compiler = $compiler; @@ -570,10 +568,8 @@ class Environment /** * Compiles a node and returns the PHP code. - * - * @return string The compiled PHP source code */ - public function compile(Node $node) + public function compile(Node $node): string { if (null === $this->compiler) { $this->compiler = new Compiler($this); @@ -585,11 +581,9 @@ class Environment /** * Compiles a template source code. * - * @return string The compiled PHP source code - * * @throws SyntaxError When there was an error during tokenizing, parsing or compiling */ - public function compileSource(Source $source) + public function compileSource(Source $source): string { try { return $this->compile($this->parse($this->tokenize($source))); @@ -597,33 +591,29 @@ class Environment $e->setSourceContext($source); throw $e; } catch (\Exception $e) { - throw new SyntaxError(sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e); + throw new SyntaxError(\sprintf('An exception has been thrown during the compilation of a template ("%s").', $e->getMessage()), -1, $source, $e); } } + /** + * @return void + */ public function setLoader(LoaderInterface $loader) { $this->loader = $loader; } - /** - * Gets the Loader instance. - * - * @return LoaderInterface - */ - public function getLoader() + public function getLoader(): LoaderInterface { return $this->loader; } /** - * Sets the default template charset. - * - * @param string $charset The default charset + * @return void */ - public function setCharset($charset) + public function setCharset(string $charset) { - if ('UTF8' === $charset = strtoupper($charset)) { + if ('UTF8' === $charset = strtoupper($charset ?: '')) { // iconv on Windows requires "UTF-8" instead of "UTF8" $charset = 'UTF-8'; } @@ -631,30 +621,18 @@ class Environment $this->charset = $charset; } - /** - * Gets the default template charset. - * - * @return string The default charset - */ - public function getCharset() + public function getCharset(): string { return $this->charset; } - /** - * Returns true if the given extension is registered. - * - * @param string $class The extension class name - * - * @return bool Whether the extension is registered or not - */ - public function hasExtension($class) + public function hasExtension(string $class): bool { return $this->extensionSet->hasExtension($class); } /** - * Adds a runtime loader. + * @return void */ public function addRuntimeLoader(RuntimeLoaderInterface $loader) { @@ -662,27 +640,29 @@ class Environment } /** - * Gets an extension by class name. + * @template TExtension of ExtensionInterface * - * @param string $class The extension class name + * @param class-string $class * - * @return ExtensionInterface + * @return TExtension */ - public function getExtension($class) + public function getExtension(string $class): ExtensionInterface { return $this->extensionSet->getExtension($class); } /** - * Returns the runtime implementation of a Twig element (filter/function/test). + * Returns the runtime implementation of a Twig element (filter/function/tag/test). * - * @param string $class A runtime class name + * @template TRuntime of object * - * @return object The runtime implementation + * @param class-string $class A runtime class name + * + * @return TRuntime The runtime implementation * * @throws RuntimeError When the template cannot be found */ - public function getRuntime($class) + public function getRuntime(string $class) { if (isset($this->runtimes[$class])) { return $this->runtimes[$class]; @@ -694,9 +674,16 @@ class Environment } } - throw new RuntimeError(sprintf('Unable to load the "%s" runtime.', $class)); + if (null !== $runtime = $this->defaultRuntimeLoader->load($class)) { + return $this->runtimes[$class] = $runtime; + } + + throw new RuntimeError(\sprintf('Unable to load the "%s" runtime.', $class)); } + /** + * @return void + */ public function addExtension(ExtensionInterface $extension) { $this->extensionSet->addExtension($extension); @@ -704,9 +691,9 @@ class Environment } /** - * Registers an array of extensions. + * @param ExtensionInterface[] $extensions An array of extensions * - * @param array $extensions An array of extensions + * @return void */ public function setExtensions(array $extensions) { @@ -715,89 +702,85 @@ class Environment } /** - * Returns all registered extensions. - * * @return ExtensionInterface[] An array of extensions (keys are for internal usage only and should not be relied on) */ - public function getExtensions() + public function getExtensions(): array { return $this->extensionSet->getExtensions(); } + /** + * @return void + */ public function addTokenParser(TokenParserInterface $parser) { $this->extensionSet->addTokenParser($parser); } /** - * Gets the registered Token Parsers. - * * @return TokenParserInterface[] * * @internal */ - public function getTokenParsers() + public function getTokenParsers(): array { return $this->extensionSet->getTokenParsers(); } /** - * Gets registered tags. - * - * @return TokenParserInterface[] - * * @internal */ - public function getTags() + public function getTokenParser(string $name): ?TokenParserInterface { - $tags = []; - foreach ($this->getTokenParsers() as $parser) { - $tags[$parser->getTag()] = $parser; - } - - return $tags; + return $this->extensionSet->getTokenParser($name); } + /** + * @param callable(string): (TokenParserInterface|false) $callable + */ + public function registerUndefinedTokenParserCallback(callable $callable): void + { + $this->extensionSet->registerUndefinedTokenParserCallback($callable); + } + + /** + * @return void + */ public function addNodeVisitor(NodeVisitorInterface $visitor) { $this->extensionSet->addNodeVisitor($visitor); } /** - * Gets the registered Node Visitors. - * * @return NodeVisitorInterface[] * * @internal */ - public function getNodeVisitors() + public function getNodeVisitors(): array { return $this->extensionSet->getNodeVisitors(); } + /** + * @return void + */ public function addFilter(TwigFilter $filter) { $this->extensionSet->addFilter($filter); } /** - * Get a filter by name. - * - * Subclasses may override this method and load filters differently; - * so no list of filters is available. - * - * @param string $name The filter name - * - * @return TwigFilter|false - * * @internal */ - public function getFilter($name) + public function getFilter(string $name): ?TwigFilter { return $this->extensionSet->getFilter($name); } - public function registerUndefinedFilterCallback(callable $callable) + /** + * @param callable(string): (TwigFilter|false) $callable + */ + public function registerUndefinedFilterCallback(callable $callable): void { $this->extensionSet->registerUndefinedFilterCallback($callable); } @@ -813,65 +796,57 @@ class Environment * * @internal */ - public function getFilters() + public function getFilters(): array { return $this->extensionSet->getFilters(); } + /** + * @return void + */ public function addTest(TwigTest $test) { $this->extensionSet->addTest($test); } /** - * Gets the registered Tests. - * * @return TwigTest[] * * @internal */ - public function getTests() + public function getTests(): array { return $this->extensionSet->getTests(); } /** - * Gets a test by name. - * - * @param string $name The test name - * - * @return TwigTest|false - * * @internal */ - public function getTest($name) + public function getTest(string $name): ?TwigTest { return $this->extensionSet->getTest($name); } + /** + * @return void + */ public function addFunction(TwigFunction $function) { $this->extensionSet->addFunction($function); } /** - * Get a function by name. - * - * Subclasses may override this method and load functions differently; - * so no list of functions is available. - * - * @param string $name function name - * - * @return TwigFunction|false - * * @internal */ - public function getFunction($name) + public function getFunction(string $name): ?TwigFunction { return $this->extensionSet->getFunction($name); } - public function registerUndefinedFunctionCallback(callable $callable) + /** + * @param callable(string): (TwigFunction|false) $callable + */ + public function registerUndefinedFunctionCallback(callable $callable): void { $this->extensionSet->registerUndefinedFunctionCallback($callable); } @@ -887,7 +862,7 @@ class Environment * * @internal */ - public function getFunctions() + public function getFunctions(): array { return $this->extensionSet->getFunctions(); } @@ -898,13 +873,14 @@ class Environment * New globals can be added before compiling or rendering a template; * but after, you can only update existing globals. * - * @param string $name The global name - * @param mixed $value The global value + * @param mixed $value The global value + * + * @return void */ - public function addGlobal($name, $value) + public function addGlobal(string $name, $value) { if ($this->extensionSet->isInitialized() && !\array_key_exists($name, $this->getGlobals())) { - throw new \LogicException(sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name)); + throw new \LogicException(\sprintf('Unable to add global "%s" as the runtime or the extensions have already been initialized.', $name)); } if (null !== $this->resolvedGlobals) { @@ -915,13 +891,9 @@ class Environment } /** - * Gets the registered Globals. - * - * @return array An array of globals - * - * @internal + * @return array */ - public function getGlobals() + public function getGlobals(): array { if ($this->extensionSet->isInitialized()) { if (null === $this->resolvedGlobals) { @@ -934,62 +906,40 @@ class Environment return array_merge($this->extensionSet->getGlobals(), $this->globals); } - /** - * Merges a context with the defined globals. - * - * @param array $context An array representing the context - * - * @return array The context merged with the globals - */ - public function mergeGlobals(array $context) + public function resetGlobals(): void { - // we don't use array_merge as the context being generally - // bigger than globals, this code is faster. - foreach ($this->getGlobals() as $key => $value) { - if (!\array_key_exists($key, $context)) { - $context[$key] = $value; - } - } - - return $context; + $this->resolvedGlobals = null; + $this->extensionSet->resetGlobals(); + } + + /** + * @deprecated since Twig 3.14 + */ + public function mergeGlobals(array $context): array + { + trigger_deprecation('twig/twig', '3.14', 'The "%s" method is deprecated.', __METHOD__); + + return $context + $this->getGlobals(); } /** - * Gets the registered unary Operators. - * - * @return array An array of unary operators - * * @internal */ - public function getUnaryOperators() + public function getExpressionParsers(): ExpressionParsers { - return $this->extensionSet->getUnaryOperators(); + return $this->extensionSet->getExpressionParsers(); } - /** - * Gets the registered binary Operators. - * - * @return array An array of binary operators - * - * @internal - */ - public function getBinaryOperators() - { - return $this->extensionSet->getBinaryOperators(); - } - - private function updateOptionsHash() + private function updateOptionsHash(): void { $this->optionsHash = implode(':', [ $this->extensionSet->getSignature(), - PHP_MAJOR_VERSION, - PHP_MINOR_VERSION, + \PHP_MAJOR_VERSION, + \PHP_MINOR_VERSION, self::VERSION, (int) $this->debug, - $this->baseTemplateClass, (int) $this->strictVariables, + $this->useYield ? '1' : '0', ]); } } - -class_alias('Twig\Environment', 'Twig_Environment'); diff --git a/vendor/twig/twig/src/Error/Error.php b/vendor/twig/twig/src/Error/Error.php index a64cbcb..015085e 100644 --- a/vendor/twig/twig/src/Error/Error.php +++ b/vendor/twig/twig/src/Error/Error.php @@ -29,201 +29,125 @@ use Twig\Template; * Whenever possible, you must set these information (original template name * and line number) yourself by passing them to the constructor. If some or all * these information are not available from where you throw the exception, then - * this class will guess them automatically (when the line number is set to -1 - * and/or the name is set to null). As this is a costly operation, this - * can be disabled by passing false for both the name and the line number - * when creating a new instance of this class. + * this class will guess them automatically. * * @author Fabien Potencier */ class Error extends \Exception { private $lineno; - private $name; private $rawMessage; - private $sourcePath; - private $sourceCode; + private ?Source $source; + private string $phpFile; + private int $phpLine; /** * Constructor. * - * Set the line number to -1 to enable its automatic guessing. - * Set the name to null to enable its automatic guessing. + * By default, automatic guessing is enabled. * - * @param string $message The error message - * @param int $lineno The template line where the error occurred - * @param Source|string|null $source The source context where the error occurred - * @param \Exception $previous The previous exception + * @param string $message The error message + * @param int $lineno The template line where the error occurred + * @param Source|null $source The source context where the error occurred */ - public function __construct(string $message, int $lineno = -1, $source = null, \Exception $previous = null) + public function __construct(string $message, int $lineno = -1, ?Source $source = null, ?\Throwable $previous = null) { parent::__construct('', 0, $previous); - if (null === $source) { - $name = null; - } elseif (!$source instanceof Source && !$source instanceof \Twig_Source) { - @trigger_error(sprintf('Passing a string as a source to %s is deprecated since Twig 2.6.1; pass a Twig\Source instance instead.', __CLASS__), E_USER_DEPRECATED); - $name = $source; - } else { - $name = $source->getName(); - $this->sourceCode = $source->getCode(); - $this->sourcePath = $source->getPath(); - } - + $this->phpFile = $this->getFile(); + $this->phpLine = $this->getLine(); $this->lineno = $lineno; - $this->name = $name; + $this->source = $source; $this->rawMessage = $message; $this->updateRepr(); } - /** - * Gets the raw message. - * - * @return string The raw message - */ - public function getRawMessage() + public function getRawMessage(): string { return $this->rawMessage; } - /** - * Gets the template line where the error occurred. - * - * @return int The template line - */ - public function getTemplateLine() + public function getTemplateLine(): int { return $this->lineno; } - /** - * Sets the template line where the error occurred. - * - * @param int $lineno The template line - */ - public function setTemplateLine($lineno) + public function setTemplateLine(int $lineno): void { $this->lineno = $lineno; - $this->updateRepr(); } - /** - * Gets the source context of the Twig template where the error occurred. - * - * @return Source|null - */ - public function getSourceContext() + public function getSourceContext(): ?Source { - return $this->name ? new Source($this->sourceCode, $this->name, $this->sourcePath) : null; + return $this->source; } - /** - * Sets the source context of the Twig template where the error occurred. - */ - public function setSourceContext(Source $source = null) + public function setSourceContext(?Source $source = null): void { - if (null === $source) { - $this->sourceCode = $this->name = $this->sourcePath = null; - } else { - $this->sourceCode = $source->getCode(); - $this->name = $source->getName(); - $this->sourcePath = $source->getPath(); + $this->source = $source; + $this->updateRepr(); + } + + public function guess(): void + { + if ($this->lineno > -1) { + return; } - $this->updateRepr(); - } - - public function guess() - { $this->guessTemplateInfo(); $this->updateRepr(); } - public function appendMessage($rawMessage) + public function appendMessage($rawMessage): void { $this->rawMessage .= $rawMessage; $this->updateRepr(); } - private function updateRepr() + private function updateRepr(): void { - $this->message = $this->rawMessage; - - if ($this->sourcePath && $this->lineno > 0) { - $this->file = $this->sourcePath; - $this->line = $this->lineno; - - return; - } - - $dot = false; - if ('.' === substr($this->message, -1)) { - $this->message = substr($this->message, 0, -1); - $dot = true; - } - - $questionMark = false; - if ('?' === substr($this->message, -1)) { - $this->message = substr($this->message, 0, -1); - $questionMark = true; - } - - if ($this->name) { - if (\is_string($this->name) || (\is_object($this->name) && method_exists($this->name, '__toString'))) { - $name = sprintf('"%s"', $this->name); + if ($this->source && $this->source->getPath()) { + // we only update the file and the line together + $this->file = $this->source->getPath(); + if ($this->lineno > 0) { + $this->line = $this->lineno; } else { - $name = json_encode($this->name); + $this->line = -1; } - $this->message .= sprintf(' in %s', $name); } - if ($this->lineno && $this->lineno >= 0) { - $this->message .= sprintf(' at line %d', $this->lineno); + $this->message = $this->rawMessage; + $last = substr($this->message, -1); + if ($punctuation = '.' === $last || '?' === $last ? $last : '') { + $this->message = substr($this->message, 0, -1); } - - if ($dot) { - $this->message .= '.'; + if ($this->source && $this->source->getName()) { + $this->message .= \sprintf(' in "%s"', $this->source->getName()); } - - if ($questionMark) { - $this->message .= '?'; + if ($this->lineno > 0) { + $this->message .= \sprintf(' at line %d', $this->lineno); + } + if ($punctuation) { + $this->message .= $punctuation; } } - private function guessTemplateInfo() + private function guessTemplateInfo(): void { + // $this->source is never null here (see guess() usage in Template) + + $this->lineno = 0; $template = null; - $templateClass = null; - - $backtrace = debug_backtrace(DEBUG_BACKTRACE_IGNORE_ARGS | DEBUG_BACKTRACE_PROVIDE_OBJECT); + $backtrace = debug_backtrace(\DEBUG_BACKTRACE_IGNORE_ARGS | \DEBUG_BACKTRACE_PROVIDE_OBJECT); foreach ($backtrace as $trace) { - if (isset($trace['object']) && $trace['object'] instanceof Template && 'Twig_Template' !== \get_class($trace['object'])) { - $currentClass = \get_class($trace['object']); - $isEmbedContainer = 0 === strpos($templateClass, $currentClass); - if (null === $this->name || ($this->name == $trace['object']->getTemplateName() && !$isEmbedContainer)) { - $template = $trace['object']; - $templateClass = \get_class($trace['object']); - } + if (isset($trace['object']) && $trace['object'] instanceof Template && $this->source->getName() === $trace['object']->getTemplateName()) { + $template = $trace['object']; + + break; } } - // update template name - if (null !== $template && null === $this->name) { - $this->name = $template->getTemplateName(); - } - - // update template path if any - if (null !== $template && null === $this->sourcePath) { - $src = $template->getSourceContext(); - $this->sourceCode = $src->getCode(); - $this->sourcePath = $src->getPath(); - } - - if (null === $template || $this->lineno > -1) { - return; - } - $r = new \ReflectionObject($template); $file = $r->getFileName(); @@ -234,8 +158,7 @@ class Error extends \Exception while ($e = array_pop($exceptions)) { $traces = $e->getTrace(); - array_unshift($traces, ['file' => $e->getFile(), 'line' => $e->getLine()]); - + array_unshift($traces, ['file' => $e instanceof Error ? $e->phpFile : $e->getFile(), 'line' => $e instanceof Error ? $e->phpLine : $e->getLine()]); while ($trace = array_shift($traces)) { if (!isset($trace['file']) || !isset($trace['line']) || $file != $trace['file']) { continue; @@ -253,5 +176,3 @@ class Error extends \Exception } } } - -class_alias('Twig\Error\Error', 'Twig_Error'); diff --git a/vendor/twig/twig/src/Error/LoaderError.php b/vendor/twig/twig/src/Error/LoaderError.php index dc5a9f1..7c8c23c 100644 --- a/vendor/twig/twig/src/Error/LoaderError.php +++ b/vendor/twig/twig/src/Error/LoaderError.php @@ -19,5 +19,3 @@ namespace Twig\Error; class LoaderError extends Error { } - -class_alias('Twig\Error\LoaderError', 'Twig_Error_Loader'); diff --git a/vendor/twig/twig/src/Error/RuntimeError.php b/vendor/twig/twig/src/Error/RuntimeError.php index 9b3f36e..f6b8476 100644 --- a/vendor/twig/twig/src/Error/RuntimeError.php +++ b/vendor/twig/twig/src/Error/RuntimeError.php @@ -20,5 +20,3 @@ namespace Twig\Error; class RuntimeError extends Error { } - -class_alias('Twig\Error\RuntimeError', 'Twig_Error_Runtime'); diff --git a/vendor/twig/twig/src/Error/SyntaxError.php b/vendor/twig/twig/src/Error/SyntaxError.php index efece92..841b653 100644 --- a/vendor/twig/twig/src/Error/SyntaxError.php +++ b/vendor/twig/twig/src/Error/SyntaxError.php @@ -25,12 +25,12 @@ class SyntaxError extends Error * @param string $name The original name of the item that does not exist * @param array $items An array of possible items */ - public function addSuggestions($name, array $items) + public function addSuggestions(string $name, array $items): void { $alternatives = []; foreach ($items as $item) { $lev = levenshtein($name, $item); - if ($lev <= \strlen($name) / 3 || false !== strpos($item, $name)) { + if ($lev <= \strlen($name) / 3 || str_contains($item, $name)) { $alternatives[$item] = $lev; } } @@ -41,8 +41,6 @@ class SyntaxError extends Error asort($alternatives); - $this->appendMessage(sprintf(' Did you mean "%s"?', implode('", "', array_keys($alternatives)))); + $this->appendMessage(\sprintf(' Did you mean "%s"?', implode('", "', array_keys($alternatives)))); } } - -class_alias('Twig\Error\SyntaxError', 'Twig_Error_Syntax'); diff --git a/vendor/twig/twig/src/ExpressionParser.php b/vendor/twig/twig/src/ExpressionParser.php index e6a2073..727cf7e 100644 --- a/vendor/twig/twig/src/ExpressionParser.php +++ b/vendor/twig/twig/src/ExpressionParser.php @@ -13,23 +13,18 @@ namespace Twig; use Twig\Error\SyntaxError; -use Twig\Node\Expression\AbstractExpression; +use Twig\ExpressionParser\Infix\DotExpressionParser; +use Twig\ExpressionParser\Infix\FilterExpressionParser; +use Twig\ExpressionParser\Infix\SquareBracketExpressionParser; use Twig\Node\Expression\ArrayExpression; -use Twig\Node\Expression\ArrowFunctionExpression; -use Twig\Node\Expression\AssignNameExpression; -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\NegUnary; -use Twig\Node\Expression\Unary\NotUnary; use Twig\Node\Expression\Unary\PosUnary; +use Twig\Node\Expression\Unary\SpreadUnary; +use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; +use Twig\Node\Nodes; /** * Parses expressions. @@ -41,356 +36,107 @@ use Twig\Node\Node; * * @author Fabien Potencier * - * @internal + * @deprecated since Twig 3.21 */ class ExpressionParser { - const OPERATOR_LEFT = 1; - const OPERATOR_RIGHT = 2; + /** + * @deprecated since Twig 3.21 + */ + public const OPERATOR_LEFT = 1; + /** + * @deprecated since Twig 3.21 + */ + public const OPERATOR_RIGHT = 2; - private $parser; - private $env; - private $unaryOperators; - private $binaryOperators; - - public function __construct(Parser $parser, Environment $env) - { - $this->parser = $parser; - $this->env = $env; - $this->unaryOperators = $env->getUnaryOperators(); - $this->binaryOperators = $env->getBinaryOperators(); + public function __construct( + private Parser $parser, + private Environment $env, + ) { + trigger_deprecation('twig/twig', '3.21', 'Class "%s" is deprecated, use "Parser::parseExpression()" instead.', __CLASS__); } - public function parseExpression($precedence = 0, $allowArrow = false) + public function parseExpression($precedence = 0) { - if ($allowArrow && $arrow = $this->parseArrow()) { - return $arrow; + if (\func_num_args() > 1) { + trigger_deprecation('twig/twig', '3.15', 'Passing a second argument ($allowArrow) to "%s()" is deprecated.', __METHOD__); } - $expr = $this->getPrimary(); - $token = $this->parser->getCurrentToken(); - while ($this->isBinary($token) && $this->binaryOperators[$token->getValue()]['precedence'] >= $precedence) { - $op = $this->binaryOperators[$token->getValue()]; - $this->parser->getStream()->next(); + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated, use "Parser::parseExpression()" instead.', __METHOD__); - if ('is not' === $token->getValue()) { - $expr = $this->parseNotTestExpression($expr); - } elseif ('is' === $token->getValue()) { - $expr = $this->parseTestExpression($expr); - } elseif (isset($op['callable'])) { - $expr = $op['callable']($this->parser, $expr); - } else { - $expr1 = $this->parseExpression(self::OPERATOR_LEFT === $op['associativity'] ? $op['precedence'] + 1 : $op['precedence']); - $class = $op['class']; - $expr = new $class($expr, $expr1, $token->getLine()); - } - - $token = $this->parser->getCurrentToken(); - } - - if (0 === $precedence) { - return $this->parseConditionalExpression($expr); - } - - return $expr; + return $this->parser->parseExpression((int) $precedence); } /** - * @return ArrowFunctionExpression|null + * @deprecated since Twig 3.21 */ - private function parseArrow() - { - $stream = $this->parser->getStream(); - - // short array syntax (one argument, no parentheses)? - if ($stream->look(1)->test(/* Token::ARROW_TYPE */ 12)) { - $line = $stream->getCurrent()->getLine(); - $token = $stream->expect(/* Token::NAME_TYPE */ 5); - $names = [new AssignNameExpression($token->getValue(), $token->getLine())]; - $stream->expect(/* Token::ARROW_TYPE */ 12); - - 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, '(')) { - return null; - } - ++$i; - while (true) { - // variable name - ++$i; - if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ',')) { - break; - } - ++$i; - } - if (!$stream->look($i)->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) { - return null; - } - ++$i; - if (!$stream->look($i)->test(/* Token::ARROW_TYPE */ 12)) { - return null; - } - - // yes, let's parse it properly - $token = $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '('); - $line = $token->getLine(); - - $names = []; - while (true) { - $token = $stream->expect(/* Token::NAME_TYPE */ 5); - $names[] = new AssignNameExpression($token->getValue(), $token->getLine()); - - if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { - break; - } - } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ')'); - $stream->expect(/* Token::ARROW_TYPE */ 12); - - return new ArrowFunctionExpression($this->parseExpression(0), new Node($names), $line); - } - - private function getPrimary(): AbstractExpression - { - $token = $this->parser->getCurrentToken(); - - if ($this->isUnary($token)) { - $operator = $this->unaryOperators[$token->getValue()]; - $this->parser->getStream()->next(); - $expr = $this->parseExpression($operator['precedence']); - $class = $operator['class']; - - return $this->parsePostfixExpression(new $class($expr, $token->getLine())); - } elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { - $this->parser->getStream()->next(); - $expr = $this->parseExpression(); - $this->parser->getStream()->expect(/* Token::PUNCTUATION_TYPE */ 9, ')', 'An opened parenthesis is not properly closed'); - - return $this->parsePostfixExpression($expr); - } - - return $this->parsePrimaryExpression(); - } - - private function parseConditionalExpression($expr): AbstractExpression - { - while ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, '?')) { - if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { - $expr2 = $this->parseExpression(); - if ($this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { - $expr3 = $this->parseExpression(); - } else { - $expr3 = new ConstantExpression('', $this->parser->getCurrentToken()->getLine()); - } - } else { - $expr2 = $expr; - $expr3 = $this->parseExpression(); - } - - $expr = new ConditionalExpression($expr, $expr2, $expr3, $this->parser->getCurrentToken()->getLine()); - } - - return $expr; - } - - private function isUnary(Token $token): bool - { - return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->unaryOperators[$token->getValue()]); - } - - private function isBinary(Token $token): bool - { - return $token->test(/* Token::OPERATOR_TYPE */ 8) && isset($this->binaryOperators[$token->getValue()]); - } - public function parsePrimaryExpression() { - $token = $this->parser->getCurrentToken(); - switch ($token->getType()) { - case /* Token::NAME_TYPE */ 5: - $this->parser->getStream()->next(); - switch ($token->getValue()) { - case 'true': - case 'TRUE': - $node = new ConstantExpression(true, $token->getLine()); - break; + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); - case 'false': - case 'FALSE': - $node = new ConstantExpression(false, $token->getLine()); - break; - - case 'none': - case 'NONE': - case 'null': - case 'NULL': - $node = new ConstantExpression(null, $token->getLine()); - break; - - default: - if ('(' === $this->parser->getCurrentToken()->getValue()) { - $node = $this->getFunctionNode($token->getValue(), $token->getLine()); - } else { - $node = new NameExpression($token->getValue(), $token->getLine()); - } - } - break; - - case /* Token::NUMBER_TYPE */ 6: - $this->parser->getStream()->next(); - $node = new ConstantExpression($token->getValue(), $token->getLine()); - break; - - case /* Token::STRING_TYPE */ 7: - case /* Token::INTERPOLATION_START_TYPE */ 10: - $node = $this->parseStringExpression(); - break; - - case /* Token::OPERATOR_TYPE */ 8: - 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(); - $node = new NameExpression($token->getValue(), $token->getLine()); - break; - } elseif (isset($this->unaryOperators[$token->getValue()])) { - $class = $this->unaryOperators[$token->getValue()]['class']; - - $ref = new \ReflectionClass($class); - if (!(\in_array($ref->getName(), [NegUnary::class, PosUnary::class, 'Twig_Node_Expression_Unary_Neg', 'Twig_Node_Expression_Unary_Pos']) - || $ref->isSubclassOf(NegUnary::class) || $ref->isSubclassOf(PosUnary::class) - || $ref->isSubclassOf('Twig_Node_Expression_Unary_Neg') || $ref->isSubclassOf('Twig_Node_Expression_Unary_Pos')) - ) { - throw new SyntaxError(sprintf('Unexpected unary operator "%s".', $token->getValue()), $token->getLine(), $this->parser->getStream()->getSourceContext()); - } - - $this->parser->getStream()->next(); - $expr = $this->parsePrimaryExpression(); - - $node = new $class($expr, $token->getLine()); - break; - } - - // no break - default: - if ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '[')) { - $node = $this->parseArrayExpression(); - } elseif ($token->test(/* Token::PUNCTUATION_TYPE */ 9, '{')) { - $node = $this->parseHashExpression(); - } elseif ($token->test(/* Token::OPERATOR_TYPE */ 8, '=') && ('==' === $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()); - } - } - - return $this->parsePostfixExpression($node); + return $this->parseExpression(); } + /** + * @deprecated since Twig 3.21 + */ public function parseStringExpression() { - $stream = $this->parser->getStream(); + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); - $nodes = []; - // a string cannot be followed by another string in a single expression - $nextCanBeString = true; - while (true) { - if ($nextCanBeString && $token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) { - $nodes[] = new ConstantExpression($token->getValue(), $token->getLine()); - $nextCanBeString = false; - } elseif ($stream->nextIf(/* Token::INTERPOLATION_START_TYPE */ 10)) { - $nodes[] = $this->parseExpression(); - $stream->expect(/* Token::INTERPOLATION_END_TYPE */ 11); - $nextCanBeString = true; - } else { - break; - } - } - - $expr = array_shift($nodes); - foreach ($nodes as $node) { - $expr = new ConcatBinary($expr, $node, $node->getTemplateLine()); - } - - return $expr; + return $this->parseExpression(); } + /** + * @deprecated since Twig 3.11, use parseExpression() instead + */ public function parseArrayExpression() { - $stream = $this->parser->getStream(); - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '[', 'An array element was expected'); + trigger_deprecation('twig/twig', '3.11', 'Calling "%s()" is deprecated, use "parseExpression()" instead.', __METHOD__); - $node = new ArrayExpression([], $stream->getCurrent()->getLine()); - $first = true; - while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { - if (!$first) { - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'An array element must be followed by a comma'); - - // trailing ,? - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { - break; - } - } - $first = false; - - $node->addElement($this->parseExpression()); - } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']', 'An opened array is not properly closed'); - - return $node; + return $this->parseExpression(); } + /** + * @deprecated since Twig 3.21 + */ + public function parseSequenceExpression() + { + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); + + return $this->parseExpression(); + } + + /** + * @deprecated since Twig 3.11, use parseExpression() instead + */ public function parseHashExpression() { - $stream = $this->parser->getStream(); - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '{', 'A hash element was expected'); + trigger_deprecation('twig/twig', '3.11', 'Calling "%s()" is deprecated, use "parseExpression()" instead.', __METHOD__); - $node = new ArrayExpression([], $stream->getCurrent()->getLine()); - $first = true; - while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) { - if (!$first) { - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'A hash value must be followed by a comma'); - - // trailing ,? - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '}')) { - break; - } - } - $first = false; - - // a hash key can be: - // - // * a number -- 12 - // * a string -- 'a' - // * a name, which is equivalent to a string -- a - // * an expression, which must be enclosed in parentheses -- (1 + 2) - if (($token = $stream->nextIf(/* Token::STRING_TYPE */ 7)) || ($token = $stream->nextIf(/* Token::NAME_TYPE */ 5)) || $token = $stream->nextIf(/* Token::NUMBER_TYPE */ 6)) { - $key = new ConstantExpression($token->getValue(), $token->getLine()); - } elseif ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { - $key = $this->parseExpression(); - } else { - $current = $stream->getCurrent(); - - throw new SyntaxError(sprintf('A hash key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', Token::typeToEnglish($current->getType()), $current->getValue()), $current->getLine(), $stream->getSourceContext()); - } - - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ':', 'A hash key must be followed by a colon (:)'); - $value = $this->parseExpression(); - - $node->addElement($value, $key); - } - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '}', 'An opened hash is not properly closed'); - - return $node; + return $this->parseExpression(); } + /** + * @deprecated since Twig 3.21 + */ + public function parseMappingExpression() + { + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); + + return $this->parseExpression(); + } + + /** + * @deprecated since Twig 3.21 + */ public function parsePostfixExpression($node) { + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); + while (true) { $token = $this->parser->getCurrentToken(); - if (/* Token::PUNCTUATION_TYPE */ 9 == $token->getType()) { + if ($token->test(Token::PUNCTUATION_TYPE)) { if ('.' == $token->getValue() || '[' == $token->getValue()) { $node = $this->parseSubscriptExpression($node); } elseif ('|' == $token->getValue()) { @@ -406,159 +152,49 @@ class ExpressionParser return $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 (!$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()); - } - - 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 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()); - } - - 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); - } - - $node = new MethodCallExpression($alias['node'], $alias['name'], $arguments, $line); - $node->setAttribute('safe', true); - - return $node; - } - - $args = $this->parseArguments(true); - $class = $this->getFunctionNodeClass($name, $line); - - return new $class($name, $args, $line); - } - } - + /** + * @deprecated since Twig 3.21 + */ public function parseSubscriptExpression($node) { - $stream = $this->parser->getStream(); - $token = $stream->next(); - $lineno = $token->getLine(); - $arguments = new ArrayExpression([], $lineno); - $type = Template::ANY_CALL; - if ('.' == $token->getValue()) { - $token = $stream->next(); - if ( - /* Token::NAME_TYPE */ 5 == $token->getType() - || - /* Token::NUMBER_TYPE */ 6 == $token->getType() - || - (/* Token::OPERATOR_TYPE */ 8 == $token->getType() && preg_match(Lexer::REGEX_NAME, $token->getValue())) - ) { - $arg = new ConstantExpression($token->getValue(), $lineno); + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { - $type = Template::METHOD_CALL; - foreach ($this->parseArguments() as $n) { - $arguments->addElement($n); - } - } - } else { - throw new SyntaxError('Expected name or number.', $lineno, $stream->getSourceContext()); - } + $parsers = new \ReflectionProperty($this->parser, 'parsers'); - if ($node instanceof NameExpression && null !== $this->parser->getImportedSymbol('template', $node->getAttribute('name'))) { - if (!$arg instanceof ConstantExpression) { - throw new SyntaxError(sprintf('Dynamic macro names are not supported (called on "%s").', $node->getAttribute('name')), $token->getLine(), $stream->getSourceContext()); - } - - $name = $arg->getAttribute('value'); - - $node = new MethodCallExpression($node, 'macro_'.$name, $arguments, $lineno); - $node->setAttribute('safe', true); - - return $node; - } - } else { - $type = Template::ARRAY_CALL; - - // slice? - $slice = false; - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ':')) { - $slice = true; - $arg = new ConstantExpression(0, $token->getLine()); - } else { - $arg = $this->parseExpression(); - } - - if ($stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ':')) { - $slice = true; - } - - if ($slice) { - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, ']')) { - $length = new ConstantExpression(null, $token->getLine()); - } else { - $length = $this->parseExpression(); - } - - $class = $this->getFilterNodeClass('slice', $token->getLine()); - $arguments = new Node([$arg, $length]); - $filter = new $class($node, new ConstantExpression('slice', $token->getLine()), $arguments, $token->getLine()); - - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']'); - - return $filter; - } - - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ']'); + if ('.' === $this->parser->getStream()->next()->getValue()) { + return $parsers->getValue($this->parser)->getByClass(DotExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken()); } - return new GetAttrExpression($node, $arg, $arguments, $type, $lineno); + return $parsers->getValue($this->parser)->getByClass(SquareBracketExpressionParser::class)->parse($this->parser, $node, $this->parser->getCurrentToken()); } + /** + * @deprecated since Twig 3.21 + */ public function parseFilterExpression($node) { + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); + $this->parser->getStream()->next(); return $this->parseFilterExpressionRaw($node); } - public function parseFilterExpressionRaw($node, $tag = null) + /** + * @deprecated since Twig 3.21 + */ + public function parseFilterExpressionRaw($node) { + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); + + $parsers = new \ReflectionProperty($this->parser, 'parsers'); + + $op = $parsers->getValue($this->parser)->getByClass(FilterExpressionParser::class); while (true) { - $token = $this->parser->getStream()->expect(/* Token::NAME_TYPE */ 5); - - $name = new ConstantExpression($token->getValue(), $token->getLine()); - if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { - $arguments = new Node(); - } else { - $arguments = $this->parseArguments(true, false, true); - } - - $class = $this->getFilterNodeClass($name->getAttribute('value'), $token->getLine()); - - $node = new $class($node, $name, $arguments, $token->getLine(), $tag); - - if (!$this->parser->getStream()->test(/* Token::PUNCTUATION_TYPE */ 9, '|')) { + $node = $op->parse($this->parser, $node, $this->parser->getCurrentToken()); + if (!$this->parser->getStream()->test(Token::OPERATOR_TYPE, '|')) { break; } - $this->parser->getStream()->next(); } @@ -568,46 +204,72 @@ class ExpressionParser /** * Parses arguments. * - * @param bool $namedArguments Whether to allow named arguments or not - * @param bool $definition Whether we are parsing arguments for a function definition - * * @return Node * * @throws SyntaxError + * + * @deprecated since Twig 3.19 Use Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments() instead */ - public function parseArguments($namedArguments = false, $definition = false, $allowArrow = false) + public function parseArguments() { + trigger_deprecation('twig/twig', '3.19', \sprintf('The "%s()" method is deprecated, use "Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments()" instead.', __METHOD__)); + + $parsePrimary = new \ReflectionMethod($this->parser, 'parsePrimary'); + + $namedArguments = false; + $definition = false; + if (\func_num_args() > 1) { + $definition = func_get_arg(1); + } + if (\func_num_args() > 0) { + trigger_deprecation('twig/twig', '3.15', 'Passing arguments to "%s()" is deprecated.', __METHOD__); + $namedArguments = func_get_arg(0); + } + $args = []; $stream = $this->parser->getStream(); - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, '(', 'A list of arguments must begin with an opening parenthesis'); - while (!$stream->test(/* Token::PUNCTUATION_TYPE */ 9, ')')) { - if (!empty($args)) { - $stream->expect(/* Token::PUNCTUATION_TYPE */ 9, ',', 'Arguments must be separated by a comma'); + $stream->expect(Token::OPERATOR_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); + $hasSpread = false; + while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) { + if ($args) { + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); + + // if the comma above was a trailing comma, early exit the argument parse loop + if ($stream->test(Token::PUNCTUATION_TYPE, ')')) { + break; + } } if ($definition) { - $token = $stream->expect(/* Token::NAME_TYPE */ 5, null, 'An argument must be a name'); - $value = new NameExpression($token->getValue(), $this->parser->getCurrentToken()->getLine()); + $token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name'); + $value = new ContextVariable($token->getValue(), $this->parser->getCurrentToken()->getLine()); } else { - $value = $this->parseExpression(0, $allowArrow); + if ($stream->nextIf(Token::SPREAD_TYPE)) { + $hasSpread = true; + $value = new SpreadUnary($this->parseExpression(), $stream->getCurrent()->getLine()); + } elseif ($hasSpread) { + throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } else { + $value = $this->parseExpression(); + } } $name = null; - if ($namedArguments && $token = $stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) { - if (!$value instanceof NameExpression) { - throw new SyntaxError(sprintf('A parameter name must be a string, "%s" given.', \get_class($value)), $token->getLine(), $stream->getSourceContext()); + if ($namedArguments && (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || (!$definition && $token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':')))) { + if (!$value instanceof ContextVariable) { + throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', $value::class), $token->getLine(), $stream->getSourceContext()); } $name = $value->getAttribute('name'); if ($definition) { - $value = $this->parsePrimaryExpression(); + $value = $parsePrimary->invoke($this->parser); if (!$this->checkConstantExpression($value)) { - throw new SyntaxError(sprintf('A default value for an argument must be a constant (a boolean, a string, a number, or an array).'), $token->getLine(), $stream->getSourceContext()); + throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).', $token->getLine(), $stream->getSourceContext()); } } else { - $value = $this->parseExpression(0, $allowArrow); + $value = $this->parseExpression(); } } @@ -615,6 +277,7 @@ class ExpressionParser if (null === $name) { $name = $value->getAttribute('name'); $value = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine()); + $value->setAttribute('is_implicit', true); } $args[$name] = $value; } else { @@ -625,174 +288,58 @@ class ExpressionParser } } } - $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); + return new Nodes($args); } + /** + * @deprecated since Twig 3.21, use "AbstractTokenParser::parseAssignmentExpression()" instead + */ public function parseAssignmentExpression() { + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated, use "AbstractTokenParser::parseAssignmentExpression()" instead.', __METHOD__); + $stream = $this->parser->getStream(); $targets = []; while (true) { $token = $this->parser->getCurrentToken(); - if ($stream->test(/* Token::OPERATOR_TYPE */ 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'])) { - throw new SyntaxError(sprintf('You cannot assign a value to "%s".', $value), $token->getLine(), $stream->getSourceContext()); - } - $targets[] = new AssignNameExpression($value, $token->getLine()); + $targets[] = new AssignContextVariable($token->getValue(), $token->getLine()); - if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } } - return new Node($targets); + return new Nodes($targets); } + /** + * @deprecated since Twig 3.21 + */ public function parseMultitargetExpression() { + trigger_deprecation('twig/twig', '3.21', 'The "%s()" method is deprecated.', __METHOD__); + $targets = []; while (true) { $targets[] = $this->parseExpression(); - if (!$this->parser->getStream()->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } } - return new Node($targets); - } - - private function parseNotTestExpression(Node $node): NotUnary - { - return new NotUnary($this->parseTestExpression($node), $this->parser->getCurrentToken()->getLine()); - } - - private function parseTestExpression(Node $node): TestExpression - { - $stream = $this->parser->getStream(); - list($name, $test) = $this->getTest($node->getTemplateLine()); - - $class = $this->getTestNodeClass($test); - $arguments = null; - if ($stream->test(/* Token::PUNCTUATION_TYPE */ 9, '(')) { - $arguments = $this->parseArguments(true); - } - - if ('defined' === $name && $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()); - } - - private function getTest(int $line): array - { - $stream = $this->parser->getStream(); - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); - - if ($test = $this->env->getTest($name)) { - return [$name, $test]; - } - - if ($stream->test(/* Token::NAME_TYPE */ 5)) { - // try 2-words tests - $name = $name.' '.$this->parser->getCurrentToken()->getValue(); - - if ($test = $this->env->getTest($name)) { - $stream->next(); - - return [$name, $test]; - } - } - - $e = new SyntaxError(sprintf('Unknown "%s" test.', $name), $line, $stream->getSourceContext()); - $e->addSuggestions($name, array_keys($this->env->getTests())); - - throw $e; - } - - private function getTestNodeClass(TwigTest $test): string - { - if ($test->isDeprecated()) { - $stream = $this->parser->getStream(); - $message = sprintf('Twig Test "%s" is deprecated', $test->getName()); - - if (!\is_bool($test->getDeprecatedVersion())) { - $message .= sprintf(' since version %s', $test->getDeprecatedVersion()); - } - if ($test->getAlternative()) { - $message .= sprintf('. Use "%s" instead', $test->getAlternative()); - } - $src = $stream->getSourceContext(); - $message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $stream->getCurrent()->getLine()); - - @trigger_error($message, E_USER_DEPRECATED); - } - - return $test->getNodeClass(); - } - - private function getFunctionNodeClass(string $name, int $line): string - { - if (false === $function = $this->env->getFunction($name)) { - $e = new SyntaxError(sprintf('Unknown "%s" function.', $name), $line, $this->parser->getStream()->getSourceContext()); - $e->addSuggestions($name, array_keys($this->env->getFunctions())); - - throw $e; - } - - if ($function->isDeprecated()) { - $message = sprintf('Twig Function "%s" is deprecated', $function->getName()); - if (!\is_bool($function->getDeprecatedVersion())) { - $message .= sprintf(' since version %s', $function->getDeprecatedVersion()); - } - if ($function->getAlternative()) { - $message .= sprintf('. Use "%s" instead', $function->getAlternative()); - } - $src = $this->parser->getStream()->getSourceContext(); - $message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line); - - @trigger_error($message, E_USER_DEPRECATED); - } - - return $function->getNodeClass(); - } - - private function getFilterNodeClass(string $name, int $line): string - { - if (false === $filter = $this->env->getFilter($name)) { - $e = new SyntaxError(sprintf('Unknown "%s" filter.', $name), $line, $this->parser->getStream()->getSourceContext()); - $e->addSuggestions($name, array_keys($this->env->getFilters())); - - throw $e; - } - - if ($filter->isDeprecated()) { - $message = sprintf('Twig Filter "%s" is deprecated', $filter->getName()); - if (!\is_bool($filter->getDeprecatedVersion())) { - $message .= sprintf(' since version %s', $filter->getDeprecatedVersion()); - } - if ($filter->getAlternative()) { - $message .= sprintf('. Use "%s" instead', $filter->getAlternative()); - } - $src = $this->parser->getStream()->getSourceContext(); - $message .= sprintf(' in %s at line %d.', $src->getPath() ?: $src->getName(), $line); - - @trigger_error($message, E_USER_DEPRECATED); - } - - return $filter->getNodeClass(); + return new Nodes($targets); } // checks that the node only contains "constant" elements + // to be removed in 4.0 private function checkConstantExpression(Node $node): bool { if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression @@ -809,6 +356,14 @@ class ExpressionParser return true; } -} -class_alias('Twig\ExpressionParser', 'Twig_ExpressionParser'); + /** + * @deprecated since Twig 3.19 Use Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments() instead + */ + public function parseOnlyArguments() + { + trigger_deprecation('twig/twig', '3.19', \sprintf('The "%s()" method is deprecated, use "Twig\ExpressionParser\Infix\ArgumentsTrait::parseNamedArguments()" instead.', __METHOD__)); + + return $this->parseArguments(); + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/AbstractExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/AbstractExpressionParser.php new file mode 100644 index 0000000..bc05bfa --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/AbstractExpressionParser.php @@ -0,0 +1,30 @@ +value, $this->getName()); + } + + public function getPrecedenceChange(): ?PrecedenceChange + { + return null; + } + + public function getAliases(): array + { + return []; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/ExpressionParserDescriptionInterface.php b/vendor/twig/twig/src/ExpressionParser/ExpressionParserDescriptionInterface.php new file mode 100644 index 0000000..686f8a5 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/ExpressionParserDescriptionInterface.php @@ -0,0 +1,17 @@ + + */ + public function getAliases(): array; +} diff --git a/vendor/twig/twig/src/ExpressionParser/ExpressionParserType.php b/vendor/twig/twig/src/ExpressionParser/ExpressionParserType.php new file mode 100644 index 0000000..8c21a8d --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/ExpressionParserType.php @@ -0,0 +1,33 @@ + + * + * @internal + */ +final class ExpressionParsers implements \IteratorAggregate +{ + /** + * @var array, array> + */ + private array $parsersByName = []; + + /** + * @var array, ExpressionParserInterface> + */ + private array $parsersByClass = []; + + /** + * @var \WeakMap>|null + */ + private ?\WeakMap $precedenceChanges = null; + + /** + * @param array $parsers + */ + public function __construct(array $parsers = []) + { + $this->add($parsers); + } + + /** + * @param array $parsers + * + * @return $this + */ + public function add(array $parsers): static + { + foreach ($parsers as $parser) { + if ($parser->getPrecedence() > 512 || $parser->getPrecedence() < 0) { + trigger_deprecation('twig/twig', '3.21', 'Precedence for "%s" must be between 0 and 512, got %d.', $parser->getName(), $parser->getPrecedence()); + // throw new \InvalidArgumentException(\sprintf('Precedence for "%s" must be between 0 and 512, got %d.', $parser->getName(), $parser->getPrecedence())); + } + $interface = $parser instanceof PrefixExpressionParserInterface ? PrefixExpressionParserInterface::class : InfixExpressionParserInterface::class; + $this->parsersByName[$interface][$parser->getName()] = $parser; + $this->parsersByClass[$parser::class] = $parser; + foreach ($parser->getAliases() as $alias) { + $this->parsersByName[$interface][$alias] = $parser; + } + } + + return $this; + } + + /** + * @template T of ExpressionParserInterface + * + * @param class-string $class + * + * @return T|null + */ + public function getByClass(string $class): ?ExpressionParserInterface + { + return $this->parsersByClass[$class] ?? null; + } + + /** + * @template T of ExpressionParserInterface + * + * @param class-string $interface + * + * @return T|null + */ + public function getByName(string $interface, string $name): ?ExpressionParserInterface + { + return $this->parsersByName[$interface][$name] ?? null; + } + + public function getIterator(): \Traversable + { + foreach ($this->parsersByName as $parsers) { + // we don't yield the keys + yield from $parsers; + } + } + + /** + * @internal + * + * @return \WeakMap> + */ + public function getPrecedenceChanges(): \WeakMap + { + if (null === $this->precedenceChanges) { + $this->precedenceChanges = new \WeakMap(); + foreach ($this as $ep) { + if (!$ep->getPrecedenceChange()) { + continue; + } + $min = min($ep->getPrecedenceChange()->getNewPrecedence(), $ep->getPrecedence()); + $max = max($ep->getPrecedenceChange()->getNewPrecedence(), $ep->getPrecedence()); + foreach ($this as $e) { + if ($e->getPrecedence() > $min && $e->getPrecedence() < $max) { + if (!isset($this->precedenceChanges[$e])) { + $this->precedenceChanges[$e] = []; + } + $this->precedenceChanges[$e][] = $ep; + } + } + } + } + + return $this->precedenceChanges; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/ArgumentsTrait.php b/vendor/twig/twig/src/ExpressionParser/Infix/ArgumentsTrait.php new file mode 100644 index 0000000..1c2ae49 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/ArgumentsTrait.php @@ -0,0 +1,79 @@ +parseNamedArguments($parser, $parseOpenParenthesis) as $k => $n) { + $arguments->addElement($n, new LocalVariable($k, $line)); + } + + return $arguments; + } + + private function parseNamedArguments(Parser $parser, bool $parseOpenParenthesis = true): Nodes + { + $args = []; + $stream = $parser->getStream(); + if ($parseOpenParenthesis) { + $stream->expect(Token::OPERATOR_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); + } + $hasSpread = false; + while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) { + if ($args) { + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); + + // if the comma above was a trailing comma, early exit the argument parse loop + if ($stream->test(Token::PUNCTUATION_TYPE, ')')) { + break; + } + } + + $value = $parser->parseExpression(); + if ($value instanceof SpreadUnary) { + $hasSpread = true; + } elseif ($hasSpread) { + throw new SyntaxError('Normal arguments must be placed before argument unpacking.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } + + $name = null; + if (($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) || ($token = $stream->nextIf(Token::PUNCTUATION_TYPE, ':'))) { + if (!$value instanceof ContextVariable) { + throw new SyntaxError(\sprintf('A parameter name must be a string, "%s" given.', $value::class), $token->getLine(), $stream->getSourceContext()); + } + $name = $value->getAttribute('name'); + $value = $parser->parseExpression(); + } + + if (null === $name) { + $args[] = $value; + } else { + $args[$name] = $value; + } + } + $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); + + return new Nodes($args); + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/ArrowExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/ArrowExpressionParser.php new file mode 100644 index 0000000..c8630da --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/ArrowExpressionParser.php @@ -0,0 +1,53 @@ +parseExpression(), $expr, $token->getLine()); + } + + public function getName(): string + { + return '=>'; + } + + public function getDescription(): string + { + return 'Arrow function (x => expr)'; + } + + public function getPrecedence(): int + { + return 250; + } + + public function getAssociativity(): InfixAssociativity + { + return InfixAssociativity::Left; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/BinaryOperatorExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/BinaryOperatorExpressionParser.php new file mode 100644 index 0000000..4c66da7 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/BinaryOperatorExpressionParser.php @@ -0,0 +1,80 @@ + */ + private string $nodeClass, + private string $name, + private int $precedence, + private InfixAssociativity $associativity = InfixAssociativity::Left, + private ?PrecedenceChange $precedenceChange = null, + private ?string $description = null, + private array $aliases = [], + ) { + } + + /** + * @return AbstractBinary + */ + public function parse(Parser $parser, AbstractExpression $left, Token $token): AbstractExpression + { + $right = $parser->parseExpression(InfixAssociativity::Left === $this->getAssociativity() ? $this->getPrecedence() + 1 : $this->getPrecedence()); + + return new ($this->nodeClass)($left, $right, $token->getLine()); + } + + public function getAssociativity(): InfixAssociativity + { + return $this->associativity; + } + + public function getName(): string + { + return $this->name; + } + + public function getDescription(): string + { + return $this->description ?? ''; + } + + public function getPrecedence(): int + { + return $this->precedence; + } + + public function getPrecedenceChange(): ?PrecedenceChange + { + return $this->precedenceChange; + } + + public function getAliases(): array + { + return $this->aliases; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/ConditionalTernaryExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/ConditionalTernaryExpressionParser.php new file mode 100644 index 0000000..9707c0a --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/ConditionalTernaryExpressionParser.php @@ -0,0 +1,62 @@ +parseExpression($this->getPrecedence()); + if ($parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ':')) { + // Ternary operator (expr ? expr2 : expr3) + $else = $parser->parseExpression($this->getPrecedence()); + } else { + // Ternary without else (expr ? expr2) + $else = new ConstantExpression('', $token->getLine()); + } + + return new ConditionalTernary($left, $then, $else, $token->getLine()); + } + + public function getName(): string + { + return '?'; + } + + public function getDescription(): string + { + return 'Conditional operator (a ? b : c)'; + } + + public function getPrecedence(): int + { + return 0; + } + + public function getAssociativity(): InfixAssociativity + { + return InfixAssociativity::Left; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/DotExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/DotExpressionParser.php new file mode 100644 index 0000000..7d1cf50 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/DotExpressionParser.php @@ -0,0 +1,99 @@ +getStream(); + $token = $stream->getCurrent(); + $lineno = $token->getLine(); + $arguments = new ArrayExpression([], $lineno); + $type = Template::ANY_CALL; + + if ($stream->nextIf(Token::OPERATOR_TYPE, '(')) { + $attribute = $parser->parseExpression(); + $stream->expect(Token::PUNCTUATION_TYPE, ')'); + } else { + $token = $stream->next(); + if ( + $token->test(Token::NAME_TYPE) + || $token->test(Token::NUMBER_TYPE) + || ($token->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) + ) { + $attribute = new ConstantExpression($token->getValue(), $token->getLine()); + } else { + throw new SyntaxError(\sprintf('Expected name or number, got value "%s" of type %s.', $token->getValue(), $token->toEnglish()), $token->getLine(), $stream->getSourceContext()); + } + } + + if ($stream->test(Token::OPERATOR_TYPE, '(')) { + $type = Template::METHOD_CALL; + $arguments = $this->parseCallableArguments($parser, $token->getLine()); + } + + if ( + $expr instanceof NameExpression + && ( + null !== $parser->getImportedSymbol('template', $expr->getAttribute('name')) + || '_self' === $expr->getAttribute('name') && $attribute instanceof ConstantExpression + ) + ) { + return new MacroReferenceExpression(new TemplateVariable($expr->getAttribute('name'), $expr->getTemplateLine()), 'macro_'.$attribute->getAttribute('value'), $arguments, $expr->getTemplateLine()); + } + + return new GetAttrExpression($expr, $attribute, $arguments, $type, $lineno); + } + + public function getName(): string + { + return '.'; + } + + public function getDescription(): string + { + return 'Get an attribute on a variable'; + } + + public function getPrecedence(): int + { + return 512; + } + + public function getAssociativity(): InfixAssociativity + { + return InfixAssociativity::Left; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/FilterExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/FilterExpressionParser.php new file mode 100644 index 0000000..0bbe6b4 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/FilterExpressionParser.php @@ -0,0 +1,85 @@ +getStream(); + $token = $stream->expect(Token::NAME_TYPE); + $line = $token->getLine(); + + if (!$stream->test(Token::OPERATOR_TYPE, '(')) { + $arguments = new EmptyNode(); + } else { + $arguments = $this->parseNamedArguments($parser); + } + + $filter = $parser->getFilter($token->getValue(), $line); + + $ready = true; + if (!isset($this->readyNodes[$class = $filter->getNodeClass()])) { + $this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class); + } + + if (!$ready = $this->readyNodes[$class]) { + trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigFilter" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class); + } + + return new $class($expr, $ready ? $filter : new ConstantExpression($filter->getName(), $line), $arguments, $line); + } + + public function getName(): string + { + return '|'; + } + + public function getDescription(): string + { + return 'Twig filter call'; + } + + public function getPrecedence(): int + { + return 512; + } + + public function getPrecedenceChange(): ?PrecedenceChange + { + return new PrecedenceChange('twig/twig', '3.21', 300); + } + + public function getAssociativity(): InfixAssociativity + { + return InfixAssociativity::Left; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/FunctionExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/FunctionExpressionParser.php new file mode 100644 index 0000000..e9cd775 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/FunctionExpressionParser.php @@ -0,0 +1,90 @@ +getLine(); + if (!$expr instanceof NameExpression) { + throw new SyntaxError('Function name must be an identifier.', $line, $parser->getStream()->getSourceContext()); + } + + $name = $expr->getAttribute('name'); + + if (null !== $alias = $parser->getImportedSymbol('function', $name)) { + return new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], $this->parseCallableArguments($parser, $line, false), $line); + } + + $args = $this->parseNamedArguments($parser, false); + + $function = $parser->getFunction($name, $line); + + if ($function->getParserCallable()) { + $fakeNode = new EmptyNode($line); + $fakeNode->setSourceContext($parser->getStream()->getSourceContext()); + + return ($function->getParserCallable())($parser, $fakeNode, $args, $line); + } + + if (!isset($this->readyNodes[$class = $function->getNodeClass()])) { + $this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class); + } + + if (!$ready = $this->readyNodes[$class]) { + trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigFunction" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class); + } + + return new $class($ready ? $function : $function->getName(), $args, $line); + } + + public function getName(): string + { + return '('; + } + + public function getDescription(): string + { + return 'Twig function call'; + } + + public function getPrecedence(): int + { + return 512; + } + + public function getAssociativity(): InfixAssociativity + { + return InfixAssociativity::Left; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/IsExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/IsExpressionParser.php new file mode 100644 index 0000000..88d54f7 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/IsExpressionParser.php @@ -0,0 +1,84 @@ +getStream(); + $test = $parser->getTest($token->getLine()); + + $arguments = null; + if ($stream->test(Token::OPERATOR_TYPE, '(')) { + $arguments = $this->parseNamedArguments($parser); + } elseif ($test->hasOneMandatoryArgument()) { + $arguments = new Nodes([0 => $parser->parseExpression($this->getPrecedence())]); + } + + if ('defined' === $test->getName() && $expr instanceof NameExpression && null !== $alias = $parser->getImportedSymbol('function', $expr->getAttribute('name'))) { + $expr = new MacroReferenceExpression($alias['node']->getNode('var'), $alias['name'], new ArrayExpression([], $expr->getTemplateLine()), $expr->getTemplateLine()); + } + + $ready = $test instanceof TwigTest; + if (!isset($this->readyNodes[$class = $test->getNodeClass()])) { + $this->readyNodes[$class] = (bool) (new \ReflectionClass($class))->getConstructor()->getAttributes(FirstClassTwigCallableReady::class); + } + + if (!$ready = $this->readyNodes[$class]) { + trigger_deprecation('twig/twig', '3.12', 'Twig node "%s" is not marked as ready for passing a "TwigTest" in the constructor instead of its name; please update your code and then add #[FirstClassTwigCallableReady] attribute to the constructor.', $class); + } + + return new $class($expr, $ready ? $test : $test->getName(), $arguments, $stream->getCurrent()->getLine()); + } + + public function getPrecedence(): int + { + return 100; + } + + public function getName(): string + { + return 'is'; + } + + public function getDescription(): string + { + return 'Twig tests'; + } + + public function getAssociativity(): InfixAssociativity + { + return InfixAssociativity::Left; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/IsNotExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/IsNotExpressionParser.php new file mode 100644 index 0000000..1e1085a --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/IsNotExpressionParser.php @@ -0,0 +1,33 @@ +getLine()); + } + + public function getName(): string + { + return 'is not'; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Infix/SquareBracketExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Infix/SquareBracketExpressionParser.php new file mode 100644 index 0000000..c47c91d --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Infix/SquareBracketExpressionParser.php @@ -0,0 +1,91 @@ +getStream(); + $lineno = $token->getLine(); + $arguments = new ArrayExpression([], $lineno); + + // slice? + $slice = false; + if ($stream->test(Token::PUNCTUATION_TYPE, ':')) { + $slice = true; + $attribute = new ConstantExpression(0, $token->getLine()); + } else { + $attribute = $parser->parseExpression(); + } + + if ($stream->nextIf(Token::PUNCTUATION_TYPE, ':')) { + $slice = true; + } + + if ($slice) { + if ($stream->test(Token::PUNCTUATION_TYPE, ']')) { + $length = new ConstantExpression(null, $token->getLine()); + } else { + $length = $parser->parseExpression(); + } + + $filter = $parser->getFilter('slice', $token->getLine()); + $arguments = new Nodes([$attribute, $length]); + $filter = new ($filter->getNodeClass())($expr, $filter, $arguments, $token->getLine()); + + $stream->expect(Token::PUNCTUATION_TYPE, ']'); + + return $filter; + } + + $stream->expect(Token::PUNCTUATION_TYPE, ']'); + + return new GetAttrExpression($expr, $attribute, $arguments, Template::ARRAY_CALL, $lineno); + } + + public function getName(): string + { + return '['; + } + + public function getDescription(): string + { + return 'Array access'; + } + + public function getPrecedence(): int + { + return 512; + } + + public function getAssociativity(): InfixAssociativity + { + return InfixAssociativity::Left; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/InfixAssociativity.php b/vendor/twig/twig/src/ExpressionParser/InfixAssociativity.php new file mode 100644 index 0000000..3aeccce --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/InfixAssociativity.php @@ -0,0 +1,18 @@ + + */ +class PrecedenceChange +{ + public function __construct( + private string $package, + private string $version, + private int $newPrecedence, + ) { + } + + public function getPackage(): string + { + return $this->package; + } + + public function getVersion(): string + { + return $this->version; + } + + public function getNewPrecedence(): int + { + return $this->newPrecedence; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Prefix/GroupingExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Prefix/GroupingExpressionParser.php new file mode 100644 index 0000000..5c6608d --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Prefix/GroupingExpressionParser.php @@ -0,0 +1,78 @@ +getStream(); + $expr = $parser->parseExpression($this->getPrecedence()); + + if ($stream->nextIf(Token::PUNCTUATION_TYPE, ')')) { + if (!$stream->test(Token::OPERATOR_TYPE, '=>')) { + return $expr->setExplicitParentheses(); + } + + return new ListExpression([$expr], $token->getLine()); + } + + // determine if we are parsing an arrow function arguments + if (!$stream->test(Token::PUNCTUATION_TYPE, ',')) { + $stream->expect(Token::PUNCTUATION_TYPE, ')', 'An opened parenthesis is not properly closed'); + } + + $names = [$expr]; + while (true) { + if ($stream->nextIf(Token::PUNCTUATION_TYPE, ')')) { + break; + } + $stream->expect(Token::PUNCTUATION_TYPE, ','); + $token = $stream->expect(Token::NAME_TYPE); + $names[] = new ContextVariable($token->getValue(), $token->getLine()); + } + + if (!$stream->test(Token::OPERATOR_TYPE, '=>')) { + throw new SyntaxError('A list of variables must be followed by an arrow.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } + + return new ListExpression($names, $token->getLine()); + } + + public function getName(): string + { + return '('; + } + + public function getDescription(): string + { + return 'Explicit group expression (a)'; + } + + public function getPrecedence(): int + { + return 0; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Prefix/LiteralExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Prefix/LiteralExpressionParser.php new file mode 100644 index 0000000..d98c9ad --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Prefix/LiteralExpressionParser.php @@ -0,0 +1,244 @@ +getStream(); + switch (true) { + case $token->test(Token::NAME_TYPE): + $stream->next(); + switch ($token->getValue()) { + case 'true': + case 'TRUE': + $this->type = 'constant'; + + return new ConstantExpression(true, $token->getLine()); + + case 'false': + case 'FALSE': + $this->type = 'constant'; + + return new ConstantExpression(false, $token->getLine()); + + case 'none': + case 'NONE': + case 'null': + case 'NULL': + $this->type = 'constant'; + + return new ConstantExpression(null, $token->getLine()); + + default: + $this->type = 'variable'; + + return new ContextVariable($token->getValue(), $token->getLine()); + } + + // no break + case $token->test(Token::NUMBER_TYPE): + $stream->next(); + $this->type = 'constant'; + + return new ConstantExpression($token->getValue(), $token->getLine()); + + case $token->test(Token::STRING_TYPE): + case $token->test(Token::INTERPOLATION_START_TYPE): + $this->type = 'string'; + + return $this->parseStringExpression($parser); + + case $token->test(Token::PUNCTUATION_TYPE): + // In 4.0, we should always return the node or throw an error for default + if ($node = match ($token->getValue()) { + '{' => $this->parseMappingExpression($parser), + default => null, + }) { + return $node; + } + + // no break + case $token->test(Token::OPERATOR_TYPE): + if ('[' === $token->getValue()) { + return $this->parseSequenceExpression($parser); + } + + if (preg_match(Lexer::REGEX_NAME, $token->getValue(), $matches) && $matches[0] == $token->getValue()) { + // in this context, string operators are variable names + $stream->next(); + $this->type = 'variable'; + + return new ContextVariable($token->getValue(), $token->getLine()); + } + + if ('=' === $token->getValue() && ('==' === $stream->look(-1)->getValue() || '!=' === $stream->look(-1)->getValue())) { + throw new SyntaxError(\sprintf('Unexpected operator of value "%s". Did you try to use "===" or "!==" for strict comparison? Use "is same as(value)" instead.', $token->getValue()), $token->getLine(), $stream->getSourceContext()); + } + + // no break + default: + throw new SyntaxError(\sprintf('Unexpected token "%s" of value "%s".', $token->toEnglish(), $token->getValue()), $token->getLine(), $stream->getSourceContext()); + } + } + + public function getName(): string + { + return $this->type; + } + + public function getDescription(): string + { + return 'A literal value (boolean, string, number, sequence, mapping, ...)'; + } + + public function getPrecedence(): int + { + // not used + return 0; + } + + private function parseStringExpression(Parser $parser) + { + $stream = $parser->getStream(); + + $nodes = []; + // a string cannot be followed by another string in a single expression + $nextCanBeString = true; + while (true) { + if ($nextCanBeString && $token = $stream->nextIf(Token::STRING_TYPE)) { + $nodes[] = new ConstantExpression($token->getValue(), $token->getLine()); + $nextCanBeString = false; + } elseif ($stream->nextIf(Token::INTERPOLATION_START_TYPE)) { + $nodes[] = $parser->parseExpression(); + $stream->expect(Token::INTERPOLATION_END_TYPE); + $nextCanBeString = true; + } else { + break; + } + } + + $expr = array_shift($nodes); + foreach ($nodes as $node) { + $expr = new ConcatBinary($expr, $node, $node->getTemplateLine()); + } + + return $expr; + } + + private function parseSequenceExpression(Parser $parser) + { + $this->type = 'sequence'; + + $stream = $parser->getStream(); + $stream->expect(Token::OPERATOR_TYPE, '[', 'A sequence element was expected'); + + $node = new ArrayExpression([], $stream->getCurrent()->getLine()); + $first = true; + while (!$stream->test(Token::PUNCTUATION_TYPE, ']')) { + if (!$first) { + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'A sequence element must be followed by a comma'); + + // trailing ,? + if ($stream->test(Token::PUNCTUATION_TYPE, ']')) { + break; + } + } + $first = false; + + $node->addElement($parser->parseExpression()); + } + $stream->expect(Token::PUNCTUATION_TYPE, ']', 'An opened sequence is not properly closed'); + + return $node; + } + + private function parseMappingExpression(Parser $parser) + { + $this->type = 'mapping'; + + $stream = $parser->getStream(); + $stream->expect(Token::PUNCTUATION_TYPE, '{', 'A mapping element was expected'); + + $node = new ArrayExpression([], $stream->getCurrent()->getLine()); + $first = true; + while (!$stream->test(Token::PUNCTUATION_TYPE, '}')) { + if (!$first) { + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'A mapping value must be followed by a comma'); + + // trailing ,? + if ($stream->test(Token::PUNCTUATION_TYPE, '}')) { + break; + } + } + $first = false; + + if ($stream->test(Token::OPERATOR_TYPE, '...')) { + $node->addElement($parser->parseExpression()); + + continue; + } + + // a mapping key can be: + // + // * a number -- 12 + // * a string -- 'a' + // * a name, which is equivalent to a string -- a + // * an expression, which must be enclosed in parentheses -- (1 + 2) + if ($token = $stream->nextIf(Token::NAME_TYPE)) { + $key = new ConstantExpression($token->getValue(), $token->getLine()); + + // {a} is a shortcut for {a:a} + if ($stream->test(Token::PUNCTUATION_TYPE, [',', '}'])) { + $value = new ContextVariable($key->getAttribute('value'), $key->getTemplateLine()); + $node->addElement($value, $key); + continue; + } + } elseif (($token = $stream->nextIf(Token::STRING_TYPE)) || $token = $stream->nextIf(Token::NUMBER_TYPE)) { + $key = new ConstantExpression($token->getValue(), $token->getLine()); + } elseif ($stream->test(Token::OPERATOR_TYPE, '(')) { + $key = $parser->parseExpression(); + } else { + $current = $stream->getCurrent(); + + throw new SyntaxError(\sprintf('A mapping key must be a quoted string, a number, a name, or an expression enclosed in parentheses (unexpected token "%s" of value "%s".', $current->toEnglish(), $current->getValue()), $current->getLine(), $stream->getSourceContext()); + } + + $stream->expect(Token::PUNCTUATION_TYPE, ':', 'A mapping key must be followed by a colon (:)'); + $value = $parser->parseExpression(); + + $node->addElement($value, $key); + } + $stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed'); + + return $node; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/Prefix/UnaryOperatorExpressionParser.php b/vendor/twig/twig/src/ExpressionParser/Prefix/UnaryOperatorExpressionParser.php new file mode 100644 index 0000000..3546894 --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/Prefix/UnaryOperatorExpressionParser.php @@ -0,0 +1,71 @@ + */ + private string $nodeClass, + private string $name, + private int $precedence, + private ?PrecedenceChange $precedenceChange = null, + private ?string $description = null, + private array $aliases = [], + ) { + } + + /** + * @return AbstractUnary + */ + public function parse(Parser $parser, Token $token): AbstractExpression + { + return new ($this->nodeClass)($parser->parseExpression($this->precedence), $token->getLine()); + } + + public function getName(): string + { + return $this->name; + } + + public function getDescription(): string + { + return $this->description ?? ''; + } + + public function getPrecedence(): int + { + return $this->precedence; + } + + public function getPrecedenceChange(): ?PrecedenceChange + { + return $this->precedenceChange; + } + + public function getAliases(): array + { + return $this->aliases; + } +} diff --git a/vendor/twig/twig/src/ExpressionParser/PrefixExpressionParserInterface.php b/vendor/twig/twig/src/ExpressionParser/PrefixExpressionParserInterface.php new file mode 100644 index 0000000..587997c --- /dev/null +++ b/vendor/twig/twig/src/ExpressionParser/PrefixExpressionParserInterface.php @@ -0,0 +1,21 @@ +getFileName(); + if (!is_file($filename)) { + return 0; + } + + $lastModified = filemtime($filename); + + // Track modifications of the runtime class if it exists and follows the naming convention + if (str_ends_with($filename, 'Extension.php') && is_file($filename = substr($filename, 0, -13).'Runtime.php')) { + $lastModified = max($lastModified, filemtime($filename)); + } + + return $lastModified; + } +} diff --git a/vendor/twig/twig/src/Extension/AttributeExtension.php b/vendor/twig/twig/src/Extension/AttributeExtension.php new file mode 100644 index 0000000..44e4f3f --- /dev/null +++ b/vendor/twig/twig/src/Extension/AttributeExtension.php @@ -0,0 +1,174 @@ + + */ +final class AttributeExtension extends AbstractExtension +{ + private array $filters; + private array $functions; + private array $tests; + + /** + * Use a runtime class using PHP attributes to define filters, functions, and tests. + * + * @param class-string $class + */ + public function __construct(private string $class) + { + } + + /** + * @return class-string + */ + public function getClass(): string + { + return $this->class; + } + + public function getFilters(): array + { + if (!isset($this->filters)) { + $this->initFromAttributes(); + } + + return $this->filters; + } + + public function getFunctions(): array + { + if (!isset($this->functions)) { + $this->initFromAttributes(); + } + + return $this->functions; + } + + public function getTests(): array + { + if (!isset($this->tests)) { + $this->initFromAttributes(); + } + + return $this->tests; + } + + public function getLastModified(): int + { + return max( + filemtime(__FILE__), + is_file($filename = (new \ReflectionClass($this->getClass()))->getFileName()) ? filemtime($filename) : 0, + ); + } + + private function initFromAttributes(): void + { + $filters = $functions = $tests = []; + $reflectionClass = new \ReflectionClass($this->getClass()); + foreach ($reflectionClass->getMethods() as $method) { + foreach ($method->getAttributes(AsTwigFilter::class) as $reflectionAttribute) { + /** @var AsTwigFilter $attribute */ + $attribute = $reflectionAttribute->newInstance(); + + $callable = new TwigFilter($attribute->name, [$reflectionClass->name, $method->getName()], [ + 'needs_context' => $attribute->needsContext ?? false, + 'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method), + 'needs_charset' => $attribute->needsCharset ?? false, + 'is_variadic' => $method->isVariadic(), + 'is_safe' => $attribute->isSafe, + 'is_safe_callback' => $attribute->isSafeCallback, + 'pre_escape' => $attribute->preEscape, + 'preserves_safety' => $attribute->preservesSafety, + 'deprecation_info' => $attribute->deprecationInfo, + ]); + + if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) { + throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigFilter, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters())); + } + + $filters[$attribute->name] = $callable; + } + + foreach ($method->getAttributes(AsTwigFunction::class) as $reflectionAttribute) { + /** @var AsTwigFunction $attribute */ + $attribute = $reflectionAttribute->newInstance(); + + $callable = new TwigFunction($attribute->name, [$reflectionClass->name, $method->getName()], [ + 'needs_context' => $attribute->needsContext ?? false, + 'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method), + 'needs_charset' => $attribute->needsCharset ?? false, + 'is_variadic' => $method->isVariadic(), + 'is_safe' => $attribute->isSafe, + 'is_safe_callback' => $attribute->isSafeCallback, + 'deprecation_info' => $attribute->deprecationInfo, + ]); + + if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) { + throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigFunction, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters())); + } + + $functions[$attribute->name] = $callable; + } + + foreach ($method->getAttributes(AsTwigTest::class) as $reflectionAttribute) { + + /** @var AsTwigTest $attribute */ + $attribute = $reflectionAttribute->newInstance(); + + $callable = new TwigTest($attribute->name, [$reflectionClass->name, $method->getName()], [ + 'needs_context' => $attribute->needsContext ?? false, + 'needs_environment' => $attribute->needsEnvironment ?? $this->needsEnvironment($method), + 'needs_charset' => $attribute->needsCharset ?? false, + 'is_variadic' => $method->isVariadic(), + 'deprecation_info' => $attribute->deprecationInfo, + ]); + + if ($callable->getMinimalNumberOfRequiredArguments() > $method->getNumberOfParameters()) { + throw new \LogicException(sprintf('"%s::%s()" needs at least %d arguments to be used AsTwigTest, but only %d defined.', $reflectionClass->getName(), $method->getName(), $callable->getMinimalNumberOfRequiredArguments(), $method->getNumberOfParameters())); + } + + $tests[$attribute->name] = $callable; + } + } + + // Assign all at the end to avoid inconsistent state in case of exception + $this->filters = array_values($filters); + $this->functions = array_values($functions); + $this->tests = array_values($tests); + } + + /** + * Detect if the first argument of the method is the environment. + */ + private function needsEnvironment(\ReflectionFunctionAbstract $function): bool + { + if (!$parameters = $function->getParameters()) { + return false; + } + + return $parameters[0]->getType() instanceof \ReflectionNamedType + && Environment::class === $parameters[0]->getType()->getName() + && !$parameters[0]->isVariadic(); + } +} diff --git a/vendor/twig/twig/src/Extension/CoreExtension.php b/vendor/twig/twig/src/Extension/CoreExtension.php index 8bc9c0d..f7e4250 100644 --- a/vendor/twig/twig/src/Extension/CoreExtension.php +++ b/vendor/twig/twig/src/Extension/CoreExtension.php @@ -9,8 +9,29 @@ * file that was distributed with this source code. */ -namespace Twig\Extension { -use Twig\ExpressionParser; +namespace Twig\Extension; + +use Twig\DeprecatedCallableInfo; +use Twig\Environment; +use Twig\Error\LoaderError; +use Twig\Error\RuntimeError; +use Twig\Error\SyntaxError; +use Twig\ExpressionParser\Infix\ArrowExpressionParser; +use Twig\ExpressionParser\Infix\BinaryOperatorExpressionParser; +use Twig\ExpressionParser\Infix\ConditionalTernaryExpressionParser; +use Twig\ExpressionParser\Infix\DotExpressionParser; +use Twig\ExpressionParser\Infix\FilterExpressionParser; +use Twig\ExpressionParser\Infix\FunctionExpressionParser; +use Twig\ExpressionParser\Infix\IsExpressionParser; +use Twig\ExpressionParser\Infix\IsNotExpressionParser; +use Twig\ExpressionParser\Infix\SquareBracketExpressionParser; +use Twig\ExpressionParser\InfixAssociativity; +use Twig\ExpressionParser\PrecedenceChange; +use Twig\ExpressionParser\Prefix\GroupingExpressionParser; +use Twig\ExpressionParser\Prefix\LiteralExpressionParser; +use Twig\ExpressionParser\Prefix\UnaryOperatorExpressionParser; +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; @@ -18,11 +39,14 @@ use Twig\Node\Expression\Binary\BitwiseOrBinary; use Twig\Node\Expression\Binary\BitwiseXorBinary; use Twig\Node\Expression\Binary\ConcatBinary; use Twig\Node\Expression\Binary\DivBinary; +use Twig\Node\Expression\Binary\ElvisBinary; use Twig\Node\Expression\Binary\EndsWithBinary; use Twig\Node\Expression\Binary\EqualBinary; use Twig\Node\Expression\Binary\FloorDivBinary; use Twig\Node\Expression\Binary\GreaterBinary; use Twig\Node\Expression\Binary\GreaterEqualBinary; +use Twig\Node\Expression\Binary\HasEveryBinary; +use Twig\Node\Expression\Binary\HasSomeBinary; use Twig\Node\Expression\Binary\InBinary; use Twig\Node\Expression\Binary\LessBinary; use Twig\Node\Expression\Binary\LessEqualBinary; @@ -31,14 +55,20 @@ use Twig\Node\Expression\Binary\ModBinary; use Twig\Node\Expression\Binary\MulBinary; use Twig\Node\Expression\Binary\NotEqualBinary; use Twig\Node\Expression\Binary\NotInBinary; +use Twig\Node\Expression\Binary\NullCoalesceBinary; use Twig\Node\Expression\Binary\OrBinary; use Twig\Node\Expression\Binary\PowerBinary; use Twig\Node\Expression\Binary\RangeBinary; use Twig\Node\Expression\Binary\SpaceshipBinary; use Twig\Node\Expression\Binary\StartsWithBinary; use Twig\Node\Expression\Binary\SubBinary; +use Twig\Node\Expression\Binary\XorBinary; +use Twig\Node\Expression\BlockReferenceExpression; use Twig\Node\Expression\Filter\DefaultFilter; -use Twig\Node\Expression\NullCoalesceExpression; +use Twig\Node\Expression\FunctionNode\EnumCasesFunction; +use Twig\Node\Expression\FunctionNode\EnumFunction; +use Twig\Node\Expression\GetAttrExpression; +use Twig\Node\Expression\ParentExpression; use Twig\Node\Expression\Test\ConstantTest; use Twig\Node\Expression\Test\DefinedTest; use Twig\Node\Expression\Test\DivisiblebyTest; @@ -46,75 +76,68 @@ use Twig\Node\Expression\Test\EvenTest; use Twig\Node\Expression\Test\NullTest; use Twig\Node\Expression\Test\OddTest; use Twig\Node\Expression\Test\SameasTest; +use Twig\Node\Expression\Test\TrueTest; use Twig\Node\Expression\Unary\NegUnary; use Twig\Node\Expression\Unary\NotUnary; use Twig\Node\Expression\Unary\PosUnary; -use Twig\NodeVisitor\MacroAutoImportNodeVisitor; +use Twig\Node\Expression\Unary\SpreadUnary; +use Twig\Node\Node; +use Twig\Parser; +use Twig\Sandbox\SecurityNotAllowedMethodError; +use Twig\Sandbox\SecurityNotAllowedPropertyError; +use Twig\Source; +use Twig\Template; +use Twig\TemplateWrapper; use Twig\TokenParser\ApplyTokenParser; use Twig\TokenParser\BlockTokenParser; use Twig\TokenParser\DeprecatedTokenParser; use Twig\TokenParser\DoTokenParser; use Twig\TokenParser\EmbedTokenParser; use Twig\TokenParser\ExtendsTokenParser; -use Twig\TokenParser\FilterTokenParser; use Twig\TokenParser\FlushTokenParser; use Twig\TokenParser\ForTokenParser; use Twig\TokenParser\FromTokenParser; +use Twig\TokenParser\GuardTokenParser; use Twig\TokenParser\IfTokenParser; use Twig\TokenParser\ImportTokenParser; use Twig\TokenParser\IncludeTokenParser; use Twig\TokenParser\MacroTokenParser; use Twig\TokenParser\SetTokenParser; -use Twig\TokenParser\SpacelessTokenParser; +use Twig\TokenParser\TypesTokenParser; use Twig\TokenParser\UseTokenParser; use Twig\TokenParser\WithTokenParser; use Twig\TwigFilter; use Twig\TwigFunction; use Twig\TwigTest; +use Twig\Util\CallableArgumentsExtractor; final class CoreExtension extends AbstractExtension { + public const ARRAY_LIKE_CLASSES = [ + 'ArrayIterator', + 'ArrayObject', + 'CachingIterator', + 'RecursiveArrayIterator', + 'RecursiveCachingIterator', + 'SplDoublyLinkedList', + 'SplFixedArray', + 'SplObjectStorage', + 'SplQueue', + 'SplStack', + 'WeakMap', + ]; + + private const DEFAULT_TRIM_CHARS = " \t\n\r\0\x0B"; + private $dateFormats = ['F j, Y H:i', '%d days']; private $numberFormat = [0, '.', ',']; private $timezone = null; - private $escapers = []; - - /** - * Defines a new escaper to be used via the escape filter. - * - * @param string $strategy The strategy name that should be used as a strategy in the escape call - * @param callable $callable A valid PHP callable - * - * @deprecated since Twig 2.11, to be removed in 3.0; use the same method on EscaperExtension instead - */ - public function setEscaper($strategy, callable $callable) - { - @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.11; use "%s::setEscaper" instead.', __METHOD__, EscaperExtension::class), E_USER_DEPRECATED); - - $this->escapers[$strategy] = $callable; - } - - /** - * Gets all defined escapers. - * - * @return callable[] An array of escapers - * - * @deprecated since Twig 2.11, to be removed in 3.0; use the same method on EscaperExtension instead - */ - public function getEscapers(/* $triggerDeprecation = true */) - { - if (0 === \func_num_args() || func_get_arg(0)) { - @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.11; use "%s::getEscapers" instead.', __METHOD__, EscaperExtension::class), E_USER_DEPRECATED); - } - - return $this->escapers; - } /** * Sets the default format to be used by the date filter. * - * @param string $format The default date format string - * @param string $dateIntervalFormat The default date interval format string + * @param string|null $format The default date format string + * @param string|null $dateIntervalFormat The default date interval format string */ public function setDateFormat($format = null, $dateIntervalFormat = null) { @@ -183,7 +206,7 @@ final class CoreExtension extends AbstractExtension return $this->numberFormat; } - public function getTokenParsers() + public function getTokenParsers(): array { return [ new ApplyTokenParser(), @@ -193,323 +216,402 @@ final class CoreExtension extends AbstractExtension new IncludeTokenParser(), new BlockTokenParser(), new UseTokenParser(), - new FilterTokenParser(), new MacroTokenParser(), new ImportTokenParser(), new FromTokenParser(), new SetTokenParser(), - new SpacelessTokenParser(), + new TypesTokenParser(), new FlushTokenParser(), new DoTokenParser(), new EmbedTokenParser(), new WithTokenParser(), new DeprecatedTokenParser(), + new GuardTokenParser(), ]; } - public function getFilters() + public function getFilters(): array { return [ // formatting filters - new TwigFilter('date', 'twig_date_format_filter', ['needs_environment' => true]), - new TwigFilter('date_modify', 'twig_date_modify_filter', ['needs_environment' => true]), - new TwigFilter('format', 'sprintf'), - new TwigFilter('replace', 'twig_replace_filter'), - new TwigFilter('number_format', 'twig_number_format_filter', ['needs_environment' => true]), + new TwigFilter('date', [$this, 'formatDate']), + new TwigFilter('date_modify', [$this, 'modifyDate']), + new TwigFilter('format', [self::class, 'sprintf']), + new TwigFilter('replace', [self::class, 'replace']), + new TwigFilter('number_format', [$this, 'formatNumber']), new TwigFilter('abs', 'abs'), - new TwigFilter('round', 'twig_round'), + new TwigFilter('round', [self::class, 'round']), // encoding - new TwigFilter('url_encode', 'twig_urlencode_filter'), + new TwigFilter('url_encode', [self::class, 'urlencode']), new TwigFilter('json_encode', 'json_encode'), - new TwigFilter('convert_encoding', 'twig_convert_encoding'), + new TwigFilter('convert_encoding', [self::class, 'convertEncoding']), // string filters - new TwigFilter('title', 'twig_title_string_filter', ['needs_environment' => true]), - new TwigFilter('capitalize', 'twig_capitalize_string_filter', ['needs_environment' => true]), - new TwigFilter('upper', 'twig_upper_filter', ['needs_environment' => true]), - new TwigFilter('lower', 'twig_lower_filter', ['needs_environment' => true]), - new TwigFilter('striptags', 'strip_tags'), - new TwigFilter('trim', 'twig_trim_filter'), - new TwigFilter('nl2br', 'nl2br', ['pre_escape' => 'html', 'is_safe' => ['html']]), - new TwigFilter('spaceless', 'twig_spaceless', ['is_safe' => ['html']]), + new TwigFilter('title', [self::class, 'titleCase'], ['needs_charset' => true]), + new TwigFilter('capitalize', [self::class, 'capitalize'], ['needs_charset' => true]), + new TwigFilter('upper', [self::class, 'upper'], ['needs_charset' => true]), + new TwigFilter('lower', [self::class, 'lower'], ['needs_charset' => true]), + 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'], 'deprecation_info' => new DeprecatedCallableInfo('twig/twig', '3.12')]), // array helpers - new TwigFilter('join', 'twig_join_filter'), - new TwigFilter('split', 'twig_split_filter', ['needs_environment' => true]), - new TwigFilter('sort', 'twig_sort_filter'), - new TwigFilter('merge', 'twig_array_merge'), - new TwigFilter('batch', 'twig_array_batch'), - new TwigFilter('column', 'twig_array_column'), - new TwigFilter('filter', 'twig_array_filter'), - new TwigFilter('map', 'twig_array_map'), - new TwigFilter('reduce', 'twig_array_reduce'), + new TwigFilter('join', [self::class, 'join']), + new TwigFilter('split', [self::class, 'split'], ['needs_charset' => true]), + new TwigFilter('sort', [self::class, 'sort'], ['needs_environment' => true]), + new TwigFilter('merge', [self::class, 'merge']), + new TwigFilter('batch', [self::class, 'batch']), + new TwigFilter('column', [self::class, 'column']), + new TwigFilter('filter', [self::class, 'filter'], ['needs_environment' => true]), + new TwigFilter('map', [self::class, 'map'], ['needs_environment' => true]), + new TwigFilter('reduce', [self::class, 'reduce'], ['needs_environment' => true]), + new TwigFilter('find', [self::class, 'find'], ['needs_environment' => true]), // string/array filters - new TwigFilter('reverse', 'twig_reverse_filter', ['needs_environment' => true]), - new TwigFilter('length', 'twig_length_filter', ['needs_environment' => true]), - new TwigFilter('slice', 'twig_slice', ['needs_environment' => true]), - new TwigFilter('first', 'twig_first', ['needs_environment' => true]), - new TwigFilter('last', 'twig_last', ['needs_environment' => true]), + new TwigFilter('reverse', [self::class, 'reverse'], ['needs_charset' => true]), + new TwigFilter('shuffle', [self::class, 'shuffle'], ['needs_charset' => true]), + new TwigFilter('length', [self::class, 'length'], ['needs_charset' => true]), + new TwigFilter('slice', [self::class, 'slice'], ['needs_charset' => true]), + new TwigFilter('first', [self::class, 'first'], ['needs_charset' => true]), + new TwigFilter('last', [self::class, 'last'], ['needs_charset' => true]), // iteration and runtime - new TwigFilter('default', '_twig_default_filter', ['node_class' => DefaultFilter::class]), - new TwigFilter('keys', 'twig_get_array_keys_filter'), + new TwigFilter('default', [self::class, 'default'], ['node_class' => DefaultFilter::class]), + new TwigFilter('keys', [self::class, 'keys']), + new TwigFilter('invoke', [self::class, 'invoke']), ]; } - public function getFunctions() + 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'), - new TwigFunction('constant', 'twig_constant'), - new TwigFunction('cycle', 'twig_cycle'), - new TwigFunction('random', 'twig_random', ['needs_environment' => true]), - new TwigFunction('date', 'twig_date_converter', ['needs_environment' => true]), - new TwigFunction('include', 'twig_include', ['needs_environment' => true, 'needs_context' => true, 'is_safe' => ['all']]), - new TwigFunction('source', 'twig_source', ['needs_environment' => true, 'is_safe' => ['all']]), + new TwigFunction('constant', [self::class, 'constant']), + new TwigFunction('cycle', [self::class, 'cycle']), + new TwigFunction('random', [self::class, 'random'], ['needs_charset' => true]), + 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]), + new TwigFunction('enum', [self::class, 'enum'], ['node_class' => EnumFunction::class]), ]; } - public function getTests() + public function getTests(): array { return [ new TwigTest('even', null, ['node_class' => EvenTest::class]), new TwigTest('odd', null, ['node_class' => OddTest::class]), new TwigTest('defined', null, ['node_class' => DefinedTest::class]), - new TwigTest('same as', null, ['node_class' => SameasTest::class]), + new TwigTest('same as', null, ['node_class' => SameasTest::class, 'one_mandatory_argument' => true]), new TwigTest('none', null, ['node_class' => NullTest::class]), new TwigTest('null', null, ['node_class' => NullTest::class]), - new TwigTest('divisible by', null, ['node_class' => DivisiblebyTest::class]), + new TwigTest('divisible by', null, ['node_class' => DivisiblebyTest::class, 'one_mandatory_argument' => true]), new TwigTest('constant', null, ['node_class' => ConstantTest::class]), - new TwigTest('empty', 'twig_test_empty'), - new TwigTest('iterable', 'twig_test_iterable'), + new TwigTest('empty', [self::class, 'testEmpty']), + new TwigTest('iterable', 'is_iterable'), + new TwigTest('sequence', [self::class, 'testSequence']), + new TwigTest('mapping', [self::class, 'testMapping']), + new TwigTest('true', null, ['node_class' => TrueTest::class]), ]; } - public function getNodeVisitors() + public function getNodeVisitors(): array { - return [new MacroAutoImportNodeVisitor()]; + return []; } - public function getOperators() + public function getExpressionParsers(): array { return [ - [ - 'not' => ['precedence' => 50, 'class' => NotUnary::class], - '-' => ['precedence' => 500, 'class' => NegUnary::class], - '+' => ['precedence' => 500, 'class' => PosUnary::class], - ], - [ - 'or' => ['precedence' => 10, 'class' => OrBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'and' => ['precedence' => 15, 'class' => AndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'b-or' => ['precedence' => 16, 'class' => BitwiseOrBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'b-xor' => ['precedence' => 17, 'class' => BitwiseXorBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'b-and' => ['precedence' => 18, 'class' => BitwiseAndBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '==' => ['precedence' => 20, 'class' => EqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '!=' => ['precedence' => 20, 'class' => NotEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '<=>' => ['precedence' => 20, 'class' => SpaceshipBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '<' => ['precedence' => 20, 'class' => LessBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '>' => ['precedence' => 20, 'class' => GreaterBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '>=' => ['precedence' => 20, 'class' => GreaterEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '<=' => ['precedence' => 20, 'class' => LessEqualBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'not in' => ['precedence' => 20, 'class' => NotInBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'in' => ['precedence' => 20, 'class' => InBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'matches' => ['precedence' => 20, 'class' => MatchesBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'starts with' => ['precedence' => 20, 'class' => StartsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'ends with' => ['precedence' => 20, 'class' => EndsWithBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '..' => ['precedence' => 25, 'class' => RangeBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '+' => ['precedence' => 30, 'class' => AddBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '-' => ['precedence' => 30, 'class' => SubBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '~' => ['precedence' => 40, 'class' => ConcatBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '*' => ['precedence' => 60, 'class' => MulBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '/' => ['precedence' => 60, 'class' => DivBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '//' => ['precedence' => 60, 'class' => FloorDivBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '%' => ['precedence' => 60, 'class' => ModBinary::class, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'is' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT], - 'is not' => ['precedence' => 100, 'associativity' => ExpressionParser::OPERATOR_LEFT], - '**' => ['precedence' => 200, 'class' => PowerBinary::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT], - '??' => ['precedence' => 300, 'class' => NullCoalesceExpression::class, 'associativity' => ExpressionParser::OPERATOR_RIGHT], - ], + // unary operators + new UnaryOperatorExpressionParser(NotUnary::class, 'not', 50, new PrecedenceChange('twig/twig', '3.15', 70)), + new UnaryOperatorExpressionParser(SpreadUnary::class, '...', 512, description: 'Spread operator'), + new UnaryOperatorExpressionParser(NegUnary::class, '-', 500), + new UnaryOperatorExpressionParser(PosUnary::class, '+', 500), + + // binary operators + new BinaryOperatorExpressionParser(ElvisBinary::class, '?:', 5, InfixAssociativity::Right, description: 'Elvis operator (a ?: b)', aliases: ['? :']), + new BinaryOperatorExpressionParser(NullCoalesceBinary::class, '??', 300, InfixAssociativity::Right, new PrecedenceChange('twig/twig', '3.15', 5), description: 'Null coalescing operator (a ?? b)'), + new BinaryOperatorExpressionParser(OrBinary::class, 'or', 10), + new BinaryOperatorExpressionParser(XorBinary::class, 'xor', 12), + new BinaryOperatorExpressionParser(AndBinary::class, 'and', 15), + new BinaryOperatorExpressionParser(BitwiseOrBinary::class, 'b-or', 16), + new BinaryOperatorExpressionParser(BitwiseXorBinary::class, 'b-xor', 17), + new BinaryOperatorExpressionParser(BitwiseAndBinary::class, 'b-and', 18), + new BinaryOperatorExpressionParser(EqualBinary::class, '==', 20), + new BinaryOperatorExpressionParser(NotEqualBinary::class, '!=', 20), + new BinaryOperatorExpressionParser(SpaceshipBinary::class, '<=>', 20), + new BinaryOperatorExpressionParser(LessBinary::class, '<', 20), + new BinaryOperatorExpressionParser(GreaterBinary::class, '>', 20), + new BinaryOperatorExpressionParser(GreaterEqualBinary::class, '>=', 20), + new BinaryOperatorExpressionParser(LessEqualBinary::class, '<=', 20), + new BinaryOperatorExpressionParser(NotInBinary::class, 'not in', 20), + new BinaryOperatorExpressionParser(InBinary::class, 'in', 20), + new BinaryOperatorExpressionParser(MatchesBinary::class, 'matches', 20), + new BinaryOperatorExpressionParser(StartsWithBinary::class, 'starts with', 20), + new BinaryOperatorExpressionParser(EndsWithBinary::class, 'ends with', 20), + new BinaryOperatorExpressionParser(HasSomeBinary::class, 'has some', 20), + new BinaryOperatorExpressionParser(HasEveryBinary::class, 'has every', 20), + new BinaryOperatorExpressionParser(RangeBinary::class, '..', 25), + new BinaryOperatorExpressionParser(AddBinary::class, '+', 30), + new BinaryOperatorExpressionParser(SubBinary::class, '-', 30), + new BinaryOperatorExpressionParser(ConcatBinary::class, '~', 40, precedenceChange: new PrecedenceChange('twig/twig', '3.15', 27)), + new BinaryOperatorExpressionParser(MulBinary::class, '*', 60), + new BinaryOperatorExpressionParser(DivBinary::class, '/', 60), + new BinaryOperatorExpressionParser(FloorDivBinary::class, '//', 60, description: 'Floor division'), + new BinaryOperatorExpressionParser(ModBinary::class, '%', 60), + new BinaryOperatorExpressionParser(PowerBinary::class, '**', 200, InfixAssociativity::Right, description: 'Exponentiation operator'), + + // ternary operator + new ConditionalTernaryExpressionParser(), + + // Twig callables + new IsExpressionParser(), + new IsNotExpressionParser(), + new FilterExpressionParser(), + new FunctionExpressionParser(), + + // get attribute operators + new DotExpressionParser(), + new SquareBracketExpressionParser(), + + // group expression + new GroupingExpressionParser(), + + // arrow function + new ArrowExpressionParser(), + + // all literals + new LiteralExpressionParser(), ]; } -} - -class_alias('Twig\Extension\CoreExtension', 'Twig_Extension_Core'); -} - -namespace { - use Twig\Environment; - use Twig\Error\LoaderError; - use Twig\Error\RuntimeError; - use Twig\Extension\CoreExtension; - use Twig\Extension\SandboxExtension; - use Twig\Markup; - use Twig\Source; - use Twig\Template; /** - * Cycles over a value. - * - * @param \ArrayAccess|array $values - * @param int $position The cycle position - * - * @return string The next value in the cycle - */ -function twig_cycle($values, $position) -{ - if (!\is_array($values) && !$values instanceof \ArrayAccess) { - return $values; + * Cycles over a sequence. + * + * @param array|\ArrayAccess $values A non-empty sequence of values + * @param int<0, max> $position The position of the value to return in the cycle + * + * @return mixed The value at the given position in the sequence, wrapping around as needed + * + * @internal + */ + public static function cycle($values, $position): mixed + { + if (!\is_array($values)) { + if (!$values instanceof \ArrayAccess) { + throw new RuntimeError('The "cycle" function expects an array or "ArrayAccess" as first argument.'); + } + + if (!is_countable($values)) { + // To be uncommented in 4.0 + // throw new RuntimeError('The "cycle" function expects a countable sequence as first argument.'); + + trigger_deprecation('twig/twig', '3.12', 'Passing a non-countable sequence of values to "%s()" is deprecated.', __METHOD__); + + return $values; + } + + $values = self::toArray($values, false); + } + + if (!$count = \count($values)) { + throw new RuntimeError('The "cycle" function expects a non-empty sequence.'); + } + + return $values[$position % $count]; } - return $values[$position % \count($values)]; -} + /** + * Returns a random value depending on the supplied parameter type: + * - a random item from a \Traversable or array + * - a random character from a string + * - a random integer between 0 and the integer parameter. + * + * @param \Traversable|array|int|float|string $values The values to pick a random item from + * @param int|null $max Maximum value used when $values is an int + * + * @return mixed A random value from the given sequence + * + * @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is) + * + * @internal + */ + public static function random(string $charset, $values = null, $max = null) + { + if (null === $values) { + return null === $max ? mt_rand() : mt_rand(0, (int) $max); + } -/** - * Returns a random value depending on the supplied parameter type: - * - a random item from a \Traversable or array - * - a random character from a string - * - a random integer between 0 and the integer parameter. - * - * @param \Traversable|array|int|float|string $values The values to pick a random item from - * @param int|null $max Maximum value used when $values is an int - * - * @throws RuntimeError when $values is an empty array (does not apply to an empty string which is returned as is) - * - * @return mixed A random value from the given sequence - */ -function twig_random(Environment $env, $values = null, $max = null) -{ - if (null === $values) { - return null === $max ? mt_rand() : mt_rand(0, $max); - } - - if (\is_int($values) || \is_float($values)) { - if (null === $max) { - if ($values < 0) { - $max = 0; - $min = $values; + if (\is_int($values) || \is_float($values)) { + if (null === $max) { + if ($values < 0) { + $max = 0; + $min = $values; + } else { + $max = $values; + $min = 0; + } } else { - $max = $values; - $min = 0; + $min = $values; } + + return mt_rand((int) $min, (int) $max); + } + + if (\is_string($values)) { + if ('' === $values) { + return ''; + } + + if ('UTF-8' !== $charset) { + $values = self::convertEncoding($values, 'UTF-8', $charset); + } + + // unicode version of str_split() + // split at all positions, but not after the start and not before the end + $values = preg_split('/(? $value) { + $values[$i] = self::convertEncoding($value, $charset, 'UTF-8'); + } + } + } + + if (!is_iterable($values)) { + return $values; + } + + $values = self::toArray($values); + + if (0 === \count($values)) { + throw new RuntimeError('The "random" function cannot pick from an empty sequence or mapping.'); + } + + return $values[array_rand($values, 1)]; + } + + /** + * Formats a date. + * + * {{ post.published_at|date("m/d/Y") }} + * + * @param \DateTimeInterface|\DateInterval|string|int|null $date A date, a timestamp or null to use the current time + * @param string|null $format The target format, null to use the default + * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged + */ + public function formatDate($date, $format = null, $timezone = null): string + { + if (null === $format) { + $formats = $this->getDateFormat(); + $format = $date instanceof \DateInterval ? $formats[1] : $formats[0]; + } + + if ($date instanceof \DateInterval) { + return $date->format($format); + } + + return $this->convertDate($date, $timezone)->format($format); + } + + /** + * Returns a new date object modified. + * + * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }} + * + * @param \DateTimeInterface|string|int|null $date A date, a timestamp or null to use the current time + * @param string $modifier A modifier string + * + * @return \DateTime|\DateTimeImmutable + * + * @internal + */ + public function modifyDate($date, $modifier) + { + return $this->convertDate($date, false)->modify($modifier); + } + + /** + * Returns a formatted string. + * + * @param string|null $format + * + * @internal + */ + public static function sprintf($format, ...$values): string + { + return \sprintf($format ?? '', ...$values); + } + + /** + * @internal + */ + public static function dateConverter(Environment $env, $date, $format = null, $timezone = null): string + { + return $env->getExtension(self::class)->formatDate($date, $format, $timezone); + } + + /** + * Converts an input to a \DateTime instance. + * + * {% if date(user.created_at) < date('+2days') %} + * {# do something #} + * {% endif %} + * + * @param \DateTimeInterface|string|int|null $date A date, a timestamp 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 + * + * @return \DateTime|\DateTimeImmutable + */ + public function convertDate($date = null, $timezone = null) + { + // determine the timezone + if (false !== $timezone) { + if (null === $timezone) { + $timezone = $this->getTimezone(); + } elseif (!$timezone instanceof \DateTimeZone) { + $timezone = new \DateTimeZone($timezone); + } + } + + // immutable dates + if ($date instanceof \DateTimeImmutable) { + return false !== $timezone ? $date->setTimezone($timezone) : $date; + } + + if ($date instanceof \DateTime) { + $date = clone $date; + if (false !== $timezone) { + $date->setTimezone($timezone); + } + + return $date; + } + + if (null === $date || 'now' === $date) { + if (null === $date) { + $date = 'now'; + } + + return new \DateTime($date, false !== $timezone ? $timezone : $this->getTimezone()); + } + + $asString = (string) $date; + if (ctype_digit($asString) || ('' !== $asString && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { + $date = new \DateTime('@'.$date); } else { - $min = $values; - $max = $max; + $date = new \DateTime($date); } - return mt_rand($min, $max); - } - - if (\is_string($values)) { - if ('' === $values) { - return ''; - } - - $charset = $env->getCharset(); - - if ('UTF-8' !== $charset) { - $values = twig_convert_encoding($values, 'UTF-8', $charset); - } - - // unicode version of str_split() - // split at all positions, but not after the start and not before the end - $values = preg_split('/(? $value) { - $values[$i] = twig_convert_encoding($value, $charset, 'UTF-8'); - } - } - } - - if (!twig_test_iterable($values)) { - return $values; - } - - $values = twig_to_array($values); - - if (0 === \count($values)) { - throw new RuntimeError('The random function cannot pick from an empty array.'); - } - - return $values[array_rand($values, 1)]; -} - -/** - * Converts a date to the given format. - * - * {{ post.published_at|date("m/d/Y") }} - * - * @param \DateTimeInterface|\DateInterval|string $date A date - * @param string|null $format The target format, null to use the default - * @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged - * - * @return string The formatted date - */ -function twig_date_format_filter(Environment $env, $date, $format = null, $timezone = null) -{ - if (null === $format) { - $formats = $env->getExtension(CoreExtension::class)->getDateFormat(); - $format = $date instanceof \DateInterval ? $formats[1] : $formats[0]; - } - - if ($date instanceof \DateInterval) { - return $date->format($format); - } - - return twig_date_converter($env, $date, $timezone)->format($format); -} - -/** - * Returns a new date object modified. - * - * {{ post.published_at|date_modify("-1day")|date("m/d/Y") }} - * - * @param \DateTimeInterface|string $date A date - * @param string $modifier A modifier string - * - * @return \DateTimeInterface - */ -function twig_date_modify_filter(Environment $env, $date, $modifier) -{ - $date = twig_date_converter($env, $date, false); - - return $date->modify($modifier); -} - -/** - * Converts an input to a \DateTime instance. - * - * {% if date(user.created_at) < date('+2days') %} - * {# do something #} - * {% endif %} - * - * @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 - * - * @return \DateTimeInterface - */ -function twig_date_converter(Environment $env, $date = null, $timezone = null) -{ - // determine the timezone - if (false !== $timezone) { - if (null === $timezone) { - $timezone = $env->getExtension(CoreExtension::class)->getTimezone(); - } elseif (!$timezone instanceof \DateTimeZone) { - $timezone = new \DateTimeZone($timezone); - } - } - - // immutable dates - if ($date instanceof \DateTimeImmutable) { - return false !== $timezone ? $date->setTimezone($timezone) : $date; - } - - if ($date instanceof \DateTimeInterface) { - $date = clone $date; if (false !== $timezone) { $date->setTimezone($timezone); } @@ -517,836 +619,1130 @@ function twig_date_converter(Environment $env, $date = null, $timezone = null) return $date; } - if (null === $date || 'now' === $date) { - return new \DateTime($date, false !== $timezone ? $timezone : $env->getExtension(CoreExtension::class)->getTimezone()); - } - - $asString = (string) $date; - if (ctype_digit($asString) || (!empty($asString) && '-' === $asString[0] && ctype_digit(substr($asString, 1)))) { - $date = new \DateTime('@'.$date); - } else { - $date = new \DateTime($date, $env->getExtension(CoreExtension::class)->getTimezone()); - } - - if (false !== $timezone) { - $date->setTimezone($timezone); - } - - return $date; -} - -/** - * Replaces strings within a string. - * - * @param string $str String to replace in - * @param array|\Traversable $from Replace values - * - * @return string - */ -function twig_replace_filter($str, $from) -{ - if (!twig_test_iterable($from)) { - throw new RuntimeError(sprintf('The "replace" filter expects an array or "Traversable" as replace values, got "%s".', \is_object($from) ? \get_class($from) : \gettype($from))); - } - - return strtr($str, twig_to_array($from)); -} - -/** - * Rounds a number. - * - * @param int|float $value The value to round - * @param int|float $precision The rounding precision - * @param string $method The method to use for rounding - * - * @return int|float The rounded number - */ -function twig_round($value, $precision = 0, $method = 'common') -{ - if ('common' == $method) { - return round($value, $precision); - } - - if ('ceil' != $method && 'floor' != $method) { - throw new RuntimeError('The round filter only supports the "common", "ceil", and "floor" methods.'); - } - - return $method($value * pow(10, $precision)) / pow(10, $precision); -} - -/** - * Number format filter. - * - * All of the formatting options can be left null, in that case the defaults will - * be used. Supplying any of the parameters will override the defaults set in the - * environment object. - * - * @param mixed $number A float/int/string of the number to format - * @param int $decimal the number of decimal points to display - * @param string $decimalPoint the character(s) to use for the decimal point - * @param string $thousandSep the character(s) to use for the thousands separator - * - * @return string The formatted number - */ -function twig_number_format_filter(Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null) -{ - $defaults = $env->getExtension(CoreExtension::class)->getNumberFormat(); - if (null === $decimal) { - $decimal = $defaults[0]; - } - - if (null === $decimalPoint) { - $decimalPoint = $defaults[1]; - } - - if (null === $thousandSep) { - $thousandSep = $defaults[2]; - } - - return number_format((float) $number, $decimal, $decimalPoint, $thousandSep); -} - -/** - * URL encodes (RFC 3986) a string as a path segment or an array as a query string. - * - * @param string|array $url A URL or an array of query parameters - * - * @return string The URL encoded value - */ -function twig_urlencode_filter($url) -{ - if (\is_array($url)) { - return http_build_query($url, '', '&', PHP_QUERY_RFC3986); - } - - return rawurlencode($url); -} - -/** - * Merges an array with another one. - * - * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %} - * - * {% set items = items|merge({ 'peugeot': 'car' }) %} - * - * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car' } #} - * - * @param array|\Traversable $arr1 An array - * @param array|\Traversable $arr2 An array - * - * @return array The merged array - */ -function twig_array_merge($arr1, $arr2) -{ - if (!twig_test_iterable($arr1)) { - throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($arr1))); - } - - if (!twig_test_iterable($arr2)) { - throw new RuntimeError(sprintf('The merge filter only works with arrays or "Traversable", got "%s" as second argument.', \gettype($arr2))); - } - - return array_merge(twig_to_array($arr1), twig_to_array($arr2)); -} - -/** - * Slices a variable. - * - * @param mixed $item A variable - * @param int $start Start of the slice - * @param int $length Size of the slice - * @param bool $preserveKeys Whether to preserve key or not (when the input is an array) - * - * @return mixed The sliced variable - */ -function twig_slice(Environment $env, $item, $start, $length = null, $preserveKeys = false) -{ - if ($item instanceof \Traversable) { - while ($item instanceof \IteratorAggregate) { - $item = $item->getIterator(); + /** + * Replaces strings within a string. + * + * @param string|null $str String to replace in + * @param array|\Traversable $from Replace values + * + * @internal + */ + public static function replace($str, $from): string + { + if (!is_iterable($from)) { + throw new RuntimeError(\sprintf('The "replace" filter expects a sequence or a mapping, got "%s".', get_debug_type($from))); } - if ($start >= 0 && $length >= 0 && $item instanceof \Iterator) { - try { - return iterator_to_array(new \LimitIterator($item, $start, null === $length ? -1 : $length), $preserveKeys); - } catch (\OutOfBoundsException $e) { - return []; + return strtr($str ?? '', self::toArray($from)); + } + + /** + * Rounds a number. + * + * @param int|float|string|null $value The value to round + * @param int|float $precision The rounding precision + * @param 'common'|'ceil'|'floor' $method The method to use for rounding + * + * @return float The rounded number + * + * @internal + */ + public static function round($value, $precision = 0, $method = 'common') + { + $value = (float) $value; + + if ('common' === $method) { + return round($value, $precision); + } + + if ('ceil' !== $method && 'floor' !== $method) { + throw new RuntimeError('The "round" filter only supports the "common", "ceil", and "floor" methods.'); + } + + return $method($value * 10 ** $precision) / 10 ** $precision; + } + + /** + * Formats a number. + * + * All of the formatting options can be left null, in that case the defaults will + * be used. Supplying any of the parameters will override the defaults set in the + * environment object. + * + * @param mixed $number A float/int/string of the number to format + * @param int|null $decimal the number of decimal points to display + * @param string|null $decimalPoint the character(s) to use for the decimal point + * @param string|null $thousandSep the character(s) to use for the thousands separator + */ + public function formatNumber($number, $decimal = null, $decimalPoint = null, $thousandSep = null): string + { + $defaults = $this->getNumberFormat(); + if (null === $decimal) { + $decimal = $defaults[0]; + } + + if (null === $decimalPoint) { + $decimalPoint = $defaults[1]; + } + + if (null === $thousandSep) { + $thousandSep = $defaults[2]; + } + + return number_format((float) $number, $decimal, $decimalPoint, $thousandSep); + } + + /** + * URL encodes (RFC 3986) a string as a path segment or an array as a query string. + * + * @param string|array|null $url A URL or an array of query parameters + * + * @internal + */ + public static function urlencode($url): string + { + if (\is_array($url)) { + return http_build_query($url, '', '&', \PHP_QUERY_RFC3986); + } + + return rawurlencode($url ?? ''); + } + + /** + * Merges any number of arrays or Traversable objects. + * + * {% set items = { 'apple': 'fruit', 'orange': 'fruit' } %} + * + * {% set items = items|merge({ 'peugeot': 'car' }, { 'banana': 'fruit' }) %} + * + * {# items now contains { 'apple': 'fruit', 'orange': 'fruit', 'peugeot': 'car', 'banana': 'fruit' } #} + * + * @param array|\Traversable ...$arrays Any number of arrays or Traversable objects to merge + * + * @internal + */ + public static function merge(...$arrays): array + { + $result = []; + + foreach ($arrays as $argNumber => $array) { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "merge" filter expects a sequence or a mapping, got "%s" for argument %d.', get_debug_type($array), $argNumber + 1)); } + + $result = array_merge($result, self::toArray($array)); } - $item = iterator_to_array($item, $preserveKeys); + return $result; } - if (\is_array($item)) { - return \array_slice($item, $start, $length, $preserveKeys); - } + /** + * Slices a variable. + * + * @param mixed $item A variable + * @param int $start Start of the slice + * @param int $length Size of the slice + * @param bool $preserveKeys Whether to preserve key or not (when the input is an array) + * + * @return mixed The sliced variable + * + * @internal + */ + public static function slice(string $charset, $item, $start, $length = null, $preserveKeys = false) + { + if ($item instanceof \Traversable) { + while ($item instanceof \IteratorAggregate) { + $item = $item->getIterator(); + } - $item = (string) $item; + if ($start >= 0 && $length >= 0 && $item instanceof \Iterator) { + try { + return iterator_to_array(new \LimitIterator($item, $start, $length ?? -1), $preserveKeys); + } catch (\OutOfBoundsException $e) { + return []; + } + } - return (string) mb_substr($item, $start, $length, $env->getCharset()); -} - -/** - * Returns the first element of the item. - * - * @param mixed $item A variable - * - * @return mixed The first element of the item - */ -function twig_first(Environment $env, $item) -{ - $elements = twig_slice($env, $item, 0, 1, false); - - return \is_string($elements) ? $elements : current($elements); -} - -/** - * Returns the last element of the item. - * - * @param mixed $item A variable - * - * @return mixed The last element of the item - */ -function twig_last(Environment $env, $item) -{ - $elements = twig_slice($env, $item, -1, 1, false); - - return \is_string($elements) ? $elements : current($elements); -} - -/** - * Joins the values to a string. - * - * The separators between elements are empty strings per default, you can define them with the optional parameters. - * - * {{ [1, 2, 3]|join(', ', ' and ') }} - * {# returns 1, 2 and 3 #} - * - * {{ [1, 2, 3]|join('|') }} - * {# returns 1|2|3 #} - * - * {{ [1, 2, 3]|join }} - * {# returns 123 #} - * - * @param array $value An array - * @param string $glue The separator - * @param string|null $and The separator for the last pair - * - * @return string The concatenated string - */ -function twig_join_filter($value, $glue = '', $and = null) -{ - if (!twig_test_iterable($value)) { - $value = (array) $value; - } - - $value = twig_to_array($value, false); - - if (0 === \count($value)) { - return ''; - } - - if (null === $and || $and === $glue) { - return implode($glue, $value); - } - - if (1 === \count($value)) { - return $value[0]; - } - - return implode($glue, \array_slice($value, 0, -1)).$and.$value[\count($value) - 1]; -} - -/** - * Splits the string into an array. - * - * {{ "one,two,three"|split(',') }} - * {# returns [one, two, three] #} - * - * {{ "one,two,three,four,five"|split(',', 3) }} - * {# returns [one, two, "three,four,five"] #} - * - * {{ "123"|split('') }} - * {# returns [1, 2, 3] #} - * - * {{ "aabbcc"|split('', 2) }} - * {# returns [aa, bb, cc] #} - * - * @param string $value A string - * @param string $delimiter The delimiter - * @param int $limit The limit - * - * @return array The split string as an array - */ -function twig_split_filter(Environment $env, $value, $delimiter, $limit = null) -{ - if (\strlen($delimiter) > 0) { - return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit); - } - - if ($limit <= 1) { - return preg_split('/(?getCharset()); - if ($length < $limit) { - return [$value]; - } - - $r = []; - for ($i = 0; $i < $length; $i += $limit) { - $r[] = mb_substr($value, $i, $limit, $env->getCharset()); - } - - 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 - */ -function _twig_default_filter($value, $default = '') -{ - if (twig_test_empty($value)) { - return $default; - } - - return $value; -} - -/** - * Returns the keys for the given array. - * - * It is useful when you want to iterate over the keys of an array: - * - * {% for key in array|keys %} - * {# ... #} - * {% endfor %} - * - * @param array $array An array - * - * @return array The keys - */ -function twig_get_array_keys_filter($array) -{ - if ($array instanceof \Traversable) { - while ($array instanceof \IteratorAggregate) { - $array = $array->getIterator(); + $item = iterator_to_array($item, $preserveKeys); } - if ($array instanceof \Iterator) { + if (\is_array($item)) { + return \array_slice($item, $start, $length, $preserveKeys); + } + + return mb_substr((string) $item, $start, $length, $charset); + } + + /** + * Returns the first element of the item. + * + * @param mixed $item A variable + * + * @return mixed The first element of the item + * + * @internal + */ + public static function first(string $charset, $item) + { + $elements = self::slice($charset, $item, 0, 1, false); + + return \is_string($elements) ? $elements : current($elements); + } + + /** + * Returns the last element of the item. + * + * @param mixed $item A variable + * + * @return mixed The last element of the item + * + * @internal + */ + public static function last(string $charset, $item) + { + $elements = self::slice($charset, $item, -1, 1, false); + + return \is_string($elements) ? $elements : current($elements); + } + + /** + * Joins the values to a string. + * + * The separators between elements are empty strings per default, you can define them with the optional parameters. + * + * {{ [1, 2, 3]|join(', ', ' and ') }} + * {# returns 1, 2 and 3 #} + * + * {{ [1, 2, 3]|join('|') }} + * {# returns 1|2|3 #} + * + * {{ [1, 2, 3]|join }} + * {# returns 123 #} + * + * @param iterable|array|string|float|int|bool|null $value An array + * @param string $glue The separator + * @param string|null $and The separator for the last pair + * + * @internal + */ + public static function join($value, $glue = '', $and = null): string + { + if (!is_iterable($value)) { + $value = (array) $value; + } + + $value = self::toArray($value, false); + + if (0 === \count($value)) { + return ''; + } + + if (null === $and || $and === $glue) { + return implode($glue, $value); + } + + if (1 === \count($value)) { + return $value[0]; + } + + return implode($glue, \array_slice($value, 0, -1)).$and.$value[\count($value) - 1]; + } + + /** + * Splits the string into an array. + * + * {{ "one,two,three"|split(',') }} + * {# returns [one, two, three] #} + * + * {{ "one,two,three,four,five"|split(',', 3) }} + * {# returns [one, two, "three,four,five"] #} + * + * {{ "123"|split('') }} + * {# returns [1, 2, 3] #} + * + * {{ "aabbcc"|split('', 2) }} + * {# returns [aa, bb, cc] #} + * + * @param string|null $value A string + * @param string $delimiter The delimiter + * @param int|null $limit The limit + * + * @internal + */ + public static function split(string $charset, $value, $delimiter, $limit = null): array + { + $value = $value ?? ''; + + if ('' !== $delimiter) { + return null === $limit ? explode($delimiter, $value) : explode($delimiter, $value, $limit); + } + + if ($limit <= 1) { + return preg_split('/(?getIterator(); + } + $keys = []; - $array->rewind(); - while ($array->valid()) { - $keys[] = $array->key(); - $array->next(); + if ($array instanceof \Iterator) { + $array->rewind(); + while ($array->valid()) { + $keys[] = $array->key(); + $array->next(); + } + + return $keys; + } + + foreach ($array as $key => $item) { + $keys[] = $key; } return $keys; } - $keys = []; - foreach ($array as $key => $item) { - $keys[] = $key; + if (!\is_array($array)) { + return []; } - return $keys; + return array_keys($array); } - if (!\is_array($array)) { - return []; + /** + * Invokes a callable. + * + * @internal + */ + public static function invoke(\Closure $arrow, ...$arguments): mixed + { + return $arrow(...$arguments); } - return array_keys($array); -} + /** + * Reverses a variable. + * + * @param array|\Traversable|string|null $item An array, a \Traversable instance, or a string + * @param bool $preserveKeys Whether to preserve key or not + * + * @return mixed The reversed input + * + * @internal + */ + public static function reverse(string $charset, $item, $preserveKeys = false) + { + if ($item instanceof \Traversable) { + return array_reverse(iterator_to_array($item), $preserveKeys); + } -/** - * Reverses a variable. - * - * @param array|\Traversable|string $item An array, a \Traversable instance, or a string - * @param bool $preserveKeys Whether to preserve key or not - * - * @return mixed The reversed input - */ -function twig_reverse_filter(Environment $env, $item, $preserveKeys = false) -{ - if ($item instanceof \Traversable) { - return array_reverse(iterator_to_array($item), $preserveKeys); + if (\is_array($item)) { + return array_reverse($item, $preserveKeys); + } + + $string = (string) $item; + + if ('UTF-8' !== $charset) { + $string = self::convertEncoding($string, 'UTF-8', $charset); + } + + preg_match_all('/./us', $string, $matches); + + $string = implode('', array_reverse($matches[0])); + + if ('UTF-8' !== $charset) { + $string = self::convertEncoding($string, $charset, 'UTF-8'); + } + + return $string; } - if (\is_array($item)) { - return array_reverse($item, $preserveKeys); - } - - $string = (string) $item; - - $charset = $env->getCharset(); - - if ('UTF-8' !== $charset) { - $item = twig_convert_encoding($string, 'UTF-8', $charset); - } - - preg_match_all('/./us', $item, $matches); - - $string = implode('', array_reverse($matches[0])); - - if ('UTF-8' !== $charset) { - $string = twig_convert_encoding($string, $charset, 'UTF-8'); - } - - return $string; -} - -/** - * Sorts an array. - * - * @param array|\Traversable $array - * - * @return array - */ -function twig_sort_filter($array, $arrow = null) -{ - if ($array instanceof \Traversable) { - $array = iterator_to_array($array); - } elseif (!\is_array($array)) { - throw new RuntimeError(sprintf('The sort filter only works with arrays or "Traversable", got "%s".', \gettype($array))); - } - - if (null !== $arrow) { - uasort($array, $arrow); - } else { - asort($array); - } - - return $array; -} - -/** - * @internal - */ -function twig_in_filter($value, $compare) -{ - if ($value instanceof Markup) { - $value = (string) $value; - } - if ($compare instanceof Markup) { - $compare = (string) $compare; - } - - if (\is_array($compare)) { - return \in_array($value, $compare, \is_object($value) || \is_resource($value)); - } elseif (\is_string($compare) && (\is_string($value) || \is_int($value) || \is_float($value))) { - return '' === $value || false !== strpos($compare, (string) $value); - } elseif ($compare instanceof \Traversable) { - if (\is_object($value) || \is_resource($value)) { - foreach ($compare as $item) { - if ($item === $value) { - return true; - } + /** + * Shuffles an array, a \Traversable instance, or a string. + * The function does not preserve keys. + * + * @param array|\Traversable|string|null $item + * + * @internal + */ + public static function shuffle(string $charset, $item) + { + if (\is_string($item)) { + if ('UTF-8' !== $charset) { + $item = self::convertEncoding($item, 'UTF-8', $charset); } + + $item = preg_split('/(?\s+<', $content)); -} - -function twig_convert_encoding($string, $to, $from) -{ - if (!function_exists('iconv')) { - throw new RuntimeError('Unable to convert encoding: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); - } - - return iconv($from, $to, $string); -} - -/** - * Returns the length of a variable. - * - * @param mixed $thing A variable - * - * @return int The length of the value - */ -function twig_length_filter(Environment $env, $thing) -{ - if (null === $thing) { - return 0; - } - - if (is_scalar($thing)) { - return mb_strlen($thing, $env->getCharset()); - } - - if ($thing instanceof \Countable || \is_array($thing) || $thing instanceof \SimpleXMLElement) { - return \count($thing); - } - - if ($thing instanceof \Traversable) { - return iterator_count($thing); - } - - if (method_exists($thing, '__toString') && !$thing instanceof \Countable) { - return mb_strlen((string) $thing, $env->getCharset()); - } - - return 1; -} - -/** - * Converts a string to uppercase. - * - * @param string $string A string - * - * @return string The uppercased string - */ -function twig_upper_filter(Environment $env, $string) -{ - return mb_strtoupper($string, $env->getCharset()); -} - -/** - * Converts a string to lowercase. - * - * @param string $string A string - * - * @return string The lowercased string - */ -function twig_lower_filter(Environment $env, $string) -{ - return mb_strtolower($string, $env->getCharset()); -} - -/** - * Returns a titlecased string. - * - * @param string $string A string - * - * @return string The titlecased string - */ -function twig_title_string_filter(Environment $env, $string) -{ - if (null !== $charset = $env->getCharset()) { - return mb_convert_case($string, MB_CASE_TITLE, $charset); - } - - return ucwords(strtolower($string)); -} - -/** - * Returns a capitalized string. - * - * @param string $string A string - * - * @return string The capitalized string - */ -function twig_capitalize_string_filter(Environment $env, $string) -{ - $charset = $env->getCharset(); - - return mb_strtoupper(mb_substr($string, 0, 1, $charset), $charset).mb_strtolower(mb_substr($string, 1, null, $charset), $charset); -} - -/** - * @internal - */ -function twig_call_macro(Template $template, string $method, array $args, int $lineno, array $context, Source $source) -{ - if (!method_exists($template, $method)) { - $parent = $template; - while ($parent = $parent->getParent($context)) { - if (method_exists($parent, $method)) { - return $parent->$method(...$args); + /** + * Compares two values using a more strict version of the PHP non-strict comparison operator. + * + * @see https://wiki.php.net/rfc/string_to_number_comparison + * @see https://wiki.php.net/rfc/trailing_whitespace_numerics + * + * @internal + */ + public static function compare($a, $b) + { + // int <=> string + if (\is_int($a) && \is_string($b)) { + $bTrim = trim($b, " \t\n\r\v\f"); + if (!is_numeric($bTrim)) { + return (string) $a <=> $b; + } + if ((int) $bTrim == $bTrim) { + return $a <=> (int) $bTrim; + } else { + return (float) $a <=> (float) $bTrim; + } + } + if (\is_string($a) && \is_int($b)) { + $aTrim = trim($a, " \t\n\r\v\f"); + if (!is_numeric($aTrim)) { + return $a <=> (string) $b; + } + if ((int) $aTrim == $aTrim) { + return (int) $aTrim <=> $b; + } else { + return (float) $aTrim <=> (float) $b; } } - throw new RuntimeError(sprintf('Macro "%s" is not defined in template "%s".', substr($method, \strlen('macro_')), $template->getTemplateName()), $lineno, $source); + // float <=> string + if (\is_float($a) && \is_string($b)) { + if (is_nan($a)) { + return 1; + } + $bTrim = trim($b, " \t\n\r\v\f"); + if (!is_numeric($bTrim)) { + return (string) $a <=> $b; + } + + return $a <=> (float) $bTrim; + } + if (\is_string($a) && \is_float($b)) { + if (is_nan($b)) { + return 1; + } + $aTrim = trim($a, " \t\n\r\v\f"); + if (!is_numeric($aTrim)) { + return $a <=> (string) $b; + } + + return (float) $aTrim <=> $b; + } + + // fallback to <=> + return $a <=> $b; } - return $template->$method(...$args); -} - -/** - * @internal - */ -function twig_ensure_traversable($seq) -{ - if ($seq instanceof \Traversable || \is_array($seq)) { - return $seq; - } - - return []; -} - -/** - * @internal - */ -function twig_to_array($seq, $preserveKeys = true) -{ - if ($seq instanceof \Traversable) { - return iterator_to_array($seq, $preserveKeys); - } - - if (!\is_array($seq)) { - return $seq; - } - - return $preserveKeys ? $seq : array_values($seq); -} - -/** - * Checks if a variable is empty. - * - * {# evaluates to true if the foo variable is null, false, or the empty string #} - * {% if foo is empty %} - * {# ... #} - * {% endif %} - * - * @param mixed $value A variable - * - * @return bool true if the value is empty, false otherwise - */ -function twig_test_empty($value) -{ - if ($value instanceof \Countable) { - return 0 == \count($value); - } - - if ($value instanceof \Traversable) { - return !iterator_count($value); - } - - if (\is_object($value) && method_exists($value, '__toString')) { - return '' === (string) $value; - } - - return '' === $value || false === $value || null === $value || [] === $value; -} - -/** - * Checks if a variable is traversable. - * - * {# evaluates to true if the foo variable is an array or a traversable object #} - * {% if foo is iterable %} - * {# ... #} - * {% endif %} - * - * @param mixed $value A variable - * - * @return bool true if the value is traversable - */ -function twig_test_iterable($value) -{ - return $value instanceof \Traversable || \is_array($value); -} - -/** - * Renders a template. - * - * @param array $context - * @param string|array $template The template to render or an array of templates to try consecutively - * @param array $variables The variables to pass to the template - * @param bool $withContext - * @param bool $ignoreMissing Whether to ignore missing templates or not - * @param bool $sandboxed Whether to sandbox the template or not - * - * @return string The rendered template - */ -function twig_include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false) -{ - $alreadySandboxed = false; - $sandbox = null; - if ($withContext) { - $variables = array_merge($context, $variables); - } - - if ($isSandboxed = $sandboxed && $env->hasExtension(SandboxExtension::class)) { - $sandbox = $env->getExtension(SandboxExtension::class); - if (!$alreadySandboxed = $sandbox->isSandboxed()) { - $sandbox->enableSandbox(); + /** + * @throws RuntimeError When an invalid pattern is used + * + * @internal + */ + public static function matches(string $regexp, ?string $str): int + { + set_error_handler(function ($t, $m) use ($regexp) { + throw new RuntimeError(\sprintf('Regexp "%s" passed to "matches" is not valid', $regexp).substr($m, 12)); + }); + try { + return preg_match($regexp, $str ?? ''); + } finally { + restore_error_handler(); } } - try { - $loaded = null; + /** + * Returns a trimmed string. + * + * @param string|\Stringable|null $string + * @param string|null $characterMask + * @param string $side left, right, or both + * + * @throws RuntimeError When an invalid trimming side is used + * + * @internal + */ + public static function trim($string, $characterMask = null, $side = 'both'): string|\Stringable + { + if (null === $characterMask) { + $characterMask = self::DEFAULT_TRIM_CHARS; + } + + $trimmed = match ($side) { + 'both' => trim($string ?? '', $characterMask), + 'left' => ltrim($string ?? '', $characterMask), + 'right' => rtrim($string ?? '', $characterMask), + default => throw new RuntimeError('Trimming side must be "left", "right" or "both".'), + }; + + // trimming a safe string with the default character mask always returns a safe string (independently of the context) + return $string instanceof Markup && self::DEFAULT_TRIM_CHARS === $characterMask ? new Markup($trimmed, $string->getCharset()) : $trimmed; + } + + /** + * Inserts HTML line breaks before all newlines in a string. + * + * @param string|null $string + * + * @internal + */ + public static function nl2br($string): string + { + return nl2br($string ?? ''); + } + + /** + * Removes whitespaces between HTML tags. + * + * @param string|null $content + * + * @internal + */ + public static function spaceless($content): string + { + return trim(preg_replace('/>\s+<', $content ?? '')); + } + + /** + * @param string|null $string + * @param string $to + * @param string $from + * + * @internal + */ + public static function convertEncoding($string, $to, $from): string + { + if (!\function_exists('iconv')) { + throw new RuntimeError('Unable to convert encoding: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); + } + + return iconv($from, $to, $string ?? ''); + } + + /** + * Returns the length of a variable. + * + * @param mixed $thing A variable + * + * @internal + */ + public static function length(string $charset, $thing): int + { + if (null === $thing) { + return 0; + } + + if (\is_scalar($thing)) { + return mb_strlen($thing, $charset); + } + + if ($thing instanceof \Countable || \is_array($thing) || $thing instanceof \SimpleXMLElement) { + return \count($thing); + } + + if ($thing instanceof \Traversable) { + return iterator_count($thing); + } + + if ($thing instanceof \Stringable) { + return mb_strlen((string) $thing, $charset); + } + + return 1; + } + + /** + * Converts a string to uppercase. + * + * @param string|null $string A string + * + * @internal + */ + public static function upper(string $charset, $string): string + { + return mb_strtoupper($string ?? '', $charset); + } + + /** + * Converts a string to lowercase. + * + * @param string|null $string A string + * + * @internal + */ + public static function lower(string $charset, $string): string + { + return mb_strtolower($string ?? '', $charset); + } + + /** + * Strips HTML and PHP tags from a string. + * + * @param string|null $string + * @param string[]|string|null $allowable_tags + * + * @internal + */ + public static function striptags($string, $allowable_tags = null): string + { + return strip_tags($string ?? '', $allowable_tags); + } + + /** + * Returns a titlecased string. + * + * @param string|null $string A string + * + * @internal + */ + public static function titleCase(string $charset, $string): string + { + return mb_convert_case($string ?? '', \MB_CASE_TITLE, $charset); + } + + /** + * Returns a capitalized string. + * + * @param string|null $string A string + * + * @internal + */ + public static function capitalize(string $charset, $string): string + { + return mb_strtoupper(mb_substr($string ?? '', 0, 1, $charset), $charset).mb_strtolower(mb_substr($string ?? '', 1, null, $charset), $charset); + } + + /** + * @internal + * + * to be removed in 4.0 + */ + public static function callMacro(Template $template, string $method, array $args, int $lineno, array $context, Source $source) + { + if (!method_exists($template, $method)) { + $parent = $template; + while ($parent = $parent->getParent($context)) { + if (method_exists($parent, $method)) { + return $parent->$method(...$args); + } + } + + throw new RuntimeError(\sprintf('Macro "%s" is not defined in template "%s".', substr($method, \strlen('macro_')), $template->getTemplateName()), $lineno, $source); + } + + return $template->$method(...$args); + } + + /** + * @template TSequence + * + * @param TSequence $seq + * + * @return ($seq is iterable ? TSequence : array{}) + * + * @internal + */ + public static function ensureTraversable($seq) + { + if (is_iterable($seq)) { + return $seq; + } + + return []; + } + + /** + * @internal + */ + public static function toArray($seq, $preserveKeys = true) + { + if ($seq instanceof \Traversable) { + return iterator_to_array($seq, $preserveKeys); + } + + if (!\is_array($seq)) { + return $seq; + } + + return $preserveKeys ? $seq : array_values($seq); + } + + /** + * Checks if a variable is empty. + * + * {# evaluates to true if the foo variable is null, false, or the empty string #} + * {% if foo is empty %} + * {# ... #} + * {% endif %} + * + * @param mixed $value A variable + * + * @internal + */ + public static function testEmpty($value): bool + { + if ($value instanceof \Countable) { + return 0 === \count($value); + } + + if ($value instanceof \Traversable) { + return !iterator_count($value); + } + + if ($value instanceof \Stringable) { + return '' === (string) $value; + } + + return '' === $value || false === $value || null === $value || [] === $value; + } + + /** + * Checks if a variable is a sequence. + * + * {# evaluates to true if the foo variable is a sequence #} + * {% if foo is sequence %} + * {# ... #} + * {% endif %} + * + * @internal + */ + public static function testSequence($value): bool + { + if ($value instanceof \ArrayObject) { + $value = $value->getArrayCopy(); + } + + if ($value instanceof \Traversable) { + $value = iterator_to_array($value); + } + + return \is_array($value) && array_is_list($value); + } + + /** + * Checks if a variable is a mapping. + * + * {# evaluates to true if the foo variable is a mapping #} + * {% if foo is mapping %} + * {# ... #} + * {% endif %} + * + * @internal + */ + public static function testMapping($value): bool + { + if ($value instanceof \ArrayObject) { + $value = $value->getArrayCopy(); + } + + if ($value instanceof \Traversable) { + $value = iterator_to_array($value); + } + + return (\is_array($value) && !array_is_list($value)) || \is_object($value); + } + + /** + * Renders a template. + * + * @param array $context + * @param string|array|TemplateWrapper $template The template to render or an array of templates to try consecutively + * @param array $variables The variables to pass to the template + * @param bool $withContext + * @param bool $ignoreMissing Whether to ignore missing templates or not + * @param bool $sandboxed Whether to sandbox the template or not + * + * @internal + */ + public static function include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false): string + { + $alreadySandboxed = false; + $sandbox = null; + if ($withContext) { + $variables = array_merge($context, $variables); + } + + if ($isSandboxed = $sandboxed && $env->hasExtension(SandboxExtension::class)) { + $sandbox = $env->getExtension(SandboxExtension::class); + if (!$alreadySandboxed = $sandbox->isSandboxed()) { + $sandbox->enableSandbox(); + } + } + try { - $loaded = $env->resolveTemplate($template); + $loaded = null; + try { + $loaded = $env->resolveTemplate($template); + } catch (LoaderError $e) { + if (!$ignoreMissing) { + throw $e; + } + + return ''; + } + + if ($isSandboxed) { + $loaded->unwrap()->checkSecurity(); + } + + return $loaded->render($variables); + } finally { + if ($isSandboxed && !$alreadySandboxed) { + $sandbox->disableSandbox(); + } + } + } + + /** + * Returns a template content without rendering it. + * + * @param string $name The template name + * @param bool $ignoreMissing Whether to ignore missing templates or not + * + * @internal + */ + public static function source(Environment $env, $name, $ignoreMissing = false): string + { + $loader = $env->getLoader(); + try { + return $loader->getSourceContext($name)->getCode(); } catch (LoaderError $e) { if (!$ignoreMissing) { throw $e; } - } - return $loaded ? $loaded->render($variables) : ''; - } finally { - if ($isSandboxed && !$alreadySandboxed) { - $sandbox->disableSandbox(); - } - } -} - -/** - * Returns a template content without rendering it. - * - * @param string $name The template name - * @param bool $ignoreMissing Whether to ignore missing templates or not - * - * @return string The template source - */ -function twig_source(Environment $env, $name, $ignoreMissing = false) -{ - $loader = $env->getLoader(); - try { - return $loader->getSourceContext($name)->getCode(); - } catch (LoaderError $e) { - if (!$ignoreMissing) { - throw $e; - } - } -} - -/** - * Provides the ability to get constants from instances as well as class/global constants. - * - * @param string $constant The name of the constant - * @param object|null $object The object to get the constant from - * - * @return string - */ -function twig_constant($constant, $object = null) -{ - if (null !== $object) { - $constant = \get_class($object).'::'.$constant; - } - - return \constant($constant); -} - -/** - * Checks if a constant exists. - * - * @param string $constant The name of the constant - * @param object|null $object The object to get the constant from - * - * @return bool - */ -function twig_constant_is_defined($constant, $object = null) -{ - if (null !== $object) { - $constant = \get_class($object).'::'.$constant; - } - - return \defined($constant); -} - -/** - * Batches item. - * - * @param array $items An array of items - * @param int $size The size of the batch - * @param mixed $fill A value used to fill missing items - * - * @return array - */ -function twig_array_batch($items, $size, $fill = null, $preserveKeys = true) -{ - if (!twig_test_iterable($items)) { - throw new RuntimeError(sprintf('The "batch" filter expects an array or "Traversable", got "%s".', \is_object($items) ? \get_class($items) : \gettype($items))); - } - - $size = ceil($size); - - $result = array_chunk(twig_to_array($items, $preserveKeys), $size, $preserveKeys); - - if (null !== $fill && $result) { - $last = \count($result) - 1; - if ($fillCount = $size - \count($result[$last])) { - for ($i = 0; $i < $fillCount; ++$i) { - $result[$last][] = $fill; - } + return ''; } } - return $result; -} + /** + * 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)); + } -/** - * Returns the attribute value for a given array/object. - * - * @param mixed $object The object or array from where to get the item - * @param mixed $item The item to get from the array or object - * @param array $arguments An array of arguments to pass if the item is an object method - * @param string $type The type of attribute (@see \Twig\Template constants) - * @param bool $isDefinedTest Whether this is only a defined check - * @param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not - * @param int $lineno The template line where the attribute was called - * - * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true - * - * @throws RuntimeError if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false - * - * @internal - */ -function twig_get_attribute(Environment $env, Source $source, $object, $item, array $arguments = [], $type = /* Template::ANY_CALL */ 'any', $isDefinedTest = false, $ignoreStrictCheck = false, $sandboxed = false, int $lineno = -1) -{ - // array - if (/* Template::METHOD_CALL */ 'method' !== $type) { - $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item; + return $enum::cases(); + } - if (((\is_array($object) || $object instanceof \ArrayObject) && (isset($object[$arrayItem]) || \array_key_exists($arrayItem, (array) $object))) - || ($object instanceof ArrayAccess && isset($object[$arrayItem])) - ) { - if ($isDefinedTest) { - return true; + /** + * Provides the ability to access enums by their class names. + * + * @template T of \UnitEnum + * + * @param class-string $enum + * + * @return T + * + * @internal + */ + public static function enum(string $enum): \UnitEnum + { + if (!enum_exists($enum)) { + throw new RuntimeError(\sprintf('"%s" is not an enum.', $enum)); + } + + if (!$cases = $enum::cases()) { + throw new RuntimeError(\sprintf('"%s" is an empty enum.', $enum)); + } + + return $cases[0]; + } + + /** + * 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 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, bool $checkDefined = false) + { + if (null !== $object) { + if ('class' === $constant) { + return $checkDefined ? true : $object::class; } - return $object[$arrayItem]; + $constant = $object::class.'::'.$constant; } - if (/* Template::ARRAY_CALL */ 'array' === $type || !\is_object($object)) { + 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)); + } + + throw new RuntimeError(\sprintf('Constant "%s" is undefined.', $constant)); + } + + return $checkDefined ? true : \constant($constant); + } + + /** + * Batches item. + * + * @param array $items An array of items + * @param int $size The size of the batch + * @param mixed $fill A value used to fill missing items + * + * @internal + */ + public static function batch($items, $size, $fill = null, $preserveKeys = true): array + { + if (!is_iterable($items)) { + throw new RuntimeError(\sprintf('The "batch" filter expects a sequence or a mapping, got "%s".', get_debug_type($items))); + } + + $size = (int) ceil($size); + + $result = array_chunk(self::toArray($items, $preserveKeys), $size, $preserveKeys); + + if (null !== $fill && $result) { + $last = \count($result) - 1; + if ($fillCount = $size - \count($result[$last])) { + for ($i = 0; $i < $fillCount; ++$i) { + $result[$last][] = $fill; + } + } + } + + return $result; + } + + /** + * Returns the attribute value for a given array/object. + * + * @param mixed $object The object or array from where to get the item + * @param mixed $item The item to get from the array or object + * @param array $arguments An array of arguments to pass if the item is an object method + * @param string $type The type of attribute (@see \Twig\Template constants) + * @param bool $isDefinedTest Whether this is only a defined check + * @param bool $ignoreStrictCheck Whether to ignore the strict attribute check or not + * @param int $lineno The template line where the attribute was called + * + * @return mixed The attribute value, or a Boolean when $isDefinedTest is true, or null when the attribute is not set and $ignoreStrictCheck is true + * + * @throws RuntimeError if the attribute does not exist and Twig is running in strict mode and $isDefinedTest is false + * + * @internal + */ + 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) + { + $propertyNotAllowedError = null; + + // array + if (Template::METHOD_CALL !== $type) { + $arrayItem = \is_bool($item) || \is_float($item) ? (int) $item : $item; + + if ($sandboxed && $object instanceof \ArrayAccess && !\in_array($object::class, self::ARRAY_LIKE_CLASSES, true)) { + try { + $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $arrayItem, $lineno, $source); + } catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) { + goto methodCheck; + } + } + + if (match (true) { + \is_array($object) => \array_key_exists($arrayItem, $object), + $object instanceof \ArrayAccess => $object->offsetExists($arrayItem), + default => false, + }) { + if ($isDefinedTest) { + return true; + } + + return $object[$arrayItem]; + } + + if (Template::ARRAY_CALL === $type || !\is_object($object)) { + if ($isDefinedTest) { + return false; + } + + if ($ignoreStrictCheck || !$env->isStrictVariables()) { + return; + } + + if ($object instanceof \ArrayAccess) { + $message = \sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, $object::class); + } elseif (\is_object($object)) { + $message = \sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, $object::class); + } elseif (\is_array($object)) { + if (!$object) { + $message = \sprintf('Key "%s" does not exist as the sequence/mapping is empty.', $arrayItem); + } else { + $message = \sprintf('Key "%s" for sequence/mapping with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object))); + } + } elseif (Template::ARRAY_CALL === $type) { + if (null === $object) { + $message = \sprintf('Impossible to access a key ("%s") on a null variable.', $item); + } else { + $message = \sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object); + } + } elseif (null === $object) { + $message = \sprintf('Impossible to access an attribute ("%s") on a null variable.', $item); + } else { + $message = \sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object); + } + + throw new RuntimeError($message, $lineno, $source); + } + } + + $item = (string) $item; + + if (!\is_object($object)) { if ($isDefinedTest) { return false; } @@ -1355,212 +1751,439 @@ function twig_get_attribute(Environment $env, Source $source, $object, $item, ar return; } - if ($object instanceof ArrayAccess) { - $message = sprintf('Key "%s" in object with ArrayAccess of class "%s" does not exist.', $arrayItem, \get_class($object)); - } elseif (\is_object($object)) { - $message = sprintf('Impossible to access a key "%s" on an object of class "%s" that does not implement ArrayAccess interface.', $item, \get_class($object)); + if (null === $object) { + $message = \sprintf('Impossible to invoke a method ("%s") on a null variable.', $item); } elseif (\is_array($object)) { - if (empty($object)) { - $message = sprintf('Key "%s" does not exist as the array is empty.', $arrayItem); - } else { - $message = sprintf('Key "%s" for array with keys "%s" does not exist.', $arrayItem, implode(', ', array_keys($object))); - } - } elseif (/* Template::ARRAY_CALL */ 'array' === $type) { - if (null === $object) { - $message = sprintf('Impossible to access a key ("%s") on a null variable.', $item); - } else { - $message = sprintf('Impossible to access a key ("%s") on a %s variable ("%s").', $item, \gettype($object), $object); - } - } elseif (null === $object) { - $message = sprintf('Impossible to access an attribute ("%s") on a null variable.', $item); + $message = \sprintf('Impossible to invoke a method ("%s") on a sequence/mapping.', $item); } else { - $message = sprintf('Impossible to access an attribute ("%s") on a %s variable ("%s").', $item, \gettype($object), $object); + $message = \sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, get_debug_type($object), $object); } throw new RuntimeError($message, $lineno, $source); } - } - if (!\is_object($object)) { - if ($isDefinedTest) { - return false; + if ($object instanceof Template) { + throw new RuntimeError('Accessing \Twig\Template attributes is forbidden.', $lineno, $source); } - if ($ignoreStrictCheck || !$env->isStrictVariables()) { - return; - } - - if (null === $object) { - $message = sprintf('Impossible to invoke a method ("%s") on a null variable.', $item); - } elseif (\is_array($object)) { - $message = sprintf('Impossible to invoke a method ("%s") on an array.', $item); - } else { - $message = sprintf('Impossible to invoke a method ("%s") on a %s variable ("%s").', $item, \gettype($object), $object); - } - - throw new RuntimeError($message, $lineno, $source); - } - - if ($object instanceof Template) { - throw new RuntimeError('Accessing \Twig\Template attributes is forbidden.', $lineno, $source); - } - - // object property - if (/* Template::METHOD_CALL */ 'method' !== $type) { - if (isset($object->$item) || \array_key_exists((string) $item, (array) $object)) { - if ($isDefinedTest) { - return true; - } - + // object property + if (Template::METHOD_CALL !== $type) { if ($sandboxed) { - $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source); + try { + $env->getExtension(SandboxExtension::class)->checkPropertyAllowed($object, $item, $lineno, $source); + } catch (SecurityNotAllowedPropertyError $propertyNotAllowedError) { + goto methodCheck; + } } - return $object->$item; + static $propertyCheckers = []; + + if ($object instanceof \Closure && '__invoke' === $item) { + return $isDefinedTest ? true : $object(); + } + + if (isset($object->$item) + || ($propertyCheckers[$object::class][$item] ??= self::getPropertyChecker($object::class, $item))($object, $item) + ) { + if ($isDefinedTest) { + return true; + } + + return $object->$item; + } + + if ($object instanceof \DateTimeInterface && \in_array($item, ['date', 'timezone', 'timezone_type'], true)) { + if ($isDefinedTest) { + return true; + } + + return ((array) $object)[$item]; + } + + if (\defined($object::class.'::'.$item)) { + if ($isDefinedTest) { + return true; + } + + return \constant($object::class.'::'.$item); + } } - } - static $cache = []; + methodCheck: - $class = \get_class($object); + static $cache = []; - // object method - // precedence: getXxx() > isXxx() > hasXxx() - if (!isset($cache[$class])) { - $methods = get_class_methods($object); - sort($methods); - $lcMethods = array_map(function ($value) { return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); }, $methods); - $classCache = []; - foreach ($methods as $i => $method) { - $classCache[$method] = $method; - $classCache[$lcName = $lcMethods[$i]] = $method; + $class = $object::class; - if ('g' === $lcName[0] && 0 === strpos($lcName, 'get')) { - $name = substr($method, 3); - $lcName = substr($lcName, 3); - } elseif ('i' === $lcName[0] && 0 === strpos($lcName, 'is')) { - $name = substr($method, 2); - $lcName = substr($lcName, 2); - } elseif ('h' === $lcName[0] && 0 === strpos($lcName, 'has')) { - $name = substr($method, 3); - $lcName = substr($lcName, 3); - if (\in_array('is'.$lcName, $lcMethods)) { + // object method + // precedence: getXxx() > isXxx() > hasXxx() + if (!isset($cache[$class])) { + $methods = get_class_methods($object); + if ($object instanceof \Closure) { + $methods[] = '__invoke'; + } + sort($methods); + $lcMethods = array_map('strtolower', $methods); + $classCache = []; + foreach ($methods as $i => $method) { + $classCache[$method] = $method; + $classCache[$lcName = $lcMethods[$i]] = $method; + + if ('g' === $lcName[0] && str_starts_with($lcName, 'get')) { + $name = substr($method, 3); + $lcName = substr($lcName, 3); + } elseif ('i' === $lcName[0] && str_starts_with($lcName, 'is')) { + $name = substr($method, 2); + $lcName = substr($lcName, 2); + } elseif ('h' === $lcName[0] && str_starts_with($lcName, 'has')) { + $name = substr($method, 3); + $lcName = substr($lcName, 3); + if (\in_array('is'.$lcName, $lcMethods, true)) { + continue; + } + } else { continue; } - } else { - continue; + + // skip get() and is() methods (in which case, $name is empty) + if ($name) { + if (!isset($classCache[$name])) { + $classCache[$name] = $method; + } + + if (!isset($classCache[$lcName])) { + $classCache[$lcName] = $method; + } + } + } + $cache[$class] = $classCache; + } + + $call = false; + if (isset($cache[$class][$item])) { + $method = $cache[$class][$item]; + } elseif (isset($cache[$class][$lcItem = strtolower($item)])) { + $method = $cache[$class][$lcItem]; + } elseif (isset($cache[$class]['__call'])) { + $method = $item; + $call = true; + } else { + if ($isDefinedTest) { + return false; } - // skip get() and is() methods (in which case, $name is empty) - if ($name) { - if (!isset($classCache[$name])) { - $classCache[$name] = $method; + if ($propertyNotAllowedError) { + throw $propertyNotAllowedError; + } + + if ($ignoreStrictCheck || !$env->isStrictVariables()) { + return; + } + + throw new RuntimeError(\sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()"/"has%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), $lineno, $source); + } + + if ($sandboxed) { + try { + $env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source); + } catch (SecurityNotAllowedMethodError $e) { + if ($isDefinedTest) { + return false; } - if (!isset($classCache[$lcName])) { - $classCache[$lcName] = $method; + if ($propertyNotAllowedError) { + throw $propertyNotAllowedError; } + + throw $e; } } - $cache[$class] = $classCache; - } - $call = false; - if (isset($cache[$class][$item])) { - $method = $cache[$class][$item]; - } elseif (isset($cache[$class][$lcItem = strtr($item, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz')])) { - $method = $cache[$class][$lcItem]; - } elseif (isset($cache[$class]['__call'])) { - $method = $item; - $call = true; - } else { if ($isDefinedTest) { - return false; + return true; } - if ($ignoreStrictCheck || !$env->isStrictVariables()) { - return; + // Some objects throw exceptions when they have __call, and the method we try + // to call is not supported. If ignoreStrictCheck is true, we should return null. + try { + $ret = $object->$method(...$arguments); + } catch (\BadMethodCallException $e) { + if ($call && ($ignoreStrictCheck || !$env->isStrictVariables())) { + return; + } + throw $e; } - throw new RuntimeError(sprintf('Neither the property "%1$s" nor one of the methods "%1$s()", "get%1$s()"/"is%1$s()"/"has%1$s()" or "__call()" exist and have public access in class "%2$s".', $item, $class), $lineno, $source); + return $ret; } - if ($isDefinedTest) { + /** + * Returns the values from a single column in the input array. + * + *
    +     *  {% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %}
    +     *
    +     *  {% set fruits = items|column('fruit') %}
    +     *
    +     *  {# fruits now contains ['apple', 'orange'] #}
    +     * 
    + * + * @param array|\Traversable $array An array + * @param int|string $name The column name + * @param int|string|null $index The column to use as the index/keys for the returned array + * + * @return array The array of values + * + * @internal + */ + public static function column($array, $name, $index = null): array + { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "column" filter expects a sequence or a mapping, got "%s".', get_debug_type($array))); + } + + if ($array instanceof \Traversable) { + $array = iterator_to_array($array); + } + + return array_column($array, $name, $index); + } + + /** + * @param \Closure $arrow + * + * @internal + */ + public static function filter(Environment $env, $array, $arrow) + { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "filter" filter expects a sequence/mapping or "Traversable", got "%s".', get_debug_type($array))); + } + + self::checkArrow($env, $arrow, 'filter', 'filter'); + + if (\is_array($array)) { + return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH); + } + + // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator + return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow); + } + + /** + * @param \Closure $arrow + * + * @internal + */ + public static function find(Environment $env, $array, $arrow) + { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "find" filter expects a sequence or a mapping, got "%s".', get_debug_type($array))); + } + + self::checkArrow($env, $arrow, 'find', 'filter'); + + foreach ($array as $k => $v) { + if ($arrow($v, $k)) { + return $v; + } + } + + return null; + } + + /** + * @param \Closure $arrow + * + * @internal + */ + public static function map(Environment $env, $array, $arrow) + { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "map" filter expects a sequence or a mapping, got "%s".', get_debug_type($array))); + } + + self::checkArrow($env, $arrow, 'map', 'filter'); + + $r = []; + foreach ($array as $k => $v) { + $r[$k] = $arrow($v, $k); + } + + return $r; + } + + /** + * @param \Closure $arrow + * + * @internal + */ + public static function reduce(Environment $env, $array, $arrow, $initial = null) + { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "reduce" filter expects a sequence or a mapping, got "%s".', get_debug_type($array))); + } + + self::checkArrow($env, $arrow, 'reduce', 'filter'); + + $accumulator = $initial; + foreach ($array as $key => $value) { + $accumulator = $arrow($accumulator, $value, $key); + } + + return $accumulator; + } + + /** + * @param \Closure $arrow + * + * @internal + */ + public static function arraySome(Environment $env, $array, $arrow) + { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "has some" test expects a sequence or a mapping, got "%s".', get_debug_type($array))); + } + + self::checkArrow($env, $arrow, 'has some', 'operator'); + + foreach ($array as $k => $v) { + if ($arrow($v, $k)) { + return true; + } + } + + return false; + } + + /** + * @param \Closure $arrow + * + * @internal + */ + public static function arrayEvery(Environment $env, $array, $arrow) + { + if (!is_iterable($array)) { + throw new RuntimeError(\sprintf('The "has every" test expects a sequence or a mapping, got "%s".', get_debug_type($array))); + } + + self::checkArrow($env, $arrow, 'has every', 'operator'); + + foreach ($array as $k => $v) { + if (!$arrow($v, $k)) { + return false; + } + } + return true; } - if ($sandboxed) { - $env->getExtension(SandboxExtension::class)->checkMethodAllowed($object, $method, $lineno, $source); - } - - // Some objects throw exceptions when they have __call, and the method we try - // to call is not supported. If ignoreStrictCheck is true, we should return null. - try { - $ret = $object->$method(...$arguments); - } catch (\BadMethodCallException $e) { - if ($call && ($ignoreStrictCheck || !$env->isStrictVariables())) { + /** + * @internal + */ + public static function checkArrow(Environment $env, $arrow, $thing, $type) + { + if ($arrow instanceof \Closure) { return; } - throw $e; + + if ($env->hasExtension(SandboxExtension::class) && $env->getExtension(SandboxExtension::class)->isSandboxed()) { + throw new RuntimeError(\sprintf('The callable passed to the "%s" %s must be a Closure in sandbox mode.', $thing, $type)); + } + + trigger_deprecation('twig/twig', '3.15', 'Passing a callable that is not a PHP \Closure as an argument to the "%s" %s is deprecated.', $thing, $type); } - return $ret; -} + /** + * @internal to be removed in Twig 4 + */ + public static function captureOutput(iterable $body): string + { + $level = ob_get_level(); + ob_start(); -/** - * Returns the values from a single column in the input array. - * - *
    - *  {% set items = [{ 'fruit' : 'apple'}, {'fruit' : 'orange' }] %}
    - *
    - *  {% set fruits = items|column('fruit') %}
    - *
    - *  {# fruits now contains ['apple', 'orange'] #}
    - * 
    - * - * @param array|Traversable $array An array - * @param mixed $name The column name - * @param mixed $index The column to use as the index/keys for the returned array - * - * @return array The array of values - */ -function twig_array_column($array, $name, $index = null): array -{ - if ($array instanceof Traversable) { - $array = iterator_to_array($array); - } elseif (!\is_array($array)) { - throw new RuntimeError(sprintf('The column filter only works with arrays or "Traversable", got "%s" as first argument.', \gettype($array))); + try { + foreach ($body as $data) { + echo $data; + } + } catch (\Throwable $e) { + while (ob_get_level() > $level) { + ob_end_clean(); + } + + throw $e; + } + + return ob_get_clean(); } - return array_column($array, $name, $index); -} + /** + * @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()); + } -function twig_array_filter($array, $arrow) -{ - if (\is_array($array)) { - return array_filter($array, $arrow, \ARRAY_FILTER_USE_BOTH); + 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); } - // the IteratorIterator wrapping is needed as some internal PHP classes are \Traversable but do not implement \Iterator - return new \CallbackFilterIterator(new \IteratorIterator($array), $arrow); -} + /** + * @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); -function twig_array_map($array, $arrow) -{ - $r = []; - foreach ($array as $k => $v) { - $r[$k] = $arrow($v, $k); + return new BlockReferenceExpression($args[0], $args[1] ?? null, $line); } - return $r; -} + /** + * @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); -function twig_array_reduce($array, $arrow, $initial = null) -{ - if (!\is_array($array)) { - $array = iterator_to_array($array); + /* + Deprecation to uncomment sometimes during the lifetime of the 4.x branch + $src = $parser->getStream()->getSourceContext(); + $dep = new DeprecatedCallableInfo('twig/twig', '3.15', 'The "attribute" function is deprecated, use the "." notation instead.'); + $dep->setName('attribute'); + $dep->setType('function'); + $dep->triggerDeprecation($src->getPath() ?: $src->getName(), $line); + */ + + return new GetAttrExpression($args[0], $args[1], $args[2] ?? null, Template::ANY_CALL, $line); } - return array_reduce($array, $arrow, $initial); -} + private static function getPropertyChecker(string $class, string $property): \Closure + { + static $classReflectors = []; + + $class = $classReflectors[$class] ??= new \ReflectionClass($class); + + if (!$class->hasProperty($property)) { + static $propertyExists; + + return $propertyExists ??= \Closure::fromCallable('property_exists'); + } + + $property = $class->getProperty($property); + + if (!$property->isPublic() || $property->isStatic()) { + static $false; + + return $false ??= static fn () => false; + } + + return static fn ($object) => $property->isInitialized($object); + } } diff --git a/vendor/twig/twig/src/Extension/DebugExtension.php b/vendor/twig/twig/src/Extension/DebugExtension.php index 2e8510d..dac21c3 100644 --- a/vendor/twig/twig/src/Extension/DebugExtension.php +++ b/vendor/twig/twig/src/Extension/DebugExtension.php @@ -9,58 +9,54 @@ * file that was distributed with this source code. */ -namespace Twig\Extension { +namespace Twig\Extension; + +use Twig\Environment; +use Twig\Template; +use Twig\TemplateWrapper; use Twig\TwigFunction; final class DebugExtension extends AbstractExtension { - public function getFunctions() + public function getFunctions(): array { // dump is safe if var_dump is overridden by xdebug $isDumpOutputHtmlSafe = \extension_loaded('xdebug') - // false means that it was not set (and the default is on) or it explicitly enabled - && (false === ini_get('xdebug.overload_var_dump') || ini_get('xdebug.overload_var_dump')) - // false means that it was not set (and the default is on) or it explicitly enabled - // xdebug.overload_var_dump produces HTML only when html_errors is also enabled - && (false === ini_get('html_errors') || ini_get('html_errors')) + // Xdebug overloads var_dump in develop mode when html_errors is enabled + && str_contains(\ini_get('xdebug.mode'), 'develop') + && (false === \ini_get('html_errors') || \ini_get('html_errors')) || 'cli' === \PHP_SAPI ; return [ - new TwigFunction('dump', 'twig_var_dump', ['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [], 'needs_context' => true, 'needs_environment' => true, 'is_variadic' => true]), + new TwigFunction('dump', [self::class, 'dump'], ['is_safe' => $isDumpOutputHtmlSafe ? ['html'] : [], 'needs_context' => true, 'needs_environment' => true, 'is_variadic' => true]), ]; } -} -class_alias('Twig\Extension\DebugExtension', 'Twig_Extension_Debug'); -} - -namespace { -use Twig\Environment; -use Twig\Template; -use Twig\TemplateWrapper; - -function twig_var_dump(Environment $env, $context, ...$vars) -{ - if (!$env->isDebug()) { - return; - } - - ob_start(); - - if (!$vars) { - $vars = []; - foreach ($context as $key => $value) { - if (!$value instanceof Template && !$value instanceof TemplateWrapper) { - $vars[$key] = $value; - } + /** + * @internal + */ + public static function dump(Environment $env, $context, ...$vars) + { + if (!$env->isDebug()) { + return; } - var_dump($vars); - } else { - var_dump(...$vars); - } + ob_start(); - return ob_get_clean(); -} + if (!$vars) { + $vars = []; + foreach ($context as $key => $value) { + if (!$value instanceof Template && !$value instanceof TemplateWrapper) { + $vars[$key] = $value; + } + } + + var_dump($vars); + } else { + var_dump(...$vars); + } + + return ob_get_clean(); + } } diff --git a/vendor/twig/twig/src/Extension/EscaperExtension.php b/vendor/twig/twig/src/Extension/EscaperExtension.php index 136f75f..c5625fa 100644 --- a/vendor/twig/twig/src/Extension/EscaperExtension.php +++ b/vendor/twig/twig/src/Extension/EscaperExtension.php @@ -9,22 +9,24 @@ * file that was distributed with this source code. */ -namespace Twig\Extension { +namespace Twig\Extension; + +use Twig\Environment; use Twig\FileExtensionEscapingStrategy; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Expression\Filter\RawFilter; +use Twig\Node\Node; use Twig\NodeVisitor\EscaperNodeVisitor; +use Twig\Runtime\EscaperRuntime; use Twig\TokenParser\AutoEscapeTokenParser; use Twig\TwigFilter; final class EscaperExtension extends AbstractExtension { - private $defaultStrategy; + private $environment; private $escapers = []; - - /** @internal */ - public $safeClasses = []; - - /** @internal */ - public $safeLookup = []; + private $escaper; + private $defaultStrategy; /** * @param string|false|callable $defaultStrategy An escaping strategy @@ -36,34 +38,68 @@ final class EscaperExtension extends AbstractExtension $this->setDefaultStrategy($defaultStrategy); } - public function getTokenParsers() + public function getTokenParsers(): array { return [new AutoEscapeTokenParser()]; } - public function getNodeVisitors() + public function getNodeVisitors(): array { return [new EscaperNodeVisitor()]; } - public function getFilters() + public function getFilters(): array { return [ - new TwigFilter('escape', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), - new TwigFilter('e', 'twig_escape_filter', ['needs_environment' => true, 'is_safe_callback' => 'twig_escape_filter_is_safe']), - new TwigFilter('raw', 'twig_raw_filter', ['is_safe' => ['all']]), + new TwigFilter('escape', [EscaperRuntime::class, 'escape'], ['is_safe_callback' => [self::class, 'escapeFilterIsSafe']]), + new TwigFilter('e', [EscaperRuntime::class, 'escape'], ['is_safe_callback' => [self::class, 'escapeFilterIsSafe']]), + new TwigFilter('raw', null, ['is_safe' => ['all'], 'node_class' => RawFilter::class]), ]; } + public function getLastModified(): int + { + return max( + parent::getLastModified(), + filemtime((new \ReflectionClass(EscaperRuntime::class))->getFileName()), + ); + } + + /** + * @deprecated since Twig 3.10 + */ + public function setEnvironment(Environment $environment): void + { + $triggerDeprecation = \func_num_args() > 1 ? func_get_arg(1) : true; + if ($triggerDeprecation) { + trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated and not needed if you are using methods from "Twig\Runtime\EscaperRuntime".', __METHOD__); + } + + $this->environment = $environment; + $this->escaper = $environment->getRuntime(EscaperRuntime::class); + } + + /** + * @return void + * + * @deprecated since Twig 3.10 + */ + public function setEscaperRuntime(EscaperRuntime $escaper) + { + trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated and not needed if you are using methods from "Twig\Runtime\EscaperRuntime".', __METHOD__); + + $this->escaper = $escaper; + } + /** * Sets the default strategy to use when not defined by the user. * * The strategy can be a valid PHP callback that takes the template * name as an argument and returns the strategy to use. * - * @param string|false|callable $defaultStrategy An escaping strategy + * @param string|false|callable(string $templateName): string $defaultStrategy An escaping strategy */ - public function setDefaultStrategy($defaultStrategy) + public function setDefaultStrategy($defaultStrategy): void { if ('name' === $defaultStrategy) { $defaultStrategy = [FileExtensionEscapingStrategy::class, 'guess']; @@ -79,7 +115,7 @@ final class EscaperExtension extends AbstractExtension * * @return string|false The default strategy to use for the template */ - public function getDefaultStrategy($name) + public function getDefaultStrategy(string $name) { // disable string callables to avoid calling a function named html or js, // or any other upcoming escaping strategy @@ -93,335 +129,90 @@ final class EscaperExtension extends AbstractExtension /** * Defines a new escaper to be used via the escape filter. * - * @param string $strategy The strategy name that should be used as a strategy in the escape call - * @param callable $callable A valid PHP callable + * @param string $strategy The strategy name that should be used as a strategy in the escape call + * @param callable(Environment, string, string): string $callable A valid PHP callable + * + * @return void + * + * @deprecated since Twig 3.10 */ public function setEscaper($strategy, callable $callable) { + trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::setEscaper()" method instead (be warned that Environment is not passed anymore to the callable).', __METHOD__); + + if (!isset($this->environment)) { + throw new \LogicException(\sprintf('You must call "setEnvironment()" before calling "%s()".', __METHOD__)); + } + $this->escapers[$strategy] = $callable; + $callable = function ($string, $charset) use ($callable) { + return $callable($this->environment, $string, $charset); + }; + + $this->escaper->setEscaper($strategy, $callable); } /** * Gets all defined escapers. * - * @return callable[] An array of escapers + * @return array An array of escapers + * + * @deprecated since Twig 3.10 */ public function getEscapers() { + trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::getEscaper()" method instead.', __METHOD__); + return $this->escapers; } + /** + * @return void + * + * @deprecated since Twig 3.10 + */ public function setSafeClasses(array $safeClasses = []) { - $this->safeClasses = []; - $this->safeLookup = []; - foreach ($safeClasses as $class => $strategies) { - $this->addSafeClass($class, $strategies); + trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::setSafeClasses()" method instead.', __METHOD__); + + if (!isset($this->escaper)) { + throw new \LogicException(\sprintf('You must call "setEnvironment()" before calling "%s()".', __METHOD__)); } + + $this->escaper->setSafeClasses($safeClasses); } + /** + * @return void + * + * @deprecated since Twig 3.10 + */ public function addSafeClass(string $class, array $strategies) { - $class = ltrim($class, '\\'); - if (!isset($this->safeClasses[$class])) { - $this->safeClasses[$class] = []; - } - $this->safeClasses[$class] = array_merge($this->safeClasses[$class], $strategies); + trigger_deprecation('twig/twig', '3.10', 'The "%s()" method is deprecated, use the "Twig\Runtime\EscaperRuntime::addSafeClass()" method instead.', __METHOD__); - foreach ($strategies as $strategy) { - $this->safeLookup[$strategy][$class] = true; - } - } -} - -class_alias('Twig\Extension\EscaperExtension', 'Twig_Extension_Escaper'); -} - -namespace { -use Twig\Environment; -use Twig\Error\RuntimeError; -use Twig\Extension\CoreExtension; -use Twig\Extension\EscaperExtension; -use Twig\Markup; -use Twig\Node\Expression\ConstantExpression; -use Twig\Node\Node; - -/** - * Marks a variable as being safe. - * - * @param string $string A PHP variable - * - * @return string - */ -function twig_raw_filter($string) -{ - return $string; -} - -/** - * Escapes a string. - * - * @param mixed $string The value to be escaped - * @param string $strategy The escaping strategy - * @param string $charset The charset - * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) - * - * @return string - */ -function twig_escape_filter(Environment $env, $string, $strategy = 'html', $charset = null, $autoescape = false) -{ - if ($autoescape && $string instanceof Markup) { - return $string; - } - - if (!\is_string($string)) { - if (\is_object($string) && method_exists($string, '__toString')) { - if ($autoescape) { - $c = \get_class($string); - $ext = $env->getExtension(EscaperExtension::class); - if (!isset($ext->safeClasses[$c])) { - $ext->safeClasses[$c] = []; - foreach (class_parents($string) + class_implements($string) as $class) { - if (isset($ext->safeClasses[$class])) { - $ext->safeClasses[$c] = array_unique(array_merge($ext->safeClasses[$c], $ext->safeClasses[$class])); - foreach ($ext->safeClasses[$class] as $s) { - $ext->safeLookup[$s][$c] = true; - } - } - } - } - if (isset($ext->safeLookup[$strategy][$c]) || isset($ext->safeLookup['all'][$c])) { - return (string) $string; - } - } - - $string = (string) $string; - } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'])) { - return $string; - } - } - - if ('' === $string) { - return ''; - } - - if (null === $charset) { - $charset = $env->getCharset(); - } - - switch ($strategy) { - case 'html': - // see https://secure.php.net/htmlspecialchars - - // Using a static variable to avoid initializing the array - // each time the function is called. Moving the declaration on the - // top of the function slow downs other escaping strategies. - static $htmlspecialcharsCharsets = [ - 'ISO-8859-1' => true, 'ISO8859-1' => true, - 'ISO-8859-15' => true, 'ISO8859-15' => true, - 'utf-8' => true, 'UTF-8' => true, - 'CP866' => true, 'IBM866' => true, '866' => true, - 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, - '1251' => true, - 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, - 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, - 'BIG5' => true, '950' => true, - 'GB2312' => true, '936' => true, - 'BIG5-HKSCS' => true, - 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, - 'EUC-JP' => true, 'EUCJP' => true, - 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, - ]; - - if (isset($htmlspecialcharsCharsets[$charset])) { - return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); - } - - if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { - // cache the lowercase variant for future iterations - $htmlspecialcharsCharsets[$charset] = true; - - return htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, $charset); - } - - $string = twig_convert_encoding($string, 'UTF-8', $charset); - $string = htmlspecialchars($string, ENT_QUOTES | ENT_SUBSTITUTE, 'UTF-8'); - - return iconv('UTF-8', $charset, $string); - - case 'js': - // escape all non-alphanumeric characters - // into their \x or \uHHHH representations - if ('UTF-8' !== $charset) { - $string = twig_convert_encoding($string, 'UTF-8', $charset); - } - - if (!preg_match('//u', $string)) { - throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); - } - - $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) { - $char = $matches[0]; - - /* - * A few characters have short escape sequences in JSON and JavaScript. - * Escape sequences supported only by JavaScript, not JSON, are ommitted. - * \" is also supported but omitted, because the resulting string is not HTML safe. - */ - static $shortMap = [ - '\\' => '\\\\', - '/' => '\\/', - "\x08" => '\b', - "\x0C" => '\f', - "\x0A" => '\n', - "\x0D" => '\r', - "\x09" => '\t', - ]; - - if (isset($shortMap[$char])) { - return $shortMap[$char]; - } - - // \uHHHH - $char = twig_convert_encoding($char, 'UTF-16BE', 'UTF-8'); - $char = strtoupper(bin2hex($char)); - - if (4 >= \strlen($char)) { - return sprintf('\u%04s', $char); - } - - return sprintf('\u%04s\u%04s', substr($char, 0, -4), substr($char, -4)); - }, $string); - - if ('UTF-8' !== $charset) { - $string = iconv('UTF-8', $charset, $string); - } - - return $string; - - case 'css': - if ('UTF-8' !== $charset) { - $string = twig_convert_encoding($string, 'UTF-8', $charset); - } - - if (!preg_match('//u', $string)) { - throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); - } - - $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) { - $char = $matches[0]; - - return sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8')); - }, $string); - - if ('UTF-8' !== $charset) { - $string = iconv('UTF-8', $charset, $string); - } - - return $string; - - case 'html_attr': - if ('UTF-8' !== $charset) { - $string = twig_convert_encoding($string, 'UTF-8', $charset); - } - - if (!preg_match('//u', $string)) { - throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); - } - - $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) { - /** - * This function is adapted from code coming from Zend Framework. - * - * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com) - * @license https://framework.zend.com/license/new-bsd New BSD License - */ - $chr = $matches[0]; - $ord = \ord($chr); - - /* - * The following replaces characters undefined in HTML with the - * hex entity for the Unicode replacement character. - */ - if (($ord <= 0x1f && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7f && $ord <= 0x9f)) { - return '�'; - } - - /* - * Check if the current character to escape has a name entity we should - * replace it with while grabbing the hex value of the character. - */ - if (1 === \strlen($chr)) { - /* - * While HTML supports far more named entities, the lowest common denominator - * has become HTML5's XML Serialisation which is restricted to the those named - * entities that XML supports. Using HTML entities would result in this error: - * XML Parsing Error: undefined entity - */ - static $entityMap = [ - 34 => '"', /* quotation mark */ - 38 => '&', /* ampersand */ - 60 => '<', /* less-than sign */ - 62 => '>', /* greater-than sign */ - ]; - - if (isset($entityMap[$ord])) { - return $entityMap[$ord]; - } - - return sprintf('&#x%02X;', $ord); - } - - /* - * Per OWASP recommendations, we'll use hex entities for any other - * characters where a named entity does not exist. - */ - return sprintf('&#x%04X;', mb_ord($chr, 'UTF-8')); - }, $string); - - if ('UTF-8' !== $charset) { - $string = iconv('UTF-8', $charset, $string); - } - - return $string; - - case 'url': - return rawurlencode($string); - - default: - static $escapers; - - if (null === $escapers) { - // merge the ones set on CoreExtension for BC (to be removed in 3.0) - $escapers = array_merge( - $env->getExtension(CoreExtension::class)->getEscapers(false), - $env->getExtension(EscaperExtension::class)->getEscapers() - ); - } - - if (isset($escapers[$strategy])) { - return $escapers[$strategy]($env, $string, $charset); - } - - $validStrategies = implode(', ', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($escapers))); - - throw new RuntimeError(sprintf('Invalid escaping strategy "%s" (valid ones: %s).', $strategy, $validStrategies)); - } -} - -/** - * @internal - */ -function twig_escape_filter_is_safe(Node $filterArgs) -{ - foreach ($filterArgs as $arg) { - if ($arg instanceof ConstantExpression) { - return [$arg->getAttribute('value')]; + if (!isset($this->escaper)) { + throw new \LogicException(\sprintf('You must call "setEnvironment()" before calling "%s()".', __METHOD__)); } - return []; + $this->escaper->addSafeClass($class, $strategies); } - return ['html']; -} + /** + * @internal + * + * @return array + */ + public static function escapeFilterIsSafe(Node $filterArgs) + { + foreach ($filterArgs as $arg) { + if ($arg instanceof ConstantExpression) { + return [$arg->getAttribute('value')]; + } + + return []; + } + + return ['html']; + } } diff --git a/vendor/twig/twig/src/Extension/ExtensionInterface.php b/vendor/twig/twig/src/Extension/ExtensionInterface.php index a083211..44356f6 100644 --- a/vendor/twig/twig/src/Extension/ExtensionInterface.php +++ b/vendor/twig/twig/src/Extension/ExtensionInterface.php @@ -11,6 +11,8 @@ namespace Twig\Extension; +use Twig\ExpressionParser\ExpressionParserInterface; +use Twig\ExpressionParser\PrecedenceChange; use Twig\NodeVisitor\NodeVisitorInterface; use Twig\TokenParser\TokenParserInterface; use Twig\TwigFilter; @@ -21,6 +23,8 @@ use Twig\TwigTest; * Interface implemented by extension classes. * * @author Fabien Potencier + * + * @method array getExpressionParsers() */ interface ExtensionInterface { @@ -62,12 +66,12 @@ interface ExtensionInterface /** * Returns a list of operators to add to the existing list. * - * @return array First array of unary operators, second array of binary operators + * @return array + * + * @psalm-return array{ + * array}>, + * array, associativity: ExpressionParser::OPERATOR_*}> + * } */ public function getOperators(); } - -class_alias('Twig\Extension\ExtensionInterface', 'Twig_ExtensionInterface'); - -// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name. -class_exists('Twig\Environment'); diff --git a/vendor/twig/twig/src/Extension/GlobalsInterface.php b/vendor/twig/twig/src/Extension/GlobalsInterface.php index 4421271..d52cd10 100644 --- a/vendor/twig/twig/src/Extension/GlobalsInterface.php +++ b/vendor/twig/twig/src/Extension/GlobalsInterface.php @@ -12,21 +12,14 @@ namespace Twig\Extension; /** - * Enables usage of the deprecated Twig\Extension\AbstractExtension::getGlobals() method. - * - * Explicitly implement this interface if you really need to implement the - * deprecated getGlobals() method in your extensions. + * Allows Twig extensions to add globals to the context. * * @author Fabien Potencier */ interface GlobalsInterface { /** - * Returns a list of global variables to add to the existing list. - * - * @return array An array of global variables + * @return array */ - public function getGlobals(); + public function getGlobals(): array; } - -class_alias('Twig\Extension\GlobalsInterface', 'Twig_Extension_GlobalsInterface'); diff --git a/vendor/twig/twig/src/Extension/InitRuntimeInterface.php b/vendor/twig/twig/src/Extension/InitRuntimeInterface.php deleted file mode 100644 index d64d3cd..0000000 --- a/vendor/twig/twig/src/Extension/InitRuntimeInterface.php +++ /dev/null @@ -1,36 +0,0 @@ - - * - * @deprecated since Twig 2.7, to be removed in 3.0 - */ -interface InitRuntimeInterface -{ - /** - * Initializes the runtime environment. - * - * This is where you can load some file that contains filter functions for instance. - */ - public function initRuntime(Environment $environment); -} - -class_alias('Twig\Extension\InitRuntimeInterface', 'Twig_Extension_InitRuntimeInterface'); diff --git a/vendor/twig/twig/src/Extension/LastModifiedExtensionInterface.php b/vendor/twig/twig/src/Extension/LastModifiedExtensionInterface.php new file mode 100644 index 0000000..4bab0c0 --- /dev/null +++ b/vendor/twig/twig/src/Extension/LastModifiedExtensionInterface.php @@ -0,0 +1,23 @@ +optimizers = $optimizers; + public function __construct( + private int $optimizers = -1, + ) { } - public function getNodeVisitors() + public function getNodeVisitors(): array { return [new OptimizerNodeVisitor($this->optimizers)]; } } - -class_alias('Twig\Extension\OptimizerExtension', 'Twig_Extension_Optimizer'); diff --git a/vendor/twig/twig/src/Extension/ProfilerExtension.php b/vendor/twig/twig/src/Extension/ProfilerExtension.php index ca5367c..43e4a44 100644 --- a/vendor/twig/twig/src/Extension/ProfilerExtension.php +++ b/vendor/twig/twig/src/Extension/ProfilerExtension.php @@ -23,12 +23,18 @@ class ProfilerExtension extends AbstractExtension $this->actives[] = $profile; } + /** + * @return void + */ public function enter(Profile $profile) { $this->actives[0]->addProfile($profile); array_unshift($this->actives, $profile); } + /** + * @return void + */ public function leave(Profile $profile) { $profile->leave(); @@ -39,10 +45,8 @@ class ProfilerExtension extends AbstractExtension } } - public function getNodeVisitors() + public function getNodeVisitors(): array { - return [new ProfilerNodeVisitor(\get_class($this))]; + return [new ProfilerNodeVisitor(static::class)]; } } - -class_alias('Twig\Extension\ProfilerExtension', 'Twig_Extension_Profiler'); diff --git a/vendor/twig/twig/src/Extension/SandboxExtension.php b/vendor/twig/twig/src/Extension/SandboxExtension.php index d16e4ed..5d0f644 100644 --- a/vendor/twig/twig/src/Extension/SandboxExtension.php +++ b/vendor/twig/twig/src/Extension/SandboxExtension.php @@ -15,6 +15,7 @@ use Twig\NodeVisitor\SandboxNodeVisitor; use Twig\Sandbox\SecurityNotAllowedMethodError; use Twig\Sandbox\SecurityNotAllowedPropertyError; use Twig\Sandbox\SecurityPolicyInterface; +use Twig\Sandbox\SourcePolicyInterface; use Twig\Source; use Twig\TokenParser\SandboxTokenParser; @@ -23,63 +24,74 @@ final class SandboxExtension extends AbstractExtension private $sandboxedGlobally; private $sandboxed; private $policy; + private $sourcePolicy; - public function __construct(SecurityPolicyInterface $policy, $sandboxed = false) + public function __construct(SecurityPolicyInterface $policy, $sandboxed = false, ?SourcePolicyInterface $sourcePolicy = null) { $this->policy = $policy; $this->sandboxedGlobally = $sandboxed; + $this->sourcePolicy = $sourcePolicy; } - public function getTokenParsers() + public function getTokenParsers(): array { return [new SandboxTokenParser()]; } - public function getNodeVisitors() + public function getNodeVisitors(): array { return [new SandboxNodeVisitor()]; } - public function enableSandbox() + public function enableSandbox(): void { $this->sandboxed = true; } - public function disableSandbox() + public function disableSandbox(): void { $this->sandboxed = false; } - public function isSandboxed() + public function isSandboxed(?Source $source = null): bool { - return $this->sandboxedGlobally || $this->sandboxed; + return $this->sandboxedGlobally || $this->sandboxed || $this->isSourceSandboxed($source); } - public function isSandboxedGlobally() + public function isSandboxedGlobally(): bool { return $this->sandboxedGlobally; } - public function setSecurityPolicy(SecurityPolicyInterface $policy) + private function isSourceSandboxed(?Source $source): bool + { + if (null === $source || null === $this->sourcePolicy) { + return false; + } + + return $this->sourcePolicy->enableSandbox($source); + } + + public function setSecurityPolicy(SecurityPolicyInterface $policy): void { $this->policy = $policy; } - public function getSecurityPolicy() + public function getSecurityPolicy(): SecurityPolicyInterface { return $this->policy; } - public function checkSecurity($tags, $filters, $functions) + public function checkSecurity($tags, $filters, $functions, ?Source $source = null): void { - if ($this->isSandboxed()) { + if ($this->isSandboxed($source)) { $this->policy->checkSecurity($tags, $filters, $functions); } } - public function checkMethodAllowed($obj, $method, int $lineno = -1, Source $source = null) + public function checkMethodAllowed($obj, $method, int $lineno = -1, ?Source $source = null): void { - if ($this->isSandboxed()) { + if ($this->isSandboxed($source)) { try { $this->policy->checkMethodAllowed($obj, $method); } catch (SecurityNotAllowedMethodError $e) { @@ -91,11 +103,11 @@ final class SandboxExtension extends AbstractExtension } } - public function checkPropertyAllowed($obj, $method, int $lineno = -1, Source $source = null) + public function checkPropertyAllowed($obj, $property, int $lineno = -1, ?Source $source = null): void { - if ($this->isSandboxed()) { + if ($this->isSandboxed($source)) { try { - $this->policy->checkPropertyAllowed($obj, $method); + $this->policy->checkPropertyAllowed($obj, $property); } catch (SecurityNotAllowedPropertyError $e) { $e->setSourceContext($source); $e->setTemplateLine($lineno); @@ -105,9 +117,18 @@ final class SandboxExtension extends AbstractExtension } } - public function ensureToStringAllowed($obj, int $lineno = -1, Source $source = null) + /** + * @throws SecurityNotAllowedMethodError + */ + public function ensureToStringAllowed($obj, int $lineno = -1, ?Source $source = null) { - if ($this->isSandboxed() && \is_object($obj) && method_exists($obj, '__toString')) { + if (\is_array($obj)) { + $this->ensureToStringAllowedForArray($obj, $lineno, $source); + + return $obj; + } + + if ($obj instanceof \Stringable && $this->isSandboxed($source)) { try { $this->policy->checkMethodAllowed($obj, '__toString'); } catch (SecurityNotAllowedMethodError $e) { @@ -120,6 +141,28 @@ final class SandboxExtension extends AbstractExtension return $obj; } -} -class_alias('Twig\Extension\SandboxExtension', 'Twig_Extension_Sandbox'); + private function ensureToStringAllowedForArray(array $obj, int $lineno, ?Source $source, array &$stack = []): void + { + foreach ($obj as $k => $v) { + if (!$v) { + continue; + } + + if (!\is_array($v)) { + $this->ensureToStringAllowed($v, $lineno, $source); + continue; + } + + if ($r = \ReflectionReference::fromArrayElement($obj, $k)) { + if (isset($stack[$r->getId()])) { + continue; + } + + $stack[$r->getId()] = true; + } + + $this->ensureToStringAllowedForArray($v, $lineno, $source, $stack); + } + } +} diff --git a/vendor/twig/twig/src/Extension/StagingExtension.php b/vendor/twig/twig/src/Extension/StagingExtension.php index 7c0c26c..59db2ca 100644 --- a/vendor/twig/twig/src/Extension/StagingExtension.php +++ b/vendor/twig/twig/src/Extension/StagingExtension.php @@ -32,71 +32,69 @@ final class StagingExtension extends AbstractExtension private $tokenParsers = []; private $tests = []; - public function addFunction(TwigFunction $function) + public function addFunction(TwigFunction $function): void { if (isset($this->functions[$function->getName()])) { - throw new \LogicException(sprintf('Function "%s" is already registered.', $function->getName())); + throw new \LogicException(\sprintf('Function "%s" is already registered.', $function->getName())); } $this->functions[$function->getName()] = $function; } - public function getFunctions() + public function getFunctions(): array { return $this->functions; } - public function addFilter(TwigFilter $filter) + public function addFilter(TwigFilter $filter): void { if (isset($this->filters[$filter->getName()])) { - throw new \LogicException(sprintf('Filter "%s" is already registered.', $filter->getName())); + throw new \LogicException(\sprintf('Filter "%s" is already registered.', $filter->getName())); } $this->filters[$filter->getName()] = $filter; } - public function getFilters() + public function getFilters(): array { return $this->filters; } - public function addNodeVisitor(NodeVisitorInterface $visitor) + public function addNodeVisitor(NodeVisitorInterface $visitor): void { $this->visitors[] = $visitor; } - public function getNodeVisitors() + public function getNodeVisitors(): array { return $this->visitors; } - public function addTokenParser(TokenParserInterface $parser) + public function addTokenParser(TokenParserInterface $parser): void { if (isset($this->tokenParsers[$parser->getTag()])) { - throw new \LogicException(sprintf('Tag "%s" is already registered.', $parser->getTag())); + throw new \LogicException(\sprintf('Tag "%s" is already registered.', $parser->getTag())); } $this->tokenParsers[$parser->getTag()] = $parser; } - public function getTokenParsers() + public function getTokenParsers(): array { return $this->tokenParsers; } - public function addTest(TwigTest $test) + public function addTest(TwigTest $test): void { if (isset($this->tests[$test->getName()])) { - throw new \LogicException(sprintf('Test "%s" is already registered.', $test->getName())); + throw new \LogicException(\sprintf('Test "%s" is already registered.', $test->getName())); } $this->tests[$test->getName()] = $test; } - public function getTests() + public function getTests(): array { return $this->tests; } } - -class_alias('Twig\Extension\StagingExtension', 'Twig_Extension_Staging'); diff --git a/vendor/twig/twig/src/Extension/StringLoaderExtension.php b/vendor/twig/twig/src/Extension/StringLoaderExtension.php index d671862..698d181 100644 --- a/vendor/twig/twig/src/Extension/StringLoaderExtension.php +++ b/vendor/twig/twig/src/Extension/StringLoaderExtension.php @@ -9,38 +9,32 @@ * file that was distributed with this source code. */ -namespace Twig\Extension { +namespace Twig\Extension; + +use Twig\Environment; +use Twig\TemplateWrapper; use Twig\TwigFunction; final class StringLoaderExtension extends AbstractExtension { - public function getFunctions() + public function getFunctions(): array { return [ - new TwigFunction('template_from_string', 'twig_template_from_string', ['needs_environment' => true]), + new TwigFunction('template_from_string', [self::class, 'templateFromString'], ['needs_environment' => true]), ]; } -} -class_alias('Twig\Extension\StringLoaderExtension', 'Twig_Extension_StringLoader'); -} - -namespace { -use Twig\Environment; -use Twig\TemplateWrapper; - -/** - * Loads a template from a string. - * - * {{ include(template_from_string("Hello {{ name }}")) }} - * - * @param string $template A template as a string or object implementing __toString() - * @param string $name An optional name of the template to be used in error messages - * - * @return TemplateWrapper - */ -function twig_template_from_string(Environment $env, $template, string $name = null) -{ - return $env->createTemplate((string) $template, $name); -} + /** + * Loads a template from a string. + * + * {{ include(template_from_string("Hello {{ name }}")) }} + * + * @param string|null $name An optional name of the template to be used in error messages + * + * @internal + */ + public static function templateFromString(Environment $env, string|\Stringable $template, ?string $name = null): TemplateWrapper + { + return $env->createTemplate((string) $template, $name); + } } diff --git a/vendor/twig/twig/src/Extension/YieldNotReadyExtension.php b/vendor/twig/twig/src/Extension/YieldNotReadyExtension.php new file mode 100644 index 0000000..49dfb80 --- /dev/null +++ b/vendor/twig/twig/src/Extension/YieldNotReadyExtension.php @@ -0,0 +1,30 @@ +useYield)]; + } +} diff --git a/vendor/twig/twig/src/ExtensionSet.php b/vendor/twig/twig/src/ExtensionSet.php index dc25b13..85a98cf 100644 --- a/vendor/twig/twig/src/ExtensionSet.php +++ b/vendor/twig/twig/src/ExtensionSet.php @@ -12,10 +12,18 @@ namespace Twig; use Twig\Error\RuntimeError; +use Twig\ExpressionParser\ExpressionParsers; +use Twig\ExpressionParser\Infix\BinaryOperatorExpressionParser; +use Twig\ExpressionParser\InfixAssociativity; +use Twig\ExpressionParser\InfixExpressionParserInterface; +use Twig\ExpressionParser\PrecedenceChange; +use Twig\ExpressionParser\Prefix\UnaryOperatorExpressionParser; +use Twig\Extension\AttributeExtension; use Twig\Extension\ExtensionInterface; use Twig\Extension\GlobalsInterface; -use Twig\Extension\InitRuntimeInterface; +use Twig\Extension\LastModifiedExtensionInterface; use Twig\Extension\StagingExtension; +use Twig\Node\Expression\AbstractExpression; use Twig\NodeVisitor\NodeVisitorInterface; use Twig\TokenParser\TokenParserInterface; @@ -32,14 +40,27 @@ final class ExtensionSet private $staging; private $parsers; private $visitors; + /** @var array */ private $filters; + /** @var array */ + private $dynamicFilters; + /** @var array */ private $tests; + /** @var array */ + private $dynamicTests; + /** @var array */ private $functions; - private $unaryOperators; - private $binaryOperators; + /** @var array */ + private $dynamicFunctions; + private ExpressionParsers $expressionParsers; + /** @var array|null */ private $globals; + /** @var array */ private $functionCallbacks = []; + /** @var array */ private $filterCallbacks = []; + /** @var array */ + private $parserCallbacks = []; private $lastModified = 0; public function __construct() @@ -48,46 +69,24 @@ final class ExtensionSet } /** - * Initializes the runtime environment. - * - * @deprecated since Twig 2.7 + * @return void */ - public function initRuntime(Environment $env) + public function initRuntime() { - if ($this->runtimeInitialized) { - return; - } - $this->runtimeInitialized = true; - - foreach ($this->extensions as $extension) { - if ($extension instanceof InitRuntimeInterface) { - $extension->initRuntime($env); - } - } } public function hasExtension(string $class): bool { - $class = ltrim($class, '\\'); - if (!isset($this->extensions[$class]) && class_exists($class, false)) { - // For BC/FC with namespaced aliases - $class = (new \ReflectionClass($class))->name; - } - - return isset($this->extensions[$class]); + return isset($this->extensions[ltrim($class, '\\')]); } public function getExtension(string $class): ExtensionInterface { $class = ltrim($class, '\\'); - if (!isset($this->extensions[$class]) && class_exists($class, false)) { - // For BC/FC with namespaced aliases - $class = (new \ReflectionClass($class))->name; - } if (!isset($this->extensions[$class])) { - throw new RuntimeError(sprintf('The "%s" extension is not enabled.', $class)); + throw new RuntimeError(\sprintf('The "%s" extension is not enabled.', $class)); } return $this->extensions[$class]; @@ -96,7 +95,7 @@ final class ExtensionSet /** * @param ExtensionInterface[] $extensions */ - public function setExtensions(array $extensions) + public function setExtensions(array $extensions): void { foreach ($extensions as $extension) { $this->addExtension($extension); @@ -127,37 +126,44 @@ final class ExtensionSet return $this->lastModified; } + $lastModified = 0; foreach ($this->extensions as $extension) { - $r = new \ReflectionObject($extension); - if (file_exists($r->getFileName()) && ($extensionTime = filemtime($r->getFileName())) > $this->lastModified) { - $this->lastModified = $extensionTime; + if ($extension instanceof LastModifiedExtensionInterface) { + $lastModified = max($extension->getLastModified(), $lastModified); + } else { + $r = new \ReflectionObject($extension); + if (is_file($r->getFileName())) { + $lastModified = max(filemtime($r->getFileName()), $lastModified); + } } } - return $this->lastModified; + return $this->lastModified = $lastModified; } - public function addExtension(ExtensionInterface $extension) + public function addExtension(ExtensionInterface $extension): void { - $class = \get_class($extension); + if ($extension instanceof AttributeExtension) { + $class = $extension->getClass(); + } else { + $class = $extension::class; + } if ($this->initialized) { - throw new \LogicException(sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class)); + throw new \LogicException(\sprintf('Unable to register extension "%s" as extensions have already been initialized.', $class)); } if (isset($this->extensions[$class])) { - throw new \LogicException(sprintf('Unable to register extension "%s" as it is already registered.', $class)); + throw new \LogicException(\sprintf('Unable to register extension "%s" as it is already registered.', $class)); } - // For BC/FC with namespaced aliases - $class = (new \ReflectionClass($class))->name; $this->extensions[$class] = $extension; } - public function addFunction(TwigFunction $function) + public function addFunction(TwigFunction $function): void { if ($this->initialized) { - throw new \LogicException(sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName())); + throw new \LogicException(\sprintf('Unable to add function "%s" as extensions have already been initialized.', $function->getName())); } $this->staging->addFunction($function); @@ -175,10 +181,7 @@ final class ExtensionSet return $this->functions; } - /** - * @return TwigFunction|false - */ - public function getFunction(string $name) + public function getFunction(string $name): ?TwigFunction { if (!$this->initialized) { $this->initExtensions(); @@ -188,14 +191,11 @@ final class ExtensionSet 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); } } @@ -205,18 +205,21 @@ final class ExtensionSet } } - return false; + return null; } - public function registerUndefinedFunctionCallback(callable $callable) + /** + * @param callable(string): (TwigFunction|false) $callable + */ + public function registerUndefinedFunctionCallback(callable $callable): void { $this->functionCallbacks[] = $callable; } - public function addFilter(TwigFilter $filter) + public function addFilter(TwigFilter $filter): void { if ($this->initialized) { - throw new \LogicException(sprintf('Unable to add filter "%s" as extensions have already been initialized.', $filter->getName())); + throw new \LogicException(\sprintf('Unable to add filter "%s" as extensions have already been initialized.', $filter->getName())); } $this->staging->addFilter($filter); @@ -234,10 +237,7 @@ final class ExtensionSet return $this->filters; } - /** - * @return TwigFilter|false - */ - public function getFilter(string $name) + public function getFilter(string $name): ?TwigFilter { if (!$this->initialized) { $this->initExtensions(); @@ -247,14 +247,11 @@ final class ExtensionSet 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); } } @@ -264,15 +261,18 @@ final class ExtensionSet } } - return false; + return null; } - public function registerUndefinedFilterCallback(callable $callable) + /** + * @param callable(string): (TwigFilter|false) $callable + */ + public function registerUndefinedFilterCallback(callable $callable): void { $this->filterCallbacks[] = $callable; } - public function addNodeVisitor(NodeVisitorInterface $visitor) + public function addNodeVisitor(NodeVisitorInterface $visitor): void { if ($this->initialized) { throw new \LogicException('Unable to add a node visitor as extensions have already been initialized.'); @@ -293,7 +293,7 @@ final class ExtensionSet return $this->visitors; } - public function addTokenParser(TokenParserInterface $parser) + public function addTokenParser(TokenParserInterface $parser): void { if ($this->initialized) { throw new \LogicException('Unable to add a token parser as extensions have already been initialized.'); @@ -314,6 +314,36 @@ final class ExtensionSet return $this->parsers; } + public function getTokenParser(string $name): ?TokenParserInterface + { + if (!$this->initialized) { + $this->initExtensions(); + } + + if (isset($this->parsers[$name])) { + return $this->parsers[$name]; + } + + foreach ($this->parserCallbacks as $callback) { + if (false !== $parser = $callback($name)) { + return $parser; + } + } + + return null; + } + + /** + * @param callable(string): (TokenParserInterface|false) $callable + */ + public function registerUndefinedTokenParserCallback(callable $callable): void + { + $this->parserCallbacks[] = $callable; + } + + /** + * @return array + */ public function getGlobals(): array { if (null !== $this->globals) { @@ -326,12 +356,7 @@ final class ExtensionSet continue; } - $extGlobals = $extension->getGlobals(); - if (!\is_array($extGlobals)) { - throw new \UnexpectedValueException(sprintf('"%s::getGlobals()" must return an array of globals.', \get_class($extension))); - } - - $globals = array_merge($globals, $extGlobals); + $globals = array_merge($globals, $extension->getGlobals()); } if ($this->initialized) { @@ -341,10 +366,15 @@ final class ExtensionSet return $globals; } - public function addTest(TwigTest $test) + public function resetGlobals(): void + { + $this->globals = null; + } + + public function addTest(TwigTest $test): void { if ($this->initialized) { - throw new \LogicException(sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName())); + throw new \LogicException(\sprintf('Unable to add test "%s" as extensions have already been initialized.', $test->getName())); } $this->staging->addTest($test); @@ -362,10 +392,7 @@ final class ExtensionSet return $this->tests; } - /** - * @return TwigTest|false - */ - public function getTest(string $name) + public function getTest(string $name): ?TwigTest { if (!$this->initialized) { $this->initExtensions(); @@ -375,49 +402,37 @@ final class ExtensionSet return $this->tests[$name]; } - foreach ($this->tests as $pattern => $test) { - $pattern = str_replace('\\*', '(.*?)', preg_quote($pattern, '#'), $count); + foreach ($this->dynamicTests as $pattern => $test) { + if (preg_match($pattern, $name, $matches)) { + array_shift($matches); - if ($count) { - if (preg_match('#^'.$pattern.'$#', $name, $matches)) { - array_shift($matches); - $test->setArguments($matches); - - return $test; - } + return $test->withDynamicArguments($name, $test->getName(), $matches); } } - return false; + return null; } - public function getUnaryOperators(): array + public function getExpressionParsers(): ExpressionParsers { if (!$this->initialized) { $this->initExtensions(); } - return $this->unaryOperators; + return $this->expressionParsers; } - public function getBinaryOperators(): array - { - if (!$this->initialized) { - $this->initExtensions(); - } - - return $this->binaryOperators; - } - - private function initExtensions() + private function initExtensions(): void { $this->parsers = []; $this->filters = []; $this->functions = []; $this->tests = []; + $this->dynamicFilters = []; + $this->dynamicFunctions = []; + $this->dynamicTests = []; $this->visitors = []; - $this->unaryOperators = []; - $this->binaryOperators = []; + $this->expressionParsers = new ExpressionParsers(); foreach ($this->extensions as $extension) { $this->initExtension($extension); @@ -427,21 +442,30 @@ final class ExtensionSet $this->initialized = true; } - private function initExtension(ExtensionInterface $extension) + 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 @@ -450,7 +474,7 @@ final class ExtensionSet throw new \LogicException('getTokenParsers() must return an array of \Twig\TokenParser\TokenParserInterface.'); } - $this->parsers[] = $parser; + $this->parsers[$parser->getTag()] = $parser; } // node visitors @@ -458,20 +482,66 @@ final class ExtensionSet $this->visitors[] = $visitor; } - // operators - if ($operators = $extension->getOperators()) { - if (!\is_array($operators)) { - throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array with operators, got "%s".', \get_class($extension), \is_object($operators) ? \get_class($operators) : \gettype($operators).(\is_resource($operators) ? '' : '#'.$operators))); - } + // expression parsers + if (method_exists($extension, 'getExpressionParsers')) { + $this->expressionParsers->add($extension->getExpressionParsers()); + } - if (2 !== \count($operators)) { - throw new \InvalidArgumentException(sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', \get_class($extension), \count($operators))); - } + $operators = $extension->getOperators(); + if (!\is_array($operators)) { + throw new \InvalidArgumentException(\sprintf('"%s::getOperators()" must return an array with operators, got "%s".', $extension::class, get_debug_type($operators).(\is_resource($operators) ? '' : '#'.$operators))); + } - $this->unaryOperators = array_merge($this->unaryOperators, $operators[0]); - $this->binaryOperators = array_merge($this->binaryOperators, $operators[1]); + if (2 !== \count($operators)) { + throw new \InvalidArgumentException(\sprintf('"%s::getOperators()" must return an array of 2 elements, got %d.', $extension::class, \count($operators))); + } + + $expressionParsers = []; + foreach ($operators[0] as $operator => $op) { + $expressionParsers[] = new UnaryOperatorExpressionParser($op['class'], $operator, $op['precedence'], $op['precedence_change'] ?? null, '', $op['aliases'] ?? []); + } + foreach ($operators[1] as $operator => $op) { + $op['associativity'] = match ($op['associativity']) { + 1 => InfixAssociativity::Left, + 2 => InfixAssociativity::Right, + default => throw new \InvalidArgumentException(\sprintf('Invalid associativity "%s" for operator "%s".', $op['associativity'], $operator)), + }; + + if (isset($op['callable'])) { + $expressionParsers[] = $this->convertInfixExpressionParser($op['class'], $operator, $op['precedence'], $op['associativity'], $op['precedence_change'] ?? null, $op['aliases'] ?? [], $op['callable']); + } else { + $expressionParsers[] = new BinaryOperatorExpressionParser($op['class'], $operator, $op['precedence'], $op['associativity'], $op['precedence_change'] ?? null, '', $op['aliases'] ?? []); + } + } + + if (\count($expressionParsers)) { + trigger_deprecation('twig/twig', '3.21', \sprintf('Extension "%s" uses the old signature for "getOperators()", please implement "getExpressionParsers()" instead.', $extension::class)); + + $this->expressionParsers->add($expressionParsers); } } -} -class_alias('Twig\ExtensionSet', 'Twig_ExtensionSet'); + private function convertInfixExpressionParser(string $nodeClass, string $operator, int $precedence, InfixAssociativity $associativity, ?PrecedenceChange $precedenceChange, array $aliases, callable $callable): InfixExpressionParserInterface + { + trigger_deprecation('twig/twig', '3.21', \sprintf('Using a non-ExpressionParserInterface object to define the "%s" binary operator is deprecated.', $operator)); + + return new class($nodeClass, $operator, $precedence, $associativity, $precedenceChange, $aliases, $callable) extends BinaryOperatorExpressionParser { + public function __construct( + string $nodeClass, + string $operator, + int $precedence, + InfixAssociativity $associativity = InfixAssociativity::Left, + ?PrecedenceChange $precedenceChange = null, + array $aliases = [], + private $callable = null, + ) { + parent::__construct($nodeClass, $operator, $precedence, $associativity, $precedenceChange, $aliases); + } + + public function parse(Parser $parser, AbstractExpression $expr, Token $token): AbstractExpression + { + return ($this->callable)($parser, $expr); + } + }; + } +} diff --git a/vendor/twig/twig/src/FileExtensionEscapingStrategy.php b/vendor/twig/twig/src/FileExtensionEscapingStrategy.php index bc95f33..2785ab7 100644 --- a/vendor/twig/twig/src/FileExtensionEscapingStrategy.php +++ b/vendor/twig/twig/src/FileExtensionEscapingStrategy.php @@ -31,20 +31,21 @@ class FileExtensionEscapingStrategy * * @return string|false The escaping strategy name to use or false to disable */ - public static function guess($name) + public static function guess(string $name) { - if (\in_array(substr($name, -1), ['/', '\\'])) { + if (\in_array(substr($name, -1), ['/', '\\'], true)) { return 'html'; // return html for directories } - if ('.twig' === substr($name, -5)) { + if (str_ends_with($name, '.twig')) { $name = substr($name, 0, -5); } - $extension = pathinfo($name, PATHINFO_EXTENSION); + $extension = pathinfo($name, \PATHINFO_EXTENSION); switch ($extension) { case 'js': + case 'json': return 'js'; case 'css': @@ -58,5 +59,3 @@ class FileExtensionEscapingStrategy } } } - -class_alias('Twig\FileExtensionEscapingStrategy', 'Twig_FileExtensionEscapingStrategy'); diff --git a/vendor/twig/twig/src/Lexer.php b/vendor/twig/twig/src/Lexer.php index 45c7290..027771a 100644 --- a/vendor/twig/twig/src/Lexer.php +++ b/vendor/twig/twig/src/Lexer.php @@ -15,12 +15,12 @@ namespace Twig; use Twig\Error\SyntaxError; /** - * Lexes a template string. - * * @author Fabien Potencier */ class Lexer { + private $isInitialized = false; + private $tokens; private $code; private $cursor; @@ -36,19 +36,38 @@ class Lexer private $position; private $positions; private $currentVarBlockLine; + private array $openingBrackets = ['{', '(', '[']; + private array $closingBrackets = ['}', ')', ']']; - const STATE_DATA = 0; - const STATE_BLOCK = 1; - const STATE_VAR = 2; - const STATE_STRING = 3; - const STATE_INTERPOLATION = 4; + public const STATE_DATA = 0; + public const STATE_BLOCK = 1; + public const STATE_VAR = 2; + public const STATE_STRING = 3; + public const STATE_INTERPOLATION = 4; - const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A'; - const REGEX_NUMBER = '/[0-9]+(?:\.[0-9]+)?([Ee][\+\-][0-9]+)?/A'; - const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; - const REGEX_DQ_STRING_DELIM = '/"/A'; - const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As'; - const PUNCTUATION = '()[]{}?:.,|'; + public const REGEX_NAME = '/[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*/A'; + public const REGEX_STRING = '/"([^#"\\\\]*(?:\\\\.[^#"\\\\]*)*)"|\'([^\'\\\\]*(?:\\\\.[^\'\\\\]*)*)\'/As'; + + public const REGEX_NUMBER = '/(?(DEFINE) + (?[0-9]+(_[0-9]+)*) # Integers (with underscores) 123_456 + (?\.(?&LNUM)) # Fractional part .456 + (?[eE][+-]?(?&LNUM)) # Exponent part E+10 + (?(?&LNUM)(?:(?&FRAC))?) # Decimal number 123_456.456 + )(?:(?&DNUM)(?:(?&EXPONENT))?) # 123_456.456E+10 + /Ax'; + + public const REGEX_DQ_STRING_DELIM = '/"/A'; + public const REGEX_DQ_STRING_PART = '/[^#"\\\\]*(?:(?:\\\\.|#(?!\{))[^#"\\\\]*)*/As'; + public const REGEX_INLINE_COMMENT = '/#[^\n]*/A'; + public const PUNCTUATION = '()[]{}?:.,|'; + + private const SPECIAL_CHARS = [ + 'f' => "\f", + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'v' => "\v", + ]; public function __construct(Environment $env, array $options = []) { @@ -63,6 +82,13 @@ class Lexer 'whitespace_line_chars' => ' \t\0\x0B', 'interpolation' => ['#{', '}'], ], $options); + } + + private function initialize(): void + { + if ($this->isInitialized) { + return; + } // when PHP 7.3 is the min version, we will be able to remove the '#' part in preg_quote as it's part of the default $this->regexes = [ @@ -112,7 +138,7 @@ class Lexer // #} 'lex_comment' => '{ (?:'. - preg_quote($this->options['whitespace_trim']).preg_quote($this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n? + preg_quote($this->options['whitespace_trim'].$this->options['tag_comment'][1], '#').'\s*\n?'. // -#}\s*\n? '|'. preg_quote($this->options['whitespace_line_trim'].$this->options['tag_comment'][1], '#').'['.$this->options['whitespace_line_chars'].']*'. // ~#}[ \t\0\x0B]* '|'. @@ -151,10 +177,14 @@ class Lexer 'interpolation_start' => '{'.preg_quote($this->options['interpolation'][0], '#').'\s*}A', 'interpolation_end' => '{\s*'.preg_quote($this->options['interpolation'][1], '#').'}A', ]; + + $this->isInitialized = true; } - public function tokenize(Source $source) + public function tokenize(Source $source): TokenStream { + $this->initialize(); + $this->source = $source; $this->code = str_replace(["\r\n", "\r"], "\n", $source->getCode()); $this->cursor = 0; @@ -167,7 +197,7 @@ class Lexer $this->position = -1; // find all token starts in one go - preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, PREG_OFFSET_CAPTURE); + preg_match_all($this->regexes['lex_tokens_start'], $this->code, $matches, \PREG_OFFSET_CAPTURE); $this->positions = $matches; while ($this->cursor < $this->end) { @@ -196,21 +226,21 @@ class Lexer } } - $this->pushToken(/* Token::EOF_TYPE */ -1); + $this->pushToken(Token::EOF_TYPE); - if (!empty($this->brackets)) { - list($expect, $lineno) = array_pop($this->brackets); - throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); + if ($this->brackets) { + [$expect, $lineno] = array_pop($this->brackets); + throw new SyntaxError(\sprintf('Unclosed "%s".', $expect), $lineno, $this->source); } return new TokenStream($this->tokens, $this->source); } - private function lexData() + 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; @@ -239,7 +269,7 @@ class Lexer $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]) { @@ -257,24 +287,24 @@ class Lexer $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; } } - private function lexBlock() + 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); + if (!$this->brackets && preg_match($this->regexes['lex_block'], $this->code, $match, 0, $this->cursor)) { + $this->pushToken(Token::BLOCK_END_TYPE); $this->moveCursor($match[0]); $this->popState(); } else { @@ -282,10 +312,10 @@ class Lexer } } - private function lexVar() + 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); + if (!$this->brackets && preg_match($this->regexes['lex_var'], $this->code, $match, 0, $this->cursor)) { + $this->pushToken(Token::VAR_END_TYPE); $this->moveCursor($match[0]); $this->popState(); } else { @@ -293,65 +323,45 @@ class Lexer } } - private function lexExpression() + private function lexExpression(): void { // whitespace if (preg_match('/\s+/A', $this->code, $match, 0, $this->cursor)) { $this->moveCursor($match[0]); if ($this->cursor >= $this->end) { - throw new SyntaxError(sprintf('Unclosed "%s".', self::STATE_BLOCK === $this->state ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source); + throw new SyntaxError(\sprintf('Unclosed "%s".', self::STATE_BLOCK === $this->state ? 'block' : 'variable'), $this->currentVarBlockLine, $this->source); } } - // arrow function - if ('=' === $this->code[$this->cursor] && '>' === $this->code[$this->cursor + 1]) { - $this->pushToken(Token::ARROW_TYPE, '=>'); - $this->moveCursor('=>'); - } // operators - elseif (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) { - $this->pushToken(/* Token::OPERATOR_TYPE */ 8, preg_replace('/\s+/', ' ', $match[0])); + if (preg_match($this->regexes['operator'], $this->code, $match, 0, $this->cursor)) { + $operator = preg_replace('/\s+/', ' ', $match[0]); + if (\in_array($operator, $this->openingBrackets, true)) { + $this->checkBrackets($operator); + } + $this->pushToken(Token::OPERATOR_TYPE, $operator); $this->moveCursor($match[0]); } // names elseif (preg_match(self::REGEX_NAME, $this->code, $match, 0, $this->cursor)) { - $this->pushToken(/* Token::NAME_TYPE */ 5, $match[0]); + $this->pushToken(Token::NAME_TYPE, $match[0]); $this->moveCursor($match[0]); } // numbers elseif (preg_match(self::REGEX_NUMBER, $this->code, $match, 0, $this->cursor)) { - $number = (float) $match[0]; // floats - if (ctype_digit($match[0]) && $number <= PHP_INT_MAX) { - $number = (int) $match[0]; // integers lower than the maximum - } - $this->pushToken(/* Token::NUMBER_TYPE */ 6, $number); + $this->pushToken(Token::NUMBER_TYPE, 0 + str_replace('_', '', $match[0])); $this->moveCursor($match[0]); } // punctuation - elseif (false !== strpos(self::PUNCTUATION, $this->code[$this->cursor])) { - // opening bracket - if (false !== strpos('([{', $this->code[$this->cursor])) { - $this->brackets[] = [$this->code[$this->cursor], $this->lineno]; - } - // closing bracket - elseif (false !== strpos(')]}', $this->code[$this->cursor])) { - if (empty($this->brackets)) { - throw new SyntaxError(sprintf('Unexpected "%s".', $this->code[$this->cursor]), $this->lineno, $this->source); - } - - list($expect, $lineno) = array_pop($this->brackets); - if ($this->code[$this->cursor] != strtr($expect, '([{', ')]}')) { - throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); - } - } - - $this->pushToken(/* Token::PUNCTUATION_TYPE */ 9, $this->code[$this->cursor]); + elseif (str_contains(self::PUNCTUATION, $this->code[$this->cursor])) { + $this->checkBrackets($this->code[$this->cursor]); + $this->pushToken(Token::PUNCTUATION_TYPE, $this->code[$this->cursor]); ++$this->cursor; } // strings elseif (preg_match(self::REGEX_STRING, $this->code, $match, 0, $this->cursor)) { - $this->pushToken(/* Token::STRING_TYPE */ 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 @@ -360,15 +370,76 @@ class Lexer $this->pushState(self::STATE_STRING); $this->moveCursor($match[0]); } + // inline comment + elseif (preg_match(self::REGEX_INLINE_COMMENT, $this->code, $match, 0, $this->cursor)) { + $this->moveCursor($match[0]); + } // unlexable else { - throw new SyntaxError(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source); + throw new SyntaxError(\sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source); } } - private function lexRawData() + private function stripcslashes(string $str, string $quoteType): string { - if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { + $result = ''; + $length = \strlen($str); + + $i = 0; + while ($i < $length) { + if (false === $pos = strpos($str, '\\', $i)) { + $result .= substr($str, $i); + break; + } + + $result .= substr($str, $i, $pos - $i); + $i = $pos + 1; + + if ($i >= $length) { + $result .= '\\'; + break; + } + + $nextChar = $str[$i]; + + if (isset(self::SPECIAL_CHARS[$nextChar])) { + $result .= self::SPECIAL_CHARS[$nextChar]; + } elseif ('\\' === $nextChar) { + $result .= $nextChar; + } elseif ("'" === $nextChar || '"' === $nextChar) { + if ($nextChar !== $quoteType) { + trigger_deprecation('twig/twig', '3.12', 'Character "%s" should not be escaped; the "\" character is ignored in Twig 3 but will not be in Twig 4. Please remove the extra "\" character at position %d in "%s" at line %d.', $nextChar, $i + 1, $this->source->getName(), $this->lineno); + } + $result .= $nextChar; + } elseif ('#' === $nextChar && $i + 1 < $length && '{' === $str[$i + 1]) { + $result .= '#{'; + ++$i; + } elseif ('x' === $nextChar && $i + 1 < $length && ctype_xdigit($str[$i + 1])) { + $hex = $str[++$i]; + if ($i + 1 < $length && ctype_xdigit($str[$i + 1])) { + $hex .= $str[++$i]; + } + $result .= \chr(hexdec($hex)); + } elseif (ctype_digit($nextChar) && $nextChar < '8') { + $octal = $nextChar; + while ($i + 1 < $length && ctype_digit($str[$i + 1]) && $str[$i + 1] < '8' && \strlen($octal) < 3) { + $octal .= $str[++$i]; + } + $result .= \chr(octdec($octal)); + } else { + trigger_deprecation('twig/twig', '3.12', 'Character "%s" should not be escaped; the "\" character is ignored in Twig 3 but will not be in Twig 4. Please remove the extra "\" character at position %d in "%s" at line %d.', $nextChar, $i + 1, $this->source->getName(), $this->lineno); + $result .= $nextChar; + } + + ++$i; + } + + return $result; + } + + private function lexRawData(): void + { + if (!preg_match($this->regexes['lex_raw_data'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) { throw new SyntaxError('Unexpected end of file: Unclosed "verbatim" block.', $this->lineno, $this->source); } @@ -387,48 +458,48 @@ class Lexer } } - $this->pushToken(/* Token::TEXT_TYPE */ 0, $text); + $this->pushToken(Token::TEXT_TYPE, $text); } - private function lexComment() + private function lexComment(): void { - if (!preg_match($this->regexes['lex_comment'], $this->code, $match, PREG_OFFSET_CAPTURE, $this->cursor)) { + if (!preg_match($this->regexes['lex_comment'], $this->code, $match, \PREG_OFFSET_CAPTURE, $this->cursor)) { throw new SyntaxError('Unclosed comment.', $this->lineno, $this->source); } $this->moveCursor(substr($this->code, $this->cursor, $match[0][1] - $this->cursor).$match[0][0]); } - private function lexString() + 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) && \strlen($match[0]) > 0) { - $this->pushToken(/* Token::STRING_TYPE */ 7, stripcslashes($match[0])); + } elseif (preg_match(self::REGEX_DQ_STRING_PART, $this->code, $match, 0, $this->cursor) && '' !== $match[0]) { + $this->pushToken(Token::STRING_TYPE, $this->stripcslashes($match[0], '"')); $this->moveCursor($match[0]); } elseif (preg_match(self::REGEX_DQ_STRING_DELIM, $this->code, $match, 0, $this->cursor)) { - list($expect, $lineno) = array_pop($this->brackets); + [$expect, $lineno] = array_pop($this->brackets); if ('"' != $this->code[$this->cursor]) { - throw new SyntaxError(sprintf('Unclosed "%s".', $expect), $lineno, $this->source); + throw new SyntaxError(\sprintf('Unclosed "%s".', $expect), $lineno, $this->source); } $this->popState(); ++$this->cursor; } else { // unlexable - throw new SyntaxError(sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source); + throw new SyntaxError(\sprintf('Unexpected character "%s".', $this->code[$this->cursor]), $this->lineno, $this->source); } } - private function lexInterpolation() + 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 { @@ -436,41 +507,44 @@ class Lexer } } - private function pushToken($type, $value = '') + 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; } $this->tokens[] = new Token($type, $value, $this->lineno); } - private function moveCursor($text) + private function moveCursor($text): void { $this->cursor += \strlen($text); $this->lineno += substr_count($text, "\n"); } - private function getOperatorRegex() + private function getOperatorRegex(): string { - $operators = array_merge( - ['='], - array_keys($this->env->getUnaryOperators()), - array_keys($this->env->getBinaryOperators()) - ); + $expressionParsers = ['=']; + foreach ($this->env->getExpressionParsers() as $expressionParser) { + $expressionParsers = array_merge($expressionParsers, [$expressionParser->getName()], $expressionParser->getAliases()); + } - $operators = array_combine($operators, array_map('strlen', $operators)); - arsort($operators); + $expressionParsers = array_combine($expressionParsers, array_map('strlen', $expressionParsers)); + arsort($expressionParsers); $regex = []; - foreach ($operators as $operator => $length) { + foreach ($expressionParsers as $expressionParser => $length) { // an operator that ends with a character must be followed by - // a whitespace or a parenthesis - if (ctype_alpha($operator[$length - 1])) { - $r = preg_quote($operator, '/').'(?=[\s()])'; - } else { - $r = preg_quote($operator, '/'); + // a whitespace, a parenthesis, an opening map [ or sequence { + $r = preg_quote($expressionParser, '/'); + if (ctype_alpha($expressionParser[$length - 1])) { + $r .= '(?=[\s()\[{])'; + } + + // an operator that begins with a character must not have a dot or pipe before + if (ctype_alpha($expressionParser[0])) { + $r = '(?states[] = $this->state; $this->state = $state; } - private function popState() + private function popState(): void { if (0 === \count($this->states)) { throw new \LogicException('Cannot pop state without a previous state.'); @@ -496,6 +570,22 @@ class Lexer $this->state = array_pop($this->states); } -} -class_alias('Twig\Lexer', 'Twig_Lexer'); + private function checkBrackets(string $code): void + { + // opening bracket + if (\in_array($code, $this->openingBrackets, true)) { + $this->brackets[] = [$code, $this->lineno]; + } elseif (\in_array($code, $this->closingBrackets, true)) { + // closing bracket + if (!$this->brackets) { + throw new SyntaxError(\sprintf('Unexpected "%s".', $code), $this->lineno, $this->source); + } + + [$expect, $lineno] = array_pop($this->brackets); + if ($code !== str_replace($this->openingBrackets, $this->closingBrackets, $expect)) { + throw new SyntaxError(\sprintf('Unclosed "%s".', $expect), $lineno, $this->source); + } + } + } +} diff --git a/vendor/twig/twig/src/Loader/ArrayLoader.php b/vendor/twig/twig/src/Loader/ArrayLoader.php index b03170b..2bb54b7 100644 --- a/vendor/twig/twig/src/Loader/ArrayLoader.php +++ b/vendor/twig/twig/src/Loader/ArrayLoader.php @@ -26,61 +26,50 @@ use Twig\Source; * * @author Fabien Potencier */ -final class ArrayLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface +final class ArrayLoader implements LoaderInterface { - private $templates = []; - /** * @param array $templates An array of templates (keys are the names, and values are the source code) */ - public function __construct(array $templates = []) - { - $this->templates = $templates; + public function __construct( + private array $templates = [], + ) { } - /** - * Adds or overrides a template. - * - * @param string $name The template name - * @param string $template The template source - */ - public function setTemplate($name, $template) + public function setTemplate(string $name, string $template): void { $this->templates[$name] = $template; } - public function getSourceContext($name) + public function getSourceContext(string $name): Source { - $name = (string) $name; if (!isset($this->templates[$name])) { - throw new LoaderError(sprintf('Template "%s" is not defined.', $name)); + throw new LoaderError(\sprintf('Template "%s" is not defined.', $name)); } return new Source($this->templates[$name], $name); } - public function exists($name) + public function exists(string $name): bool { return isset($this->templates[$name]); } - public function getCacheKey($name) + public function getCacheKey(string $name): string { if (!isset($this->templates[$name])) { - throw new LoaderError(sprintf('Template "%s" is not defined.', $name)); + throw new LoaderError(\sprintf('Template "%s" is not defined.', $name)); } return $name.':'.$this->templates[$name]; } - public function isFresh($name, $time) + public function isFresh(string $name, int $time): bool { if (!isset($this->templates[$name])) { - throw new LoaderError(sprintf('Template "%s" is not defined.', $name)); + throw new LoaderError(\sprintf('Template "%s" is not defined.', $name)); } return true; } } - -class_alias('Twig\Loader\ArrayLoader', 'Twig_Loader_Array'); diff --git a/vendor/twig/twig/src/Loader/ChainLoader.php b/vendor/twig/twig/src/Loader/ChainLoader.php index edb9df8..0859dcd 100644 --- a/vendor/twig/twig/src/Loader/ChainLoader.php +++ b/vendor/twig/twig/src/Loader/ChainLoader.php @@ -12,45 +12,57 @@ namespace Twig\Loader; use Twig\Error\LoaderError; +use Twig\Source; /** * Loads templates from other loaders. * * @author Fabien Potencier */ -final class ChainLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface +final class ChainLoader implements LoaderInterface { + /** + * @var array + */ private $hasSourceCache = []; - private $loaders = []; /** - * @param LoaderInterface[] $loaders + * @param iterable $loaders */ - public function __construct(array $loaders = []) - { - foreach ($loaders as $loader) { - $this->addLoader($loader); - } + public function __construct( + private iterable $loaders = [], + ) { } - public function addLoader(LoaderInterface $loader) + 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 = []; } /** * @return LoaderInterface[] */ - public function getLoaders() + public function getLoaders(): array { + if (!\is_array($this->loaders)) { + $this->loaders = iterator_to_array($this->loaders, false); + } + return $this->loaders; } - public function getSourceContext($name) + public function getSourceContext(string $name): Source { $exceptions = []; - foreach ($this->loaders as $loader) { + + foreach ($this->getLoaders() as $loader) { if (!$loader->exists($name)) { continue; } @@ -62,16 +74,16 @@ final class ChainLoader implements LoaderInterface, ExistsLoaderInterface, Sourc } } - throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); + throw new LoaderError(\sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } - public function exists($name) + public function exists(string $name): bool { if (isset($this->hasSourceCache[$name])) { return $this->hasSourceCache[$name]; } - foreach ($this->loaders as $loader) { + foreach ($this->getLoaders() as $loader) { if ($loader->exists($name)) { return $this->hasSourceCache[$name] = true; } @@ -80,10 +92,11 @@ final class ChainLoader implements LoaderInterface, ExistsLoaderInterface, Sourc return $this->hasSourceCache[$name] = false; } - public function getCacheKey($name) + public function getCacheKey(string $name): string { $exceptions = []; - foreach ($this->loaders as $loader) { + + foreach ($this->getLoaders() as $loader) { if (!$loader->exists($name)) { continue; } @@ -91,17 +104,18 @@ final class ChainLoader implements LoaderInterface, ExistsLoaderInterface, Sourc try { return $loader->getCacheKey($name); } catch (LoaderError $e) { - $exceptions[] = \get_class($loader).': '.$e->getMessage(); + $exceptions[] = $loader::class.': '.$e->getMessage(); } } - throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); + throw new LoaderError(\sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } - public function isFresh($name, $time) + public function isFresh(string $name, int $time): bool { $exceptions = []; - foreach ($this->loaders as $loader) { + + foreach ($this->getLoaders() as $loader) { if (!$loader->exists($name)) { continue; } @@ -109,12 +123,10 @@ final class ChainLoader implements LoaderInterface, ExistsLoaderInterface, Sourc try { return $loader->isFresh($name, $time); } catch (LoaderError $e) { - $exceptions[] = \get_class($loader).': '.$e->getMessage(); + $exceptions[] = $loader::class.': '.$e->getMessage(); } } - throw new LoaderError(sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); + throw new LoaderError(\sprintf('Template "%s" is not defined%s.', $name, $exceptions ? ' ('.implode(', ', $exceptions).')' : '')); } } - -class_alias('Twig\Loader\ChainLoader', 'Twig_Loader_Chain'); diff --git a/vendor/twig/twig/src/Loader/ExistsLoaderInterface.php b/vendor/twig/twig/src/Loader/ExistsLoaderInterface.php deleted file mode 100644 index aab8bd8..0000000 --- a/vendor/twig/twig/src/Loader/ExistsLoaderInterface.php +++ /dev/null @@ -1,23 +0,0 @@ - */ -class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, SourceContextLoaderInterface +class FilesystemLoader implements LoaderInterface { /** Identifier of the main namespace. */ - const MAIN_NAMESPACE = '__main__'; + public const MAIN_NAMESPACE = '__main__'; + /** + * @var array> + */ protected $paths = []; protected $cache = []; protected $errorCache = []; @@ -31,13 +34,13 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source private $rootPath; /** - * @param string|array $paths A path or an array of paths where to look for templates - * @param string|null $rootPath The root path common to all relative paths (null for getcwd()) + * @param string|string[] $paths A path or an array of paths where to look for templates + * @param string|null $rootPath The root path common to all relative paths (null for getcwd()) */ - public function __construct($paths = [], string $rootPath = null) + public function __construct($paths = [], ?string $rootPath = null) { - $this->rootPath = (null === $rootPath ? getcwd() : $rootPath).\DIRECTORY_SEPARATOR; - if (false !== $realPath = realpath($rootPath)) { + $this->rootPath = ($rootPath ?? getcwd()).\DIRECTORY_SEPARATOR; + if (null !== $rootPath && false !== ($realPath = realpath($rootPath))) { $this->rootPath = $realPath.\DIRECTORY_SEPARATOR; } @@ -49,13 +52,11 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source /** * Returns the paths to the templates. * - * @param string $namespace A path namespace - * - * @return array The array of paths where to look for templates + * @return list */ - public function getPaths($namespace = self::MAIN_NAMESPACE) + public function getPaths(string $namespace = self::MAIN_NAMESPACE): array { - return isset($this->paths[$namespace]) ? $this->paths[$namespace] : []; + return $this->paths[$namespace] ?? []; } /** @@ -63,20 +64,17 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source * * The main namespace is always defined. * - * @return array The array of defined namespaces + * @return list */ - public function getNamespaces() + public function getNamespaces(): array { return array_keys($this->paths); } /** - * Sets the paths where templates are stored. - * - * @param string|array $paths A path or an array of paths where to look for templates - * @param string $namespace A path namespace + * @param string|string[] $paths A path or an array of paths where to look for templates */ - public function setPaths($paths, $namespace = self::MAIN_NAMESPACE) + public function setPaths($paths, string $namespace = self::MAIN_NAMESPACE): void { if (!\is_array($paths)) { $paths = [$paths]; @@ -89,42 +87,32 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source } /** - * Adds a path where templates are stored. - * - * @param string $path A path where to look for templates - * @param string $namespace A path namespace - * * @throws LoaderError */ - public function addPath($path, $namespace = self::MAIN_NAMESPACE) + public function addPath(string $path, string $namespace = self::MAIN_NAMESPACE): void { // invalidate the cache $this->cache = $this->errorCache = []; $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path; if (!is_dir($checkPath)) { - throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath)); + throw new LoaderError(\sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath)); } $this->paths[$namespace][] = rtrim($path, '/\\'); } /** - * Prepends a path where templates are stored. - * - * @param string $path A path where to look for templates - * @param string $namespace A path namespace - * * @throws LoaderError */ - public function prependPath($path, $namespace = self::MAIN_NAMESPACE) + public function prependPath(string $path, string $namespace = self::MAIN_NAMESPACE): void { // invalidate the cache $this->cache = $this->errorCache = []; $checkPath = $this->isAbsolutePath($path) ? $path : $this->rootPath.$path; if (!is_dir($checkPath)) { - throw new LoaderError(sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath)); + throw new LoaderError(\sprintf('The "%s" directory does not exist ("%s").', $path, $checkPath)); } $path = rtrim($path, '/\\'); @@ -136,18 +124,18 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source } } - public function getSourceContext($name) + public function getSourceContext(string $name): Source { - if (null === ($path = $this->findTemplate($name)) || false === $path) { + if (null === $path = $this->findTemplate($name)) { return new Source('', $name, ''); } return new Source(file_get_contents($path), $name, $path); } - public function getCacheKey($name) + public function getCacheKey(string $name): string { - if (null === ($path = $this->findTemplate($name)) || false === $path) { + if (null === $path = $this->findTemplate($name)) { return ''; } $len = \strlen($this->rootPath); @@ -158,7 +146,10 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source return $path; } - public function exists($name) + /** + * @return bool + */ + public function exists(string $name) { $name = $this->normalizeName($name); @@ -166,13 +157,13 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source return true; } - return null !== ($path = $this->findTemplate($name, false)) && false !== $path; + return null !== $this->findTemplate($name, false); } - public function isFresh($name, $time) + public function isFresh(string $name, int $time): bool { // false support to be removed in 3.0 - if (null === ($path = $this->findTemplate($name)) || false === $path) { + if (null === $path = $this->findTemplate($name)) { return false; } @@ -180,16 +171,9 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source } /** - * Checks if the template can be found. - * - * In Twig 3.0, findTemplate must return a string or null (returning false won't work anymore). - * - * @param string $name The template name - * @param bool $throw Whether to throw an exception when an error occurs - * - * @return string|false|null The template name or false/null + * @return string|null */ - protected function findTemplate($name, $throw = true) + protected function findTemplate(string $name, bool $throw = true) { $name = $this->normalizeName($name); @@ -199,29 +183,29 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source if (isset($this->errorCache[$name])) { if (!$throw) { - return false; + return null; } throw new LoaderError($this->errorCache[$name]); } try { - $this->validateName($name); + [$namespace, $shortname] = $this->parseName($name); - list($namespace, $shortname) = $this->parseName($name); + $this->validateName($shortname); } catch (LoaderError $e) { if (!$throw) { - return false; + return null; } throw $e; } if (!isset($this->paths[$namespace])) { - $this->errorCache[$name] = sprintf('There are no registered paths for namespace "%s".', $namespace); + $this->errorCache[$name] = \sprintf('There are no registered paths for namespace "%s".', $namespace); if (!$throw) { - return false; + return null; } throw new LoaderError($this->errorCache[$name]); @@ -241,25 +225,25 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source } } - $this->errorCache[$name] = sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])); + $this->errorCache[$name] = \sprintf('Unable to find template "%s" (looked into: %s).', $name, implode(', ', $this->paths[$namespace])); if (!$throw) { - return false; + return null; } throw new LoaderError($this->errorCache[$name]); } - private function normalizeName($name) + private function normalizeName(string $name): string { return preg_replace('#/{2,}#', '/', str_replace('\\', '/', $name)); } - private function parseName($name, $default = self::MAIN_NAMESPACE) + private function parseName(string $name, string $default = self::MAIN_NAMESPACE): array { if (isset($name[0]) && '@' == $name[0]) { if (false === $pos = strpos($name, '/')) { - throw new LoaderError(sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); + throw new LoaderError(\sprintf('Malformed namespaced template name "%s" (expecting "@namespace/template_name").', $name)); } $namespace = substr($name, 1, $pos - 1); @@ -271,9 +255,9 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source return [$default, $name]; } - private function validateName($name) + private function validateName(string $name): void { - if (false !== strpos($name, "\0")) { + if (str_contains($name, "\0")) { throw new LoaderError('A template name cannot contain NUL bytes.'); } @@ -288,21 +272,19 @@ class FilesystemLoader implements LoaderInterface, ExistsLoaderInterface, Source } if ($level < 0) { - throw new LoaderError(sprintf('Looks like you try to load a template outside configured directories (%s).', $name)); + throw new LoaderError(\sprintf('Looks like you try to load a template outside configured directories (%s).', $name)); } } } - private function isAbsolutePath($file) + private function isAbsolutePath(string $file): bool { return strspn($file, '/\\', 0, 1) || (\strlen($file) > 3 && ctype_alpha($file[0]) && ':' === $file[1] && strspn($file, '/\\', 2, 1) ) - || null !== parse_url($file, PHP_URL_SCHEME) + || null !== parse_url($file, \PHP_URL_SCHEME) ; } } - -class_alias('Twig\Loader\FilesystemLoader', 'Twig_Loader_Filesystem'); diff --git a/vendor/twig/twig/src/Loader/LoaderInterface.php b/vendor/twig/twig/src/Loader/LoaderInterface.php index 5ccd2c7..fec7e85 100644 --- a/vendor/twig/twig/src/Loader/LoaderInterface.php +++ b/vendor/twig/twig/src/Loader/LoaderInterface.php @@ -24,46 +24,26 @@ interface LoaderInterface /** * Returns the source context for a given template logical name. * - * @param string $name The template logical name - * - * @return Source - * * @throws LoaderError When $name is not found */ - public function getSourceContext($name); + public function getSourceContext(string $name): Source; /** * 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 LoaderError When $name is not found + */ + public function getCacheKey(string $name): string; + + /** + * @param int $time Timestamp of the last modification time of the cached template * * @throws LoaderError When $name is not found */ - public function getCacheKey($name); + public function isFresh(string $name, int $time): bool; /** - * Returns true if the template is still fresh. - * - * @param string $name The template name - * @param int $time Timestamp of the last modification time of the - * cached template - * - * @return bool true if the template is fresh, false otherwise - * - * @throws LoaderError When $name is not found + * @return bool */ - 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); + public function exists(string $name); } - -class_alias('Twig\Loader\LoaderInterface', 'Twig_LoaderInterface'); diff --git a/vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php b/vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php deleted file mode 100644 index 4fdb17e..0000000 --- a/vendor/twig/twig/src/Loader/SourceContextLoaderInterface.php +++ /dev/null @@ -1,21 +0,0 @@ - */ -class Markup implements \Countable, \JsonSerializable +class Markup implements \Countable, \JsonSerializable, \Stringable { private $content; - private $charset; + private ?string $charset; public function __construct($content, $charset) { @@ -27,20 +27,31 @@ class Markup implements \Countable, \JsonSerializable $this->charset = $charset; } - public function __toString() + public function __toString(): string { return $this->content; } + public function getCharset(): string + { + return $this->charset; + } + + /** + * @return int + */ + #[\ReturnTypeWillChange] public function count() { return mb_strlen($this->content, $this->charset); } + /** + * @return mixed + */ + #[\ReturnTypeWillChange] public function jsonSerialize() { return $this->content; } } - -class_alias('Twig\Markup', 'Twig_Markup'); diff --git a/vendor/twig/twig/src/Node/AutoEscapeNode.php b/vendor/twig/twig/src/Node/AutoEscapeNode.php index 0bd5ae1..ee80639 100644 --- a/vendor/twig/twig/src/Node/AutoEscapeNode.php +++ b/vendor/twig/twig/src/Node/AutoEscapeNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -24,17 +25,16 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[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) + public function compile(Compiler $compiler): void { $compiler->subcompile($this->getNode('body')); } } - -class_alias('Twig\Node\AutoEscapeNode', 'Twig_Node_AutoEscape'); diff --git a/vendor/twig/twig/src/Node/BlockNode.php b/vendor/twig/twig/src/Node/BlockNode.php index 4da6e6f..b4f939c 100644 --- a/vendor/twig/twig/src/Node/BlockNode.php +++ b/vendor/twig/twig/src/Node/BlockNode.php @@ -12,6 +12,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -19,28 +20,31 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[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) + public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) - ->write(sprintf("public function block_%s(\$context, array \$blocks = [])\n", $this->getAttribute('name')), "{\n") + ->write("/**\n") + ->write(" * @return iterable\n") + ->write(" */\n") + ->write(\sprintf("public function block_%s(array \$context, array \$blocks = []): iterable\n", $this->getAttribute('name')), "{\n") ->indent() ->write("\$macros = \$this->macros;\n") ; $compiler ->subcompile($this->getNode('body')) + ->write("yield from [];\n") ->outdent() ->write("}\n\n") ; } } - -class_alias('Twig\Node\BlockNode', 'Twig_Node_Block'); diff --git a/vendor/twig/twig/src/Node/BlockReferenceNode.php b/vendor/twig/twig/src/Node/BlockReferenceNode.php index c46d8b3..7c313a0 100644 --- a/vendor/twig/twig/src/Node/BlockReferenceNode.php +++ b/vendor/twig/twig/src/Node/BlockReferenceNode.php @@ -12,6 +12,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -19,20 +20,19 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[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) + public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) - ->write(sprintf("\$this->displayBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name'))) + ->write(\sprintf("yield from \$this->unwrap()->yieldBlock('%s', \$context, \$blocks);\n", $this->getAttribute('name'))) ; } } - -class_alias('Twig\Node\BlockReferenceNode', 'Twig_Node_BlockReference'); diff --git a/vendor/twig/twig/src/Node/BodyNode.php b/vendor/twig/twig/src/Node/BodyNode.php index 5290be5..08115b3 100644 --- a/vendor/twig/twig/src/Node/BodyNode.php +++ b/vendor/twig/twig/src/Node/BodyNode.php @@ -11,13 +11,14 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; + /** * Represents a body node. * * @author Fabien Potencier */ +#[YieldReady] class BodyNode extends Node { } - -class_alias('Twig\Node\BodyNode', 'Twig_Node_Body'); diff --git a/vendor/twig/twig/src/Node/CaptureNode.php b/vendor/twig/twig/src/Node/CaptureNode.php new file mode 100644 index 0000000..3b7f0b6 --- /dev/null +++ b/vendor/twig/twig/src/Node/CaptureNode.php @@ -0,0 +1,57 @@ + + */ +#[YieldReady] +class CaptureNode extends Node +{ + public function __construct(Node $body, int $lineno) + { + parent::__construct(['body' => $body], ['raw' => false], $lineno); + } + + public function compile(Compiler $compiler): void + { + $useYield = $compiler->getEnvironment()->useYield(); + + if (!$this->getAttribute('raw')) { + $compiler->raw("('' === \$tmp = "); + } + $compiler + ->raw($useYield ? "implode('', iterator_to_array(" : '\\Twig\\Extension\\CoreExtension::captureOutput(') + ->raw("(function () use (&\$context, \$macros, \$blocks) {\n") + ->indent() + ->subcompile($this->getNode('body')) + ->write("yield from [];\n") + ->outdent() + ->write('})()') + ; + if ($useYield) { + $compiler->raw(', false))'); + } else { + $compiler->raw(')'); + } + if (!$this->getAttribute('raw')) { + $compiler->raw(") ? '' : new Markup(\$tmp, \$this->env->getCharset());"); + } else { + $compiler->raw(';'); + } + } +} diff --git a/vendor/twig/twig/src/Node/CheckSecurityCallNode.php b/vendor/twig/twig/src/Node/CheckSecurityCallNode.php new file mode 100644 index 0000000..bb8783b --- /dev/null +++ b/vendor/twig/twig/src/Node/CheckSecurityCallNode.php @@ -0,0 +1,33 @@ + + */ +#[YieldReady] +class CheckSecurityCallNode extends Node +{ + /** + * @return void + */ + public function compile(Compiler $compiler) + { + $compiler + ->write("\$this->sandbox = \$this->extensions[SandboxExtension::class];\n") + ->write("\$this->checkSecurity();\n") + ; + } +} diff --git a/vendor/twig/twig/src/Node/CheckSecurityNode.php b/vendor/twig/twig/src/Node/CheckSecurityNode.php index 59857ca..6e591aa 100644 --- a/vendor/twig/twig/src/Node/CheckSecurityNode.php +++ b/vendor/twig/twig/src/Node/CheckSecurityNode.php @@ -11,17 +11,24 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** * @author Fabien Potencier */ +#[YieldReady] class CheckSecurityNode extends Node { private $usedFilters; private $usedTags; private $usedFunctions; + /** + * @param array $usedFilters + * @param array $usedTags + * @param array $usedFunctions + */ public function __construct(array $usedFilters, array $usedTags, array $usedFunctions) { $this->usedFilters = $usedFilters; @@ -31,31 +38,24 @@ class CheckSecurityNode extends Node parent::__construct(); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { - $tags = $filters = $functions = []; - foreach (['tags', 'filters', 'functions'] as $type) { - foreach ($this->{'used'.ucfirst($type)} as $name => $node) { - if ($node instanceof Node) { - ${$type}[$name] = $node->getTemplateLine(); - } else { - ${$type}[$node] = null; - } - } - } - $compiler - ->write("\$this->sandbox = \$this->env->getExtension('\Twig\Extension\SandboxExtension');\n") - ->write('$tags = ')->repr(array_filter($tags))->raw(";\n") - ->write('$filters = ')->repr(array_filter($filters))->raw(";\n") - ->write('$functions = ')->repr(array_filter($functions))->raw(";\n\n") + ->write("\n") + ->write("public function checkSecurity()\n") + ->write("{\n") + ->indent() + ->write('static $tags = ')->repr(array_filter($this->usedTags))->raw(";\n") + ->write('static $filters = ')->repr(array_filter($this->usedFilters))->raw(";\n") + ->write('static $functions = ')->repr(array_filter($this->usedFunctions))->raw(";\n\n") ->write("try {\n") ->indent() ->write("\$this->sandbox->checkSecurity(\n") ->indent() - ->write(!$tags ? "[],\n" : "['".implode("', '", array_keys($tags))."'],\n") - ->write(!$filters ? "[],\n" : "['".implode("', '", array_keys($filters))."'],\n") - ->write(!$functions ? "[]\n" : "['".implode("', '", array_keys($functions))."']\n") + ->write(!$this->usedTags ? "[],\n" : "['".implode("', '", array_keys($this->usedTags))."'],\n") + ->write(!$this->usedFilters ? "[],\n" : "['".implode("', '", array_keys($this->usedFilters))."'],\n") + ->write(!$this->usedFunctions ? "[],\n" : "['".implode("', '", array_keys($this->usedFunctions))."'],\n") + ->write("\$this->source\n") ->outdent() ->write(");\n") ->outdent() @@ -78,8 +78,8 @@ class CheckSecurityNode extends Node ->write("throw \$e;\n") ->outdent() ->write("}\n\n") + ->outdent() + ->write("}\n") ; } } - -class_alias('Twig\Node\CheckSecurityNode', 'Twig_Node_CheckSecurity'); diff --git a/vendor/twig/twig/src/Node/CheckToStringNode.php b/vendor/twig/twig/src/Node/CheckToStringNode.php index 02b42fc..937240c 100644 --- a/vendor/twig/twig/src/Node/CheckToStringNode.php +++ b/vendor/twig/twig/src/Node/CheckToStringNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; @@ -24,14 +25,15 @@ use Twig\Node\Expression\AbstractExpression; * * @author Fabien Potencier */ +#[YieldReady] 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) + public function compile(Compiler $compiler): void { $expr = $this->getNode('expr'); $compiler diff --git a/vendor/twig/twig/src/Node/DeprecatedNode.php b/vendor/twig/twig/src/Node/DeprecatedNode.php index accd768..0772adf 100644 --- a/vendor/twig/twig/src/Node/DeprecatedNode.php +++ b/vendor/twig/twig/src/Node/DeprecatedNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ConstantExpression; @@ -20,36 +21,53 @@ use Twig\Node\Expression\ConstantExpression; * * @author Yonel Ceruto */ +#[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) + public function compile(Compiler $compiler): void { $compiler->addDebugInfo($this); $expr = $this->getNode('expr'); - if ($expr instanceof ConstantExpression) { - $compiler->write('@trigger_error(') - ->subcompile($expr); - } else { + if (!$expr instanceof ConstantExpression) { $varName = $compiler->getVarName(); - $compiler->write(sprintf('$%s = ', $varName)) + $compiler + ->write(\sprintf('$%s = ', $varName)) ->subcompile($expr) ->raw(";\n") - ->write(sprintf('@trigger_error($%s', $varName)); + ; + } + + $compiler->write('trigger_deprecation('); + if ($this->hasNode('package')) { + $compiler->subcompile($this->getNode('package')); + } else { + $compiler->raw("''"); + } + $compiler->raw(', '); + if ($this->hasNode('version')) { + $compiler->subcompile($this->getNode('version')); + } else { + $compiler->raw("''"); + } + $compiler->raw(', '); + + if ($expr instanceof ConstantExpression) { + $compiler->subcompile($expr); + } else { + $compiler->write(\sprintf('$%s', $varName)); } $compiler ->raw('.') - ->string(sprintf(' ("%s" at line %d).', $this->getTemplateName(), $this->getTemplateLine())) - ->raw(", E_USER_DEPRECATED);\n") + ->string(\sprintf(' in "%s" at line %d.', $this->getTemplateName(), $this->getTemplateLine())) + ->raw(");\n") ; } } - -class_alias('Twig\Node\DeprecatedNode', 'Twig_Node_Deprecated'); diff --git a/vendor/twig/twig/src/Node/DoNode.php b/vendor/twig/twig/src/Node/DoNode.php index d74804c..1593fd0 100644 --- a/vendor/twig/twig/src/Node/DoNode.php +++ b/vendor/twig/twig/src/Node/DoNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; @@ -19,14 +20,15 @@ use Twig\Node\Expression\AbstractExpression; * * @author Fabien Potencier */ +#[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) + public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) @@ -36,5 +38,3 @@ class DoNode extends Node ; } } - -class_alias('Twig\Node\DoNode', 'Twig_Node_Do'); diff --git a/vendor/twig/twig/src/Node/EmbedNode.php b/vendor/twig/twig/src/Node/EmbedNode.php index 4a1ef6f..fe4365b 100644 --- a/vendor/twig/twig/src/Node/EmbedNode.php +++ b/vendor/twig/twig/src/Node/EmbedNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ConstantExpression; @@ -20,31 +21,34 @@ use Twig\Node\Expression\ConstantExpression; * * @author Fabien Potencier */ +#[YieldReady] class EmbedNode extends IncludeNode { // we don't inject the module to avoid node visitors to traverse it twice (as it will be already visited in the main module) - public function __construct(string $name, int $index, AbstractExpression $variables = null, bool $only = false, bool $ignoreMissing = false, 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); } - protected function addGetTemplate(Compiler $compiler) + protected function addGetTemplate(Compiler $compiler, string $template = ''): void { $compiler - ->write('$this->loadTemplate(') + ->raw('$this->load(') ->string($this->getAttribute('name')) ->raw(', ') - ->repr($this->getTemplateName()) - ->raw(', ') ->repr($this->getTemplateLine()) ->raw(', ') ->string($this->getAttribute('index')) ->raw(')') ; + if ($this->getAttribute('ignore_missing')) { + $compiler + ->raw(";\n") + ->write(\sprintf("\$%s->getParent(\$context);\n", $template)) + ; + } } } - -class_alias('Twig\Node\EmbedNode', 'Twig_Node_Embed'); diff --git a/vendor/twig/twig/src/Node/EmptyNode.php b/vendor/twig/twig/src/Node/EmptyNode.php new file mode 100644 index 0000000..fd4717f --- /dev/null +++ b/vendor/twig/twig/src/Node/EmptyNode.php @@ -0,0 +1,33 @@ + + */ +#[YieldReady] +final class EmptyNode extends Node +{ + public function __construct(int $lineno = 0) + { + parent::__construct([], [], $lineno); + } + + public function setNode(string $name, Node $node): void + { + throw new \LogicException('EmptyNode cannot have children.'); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/AbstractExpression.php b/vendor/twig/twig/src/Node/Expression/AbstractExpression.php index a352892..22d8617 100644 --- a/vendor/twig/twig/src/Node/Expression/AbstractExpression.php +++ b/vendor/twig/twig/src/Node/Expression/AbstractExpression.php @@ -21,6 +21,23 @@ use Twig\Node\Node; */ abstract class AbstractExpression extends Node { -} + public function isGenerator(): bool + { + return $this->hasAttribute('is_generator') && $this->getAttribute('is_generator'); + } -class_alias('Twig\Node\Expression\AbstractExpression', 'Twig_Node_Expression'); + /** + * @return static + */ + public function setExplicitParentheses(): self + { + $this->setAttribute('with_parentheses', true); + + return $this; + } + + public function hasExplicitParentheses(): bool + { + return $this->hasAttribute('with_parentheses') && $this->getAttribute('with_parentheses'); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/ArrayExpression.php b/vendor/twig/twig/src/Node/Expression/ArrayExpression.php index 917675d..b6f8a6b 100644 --- a/vendor/twig/twig/src/Node/Expression/ArrayExpression.php +++ b/vendor/twig/twig/src/Node/Expression/ArrayExpression.php @@ -12,9 +12,14 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Node\Expression\Unary\SpreadUnary; +use Twig\Node\Expression\Unary\StringCastUnary; +use Twig\Node\Expression\Variable\ContextVariable; -class ArrayExpression extends AbstractExpression +class ArrayExpression extends AbstractExpression implements SupportDefinedTestInterface, ReturnArrayInterface { + use SupportDefinedTestTrait; + private $index; public function __construct(array $elements, int $lineno) @@ -29,10 +34,9 @@ class ArrayExpression extends AbstractExpression } } - public function getKeyValuePairs() + public function getKeyValuePairs(): array { $pairs = []; - foreach (array_chunk($this->nodes, 2) as $pair) { $pairs[] = [ 'key' => $pair[0], @@ -43,7 +47,7 @@ class ArrayExpression extends AbstractExpression return $pairs; } - public function hasElement(AbstractExpression $key) + public function hasElement(AbstractExpression $key): bool { foreach ($this->getKeyValuePairs() as $pair) { // we compare the string representation of the keys @@ -56,7 +60,7 @@ class ArrayExpression extends AbstractExpression return false; } - public function addElement(AbstractExpression $value, AbstractExpression $key = null) + public function addElement(AbstractExpression $value, ?AbstractExpression $key = null): void { if (null === $key) { $key = new ConstantExpression(++$this->index, $value->getTemplateLine()); @@ -65,24 +69,44 @@ class ArrayExpression extends AbstractExpression array_push($this->nodes, $key, $value); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { + if ($this->definedTest) { + $compiler->repr(true); + + return; + } + $compiler->raw('['); - $first = true; - foreach ($this->getKeyValuePairs() as $pair) { - if (!$first) { + $isSequence = true; + foreach ($this->getKeyValuePairs() as $i => $pair) { + if (0 !== $i) { $compiler->raw(', '); } - $first = false; - $compiler - ->subcompile($pair['key']) - ->raw(' => ') - ->subcompile($pair['value']) - ; + $key = null; + if ($pair['key'] instanceof ContextVariable) { + $pair['key'] = new StringCastUnary($pair['key'], $pair['key']->getTemplateLine()); + } elseif ($pair['key'] instanceof TempNameExpression) { + $key = $pair['key']->getAttribute('name'); + $pair['key'] = new ConstantExpression($key, $pair['key']->getTemplateLine()); + } elseif ($pair['key'] instanceof ConstantExpression) { + $key = $pair['key']->getAttribute('value'); + } + + if ($key !== $i) { + $isSequence = false; + } + + if (!$isSequence && !$pair['value'] instanceof SpreadUnary) { + $compiler + ->subcompile($pair['key']) + ->raw(' => ') + ; + } + + $compiler->subcompile($pair['value']); } $compiler->raw(']'); } } - -class_alias('Twig\Node\Expression\ArrayExpression', 'Twig_Node_Expression_Array'); diff --git a/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php b/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php index b5b720e..552b8fe 100644 --- a/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php +++ b/vendor/twig/twig/src/Node/Expression/ArrowFunctionExpression.php @@ -12,6 +12,9 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Error\SyntaxError; +use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; /** @@ -21,29 +24,25 @@ use Twig\Node\Node; */ 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); + if (!$names instanceof ListExpression && !$names instanceof ContextVariable) { + throw new SyntaxError('The arrow function argument must be a list of variables or a single variable.', $names->getTemplateLine(), $names->getSourceContext()); + } + + if ($names instanceof ContextVariable) { + $names = new ListExpression([new AssignContextVariable($names->getAttribute('name'), $names->getTemplateLine())], $lineno); + } + + parent::__construct(['expr' => $expr, 'names' => $names], [], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) ->raw('function (') - ; - foreach ($this->getNode('names') as $i => $name) { - if ($i) { - $compiler->raw(', '); - } - - $compiler - ->raw('$__') - ->raw($name->getAttribute('name')) - ->raw('__') - ; - } - $compiler + ->subcompile($this->getNode('names')) ->raw(') use ($context, $macros) { ') ; foreach ($this->getNode('names') as $name) { diff --git a/vendor/twig/twig/src/Node/Expression/AssignNameExpression.php b/vendor/twig/twig/src/Node/Expression/AssignNameExpression.php index 62c4ac0..9a7f0f9 100644 --- a/vendor/twig/twig/src/Node/Expression/AssignNameExpression.php +++ b/vendor/twig/twig/src/Node/Expression/AssignNameExpression.php @@ -13,10 +13,27 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Error\SyntaxError; +use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\Expression\Variable\ContextVariable; -class AssignNameExpression extends NameExpression +class AssignNameExpression extends ContextVariable { - public function compile(Compiler $compiler) + public function __construct(string $name, int $lineno) + { + if (self::class === static::class) { + trigger_deprecation('twig/twig', '3.15', 'The "%s" class is deprecated, use "%s" instead.', self::class, AssignContextVariable::class); + } + + // All names supported by ExpressionParser::parsePrimaryExpression() should be excluded + if (\in_array(strtolower($name), ['true', 'false', 'none', 'null'], true)) { + throw new SyntaxError(\sprintf('You cannot assign a value to "%s".', $name), $lineno); + } + + parent::__construct($name, $lineno); + } + + public function compile(Compiler $compiler): void { $compiler ->raw('$context[') @@ -25,5 +42,3 @@ class AssignNameExpression extends NameExpression ; } } - -class_alias('Twig\Node\Expression\AssignNameExpression', 'Twig_Node_Expression_AssignName'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php index 67c388a..b4bf666 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/AbstractBinary.php @@ -16,14 +16,25 @@ use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Node; -abstract class AbstractBinary extends AbstractExpression +abstract class AbstractBinary extends AbstractExpression implements BinaryInterface { + /** + * @param AbstractExpression $left + * @param AbstractExpression $right + */ public function __construct(Node $left, Node $right, int $lineno) { + if (!$left instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "left" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $left::class); + } + if (!$right instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "right" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $right::class); + } + parent::__construct(['left' => $left, 'right' => $right], [], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('(') @@ -38,7 +49,5 @@ abstract class AbstractBinary extends AbstractExpression ; } - abstract public function operator(Compiler $compiler); + abstract public function operator(Compiler $compiler): Compiler; } - -class_alias('Twig\Node\Expression\Binary\AbstractBinary', 'Twig_Node_Expression_Binary'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php index f7719a1..42377ae 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/AddBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class AddBinary extends AbstractBinary +class AddBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('+'); } } - -class_alias('Twig\Node\Expression\Binary\AddBinary', 'Twig_Node_Expression_Binary_Add'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php index 484597d..454ea70 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/AndBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class AndBinary extends AbstractBinary +class AndBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('&&'); } } - -class_alias('Twig\Node\Expression\Binary\AndBinary', 'Twig_Node_Expression_Binary_And'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/BinaryInterface.php b/vendor/twig/twig/src/Node/Expression/Binary/BinaryInterface.php new file mode 100644 index 0000000..eeeb2eb --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Binary/BinaryInterface.php @@ -0,0 +1,22 @@ +raw('&'); } } - -class_alias('Twig\Node\Expression\Binary\BitwiseAndBinary', 'Twig_Node_Expression_Binary_BitwiseAnd'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php index 7d5d260..ec17e22 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/BitwiseOrBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class BitwiseOrBinary extends AbstractBinary +class BitwiseOrBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('|'); } } - -class_alias('Twig\Node\Expression\Binary\BitwiseOrBinary', 'Twig_Node_Expression_Binary_BitwiseOr'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php index 7291987..e6432a7 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/BitwiseXorBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class BitwiseXorBinary extends AbstractBinary +class BitwiseXorBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('^'); } } - -class_alias('Twig\Node\Expression\Binary\BitwiseXorBinary', 'Twig_Node_Expression_Binary_BitwiseXor'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php index f6e5938..75ee654 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/ConcatBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnStringInterface; -class ConcatBinary extends AbstractBinary +class ConcatBinary extends AbstractBinary implements ReturnStringInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('.'); } } - -class_alias('Twig\Node\Expression\Binary\ConcatBinary', 'Twig_Node_Expression_Binary_Concat'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php index ebfcc75..11c061e 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/DivBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class DivBinary extends AbstractBinary +class DivBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('/'); } } - -class_alias('Twig\Node\Expression\Binary\DivBinary', 'Twig_Node_Expression_Binary_Div'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/ElvisBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/ElvisBinary.php new file mode 100644 index 0000000..2552224 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Binary/ElvisBinary.php @@ -0,0 +1,55 @@ +setNode('test', clone $left); + $left->setAttribute('always_defined', true); + } + + public function compile(Compiler $compiler): void + { + $compiler + ->raw('((') + ->subcompile($this->getNode('test')) + ->raw(') ? (') + ->subcompile($this->getNode('left')) + ->raw(') : (') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler + { + return $compiler->raw('?:'); + } + + public function getOperandNamesToEscape(): array + { + return ['left', 'right']; + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php index 41a0065..e689d66 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/EndsWithBinary.php @@ -12,26 +12,25 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class EndsWithBinary extends AbstractBinary +class EndsWithBinary extends AbstractBinary implements ReturnBoolInterface { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $left = $compiler->getVarName(); $right = $compiler->getVarName(); $compiler - ->raw(sprintf('(is_string($%s = ', $left)) + ->raw(\sprintf('(is_string($%s = ', $left)) ->subcompile($this->getNode('left')) - ->raw(sprintf(') && is_string($%s = ', $right)) + ->raw(\sprintf(') && is_string($%s = ', $right)) ->subcompile($this->getNode('right')) - ->raw(sprintf(') && (\'\' === $%2$s || $%2$s === substr($%1$s, -strlen($%2$s))))', $left, $right)) + ->raw(\sprintf(') && str_ends_with($%1$s, $%2$s))', $left, $right)) ; } - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw(''); } } - -class_alias('Twig\Node\Expression\Binary\EndsWithBinary', 'Twig_Node_Expression_Binary_EndsWith'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php index 84904c3..8c36503 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/EqualBinary.php @@ -12,13 +12,29 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class EqualBinary extends AbstractBinary +class EqualBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function compile(Compiler $compiler): void + { + if (\PHP_VERSION_ID >= 80000) { + parent::compile($compiler); + + return; + } + + $compiler + ->raw('(0 === CoreExtension::compare(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler { return $compiler->raw('=='); } } - -class_alias('Twig\Node\Expression\Binary\EqualBinary', 'Twig_Node_Expression_Binary_Equal'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php index 4dd5e3d..a60ab3b 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/FloorDivBinary.php @@ -12,20 +12,19 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class FloorDivBinary extends AbstractBinary +class FloorDivBinary extends AbstractBinary implements ReturnNumberInterface { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler->raw('(int) floor('); parent::compile($compiler); $compiler->raw(')'); } - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('/'); } } - -class_alias('Twig\Node\Expression\Binary\FloorDivBinary', 'Twig_Node_Expression_Binary_FloorDiv'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php index be73001..71a980b 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/GreaterBinary.php @@ -12,13 +12,29 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class GreaterBinary extends AbstractBinary +class GreaterBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function compile(Compiler $compiler): void + { + if (\PHP_VERSION_ID >= 80000) { + parent::compile($compiler); + + return; + } + + $compiler + ->raw('(1 === CoreExtension::compare(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler { return $compiler->raw('>'); } } - -class_alias('Twig\Node\Expression\Binary\GreaterBinary', 'Twig_Node_Expression_Binary_Greater'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php index 5c2ae72..c92e61b 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/GreaterEqualBinary.php @@ -12,13 +12,29 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class GreaterEqualBinary extends AbstractBinary +class GreaterEqualBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function compile(Compiler $compiler): void + { + if (\PHP_VERSION_ID >= 80000) { + parent::compile($compiler); + + return; + } + + $compiler + ->raw('(0 <= CoreExtension::compare(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler { return $compiler->raw('>='); } } - -class_alias('Twig\Node\Expression\Binary\GreaterEqualBinary', 'Twig_Node_Expression_Binary_GreaterEqual'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/HasEveryBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/HasEveryBinary.php new file mode 100644 index 0000000..22b3801 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Binary/HasEveryBinary.php @@ -0,0 +1,34 @@ +raw('CoreExtension::arrayEvery($this->env, ') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Compiler $compiler): Compiler + { + return $compiler->raw(''); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Binary/HasSomeBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/HasSomeBinary.php new file mode 100644 index 0000000..a2a363e --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Binary/HasSomeBinary.php @@ -0,0 +1,34 @@ +raw('CoreExtension::arraySome($this->env, ') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw(')') + ; + } + + public function operator(Compiler $compiler): Compiler + { + return $compiler->raw(''); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Binary/InBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/InBinary.php index f00b230..31a21e7 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/InBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/InBinary.php @@ -12,13 +12,14 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class InBinary extends AbstractBinary +class InBinary extends AbstractBinary implements ReturnBoolInterface { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler - ->raw('twig_in_filter(') + ->raw('CoreExtension::inFilter(') ->subcompile($this->getNode('left')) ->raw(', ') ->subcompile($this->getNode('right')) @@ -26,10 +27,8 @@ class InBinary extends AbstractBinary ; } - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('in'); } } - -class_alias('Twig\Node\Expression\Binary\InBinary', 'Twig_Node_Expression_Binary_In'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php index 2b202da..293d98d 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/LessBinary.php @@ -12,13 +12,29 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class LessBinary extends AbstractBinary +class LessBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function compile(Compiler $compiler): void + { + if (\PHP_VERSION_ID >= 80000) { + parent::compile($compiler); + + return; + } + + $compiler + ->raw('(-1 === CoreExtension::compare(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler { return $compiler->raw('<'); } } - -class_alias('Twig\Node\Expression\Binary\LessBinary', 'Twig_Node_Expression_Binary_Less'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php index 4fffafe..239d9fd 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/LessEqualBinary.php @@ -12,13 +12,29 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class LessEqualBinary extends AbstractBinary +class LessEqualBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function compile(Compiler $compiler): void + { + if (\PHP_VERSION_ID >= 80000) { + parent::compile($compiler); + + return; + } + + $compiler + ->raw('(0 >= CoreExtension::compare(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler { return $compiler->raw('<='); } } - -class_alias('Twig\Node\Expression\Binary\LessEqualBinary', 'Twig_Node_Expression_Binary_LessEqual'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php index ae810b2..32e8d34 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/MatchesBinary.php @@ -12,13 +12,32 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Error\SyntaxError; +use Twig\Node\Expression\ReturnBoolInterface; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Node; -class MatchesBinary extends AbstractBinary +class MatchesBinary extends AbstractBinary implements ReturnBoolInterface { - public function compile(Compiler $compiler) + public function __construct(Node $left, Node $right, int $lineno) + { + if ($right instanceof ConstantExpression) { + $regexp = $right->getAttribute('value'); + set_error_handler(static fn ($t, $m) => throw new SyntaxError(\sprintf('Regexp "%s" passed to "matches" is not valid: %s.', $regexp, substr($m, 14)), $lineno)); + try { + preg_match($regexp, ''); + } finally { + restore_error_handler(); + } + } + + parent::__construct($left, $right, $lineno); + } + + public function compile(Compiler $compiler): void { $compiler - ->raw('preg_match(') + ->raw('CoreExtension::matches(') ->subcompile($this->getNode('right')) ->raw(', ') ->subcompile($this->getNode('left')) @@ -26,10 +45,8 @@ class MatchesBinary extends AbstractBinary ; } - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw(''); } } - -class_alias('Twig\Node\Expression\Binary\MatchesBinary', 'Twig_Node_Expression_Binary_Matches'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php index e6a2b36..aef48f3 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/ModBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class ModBinary extends AbstractBinary +class ModBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('%'); } } - -class_alias('Twig\Node\Expression\Binary\ModBinary', 'Twig_Node_Expression_Binary_Mod'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php index cd65f5d..beb881a 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/MulBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class MulBinary extends AbstractBinary +class MulBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('*'); } } - -class_alias('Twig\Node\Expression\Binary\MulBinary', 'Twig_Node_Expression_Binary_Mul'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php index df5c6a2..fd24ef9 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/NotEqualBinary.php @@ -12,13 +12,29 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class NotEqualBinary extends AbstractBinary +class NotEqualBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function compile(Compiler $compiler): void + { + if (\PHP_VERSION_ID >= 80000) { + parent::compile($compiler); + + return; + } + + $compiler + ->raw('(0 !== CoreExtension::compare(') + ->subcompile($this->getNode('left')) + ->raw(', ') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler { return $compiler->raw('!='); } } - -class_alias('Twig\Node\Expression\Binary\NotEqualBinary', 'Twig_Node_Expression_Binary_NotEqual'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php index ed2034e..9fd2731 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/NotInBinary.php @@ -12,13 +12,14 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class NotInBinary extends AbstractBinary +class NotInBinary extends AbstractBinary implements ReturnBoolInterface { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler - ->raw('!twig_in_filter(') + ->raw('!CoreExtension::inFilter(') ->subcompile($this->getNode('left')) ->raw(', ') ->subcompile($this->getNode('right')) @@ -26,10 +27,8 @@ class NotInBinary extends AbstractBinary ; } - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('not in'); } } - -class_alias('Twig\Node\Expression\Binary\NotInBinary', 'Twig_Node_Expression_Binary_NotIn'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/NullCoalesceBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/NullCoalesceBinary.php new file mode 100644 index 0000000..a047b60 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Binary/NullCoalesceBinary.php @@ -0,0 +1,71 @@ +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, new TwigTest('null'), new EmptyNode(), $left->getTemplateLine()), $left->getTemplateLine()), + $left->getTemplateLine(), + ); + } + + $left->setAttribute('always_defined', true); + $this->setNode('test', $test); + } + + public function compile(Compiler $compiler): void + { + $compiler + ->raw('((') + ->subcompile($this->getNode('test')) + ->raw(') ? (') + ->subcompile($this->getNode('left')) + ->raw(') : (') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function operator(Compiler $compiler): Compiler + { + return $compiler->raw('??'); + } + + public function getOperandNamesToEscape(): array + { + return ['left', 'right']; + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php index 8f9da43..82dcb7e 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/OrBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class OrBinary extends AbstractBinary +class OrBinary extends AbstractBinary implements ReturnBoolInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('||'); } } - -class_alias('Twig\Node\Expression\Binary\OrBinary', 'Twig_Node_Expression_Binary_Or'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php index 32d0214..5325e8e 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/PowerBinary.php @@ -12,13 +12,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class PowerBinary extends AbstractBinary +class PowerBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('**'); } } - -class_alias('Twig\Node\Expression\Binary\PowerBinary', 'Twig_Node_Expression_Binary_Power'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php index e9c0cdf..f318d8e 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/RangeBinary.php @@ -12,10 +12,11 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnArrayInterface; -class RangeBinary extends AbstractBinary +class RangeBinary extends AbstractBinary implements ReturnArrayInterface { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('range(') @@ -26,10 +27,8 @@ class RangeBinary extends AbstractBinary ; } - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('..'); } } - -class_alias('Twig\Node\Expression\Binary\RangeBinary', 'Twig_Node_Expression_Binary_Range'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/SpaceshipBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/SpaceshipBinary.php index 5245e40..c0a28b0 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/SpaceshipBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/SpaceshipBinary.php @@ -12,10 +12,11 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class SpaceshipBinary extends AbstractBinary +class SpaceshipBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('<=>'); } diff --git a/vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php index 1fe59fb..ef2fc95 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/StartsWithBinary.php @@ -12,26 +12,25 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnBoolInterface; -class StartsWithBinary extends AbstractBinary +class StartsWithBinary extends AbstractBinary implements ReturnBoolInterface { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $left = $compiler->getVarName(); $right = $compiler->getVarName(); $compiler - ->raw(sprintf('(is_string($%s = ', $left)) + ->raw(\sprintf('(is_string($%s = ', $left)) ->subcompile($this->getNode('left')) - ->raw(sprintf(') && is_string($%s = ', $right)) + ->raw(\sprintf(') && is_string($%s = ', $right)) ->subcompile($this->getNode('right')) - ->raw(sprintf(') && (\'\' === $%2$s || 0 === strpos($%1$s, $%2$s)))', $left, $right)) + ->raw(\sprintf(') && str_starts_with($%1$s, $%2$s))', $left, $right)) ; } - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw(''); } } - -class_alias('Twig\Node\Expression\Binary\StartsWithBinary', 'Twig_Node_Expression_Binary_StartsWith'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php index 2546975..10663f5 100644 --- a/vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php +++ b/vendor/twig/twig/src/Node/Expression/Binary/SubBinary.php @@ -13,13 +13,12 @@ namespace Twig\Node\Expression\Binary; use Twig\Compiler; +use Twig\Node\Expression\ReturnNumberInterface; -class SubBinary extends AbstractBinary +class SubBinary extends AbstractBinary implements ReturnNumberInterface { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { return $compiler->raw('-'); } } - -class_alias('Twig\Node\Expression\Binary\SubBinary', 'Twig_Node_Expression_Binary_Sub'); diff --git a/vendor/twig/twig/src/Node/Expression/Binary/XorBinary.php b/vendor/twig/twig/src/Node/Expression/Binary/XorBinary.php new file mode 100644 index 0000000..6f412d2 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Binary/XorBinary.php @@ -0,0 +1,24 @@ +raw('xor'); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php b/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php index c68989a..cb7d38c 100644 --- a/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php +++ b/vendor/twig/twig/src/Node/Expression/BlockReferenceExpression.php @@ -20,28 +20,39 @@ use Twig\Node\Node; * * @author Fabien Potencier */ -class BlockReferenceExpression extends AbstractExpression +class BlockReferenceExpression extends AbstractExpression implements SupportDefinedTestInterface { - public function __construct(Node $name, Node $template = null, int $lineno, string $tag = null) + use SupportDefinedTestDeprecationTrait; + use SupportDefinedTestTrait; + + /** + * @param AbstractExpression $name + */ + public function __construct(Node $name, ?Node $template, int $lineno) { + if (!$name instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $name::class); + } + $nodes = ['name' => $name]; if (null !== $template) { $nodes['template'] = $template; } - parent::__construct($nodes, ['is_defined_test' => false, 'output' => false], $lineno, $tag); + parent::__construct($nodes, ['output' => false], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { - if ($this->getAttribute('is_defined_test')) { + if ($this->definedTest) { $this->compileTemplateCall($compiler, 'hasBlock'); } else { if ($this->getAttribute('output')) { $compiler->addDebugInfo($this); + $compiler->write('yield from '); $this - ->compileTemplateCall($compiler, 'displayBlock') + ->compileTemplateCall($compiler, 'yieldBlock') ->raw(";\n"); } else { $this->compileTemplateCall($compiler, 'renderBlock'); @@ -55,17 +66,15 @@ class BlockReferenceExpression extends AbstractExpression $compiler->write('$this'); } else { $compiler - ->write('$this->loadTemplate(') + ->write('$this->load(') ->subcompile($this->getNode('template')) ->raw(', ') - ->repr($this->getTemplateName()) - ->raw(', ') ->repr($this->getTemplateLine()) ->raw(')') ; } - $compiler->raw(sprintf('->%s', $method)); + $compiler->raw(\sprintf('->unwrap()->%s', $method)); return $this->compileBlockArguments($compiler); } @@ -84,5 +93,3 @@ class BlockReferenceExpression extends AbstractExpression return $compiler->raw(')'); } } - -class_alias('Twig\Node\Expression\BlockReferenceExpression', 'Twig_Node_Expression_BlockReference'); diff --git a/vendor/twig/twig/src/Node/Expression/CallExpression.php b/vendor/twig/twig/src/Node/Expression/CallExpression.php index 4ecd2c1..330d825 100644 --- a/vendor/twig/twig/src/Node/Expression/CallExpression.php +++ b/vendor/twig/twig/src/Node/Expression/CallExpression.php @@ -15,64 +15,84 @@ use Twig\Compiler; 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 { - private $reflector; + private $reflector = null; + /** + * @return void + */ protected function compileCallable(Compiler $compiler) { - $callable = $this->getAttribute('callable'); + $twigCallable = $this->getTwigCallable(); + $callable = $twigCallable->getCallable(); - $closingParenthesis = false; - $isArray = false; - if (\is_string($callable) && false === strpos($callable, '::')) { + if (\is_string($callable) && !str_contains($callable, '::')) { $compiler->raw($callable); } else { - list($r, $callable) = $this->reflectCallable($callable); - if ($r instanceof \ReflectionMethod && \is_string($callable[0])) { - if ($r->isStatic()) { - $compiler->raw(sprintf('%s::%s', $callable[0], $callable[1])); + $rc = $this->reflectCallable($twigCallable); + $r = $rc->getReflector(); + $callable = $rc->getCallable(); + + if (\is_string($callable)) { + $compiler->raw($callable); + } elseif (\is_array($callable) && \is_string($callable[0])) { + if (!$r instanceof \ReflectionMethod || $r->isStatic()) { + $compiler->raw(\sprintf('%s::%s', $callable[0], $callable[1])); } else { - $compiler->raw(sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1])); + $compiler->raw(\sprintf('$this->env->getRuntime(\'%s\')->%s', $callable[0], $callable[1])); } - } elseif ($r instanceof \ReflectionMethod && $callable[0] instanceof ExtensionInterface) { - // For BC/FC with namespaced aliases - $class = (new \ReflectionClass(\get_class($callable[0])))->name; + } elseif (\is_array($callable) && $callable[0] instanceof ExtensionInterface) { + $class = \get_class($callable[0]); if (!$compiler->getEnvironment()->hasExtension($class)) { // Compile a non-optimized call to trigger a \Twig\Error\RuntimeError, which cannot be a compile-time error - $compiler->raw(sprintf('$this->env->getExtension(\'%s\')', $class)); + $compiler->raw(\sprintf('$this->env->getExtension(\'%s\')', $class)); } else { - $compiler->raw(sprintf('$this->extensions[\'%s\']', ltrim($class, '\\'))); + $compiler->raw(\sprintf('$this->extensions[\'%s\']', ltrim($class, '\\'))); } - $compiler->raw(sprintf('->%s', $callable[1])); + $compiler->raw(\sprintf('->%s', $callable[1])); } else { - $closingParenthesis = true; - $isArray = true; - $compiler->raw(sprintf('call_user_func_array($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())); } } - $this->compileArguments($compiler, $isArray); - - if ($closingParenthesis) { - $compiler->raw(')'); - } + $this->compileArguments($compiler); } - protected function compileArguments(Compiler $compiler, $isArray = false) + protected function compileArguments(Compiler $compiler, $isArray = false): void { + if (\func_num_args() >= 2) { + trigger_deprecation('twig/twig', '3.11', 'Passing a second argument to "%s()" is deprecated.', __METHOD__); + } + $compiler->raw($isArray ? '[' : '('); $first = true; - if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { + $twigCallable = $this->getAttribute('twig_callable'); + + if ($twigCallable->needsCharset()) { + $compiler->raw('$this->env->getCharset()'); + $first = false; + } + + if ($twigCallable->needsEnvironment()) { + if (!$first) { + $compiler->raw(', '); + } $compiler->raw('$this->env'); $first = false; } - if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { + if ($twigCallable->needsContext()) { if (!$first) { $compiler->raw(', '); } @@ -80,14 +100,12 @@ abstract class CallExpression extends AbstractExpression $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')) { @@ -99,8 +117,7 @@ abstract class CallExpression extends AbstractExpression } 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(', '); @@ -113,8 +130,13 @@ abstract class CallExpression extends AbstractExpression $compiler->raw($isArray ? ']' : ')'); } - protected function getArguments($callable = null, $arguments) + /** + * @deprecated since Twig 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'); @@ -125,43 +147,52 @@ abstract class CallExpression extends AbstractExpression $named = true; $name = $this->normalizeName($name); } elseif ($named) { - throw new SyntaxError(sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName), $this->getTemplateLine(), $this->getSourceContext()); + throw new SyntaxError(\sprintf('Positional arguments cannot be used after named arguments for %s "%s".', $callType, $callName), $this->getTemplateLine(), $this->getSourceContext()); } $parameters[$name] = $node; } - $isVariadic = $this->hasAttribute('is_variadic') && $this->getAttribute('is_variadic'); + $isVariadic = $this->getAttribute('twig_callable')->isVariadic(); if (!$named && !$isVariadic) { return $parameters; } if (!$callable) { if ($named) { - $message = sprintf('Named arguments are not supported for %s "%s".', $callType, $callName); + $message = \sprintf('Named arguments are not supported for %s "%s".', $callType, $callName); } else { - $message = sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName); + $message = \sprintf('Arbitrary positional arguments are not supported for %s "%s".', $callType, $callName); } throw new \LogicException($message); } - list($callableParameters, $isPhpVariadic) = $this->getCallableParameters($callable, $isVariadic); + [$callableParameters, $isPhpVariadic] = $this->getCallableParameters($callable, $isVariadic); $arguments = []; $names = []; $missingArguments = []; $optionalArguments = []; $pos = 0; foreach ($callableParameters as $callableParameter) { - $names[] = $name = $this->normalizeName($callableParameter->name); + $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, $parameters)) { if (\array_key_exists($pos, $parameters)) { - throw new SyntaxError(sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext()); + throw new SyntaxError(\sprintf('Argument "%s" is defined twice for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext()); } if (\count($missingArguments)) { - throw new SyntaxError(sprintf( + 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, $callType, $callName, implode(', ', $names), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments) ), $this->getTemplateLine(), $this->getSourceContext()); @@ -180,13 +211,13 @@ abstract class CallExpression extends AbstractExpression } elseif ($callableParameter->isDefaultValueAvailable()) { $optionalArguments[] = new ConstantExpression($callableParameter->getDefaultValue(), -1); } elseif ($callableParameter->isOptional()) { - if (empty($parameters)) { + if (!$parameters) { break; } else { $missingArguments[] = $name; } } else { - throw new SyntaxError(sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext()); + throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $name, $callType, $callName), $this->getTemplateLine(), $this->getSourceContext()); } } @@ -207,7 +238,7 @@ abstract class CallExpression extends AbstractExpression } } - if (!empty($parameters)) { + if ($parameters) { $unknownParameter = null; foreach ($parameters as $parameter) { if ($parameter instanceof Node) { @@ -217,7 +248,7 @@ abstract class CallExpression extends AbstractExpression } throw new SyntaxError( - sprintf( + \sprintf( 'Unknown argument%s "%s" for %s "%s(%s)".', \count($parameters) > 1 ? 's' : '', implode('", "', array_keys($parameters)), $callType, $callName, implode(', ', $names) ), @@ -229,85 +260,106 @@ abstract class CallExpression extends AbstractExpression return $arguments; } - protected function normalizeName($name) + /** + * @deprecated since Twig 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 { - list($r) = $this->reflectCallable($callable); - if (null === $r) { - return [[], false]; - } + $twigCallable = $this->getAttribute('twig_callable'); + $rc = $this->reflectCallable($twigCallable); + $r = $rc->getReflector(); + $callableName = $rc->getName(); $parameters = $r->getParameters(); if ($this->hasNode('node')) { array_shift($parameters); } - if ($this->hasAttribute('needs_environment') && $this->getAttribute('needs_environment')) { + if ($twigCallable->needsCharset()) { array_shift($parameters); } - if ($this->hasAttribute('needs_context') && $this->getAttribute('needs_context')) { + if ($twigCallable->needsEnvironment()) { array_shift($parameters); } - if ($this->hasAttribute('arguments') && null !== $this->getAttribute('arguments')) { - foreach ($this->getAttribute('arguments') as $argument) { - array_shift($parameters); - } + if ($twigCallable->needsContext()) { + array_shift($parameters); } + foreach ($twigCallable->getArguments() as $argument) { + array_shift($parameters); + } + $isPhpVariadic = false; if ($isVariadic) { $argument = end($parameters); - if ($argument && $argument->isArray() && $argument->isDefaultValueAvailable() && [] === $argument->getDefaultValue()) { + $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 { - $callableName = $r->name; - if ($r instanceof \ReflectionMethod) { - $callableName = $r->getDeclaringClass()->name.'::'.$callableName; - } - - throw new \LogicException(sprintf('The last parameter of "%s" for %s "%s" must be an array with default value, eg. "array $arg = []".', $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) + private function reflectCallable(TwigCallableInterface $callable): ReflectionCallable { - if (null !== $this->reflector) { - return $this->reflector; + if (!$this->reflector) { + $this->reflector = new ReflectionCallable($callable); } - if (\is_array($callable)) { - if (!method_exists($callable[0], $callable[1])) { - // __call() - return [null, []]; - } - $r = new \ReflectionMethod($callable[0], $callable[1]); - } elseif (\is_object($callable) && !$callable instanceof \Closure) { - $r = new \ReflectionObject($callable); - $r = $r->getMethod('__invoke'); - $callable = [$callable, '__invoke']; - } elseif (\is_string($callable) && false !== $pos = strpos($callable, '::')) { - $class = substr($callable, 0, $pos); - $method = substr($callable, $pos + 2); - if (!method_exists($class, $method)) { - // __staticCall() - return [null, []]; - } - $r = new \ReflectionMethod($callable); - $callable = [$class, $method]; - } else { - $r = new \ReflectionFunction($callable); - } + return $this->reflector; + } - return $this->reflector = [$r, $callable]; + /** + * 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') ? $this->getAttribute('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') ? $this->getAttribute('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') ? $this->getAttribute('arguments') : $current->getArguments()), + }); + + return $this->getAttribute('twig_callable'); } } - -class_alias('Twig\Node\Expression\CallExpression', 'Twig_Node_Expression_Call'); diff --git a/vendor/twig/twig/src/Node/Expression/ConditionalExpression.php b/vendor/twig/twig/src/Node/Expression/ConditionalExpression.php index 8c367d3..7fe309c 100644 --- a/vendor/twig/twig/src/Node/Expression/ConditionalExpression.php +++ b/vendor/twig/twig/src/Node/Expression/ConditionalExpression.php @@ -13,26 +13,41 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Node\Expression\Ternary\ConditionalTernary; -class ConditionalExpression extends AbstractExpression +class ConditionalExpression extends AbstractExpression implements OperatorEscapeInterface { public function __construct(AbstractExpression $expr1, AbstractExpression $expr2, AbstractExpression $expr3, int $lineno) { + trigger_deprecation('twig/twig', '3.17', \sprintf('"%s" is deprecated; use "%s" instead.', __CLASS__, ConditionalTernary::class)); + parent::__construct(['expr1' => $expr1, 'expr2' => $expr2, 'expr3' => $expr3], [], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { - $compiler - ->raw('((') - ->subcompile($this->getNode('expr1')) - ->raw(') ? (') - ->subcompile($this->getNode('expr2')) - ->raw(') : (') - ->subcompile($this->getNode('expr3')) - ->raw('))') - ; + // Ternary with no then uses Elvis operator + if ($this->getNode('expr1') === $this->getNode('expr2')) { + $compiler + ->raw('((') + ->subcompile($this->getNode('expr1')) + ->raw(') ?: (') + ->subcompile($this->getNode('expr3')) + ->raw('))'); + } else { + $compiler + ->raw('((') + ->subcompile($this->getNode('expr1')) + ->raw(') ? (') + ->subcompile($this->getNode('expr2')) + ->raw(') : (') + ->subcompile($this->getNode('expr3')) + ->raw('))'); + } + } + + public function getOperandNamesToEscape(): array + { + return ['expr2', 'expr3']; } } - -class_alias('Twig\Node\Expression\ConditionalExpression', 'Twig_Node_Expression_Conditional'); diff --git a/vendor/twig/twig/src/Node/Expression/ConstantExpression.php b/vendor/twig/twig/src/Node/Expression/ConstantExpression.php index 46e0ac3..12dc062 100644 --- a/vendor/twig/twig/src/Node/Expression/ConstantExpression.php +++ b/vendor/twig/twig/src/Node/Expression/ConstantExpression.php @@ -14,17 +14,20 @@ namespace Twig\Node\Expression; use Twig\Compiler; -class ConstantExpression extends AbstractExpression +/** + * @final + */ +class ConstantExpression extends AbstractExpression implements SupportDefinedTestInterface, ReturnPrimitiveTypeInterface { + use SupportDefinedTestTrait; + public function __construct($value, int $lineno) { parent::__construct([], ['value' => $value], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { - $compiler->repr($this->getAttribute('value')); + $compiler->repr($this->definedTest ? true : $this->getAttribute('value')); } } - -class_alias('Twig\Node\Expression\ConstantExpression', 'Twig_Node_Expression_Constant'); diff --git a/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php b/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php index 0dacae8..04ef06c 100644 --- a/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php +++ b/vendor/twig/twig/src/Node/Expression/Filter/DefaultFilter.php @@ -11,14 +11,20 @@ namespace Twig\Node\Expression\Filter; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; -use Twig\Node\Expression\ConditionalExpression; +use Twig\Extension\CoreExtension; +use Twig\Node\EmptyNode; +use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\GetAttrExpression; -use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Ternary\ConditionalTernary; use Twig\Node\Expression\Test\DefinedTest; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; +use Twig\TwigFilter; +use Twig\TwigTest; /** * Returns the value or the default value when it is undefined or empty. @@ -29,26 +35,38 @@ use Twig\Node\Node; */ class DefaultFilter extends FilterExpression { - public function __construct(Node $node, ConstantExpression $filterName, Node $arguments, int $lineno, string $tag = null) + /** + * @param AbstractExpression $node + */ + #[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 (!$node instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $node::class); + } - if ('default' === $filterName->getAttribute('value') && ($node instanceof NameExpression || $node instanceof GetAttrExpression)) { - $test = new DefinedTest(clone $node, 'defined', new Node(), $node->getTemplateLine()); - $false = \count($arguments) ? $arguments->getNode(0) : new ConstantExpression('', $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()); + } - $node = new ConditionalExpression($test, $default, $false, $node->getTemplateLine()); + if ('default' === $name && ($node instanceof ContextVariable || $node instanceof GetAttrExpression)) { + $test = new DefinedTest(clone $node, new TwigTest('defined'), new EmptyNode(), $node->getTemplateLine()); + $false = \count($arguments) ? $arguments->getNode('0') : new ConstantExpression('', $node->getTemplateLine()); + + $node = new ConditionalTernary($test, $default, $false, $node->getTemplateLine()); } else { $node = $default; } - parent::__construct($node, $filterName, $arguments, $lineno, $tag); + parent::__construct($node, $filter, $arguments, $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler->subcompile($this->getNode('node')); } } - -class_alias('Twig\Node\Expression\Filter\DefaultFilter', 'Twig_Node_Expression_Filter_Default'); diff --git a/vendor/twig/twig/src/Node/Expression/Filter/RawFilter.php b/vendor/twig/twig/src/Node/Expression/Filter/RawFilter.php new file mode 100644 index 0000000..707e8ec --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Filter/RawFilter.php @@ -0,0 +1,45 @@ + + */ +class RawFilter extends FilterExpression +{ + /** + * @param AbstractExpression $node + */ + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigFilter|ConstantExpression|null $filter = null, ?Node $arguments = null, int $lineno = 0) + { + if (!$node instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $node::class); + } + + parent::__construct($node, $filter ?: new TwigFilter('raw', null, ['is_safe' => ['all']]), $arguments ?: new EmptyNode(), $lineno ?: $node->getTemplateLine()); + } + + public function compile(Compiler $compiler): void + { + $compiler->subcompile($this->getNode('node')); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/FilterExpression.php b/vendor/twig/twig/src/Node/Expression/FilterExpression.php index 41b0734..a66b026 100644 --- a/vendor/twig/twig/src/Node/Expression/FilterExpression.php +++ b/vendor/twig/twig/src/Node/Expression/FilterExpression.php @@ -12,31 +12,69 @@ 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) + /** + * @param AbstractExpression $node + */ + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigFilter|ConstantExpression $filter, Node $arguments, int $lineno) { - parent::__construct(['node' => $node, 'filter' => $filterName, 'arguments' => $arguments], [], $lineno, $tag); + if (!$node instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $node::class); + } + + 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) + public function compile(Compiler $compiler): void { - $name = $this->getNode('filter')->getAttribute('value'); - $filter = $compiler->getEnvironment()->getFilter($name); + $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->removeAttribute('twig_callable'); + } + if ('raw' === $name) { + trigger_deprecation('twig/twig', '3.11', 'Creating the "raw" filter via "FilterExpression" is deprecated; use "RawFilter" instead.'); - $this->setAttribute('name', $name); - $this->setAttribute('type', 'filter'); - $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()); + $compiler->subcompile($this->getNode('node')); + + return; + } + + if (!$this->hasAttribute('twig_callable')) { + $this->setAttribute('twig_callable', $compiler->getEnvironment()->getFilter($name)); + } $this->compileCallable($compiler); } } - -class_alias('Twig\Node\Expression\FilterExpression', 'Twig_Node_Expression_Filter'); diff --git a/vendor/twig/twig/src/Node/Expression/FunctionExpression.php b/vendor/twig/twig/src/Node/Expression/FunctionExpression.php index 429dbb9..183145c 100644 --- a/vendor/twig/twig/src/Node/Expression/FunctionExpression.php +++ b/vendor/twig/twig/src/Node/Expression/FunctionExpression.php @@ -11,35 +11,71 @@ namespace Twig\Node\Expression; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; +use Twig\Node\NameDeprecation; use Twig\Node\Node; +use Twig\TwigFunction; -class FunctionExpression extends CallExpression +class FunctionExpression extends CallExpression implements SupportDefinedTestInterface { - public function __construct(string $name, Node $arguments, int $lineno) + use SupportDefinedTestDeprecationTrait; + use SupportDefinedTestTrait; + + #[FirstClassTwigCallableReady] + public function __construct(TwigFunction|string $function, Node $arguments, int $lineno) { - parent::__construct(['arguments' => $arguments], ['name' => $name, 'is_defined_test' => false], $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'], $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 enableDefinedTest(): void + { + if ('constant' === $this->getAttribute('name')) { + $this->definedTest = true; + } + } + + /** + * @return void + */ public function compile(Compiler $compiler) { $name = $this->getAttribute('name'); - $function = $compiler->getEnvironment()->getFunction($name); - - $this->setAttribute('name', $name); - $this->setAttribute('type', 'function'); - $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 = 'twig_constant_is_defined'; + 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)); + } + + if ('constant' === $name && $this->isDefinedTestEnabled()) { + $this->getNode('arguments')->setNode('checkDefined', new ConstantExpression(true, $this->getTemplateLine())); } - $this->setAttribute('callable', $callable); - $this->setAttribute('is_variadic', $function->isVariadic()); $this->compileCallable($compiler); } } - -class_alias('Twig\Node\Expression\FunctionExpression', 'Twig_Node_Expression_Function'); diff --git a/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumCasesFunction.php b/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumCasesFunction.php new file mode 100644 index 0000000..7e5c25f --- /dev/null +++ b/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/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumFunction.php b/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumFunction.php new file mode 100644 index 0000000..1f8b0ec --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/FunctionNode/EnumFunction.php @@ -0,0 +1,45 @@ +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" function must be a string.', $this->getTemplateLine(), $this->getSourceContext()); + } + + if (!enum_exists($value)) { + throw new SyntaxError(\sprintf('The first argument of the "enum" function must be the name of an enum, "%s" given.', $value), $this->getTemplateLine(), $this->getSourceContext()); + } + + if (!$cases = $value::cases()) { + throw new SyntaxError(\sprintf('The first argument of the "enum" function must be a non-empty enum, "%s" given.', $value), $this->getTemplateLine(), $this->getSourceContext()); + } + + $compiler->raw(\sprintf('%s::%s', $value, $cases[0]->name)); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/GetAttrExpression.php b/vendor/twig/twig/src/Node/Expression/GetAttrExpression.php index 7b06617..781c8af 100644 --- a/vendor/twig/twig/src/Node/Expression/GetAttrExpression.php +++ b/vendor/twig/twig/src/Node/Expression/GetAttrExpression.php @@ -14,29 +14,47 @@ namespace Twig\Node\Expression; use Twig\Compiler; use Twig\Extension\SandboxExtension; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Template; -class GetAttrExpression extends AbstractExpression +class GetAttrExpression extends AbstractExpression implements SupportDefinedTestInterface { - public function __construct(AbstractExpression $node, AbstractExpression $attribute, AbstractExpression $arguments = null, string $type, int $lineno) + use SupportDefinedTestDeprecationTrait; + use SupportDefinedTestTrait; + + /** + * @param ArrayExpression|NameExpression|null $arguments + */ + public function __construct(AbstractExpression $node, AbstractExpression $attribute, ?AbstractExpression $arguments, string $type, int $lineno) { $nodes = ['node' => $node, 'attribute' => $attribute]; if (null !== $arguments) { $nodes['arguments'] = $arguments; } - parent::__construct($nodes, ['type' => $type, 'is_defined_test' => false, 'ignore_strict_check' => false, 'optimizable' => true], $lineno); + if ($arguments && !$arguments instanceof ArrayExpression && !$arguments instanceof ContextVariable) { + trigger_deprecation('twig/twig', '3.15', \sprintf('Not passing a "%s" instance as the "arguments" argument of the "%s" constructor is deprecated ("%s" given).', ArrayExpression::class, static::class, $arguments::class)); + } + + parent::__construct($nodes, ['type' => $type, 'ignore_strict_check' => false, 'optimizable' => true], $lineno); } - public function compile(Compiler $compiler) + public function enableDefinedTest(): void + { + $this->definedTest = true; + $this->changeIgnoreStrictCheck($this); + } + + public function compile(Compiler $compiler): void { $env = $compiler->getEnvironment(); + $arrayAccessSandbox = false; // optimize array calls if ( $this->getAttribute('optimizable') && (!$env->isStrictVariables() || $this->getAttribute('ignore_strict_check')) - && !$this->getAttribute('is_defined_test') + && !$this->definedTest && Template::ARRAY_CALL === $this->getAttribute('type') ) { $var = '$'.$compiler->getVarName(); @@ -44,20 +62,38 @@ class GetAttrExpression extends AbstractExpression ->raw('(('.$var.' = ') ->subcompile($this->getNode('node')) ->raw(') && is_array(') - ->raw($var) + ->raw($var); + + if (!$env->hasExtension(SandboxExtension::class)) { + $compiler + ->raw(') || ') + ->raw($var) + ->raw(' instanceof ArrayAccess ? (') + ->raw($var) + ->raw('[') + ->subcompile($this->getNode('attribute')) + ->raw('] ?? null) : null)') + ; + + return; + } + + $arrayAccessSandbox = true; + + $compiler ->raw(') || ') ->raw($var) - ->raw(' instanceof ArrayAccess ? (') + ->raw(' instanceof ArrayAccess && in_array(') + ->raw($var.'::class') + ->raw(', CoreExtension::ARRAY_LIKE_CLASSES, true) ? (') ->raw($var) ->raw('[') ->subcompile($this->getNode('attribute')) - ->raw('] ?? null) : null)') + ->raw('] ?? null) : ') ; - - return; } - $compiler->raw('twig_get_attribute($this->env, $this->source, '); + $compiler->raw('CoreExtension::getAttribute($this->env, $this->source, '); if ($this->getAttribute('ignore_strict_check')) { $this->getNode('node')->setAttribute('ignore_strict_check', true); @@ -77,13 +113,25 @@ class GetAttrExpression extends AbstractExpression $compiler->raw(', ') ->repr($this->getAttribute('type')) - ->raw(', ')->repr($this->getAttribute('is_defined_test')) + ->raw(', ')->repr($this->definedTest) ->raw(', ')->repr($this->getAttribute('ignore_strict_check')) ->raw(', ')->repr($env->hasExtension(SandboxExtension::class)) ->raw(', ')->repr($this->getNode('node')->getTemplateLine()) ->raw(')') ; + + if ($arrayAccessSandbox) { + $compiler->raw(')'); + } + } + + private function changeIgnoreStrictCheck(GetAttrExpression $node): void + { + $node->setAttribute('optimizable', false); + $node->setAttribute('ignore_strict_check', true); + + if ($node->getNode('node') instanceof GetAttrExpression) { + $this->changeIgnoreStrictCheck($node->getNode('node')); + } } } - -class_alias('Twig\Node\Expression\GetAttrExpression', 'Twig_Node_Expression_GetAttr'); diff --git a/vendor/twig/twig/src/Node/Expression/InlinePrint.php b/vendor/twig/twig/src/Node/Expression/InlinePrint.php index 469e736..5509f79 100644 --- a/vendor/twig/twig/src/Node/Expression/InlinePrint.php +++ b/vendor/twig/twig/src/Node/Expression/InlinePrint.php @@ -19,17 +19,21 @@ use Twig\Node\Node; */ final class InlinePrint extends AbstractExpression { - public function __construct(Node $node, $lineno) + /** + * @param AbstractExpression $node + */ + public function __construct(Node $node, int $lineno) { + trigger_deprecation('twig/twig', '3.16', \sprintf('The "%s" class is deprecated with no replacement.', static::class)); + parent::__construct(['node' => $node], [], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler - ->raw('print (') + ->raw('yield ') ->subcompile($this->getNode('node')) - ->raw(')') ; } } diff --git a/vendor/twig/twig/src/Node/Expression/ListExpression.php b/vendor/twig/twig/src/Node/Expression/ListExpression.php new file mode 100644 index 0000000..dd7fc1f --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/ListExpression.php @@ -0,0 +1,41 @@ + $items + */ + public function __construct(array $items, int $lineno) + { + parent::__construct($items, [], $lineno); + } + + public function compile(Compiler $compiler): void + { + foreach ($this as $i => $name) { + if ($i) { + $compiler->raw(', '); + } + + $compiler + ->raw('$__') + ->raw($name->getAttribute('name')) + ->raw('__') + ; + } + } +} diff --git a/vendor/twig/twig/src/Node/Expression/MacroReferenceExpression.php b/vendor/twig/twig/src/Node/Expression/MacroReferenceExpression.php new file mode 100644 index 0000000..fd7f1e7 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/MacroReferenceExpression.php @@ -0,0 +1,59 @@ + + */ +class MacroReferenceExpression extends AbstractExpression implements SupportDefinedTestInterface +{ + use SupportDefinedTestDeprecationTrait; + use SupportDefinedTestTrait; + + public function __construct(TemplateVariable $template, string $name, AbstractExpression $arguments, int $lineno) + { + parent::__construct(['template' => $template, 'arguments' => $arguments], ['name' => $name], $lineno); + } + + public function compile(Compiler $compiler): void + { + if ($this->definedTest) { + $compiler + ->subcompile($this->getNode('template')) + ->raw('->hasMacro(') + ->repr($this->getAttribute('name')) + ->raw(', $context') + ->raw(')') + ; + + return; + } + + $compiler + ->subcompile($this->getNode('template')) + ->raw('->getTemplateForMacro(') + ->repr($this->getAttribute('name')) + ->raw(', $context, ') + ->repr($this->getTemplateLine()) + ->raw(', $this->getSourceContext())') + ->raw(\sprintf('->%s', $this->getAttribute('name'))) + ->raw('(...') + ->subcompile($this->getNode('arguments')) + ->raw(')') + ; + } +} diff --git a/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php b/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php index d5287f8..4b18053 100644 --- a/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php +++ b/vendor/twig/twig/src/Node/Expression/MethodCallExpression.php @@ -12,21 +12,27 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Node\Expression\Variable\ContextVariable; -class MethodCallExpression extends AbstractExpression +class MethodCallExpression extends AbstractExpression implements SupportDefinedTestInterface { + use SupportDefinedTestDeprecationTrait; + use SupportDefinedTestTrait; + public function __construct(AbstractExpression $node, string $method, ArrayExpression $arguments, int $lineno) { - parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false, 'is_defined_test' => false], $lineno); + trigger_deprecation('twig/twig', '3.15', 'The "%s" class is deprecated, use "%s" instead.', __CLASS__, MacroReferenceExpression::class); - if ($node instanceof NameExpression) { + parent::__construct(['node' => $node, 'arguments' => $arguments], ['method' => $method, 'safe' => false], $lineno); + + if ($node instanceof ContextVariable) { $node->setAttribute('always_defined', true); } } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { - if ($this->getAttribute('is_defined_test')) { + if ($this->definedTest) { $compiler ->raw('method_exists($macros[') ->repr($this->getNode('node')->getAttribute('name')) @@ -39,26 +45,14 @@ class MethodCallExpression extends AbstractExpression } $compiler - ->raw('twig_call_macro($macros[') + ->raw('CoreExtension::callMacro($macros[') ->repr($this->getNode('node')->getAttribute('name')) ->raw('], ') ->repr($this->getAttribute('method')) - ->raw(', [') - ; - $first = true; - foreach ($this->getNode('arguments')->getKeyValuePairs() as $pair) { - if (!$first) { - $compiler->raw(', '); - } - $first = false; - - $compiler->subcompile($pair['value']); - } - $compiler - ->raw('], ') + ->raw(', ') + ->subcompile($this->getNode('arguments')) + ->raw(', ') ->repr($this->getTemplateLine()) ->raw(', $context, $this->getSourceContext())'); } } - -class_alias('Twig\Node\Expression\MethodCallExpression', 'Twig_Node_Expression_MethodCall'); diff --git a/vendor/twig/twig/src/Node/Expression/NameExpression.php b/vendor/twig/twig/src/Node/Expression/NameExpression.php index ff7a046..0e03674 100644 --- a/vendor/twig/twig/src/Node/Expression/NameExpression.php +++ b/vendor/twig/twig/src/Node/Expression/NameExpression.php @@ -13,9 +13,13 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Node\Expression\Variable\ContextVariable; -class NameExpression extends AbstractExpression +class NameExpression extends AbstractExpression implements SupportDefinedTestInterface { + use SupportDefinedTestDeprecationTrait; + use SupportDefinedTestTrait; + private $specialVars = [ '_self' => '$this->getTemplateName()', '_context' => '$context', @@ -24,19 +28,23 @@ class NameExpression extends AbstractExpression public function __construct(string $name, int $lineno) { - parent::__construct([], ['name' => $name, 'is_defined_test' => false, 'ignore_strict_check' => false, 'always_defined' => false], $lineno); + if (self::class === static::class) { + trigger_deprecation('twig/twig', '3.15', 'The "%s" class is deprecated, use "%s" instead.', self::class, ContextVariable::class); + } + + parent::__construct([], ['name' => $name, 'ignore_strict_check' => false, 'always_defined' => false], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $name = $this->getAttribute('name'); $compiler->addDebugInfo($this); - if ($this->getAttribute('is_defined_test')) { - if ($this->isSpecial()) { + if ($this->definedTest) { + if (isset($this->specialVars[$name]) || $this->getAttribute('always_defined')) { $compiler->repr(true); - } elseif (\PHP_VERSION_ID >= 700400) { + } elseif (\PHP_VERSION_ID >= 70400) { $compiler ->raw('array_key_exists(') ->string($name) @@ -51,7 +59,7 @@ class NameExpression extends AbstractExpression ->raw(', $context))') ; } - } elseif ($this->isSpecial()) { + } elseif (isset($this->specialVars[$name])) { $compiler->raw($this->specialVars[$name]); } elseif ($this->getAttribute('always_defined')) { $compiler @@ -85,15 +93,23 @@ class NameExpression extends AbstractExpression } } + /** + * @deprecated since Twig 3.11 (to be removed in 4.0) + */ public function isSpecial() { + trigger_deprecation('twig/twig', '3.11', 'The "%s()" method is deprecated and will be removed in Twig 4.0.', __METHOD__); + return isset($this->specialVars[$this->getAttribute('name')]); } + /** + * @deprecated since Twig 3.11 (to be removed in 4.0) + */ public function isSimple() { - return !$this->isSpecial() && !$this->getAttribute('is_defined_test'); + trigger_deprecation('twig/twig', '3.11', 'The "%s()" method is deprecated and will be removed in Twig 4.0.', __METHOD__); + + return !isset($this->specialVars[$this->getAttribute('name')]) && !$this->definedTest; } } - -class_alias('Twig\Node\Expression\NameExpression', 'Twig_Node_Expression_Name'); diff --git a/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php b/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php index de03ff2..f397f71 100644 --- a/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php +++ b/vendor/twig/twig/src/Node/Expression/NullCoalesceExpression.php @@ -12,22 +12,39 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Node\EmptyNode; use Twig\Node\Expression\Binary\AndBinary; +use Twig\Node\Expression\Binary\NullCoalesceBinary; use Twig\Node\Expression\Test\DefinedTest; use Twig\Node\Expression\Test\NullTest; use Twig\Node\Expression\Unary\NotUnary; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; +use Twig\TwigTest; class NullCoalesceExpression extends ConditionalExpression { + /** + * @param AbstractExpression $left + * @param AbstractExpression $right + */ public function __construct(Node $left, Node $right, int $lineno) { - $test = new DefinedTest(clone $left, 'defined', new Node(), $left->getTemplateLine()); + trigger_deprecation('twig/twig', '3.17', \sprintf('"%s" is deprecated; use "%s" instead.', __CLASS__, NullCoalesceBinary::class)); + + if (!$left instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "left" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $left::class); + } + if (!$right instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "right" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $right::class); + } + + $test = new DefinedTest(clone $left, new TwigTest('defined'), new EmptyNode(), $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 EmptyNode(), $left->getTemplateLine()), $left->getTemplateLine()), $left->getTemplateLine() ); } @@ -35,7 +52,7 @@ class NullCoalesceExpression extends ConditionalExpression parent::__construct($test, $left, $right, $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { /* * This optimizes only one case. PHP 7 also supports more complex expressions @@ -44,7 +61,7 @@ class NullCoalesceExpression extends ConditionalExpression * cases might be implemented as an optimizer node visitor, but has not been done * as benefits are probably not worth the added complexity. */ - if ($this->getNode('expr2') instanceof NameExpression) { + if ($this->getNode('expr2') instanceof ContextVariable) { $this->getNode('expr2')->setAttribute('always_defined', true); $compiler ->raw('((') @@ -58,5 +75,3 @@ class NullCoalesceExpression extends ConditionalExpression } } } - -class_alias('Twig\Node\Expression\NullCoalesceExpression', 'Twig_Node_Expression_NullCoalesce'); diff --git a/vendor/twig/twig/src/Node/Expression/OperatorEscapeInterface.php b/vendor/twig/twig/src/Node/Expression/OperatorEscapeInterface.php new file mode 100644 index 0000000..06db6c6 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/OperatorEscapeInterface.php @@ -0,0 +1,25 @@ + 1. + * + * @author Fabien Potencier + */ +interface OperatorEscapeInterface +{ + /** + * @return string[] + */ + public function getOperandNamesToEscape(): array; +} diff --git a/vendor/twig/twig/src/Node/Expression/ParentExpression.php b/vendor/twig/twig/src/Node/Expression/ParentExpression.php index 294ab39..22fe38f 100644 --- a/vendor/twig/twig/src/Node/Expression/ParentExpression.php +++ b/vendor/twig/twig/src/Node/Expression/ParentExpression.php @@ -21,17 +21,17 @@ use Twig\Compiler; */ 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) + public function compile(Compiler $compiler): void { if ($this->getAttribute('output')) { $compiler ->addDebugInfo($this) - ->write('$this->displayParentBlock(') + ->write('yield from $this->yieldParentBlock(') ->string($this->getAttribute('name')) ->raw(", \$context, \$blocks);\n") ; @@ -44,5 +44,3 @@ class ParentExpression extends AbstractExpression } } } - -class_alias('Twig\Node\Expression\ParentExpression', 'Twig_Node_Expression_Parent'); diff --git a/vendor/twig/twig/src/Node/Expression/ReturnArrayInterface.php b/vendor/twig/twig/src/Node/Expression/ReturnArrayInterface.php new file mode 100644 index 0000000..a74864b --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/ReturnArrayInterface.php @@ -0,0 +1,16 @@ + + */ +trait SupportDefinedTestDeprecationTrait +{ + public function getAttribute($name, $default = null) + { + if ('is_defined_test' === $name) { + trigger_deprecation('twig/twig', '3.21', 'The "is_defined_test" attribute is deprecated, call "isDefinedTestEnabled()" instead.'); + + return $this->isDefinedTestEnabled(); + } + + return parent::getAttribute($name, $default); + } + + public function setAttribute(string $name, $value): void + { + if ('is_defined_test' === $name) { + trigger_deprecation('twig/twig', '3.21', 'The "is_defined_test" attribute is deprecated, call "enableDefinedTest()" instead.'); + + $this->definedTest = (bool) $value; + } else { + parent::setAttribute($name, $value); + } + } +} diff --git a/vendor/twig/twig/src/Node/Expression/SupportDefinedTestInterface.php b/vendor/twig/twig/src/Node/Expression/SupportDefinedTestInterface.php new file mode 100644 index 0000000..450c691 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/SupportDefinedTestInterface.php @@ -0,0 +1,24 @@ + + */ +interface SupportDefinedTestInterface +{ + public function enableDefinedTest(): void; + + public function isDefinedTestEnabled(): bool; +} diff --git a/vendor/twig/twig/src/Node/Expression/SupportDefinedTestTrait.php b/vendor/twig/twig/src/Node/Expression/SupportDefinedTestTrait.php new file mode 100644 index 0000000..4cf1a58 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/SupportDefinedTestTrait.php @@ -0,0 +1,27 @@ +definedTest = true; + } + + public function isDefinedTestEnabled(): bool + { + return $this->definedTest; + } +} diff --git a/vendor/twig/twig/src/Node/Expression/TempNameExpression.php b/vendor/twig/twig/src/Node/Expression/TempNameExpression.php index e7a1a89..f996aab 100644 --- a/vendor/twig/twig/src/Node/Expression/TempNameExpression.php +++ b/vendor/twig/twig/src/Node/Expression/TempNameExpression.php @@ -12,22 +12,38 @@ namespace Twig\Node\Expression; use Twig\Compiler; +use Twig\Error\SyntaxError; class TempNameExpression extends AbstractExpression { - public function __construct(string $name, int $lineno) + public const RESERVED_NAMES = ['varargs', 'context', 'macros', 'blocks', 'this']; + + public function __construct(string|int|null $name, int $lineno) { + // All names supported by ExpressionParser::parsePrimaryExpression() should be excluded + if ($name && \in_array(strtolower($name), ['true', 'false', 'none', 'null'], true)) { + throw new SyntaxError(\sprintf('You cannot assign a value to "%s".', $name), $lineno); + } + + if (self::class === static::class) { + trigger_deprecation('twig/twig', '3.15', 'The "%s" class is deprecated.', self::class); + } + + if (null !== $name && (\is_int($name) || ctype_digit($name))) { + $name = (int) $name; + } elseif (\in_array($name, self::RESERVED_NAMES, true)) { + $name = "\u{035C}".$name; + } + parent::__construct([], ['name' => $name], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { - $compiler - ->raw('$_') - ->raw($this->getAttribute('name')) - ->raw('_') - ; + if (null === $this->getAttribute('name')) { + $this->setAttribute('name', $compiler->getVarName()); + } + + $compiler->raw('$'.$this->getAttribute('name')); } } - -class_alias('Twig\Node\Expression\TempNameExpression', 'Twig_Node_Expression_TempName'); diff --git a/vendor/twig/twig/src/Node/Expression/Ternary/ConditionalTernary.php b/vendor/twig/twig/src/Node/Expression/Ternary/ConditionalTernary.php new file mode 100644 index 0000000..f7cd78c --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Ternary/ConditionalTernary.php @@ -0,0 +1,49 @@ +getTemplateLine()); + } + + parent::__construct(['test' => $test, 'left' => $left, 'right' => $right], [], $lineno); + } + + public function compile(Compiler $compiler): void + { + $compiler + ->raw('((') + ->subcompile($this->getNode('test')) + ->raw(') ? (') + ->subcompile($this->getNode('left')) + ->raw(') : (') + ->subcompile($this->getNode('right')) + ->raw('))') + ; + } + + public function getOperandNamesToEscape(): array + { + return ['left', 'right']; + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php b/vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php index 78353a8..867fd09 100644 --- a/vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php +++ b/vendor/twig/twig/src/Node/Expression/Test/ConstantTest.php @@ -25,7 +25,7 @@ use Twig\Node\Expression\TestExpression; */ class ConstantTest extends TestExpression { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('(') @@ -33,19 +33,17 @@ class ConstantTest extends TestExpression ->raw(' === constant(') ; - if ($this->getNode('arguments')->hasNode(1)) { + if ($this->getNode('arguments')->hasNode('1')) { $compiler ->raw('get_class(') - ->subcompile($this->getNode('arguments')->getNode(1)) + ->subcompile($this->getNode('arguments')->getNode('1')) ->raw(')."::".') ; } $compiler - ->subcompile($this->getNode('arguments')->getNode(0)) + ->subcompile($this->getNode('arguments')->getNode('0')) ->raw('))') ; } } - -class_alias('Twig\Node\Expression\Test\ConstantTest', 'Twig_Node_Expression_Test_Constant'); diff --git a/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php b/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php index 7a89840..f17715b 100644 --- a/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php +++ b/vendor/twig/twig/src/Node/Expression/Test/DefinedTest.php @@ -11,17 +11,22 @@ namespace Twig\Node\Expression\Test; +use Twig\Attribute\FirstClassTwigCallableReady; use Twig\Compiler; use Twig\Error\SyntaxError; +use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\BlockReferenceExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\GetAttrExpression; +use Twig\Node\Expression\MacroReferenceExpression; use Twig\Node\Expression\MethodCallExpression; -use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\SupportDefinedTestInterface; use Twig\Node\Expression\TestExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; +use Twig\TwigTest; /** * Checks if a variable is defined in the current context. @@ -35,42 +40,31 @@ use Twig\Node\Node; */ class DefinedTest extends TestExpression { - public function __construct(Node $node, string $name, Node $arguments = null, int $lineno) + /** + * @param AbstractExpression $node + */ + #[FirstClassTwigCallableReady] + public function __construct(Node $node, TwigTest|string $name, ?Node $arguments, int $lineno) { - if ($node instanceof NameExpression) { - $node->setAttribute('is_defined_test', true); - } elseif ($node instanceof GetAttrExpression) { - $node->setAttribute('is_defined_test', true); - $this->changeIgnoreStrictCheck($node); - } elseif ($node instanceof BlockReferenceExpression) { - $node->setAttribute('is_defined_test', true); - } elseif ($node instanceof FunctionExpression && 'constant' === $node->getAttribute('name')) { - $node->setAttribute('is_defined_test', true); - } elseif ($node instanceof ConstantExpression || $node instanceof ArrayExpression) { - $node = new ConstantExpression(true, $node->getTemplateLine()); - } elseif ($node instanceof MethodCallExpression) { - $node->setAttribute('is_defined_test', true); - } else { + if (!$node instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $node::class); + } + + if (!$node instanceof SupportDefinedTestInterface) { throw new SyntaxError('The "defined" test only works with simple variables.', $lineno); } + $node->enableDefinedTest(); + + 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); } - private function changeIgnoreStrictCheck(GetAttrExpression $node) - { - $node->setAttribute('optimizable', false); - $node->setAttribute('ignore_strict_check', true); - - if ($node->getNode('node') instanceof GetAttrExpression) { - $this->changeIgnoreStrictCheck($node->getNode('node')); - } - } - - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler->subcompile($this->getNode('node')); } } - -class_alias('Twig\Node\Expression\Test\DefinedTest', 'Twig_Node_Expression_Test_Defined'); diff --git a/vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php b/vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php index 05c8ad8..90d58a4 100644 --- a/vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php +++ b/vendor/twig/twig/src/Node/Expression/Test/DivisiblebyTest.php @@ -23,16 +23,14 @@ use Twig\Node\Expression\TestExpression; */ class DivisiblebyTest extends TestExpression { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('(0 == ') ->subcompile($this->getNode('node')) ->raw(' % ') - ->subcompile($this->getNode('arguments')->getNode(0)) + ->subcompile($this->getNode('arguments')->getNode('0')) ->raw(')') ; } } - -class_alias('Twig\Node\Expression\Test\DivisiblebyTest', 'Twig_Node_Expression_Test_Divisibleby'); diff --git a/vendor/twig/twig/src/Node/Expression/Test/EvenTest.php b/vendor/twig/twig/src/Node/Expression/Test/EvenTest.php index 3b955d2..a0e3ed6 100644 --- a/vendor/twig/twig/src/Node/Expression/Test/EvenTest.php +++ b/vendor/twig/twig/src/Node/Expression/Test/EvenTest.php @@ -23,7 +23,7 @@ use Twig\Node\Expression\TestExpression; */ class EvenTest extends TestExpression { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('(') @@ -33,5 +33,3 @@ class EvenTest extends TestExpression ; } } - -class_alias('Twig\Node\Expression\Test\EvenTest', 'Twig_Node_Expression_Test_Even'); diff --git a/vendor/twig/twig/src/Node/Expression/Test/NullTest.php b/vendor/twig/twig/src/Node/Expression/Test/NullTest.php index 24d3997..be5d388 100644 --- a/vendor/twig/twig/src/Node/Expression/Test/NullTest.php +++ b/vendor/twig/twig/src/Node/Expression/Test/NullTest.php @@ -15,7 +15,7 @@ use Twig\Compiler; use Twig\Node\Expression\TestExpression; /** - * Checks that a variable is null. + * Checks that an expression is null. * * {{ var is none }} * @@ -23,7 +23,7 @@ use Twig\Node\Expression\TestExpression; */ class NullTest extends TestExpression { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('(null === ') @@ -32,5 +32,3 @@ class NullTest extends TestExpression ; } } - -class_alias('Twig\Node\Expression\Test\NullTest', 'Twig_Node_Expression_Test_Null'); diff --git a/vendor/twig/twig/src/Node/Expression/Test/OddTest.php b/vendor/twig/twig/src/Node/Expression/Test/OddTest.php index 2dc693a..d56c711 100644 --- a/vendor/twig/twig/src/Node/Expression/Test/OddTest.php +++ b/vendor/twig/twig/src/Node/Expression/Test/OddTest.php @@ -23,15 +23,13 @@ use Twig\Node\Expression\TestExpression; */ class OddTest extends TestExpression { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('(') ->subcompile($this->getNode('node')) - ->raw(' % 2 == 1') + ->raw(' % 2 != 0') ->raw(')') ; } } - -class_alias('Twig\Node\Expression\Test\OddTest', 'Twig_Node_Expression_Test_Odd'); diff --git a/vendor/twig/twig/src/Node/Expression/Test/SameasTest.php b/vendor/twig/twig/src/Node/Expression/Test/SameasTest.php index 75f2b82..f1e24db 100644 --- a/vendor/twig/twig/src/Node/Expression/Test/SameasTest.php +++ b/vendor/twig/twig/src/Node/Expression/Test/SameasTest.php @@ -21,16 +21,14 @@ use Twig\Node\Expression\TestExpression; */ class SameasTest extends TestExpression { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->raw('(') ->subcompile($this->getNode('node')) ->raw(' === ') - ->subcompile($this->getNode('arguments')->getNode(0)) + ->subcompile($this->getNode('arguments')->getNode('0')) ->raw(')') ; } } - -class_alias('Twig\Node\Expression\Test\SameasTest', 'Twig_Node_Expression_Test_Sameas'); diff --git a/vendor/twig/twig/src/Node/Expression/Test/TrueTest.php b/vendor/twig/twig/src/Node/Expression/Test/TrueTest.php new file mode 100644 index 0000000..22186a6 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Test/TrueTest.php @@ -0,0 +1,34 @@ + + */ +class TrueTest extends TestExpression +{ + public function compile(Compiler $compiler): void + { + $compiler + ->raw('(($tmp = ') + ->subcompile($this->getNode('node')) + ->raw(') && $tmp instanceof Markup ? (string) $tmp : $tmp)') + ; + } +} diff --git a/vendor/twig/twig/src/Node/Expression/TestExpression.php b/vendor/twig/twig/src/Node/Expression/TestExpression.php index 50aab05..3b51dd3 100644 --- a/vendor/twig/twig/src/Node/Expression/TestExpression.php +++ b/vendor/twig/twig/src/Node/Expression/TestExpression.php @@ -11,34 +11,63 @@ 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 +class TestExpression extends CallExpression implements ReturnBoolInterface { - public function __construct(Node $node, string $name, Node $arguments = null, int $lineno) + #[FirstClassTwigCallableReady] + /** + * @param AbstractExpression $node + */ + public function __construct(Node $node, string|TwigTest $test, ?Node $arguments, int $lineno) { + if (!$node instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance to the "node" argument of "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $node::class); + } + $nodes = ['node' => $node]; if (null !== $arguments) { $nodes['arguments'] = $arguments; } - parent::__construct($nodes, ['name' => $name], $lineno); + 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) + public function compile(Compiler $compiler): void { $name = $this->getAttribute('name'); - $test = $compiler->getEnvironment()->getTest($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('name', $name); - $this->setAttribute('type', 'test'); - $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); } } - -class_alias('Twig\Node\Expression\TestExpression', 'Twig_Node_Expression_Test'); diff --git a/vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php b/vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php index 4896280..09f3d09 100644 --- a/vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php +++ b/vendor/twig/twig/src/Node/Expression/Unary/AbstractUnary.php @@ -16,21 +16,33 @@ use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Node; -abstract class AbstractUnary extends AbstractExpression +abstract class AbstractUnary extends AbstractExpression implements UnaryInterface { + /** + * @param AbstractExpression $node + */ public function __construct(Node $node, int $lineno) { - parent::__construct(['node' => $node], [], $lineno); + if (!$node instanceof AbstractExpression) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance argument to "%s" is deprecated ("%s" given).', AbstractExpression::class, static::class, $node::class); + } + + parent::__construct(['node' => $node], ['with_parentheses' => false], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { - $compiler->raw(' '); + if ($this->hasExplicitParentheses()) { + $compiler->raw('('); + } else { + $compiler->raw(' '); + } $this->operator($compiler); $compiler->subcompile($this->getNode('node')); + if ($this->hasExplicitParentheses()) { + $compiler->raw(')'); + } } - abstract public function operator(Compiler $compiler); + abstract public function operator(Compiler $compiler): Compiler; } - -class_alias('Twig\Node\Expression\Unary\AbstractUnary', 'Twig_Node_Expression_Unary'); diff --git a/vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php b/vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php index dfb6f54..dc2f235 100644 --- a/vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php +++ b/vendor/twig/twig/src/Node/Expression/Unary/NegUnary.php @@ -16,10 +16,8 @@ use Twig\Compiler; class NegUnary extends AbstractUnary { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { - $compiler->raw('-'); + return $compiler->raw('-'); } } - -class_alias('Twig\Node\Expression\Unary\NegUnary', 'Twig_Node_Expression_Unary_Neg'); diff --git a/vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php b/vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php index 7bdde96..55c11ba 100644 --- a/vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php +++ b/vendor/twig/twig/src/Node/Expression/Unary/NotUnary.php @@ -16,10 +16,8 @@ use Twig\Compiler; class NotUnary extends AbstractUnary { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { - $compiler->raw('!'); + return $compiler->raw('!'); } } - -class_alias('Twig\Node\Expression\Unary\NotUnary', 'Twig_Node_Expression_Unary_Not'); diff --git a/vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php b/vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php index 52d5d0c..4b0a062 100644 --- a/vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php +++ b/vendor/twig/twig/src/Node/Expression/Unary/PosUnary.php @@ -16,10 +16,8 @@ use Twig\Compiler; class PosUnary extends AbstractUnary { - public function operator(Compiler $compiler) + public function operator(Compiler $compiler): Compiler { - $compiler->raw('+'); + return $compiler->raw('+'); } } - -class_alias('Twig\Node\Expression\Unary\PosUnary', 'Twig_Node_Expression_Unary_Pos'); diff --git a/vendor/twig/twig/src/Node/Expression/Unary/SpreadUnary.php b/vendor/twig/twig/src/Node/Expression/Unary/SpreadUnary.php new file mode 100644 index 0000000..f99072c --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Unary/SpreadUnary.php @@ -0,0 +1,22 @@ +raw('...'); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Unary/StringCastUnary.php b/vendor/twig/twig/src/Node/Expression/Unary/StringCastUnary.php new file mode 100644 index 0000000..87ea17c --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Unary/StringCastUnary.php @@ -0,0 +1,22 @@ +raw('(string)'); + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Unary/UnaryInterface.php b/vendor/twig/twig/src/Node/Expression/Unary/UnaryInterface.php new file mode 100644 index 0000000..b094ef4 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Unary/UnaryInterface.php @@ -0,0 +1,22 @@ + $var], ['global' => $global], $var->getTemplateLine()); + } + + public function compile(Compiler $compiler): void + { + /** @var TemplateVariable $var */ + $var = $this->nodes['var']; + + $compiler + ->addDebugInfo($this) + ->write('$macros[') + ->string($var->getName($compiler)) + ->raw('] = ') + ; + + if ($this->getAttribute('global')) { + $compiler + ->raw('$this->macros[') + ->string($var->getName($compiler)) + ->raw('] = ') + ; + } + } +} diff --git a/vendor/twig/twig/src/Node/Expression/Variable/ContextVariable.php b/vendor/twig/twig/src/Node/Expression/Variable/ContextVariable.php new file mode 100644 index 0000000..01bbcb7 --- /dev/null +++ b/vendor/twig/twig/src/Node/Expression/Variable/ContextVariable.php @@ -0,0 +1,18 @@ +getAttribute('name')) { + $this->setAttribute('name', $compiler->getVarName()); + } + + return $this->getAttribute('name'); + } + + public function compile(Compiler $compiler): void + { + $name = $this->getName($compiler); + + if ('_self' === $name) { + $compiler->raw('$this'); + } else { + $compiler + ->raw('$macros[') + ->string($name) + ->raw(']') + ; + } + } +} diff --git a/vendor/twig/twig/src/Node/Expression/VariadicExpression.php b/vendor/twig/twig/src/Node/Expression/VariadicExpression.php index 3351e1a..a1bdb48 100644 --- a/vendor/twig/twig/src/Node/Expression/VariadicExpression.php +++ b/vendor/twig/twig/src/Node/Expression/VariadicExpression.php @@ -15,7 +15,7 @@ use Twig\Compiler; class VariadicExpression extends ArrayExpression { - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler->raw('...'); diff --git a/vendor/twig/twig/src/Node/FlushNode.php b/vendor/twig/twig/src/Node/FlushNode.php index b88f340..ff3bd1c 100644 --- a/vendor/twig/twig/src/Node/FlushNode.php +++ b/vendor/twig/twig/src/Node/FlushNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -18,20 +19,22 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[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) + 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"); } } - -class_alias('Twig\Node\FlushNode', 'Twig_Node_Flush'); diff --git a/vendor/twig/twig/src/Node/ForElseNode.php b/vendor/twig/twig/src/Node/ForElseNode.php new file mode 100644 index 0000000..56d6646 --- /dev/null +++ b/vendor/twig/twig/src/Node/ForElseNode.php @@ -0,0 +1,41 @@ + + */ +#[YieldReady] +class ForElseNode extends Node +{ + public function __construct(Node $body, int $lineno) + { + parent::__construct(['body' => $body], [], $lineno); + } + + public function compile(Compiler $compiler): void + { + $compiler + ->addDebugInfo($this) + ->write("if (!\$context['_iterated']) {\n") + ->indent() + ->subcompile($this->getNode('body')) + ->outdent() + ->write("}\n") + ; + } +} diff --git a/vendor/twig/twig/src/Node/ForLoopNode.php b/vendor/twig/twig/src/Node/ForLoopNode.php index 42aedd7..1f0a4f3 100644 --- a/vendor/twig/twig/src/Node/ForLoopNode.php +++ b/vendor/twig/twig/src/Node/ForLoopNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -18,14 +19,15 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[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) + public function compile(Compiler $compiler): void { if ($this->getAttribute('else')) { $compiler->write("\$context['_iterated'] = true;\n"); @@ -36,21 +38,14 @@ class ForLoopNode extends Node ->write("++\$context['loop']['index0'];\n") ->write("++\$context['loop']['index'];\n") ->write("\$context['loop']['first'] = false;\n") + ->write("if (isset(\$context['loop']['revindex0'], \$context['loop']['revindex'])) {\n") + ->indent() + ->write("--\$context['loop']['revindex0'];\n") + ->write("--\$context['loop']['revindex'];\n") + ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n") + ->outdent() + ->write("}\n") ; - - if (!$this->getAttribute('ifexpr')) { - $compiler - ->write("if (isset(\$context['loop']['length'])) {\n") - ->indent() - ->write("--\$context['loop']['revindex0'];\n") - ->write("--\$context['loop']['revindex'];\n") - ->write("\$context['loop']['last'] = 0 === \$context['loop']['revindex0'];\n") - ->outdent() - ->write("}\n") - ; - } } } } - -class_alias('Twig\Node\ForLoopNode', 'Twig_Node_ForLoop'); diff --git a/vendor/twig/twig/src/Node/ForNode.php b/vendor/twig/twig/src/Node/ForNode.php index 54afe93..2c86622 100644 --- a/vendor/twig/twig/src/Node/ForNode.php +++ b/vendor/twig/twig/src/Node/ForNode.php @@ -12,25 +12,33 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; -use Twig\Node\Expression\AssignNameExpression; +use Twig\Node\Expression\Variable\AssignContextVariable; /** * Represents a for node. * * @author Fabien Potencier */ +#[YieldReady] class ForNode extends Node { private $loop; - public function __construct(AssignNameExpression $keyTarget, AssignNameExpression $valueTarget, AbstractExpression $seq, AbstractExpression $ifexpr = null, Node $body, Node $else = null, int $lineno, string $tag = null) + public function __construct(AssignContextVariable $keyTarget, AssignContextVariable $valueTarget, AbstractExpression $seq, ?Node $ifexpr, Node $body, ?Node $else, int $lineno) { - $body = new Node([$body, $this->loop = new ForLoopNode($lineno, $tag)]); + $body = new Nodes([$body, $this->loop = new ForLoopNode($lineno)]); if (null !== $ifexpr) { - $body = new IfNode(new Node([$ifexpr, $body]), null, $lineno, $tag); + trigger_deprecation('twig/twig', '3.19', \sprintf('Passing not-null to the "ifexpr" argument of the "%s" constructor is deprecated.', static::class)); + } + + if (null !== $else && !$else instanceof ForElseNode) { + trigger_deprecation('twig/twig', '3.19', \sprintf('Not passing an instance of "%s" to the "else" argument of the "%s" constructor is deprecated.', ForElseNode::class, static::class)); + + $else = new ForElseNode($else, $else->getTemplateLine()); } $nodes = ['key_target' => $keyTarget, 'value_target' => $valueTarget, 'seq' => $seq, 'body' => $body]; @@ -38,15 +46,15 @@ class ForNode extends Node $nodes['else'] = $else; } - parent::__construct($nodes, ['with_loop' => true, 'ifexpr' => null !== $ifexpr], $lineno, $tag); + parent::__construct($nodes, ['with_loop' => true], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) ->write("\$context['_parent'] = \$context;\n") - ->write("\$context['_seq'] = twig_ensure_traversable(") + ->write("\$context['_seq'] = CoreExtension::ensureTraversable(") ->subcompile($this->getNode('seq')) ->raw(");\n") ; @@ -63,26 +71,20 @@ class ForNode extends Node ->write(" 'index' => 1,\n") ->write(" 'first' => true,\n") ->write("];\n") + ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof \Countable)) {\n") + ->indent() + ->write("\$length = count(\$context['_seq']);\n") + ->write("\$context['loop']['revindex0'] = \$length - 1;\n") + ->write("\$context['loop']['revindex'] = \$length;\n") + ->write("\$context['loop']['length'] = \$length;\n") + ->write("\$context['loop']['last'] = 1 === \$length;\n") + ->outdent() + ->write("}\n") ; - - if (!$this->getAttribute('ifexpr')) { - $compiler - ->write("if (is_array(\$context['_seq']) || (is_object(\$context['_seq']) && \$context['_seq'] instanceof \Countable)) {\n") - ->indent() - ->write("\$length = count(\$context['_seq']);\n") - ->write("\$context['loop']['revindex0'] = \$length - 1;\n") - ->write("\$context['loop']['revindex'] = \$length;\n") - ->write("\$context['loop']['length'] = \$length;\n") - ->write("\$context['loop']['last'] = 1 === \$length;\n") - ->outdent() - ->write("}\n") - ; - } } $this->loop->setAttribute('else', $this->hasNode('else')); $this->loop->setAttribute('with_loop', $this->getAttribute('with_loop')); - $this->loop->setAttribute('ifexpr', $this->getAttribute('ifexpr')); $compiler ->write("foreach (\$context['_seq'] as ") @@ -97,23 +99,22 @@ class ForNode extends Node ; if ($this->hasNode('else')) { - $compiler - ->write("if (!\$context['_iterated']) {\n") - ->indent() - ->subcompile($this->getNode('else')) - ->outdent() - ->write("}\n") - ; + $compiler->subcompile($this->getNode('else')); } $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[\''.$this->getNode('key_target')->getAttribute('name').'\'], $context[\''.$this->getNode('value_target')->getAttribute('name').'\'], $context[\'_parent\']'); + if ($this->hasNode('else')) { + $compiler->raw(', $context[\'_iterated\']'); + } + 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"); } } - -class_alias('Twig\Node\ForNode', 'Twig_Node_For'); diff --git a/vendor/twig/twig/src/Node/IfNode.php b/vendor/twig/twig/src/Node/IfNode.php index 814a6f3..2c0e2a8 100644 --- a/vendor/twig/twig/src/Node/IfNode.php +++ b/vendor/twig/twig/src/Node/IfNode.php @@ -12,26 +12,37 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; +use Twig\Node\Expression\ReturnPrimitiveTypeInterface; +use Twig\Node\Expression\Test\TrueTest; +use Twig\TwigTest; /** * Represents an if node. * * @author Fabien Potencier */ +#[YieldReady] class IfNode extends Node { - public function __construct(Node $tests, Node $else = null, int $lineno, string $tag = null) + public function __construct(Node $tests, ?Node $else, int $lineno) { + for ($i = 0, $count = \count($tests); $i < $count; $i += 2) { + $test = $tests->getNode((string) $i); + if (!$test instanceof ReturnPrimitiveTypeInterface) { + $tests->setNode($i, new TrueTest($test, new TwigTest('true'), null, $test->getTemplateLine())); + } + } $nodes = ['tests' => $tests]; if (null !== $else) { $nodes['else'] = $else; } - parent::__construct($nodes, [], $lineno, $tag); + parent::__construct($nodes, [], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler->addDebugInfo($this); for ($i = 0, $count = \count($this->getNode('tests')); $i < $count; $i += 2) { @@ -47,11 +58,14 @@ class IfNode extends Node } $compiler - ->subcompile($this->getNode('tests')->getNode($i)) + ->subcompile($this->getNode('tests')->getNode((string) $i)) ->raw(") {\n") ->indent() - ->subcompile($this->getNode('tests')->getNode($i + 1)) ; + // The node might not exists if the content is empty + if ($this->getNode('tests')->hasNode((string) ($i + 1))) { + $compiler->subcompile($this->getNode('tests')->getNode((string) ($i + 1))); + } } if ($this->hasNode('else')) { @@ -68,5 +82,3 @@ class IfNode extends Node ->write("}\n"); } } - -class_alias('Twig\Node\IfNode', 'Twig_Node_If'); diff --git a/vendor/twig/twig/src/Node/ImportNode.php b/vendor/twig/twig/src/Node/ImportNode.php index b661f43..92bdd5e 100644 --- a/vendor/twig/twig/src/Node/ImportNode.php +++ b/vendor/twig/twig/src/Node/ImportNode.php @@ -11,48 +11,46 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; -use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\AssignTemplateVariable; +use Twig\Node\Expression\Variable\ContextVariable; /** * Represents an import node. * * @author Fabien Potencier */ +#[YieldReady] class ImportNode extends Node { - public function __construct(AbstractExpression $expr, AbstractExpression $var, int $lineno, string $tag = null, bool $global = true) + public function __construct(AbstractExpression $expr, AbstractExpression|AssignTemplateVariable $var, int $lineno) { - parent::__construct(['expr' => $expr, 'var' => $var], ['global' => $global], $lineno, $tag); - } - - public function compile(Compiler $compiler) - { - $compiler - ->addDebugInfo($this) - ->write('$macros[') - ->repr($this->getNode('var')->getAttribute('name')) - ->raw('] = ') - ; - - if ($this->getAttribute('global')) { - $compiler - ->raw('$this->macros[') - ->repr($this->getNode('var')->getAttribute('name')) - ->raw('] = ') - ; + if (\func_num_args() > 3) { + trigger_deprecation('twig/twig', '3.15', \sprintf('Passing more than 3 arguments to "%s()" is deprecated.', __METHOD__)); } - if ($this->getNode('expr') instanceof NameExpression && '_self' === $this->getNode('expr')->getAttribute('name')) { + if (!$var instanceof AssignTemplateVariable) { + trigger_deprecation('twig/twig', '3.15', \sprintf('Passing a "%s" instance as the second argument of "%s" is deprecated, pass a "%s" instead.', $var::class, __CLASS__, AssignTemplateVariable::class)); + + $var = new AssignTemplateVariable($var->getAttribute('name'), $lineno); + } + + parent::__construct(['expr' => $expr, 'var' => $var], [], $lineno); + } + + public function compile(Compiler $compiler): void + { + $compiler->subcompile($this->getNode('var')); + + if ($this->getNode('expr') instanceof ContextVariable && '_self' === $this->getNode('expr')->getAttribute('name')) { $compiler->raw('$this'); } else { $compiler - ->raw('$this->loadTemplate(') + ->raw('$this->load(') ->subcompile($this->getNode('expr')) ->raw(', ') - ->repr($this->getTemplateName()) - ->raw(', ') ->repr($this->getTemplateLine()) ->raw(')->unwrap()') ; @@ -61,5 +59,3 @@ class ImportNode extends Node $compiler->raw(";\n"); } } - -class_alias('Twig\Node\ImportNode', 'Twig_Node_Import'); diff --git a/vendor/twig/twig/src/Node/IncludeNode.php b/vendor/twig/twig/src/Node/IncludeNode.php index d453030..6e17300 100644 --- a/vendor/twig/twig/src/Node/IncludeNode.php +++ b/vendor/twig/twig/src/Node/IncludeNode.php @@ -12,6 +12,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; @@ -20,19 +21,20 @@ use Twig\Node\Expression\AbstractExpression; * * @author Fabien Potencier */ +#[YieldReady] class IncludeNode extends Node implements NodeOutputInterface { - public function __construct(AbstractExpression $expr, AbstractExpression $variables = null, bool $only = false, bool $ignoreMissing = false, 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' => (bool) $only, 'ignore_missing' => (bool) $ignoreMissing], $lineno, $tag); + parent::__construct($nodes, ['only' => $only, 'ignore_missing' => $ignoreMissing], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler->addDebugInfo($this); @@ -40,13 +42,12 @@ class IncludeNode extends Node implements NodeOutputInterface $template = $compiler->getVarName(); $compiler - ->write(sprintf("$%s = null;\n", $template)) ->write("try {\n") ->indent() - ->write(sprintf('$%s = ', $template)) + ->write(\sprintf('$%s = ', $template)) ; - $this->addGetTemplate($compiler); + $this->addGetTemplate($compiler, $template); $compiler ->raw(";\n") @@ -54,12 +55,14 @@ class IncludeNode extends Node implements NodeOutputInterface ->write("} catch (LoaderError \$e) {\n") ->indent() ->write("// ignore missing template\n") + ->write(\sprintf("\$$template = null;\n", $template)) ->outdent() ->write("}\n") - ->write(sprintf("if ($%s) {\n", $template)) + ->write(\sprintf("if ($%s) {\n", $template)) ->indent() - ->write(sprintf('$%s->display(', $template)) + ->write(\sprintf('yield from $%s->unwrap()->yield(', $template)) ; + $this->addTemplateArguments($compiler); $compiler ->raw(");\n") @@ -67,42 +70,45 @@ class IncludeNode extends Node implements NodeOutputInterface ->write("}\n") ; } else { + $compiler->write('yield from '); $this->addGetTemplate($compiler); - $compiler->raw('->display('); + $compiler->raw('->unwrap()->yield('); $this->addTemplateArguments($compiler); $compiler->raw(");\n"); } } - protected function addGetTemplate(Compiler $compiler) + /** + * @return void + */ + protected function addGetTemplate(Compiler $compiler/* , string $template = '' */) { $compiler - ->write('$this->loadTemplate(') + ->raw('$this->load(') ->subcompile($this->getNode('expr')) ->raw(', ') - ->repr($this->getTemplateName()) - ->raw(', ') ->repr($this->getTemplateLine()) ->raw(')') ; } + /** + * @return void + */ protected function addTemplateArguments(Compiler $compiler) { if (!$this->hasNode('variables')) { $compiler->raw(false === $this->getAttribute('only') ? '$context' : '[]'); } elseif (false === $this->getAttribute('only')) { $compiler - ->raw('twig_array_merge($context, ') + ->raw('CoreExtension::merge($context, ') ->subcompile($this->getNode('variables')) ->raw(')') ; } else { - $compiler->raw('twig_to_array('); + $compiler->raw('CoreExtension::toArray('); $compiler->subcompile($this->getNode('variables')); $compiler->raw(')'); } } } - -class_alias('Twig\Node\IncludeNode', 'Twig_Node_Include'); diff --git a/vendor/twig/twig/src/Node/MacroNode.php b/vendor/twig/twig/src/Node/MacroNode.php index a133720..db3ca45 100644 --- a/vendor/twig/twig/src/Node/MacroNode.php +++ b/vendor/twig/twig/src/Node/MacroNode.php @@ -11,105 +11,111 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Error\SyntaxError; +use Twig\Node\Expression\ArrayExpression; +use Twig\Node\Expression\Variable\LocalVariable; /** * Represents a macro node. * * @author Fabien Potencier */ +#[YieldReady] class MacroNode extends Node { - const VARARGS_NAME = 'varargs'; + public const VARARGS_NAME = 'varargs'; - public function __construct(string $name, Node $body, Node $arguments, int $lineno, string $tag = null) + /** + * @param BodyNode $body + * @param ArrayExpression $arguments + */ + public function __construct(string $name, Node $body, Node $arguments, int $lineno) { - 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()); + 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 ("%s" given).', BodyNode::class, static::class, $body::class)); + } + + if (!$arguments instanceof ArrayExpression) { + trigger_deprecation('twig/twig', '3.15', \sprintf('Not passing a "%s" instance as the "arguments" argument of the "%s" constructor is deprecated ("%s" given).', ArrayExpression::class, static::class, $arguments::class)); + + $args = new ArrayExpression([], $arguments->getTemplateLine()); + foreach ($arguments as $n => $default) { + $args->addElement($default, new LocalVariable($n, $default->getTemplateLine())); + } + $arguments = $args; + } + + foreach ($arguments->getKeyValuePairs() as $pair) { + if ("\u{035C}".self::VARARGS_NAME === $pair['key']->getAttribute('name')) { + 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), $pair['value']->getTemplateLine(), $pair['value']->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) + public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) - ->write(sprintf('public function macro_%s(', $this->getAttribute('name'))) + ->write(\sprintf('public function macro_%s(', $this->getAttribute('name'))) ; - $count = \count($this->getNode('arguments')); - $pos = 0; - foreach ($this->getNode('arguments') as $name => $default) { + /** @var ArrayExpression $arguments */ + $arguments = $this->getNode('arguments'); + foreach ($arguments->getKeyValuePairs() as $pair) { + $name = $pair['key']; + $default = $pair['value']; $compiler - ->raw('$__'.$name.'__ = ') + ->subcompile($name) + ->raw(' = ') ->subcompile($default) + ->raw(', ') ; - - if (++$pos < $count) { - $compiler->raw(', '); - } - } - - if ($count) { - $compiler->raw(', '); } $compiler - ->raw('...$__varargs__') - ->raw(")\n") + ->raw('...$varargs') + ->raw("): string|Markup\n") ->write("{\n") ->indent() ->write("\$macros = \$this->macros;\n") - ->write("\$context = \$this->env->mergeGlobals([\n") + ->write("\$context = [\n") ->indent() ; - foreach ($this->getNode('arguments') as $name => $default) { + foreach ($arguments->getKeyValuePairs() as $pair) { + $name = $pair['key']; + $var = $name->getAttribute('name'); + if (str_starts_with($var, "\u{035C}")) { + $var = substr($var, \strlen("\u{035C}")); + } $compiler ->write('') - ->string($name) - ->raw(' => $__'.$name.'__') + ->string($var) + ->raw(' => ') + ->subcompile($name) ->raw(",\n") ; } + $node = new CaptureNode($this->getNode('body'), $this->getNode('body')->lineno); + $compiler ->write('') ->string(self::VARARGS_NAME) ->raw(' => ') - ; - - $compiler - ->raw("\$__varargs__,\n") + ->raw("\$varargs,\n") ->outdent() - ->write("]);\n\n") + ->write("] + \$this->env->getGlobals();\n\n") ->write("\$blocks = [];\n\n") - ; - if ($compiler->getEnvironment()->isDebug()) { - $compiler->write("ob_start();\n"); - } else { - $compiler->write("ob_start(function () { return ''; });\n"); - } - $compiler - ->write("try {\n") - ->indent() - ->subcompile($this->getNode('body')) + ->write('return ') + ->subcompile($node) ->raw("\n") - ->write("return ('' === \$tmp = ob_get_contents()) ? '' : new Markup(\$tmp, \$this->env->getCharset());\n") - ->outdent() - ->write("} finally {\n") - ->indent() - ->write("ob_end_clean();\n") - ->outdent() - ->write("}\n") ->outdent() ->write("}\n\n") ; } } - -class_alias('Twig\Node\MacroNode', 'Twig_Node_Macro'); diff --git a/vendor/twig/twig/src/Node/ModuleNode.php b/vendor/twig/twig/src/Node/ModuleNode.php index b23a342..71c5720 100644 --- a/vendor/twig/twig/src/Node/ModuleNode.php +++ b/vendor/twig/twig/src/Node/ModuleNode.php @@ -12,6 +12,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ConstantExpression; @@ -20,20 +21,31 @@ use Twig\Source; /** * 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 - * - * @final since Twig 2.4.0 */ -class ModuleNode extends Node +#[YieldReady] +final class ModuleNode extends Node { - public function __construct(Node $body, AbstractExpression $parent = null, Node $blocks, Node $macros, Node $traits, $embeddedTemplates, Source $source) + /** + * @param BodyNode $body + */ + public function __construct(Node $body, ?AbstractExpression $parent, Node $blocks, Node $macros, Node $traits, $embeddedTemplates, Source $source) { - if (__CLASS__ !== \get_class($this)) { - @trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', E_USER_DEPRECATED); + 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)); + } + if (!$embeddedTemplates instanceof Node) { + trigger_deprecation('twig/twig', '3.21', \sprintf('Not passing a "%s" instance as the "embedded_templates" argument of the "%s" constructor is deprecated.', Node::class, static::class)); + + if (null !== $embeddedTemplates) { + $embeddedTemplates = new Nodes($embeddedTemplates); + } else { + $embeddedTemplates = new EmptyNode(); + } } $nodes = [ @@ -41,11 +53,11 @@ class ModuleNode extends Node 'blocks' => $blocks, 'macros' => $macros, 'traits' => $traits, - 'display_start' => new Node(), - 'display_end' => new Node(), - 'constructor_start' => new Node(), - 'constructor_end' => new Node(), - 'class_end' => new Node(), + 'display_start' => new Nodes(), + 'display_end' => new Nodes(), + 'constructor_start' => new Nodes(), + 'constructor_end' => new Nodes(), + 'class_end' => new Nodes(), ]; if (null !== $parent) { $nodes['parent'] = $parent; @@ -61,12 +73,15 @@ class ModuleNode extends Node $this->setSourceContext($source); } + /** + * @return void + */ public function setIndex($index) { $this->setAttribute('index', $index); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $this->compileTemplate($compiler); @@ -75,6 +90,9 @@ class ModuleNode extends Node } } + /** + * @return void + */ protected function compileTemplate(Compiler $compiler) { if (!$this->getAttribute('index')) { @@ -104,6 +122,9 @@ class ModuleNode extends Node $this->compileClassFooter($compiler); } + /** + * @return void + */ protected function compileGetParent(Compiler $compiler) { if (!$this->hasNode('parent')) { @@ -112,7 +133,7 @@ class ModuleNode extends Node $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 ') @@ -122,11 +143,9 @@ class ModuleNode extends Node $compiler->subcompile($parent); } else { $compiler - ->raw('$this->loadTemplate(') + ->raw('$this->load(') ->subcompile($parent) ->raw(', ') - ->repr($this->getSourceContext()->getName()) - ->raw(', ') ->repr($parent->getTemplateLine()) ->raw(')') ; @@ -139,6 +158,9 @@ class ModuleNode extends Node ; } + /** + * @return void + */ protected function compileClassHeader(Compiler $compiler) { $compiler @@ -149,6 +171,7 @@ class ModuleNode extends Node ->write("use Twig\Environment;\n") ->write("use Twig\Error\LoaderError;\n") ->write("use Twig\Error\RuntimeError;\n") + ->write("use Twig\Extension\CoreExtension;\n") ->write("use Twig\Extension\SandboxExtension;\n") ->write("use Twig\Markup;\n") ->write("use Twig\Sandbox\SecurityError;\n") @@ -156,21 +179,29 @@ class ModuleNode extends Node ->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 // if the template name contains */, add a blank to avoid a PHP parse error ->write('/* '.str_replace('*/', '* /', $this->getSourceContext()->getName())." */\n") ->write('class '.$compiler->getEnvironment()->getTemplateClass($this->getSourceContext()->getName(), $this->getAttribute('index'))) - ->raw(sprintf(" extends %s\n", $compiler->getEnvironment()->getBaseTemplateClass(false))) + ->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") ; } + /** + * @return void + */ protected function compileConstructor(Compiler $compiler) { $compiler @@ -194,14 +225,12 @@ class ModuleNode extends Node $compiler ->addDebugInfo($node) - ->write(sprintf('$_trait_%s = $this->loadTemplate(', $i)) + ->write(\sprintf('$_trait_%s = $this->load(', $i)) ->subcompile($node) ->raw(', ') - ->repr($node->getTemplateName()) - ->raw(', ') ->repr($node->getTemplateLine()) ->raw(");\n") - ->write(sprintf("if (!\$_trait_%s->isTraitable()) {\n", $i)) + ->write(\sprintf("if (!\$_trait_%s->unwrap()->isTraitable()) {\n", $i)) ->indent() ->write("throw new RuntimeError('Template \"'.") ->subcompile($trait->getNode('template')) @@ -210,12 +239,12 @@ class ModuleNode extends Node ->raw(", \$this->source);\n") ->outdent() ->write("}\n") - ->write(sprintf("\$_trait_%s_blocks = \$_trait_%s->getBlocks();\n\n", $i, $i)) + ->write(\sprintf("\$_trait_%s_blocks = \$_trait_%s->unwrap()->getBlocks();\n\n", $i, $i)) ; foreach ($trait->getNode('targets') as $key => $value) { $compiler - ->write(sprintf('if (!isset($_trait_%s_blocks[', $i)) + ->write(\sprintf('if (!isset($_trait_%s_blocks[', $i)) ->string($key) ->raw("])) {\n") ->indent() @@ -229,13 +258,17 @@ class ModuleNode extends Node ->outdent() ->write("}\n\n") - ->write(sprintf('$_trait_%s_blocks[', $i)) + ->write(\sprintf('$_trait_%s_blocks[', $i)) ->subcompile($value) - ->raw(sprintf('] = $_trait_%s_blocks[', $i)) + ->raw(\sprintf('] = $_trait_%s_blocks[', $i)) ->string($key) - ->raw(sprintf(']; unset($_trait_%s_blocks[', $i)) + ->raw(\sprintf(']; unset($_trait_%s_blocks[', $i)) ->string($key) - ->raw("]);\n\n") + ->raw(']); $this->traitAliases[') + ->subcompile($value) + ->raw('] = ') + ->string($key) + ->raw(";\n\n") ; } } @@ -248,7 +281,7 @@ class ModuleNode extends Node for ($i = 0; $i < $countTraits; ++$i) { $compiler - ->write(sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ? '' : ',')."\n", $i)) + ->write(\sprintf('$_trait_%s_blocks'.($i == $countTraits - 1 ? '' : ',')."\n", $i)) ; } @@ -281,7 +314,7 @@ class ModuleNode extends Node foreach ($this->getNode('blocks') as $name => $node) { $compiler - ->write(sprintf("'%s' => [\$this, 'block_%s'],\n", $name, $name)) + ->write(\sprintf("'%s' => [\$this, 'block_%s'],\n", $name, $name)) ; } @@ -306,10 +339,13 @@ class ModuleNode extends Node ; } + /** + * @return void + */ protected function compileDisplay(Compiler $compiler) { $compiler - ->write("protected function doDisplay(array \$context, array \$blocks = [])\n", "{\n") + ->write("protected function doDisplay(array \$context, array \$blocks = []): iterable\n", "{\n") ->indent() ->write("\$macros = \$this->macros;\n") ->subcompile($this->getNode('display_start')) @@ -322,28 +358,38 @@ class ModuleNode extends Node $compiler->addDebugInfo($parent); if ($parent instanceof ConstantExpression) { $compiler - ->write('$this->parent = $this->loadTemplate(') + ->write('$this->parent = $this->load(') ->subcompile($parent) ->raw(', ') - ->repr($this->getSourceContext()->getName()) - ->raw(', ') ->repr($parent->getTemplateLine()) ->raw(");\n") ; - $compiler->write('$this->parent'); - } else { - $compiler->write('$this->getParent($context)'); } - $compiler->raw("->display(\$context, array_merge(\$this->blocks, \$blocks));\n"); + $compiler->write('yield from '); + + if ($parent instanceof ConstantExpression) { + $compiler->raw('$this->parent'); + } else { + $compiler->raw('$this->getParent($context)'); + } + $compiler->raw("->unwrap()->yield(\$context, array_merge(\$this->blocks, \$blocks));\n"); + } + + $compiler->subcompile($this->getNode('display_end')); + + if (!$this->hasNode('parent')) { + $compiler->write("yield from [];\n"); } $compiler - ->subcompile($this->getNode('display_end')) ->outdent() ->write("}\n\n") ; } + /** + * @return void + */ protected function compileClassFooter(Compiler $compiler) { $compiler @@ -353,15 +399,24 @@ class ModuleNode extends Node ; } + /** + * @return void + */ protected function compileMacros(Compiler $compiler) { $compiler->subcompile($this->getNode('macros')); } + /** + * @return void + */ protected function compileGetTemplateName(Compiler $compiler) { $compiler - ->write("public function getTemplateName()\n", "{\n") + ->write("/**\n") + ->write(" * @codeCoverageIgnore\n") + ->write(" */\n") + ->write("public function getTemplateName(): string\n", "{\n") ->indent() ->write('return ') ->repr($this->getSourceContext()->getName()) @@ -371,6 +426,9 @@ class ModuleNode extends Node ; } + /** + * @return void + */ protected function compileIsTraitable(Compiler $compiler) { // A template can be used as a trait if: @@ -383,13 +441,13 @@ class ModuleNode extends Node $traitable = !$this->hasNode('parent') && 0 === \count($this->getNode('macros')); if ($traitable) { if ($this->getNode('body') instanceof BodyNode) { - $nodes = $this->getNode('body')->getNode(0); + $nodes = $this->getNode('body')->getNode('0'); } else { $nodes = $this->getNode('body'); } if (!\count($nodes)) { - $nodes = new Node([$nodes]); + $nodes = new Nodes([$nodes]); } foreach ($nodes as $node) { @@ -397,14 +455,6 @@ class ModuleNode extends Node continue; } - if ($node instanceof TextNode && ctype_space($node->getAttribute('data'))) { - continue; - } - - if ($node instanceof BlockReferenceNode) { - continue; - } - $traitable = false; break; } @@ -415,29 +465,41 @@ class ModuleNode extends Node } $compiler - ->write("public function isTraitable()\n", "{\n") + ->write("/**\n") + ->write(" * @codeCoverageIgnore\n") + ->write(" */\n") + ->write("public function isTraitable(): bool\n", "{\n") ->indent() - ->write(sprintf("return %s;\n", $traitable ? 'true' : 'false')) + ->write("return false;\n") ->outdent() ->write("}\n\n") ; } + /** + * @return void + */ protected function compileDebugInfo(Compiler $compiler) { $compiler - ->write("public function getDebugInfo()\n", "{\n") + ->write("/**\n") + ->write(" * @codeCoverageIgnore\n") + ->write(" */\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)))) + ->write(\sprintf("return %s;\n", str_replace("\n", '', var_export(array_reverse($compiler->getDebugInfo(), true), true)))) ->outdent() ->write("}\n\n") ; } + /** + * @return void + */ 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() : '') @@ -450,23 +512,4 @@ class ModuleNode extends Node ->write("}\n") ; } - - protected function compileLoadTemplate(Compiler $compiler, $node, $var) - { - if ($node instanceof ConstantExpression) { - $compiler - ->write(sprintf('%s = $this->loadTemplate(', $var)) - ->subcompile($node) - ->raw(', ') - ->repr($node->getTemplateName()) - ->raw(', ') - ->repr($node->getTemplateLine()) - ->raw(");\n") - ; - } else { - throw new \LogicException('Trait templates can only be constant nodes.'); - } - } } - -class_alias('Twig\Node\ModuleNode', 'Twig_Node_Module'); diff --git a/vendor/twig/twig/src/Node/NameDeprecation.php b/vendor/twig/twig/src/Node/NameDeprecation.php new file mode 100644 index 0000000..63ab285 --- /dev/null +++ b/vendor/twig/twig/src/Node/NameDeprecation.php @@ -0,0 +1,46 @@ + + */ +class NameDeprecation +{ + private $package; + private $version; + private $newName; + + public function __construct(string $package = '', string $version = '', string $newName = '') + { + $this->package = $package; + $this->version = $version; + $this->newName = $newName; + } + + public function getPackage(): string + { + return $this->package; + } + + public function getVersion(): string + { + return $this->version; + } + + public function getNewName(): string + { + return $this->newName; + } +} diff --git a/vendor/twig/twig/src/Node/Node.php b/vendor/twig/twig/src/Node/Node.php index 909a687..dcf912c 100644 --- a/vendor/twig/twig/src/Node/Node.php +++ b/vendor/twig/twig/src/Node/Node.php @@ -12,6 +12,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Source; @@ -19,193 +20,275 @@ use Twig\Source; * Represents a node in the AST. * * @author Fabien Potencier + * + * @implements \IteratorAggregate */ +#[YieldReady] class Node implements \Countable, \IteratorAggregate { + /** + * @var array + */ protected $nodes; protected $attributes; protected $lineno; protected $tag; - private $name; private $sourceContext; + /** @var array */ + private $nodeNameDeprecations = []; + /** @var array */ + 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) { + if (self::class === static::class) { + trigger_deprecation('twig/twig', '3.15', \sprintf('Instantiating "%s" directly is deprecated; the class will become abstract in 4.0.', self::class)); + } + foreach ($nodes as $name => $node) { if (!$node instanceof self) { - throw new \InvalidArgumentException(sprintf('Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a \Twig\Node\Node instance.', \is_object($node) ? \get_class($node) : (null === $node ? 'null' : \gettype($node)), $name, \get_class($this))); + throw new \InvalidArgumentException(\sprintf('Using "%s" for the value of node "%s" of "%s" is not supported. You must pass a \Twig\Node\Node instance.', get_debug_type($node), $name, static::class)); } } $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() + public function __toString(): string { - $attributes = []; - foreach ($this->attributes as $name => $value) { - $attributes[] = sprintf('%s: %s', $name, str_replace("\n", '', var_export($value, true))); + $repr = static::class; + + if ($this->tag) { + $repr .= \sprintf("\n tag: %s", $this->tag); } - $repr = [\get_class($this).'('.implode(', ', $attributes)]; + $attributes = []; + foreach ($this->attributes as $name => $value) { + 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); + } + + 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; } + public function __clone() + { + foreach ($this->nodes as $name => $node) { + $this->nodes[$name] = clone $node; + } + } + + /** + * @return void + */ public function compile(Compiler $compiler) { foreach ($this->nodes as $node) { - $node->compile($compiler); + $compiler->subcompile($node); } } - public function getTemplateLine() + public function getTemplateLine(): int { return $this->lineno; } - public function getNodeTag() + public function getNodeTag(): ?string { return $this->tag; } /** - * @return bool + * @internal */ - public function hasAttribute($name) + 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); } - /** - * @return mixed - */ - public function getAttribute($name) + public function getAttribute(string $name) { if (!\array_key_exists($name, $this->attributes)) { - throw new \LogicException(sprintf('Attribute "%s" does not exist for Node "%s".', $name, \get_class($this))); + throw new \LogicException(\sprintf('Attribute "%s" does not exist for Node "%s".', $name, static::class)); + } + + $triggerDeprecation = \func_num_args() > 1 ? func_get_arg(1) : true; + if ($triggerDeprecation && isset($this->attributeNameDeprecations[$name])) { + $dep = $this->attributeNameDeprecations[$name]; + if ($dep->getNewName()) { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Getting attribute "%s" on a "%s" class is deprecated, get the "%s" attribute instead.', $name, static::class, $dep->getNewName()); + } else { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Getting attribute "%s" on a "%s" class is deprecated.', $name, static::class); + } } return $this->attributes[$name]; } - /** - * @param string $name - * @param mixed $value - */ - public function setAttribute($name, $value) + public function setAttribute(string $name, $value): void { + $triggerDeprecation = \func_num_args() > 2 ? func_get_arg(2) : true; + if ($triggerDeprecation && isset($this->attributeNameDeprecations[$name])) { + $dep = $this->attributeNameDeprecations[$name]; + if ($dep->getNewName()) { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Setting attribute "%s" on a "%s" class is deprecated, set the "%s" attribute instead.', $name, static::class, $dep->getNewName()); + } else { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Setting attribute "%s" on a "%s" class is deprecated.', $name, static::class); + } + } + $this->attributes[$name] = $value; } - public function removeAttribute($name) + public function deprecateAttribute(string $name, NameDeprecation $dep): void + { + $this->attributeNameDeprecations[$name] = $dep; + } + + public function removeAttribute(string $name): void { unset($this->attributes[$name]); } /** - * @return bool + * @param string|int $name */ - public function hasNode($name) + public function hasNode(string $name): bool { return isset($this->nodes[$name]); } /** - * @return Node + * @param string|int $name */ - public function getNode($name) + public function getNode(string $name): self { if (!isset($this->nodes[$name])) { - throw new \LogicException(sprintf('Node "%s" does not exist for Node "%s".', $name, \get_class($this))); + throw new \LogicException(\sprintf('Node "%s" does not exist for Node "%s".', $name, static::class)); + } + + $triggerDeprecation = \func_num_args() > 1 ? func_get_arg(1) : true; + if ($triggerDeprecation && isset($this->nodeNameDeprecations[$name])) { + $dep = $this->nodeNameDeprecations[$name]; + if ($dep->getNewName()) { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Getting node "%s" on a "%s" class is deprecated, get the "%s" node instead.', $name, static::class, $dep->getNewName()); + } else { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Getting node "%s" on a "%s" class is deprecated.', $name, static::class); + } } return $this->nodes[$name]; } - public function setNode($name, self $node) + /** + * @param string|int $name + */ + public function setNode(string $name, self $node): void { + $triggerDeprecation = \func_num_args() > 2 ? func_get_arg(2) : true; + if ($triggerDeprecation && isset($this->nodeNameDeprecations[$name])) { + $dep = $this->nodeNameDeprecations[$name]; + if ($dep->getNewName()) { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Setting node "%s" on a "%s" class is deprecated, set the "%s" node instead.', $name, static::class, $dep->getNewName()); + } else { + trigger_deprecation($dep->getPackage(), $dep->getVersion(), 'Setting node "%s" on a "%s" class is deprecated.', $name, static::class); + } + } + + if (null !== $this->sourceContext) { + $node->setSourceContext($this->sourceContext); + } $this->nodes[$name] = $node; } - public function removeNode($name) + /** + * @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; + } + + /** + * @return int + */ + #[\ReturnTypeWillChange] public function count() { return \count($this->nodes); } - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->nodes); } - /** - * @deprecated since 2.8 (to be removed in 3.0) - */ - public function setTemplateName($name/*, $triggerDeprecation = true */) - { - $triggerDeprecation = 2 > \func_num_args() || \func_get_arg(1); - if ($triggerDeprecation) { - @trigger_error('The '.__METHOD__.' method is deprecated since version 2.8 and will be removed in 3.0. Use setSourceContext() instead.', E_USER_DEPRECATED); - } - - $this->name = $name; - foreach ($this->nodes as $node) { - $node->setTemplateName($name, $triggerDeprecation); - } - } - - public function getTemplateName() + public function getTemplateName(): ?string { return $this->sourceContext ? $this->sourceContext->getName() : null; } - public function setSourceContext(Source $source) + public function setSourceContext(Source $source): void { $this->sourceContext = $source; foreach ($this->nodes as $node) { $node->setSourceContext($source); } - - $this->setTemplateName($source->getName(), false); } - public function getSourceContext() + public function getSourceContext(): ?Source { return $this->sourceContext; } } - -class_alias('Twig\Node\Node', 'Twig_Node'); - -// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name. -class_exists('Twig\Compiler'); diff --git a/vendor/twig/twig/src/Node/NodeCaptureInterface.php b/vendor/twig/twig/src/Node/NodeCaptureInterface.php index 474003f..9fb6a0c 100644 --- a/vendor/twig/twig/src/Node/NodeCaptureInterface.php +++ b/vendor/twig/twig/src/Node/NodeCaptureInterface.php @@ -19,5 +19,3 @@ namespace Twig\Node; interface NodeCaptureInterface { } - -class_alias('Twig\Node\NodeCaptureInterface', 'Twig_NodeCaptureInterface'); diff --git a/vendor/twig/twig/src/Node/NodeOutputInterface.php b/vendor/twig/twig/src/Node/NodeOutputInterface.php index 8b046ee..5e35b40 100644 --- a/vendor/twig/twig/src/Node/NodeOutputInterface.php +++ b/vendor/twig/twig/src/Node/NodeOutputInterface.php @@ -19,5 +19,3 @@ namespace Twig\Node; interface NodeOutputInterface { } - -class_alias('Twig\Node\NodeOutputInterface', 'Twig_NodeOutputInterface'); diff --git a/vendor/twig/twig/src/Node/Nodes.php b/vendor/twig/twig/src/Node/Nodes.php new file mode 100644 index 0000000..bd67053 --- /dev/null +++ b/vendor/twig/twig/src/Node/Nodes.php @@ -0,0 +1,28 @@ + + */ +#[YieldReady] +final class Nodes extends Node +{ + public function __construct(array $nodes = [], int $lineno = 0) + { + parent::__construct($nodes, [], $lineno); + } +} diff --git a/vendor/twig/twig/src/Node/PrintNode.php b/vendor/twig/twig/src/Node/PrintNode.php index fcc086a..e3c23bb 100644 --- a/vendor/twig/twig/src/Node/PrintNode.php +++ b/vendor/twig/twig/src/Node/PrintNode.php @@ -12,6 +12,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\AbstractExpression; @@ -20,22 +21,24 @@ use Twig\Node\Expression\AbstractExpression; * * @author Fabien Potencier */ +#[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) + public function compile(Compiler $compiler): void { + /** @var AbstractExpression */ + $expr = $this->getNode('expr'); + $compiler ->addDebugInfo($this) - ->write('echo ') - ->subcompile($this->getNode('expr')) + ->write($expr->isGenerator() ? 'yield from ' : 'yield ') + ->subcompile($expr) ->raw(";\n") ; } } - -class_alias('Twig\Node\PrintNode', 'Twig_Node_Print'); diff --git a/vendor/twig/twig/src/Node/SandboxNode.php b/vendor/twig/twig/src/Node/SandboxNode.php index fe59313..d51cea4 100644 --- a/vendor/twig/twig/src/Node/SandboxNode.php +++ b/vendor/twig/twig/src/Node/SandboxNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -18,14 +19,15 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[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) + public function compile(Compiler $compiler): void { $compiler ->addDebugInfo($this) @@ -34,14 +36,19 @@ class SandboxNode extends Node ->write("\$this->sandbox->enableSandbox();\n") ->outdent() ->write("}\n") + ->write("try {\n") + ->indent() ->subcompile($this->getNode('body')) + ->outdent() + ->write("} finally {\n") + ->indent() ->write("if (!\$alreadySandboxed) {\n") ->indent() ->write("\$this->sandbox->disableSandbox();\n") ->outdent() ->write("}\n") + ->outdent() + ->write("}\n") ; } } - -class_alias('Twig\Node\SandboxNode', 'Twig_Node_Sandbox'); diff --git a/vendor/twig/twig/src/Node/SandboxedPrintNode.php b/vendor/twig/twig/src/Node/SandboxedPrintNode.php deleted file mode 100644 index 54e92e6..0000000 --- a/vendor/twig/twig/src/Node/SandboxedPrintNode.php +++ /dev/null @@ -1,54 +0,0 @@ - - */ -class SandboxedPrintNode extends PrintNode -{ - public function compile(Compiler $compiler) - { - $compiler - ->addDebugInfo($this) - ->write('echo ') - ; - $expr = $this->getNode('expr'); - if ($expr instanceof ConstantExpression) { - $compiler - ->subcompile($expr) - ->raw(";\n") - ; - } else { - $compiler - ->write('$this->extensions[SandboxExtension::class]->ensureToStringAllowed(') - ->subcompile($expr) - ->raw(', ') - ->repr($expr->getTemplateLine()) - ->raw(", \$this->source);\n") - ; - } - } -} - -class_alias('Twig\Node\SandboxedPrintNode', 'Twig_Node_SandboxedPrint'); diff --git a/vendor/twig/twig/src/Node/SetNode.php b/vendor/twig/twig/src/Node/SetNode.php index 3cf4615..7b063b0 100644 --- a/vendor/twig/twig/src/Node/SetNode.php +++ b/vendor/twig/twig/src/Node/SetNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Expression\ConstantExpression; @@ -19,34 +20,43 @@ use Twig\Node\Expression\ConstantExpression; * * @author Fabien Potencier */ +#[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) { - parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => false], $lineno, $tag); - /* * Optimizes the node when capture is used for a large block of text. * * {% set foo %}foo{% endset %} is compiled to $context['foo'] = new Twig\Markup("foo"); */ - if ($this->getAttribute('capture')) { - $this->setAttribute('safe', true); - - $values = $this->getNode('values'); - if ($values instanceof TextNode) { - $this->setNode('values', new ConstantExpression($values->getAttribute('data'), $values->getTemplateLine())); - $this->setAttribute('capture', false); + $safe = false; + if ($capture) { + $safe = true; + // Node::class === get_class($values) should be removed in Twig 4.0 + if (($values instanceof Nodes || Node::class === $values::class) && !\count($values)) { + $values = new ConstantExpression('', $values->getTemplateLine()); + $capture = false; + } elseif ($values instanceof TextNode) { + $values = new ConstantExpression($values->getAttribute('data'), $values->getTemplateLine()); + $capture = false; + } elseif ($values instanceof PrintNode && $values->getNode('expr') instanceof ConstantExpression) { + $values = $values->getNode('expr'); + $capture = false; + } else { + $values = new CaptureNode($values, $values->getTemplateLine()); } } + + parent::__construct(['names' => $names, 'values' => $values], ['capture' => $capture, 'safe' => $safe], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler->addDebugInfo($this); if (\count($this->getNode('names')) > 1) { - $compiler->write('list('); + $compiler->write('['); foreach ($this->getNode('names') as $idx => $node) { if ($idx) { $compiler->raw(', '); @@ -54,29 +64,15 @@ class SetNode extends Node implements NodeCaptureInterface $compiler->subcompile($node); } - $compiler->raw(')'); + $compiler->raw(']'); } else { - if ($this->getAttribute('capture')) { - if ($compiler->getEnvironment()->isDebug()) { - $compiler->write("ob_start();\n"); - } else { - $compiler->write("ob_start(function () { return ''; });\n"); - } - $compiler - ->subcompile($this->getNode('values')) - ; - } - $compiler->subcompile($this->getNode('names'), false); - - if ($this->getAttribute('capture')) { - $compiler->raw(" = ('' === \$tmp = ob_get_clean()) ? '' : new Markup(\$tmp, \$this->env->getCharset())"); - } } + $compiler->raw(' = '); - if (!$this->getAttribute('capture')) { - $compiler->raw(' = '); - + if ($this->getAttribute('capture')) { + $compiler->subcompile($this->getNode('values')); + } else { if (\count($this->getNode('names')) > 1) { $compiler->write('['); foreach ($this->getNode('values') as $idx => $value) { @@ -89,19 +85,31 @@ class SetNode extends Node implements NodeCaptureInterface $compiler->raw(']'); } else { if ($this->getAttribute('safe')) { - $compiler - ->raw("('' === \$tmp = ") - ->subcompile($this->getNode('values')) - ->raw(") ? '' : new Markup(\$tmp, \$this->env->getCharset())") - ; + if ($this->getNode('values') instanceof ConstantExpression) { + if ('' === $this->getNode('values')->getAttribute('value')) { + $compiler->raw('""'); + } else { + $compiler + ->raw('new Markup(') + ->subcompile($this->getNode('values')) + ->raw(', $this->env->getCharset())') + ; + } + } else { + $compiler + ->raw("('' === \$tmp = ") + ->subcompile($this->getNode('values')) + ->raw(") ? '' : new Markup(\$tmp, \$this->env->getCharset())") + ; + } } else { $compiler->subcompile($this->getNode('values')); } } + + $compiler->raw(';'); } - $compiler->raw(";\n"); + $compiler->raw("\n"); } } - -class_alias('Twig\Node\SetNode', 'Twig_Node_Set'); diff --git a/vendor/twig/twig/src/Node/SpacelessNode.php b/vendor/twig/twig/src/Node/SpacelessNode.php deleted file mode 100644 index 8fc4a2d..0000000 --- a/vendor/twig/twig/src/Node/SpacelessNode.php +++ /dev/null @@ -1,49 +0,0 @@ - - */ -class SpacelessNode extends Node implements NodeOutputInterface -{ - public function __construct(Node $body, int $lineno, string $tag = 'spaceless') - { - parent::__construct(['body' => $body], [], $lineno, $tag); - } - - public function compile(Compiler $compiler) - { - $compiler - ->addDebugInfo($this) - ; - if ($compiler->getEnvironment()->isDebug()) { - $compiler->write("ob_start();\n"); - } else { - $compiler->write("ob_start(function () { return ''; });\n"); - } - $compiler - ->subcompile($this->getNode('body')) - ->write("echo trim(preg_replace('/>\s+<', ob_get_clean()));\n") - ; - } -} - -class_alias('Twig\Node\SpacelessNode', 'Twig_Node_Spaceless'); diff --git a/vendor/twig/twig/src/Node/TextNode.php b/vendor/twig/twig/src/Node/TextNode.php index 85640a5..fae65fb 100644 --- a/vendor/twig/twig/src/Node/TextNode.php +++ b/vendor/twig/twig/src/Node/TextNode.php @@ -12,6 +12,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -19,6 +20,7 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[YieldReady] class TextNode extends Node implements NodeOutputInterface { public function __construct(string $data, int $lineno) @@ -26,15 +28,14 @@ class TextNode extends Node implements NodeOutputInterface parent::__construct([], ['data' => $data], $lineno); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { + $compiler->addDebugInfo($this); + $compiler - ->addDebugInfo($this) - ->write('echo ') + ->write('yield ') ->string($this->getAttribute('data')) ->raw(";\n") ; } } - -class_alias('Twig\Node\TextNode', 'Twig_Node_Text'); diff --git a/vendor/twig/twig/src/Node/TypesNode.php b/vendor/twig/twig/src/Node/TypesNode.php new file mode 100644 index 0000000..b594984 --- /dev/null +++ b/vendor/twig/twig/src/Node/TypesNode.php @@ -0,0 +1,31 @@ + + */ +#[YieldReady] +class TypesNode extends Node +{ + /** + * @param array $types + */ + public function __construct(array $types, int $lineno) + { + parent::__construct([], ['mapping' => $types], $lineno); + } + + /** + * @return void + */ + public function compile(Compiler $compiler) + { + // Don't compile anything. + } +} diff --git a/vendor/twig/twig/src/Node/WithNode.php b/vendor/twig/twig/src/Node/WithNode.php index 74d1ea0..487e280 100644 --- a/vendor/twig/twig/src/Node/WithNode.php +++ b/vendor/twig/twig/src/Node/WithNode.php @@ -11,6 +11,7 @@ namespace Twig\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; /** @@ -18,55 +19,54 @@ use Twig\Compiler; * * @author Fabien Potencier */ +#[YieldReady] class WithNode extends Node { - public function __construct(Node $body, Node $variables = null, bool $only = false, 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) + public function compile(Compiler $compiler): void { $compiler->addDebugInfo($this); + $parentContextName = $compiler->getVarName(); + + $compiler->write(\sprintf("\$%s = \$context;\n", $parentContextName)); + if ($this->hasNode('variables')) { $node = $this->getNode('variables'); $varsName = $compiler->getVarName(); $compiler - ->write(sprintf('$%s = ', $varsName)) + ->write(\sprintf('$%s = ', $varsName)) ->subcompile($node) ->raw(";\n") - ->write(sprintf("if (!twig_test_iterable(\$%s)) {\n", $varsName)) + ->write(\sprintf("if (!is_iterable(\$%s)) {\n", $varsName)) ->indent() - ->write("throw new RuntimeError('Variables passed to the \"with\" tag must be a hash.', ") + ->write("throw new RuntimeError('Variables passed to the \"with\" tag must be a mapping.', ") ->repr($node->getTemplateLine()) ->raw(", \$this->getSourceContext());\n") ->outdent() ->write("}\n") - ->write(sprintf("\$%s = twig_to_array(\$%s);\n", $varsName, $varsName)) + ->write(\sprintf("\$%s = CoreExtension::toArray(\$%s);\n", $varsName, $varsName)) ; if ($this->getAttribute('only')) { - $compiler->write("\$context = ['_parent' => \$context];\n"); - } else { - $compiler->write("\$context['_parent'] = \$context;\n"); + $compiler->write("\$context = [];\n"); } - $compiler->write(sprintf("\$context = \$this->env->mergeGlobals(array_merge(\$context, \$%s));\n", $varsName)); - } else { - $compiler->write("\$context['_parent'] = \$context;\n"); + $compiler->write(\sprintf("\$context = \$%s + \$context + \$this->env->getGlobals();\n", $varsName)); } $compiler ->subcompile($this->getNode('body')) - ->write("\$context = \$context['_parent'];\n") + ->write(\sprintf("\$context = \$%s;\n", $parentContextName)) ; } } - -class_alias('Twig\Node\WithNode', 'Twig_Node_With'); diff --git a/vendor/twig/twig/src/NodeTraverser.php b/vendor/twig/twig/src/NodeTraverser.php index 625b049..47a2d5c 100644 --- a/vendor/twig/twig/src/NodeTraverser.php +++ b/vendor/twig/twig/src/NodeTraverser.php @@ -37,7 +37,7 @@ final class NodeTraverser } } - public function addVisitor(NodeVisitorInterface $visitor) + public function addVisitor(NodeVisitorInterface $visitor): void { $this->visitors[$visitor->getPriority()][] = $visitor; } @@ -57,23 +57,16 @@ final class NodeTraverser return $node; } - /** - * @return Node|null - */ - private function traverseForVisitor(NodeVisitorInterface $visitor, Node $node) + private function traverseForVisitor(NodeVisitorInterface $visitor, Node $node): ?Node { $node = $visitor->enterNode($node, $this->env); foreach ($node as $k => $n) { - if (false !== ($m = $this->traverseForVisitor($visitor, $n)) && null !== $m) { + if (null !== $m = $this->traverseForVisitor($visitor, $n)) { if ($m !== $n) { $node->setNode($k, $m); } } else { - if (false === $m) { - @trigger_error('Returning "false" to remove a Node from NodeVisitorInterface::leaveNode() is deprecated since Twig version 2.9; return "null" instead.', E_USER_DEPRECATED); - } - $node->removeNode($k); } } @@ -81,5 +74,3 @@ final class NodeTraverser return $visitor->leaveNode($node, $this->env); } } - -class_alias('Twig\NodeTraverser', 'Twig_NodeTraverser'); diff --git a/vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php index 9c6cb12..38b1ec9 100644 --- a/vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php +++ b/vendor/twig/twig/src/NodeVisitor/AbstractNodeVisitor.php @@ -17,18 +17,18 @@ use Twig\Node\Node; /** * Used to make node visitors compatible with Twig 1.x and 2.x. * - * To be removed in Twig 3.1. - * * @author Fabien Potencier + * + * @deprecated since Twig 3.9 (to be removed in 4.0) */ abstract class AbstractNodeVisitor implements NodeVisitorInterface { - final public function enterNode(Node $node, Environment $env) + final public function enterNode(Node $node, Environment $env): Node { return $this->doEnterNode($node, $env); } - final public function leaveNode(Node $node, Environment $env) + final public function leaveNode(Node $node, Environment $env): ?Node { return $this->doLeaveNode($node, $env); } @@ -47,5 +47,3 @@ abstract class AbstractNodeVisitor implements NodeVisitorInterface */ abstract protected function doLeaveNode(Node $node, Environment $env); } - -class_alias('Twig\NodeVisitor\AbstractNodeVisitor', 'Twig_BaseNodeVisitor'); diff --git a/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php index bfbfdc9..a9f8297 100644 --- a/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php +++ b/vendor/twig/twig/src/NodeVisitor/EscaperNodeVisitor.php @@ -16,21 +16,23 @@ use Twig\Extension\EscaperExtension; use Twig\Node\AutoEscapeNode; use Twig\Node\BlockNode; use Twig\Node\BlockReferenceNode; -use Twig\Node\DoNode; -use Twig\Node\Expression\ConditionalExpression; +use Twig\Node\Expression\AbstractExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; -use Twig\Node\Expression\InlinePrint; +use Twig\Node\Expression\OperatorEscapeInterface; use Twig\Node\ImportNode; use Twig\Node\ModuleNode; use Twig\Node\Node; +use Twig\Node\Nodes; use Twig\Node\PrintNode; use Twig\NodeTraverser; /** * @author Fabien Potencier + * + * @internal */ -final class EscaperNodeVisitor extends AbstractNodeVisitor +final class EscaperNodeVisitor implements NodeVisitorInterface { private $statusStack = []; private $blocks = []; @@ -44,7 +46,7 @@ final class EscaperNodeVisitor extends AbstractNodeVisitor $this->safeAnalysis = new SafeAnalysisNodeVisitor(); } - protected function doEnterNode(Node $node, Environment $env) + public function enterNode(Node $node, Environment $env): Node { if ($node instanceof ModuleNode) { if ($env->hasExtension(EscaperExtension::class) && $defaultStrategy = $env->getExtension(EscaperExtension::class)->getDefaultStrategy($node->getTemplateName())) { @@ -55,15 +57,15 @@ final class EscaperNodeVisitor extends AbstractNodeVisitor } elseif ($node instanceof AutoEscapeNode) { $this->statusStack[] = $node->getAttribute('value'); } elseif ($node instanceof BlockNode) { - $this->statusStack[] = isset($this->blocks[$node->getAttribute('name')]) ? $this->blocks[$node->getAttribute('name')] : $this->needEscaping($env); + $this->statusStack[] = $this->blocks[$node->getAttribute('name')] ?? $this->needEscaping(); } elseif ($node instanceof ImportNode) { - $this->safeVars[] = $node->getNode('var')->getAttribute('name'); + $this->safeVars[] = $node->getNode('var')->getNode('var')->getAttribute('name'); } return $node; } - protected function doLeaveNode(Node $node, Environment $env) + public function leaveNode(Node $node, Environment $env): ?Node { if ($node instanceof ModuleNode) { $this->defaultStrategy = false; @@ -71,103 +73,77 @@ final class EscaperNodeVisitor extends AbstractNodeVisitor $this->blocks = []; } elseif ($node instanceof FilterExpression) { return $this->preEscapeFilterNode($node, $env); - } elseif ($node instanceof PrintNode && false !== $type = $this->needEscaping($env)) { + } elseif ($node instanceof PrintNode && false !== $type = $this->needEscaping()) { $expression = $node->getNode('expr'); - if ($expression instanceof ConditionalExpression && $this->shouldUnwrapConditional($expression, $env, $type)) { - return new DoNode($this->unwrapConditional($expression, $env, $type), $expression->getTemplateLine()); + if ($expression instanceof OperatorEscapeInterface) { + $this->escapeConditional($expression, $env, $type); + } else { + $node->setNode('expr', $this->escapeExpression($expression, $env, $type)); } - return $this->escapePrintNode($node, $env, $type); + return $node; } if ($node instanceof AutoEscapeNode || $node instanceof BlockNode) { array_pop($this->statusStack); } elseif ($node instanceof BlockReferenceNode) { - $this->blocks[$node->getAttribute('name')] = $this->needEscaping($env); + $this->blocks[$node->getAttribute('name')] = $this->needEscaping(); } return $node; } - private function shouldUnwrapConditional(ConditionalExpression $expression, Environment $env, $type) + /** + * @param AbstractExpression&OperatorEscapeInterface $expression + */ + private function escapeConditional($expression, Environment $env, string $type): void { - $expr2Safe = $this->isSafeFor($type, $expression->getNode('expr2'), $env); - $expr3Safe = $this->isSafeFor($type, $expression->getNode('expr3'), $env); - - return $expr2Safe !== $expr3Safe; + foreach ($expression->getOperandNamesToEscape() as $name) { + /** @var AbstractExpression $operand */ + $operand = $expression->getNode($name); + if ($operand instanceof OperatorEscapeInterface) { + $this->escapeConditional($operand, $env, $type); + } else { + $expression->setNode($name, $this->escapeExpression($operand, $env, $type)); + } + } } - private function unwrapConditional(ConditionalExpression $expression, Environment $env, $type) + private function escapeExpression(AbstractExpression $expression, Environment $env, string $type): AbstractExpression { - // convert "echo a ? b : c" to "a ? echo b : echo c" recursively - $expr2 = $expression->getNode('expr2'); - if ($expr2 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr2, $env, $type)) { - $expr2 = $this->unwrapConditional($expr2, $env, $type); + return $this->isSafeFor($type, $expression, $env) ? $expression : $this->getEscaperFilter($env, $type, $expression); + } + + private function preEscapeFilterNode(FilterExpression $filter, Environment $env): FilterExpression + { + if ($filter->hasAttribute('twig_callable')) { + $type = $filter->getAttribute('twig_callable')->getPreEscape(); } else { - $expr2 = $this->escapeInlinePrintNode(new InlinePrint($expr2, $expr2->getTemplateLine()), $env, $type); - } - $expr3 = $expression->getNode('expr3'); - if ($expr3 instanceof ConditionalExpression && $this->shouldUnwrapConditional($expr3, $env, $type)) { - $expr3 = $this->unwrapConditional($expr3, $env, $type); - } else { - $expr3 = $this->escapeInlinePrintNode(new InlinePrint($expr3, $expr3->getTemplateLine()), $env, $type); + // legacy + $name = $filter->getNode('filter', false)->getAttribute('value'); + $type = $env->getFilter($name)->getPreEscape(); } - return new ConditionalExpression($expression->getNode('expr1'), $expr2, $expr3, $expression->getTemplateLine()); - } - - private function escapeInlinePrintNode(InlinePrint $node, Environment $env, $type) - { - $expression = $node->getNode('node'); - - if ($this->isSafeFor($type, $expression, $env)) { - return $node; - } - - return new InlinePrint($this->getEscaperFilter($type, $expression), $node->getTemplateLine()); - } - - private function escapePrintNode(PrintNode $node, Environment $env, $type) - { - if (false === $type) { - return $node; - } - - $expression = $node->getNode('expr'); - - if ($this->isSafeFor($type, $expression, $env)) { - return $node; - } - - $class = \get_class($node); - - return new $class($this->getEscaperFilter($type, $expression), $node->getTemplateLine()); - } - - private function preEscapeFilterNode(FilterExpression $filter, Environment $env) - { - $name = $filter->getNode('filter')->getAttribute('value'); - - $type = $env->getFilter($name)->getPreEscape(); if (null === $type) { return $filter; } + /** @var AbstractExpression $node */ $node = $filter->getNode('node'); if ($this->isSafeFor($type, $node, $env)) { return $filter; } - $filter->setNode('node', $this->getEscaperFilter($type, $node)); + $filter->setNode('node', $this->getEscaperFilter($env, $type, $node)); return $filter; } - private function isSafeFor($type, Node $expression, $env) + private function isSafeFor(string $type, AbstractExpression $expression, Environment $env): bool { $safe = $this->safeAnalysis->getSafe($expression); - if (null === $safe) { + if (!$safe) { if (null === $this->traverser) { $this->traverser = new NodeTraverser($env, [$this->safeAnalysis]); } @@ -178,31 +154,32 @@ final class EscaperNodeVisitor extends AbstractNodeVisitor $safe = $this->safeAnalysis->getSafe($expression); } - return \in_array($type, $safe) || \in_array('all', $safe); + return \in_array($type, $safe, true) || \in_array('all', $safe, true); } - private function needEscaping(Environment $env) + /** + * @return string|false + */ + private function needEscaping(): string|bool { if (\count($this->statusStack)) { return $this->statusStack[\count($this->statusStack) - 1]; } - return $this->defaultStrategy ? $this->defaultStrategy : false; + return $this->defaultStrategy ?: false; } - private function getEscaperFilter(string $type, Node $node): FilterExpression + private function getEscaperFilter(Environment $env, string $type, AbstractExpression $node): FilterExpression { $line = $node->getTemplateLine(); - $name = new ConstantExpression('escape', $line); - $args = new Node([new ConstantExpression((string) $type, $line), new ConstantExpression(null, $line), new ConstantExpression(true, $line)]); + $filter = $env->getFilter('escape'); + $args = new Nodes([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() + public function getPriority(): int { return 0; } } - -class_alias('Twig\NodeVisitor\EscaperNodeVisitor', 'Twig_NodeVisitor_Escaper'); diff --git a/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php deleted file mode 100644 index f41d463..0000000 --- a/vendor/twig/twig/src/NodeVisitor/MacroAutoImportNodeVisitor.php +++ /dev/null @@ -1,72 +0,0 @@ - - */ -final class MacroAutoImportNodeVisitor implements NodeVisitorInterface -{ - private $inAModule = false; - private $hasMacroCalls = false; - - public function enterNode(Node $node, Environment $env) - { - if ($node instanceof ModuleNode) { - $this->inAModule = true; - $this->hasMacroCalls = false; - } - - return $node; - } - - public function leaveNode(Node $node, Environment $env) - { - 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)); - } - } elseif ($this->inAModule) { - if ( - $node instanceof GetAttrExpression && - $node->getNode('node') instanceof NameExpression && - '_self' === $node->getNode('node')->getAttribute('name') && - $node->getNode('attribute') instanceof ConstantExpression - ) { - $this->hasMacroCalls = true; - - $name = $node->getNode('attribute')->getAttribute('value'); - $node = new MethodCallExpression($node->getNode('node'), 'macro_'.$name, $node->getNode('arguments'), $node->getTemplateLine()); - $node->setAttribute('safe', true); - } - } - - return $node; - } - - public function getPriority() - { - // we must be ran before auto-escaping - return -10; - } -} diff --git a/vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php b/vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php index e0ffae2..59e836d 100644 --- a/vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php +++ b/vendor/twig/twig/src/NodeVisitor/NodeVisitorInterface.php @@ -26,14 +26,14 @@ interface NodeVisitorInterface * * @return Node The modified node */ - public function enterNode(Node $node, Environment $env); + public function enterNode(Node $node, Environment $env): Node; /** * Called after child nodes are visited. * * @return Node|null The modified node or null if the node must be removed */ - public function leaveNode(Node $node, Environment $env); + public function leaveNode(Node $node, Environment $env): ?Node; /** * Returns the priority for this visitor. @@ -44,8 +44,3 @@ interface NodeVisitorInterface */ public function getPriority(); } - -class_alias('Twig\NodeVisitor\NodeVisitorInterface', 'Twig_NodeVisitorInterface'); - -// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name. -class_exists('Twig\Environment'); diff --git a/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php index 9f7cae4..b778ba4 100644 --- a/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php +++ b/vendor/twig/twig/src/NodeVisitor/OptimizerNodeVisitor.php @@ -15,15 +15,15 @@ use Twig\Environment; use Twig\Node\BlockReferenceNode; use Twig\Node\Expression\BlockReferenceExpression; use Twig\Node\Expression\ConstantExpression; -use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\GetAttrExpression; -use Twig\Node\Expression\NameExpression; use Twig\Node\Expression\ParentExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\ForNode; use Twig\Node\IncludeNode; use Twig\Node\Node; use Twig\Node\PrintNode; +use Twig\Node\TextNode; /** * Tries to optimize the AST. @@ -34,52 +34,55 @@ use Twig\Node\PrintNode; * optimizer mode. * * @author Fabien Potencier + * + * @internal */ -final class OptimizerNodeVisitor extends AbstractNodeVisitor +final class OptimizerNodeVisitor implements NodeVisitorInterface { - const OPTIMIZE_ALL = -1; - const OPTIMIZE_NONE = 0; - const OPTIMIZE_FOR = 2; - const OPTIMIZE_RAW_FILTER = 4; - // obsolete, does not do anything - const OPTIMIZE_VAR_ACCESS = 8; + public const OPTIMIZE_ALL = -1; + public const OPTIMIZE_NONE = 0; + public const OPTIMIZE_FOR = 2; + public const OPTIMIZE_RAW_FILTER = 4; + public const OPTIMIZE_TEXT_NODES = 8; private $loops = []; private $loopsTargets = []; - private $optimizers; /** * @param int $optimizers The optimizer mode */ - public function __construct(int $optimizers = -1) - { - if (!\is_int($optimizers) || $optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_VAR_ACCESS)) { - throw new \InvalidArgumentException(sprintf('Optimizer mode "%s" is not valid.', $optimizers)); + public function __construct( + private int $optimizers = -1, + ) { + if ($optimizers > (self::OPTIMIZE_FOR | self::OPTIMIZE_RAW_FILTER | self::OPTIMIZE_TEXT_NODES)) { + throw new \InvalidArgumentException(\sprintf('Optimizer mode "%s" is not valid.', $optimizers)); } - $this->optimizers = $optimizers; + if (-1 !== $optimizers && self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $optimizers)) { + 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.'); + } } - protected function doEnterNode(Node $node, Environment $env) + public function enterNode(Node $node, Environment $env): Node { if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { - $this->enterOptimizeFor($node, $env); + $this->enterOptimizeFor($node); } return $node; } - protected function doLeaveNode(Node $node, Environment $env) + public function leaveNode(Node $node, Environment $env): ?Node { if (self::OPTIMIZE_FOR === (self::OPTIMIZE_FOR & $this->optimizers)) { - $this->leaveOptimizeFor($node, $env); + $this->leaveOptimizeFor($node); } - if (self::OPTIMIZE_RAW_FILTER === (self::OPTIMIZE_RAW_FILTER & $this->optimizers)) { - $node = $this->optimizeRawFilter($node, $env); - } - - $node = $this->optimizePrintNode($node, $env); + $node = $this->optimizePrintNode($node); return $node; } @@ -91,16 +94,21 @@ final class OptimizerNodeVisitor extends AbstractNodeVisitor * * * "echo $this->render(Parent)Block()" with "$this->display(Parent)Block()" */ - private function optimizePrintNode(Node $node, Environment $env): Node + private function optimizePrintNode(Node $node): Node { if (!$node instanceof PrintNode) { return $node; } $exprNode = $node->getNode('expr'); + + if ($exprNode instanceof ConstantExpression && \is_string($exprNode->getAttribute('value'))) { + return new TextNode($exprNode->getAttribute('value'), $exprNode->getTemplateLine()); + } + if ( - $exprNode instanceof BlockReferenceExpression || - $exprNode instanceof ParentExpression + $exprNode instanceof BlockReferenceExpression + || $exprNode instanceof ParentExpression ) { $exprNode->setAttribute('output', true); @@ -110,22 +118,10 @@ final class OptimizerNodeVisitor extends AbstractNodeVisitor return $node; } - /** - * Removes "raw" filters. - */ - private function optimizeRawFilter(Node $node, Environment $env): Node - { - if ($node instanceof FilterExpression && 'raw' == $node->getNode('filter')->getAttribute('value')) { - return $node->getNode('node'); - } - - return $node; - } - /** * Optimizes "for" tag by removing the "loop" variable creation whenever possible. */ - private function enterOptimizeFor(Node $node, Environment $env) + private function enterOptimizeFor(Node $node): void { if ($node instanceof ForNode) { // disable the loop variable by default @@ -141,13 +137,13 @@ final class OptimizerNodeVisitor extends AbstractNodeVisitor // when do we need to add the loop variable back? // the loop variable is referenced for the current loop - elseif ($node instanceof NameExpression && 'loop' === $node->getAttribute('name')) { + elseif ($node instanceof ContextVariable && 'loop' === $node->getAttribute('name')) { $node->setAttribute('always_defined', true); $this->addLoopToCurrent(); } // optimize access to loop targets - elseif ($node instanceof NameExpression && \in_array($node->getAttribute('name'), $this->loopsTargets)) { + elseif ($node instanceof ContextVariable && \in_array($node->getAttribute('name'), $this->loopsTargets, true)) { $node->setAttribute('always_defined', true); } @@ -166,7 +162,7 @@ final class OptimizerNodeVisitor extends AbstractNodeVisitor && 'include' === $node->getAttribute('name') && (!$node->getNode('arguments')->hasNode('with_context') || false !== $node->getNode('arguments')->getNode('with_context')->getAttribute('value') - ) + ) ) { $this->addLoopToAll(); } @@ -175,12 +171,12 @@ final class OptimizerNodeVisitor extends AbstractNodeVisitor elseif ($node instanceof GetAttrExpression && (!$node->getNode('attribute') instanceof ConstantExpression || 'parent' === $node->getNode('attribute')->getAttribute('value') - ) + ) && (true === $this->loops[0]->getAttribute('with_loop') - || ($node->getNode('node') instanceof NameExpression - && 'loop' === $node->getNode('node')->getAttribute('name') - ) - ) + || ($node->getNode('node') instanceof ContextVariable + && 'loop' === $node->getNode('node')->getAttribute('name') + ) + ) ) { $this->addLoopToAll(); } @@ -189,7 +185,7 @@ final class OptimizerNodeVisitor extends AbstractNodeVisitor /** * Optimizes "for" tag by removing the "loop" variable creation whenever possible. */ - private function leaveOptimizeFor(Node $node, Environment $env) + private function leaveOptimizeFor(Node $node): void { if ($node instanceof ForNode) { array_shift($this->loops); @@ -198,22 +194,20 @@ final class OptimizerNodeVisitor extends AbstractNodeVisitor } } - private function addLoopToCurrent() + private function addLoopToCurrent(): void { $this->loops[0]->setAttribute('with_loop', true); } - private function addLoopToAll() + private function addLoopToAll(): void { foreach ($this->loops as $loop) { $loop->setAttribute('with_loop', true); } } - public function getPriority() + public function getPriority(): int { return 255; } } - -class_alias('Twig\NodeVisitor\OptimizerNodeVisitor', 'Twig_NodeVisitor_Optimizer'); diff --git a/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php index 02a2af4..8cb5f7a 100644 --- a/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php +++ b/vendor/twig/twig/src/NodeVisitor/SafeAnalysisNodeVisitor.php @@ -13,31 +13,38 @@ namespace Twig\NodeVisitor; use Twig\Environment; use Twig\Node\Expression\BlockReferenceExpression; -use Twig\Node\Expression\ConditionalExpression; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\GetAttrExpression; +use Twig\Node\Expression\MacroReferenceExpression; use Twig\Node\Expression\MethodCallExpression; -use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\OperatorEscapeInterface; use Twig\Node\Expression\ParentExpression; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\Node; -final class SafeAnalysisNodeVisitor extends AbstractNodeVisitor +/** + * @internal + */ +final class SafeAnalysisNodeVisitor implements NodeVisitorInterface { private $data = []; private $safeVars = []; - public function setSafeVars($safeVars) + public function setSafeVars(array $safeVars): void { $this->safeVars = $safeVars; } + /** + * @return array + */ public function getSafe(Node $node) { - $hash = spl_object_hash($node); + $hash = spl_object_id($node); if (!isset($this->data[$hash])) { - return; + return []; } foreach ($this->data[$hash] as $bucket) { @@ -45,17 +52,19 @@ final class SafeAnalysisNodeVisitor extends AbstractNodeVisitor continue; } - if (\in_array('html_attr', $bucket['value'])) { + if (\in_array('html_attr', $bucket['value'], true)) { $bucket['value'][] = 'html'; } return $bucket['value']; } + + return []; } - private function setSafe(Node $node, array $safe) + private function setSafe(Node $node, array $safe): void { - $hash = spl_object_hash($node); + $hash = spl_object_id($node); if (isset($this->data[$hash])) { foreach ($this->data[$hash] as &$bucket) { if ($bucket['key'] === $node) { @@ -71,12 +80,12 @@ final class SafeAnalysisNodeVisitor extends AbstractNodeVisitor ]; } - protected function doEnterNode(Node $node, Environment $env) + public function enterNode(Node $node, Environment $env): Node { return $node; } - protected function doLeaveNode(Node $node, Environment $env) + public function leaveNode(Node $node, Environment $env): ?Node { if ($node instanceof ConstantExpression) { // constants are marked safe for all @@ -87,74 +96,85 @@ final class SafeAnalysisNodeVisitor extends AbstractNodeVisitor } elseif ($node instanceof ParentExpression) { // parent block is safe by definition $this->setSafe($node, ['all']); - } elseif ($node instanceof ConditionalExpression) { - // intersect safeness of both operands - $safe = $this->intersectSafe($this->getSafe($node->getNode('expr2')), $this->getSafe($node->getNode('expr3'))); - $this->setSafe($node, $safe); + } elseif ($node instanceof OperatorEscapeInterface) { + // intersect safeness of operands + $operands = $node->getOperandNamesToEscape(); + if (2 < \count($operands)) { + throw new \LogicException(\sprintf('Operators with more than 2 operands are not supported yet, got %d.', \count($operands))); + } elseif (2 === \count($operands)) { + $safe = $this->intersectSafe($this->getSafe($node->getNode($operands[0])), $this->getSafe($node->getNode($operands[1]))); + $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 (false !== $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) { + trigger_deprecation('twig/twig', '3.16', 'The "%s::getSafe()" method should not return "null" anymore, return "[]" instead.', $filter::class); + $safe = []; + } + + if (!$safe) { $safe = $this->intersectSafe($this->getSafe($node->getNode('node')), $filter->getPreservesSafety()); } $this->setSafe($node, $safe); - } else { - $this->setSafe($node, []); } } elseif ($node instanceof FunctionExpression) { // function expression is safe when the function is safe - $name = $node->getAttribute('name'); - $args = $node->getNode('arguments'); - $function = $env->getFunction($name); - if (false !== $function) { - $this->setSafe($node, $function->getSafe($args)); + if ($node->hasAttribute('twig_callable')) { + $function = $node->getAttribute('twig_callable'); } else { - $this->setSafe($node, []); + // legacy + $function = $env->getFunction($node->getAttribute('name')); } - } elseif ($node instanceof MethodCallExpression) { - if ($node->getAttribute('safe')) { - $this->setSafe($node, ['all']); - } else { - $this->setSafe($node, []); + + if ($function) { + $safe = $function->getSafe($node->getNode('arguments')); + if (null === $safe) { + trigger_deprecation('twig/twig', '3.16', 'The "%s::getSafe()" method should not return "null" anymore, return "[]" instead.', $function::class); + $safe = []; + } + $this->setSafe($node, $safe); } - } elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression) { + } elseif ($node instanceof MethodCallExpression || $node instanceof MacroReferenceExpression) { + // all macro calls are safe + $this->setSafe($node, ['all']); + } elseif ($node instanceof GetAttrExpression && $node->getNode('node') instanceof ContextVariable) { $name = $node->getNode('node')->getAttribute('name'); - if (\in_array($name, $this->safeVars)) { + if (\in_array($name, $this->safeVars, true)) { $this->setSafe($node, ['all']); - } else { - $this->setSafe($node, []); } - } else { - $this->setSafe($node, []); } return $node; } - private function intersectSafe(array $a = null, array $b = null): array + private function intersectSafe(array $a, array $b): array { - if (null === $a || null === $b) { + if (!$a || !$b) { return []; } - if (\in_array('all', $a)) { + if (\in_array('all', $a, true)) { return $b; } - if (\in_array('all', $b)) { + if (\in_array('all', $b, true)) { return $a; } return array_intersect($a, $b); } - public function getPriority() + public function getPriority(): int { return 0; } } - -class_alias('Twig\NodeVisitor\SafeAnalysisNodeVisitor', 'Twig_NodeVisitor_SafeAnalysis'); diff --git a/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php index 3e8d0bc..9dd48f5 100644 --- a/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php +++ b/vendor/twig/twig/src/NodeVisitor/SandboxNodeVisitor.php @@ -12,32 +12,40 @@ namespace Twig\NodeVisitor; use Twig\Environment; +use Twig\Node\CheckSecurityCallNode; use Twig\Node\CheckSecurityNode; use Twig\Node\CheckToStringNode; +use Twig\Node\Expression\ArrayExpression; use Twig\Node\Expression\Binary\ConcatBinary; use Twig\Node\Expression\Binary\RangeBinary; use Twig\Node\Expression\FilterExpression; use Twig\Node\Expression\FunctionExpression; use Twig\Node\Expression\GetAttrExpression; -use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Unary\SpreadUnary; +use Twig\Node\Expression\Variable\ContextVariable; use Twig\Node\ModuleNode; use Twig\Node\Node; +use Twig\Node\Nodes; use Twig\Node\PrintNode; use Twig\Node\SetNode; /** * @author Fabien Potencier + * + * @internal */ -final class SandboxNodeVisitor extends AbstractNodeVisitor +final class SandboxNodeVisitor implements NodeVisitorInterface { private $inAModule = false; + /** @var array */ private $tags; + /** @var array */ private $filters; + /** @var array */ private $functions; - private $needsToStringWrap = false; - protected function doEnterNode(Node $node, Environment $env) + public function enterNode(Node $node, Environment $env): Node { if ($node instanceof ModuleNode) { $this->inAModule = true; @@ -49,22 +57,22 @@ final class SandboxNodeVisitor extends AbstractNodeVisitor } elseif ($this->inAModule) { // look for tags if ($node->getNodeTag() && !isset($this->tags[$node->getNodeTag()])) { - $this->tags[$node->getNodeTag()] = $node; + $this->tags[$node->getNodeTag()] = $node->getTemplateLine(); } // look for filters - if ($node instanceof FilterExpression && !isset($this->filters[$node->getNode('filter')->getAttribute('value')])) { - $this->filters[$node->getNode('filter')->getAttribute('value')] = $node; + if ($node instanceof FilterExpression && !isset($this->filters[$node->getAttribute('name')])) { + $this->filters[$node->getAttribute('name')] = $node->getTemplateLine(); } // look for functions if ($node instanceof FunctionExpression && !isset($this->functions[$node->getAttribute('name')])) { - $this->functions[$node->getAttribute('name')] = $node; + $this->functions[$node->getAttribute('name')] = $node->getTemplateLine(); } // the .. operator is equivalent to the range() function if ($node instanceof RangeBinary && !isset($this->functions['range'])) { - $this->functions['range'] = $node; + $this->functions['range'] = $node->getTemplateLine(); } if ($node instanceof PrintNode) { @@ -95,12 +103,13 @@ final class SandboxNodeVisitor extends AbstractNodeVisitor return $node; } - protected function doLeaveNode(Node $node, Environment $env) + public function leaveNode(Node $node, Environment $env): ?Node { if ($node instanceof ModuleNode) { $this->inAModule = false; - $node->getNode('constructor_end')->setNode('_security_check', new Node([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('display_start')])); + $node->setNode('constructor_end', new Nodes([new CheckSecurityCallNode(), $node->getNode('constructor_end')])); + $node->setNode('class_end', new Nodes([new CheckSecurityNode($this->filters, $this->tags, $this->functions), $node->getNode('class_end')])); } elseif ($this->inAModule) { if ($node instanceof PrintNode || $node instanceof SetNode) { $this->needsToStringWrap = false; @@ -110,15 +119,21 @@ final class SandboxNodeVisitor extends AbstractNodeVisitor return $node; } - private function wrapNode(Node $node, string $name) + private function wrapNode(Node $node, string $name): void { $expr = $node->getNode($name); - if ($expr instanceof NameExpression || $expr instanceof GetAttrExpression) { + if (($expr instanceof ContextVariable || $expr instanceof GetAttrExpression) && !$expr->isGenerator()) { $node->setNode($name, new CheckToStringNode($expr)); + } elseif ($expr instanceof SpreadUnary) { + $this->wrapNode($expr, 'node'); + } elseif ($expr instanceof ArrayExpression) { + foreach ($expr as $name => $_) { + $this->wrapNode($expr, $name); + } } } - private function wrapArrayNode(Node $node, string $name) + private function wrapArrayNode(Node $node, string $name): void { $args = $node->getNode($name); foreach ($args as $name => $_) { @@ -126,10 +141,8 @@ final class SandboxNodeVisitor extends AbstractNodeVisitor } } - public function getPriority() + public function getPriority(): int { return 0; } } - -class_alias('Twig\NodeVisitor\SandboxNodeVisitor', 'Twig_NodeVisitor_Sandbox'); diff --git a/vendor/twig/twig/src/NodeVisitor/YieldNotReadyNodeVisitor.php b/vendor/twig/twig/src/NodeVisitor/YieldNotReadyNodeVisitor.php new file mode 100644 index 0000000..4d6cf60 --- /dev/null +++ b/vendor/twig/twig/src/NodeVisitor/YieldNotReadyNodeVisitor.php @@ -0,0 +1,59 @@ +yieldReadyNodes[$class])) { + return $node; + } + + if (!$this->yieldReadyNodes[$class] = (bool) (new \ReflectionClass($class))->getAttributes(YieldReady::class)) { + if ($this->useYield) { + throw new \LogicException(\sprintf('You cannot enable the "use_yield" option of Twig as node "%s" is not marked as ready for it; please make it ready and then flag it with the #[\Twig\Attribute\YieldReady] attribute.', $class)); + } + + trigger_deprecation('twig/twig', '3.9', 'Twig node "%s" is not marked as ready for using "yield" instead of "echo"; please make it ready and then flag it with the #[\Twig\Attribute\YieldReady] attribute.', $class); + } + + return $node; + } + + public function leaveNode(Node $node, Environment $env): ?Node + { + return $node; + } + + public function getPriority(): int + { + return 255; + } +} diff --git a/vendor/twig/twig/src/OperatorPrecedenceChange.php b/vendor/twig/twig/src/OperatorPrecedenceChange.php new file mode 100644 index 0000000..31ebaef --- /dev/null +++ b/vendor/twig/twig/src/OperatorPrecedenceChange.php @@ -0,0 +1,34 @@ + + * + * @deprecated since Twig 1.20 Use Twig\ExpressionParser\PrecedenceChange instead + */ +class OperatorPrecedenceChange extends PrecedenceChange +{ + public function __construct( + private string $package, + private string $version, + private int $newPrecedence, + ) { + trigger_deprecation('twig/twig', '3.21', 'The "%s" class is deprecated since Twig 3.21. Use "%s" instead.', self::class, PrecedenceChange::class); + + parent::__construct($package, $version, $newPrecedence); + } +} diff --git a/vendor/twig/twig/src/Parser.php b/vendor/twig/twig/src/Parser.php index 8a937e3..1937b7e 100644 --- a/vendor/twig/twig/src/Parser.php +++ b/vendor/twig/twig/src/Parser.php @@ -13,77 +13,80 @@ namespace Twig; use Twig\Error\SyntaxError; +use Twig\ExpressionParser\ExpressionParserInterface; +use Twig\ExpressionParser\ExpressionParsers; +use Twig\ExpressionParser\ExpressionParserType; +use Twig\ExpressionParser\InfixExpressionParserInterface; +use Twig\ExpressionParser\Prefix\LiteralExpressionParser; +use Twig\ExpressionParser\PrefixExpressionParserInterface; use Twig\Node\BlockNode; use Twig\Node\BlockReferenceNode; use Twig\Node\BodyNode; +use Twig\Node\EmptyNode; use Twig\Node\Expression\AbstractExpression; +use Twig\Node\Expression\Variable\AssignTemplateVariable; +use Twig\Node\Expression\Variable\TemplateVariable; use Twig\Node\MacroNode; use Twig\Node\ModuleNode; use Twig\Node\Node; use Twig\Node\NodeCaptureInterface; use Twig\Node\NodeOutputInterface; +use Twig\Node\Nodes; use Twig\Node\PrintNode; -use Twig\Node\SpacelessNode; use Twig\Node\TextNode; use Twig\TokenParser\TokenParserInterface; +use Twig\Util\ReflectionCallable; /** - * Default parser implementation. - * * @author Fabien Potencier */ class Parser { private $stack = []; + private ?\WeakMap $expressionRefs = null; private $stream; private $parent; - private $handlers; private $visitors; private $expressionParser; private $blocks; private $blockStack; private $macros; - private $env; private $importedSymbols; private $traits; private $embeddedTemplates = []; private $varNameSalt = 0; + private $ignoreUnknownTwigCallables = false; + private ExpressionParsers $parsers; - public function __construct(Environment $env) - { - $this->env = $env; + public function __construct( + private Environment $env, + ) { + $this->parsers = $env->getExpressionParsers(); } - public function getVarName() + public function getEnvironment(): Environment { - return sprintf('__internal_%s', hash('sha256', __METHOD__.$this->stream->getSourceContext()->getCode().$this->varNameSalt++)); + return $this->env; } - public function parse(TokenStream $stream, $test = null, $dropNeedle = false) + public function getVarName(): string + { + trigger_deprecation('twig/twig', '3.15', 'The "%s()" method is deprecated.', __METHOD__); + + return \sprintf('__internal_parse_%d', $this->varNameSalt++); + } + + public function parse(TokenStream $stream, $test = null, bool $dropNeedle = false): ModuleNode { $vars = get_object_vars($this); - unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames']); + unset($vars['stack'], $vars['env'], $vars['handlers'], $vars['visitors'], $vars['expressionParser'], $vars['reservedMacroNames'], $vars['varNameSalt']); $this->stack[] = $vars; - // tag handlers - if (null === $this->handlers) { - $this->handlers = []; - foreach ($this->env->getTokenParsers() as $handler) { - $handler->setParser($this); - - $this->handlers[$handler->getTag()] = $handler; - } - } - // node visitors if (null === $this->visitors) { $this->visitors = $this->env->getNodeVisitors(); } - if (null === $this->expressionParser) { - $this->expressionParser = new ExpressionParser($this, $this->env); - } - $this->stream = $stream; $this->parent = null; $this->blocks = []; @@ -92,13 +95,13 @@ class Parser $this->blockStack = []; $this->importedSymbols = [[]]; $this->embeddedTemplates = []; - $this->varNameSalt = 0; + $this->expressionRefs = new \WeakMap(); try { $body = $this->subparse($test, $dropNeedle); if (null !== $this->parent && null === $body = $this->filterBodyNodes($body)) { - $body = new Node(); + $body = new EmptyNode(); } } catch (SyntaxError $e) { if (!$e->getSourceContext()) { @@ -106,16 +109,29 @@ class Parser } if (!$e->getTemplateLine()) { - $e->setTemplateLine($this->stream->getCurrent()->getLine()); + $e->setTemplateLine($this->getCurrentToken()->getLine()); } throw $e; + } finally { + $this->expressionRefs = null; } - $node = new ModuleNode(new BodyNode([$body]), $this->parent, new Node($this->blocks), new Node($this->macros), new Node($this->traits), $this->embeddedTemplates, $stream->getSourceContext()); + $node = new ModuleNode( + new BodyNode([$body]), + $this->parent, + $this->blocks ? new Nodes($this->blocks) : new EmptyNode(), + $this->macros ? new Nodes($this->macros) : new EmptyNode(), + $this->traits ? new Nodes($this->traits) : new EmptyNode(), + $this->embeddedTemplates ? new Nodes($this->embeddedTemplates) : new EmptyNode(), + $stream->getSourceContext(), + ); $traverser = new NodeTraverser($this->env, $this->visitors); + /** + * @var ModuleNode $node + */ $node = $traverser->traverse($node); // restore previous stack so previous parse() call can resume working @@ -126,29 +142,45 @@ class Parser return $node; } - public function subparse($test, $dropNeedle = false) + public function shouldIgnoreUnknownTwigCallables(): bool + { + return $this->ignoreUnknownTwigCallables; + } + + public function subparseIgnoreUnknownTwigCallables($test, bool $dropNeedle = false): void + { + $previous = $this->ignoreUnknownTwigCallables; + $this->ignoreUnknownTwigCallables = true; + try { + $this->subparse($test, $dropNeedle); + } finally { + $this->ignoreUnknownTwigCallables = $previous; + } + } + + public function subparse($test, bool $dropNeedle = false): Node { $lineno = $this->getCurrentToken()->getLine(); $rv = []; while (!$this->stream->isEOF()) { - switch ($this->getCurrentToken()->getType()) { - case /* Token::TEXT_TYPE */ 0: + switch (true) { + case $this->stream->getCurrent()->test(Token::TEXT_TYPE): $token = $this->stream->next(); $rv[] = new TextNode($token->getValue(), $token->getLine()); break; - case /* Token::VAR_START_TYPE */ 2: + case $this->stream->getCurrent()->test(Token::VAR_START_TYPE): $token = $this->stream->next(); - $expr = $this->expressionParser->parseExpression(); - $this->stream->expect(/* Token::VAR_END_TYPE */ 4); + $expr = $this->parseExpression(); + $this->stream->expect(Token::VAR_END_TYPE); $rv[] = new PrintNode($expr, $token->getLine()); break; - case /* Token::BLOCK_START_TYPE */ 1: + case $this->stream->getCurrent()->test(Token::BLOCK_START_TYPE): $this->stream->next(); $token = $this->getCurrentToken(); - if (/* Token::NAME_TYPE */ 5 !== $token->getType()) { + if (!$token->test(Token::NAME_TYPE)) { throw new SyntaxError('A block must start with a tag name.', $token->getLine(), $this->stream->getSourceContext()); } @@ -161,19 +193,20 @@ class Parser return $rv[0]; } - return new Node($rv, [], $lineno); + return new Nodes($rv, $lineno); } - if (!isset($this->handlers[$token->getValue()])) { + if (!$subparser = $this->env->getTokenParser($token->getValue())) { if (null !== $test) { - $e = new SyntaxError(sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); + $e = new SyntaxError(\sprintf('Unexpected "%s" tag', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); - if (\is_array($test) && isset($test[0]) && $test[0] instanceof TokenParserInterface) { - $e->appendMessage(sprintf(' (expecting closing tag for the "%s" tag defined near line %s).', $test[0]->getTag(), $lineno)); + $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)); } } else { - $e = new SyntaxError(sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); - $e->addSuggestions($token->getValue(), array_keys($this->env->getTags())); + $e = new SyntaxError(\sprintf('Unknown "%s" tag.', $token->getValue()), $token->getLine(), $this->stream->getSourceContext()); + $e->addSuggestions($token->getValue(), array_keys($this->env->getTokenParsers())); } throw $e; @@ -181,15 +214,18 @@ class Parser $this->stream->next(); - $subparser = $this->handlers[$token->getValue()]; + $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()); } } @@ -197,74 +233,84 @@ class Parser return $rv[0]; } - return new Node($rv, [], $lineno); + return new Nodes($rv, $lineno); } - public function getBlockStack() + public function getBlockStack(): array { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->blockStack; } + /** + * @return string|null + */ public function peekBlockStack() { - return isset($this->blockStack[\count($this->blockStack) - 1]) ? $this->blockStack[\count($this->blockStack) - 1] : null; + return $this->blockStack[\count($this->blockStack) - 1] ?? null; } - public function popBlockStack() + public function popBlockStack(): void { array_pop($this->blockStack); } - public function pushBlockStack($name) + public function pushBlockStack($name): void { $this->blockStack[] = $name; } - public function hasBlock($name) + 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($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($name, BlockNode $value) + 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($name) + public function hasMacro(string $name): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return isset($this->macros[$name]); } - public function setMacro($name, MacroNode $node) + public function setMacro(string $name, MacroNode $node): void { $this->macros[$name] = $node; } - /** - * @deprecated since Twig 2.7 as there are no reserved macro names anymore, will be removed in 3.0. - */ - public function isReservedMacroName($name) - { - @trigger_error(sprintf('The "%s" method is deprecated since Twig 2.7 and will be removed in 3.0.', __METHOD__), E_USER_DEPRECATED); - - return false; - } - - public function addTrait($trait) + public function addTrait($trait): void { $this->traits[] = $trait; } - public function hasTraits() + public function hasTraits(): bool { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return \count($this->traits) > 0; } + /** + * @return void + */ public function embedTemplate(ModuleNode $template) { $template->setIndex(mt_rand()); @@ -272,80 +318,219 @@ class Parser $this->embeddedTemplates[] = $template; } - public function addImportedSymbol($type, $alias, $name = null, AbstractExpression $node = null) + public function addImportedSymbol(string $type, string $alias, ?string $name = null, AbstractExpression|AssignTemplateVariable|null $internalRef = null): void { - $this->importedSymbols[0][$type][$alias] = ['name' => $name, 'node' => $node]; + if ($internalRef && !$internalRef instanceof AssignTemplateVariable) { + trigger_deprecation('twig/twig', '3.15', 'Not passing a "%s" instance as an internal reference is deprecated ("%s" given).', __METHOD__, AssignTemplateVariable::class, $internalRef::class); + + $internalRef = new AssignTemplateVariable(new TemplateVariable($internalRef->getAttribute('name'), $internalRef->getTemplateLine()), $internalRef->getAttribute('global')); + } + + $this->importedSymbols[0][$type][$alias] = ['name' => $name, 'node' => $internalRef]; } - public function getImportedSymbol($type, $alias) + /** + * @return array{name: string, node: AssignTemplateVariable|null}|null + */ + public function getImportedSymbol(string $type, string $alias) { // if the symbol does not exist in the current scope (0), try in the main/global scope (last index) return $this->importedSymbols[0][$type][$alias] ?? ($this->importedSymbols[\count($this->importedSymbols) - 1][$type][$alias] ?? null); } - public function isMainScope() + public function isMainScope(): bool { return 1 === \count($this->importedSymbols); } - public function pushLocalScope() + public function pushLocalScope(): void { array_unshift($this->importedSymbols, []); } - public function popLocalScope() + public function popLocalScope(): void { array_shift($this->importedSymbols); } /** - * @return ExpressionParser + * @deprecated since Twig 3.21 */ - public function getExpressionParser() + public function getExpressionParser(): ExpressionParser { + trigger_deprecation('twig/twig', '3.21', 'Method "%s()" is deprecated, use "parseExpression()" instead.', __METHOD__); + + if (null === $this->expressionParser) { + $this->expressionParser = new ExpressionParser($this, $this->env); + } + return $this->expressionParser; } - public function getParent() + public function parseExpression(int $precedence = 0): AbstractExpression { + $token = $this->getCurrentToken(); + if ($token->test(Token::OPERATOR_TYPE) && $ep = $this->parsers->getByName(PrefixExpressionParserInterface::class, $token->getValue())) { + $this->getStream()->next(); + $expr = $ep->parse($this, $token); + $this->checkPrecedenceDeprecations($ep, $expr); + } else { + $expr = $this->parsers->getByClass(LiteralExpressionParser::class)->parse($this, $token); + } + + $token = $this->getCurrentToken(); + while ($token->test(Token::OPERATOR_TYPE) && ($ep = $this->parsers->getByName(InfixExpressionParserInterface::class, $token->getValue())) && $ep->getPrecedence() >= $precedence) { + $this->getStream()->next(); + $expr = $ep->parse($this, $expr, $token); + $this->checkPrecedenceDeprecations($ep, $expr); + $token = $this->getCurrentToken(); + } + + return $expr; + } + + public function getParent(): ?Node + { + trigger_deprecation('twig/twig', '3.12', 'Method "%s()" is deprecated.', __METHOD__); + return $this->parent; } - public function setParent($parent) + /** + * @return bool + */ + 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; } - /** - * @return TokenStream - */ - public function getStream() + public function getStream(): TokenStream { return $this->stream; } - /** - * @return Token - */ - public function getCurrentToken() + public function getCurrentToken(): Token { return $this->stream->getCurrent(); } - private function filterBodyNodes(Node $node, bool $nested = false) + public function getFunction(string $name, int $line): TwigFunction + { + try { + $function = $this->env->getFunction($name); + } catch (SyntaxError $e) { + if (!$this->shouldIgnoreUnknownTwigCallables()) { + throw $e; + } + + $function = null; + } + + if (!$function) { + if ($this->shouldIgnoreUnknownTwigCallables()) { + return new TwigFunction($name, fn () => ''); + } + $e = new SyntaxError(\sprintf('Unknown "%s" function.', $name), $line, $this->stream->getSourceContext()); + $e->addSuggestions($name, array_keys($this->env->getFunctions())); + + throw $e; + } + + if ($function->isDeprecated()) { + $src = $this->stream->getSourceContext(); + $function->triggerDeprecation($src->getPath() ?: $src->getName(), $line); + } + + return $function; + } + + public function getFilter(string $name, int $line): TwigFilter + { + try { + $filter = $this->env->getFilter($name); + } catch (SyntaxError $e) { + if (!$this->shouldIgnoreUnknownTwigCallables()) { + throw $e; + } + + $filter = null; + } + if (!$filter) { + if ($this->shouldIgnoreUnknownTwigCallables()) { + return new TwigFilter($name, fn () => ''); + } + $e = new SyntaxError(\sprintf('Unknown "%s" filter.', $name), $line, $this->stream->getSourceContext()); + $e->addSuggestions($name, array_keys($this->env->getFilters())); + + throw $e; + } + + if ($filter->isDeprecated()) { + $src = $this->stream->getSourceContext(); + $filter->triggerDeprecation($src->getPath() ?: $src->getName(), $line); + } + + return $filter; + } + + public function getTest(int $line): TwigTest + { + $name = $this->stream->expect(Token::NAME_TYPE)->getValue(); + + if ($this->stream->test(Token::NAME_TYPE)) { + // try 2-words tests + $name = $name.' '.$this->getCurrentToken()->getValue(); + + if ($test = $this->env->getTest($name)) { + $this->stream->next(); + } + } else { + $test = $this->env->getTest($name); + } + + if (!$test) { + if ($this->shouldIgnoreUnknownTwigCallables()) { + return new TwigTest($name, fn () => ''); + } + $e = new SyntaxError(\sprintf('Unknown "%s" test.', $name), $line, $this->stream->getSourceContext()); + $e->addSuggestions($name, array_keys($this->env->getTests())); + + throw $e; + } + + if ($test->isDeprecated()) { + $src = $this->stream->getSourceContext(); + $test->triggerDeprecation($src->getPath() ?: $src->getName(), $this->stream->getCurrent()->getLine()); + } + + return $test; + } + + private function filterBodyNodes(Node $node, bool $nested = false): ?Node { // check that the body does not contain non-empty output nodes if ( ($node instanceof TextNode && !ctype_space($node->getAttribute('data'))) - || - // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0 - (!$node instanceof TextNode && !$node instanceof BlockReferenceNode && ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode)) + || (!$node instanceof TextNode && !$node instanceof BlockReferenceNode && $node instanceof NodeOutputInterface) ) { - if (false !== strpos((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) { + if (str_contains((string) $node, \chr(0xEF).\chr(0xBB).\chr(0xBF))) { $t = substr($node->getAttribute('data'), 3); if ('' === $t || ctype_space($t)) { // bypass empty nodes starting with a BOM - return; + return null; } } @@ -358,29 +543,21 @@ class Parser return $node; } - // to be removed completely in Twig 3.0 - if (!$nested && $node instanceof SpacelessNode) { - @trigger_error(sprintf('Using the spaceless tag at the root level of a child template in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), E_USER_DEPRECATED); - } - // "block" tags that are not captured (see above) are only used for defining // the content of the block. In such a case, nesting it does not work as // expected as the definition is not part of the default template code flow. - if ($nested && ($node instanceof BlockReferenceNode || $node instanceof \Twig_Node_BlockReference)) { - //throw new SyntaxError('A block definition cannot be nested under non-capturing nodes.', $node->getTemplateLine(), $this->stream->getSourceContext()); - @trigger_error(sprintf('Nesting a block definition under a non-capturing node in "%s" at line %d is deprecated since Twig 2.5.0 and will become a syntax error in 3.0.', $this->stream->getSourceContext()->getName(), $node->getTemplateLine()), E_USER_DEPRECATED); - - return; + if ($nested && $node instanceof BlockReferenceNode) { + throw new SyntaxError('A block definition cannot be nested under non-capturing nodes.', $node->getTemplateLine(), $this->stream->getSourceContext()); } - // the "&& !$node instanceof SpacelessNode" part of the condition must be removed in 3.0 - if ($node instanceof NodeOutputInterface && !$node instanceof SpacelessNode) { - return; + if ($node instanceof NodeOutputInterface) { + return null; } // here, $nested means "being at the root level of a child template" - // we need to discard the wrapping "Twig_Node" for the "body" node - $nested = $nested || ('Twig_Node' !== \get_class($node) && Node::class !== \get_class($node)); + // we need to discard the wrapping "Node" for the "body" node + // Node::class !== \get_class($node) should be removed in Twig 4.0 + $nested = $nested || (Node::class !== $node::class && !$node instanceof Nodes); foreach ($node as $k => $n) { if (null !== $n && null === $this->filterBodyNodes($n, $nested)) { $node->removeNode($k); @@ -389,6 +566,43 @@ class Parser return $node; } -} -class_alias('Twig\Parser', 'Twig_Parser'); + private function checkPrecedenceDeprecations(ExpressionParserInterface $expressionParser, AbstractExpression $expr) + { + $this->expressionRefs[$expr] = $expressionParser; + $precedenceChanges = $this->parsers->getPrecedenceChanges(); + + // Check that the all nodes that are between the 2 precedences have explicit parentheses + if (!isset($precedenceChanges[$expressionParser])) { + return; + } + + if ($expr->hasExplicitParentheses()) { + return; + } + + if ($expressionParser instanceof PrefixExpressionParserInterface) { + /** @var AbstractExpression $node */ + $node = $expr->getNode('node'); + foreach ($precedenceChanges as $ep => $changes) { + if (!\in_array($expressionParser, $changes, true)) { + continue; + } + if (isset($this->expressionRefs[$node]) && $ep === $this->expressionRefs[$node]) { + $change = $expressionParser->getPrecedenceChange(); + trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $expressionParser->getName(), ExpressionParserType::getType($expressionParser)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine())); + } + } + } + + foreach ($precedenceChanges[$expressionParser] as $ep) { + foreach ($expr as $node) { + /** @var AbstractExpression $node */ + if (isset($this->expressionRefs[$node]) && $ep === $this->expressionRefs[$node] && !$node->hasExplicitParentheses()) { + $change = $ep->getPrecedenceChange(); + trigger_deprecation($change->getPackage(), $change->getVersion(), \sprintf('As the "%s" %s operator will change its precedence in the next major version, add explicit parentheses to avoid behavior change in "%s" at line %d.', $ep->getName(), ExpressionParserType::getType($ep)->value, $this->getStream()->getSourceContext()->getName(), $node->getTemplateLine())); + } + } + } + } +} diff --git a/vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php b/vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php index 1631987..267718c 100644 --- a/vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php +++ b/vendor/twig/twig/src/Profiler/Dumper/BaseDumper.php @@ -20,16 +20,16 @@ abstract class BaseDumper { private $root; - public function dump(Profile $profile) + public function dump(Profile $profile): string { return $this->dumpProfile($profile); } - abstract protected function formatTemplate(Profile $profile, $prefix); + abstract protected function formatTemplate(Profile $profile, $prefix): string; - abstract protected function formatNonTemplate(Profile $profile, $prefix); + abstract protected function formatNonTemplate(Profile $profile, $prefix): string; - abstract protected function formatTime(Profile $profile, $percent); + abstract protected function formatTime(Profile $profile, $percent): string; private function dumpProfile(Profile $profile, $prefix = '', $sibling = false): string { @@ -50,7 +50,7 @@ abstract class BaseDumper if ($profile->getDuration() * 1000 < 1) { $str = $start."\n"; } else { - $str = sprintf("%s %s\n", $start, $this->formatTime($profile, $percent)); + $str = \sprintf("%s %s\n", $start, $this->formatTime($profile, $percent)); } $nCount = \count($profile->getProfiles()); @@ -61,5 +61,3 @@ abstract class BaseDumper return $str; } } - -class_alias('Twig\Profiler\Dumper\BaseDumper', 'Twig_Profiler_Dumper_Base'); diff --git a/vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php b/vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php index f333429..7cfae16 100644 --- a/vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php +++ b/vendor/twig/twig/src/Profiler/Dumper/BlackfireDumper.php @@ -18,42 +18,42 @@ use Twig\Profiler\Profile; */ final class BlackfireDumper { - public function dump(Profile $profile) + public function dump(Profile $profile): string { $data = []; $this->dumpProfile('main()', $profile, $data); $this->dumpChildren('main()', $profile, $data); - $start = sprintf('%f', microtime(true)); + $start = \sprintf('%f', microtime(true)); $str = << $values) { - $str .= "{$name}//{$values['ct']} {$values['wt']} {$values['mu']} {$values['pmu']}\n"; + $str .= "$name//{$values['ct']} {$values['wt']} {$values['mu']} {$values['pmu']}\n"; } return $str; } - private function dumpChildren(string $parent, Profile $profile, &$data) + private function dumpChildren(string $parent, Profile $profile, &$data): void { foreach ($profile as $p) { if ($p->isTemplate()) { $name = $p->getTemplate(); } else { - $name = sprintf('%s::%s(%s)', $p->getTemplate(), $p->getType(), $p->getName()); + $name = \sprintf('%s::%s(%s)', $p->getTemplate(), $p->getType(), $p->getName()); } - $this->dumpProfile(sprintf('%s==>%s', $parent, $name), $p, $data); + $this->dumpProfile(\sprintf('%s==>%s', $parent, $name), $p, $data); $this->dumpChildren($name, $p, $data); } } - private function dumpProfile(string $edge, Profile $profile, &$data) + private function dumpProfile(string $edge, Profile $profile, &$data): void { if (isset($data[$edge])) { ++$data[$edge]['ct']; @@ -70,5 +70,3 @@ EOF; } } } - -class_alias('Twig\Profiler\Dumper\BlackfireDumper', 'Twig_Profiler_Dumper_Blackfire'); diff --git a/vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php b/vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php index 5be5abe..cdab2de 100644 --- a/vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php +++ b/vendor/twig/twig/src/Profiler/Dumper/HtmlDumper.php @@ -25,25 +25,23 @@ final class HtmlDumper extends BaseDumper 'big' => '#d44', ]; - public function dump(Profile $profile) + public function dump(Profile $profile): string { return '
    '.parent::dump($profile).'
    '; } - protected function formatTemplate(Profile $profile, $prefix) + protected function formatTemplate(Profile $profile, $prefix): string { - return sprintf('%s└ %s', $prefix, self::$colors['template'], $profile->getTemplate()); + return \sprintf('%s└ %s', $prefix, self::$colors['template'], $profile->getTemplate()); } - protected function formatNonTemplate(Profile $profile, $prefix) + protected function formatNonTemplate(Profile $profile, $prefix): string { - return sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), isset(self::$colors[$profile->getType()]) ? self::$colors[$profile->getType()] : 'auto', $profile->getName()); + return \sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), self::$colors[$profile->getType()] ?? 'auto', $profile->getName()); } - protected function formatTime(Profile $profile, $percent) + protected function formatTime(Profile $profile, $percent): string { - return sprintf('%.2fms/%.0f%%', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent); + return \sprintf('%.2fms/%.0f%%', $percent > 20 ? self::$colors['big'] : 'auto', $profile->getDuration() * 1000, $percent); } } - -class_alias('Twig\Profiler\Dumper\HtmlDumper', 'Twig_Profiler_Dumper_Html'); diff --git a/vendor/twig/twig/src/Profiler/Dumper/TextDumper.php b/vendor/twig/twig/src/Profiler/Dumper/TextDumper.php index 395ef9d..1c1f77e 100644 --- a/vendor/twig/twig/src/Profiler/Dumper/TextDumper.php +++ b/vendor/twig/twig/src/Profiler/Dumper/TextDumper.php @@ -18,20 +18,18 @@ use Twig\Profiler\Profile; */ final class TextDumper extends BaseDumper { - protected function formatTemplate(Profile $profile, $prefix) + protected function formatTemplate(Profile $profile, $prefix): string { - return sprintf('%s└ %s', $prefix, $profile->getTemplate()); + return \sprintf('%s└ %s', $prefix, $profile->getTemplate()); } - protected function formatNonTemplate(Profile $profile, $prefix) + protected function formatNonTemplate(Profile $profile, $prefix): string { - return sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), $profile->getName()); + return \sprintf('%s└ %s::%s(%s)', $prefix, $profile->getTemplate(), $profile->getType(), $profile->getName()); } - protected function formatTime(Profile $profile, $percent) + protected function formatTime(Profile $profile, $percent): string { - return sprintf('%.2fms/%.0f%%', $profile->getDuration() * 1000, $percent); + return \sprintf('%.2fms/%.0f%%', $profile->getDuration() * 1000, $percent); } } - -class_alias('Twig\Profiler\Dumper\TextDumper', 'Twig_Profiler_Dumper_Text'); diff --git a/vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php b/vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php index 91de5ff..4d8e504 100644 --- a/vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php +++ b/vendor/twig/twig/src/Profiler/Node/EnterProfileNode.php @@ -11,6 +11,7 @@ namespace Twig\Profiler\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Node; @@ -19,6 +20,7 @@ use Twig\Node\Node; * * @author Fabien Potencier */ +#[YieldReady] class EnterProfileNode extends Node { public function __construct(string $extensionName, string $type, string $name, string $varName) @@ -26,13 +28,13 @@ class EnterProfileNode extends Node parent::__construct([], ['extension_name' => $extensionName, 'name' => $name, 'type' => $type, 'var_name' => $varName]); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler - ->write(sprintf('$%s = $this->extensions[', $this->getAttribute('var_name'))) + ->write(\sprintf('$%s = $this->extensions[', $this->getAttribute('var_name'))) ->repr($this->getAttribute('extension_name')) ->raw("];\n") - ->write(sprintf('$%s->enter($%s = new \Twig\Profiler\Profile($this->getTemplateName(), ', $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof')) + ->write(\sprintf('$%s->enter($%s = new \Twig\Profiler\Profile($this->getTemplateName(), ', $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof')) ->repr($this->getAttribute('type')) ->raw(', ') ->repr($this->getAttribute('name')) @@ -40,5 +42,3 @@ class EnterProfileNode extends Node ; } } - -class_alias('Twig\Profiler\Node\EnterProfileNode', 'Twig_Profiler_Node_EnterProfile'); diff --git a/vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php b/vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php index 7fbf435..bd9227e 100644 --- a/vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php +++ b/vendor/twig/twig/src/Profiler/Node/LeaveProfileNode.php @@ -11,6 +11,7 @@ namespace Twig\Profiler\Node; +use Twig\Attribute\YieldReady; use Twig\Compiler; use Twig\Node\Node; @@ -19,6 +20,7 @@ use Twig\Node\Node; * * @author Fabien Potencier */ +#[YieldReady] class LeaveProfileNode extends Node { public function __construct(string $varName) @@ -26,13 +28,11 @@ class LeaveProfileNode extends Node parent::__construct([], ['var_name' => $varName]); } - public function compile(Compiler $compiler) + public function compile(Compiler $compiler): void { $compiler ->write("\n") - ->write(sprintf("\$%s->leave(\$%s);\n\n", $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof')) + ->write(\sprintf("\$%s->leave(\$%s);\n\n", $this->getAttribute('var_name'), $this->getAttribute('var_name').'_prof')) ; } } - -class_alias('Twig\Profiler\Node\LeaveProfileNode', 'Twig_Profiler_Node_LeaveProfile'); diff --git a/vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php b/vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php index f19f6b7..4c5c200 100644 --- a/vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php +++ b/vendor/twig/twig/src/Profiler/NodeVisitor/ProfilerNodeVisitor.php @@ -17,7 +17,8 @@ use Twig\Node\BodyNode; use Twig\Node\MacroNode; use Twig\Node\ModuleNode; use Twig\Node\Node; -use Twig\NodeVisitor\AbstractNodeVisitor; +use Twig\Node\Nodes; +use Twig\NodeVisitor\NodeVisitorInterface; use Twig\Profiler\Node\EnterProfileNode; use Twig\Profiler\Node\LeaveProfileNode; use Twig\Profiler\Profile; @@ -25,54 +26,45 @@ use Twig\Profiler\Profile; /** * @author Fabien Potencier */ -final class ProfilerNodeVisitor extends AbstractNodeVisitor +final class ProfilerNodeVisitor implements NodeVisitorInterface { - private $extensionName; + private $varName; - public function __construct(string $extensionName) - { - $this->extensionName = $extensionName; + public function __construct( + private string $extensionName, + ) { + $this->varName = \sprintf('__internal_%s', hash(\PHP_VERSION_ID < 80100 ? 'sha256' : 'xxh128', $extensionName)); } - protected function doEnterNode(Node $node, Environment $env) + public function enterNode(Node $node, Environment $env): Node { return $node; } - protected function doLeaveNode(Node $node, Environment $env) + public function leaveNode(Node $node, Environment $env): ?Node { if ($node instanceof ModuleNode) { - $varName = $this->getVarName(); - $node->setNode('display_start', new Node([new EnterProfileNode($this->extensionName, Profile::TEMPLATE, $node->getTemplateName(), $varName), $node->getNode('display_start')])); - $node->setNode('display_end', new Node([new LeaveProfileNode($varName), $node->getNode('display_end')])); + $node->setNode('display_start', new Nodes([new EnterProfileNode($this->extensionName, Profile::TEMPLATE, $node->getTemplateName(), $this->varName), $node->getNode('display_start')])); + $node->setNode('display_end', new Nodes([new LeaveProfileNode($this->varName), $node->getNode('display_end')])); } elseif ($node instanceof BlockNode) { - $varName = $this->getVarName(); $node->setNode('body', new BodyNode([ - new EnterProfileNode($this->extensionName, Profile::BLOCK, $node->getAttribute('name'), $varName), + new EnterProfileNode($this->extensionName, Profile::BLOCK, $node->getAttribute('name'), $this->varName), $node->getNode('body'), - new LeaveProfileNode($varName), + new LeaveProfileNode($this->varName), ])); } elseif ($node instanceof MacroNode) { - $varName = $this->getVarName(); $node->setNode('body', new BodyNode([ - new EnterProfileNode($this->extensionName, Profile::MACRO, $node->getAttribute('name'), $varName), + new EnterProfileNode($this->extensionName, Profile::MACRO, $node->getAttribute('name'), $this->varName), $node->getNode('body'), - new LeaveProfileNode($varName), + new LeaveProfileNode($this->varName), ])); } return $node; } - private function getVarName(): string - { - return sprintf('__internal_%s', hash('sha256', $this->extensionName)); - } - - public function getPriority() + public function getPriority(): int { return 0; } } - -class_alias('Twig\Profiler\NodeVisitor\ProfilerNodeVisitor', 'Twig_Profiler_NodeVisitor_Profiler'); diff --git a/vendor/twig/twig/src/Profiler/Profile.php b/vendor/twig/twig/src/Profiler/Profile.php index e9726d6..a3c6ee0 100644 --- a/vendor/twig/twig/src/Profiler/Profile.php +++ b/vendor/twig/twig/src/Profiler/Profile.php @@ -13,86 +13,78 @@ namespace Twig\Profiler; /** * @author Fabien Potencier - * - * @final since Twig 2.4.0 */ -class Profile implements \IteratorAggregate, \Serializable +final class Profile implements \IteratorAggregate, \Serializable { - const ROOT = 'ROOT'; - const BLOCK = 'block'; - const TEMPLATE = 'template'; - const MACRO = 'macro'; - - private $template; - private $name; - private $type; + public const ROOT = 'ROOT'; + public const BLOCK = 'block'; + public const TEMPLATE = 'template'; + public const MACRO = 'macro'; private $starts = []; private $ends = []; private $profiles = []; - public function __construct(string $template = 'main', string $type = self::ROOT, string $name = 'main') - { - if (__CLASS__ !== \get_class($this)) { - @trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', E_USER_DEPRECATED); - } - - $this->template = $template; - $this->type = $type; - $this->name = 0 === strpos($name, '__internal_') ? 'INTERNAL' : $name; + public function __construct( + private string $template = 'main', + private string $type = self::ROOT, + private string $name = 'main', + ) { + $this->name = str_starts_with($name, '__internal_') ? 'INTERNAL' : $name; $this->enter(); } - public function getTemplate() + public function getTemplate(): string { return $this->template; } - public function getType() + public function getType(): string { return $this->type; } - public function getName() + public function getName(): string { return $this->name; } - public function isRoot() + public function isRoot(): bool { return self::ROOT === $this->type; } - public function isTemplate() + public function isTemplate(): bool { return self::TEMPLATE === $this->type; } - public function isBlock() + public function isBlock(): bool { return self::BLOCK === $this->type; } - public function isMacro() + public function isMacro(): bool { return self::MACRO === $this->type; } - public function getProfiles() + /** + * @return Profile[] + */ + public function getProfiles(): array { return $this->profiles; } - public function addProfile(self $profile) + public function addProfile(self $profile): void { $this->profiles[] = $profile; } /** * Returns the duration in microseconds. - * - * @return float */ - public function getDuration() + public function getDuration(): float { if ($this->isRoot() && $this->profiles) { // for the root node with children, duration is the sum of all child durations @@ -108,21 +100,33 @@ class Profile implements \IteratorAggregate, \Serializable } /** - * Returns the memory usage in bytes. - * - * @return int + * Returns the start time in microseconds. */ - public function getMemoryUsage() + public function getStartTime(): float + { + return $this->starts['wt'] ?? 0.0; + } + + /** + * Returns the end time in microseconds. + */ + public function getEndTime(): float + { + return $this->ends['wt'] ?? 0.0; + } + + /** + * Returns the memory usage in bytes. + */ + public function getMemoryUsage(): int { return isset($this->ends['mu']) && isset($this->starts['mu']) ? $this->ends['mu'] - $this->starts['mu'] : 0; } /** * Returns the peak memory usage in bytes. - * - * @return int */ - public function getPeakMemoryUsage() + public function getPeakMemoryUsage(): int { return isset($this->ends['pmu']) && isset($this->starts['pmu']) ? $this->ends['pmu'] - $this->starts['pmu'] : 0; } @@ -130,7 +134,7 @@ class Profile implements \IteratorAggregate, \Serializable /** * Starts the profiling. */ - public function enter() + public function enter(): void { $this->starts = [ 'wt' => microtime(true), @@ -142,7 +146,7 @@ class Profile implements \IteratorAggregate, \Serializable /** * Stops the profiling. */ - public function leave() + public function leave(): void { $this->ends = [ 'wt' => microtime(true), @@ -151,23 +155,23 @@ class Profile implements \IteratorAggregate, \Serializable ]; } - public function reset() + public function reset(): void { $this->starts = $this->ends = $this->profiles = []; $this->enter(); } - public function getIterator() + public function getIterator(): \Traversable { return new \ArrayIterator($this->profiles); } - public function serialize() + public function serialize(): string { return serialize($this->__serialize()); } - public function unserialize($data) + public function unserialize($data): void { $this->__unserialize(unserialize($data)); } @@ -175,7 +179,7 @@ class Profile implements \IteratorAggregate, \Serializable /** * @internal */ - public function __serialize() + public function __serialize(): array { return [$this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles]; } @@ -183,10 +187,8 @@ class Profile implements \IteratorAggregate, \Serializable /** * @internal */ - public function __unserialize(array $data) + public function __unserialize(array $data): void { - list($this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles) = $data; + [$this->template, $this->name, $this->type, $this->starts, $this->ends, $this->profiles] = $data; } } - -class_alias('Twig\Profiler\Profile', 'Twig_Profiler_Profile'); diff --git a/vendor/twig/twig/src/Resources/core.php b/vendor/twig/twig/src/Resources/core.php new file mode 100644 index 0000000..bc0b271 --- /dev/null +++ b/vendor/twig/twig/src/Resources/core.php @@ -0,0 +1,541 @@ +getCharset(), $values, $max); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_date_format_filter(Environment $env, $date, $format = null, $timezone = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return $env->getExtension(CoreExtension::class)->formatDate($date, $format, $timezone); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_date_modify_filter(Environment $env, $date, $modifier) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return $env->getExtension(CoreExtension::class)->modifyDate($date, $modifier); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_sprintf($format, ...$values) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::sprintf($format, ...$values); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_date_converter(Environment $env, $date = null, $timezone = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return $env->getExtension(CoreExtension::class)->convertDate($date, $timezone); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_replace_filter($str, $from) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::replace($str, $from); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_round($value, $precision = 0, $method = 'common') +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::round($value, $precision, $method); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_number_format_filter(Environment $env, $number, $decimal = null, $decimalPoint = null, $thousandSep = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return $env->getExtension(CoreExtension::class)->formatNumber($number, $decimal, $decimalPoint, $thousandSep); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_urlencode_filter($url) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::urlencode($url); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_merge(...$arrays) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::merge(...$arrays); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_slice(Environment $env, $item, $start, $length = null, $preserveKeys = false) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::slice($env->getCharset(), $item, $start, $length, $preserveKeys); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_first(Environment $env, $item) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::first($env->getCharset(), $item); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_last(Environment $env, $item) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::last($env->getCharset(), $item); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_join_filter($value, $glue = '', $and = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::join($value, $glue, $and); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_split_filter(Environment $env, $value, $delimiter, $limit = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::split($env->getCharset(), $value, $delimiter, $limit); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_get_array_keys_filter($array) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::keys($array); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_reverse_filter(Environment $env, $item, $preserveKeys = false) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::reverse($env->getCharset(), $item, $preserveKeys); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_sort_filter(Environment $env, $array, $arrow = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::sort($env, $array, $arrow); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_matches(string $regexp, ?string $str) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::matches($regexp, $str); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_trim_filter($string, $characterMask = null, $side = 'both') +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::trim($string, $characterMask, $side); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_nl2br($string) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::nl2br($string); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_spaceless($content) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::spaceless($content); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_convert_encoding($string, $to, $from) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::convertEncoding($string, $to, $from); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_length_filter(Environment $env, $thing) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::length($env->getCharset(), $thing); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_upper_filter(Environment $env, $string) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::upper($env->getCharset(), $string); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_lower_filter(Environment $env, $string) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::lower($env->getCharset(), $string); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_striptags($string, $allowable_tags = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::striptags($string, $allowable_tags); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_title_string_filter(Environment $env, $string) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::titleCase($env->getCharset(), $string); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_capitalize_string_filter(Environment $env, $string) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::capitalize($env->getCharset(), $string); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_test_empty($value) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::testEmpty($value); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_test_iterable($value) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return is_iterable($value); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_include(Environment $env, $context, $template, $variables = [], $withContext = true, $ignoreMissing = false, $sandboxed = false) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::include($env, $context, $template, $variables, $withContext, $ignoreMissing, $sandboxed); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_source(Environment $env, $name, $ignoreMissing = false) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::source($env, $name, $ignoreMissing); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_constant($constant, $object = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::constant($constant, $object); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_constant_is_defined($constant, $object = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::constant($constant, $object, true); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_batch($items, $size, $fill = null, $preserveKeys = true) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::batch($items, $size, $fill, $preserveKeys); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_column($array, $name, $index = null): array +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::column($array, $name, $index); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_filter(Environment $env, $array, $arrow) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::filter($env, $array, $arrow); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_map(Environment $env, $array, $arrow) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::map($env, $array, $arrow); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_reduce(Environment $env, $array, $arrow, $initial = null) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::reduce($env, $array, $arrow, $initial); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_some(Environment $env, $array, $arrow) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::arraySome($env, $array, $arrow); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_array_every(Environment $env, $array, $arrow) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return CoreExtension::arrayEvery($env, $array, $arrow); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_check_arrow_in_sandbox(Environment $env, $arrow, $thing, $type) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + CoreExtension::checkArrow($env, $arrow, $thing, $type); +} diff --git a/vendor/twig/twig/src/Resources/debug.php b/vendor/twig/twig/src/Resources/debug.php new file mode 100644 index 0000000..104b4f4 --- /dev/null +++ b/vendor/twig/twig/src/Resources/debug.php @@ -0,0 +1,25 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Twig\Environment; +use Twig\Extension\DebugExtension; + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_var_dump(Environment $env, $context, ...$vars) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + DebugExtension::dump($env, $context, ...$vars); +} diff --git a/vendor/twig/twig/src/Resources/escaper.php b/vendor/twig/twig/src/Resources/escaper.php new file mode 100644 index 0000000..a2ee8e7 --- /dev/null +++ b/vendor/twig/twig/src/Resources/escaper.php @@ -0,0 +1,51 @@ +getRuntime(EscaperRuntime::class)->escape($string, $strategy, $charset, $autoescape); +} + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_escape_filter_is_safe(Node $filterArgs) +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return EscaperExtension::escapeFilterIsSafe($filterArgs); +} diff --git a/vendor/twig/twig/src/Resources/string_loader.php b/vendor/twig/twig/src/Resources/string_loader.php new file mode 100644 index 0000000..8f0e649 --- /dev/null +++ b/vendor/twig/twig/src/Resources/string_loader.php @@ -0,0 +1,26 @@ + + * + * For the full copyright and license information, please view the LICENSE + * file that was distributed with this source code. + */ + +use Twig\Environment; +use Twig\Extension\StringLoaderExtension; +use Twig\TemplateWrapper; + +/** + * @internal + * + * @deprecated since Twig 3.9 + */ +function twig_template_from_string(Environment $env, $template, ?string $name = null): TemplateWrapper +{ + trigger_deprecation('twig/twig', '3.9', 'Using the internal "%s" function is deprecated.', __FUNCTION__); + + return StringLoaderExtension::templateFromString($env, $template, $name); +} diff --git a/vendor/twig/twig/src/Runtime/EscaperRuntime.php b/vendor/twig/twig/src/Runtime/EscaperRuntime.php new file mode 100644 index 0000000..17ed76c --- /dev/null +++ b/vendor/twig/twig/src/Runtime/EscaperRuntime.php @@ -0,0 +1,340 @@ + */ + private $escapers = []; + + /** @internal */ + public $safeClasses = []; + + /** @internal */ + public $safeLookup = []; + + public function __construct( + private $charset = 'UTF-8', + ) { + } + + /** + * Defines a new escaper to be used via the escape filter. + * + * @param string $strategy The strategy name that should be used as a strategy in the escape call + * @param callable(string $string, string $charset): string $callable A valid PHP callable + * + * @return void + */ + public function setEscaper($strategy, callable $callable) + { + $this->escapers[$strategy] = $callable; + } + + /** + * Gets all defined escapers. + * + * @return array An array of escapers + */ + public function getEscapers() + { + return $this->escapers; + } + + /** + * @param array, string[]> $safeClasses + * + * @return void + */ + public function setSafeClasses(array $safeClasses = []) + { + $this->safeClasses = []; + $this->safeLookup = []; + foreach ($safeClasses as $class => $strategies) { + $this->addSafeClass($class, $strategies); + } + } + + /** + * @param class-string<\Stringable> $class + * @param string[] $strategies + * + * @return void + */ + public function addSafeClass(string $class, array $strategies) + { + $class = ltrim($class, '\\'); + if (!isset($this->safeClasses[$class])) { + $this->safeClasses[$class] = []; + } + $this->safeClasses[$class] = array_merge($this->safeClasses[$class], $strategies); + + foreach ($strategies as $strategy) { + $this->safeLookup[$strategy][$class] = true; + } + } + + /** + * Escapes a string. + * + * @param mixed $string The value to be escaped + * @param string $strategy The escaping strategy + * @param string|null $charset The charset + * @param bool $autoescape Whether the function is called by the auto-escaping feature (true) or by the developer (false) + * + * @throws RuntimeError + */ + public function escape($string, string $strategy = 'html', ?string $charset = null, bool $autoescape = false) + { + if ($autoescape && $string instanceof Markup) { + return $string; + } + + if (!\is_string($string)) { + if ($string instanceof \Stringable) { + if ($autoescape) { + $c = $string::class; + if (!isset($this->safeClasses[$c])) { + $this->safeClasses[$c] = []; + foreach (class_parents($string) + class_implements($string) as $class) { + if (isset($this->safeClasses[$class])) { + $this->safeClasses[$c] = array_unique(array_merge($this->safeClasses[$c], $this->safeClasses[$class])); + foreach ($this->safeClasses[$class] as $s) { + $this->safeLookup[$s][$c] = true; + } + } + } + } + if (isset($this->safeLookup[$strategy][$c]) || isset($this->safeLookup['all'][$c])) { + return (string) $string; + } + } + + $string = (string) $string; + } elseif (\in_array($strategy, ['html', 'js', 'css', 'html_attr', 'url'], true)) { + // we return the input as is (which can be of any type) + return $string; + } + } + + if ('' === $string) { + return ''; + } + + $charset = $charset ?: $this->charset; + + switch ($strategy) { + case 'html': + // see https://www.php.net/htmlspecialchars + + // Using a static variable to avoid initializing the array + // each time the function is called. Moving the declaration on the + // top of the function slow downs other escaping strategies. + static $htmlspecialcharsCharsets = [ + 'ISO-8859-1' => true, 'ISO8859-1' => true, + 'ISO-8859-15' => true, 'ISO8859-15' => true, + 'utf-8' => true, 'UTF-8' => true, + 'CP866' => true, 'IBM866' => true, '866' => true, + 'CP1251' => true, 'WINDOWS-1251' => true, 'WIN-1251' => true, + '1251' => true, + 'CP1252' => true, 'WINDOWS-1252' => true, '1252' => true, + 'KOI8-R' => true, 'KOI8-RU' => true, 'KOI8R' => true, + 'BIG5' => true, '950' => true, + 'GB2312' => true, '936' => true, + 'BIG5-HKSCS' => true, + 'SHIFT_JIS' => true, 'SJIS' => true, '932' => true, + 'EUC-JP' => true, 'EUCJP' => true, + 'ISO8859-5' => true, 'ISO-8859-5' => true, 'MACROMAN' => true, + ]; + + if (isset($htmlspecialcharsCharsets[$charset])) { + return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); + } + + if (isset($htmlspecialcharsCharsets[strtoupper($charset)])) { + // cache the lowercase variant for future iterations + $htmlspecialcharsCharsets[$charset] = true; + + return htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, $charset); + } + + $string = $this->convertEncoding($string, 'UTF-8', $charset); + $string = htmlspecialchars($string, \ENT_QUOTES | \ENT_SUBSTITUTE, 'UTF-8'); + + return iconv('UTF-8', $charset, $string); + + case 'js': + // escape all non-alphanumeric characters + // into their \x or \uHHHH representations + if ('UTF-8' !== $charset) { + $string = $this->convertEncoding($string, 'UTF-8', $charset); + } + + if (!preg_match('//u', $string)) { + throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9,\._]#Su', function ($matches) { + $char = $matches[0]; + + /* + * A few characters have short escape sequences in JSON and JavaScript. + * Escape sequences supported only by JavaScript, not JSON, are omitted. + * \" is also supported but omitted, because the resulting string is not HTML safe. + */ + static $shortMap = [ + '\\' => '\\\\', + '/' => '\\/', + "\x08" => '\b', + "\x0C" => '\f', + "\x0A" => '\n', + "\x0D" => '\r', + "\x09" => '\t', + ]; + + if (isset($shortMap[$char])) { + return $shortMap[$char]; + } + + $codepoint = mb_ord($char, 'UTF-8'); + if (0x10000 > $codepoint) { + return \sprintf('\u%04X', $codepoint); + } + + // Split characters outside the BMP into surrogate pairs + // https://tools.ietf.org/html/rfc2781.html#section-2.1 + $u = $codepoint - 0x10000; + $high = 0xD800 | ($u >> 10); + $low = 0xDC00 | ($u & 0x3FF); + + return \sprintf('\u%04X\u%04X', $high, $low); + }, $string); + + if ('UTF-8' !== $charset) { + $string = iconv('UTF-8', $charset, $string); + } + + return $string; + + case 'css': + if ('UTF-8' !== $charset) { + $string = $this->convertEncoding($string, 'UTF-8', $charset); + } + + if (!preg_match('//u', $string)) { + throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9]#Su', function ($matches) { + $char = $matches[0]; + + return \sprintf('\\%X ', 1 === \strlen($char) ? \ord($char) : mb_ord($char, 'UTF-8')); + }, $string); + + if ('UTF-8' !== $charset) { + $string = iconv('UTF-8', $charset, $string); + } + + return $string; + + case 'html_attr': + if ('UTF-8' !== $charset) { + $string = $this->convertEncoding($string, 'UTF-8', $charset); + } + + if (!preg_match('//u', $string)) { + throw new RuntimeError('The string to escape is not a valid UTF-8 string.'); + } + + $string = preg_replace_callback('#[^a-zA-Z0-9,\.\-_]#Su', function ($matches) { + /** + * This function is adapted from code coming from Zend Framework. + * + * @copyright Copyright (c) 2005-2012 Zend Technologies USA Inc. (https://www.zend.com) + * @license https://framework.zend.com/license/new-bsd New BSD License + */ + $chr = $matches[0]; + $ord = \ord($chr); + + /* + * The following replaces characters undefined in HTML with the + * hex entity for the Unicode replacement character. + */ + if (($ord <= 0x1F && "\t" != $chr && "\n" != $chr && "\r" != $chr) || ($ord >= 0x7F && $ord <= 0x9F)) { + return '�'; + } + + /* + * Check if the current character to escape has a name entity we should + * replace it with while grabbing the hex value of the character. + */ + if (1 === \strlen($chr)) { + /* + * While HTML supports far more named entities, the lowest common denominator + * has become HTML5's XML Serialisation which is restricted to the those named + * entities that XML supports. Using HTML entities would result in this error: + * XML Parsing Error: undefined entity + */ + static $entityMap = [ + 34 => '"', /* quotation mark */ + 38 => '&', /* ampersand */ + 60 => '<', /* less-than sign */ + 62 => '>', /* greater-than sign */ + ]; + + if (isset($entityMap[$ord])) { + return $entityMap[$ord]; + } + + return \sprintf('&#x%02X;', $ord); + } + + /* + * Per OWASP recommendations, we'll use hex entities for any other + * characters where a named entity does not exist. + */ + return \sprintf('&#x%04X;', mb_ord($chr, 'UTF-8')); + }, $string); + + if ('UTF-8' !== $charset) { + $string = iconv('UTF-8', $charset, $string); + } + + return $string; + + case 'url': + return rawurlencode($string); + + default: + if (\array_key_exists($strategy, $this->escapers)) { + return $this->escapers[$strategy]($string, $charset); + } + + $validStrategies = implode('", "', array_merge(['html', 'js', 'url', 'css', 'html_attr'], array_keys($this->escapers))); + + throw new RuntimeError(\sprintf('Invalid escaping strategy "%s" (valid ones: "%s").', $strategy, $validStrategies)); + } + } + + private function convertEncoding(string $string, string $to, string $from) + { + if (!\function_exists('iconv')) { + throw new RuntimeError('Unable to convert encoding: required function iconv() does not exist. You should install ext-iconv or symfony/polyfill-iconv.'); + } + + return iconv($from, $to, $string); + } +} diff --git a/vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php b/vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php index 04a6602..0510668 100644 --- a/vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php +++ b/vendor/twig/twig/src/RuntimeLoader/ContainerRuntimeLoader.php @@ -23,19 +23,13 @@ use Psr\Container\ContainerInterface; */ class ContainerRuntimeLoader implements RuntimeLoaderInterface { - private $container; - - public function __construct(ContainerInterface $container) - { - $this->container = $container; + public function __construct( + private ContainerInterface $container, + ) { } - public function load($class) + public function load(string $class) { - if ($this->container->has($class)) { - return $this->container->get($class); - } + return $this->container->has($class) ? $this->container->get($class) : null; } } - -class_alias('Twig\RuntimeLoader\ContainerRuntimeLoader', 'Twig_ContainerRuntimeLoader'); diff --git a/vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php b/vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php index e4676f7..5d4e70b 100644 --- a/vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php +++ b/vendor/twig/twig/src/RuntimeLoader/FactoryRuntimeLoader.php @@ -18,24 +18,22 @@ namespace Twig\RuntimeLoader; */ class FactoryRuntimeLoader implements RuntimeLoaderInterface { - private $map; - /** * @param array $map An array where keys are class names and values factory callables */ - public function __construct(array $map = []) - { - $this->map = $map; + public function __construct( + private array $map = [], + ) { } - public function load($class) + public function load(string $class) { - if (isset($this->map[$class])) { - $runtimeFactory = $this->map[$class]; - - return $runtimeFactory(); + if (!isset($this->map[$class])) { + return null; } + + $runtimeFactory = $this->map[$class]; + + return $runtimeFactory(); } } - -class_alias('Twig\RuntimeLoader\FactoryRuntimeLoader', 'Twig_FactoryRuntimeLoader'); diff --git a/vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php b/vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php index 4eb5ad8..9e5b204 100644 --- a/vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php +++ b/vendor/twig/twig/src/RuntimeLoader/RuntimeLoaderInterface.php @@ -21,11 +21,7 @@ interface RuntimeLoaderInterface /** * Creates the runtime implementation of a Twig element (filter/function/test). * - * @param string $class A runtime class - * * @return object|null The runtime instance or null if the loader does not know how to create the runtime for this class */ - public function load($class); + public function load(string $class); } - -class_alias('Twig\RuntimeLoader\RuntimeLoaderInterface', 'Twig_RuntimeLoaderInterface'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityError.php b/vendor/twig/twig/src/Sandbox/SecurityError.php index 5f96d46..30a404f 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityError.php +++ b/vendor/twig/twig/src/Sandbox/SecurityError.php @@ -21,5 +21,3 @@ use Twig\Error\Error; class SecurityError extends Error { } - -class_alias('Twig\Sandbox\SecurityError', 'Twig_Sandbox_SecurityError'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php index 767ec5b..9293a3f 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php +++ b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFilterError.php @@ -15,32 +15,19 @@ namespace Twig\Sandbox; * Exception thrown when a not allowed filter is used in a template. * * @author Martin Hasoň - * - * @final */ -class SecurityNotAllowedFilterError extends SecurityError +final class SecurityNotAllowedFilterError extends SecurityError { - private $filterName; + private string $filterName; - public function __construct(string $message, string $functionName, int $lineno = -1, string $filename = null, \Exception $previous = null) + public function __construct(string $message, string $functionName) { - if (-1 !== $lineno) { - @trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $filename) { - @trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $previous) { - @trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - parent::__construct($message, $lineno, $filename, $previous); + parent::__construct($message); $this->filterName = $functionName; } - public function getFilterName() + public function getFilterName(): string { return $this->filterName; } } - -class_alias('Twig\Sandbox\SecurityNotAllowedFilterError', 'Twig_Sandbox_SecurityNotAllowedFilterError'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php index 5a30139..71c9f02 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php +++ b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedFunctionError.php @@ -15,32 +15,19 @@ namespace Twig\Sandbox; * Exception thrown when a not allowed function is used in a template. * * @author Martin Hasoň - * - * @final */ -class SecurityNotAllowedFunctionError extends SecurityError +final class SecurityNotAllowedFunctionError extends SecurityError { - private $functionName; + private string $functionName; - public function __construct(string $message, string $functionName, int $lineno = -1, string $filename = null, \Exception $previous = null) + public function __construct(string $message, string $functionName) { - if (-1 !== $lineno) { - @trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $filename) { - @trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $previous) { - @trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - parent::__construct($message, $lineno, $filename, $previous); + parent::__construct($message); $this->functionName = $functionName; } - public function getFunctionName() + public function getFunctionName(): string { return $this->functionName; } } - -class_alias('Twig\Sandbox\SecurityNotAllowedFunctionError', 'Twig_Sandbox_SecurityNotAllowedFunctionError'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php index c8103ea..98e8e43 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php +++ b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedMethodError.php @@ -15,39 +15,26 @@ namespace Twig\Sandbox; * Exception thrown when a not allowed class method is used in a template. * * @author Kit Burton-Senior - * - * @final */ -class SecurityNotAllowedMethodError extends SecurityError +final class SecurityNotAllowedMethodError extends SecurityError { - private $className; - private $methodName; + private string $className; + private string $methodName; - public function __construct(string $message, string $className, string $methodName, int $lineno = -1, string $filename = null, \Exception $previous = null) + public function __construct(string $message, string $className, string $methodName) { - if (-1 !== $lineno) { - @trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $filename) { - @trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $previous) { - @trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - parent::__construct($message, $lineno, $filename, $previous); + parent::__construct($message); $this->className = $className; $this->methodName = $methodName; } - public function getClassName() + public function getClassName(): string { return $this->className; } - public function getMethodName() + public function getMethodName(): string { return $this->methodName; } } - -class_alias('Twig\Sandbox\SecurityNotAllowedMethodError', 'Twig_Sandbox_SecurityNotAllowedMethodError'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php index d148f08..e74ffed 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php +++ b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedPropertyError.php @@ -15,39 +15,26 @@ namespace Twig\Sandbox; * Exception thrown when a not allowed class property is used in a template. * * @author Kit Burton-Senior - * - * @final */ -class SecurityNotAllowedPropertyError extends SecurityError +final class SecurityNotAllowedPropertyError extends SecurityError { - private $className; - private $propertyName; + private string $className; + private string $propertyName; - public function __construct(string $message, string $className, string $propertyName, int $lineno = -1, string $filename = null, \Exception $previous = null) + public function __construct(string $message, string $className, string $propertyName) { - if (-1 !== $lineno) { - @trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $filename) { - @trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $previous) { - @trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - parent::__construct($message, $lineno, $filename, $previous); + parent::__construct($message); $this->className = $className; $this->propertyName = $propertyName; } - public function getClassName() + public function getClassName(): string { return $this->className; } - public function getPropertyName() + public function getPropertyName(): string { return $this->propertyName; } } - -class_alias('Twig\Sandbox\SecurityNotAllowedPropertyError', 'Twig_Sandbox_SecurityNotAllowedPropertyError'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php index 25f6361..f9cd625 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php +++ b/vendor/twig/twig/src/Sandbox/SecurityNotAllowedTagError.php @@ -15,32 +15,19 @@ namespace Twig\Sandbox; * Exception thrown when a not allowed tag is used in a template. * * @author Martin Hasoň - * - * @final */ -class SecurityNotAllowedTagError extends SecurityError +final class SecurityNotAllowedTagError extends SecurityError { - private $tagName; + private string $tagName; - public function __construct(string $message, string $tagName, int $lineno = -1, string $filename = null, \Exception $previous = null) + public function __construct(string $message, string $tagName) { - if (-1 !== $lineno) { - @trigger_error(sprintf('Passing $lineno as a 3th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $filename) { - @trigger_error(sprintf('Passing $filename as a 4th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - if (null !== $previous) { - @trigger_error(sprintf('Passing $previous as a 5th argument of the %s constructor is deprecated since Twig 2.8.1.', __CLASS__), E_USER_DEPRECATED); - } - parent::__construct($message, $lineno, $filename, $previous); + parent::__construct($message); $this->tagName = $tagName; } - public function getTagName() + public function getTagName(): string { return $this->tagName; } } - -class_alias('Twig\Sandbox\SecurityNotAllowedTagError', 'Twig_Sandbox_SecurityNotAllowedTagError'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityPolicy.php b/vendor/twig/twig/src/Sandbox/SecurityPolicy.php index 1406e80..b2c83ee 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityPolicy.php +++ b/vendor/twig/twig/src/Sandbox/SecurityPolicy.php @@ -36,93 +36,95 @@ final class SecurityPolicy implements SecurityPolicyInterface $this->allowedFunctions = $allowedFunctions; } - public function setAllowedTags(array $tags) + public function setAllowedTags(array $tags): void { $this->allowedTags = $tags; } - public function setAllowedFilters(array $filters) + public function setAllowedFilters(array $filters): void { $this->allowedFilters = $filters; } - public function setAllowedMethods(array $methods) + public function setAllowedMethods(array $methods): void { $this->allowedMethods = []; foreach ($methods as $class => $m) { - $this->allowedMethods[$class] = array_map(function ($value) { return strtr($value, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); }, \is_array($m) ? $m : [$m]); + $this->allowedMethods[$class] = array_map('strtolower', \is_array($m) ? $m : [$m]); } } - public function setAllowedProperties(array $properties) + public function setAllowedProperties(array $properties): void { $this->allowedProperties = $properties; } - public function setAllowedFunctions(array $functions) + public function setAllowedFunctions(array $functions): void { $this->allowedFunctions = $functions; } - public function checkSecurity($tags, $filters, $functions) + 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 (!\in_array($tag, $this->allowedTags, true)) { + 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); + } } } foreach ($filters as $filter) { - if (!\in_array($filter, $this->allowedFilters)) { - throw new SecurityNotAllowedFilterError(sprintf('Filter "%s" is not allowed.', $filter), $filter); + if (!\in_array($filter, $this->allowedFilters, true)) { + throw new SecurityNotAllowedFilterError(\sprintf('Filter "%s" is not allowed.', $filter), $filter); } } foreach ($functions as $function) { - if (!\in_array($function, $this->allowedFunctions)) { - throw new SecurityNotAllowedFunctionError(sprintf('Function "%s" is not allowed.', $function), $function); + if (!\in_array($function, $this->allowedFunctions, true)) { + throw new SecurityNotAllowedFunctionError(\sprintf('Function "%s" is not allowed.', $function), $function); } } } - public function checkMethodAllowed($obj, $method) + public function checkMethodAllowed($obj, $method): void { if ($obj instanceof Template || $obj instanceof Markup) { return; } $allowed = false; - $method = strtr($method, 'ABCDEFGHIJKLMNOPQRSTUVWXYZ', 'abcdefghijklmnopqrstuvwxyz'); + $method = strtolower($method); foreach ($this->allowedMethods as $class => $methods) { - if ($obj instanceof $class) { - $allowed = \in_array($method, $methods); - + if ($obj instanceof $class && \in_array($method, $methods, true)) { + $allowed = true; break; } } if (!$allowed) { - $class = \get_class($obj); - throw new SecurityNotAllowedMethodError(sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, $class), $class, $method); + $class = $obj::class; + throw new SecurityNotAllowedMethodError(\sprintf('Calling "%s" method on a "%s" object is not allowed.', $method, $class), $class, $method); } } - public function checkPropertyAllowed($obj, $property) + public function checkPropertyAllowed($obj, $property): void { $allowed = false; foreach ($this->allowedProperties as $class => $properties) { - if ($obj instanceof $class) { - $allowed = \in_array($property, \is_array($properties) ? $properties : [$properties]); - + if ($obj instanceof $class && \in_array($property, \is_array($properties) ? $properties : [$properties], true)) { + $allowed = true; break; } } if (!$allowed) { - $class = \get_class($obj); - throw new SecurityNotAllowedPropertyError(sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, $class), $class, $property); + $class = $obj::class; + throw new SecurityNotAllowedPropertyError(\sprintf('Calling "%s" property on a "%s" object is not allowed.', $property, $class), $class, $property); } } } - -class_alias('Twig\Sandbox\SecurityPolicy', 'Twig_Sandbox_SecurityPolicy'); diff --git a/vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php b/vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php index 8b2ab4a..36471c5 100644 --- a/vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php +++ b/vendor/twig/twig/src/Sandbox/SecurityPolicyInterface.php @@ -19,19 +19,27 @@ namespace Twig\Sandbox; interface SecurityPolicyInterface { /** + * @param string[] $tags + * @param string[] $filters + * @param string[] $functions + * * @throws SecurityError */ - public function checkSecurity($tags, $filters, $functions); + public function checkSecurity($tags, $filters, $functions): void; /** + * @param object $obj + * @param string $method + * * @throws SecurityNotAllowedMethodError */ - public function checkMethodAllowed($obj, $method); + public function checkMethodAllowed($obj, $method): void; /** + * @param object $obj + * @param string $property + * * @throws SecurityNotAllowedPropertyError */ - public function checkPropertyAllowed($obj, $method); + public function checkPropertyAllowed($obj, $property): void; } - -class_alias('Twig\Sandbox\SecurityPolicyInterface', 'Twig_Sandbox_SecurityPolicyInterface'); diff --git a/vendor/twig/twig/src/Sandbox/SourcePolicyInterface.php b/vendor/twig/twig/src/Sandbox/SourcePolicyInterface.php new file mode 100644 index 0000000..b952f1e --- /dev/null +++ b/vendor/twig/twig/src/Sandbox/SourcePolicyInterface.php @@ -0,0 +1,24 @@ +code = $code; - $this->name = $name; - $this->path = $path; + public function __construct( + private string $code, + private string $name, + private string $path = '', + ) { } public function getCode(): string @@ -39,7 +35,7 @@ final class Source return $this->code; } - public function getName() + public function getName(): string { return $this->name; } @@ -49,5 +45,3 @@ final class Source return $this->path; } } - -class_alias('Twig\Source', 'Twig_Source'); diff --git a/vendor/twig/twig/src/Template.php b/vendor/twig/twig/src/Template.php index 0f09e19..faf7aee 100644 --- a/vendor/twig/twig/src/Template.php +++ b/vendor/twig/twig/src/Template.php @@ -13,7 +13,6 @@ namespace Twig; use Twig\Error\Error; -use Twig\Error\LoaderError; use Twig\Error\RuntimeError; /** @@ -29,55 +28,43 @@ use Twig\Error\RuntimeError; */ abstract class Template { - const ANY_CALL = 'any'; - const ARRAY_CALL = 'array'; - const METHOD_CALL = 'method'; + public const ANY_CALL = 'any'; + public const ARRAY_CALL = 'array'; + public const METHOD_CALL = 'method'; protected $parent; protected $parents = []; - protected $env; protected $blocks = []; protected $traits = []; + protected $traitAliases = []; protected $extensions = []; protected $sandbox; - public function __construct(Environment $env) - { - $this->env = $env; + private $useYield; + + public function __construct( + protected Environment $env, + ) { + $this->useYield = $env->useYield(); $this->extensions = $env->getExtensions(); } - /** - * @internal this method will be removed in 3.0 and is only used internally to provide an upgrade path from 1.x to 2.0 - */ - public function __toString() - { - return $this->getTemplateName(); - } - /** * 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 */ - public function getSourceContext() - { - return new Source('', $this->getTemplateName()); - } + abstract public function getSourceContext(): Source; /** * Returns the parent template. @@ -85,46 +72,35 @@ abstract class Template * This method is for internal use only and should never be called * directly. * - * @param array $context - * - * @return Template|TemplateWrapper|false The parent template or false if there is no parent + * @return self|TemplateWrapper|false The parent template or false if there is no parent */ - public function getParent(array $context) + public function getParent(array $context): self|TemplateWrapper|false { if (null !== $this->parent) { return $this->parent; } - try { - $parent = $this->doGetParent($context); + if (!$parent = $this->doGetParent($context)) { + return false; + } - if (false === $parent) { - return false; - } + if ($parent instanceof self || $parent instanceof TemplateWrapper) { + return $this->parents[$parent->getSourceContext()->getName()] = $parent; + } - if ($parent instanceof self || $parent instanceof TemplateWrapper) { - return $this->parents[$parent->getSourceContext()->getName()] = $parent; - } - - if (!isset($this->parents[$parent])) { - $this->parents[$parent] = $this->loadTemplate($parent); - } - } catch (LoaderError $e) { - $e->setSourceContext(null); - $e->guess(); - - throw $e; + if (!isset($this->parents[$parent])) { + $this->parents[$parent] = $this->load($parent, -1); } 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; } @@ -139,14 +115,10 @@ abstract class Template * @param array $context The context * @param array $blocks The current set of blocks */ - public function displayParentBlock($name, array $context, array $blocks = []) + public function displayParentBlock($name, array $context, array $blocks = []): void { - if (isset($this->traits[$name])) { - $this->traits[$name][0]->displayBlock($name, $context, $blocks, false); - } elseif (false !== $parent = $this->getParent($context)) { - $parent->displayBlock($name, $context, $blocks, false); - } else { - throw new RuntimeError(sprintf('The template has no parent and no traits defining the "%s" block.', $name), -1, $this->getSourceContext()); + foreach ($this->yieldParentBlock($name, $context, $blocks) as $data) { + echo $data; } } @@ -161,7 +133,297 @@ abstract class Template * @param array $blocks The current set of blocks * @param bool $useBlocks Whether to use the current set of blocks */ - public function displayBlock($name, array $context, array $blocks = [], $useBlocks = true, self $templateContext = null) + public function displayBlock($name, array $context, array $blocks = [], $useBlocks = true, ?self $templateContext = null): void + { + foreach ($this->yieldBlock($name, $context, $blocks, $useBlocks, $templateContext) as $data) { + echo $data; + } + } + + /** + * Renders a parent block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to render from the parent + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return string The rendered block + */ + public function renderParentBlock($name, array $context, array $blocks = []): string + { + 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; + } + + return $content; + } + + /** + * Renders a block. + * + * This method is for internal use only and should never be called + * directly. + * + * @param string $name The block name to render + * @param array $context The context + * @param array $blocks The current set of blocks + * @param bool $useBlocks Whether to use the current set of blocks + * + * @return string The rendered block + */ + public function renderBlock($name, array $context, array $blocks = [], $useBlocks = true): string + { + 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; + } + + return $content; + } + + /** + * Returns whether a block exists or not in the current context of the template. + * + * This method checks blocks defined in the current template + * or defined in "used" traits or defined in parent templates. + * + * @param string $name The block name + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return bool true if the block exists, false otherwise + */ + public function hasBlock($name, array $context, array $blocks = []): bool + { + if (isset($blocks[$name])) { + return $blocks[$name][0] instanceof self; + } + + if (isset($this->blocks[$name])) { + return true; + } + + if ($parent = $this->getParent($context)) { + return $parent->hasBlock($name, $context); + } + + return false; + } + + /** + * Returns all block names in the current context of the template. + * + * This method checks blocks defined in the current template + * or defined in "used" traits or defined in parent templates. + * + * @param array $context The context + * @param array $blocks The current set of blocks + * + * @return array An array of block names + */ + public function getBlockNames(array $context, array $blocks = []): array + { + $names = array_merge(array_keys($blocks), array_keys($this->blocks)); + + if ($parent = $this->getParent($context)) { + $names = array_merge($names, $parent->getBlockNames($context)); + } + + return array_unique($names); + } + + /** + * @param string|TemplateWrapper|array $template + */ + protected function load(string|TemplateWrapper|array $template, int $line, int|null $index = null): self + { + try { + if (\is_array($template)) { + return $this->env->resolveTemplate($template)->unwrap(); + } + + if ($template instanceof TemplateWrapper) { + return $template->unwrap(); + } + + if ($template === $this->getTemplateName()) { + $class = static::class; + if (false !== $pos = strrpos($class, '___', -1)) { + $class = substr($class, 0, $pos); + } + } else { + $class = $this->env->getTemplateClass($template); + } + + return $this->env->loadTemplate($class, $template, $index); + } catch (Error $e) { + if (!$e->getSourceContext()) { + $e->setSourceContext($this->getSourceContext()); + } + + if ($e->getTemplateLine() > 0) { + throw $e; + } + + if (-1 === $line) { + $e->guess(); + } else { + $e->setTemplateLine($line); + } + + throw $e; + } + } + + /** + * @param string|TemplateWrapper|array $template + * + * @deprecated since Twig 3.21 and will be removed in 4.0. Use Template::load() instead. + */ + protected function loadTemplate($template, $templateName = null, int|null $line = null, int|null $index = null): self|TemplateWrapper + { + trigger_deprecation('twig/twig', '3.21', 'The "%s" method is deprecated.', __METHOD__); + + if (null === $line) { + $line = -1; + } + + if ($template instanceof self) { + return $template; + } + + return $this->load($template, $line, $index); + } + + /** + * @internal + * + * @return $this + */ + public function unwrap(): self + { + return $this; + } + + /** + * Returns all blocks. + * + * This method is for internal use only and should never be called + * directly. + * + * @return array An array of blocks + */ + public function getBlocks(): array + { + return $this->blocks; + } + + public function display(array $context, array $blocks = []): void + { + foreach ($this->yield($context, $blocks) as $data) { + echo $data; + } + } + + 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; + } + + return $content; + } + + /** + * @return iterable + */ + public function yield(array $context, array $blocks = []): iterable + { + $context += $this->env->getGlobals(); + $blocks = array_merge($this->blocks, $blocks); + + try { + yield from $this->doDisplay($context, $blocks); + } catch (Error $e) { + if (!$e->getSourceContext()) { + $e->setSourceContext($this->getSourceContext()); + } + + // this is mostly useful for \Twig\Error\LoaderError exceptions + // see \Twig\Error\LoaderError + if (-1 === $e->getTemplateLine()) { + $e->guess(); + } + + throw $e; + } catch (\Throwable $e) { + $e = new RuntimeError(\sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e); + $e->guess(); + + throw $e; + } + } + + /** + * @return iterable + */ + public function yieldBlock($name, array $context, array $blocks = [], $useBlocks = true, ?self $templateContext = null): iterable { if ($useBlocks && isset($blocks[$name])) { $template = $blocks[$name][0]; @@ -181,7 +443,7 @@ abstract class Template if (null !== $template) { try { - $template->$block($context, $blocks); + yield from $template->$block($context, $blocks); } catch (Error $e) { if (!$e->getSourceContext()) { $e->setSourceContext($template->getSourceContext()); @@ -194,235 +456,71 @@ abstract class Template } throw $e; - } catch (\Exception $e) { - $e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e); + } catch (\Throwable $e) { + $e = new RuntimeError(\sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $template->getSourceContext(), $e); $e->guess(); throw $e; } - } elseif (false !== $parent = $this->getParent($context)) { - $parent->displayBlock($name, $context, array_merge($this->blocks, $blocks), false, $templateContext ?? $this); + } elseif ($parent = $this->getParent($context)) { + yield from $parent->unwrap()->yieldBlock($name, $context, array_merge($this->blocks, $blocks), false, $templateContext ?? $this); } elseif (isset($blocks[$name])) { - throw new RuntimeError(sprintf('Block "%s" should not call parent() in "%s" as the block does not exist in the parent template "%s".', $name, $blocks[$name][0]->getTemplateName(), $this->getTemplateName()), -1, $blocks[$name][0]->getSourceContext()); + throw new RuntimeError(\sprintf('Block "%s" should not call parent() in "%s" as the block does not exist in the parent template "%s".', $name, $blocks[$name][0]->getTemplateName(), $this->getTemplateName()), -1, $blocks[$name][0]->getSourceContext()); } else { - throw new RuntimeError(sprintf('Block "%s" on template "%s" does not exist.', $name, $this->getTemplateName()), -1, ($templateContext ?? $this)->getSourceContext()); + throw new RuntimeError(\sprintf('Block "%s" on template "%s" does not exist.', $name, $this->getTemplateName()), -1, ($templateContext ?? $this)->getSourceContext()); } } /** - * Renders a parent block. + * Yields a parent block. * * This method is for internal use only and should never be called * directly. * - * @param string $name The block name to render from the parent + * @param string $name The block name to display from the parent * @param array $context The context * @param array $blocks The current set of blocks * - * @return string The rendered block + * @return iterable */ - public function renderParentBlock($name, array $context, array $blocks = []) + public function yieldParentBlock($name, array $context, array $blocks = []): iterable { - if ($this->env->isDebug()) { - ob_start(); + if (isset($this->traits[$name])) { + yield from $this->traits[$name][0]->yieldBlock($this->traitAliases[$name] ?? $name, $context, $blocks, false); + } elseif ($parent = $this->getParent($context)) { + yield from $parent->unwrap()->yieldBlock($name, $context, $blocks, false); } else { - ob_start(function () { return ''; }); + throw new RuntimeError(\sprintf('The template has no parent and no traits defining the "%s" block.', $name), -1, $this->getSourceContext()); } - $this->displayParentBlock($name, $context, $blocks); - - return ob_get_clean(); } - /** - * Renders a block. - * - * This method is for internal use only and should never be called - * directly. - * - * @param string $name The block name to render - * @param array $context The context - * @param array $blocks The current set of blocks - * @param bool $useBlocks Whether to use the current set of blocks - * - * @return string The rendered block - */ - public function renderBlock($name, array $context, array $blocks = [], $useBlocks = true) + protected function hasMacro(string $name, array $context): bool { - if ($this->env->isDebug()) { - ob_start(); - } else { - ob_start(function () { return ''; }); - } - $this->displayBlock($name, $context, $blocks, $useBlocks); - - return ob_get_clean(); - } - - /** - * Returns whether a block exists or not in the current context of the template. - * - * This method checks blocks defined in the current template - * or defined in "used" traits or defined in parent templates. - * - * @param string $name The block name - * @param array $context The context - * @param array $blocks The current set of blocks - * - * @return bool true if the block exists, false otherwise - */ - public function hasBlock($name, array $context, array $blocks = []) - { - if (isset($blocks[$name])) { - return $blocks[$name][0] instanceof self; - } - - if (isset($this->blocks[$name])) { + if (method_exists($this, $name)) { return true; } - if (false !== $parent = $this->getParent($context)) { - return $parent->hasBlock($name, $context); + if (!$parent = $this->getParent($context)) { + return false; } - return false; + return $parent->hasMacro($name, $context); } - /** - * Returns all block names in the current context of the template. - * - * This method checks blocks defined in the current template - * or defined in "used" traits or defined in parent templates. - * - * @param array $context The context - * @param array $blocks The current set of blocks - * - * @return array An array of block names - */ - public function getBlockNames(array $context, array $blocks = []) + protected function getTemplateForMacro(string $name, array $context, int $line, Source $source): self { - $names = array_merge(array_keys($blocks), array_keys($this->blocks)); - - if (false !== $parent = $this->getParent($context)) { - $names = array_merge($names, $parent->getBlockNames($context)); + if (method_exists($this, $name)) { + return $this; } - return array_unique($names); - } - - /** - * @return Template|TemplateWrapper - */ - protected function loadTemplate($template, $templateName = null, $line = null, $index = null) - { - try { - if (\is_array($template)) { - return $this->env->resolveTemplate($template); + $parent = $this; + while ($parent = $parent->getParent($context)) { + if (method_exists($parent, $name)) { + return $parent; } - - if ($template instanceof self || $template instanceof TemplateWrapper) { - return $template; - } - - if ($template === $this->getTemplateName()) { - $class = \get_class($this); - if (false !== $pos = strrpos($class, '___', -1)) { - $class = substr($class, 0, $pos); - } - - return $this->env->loadClass($class, $template, $index); - } - - return $this->env->loadTemplate($template, $index); - } catch (Error $e) { - if (!$e->getSourceContext()) { - $e->setSourceContext($templateName ? new Source('', $templateName) : $this->getSourceContext()); - } - - if ($e->getTemplateLine() > 0) { - throw $e; - } - - if (!$line) { - $e->guess(); - } else { - $e->setTemplateLine($line); - } - - throw $e; - } - } - - /** - * @internal - * - * @return Template - */ - protected function unwrap() - { - return $this; - } - - /** - * Returns all blocks. - * - * This method is for internal use only and should never be called - * directly. - * - * @return array An array of blocks - */ - public function getBlocks() - { - return $this->blocks; - } - - public function display(array $context, array $blocks = []) - { - $this->displayWithErrorHandling($this->env->mergeGlobals($context), array_merge($this->blocks, $blocks)); - } - - public function render(array $context) - { - $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(); - } - - protected function displayWithErrorHandling(array $context, array $blocks = []) - { - try { - $this->doDisplay($context, $blocks); - } catch (Error $e) { - if (!$e->getSourceContext()) { - $e->setSourceContext($this->getSourceContext()); - } - - // this is mostly useful for \Twig\Error\LoaderError exceptions - // see \Twig\Error\LoaderError - if (-1 === $e->getTemplateLine()) { - $e->guess(); - } - - throw $e; - } catch (\Exception $e) { - $e = new RuntimeError(sprintf('An exception has been thrown during the rendering of a template ("%s").', $e->getMessage()), -1, $this->getSourceContext(), $e); - $e->guess(); - - throw $e; - } + throw new RuntimeError(\sprintf('Macro "%s" is not defined in template "%s".', substr($name, \strlen('macro_')), $this->getTemplateName()), $line, $source); } /** @@ -430,8 +528,8 @@ abstract class Template * * @param array $context An array of parameters to pass to the template * @param array $blocks An array of blocks to pass to the template + * + * @return iterable */ - abstract protected function doDisplay(array $context, array $blocks = []); + abstract protected function doDisplay(array $context, array $blocks = []): iterable; } - -class_alias('Twig\Template', 'Twig_Template'); diff --git a/vendor/twig/twig/src/TemplateWrapper.php b/vendor/twig/twig/src/TemplateWrapper.php index 8b44815..265ce3e 100644 --- a/vendor/twig/twig/src/TemplateWrapper.php +++ b/vendor/twig/twig/src/TemplateWrapper.php @@ -18,37 +18,41 @@ namespace Twig; */ final class TemplateWrapper { - private $env; - private $template; - /** * This method is for internal use only and should never be called * directly (use Twig\Environment::load() instead). * * @internal */ - public function __construct(Environment $env, Template $template) - { - $this->env = $env; - $this->template = $template; + public function __construct( + private Environment $env, + private Template $template, + ) { } /** - * Renders the template. - * - * @param array $context An array of parameters to pass to the template + * @return iterable */ + public function stream(array $context = []): iterable + { + yield from $this->template->yield($context); + } + + /** + * @return iterable + */ + public function streamBlock(string $name, array $context = []): iterable + { + yield from $this->template->yieldBlock($name, $context); + } + public function render(array $context = []): string { - // using func_get_args() allows to not expose the blocks argument - // as it should only be used by internal code - return $this->template->render($context, \func_get_args()[1] ?? []); + return $this->template->render($context); } /** - * Displays the template. - * - * @param array $context An array of parameters to pass to the template + * @return void */ public function display(array $context = []) { @@ -57,22 +61,12 @@ final class TemplateWrapper $this->template->display($context, \func_get_args()[1] ?? []); } - /** - * Checks if a block is defined. - * - * @param string $name The block name - * @param array $context An array of parameters to pass to the template - */ public function hasBlock(string $name, array $context = []): bool { return $this->template->hasBlock($name, $context); } /** - * Returns defined block names in the template. - * - * @param array $context An array of parameters to pass to the template - * * @return string[] An array of defined template block names */ public function getBlockNames(array $context = []): array @@ -80,45 +74,20 @@ final class TemplateWrapper return $this->template->getBlockNames($context); } - /** - * Renders a template block. - * - * @param string $name The block name to render - * @param array $context An array of parameters to pass to the template - * - * @return string The rendered block - */ public function renderBlock(string $name, array $context = []): string { - $context = $this->env->mergeGlobals($context); - $level = ob_get_level(); - if ($this->env->isDebug()) { - ob_start(); - } else { - ob_start(function () { return ''; }); - } - try { - $this->template->displayBlock($name, $context); - } catch (\Throwable $e) { - while (ob_get_level() > $level) { - ob_end_clean(); - } - - throw $e; - } - - return ob_get_clean(); + return $this->template->renderBlock($name, $context + $this->env->getGlobals()); } /** - * Displays a template block. - * - * @param string $name The block name to render - * @param array $context An array of parameters to pass to the template + * @return void */ public function displayBlock(string $name, array $context = []) { - $this->template->displayBlock($name, $this->env->mergeGlobals($context)); + $context += $this->env->getGlobals(); + foreach ($this->template->yieldBlock($name, $context) as $data) { + echo $data; + } } public function getSourceContext(): Source @@ -141,5 +110,3 @@ final class TemplateWrapper return $this->template; } } - -class_alias('Twig\TemplateWrapper', 'Twig_TemplateWrapper'); diff --git a/vendor/twig/twig/src/Test/IntegrationTestCase.php b/vendor/twig/twig/src/Test/IntegrationTestCase.php index d9c3290..f3f7adc 100644 --- a/vendor/twig/twig/src/Test/IntegrationTestCase.php +++ b/vendor/twig/twig/src/Test/IntegrationTestCase.php @@ -17,6 +17,7 @@ use Twig\Error\Error; use Twig\Extension\ExtensionInterface; use Twig\Loader\ArrayLoader; use Twig\RuntimeLoader\RuntimeLoaderInterface; +use Twig\TokenParser\TokenParserInterface; use Twig\TwigFilter; use Twig\TwigFunction; use Twig\TwigTest; @@ -30,9 +31,19 @@ use Twig\TwigTest; abstract class IntegrationTestCase extends TestCase { /** + * @deprecated since Twig 3.13, use getFixturesDirectory() instead. + * * @return string */ - abstract protected function getFixturesDir(); + protected function getFixturesDir() + { + throw new \BadMethodCallException('Not implemented.'); + } + + protected static function getFixturesDirectory(): string + { + throw new \BadMethodCallException('Not implemented.'); + } /** * @return RuntimeLoaderInterface[] @@ -74,8 +85,34 @@ abstract class IntegrationTestCase extends TestCase return []; } + /** + * @return array + */ + protected function getUndefinedFilterCallbacks(): array + { + return []; + } + + /** + * @return array + */ + protected function getUndefinedFunctionCallbacks(): array + { + return []; + } + + /** + * @return array + */ + protected function getUndefinedTokenParserCallbacks(): array + { + return []; + } + /** * @dataProvider getTests + * + * @return void */ public function testIntegration($file, $message, $condition, $templates, $exception, $outputs, $deprecation = '') { @@ -84,16 +121,31 @@ abstract class IntegrationTestCase extends TestCase /** * @dataProvider getLegacyTests + * * @group legacy + * + * @return void */ public function testLegacyIntegration($file, $message, $condition, $templates, $exception, $outputs, $deprecation = '') { $this->doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs, $deprecation); } + /** + * @return iterable + * + * @final since Twig 3.13 + */ public function getTests($name, $legacyTests = false) { - $fixturesDir = realpath($this->getFixturesDir()); + try { + $fixturesDir = static::getFixturesDirectory(); + } catch (\BadMethodCallException) { + trigger_deprecation('twig/twig', '3.13', 'Not overriding "%s::getFixturesDirectory()" in "%s" is deprecated. This method will be abstract in 4.0.', self::class, static::class); + $fixturesDir = $this->getFixturesDir(); + } + + $fixturesDir = realpath($fixturesDir); $tests = []; foreach (new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($fixturesDir), \RecursiveIteratorIterator::LEAVES_ONLY) as $file) { @@ -101,7 +153,7 @@ abstract class IntegrationTestCase extends TestCase continue; } - if ($legacyTests xor false !== strpos($file->getRealpath(), '.legacy.test')) { + if ($legacyTests xor str_contains($file->getRealpath(), '.legacy.test')) { continue; } @@ -120,15 +172,15 @@ abstract class IntegrationTestCase extends TestCase $deprecation = $match[3]; $templates = self::parseTemplates($match[4]); $exception = false; - preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, PREG_SET_ORDER); + preg_match_all('/--DATA--(.*?)(?:--CONFIG--(.*?))?--EXPECT--(.*?)(?=\-\-DATA\-\-|$)/s', $test, $outputs, \PREG_SET_ORDER); } else { - throw new \InvalidArgumentException(sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file))); + throw new \InvalidArgumentException(\sprintf('Test "%s" is not valid.', str_replace($fixturesDir.'/', '', $file))); } - $tests[] = [str_replace($fixturesDir.'/', '', $file), $message, $condition, $templates, $exception, $outputs, $deprecation]; + $tests[str_replace($fixturesDir.'/', '', $file)] = [str_replace($fixturesDir.'/', '', $file), $message, $condition, $templates, $exception, $outputs, $deprecation]; } - if ($legacyTests && empty($tests)) { + if ($legacyTests && !$tests) { // add a dummy test to avoid a PHPUnit message return [['not', '-', '', [], '', []]]; } @@ -136,11 +188,19 @@ abstract class IntegrationTestCase extends TestCase return $tests; } + /** + * @final since Twig 3.13 + * + * @return iterable + */ public function getLegacyTests() { return $this->getTests('testLegacyIntegration', true); } + /** + * @return void + */ protected function doIntegrationTest($file, $message, $condition, $templates, $exception, $outputs, $deprecation = '') { if (!$outputs) { @@ -148,19 +208,23 @@ abstract class IntegrationTestCase extends TestCase } if ($condition) { + $ret = ''; eval('$ret = '.$condition.';'); if (!$ret) { $this->markTestSkipped($condition); } } - $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) { @@ -183,15 +247,22 @@ abstract class IntegrationTestCase extends TestCase $twig->addFunction($function); } - // avoid using the same PHP class name for different cases - $p = new \ReflectionProperty($twig, 'templateClassPrefix'); - $p->setAccessible(true); - $p->setValue($twig, '__TwigTemplate_'.hash('sha256', uniqid(mt_rand(), true), false).'_'); + foreach ($this->getUndefinedFilterCallbacks() as $callback) { + $twig->registerUndefinedFilterCallback($callback); + } + + foreach ($this->getUndefinedFunctionCallbacks() as $callback) { + $twig->registerUndefinedFunctionCallback($callback); + } + + foreach ($this->getUndefinedTokenParserCallbacks() as $callback) { + $twig->registerUndefinedTokenParserCallback($callback); + } $deprecations = []; try { $prevHandler = set_error_handler(function ($type, $msg, $file, $line, $context = []) use (&$deprecations, &$prevHandler) { - if (E_USER_DEPRECATED === $type) { + if (\E_USER_DEPRECATED === $type) { $deprecations[] = $msg; return true; @@ -204,14 +275,14 @@ abstract class IntegrationTestCase extends TestCase } catch (\Exception $e) { if (false !== $exception) { $message = $e->getMessage(); - $this->assertSame(trim($exception), trim(sprintf('%s: %s', \get_class($e), $message))); + $this->assertSame(trim($exception), trim(\sprintf('%s: %s', $e::class, $message))); $last = substr($message, \strlen($message) - 1); $this->assertTrue('.' === $last || '?' === $last, 'Exception message must end with a dot or a question mark.'); return; } - throw new Error(sprintf('%s: %s', \get_class($e), $e->getMessage()), -1, null, $e); + throw new Error(\sprintf('%s: %s', $e::class, $e->getMessage()), -1, null, $e); } finally { restore_error_handler(); } @@ -222,18 +293,18 @@ abstract class IntegrationTestCase extends TestCase $output = trim($template->render(eval($match[1].';')), "\n "); } catch (\Exception $e) { if (false !== $exception) { - $this->assertSame(trim($exception), trim(sprintf('%s: %s', \get_class($e), $e->getMessage()))); + $this->assertStringMatchesFormat(trim($exception), trim(\sprintf('%s: %s', $e::class, $e->getMessage()))); return; } - $e = new Error(sprintf('%s: %s', \get_class($e), $e->getMessage()), -1, null, $e); + $e = new Error(\sprintf('%s: %s', $e::class, $e->getMessage()), -1, null, $e); - $output = trim(sprintf('%s: %s', \get_class($e), $e->getMessage())); + $output = trim(\sprintf('%s: %s', $e::class, $e->getMessage())); } if (false !== $exception) { - list($class) = explode(':', $exception); + [$class] = explode(':', $exception); $constraintClass = class_exists('PHPUnit\Framework\Constraint\Exception') ? 'PHPUnit\Framework\Constraint\Exception' : 'PHPUnit_Framework_Constraint_Exception'; $this->assertThat(null, new $constraintClass($class)); } @@ -252,16 +323,17 @@ abstract class IntegrationTestCase extends TestCase } } + /** + * @return array + */ protected static function parseTemplates($test) { $templates = []; - preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, PREG_SET_ORDER); + preg_match_all('/--TEMPLATE(?:\((.*?)\))?--(.*?)(?=\-\-TEMPLATE|$)/s', $test, $matches, \PREG_SET_ORDER); foreach ($matches as $match) { - $templates[($match[1] ? $match[1] : 'index.twig')] = $match[2]; + $templates[$match[1] ?: 'index.twig'] = $match[2]; } return $templates; } } - -class_alias('Twig\Test\IntegrationTestCase', 'Twig_Test_IntegrationTestCase'); diff --git a/vendor/twig/twig/src/Test/NodeTestCase.php b/vendor/twig/twig/src/Test/NodeTestCase.php index 368ceb1..0cb5b2f 100644 --- a/vendor/twig/twig/src/Test/NodeTestCase.php +++ b/vendor/twig/twig/src/Test/NodeTestCase.php @@ -11,6 +11,8 @@ namespace Twig\Test; +use PHPUnit\Framework\Attributes\BeforeClass; +use PHPUnit\Framework\Attributes\DataProvider; use PHPUnit\Framework\TestCase; use Twig\Compiler; use Twig\Environment; @@ -19,17 +21,45 @@ use Twig\Node\Node; abstract class NodeTestCase extends TestCase { - abstract public function getTests(); + /** + * @var Environment + */ + private $currentEnv; + + /** + * @return iterable + */ + public function getTests() + { + return []; + } + + /** + * @return iterable + */ + public static function provideTests(): iterable + { + trigger_deprecation('twig/twig', '3.13', 'Not implementing "%s()" in "%s" is deprecated. This method will be abstract in 4.0.', __METHOD__, static::class); + + return []; + } /** * @dataProvider getTests + * @dataProvider provideTests + * + * @return void */ + #[DataProvider('getTests'), DataProvider('provideTests')] public function testCompile($node, $source, $environment = null, $isPattern = false) { $this->assertNodeCompilation($source, $node, $environment, $isPattern); } - public function assertNodeCompilation($source, Node $node, Environment $environment = null, $isPattern = false) + /** + * @return void + */ + public function assertNodeCompilation($source, Node $node, ?Environment $environment = null, $isPattern = false) { $compiler = $this->getCompiler($environment); $compiler->compile($node); @@ -41,27 +71,72 @@ abstract class NodeTestCase extends TestCase } } - protected function getCompiler(Environment $environment = null) + /** + * @return Compiler + */ + protected function getCompiler(?Environment $environment = null) { - return new Compiler(null === $environment ? $this->getEnvironment() : $environment); + return new Compiler($environment ?? $this->getEnvironment()); } + /** + * @return Environment + * + * @final since Twig 3.13 + */ protected function getEnvironment() { - return new Environment(new ArrayLoader([])); + return $this->currentEnv ??= static::createEnvironment(); } + protected static function createEnvironment(): Environment + { + return new Environment(new ArrayLoader()); + } + + /** + * @return string + * + * @deprecated since Twig 3.13, use createVariableGetter() instead. + */ protected function getVariableGetter($name, $line = false) { - $line = $line > 0 ? "// line {$line}\n" : ''; + trigger_deprecation('twig/twig', '3.13', 'Method "%s()" is deprecated, use "createVariableGetter()" instead.', __METHOD__); - return sprintf('%s($context["%s"] ?? null)', $line, $name); + return self::createVariableGetter($name, $line); } + final protected static function createVariableGetter(string $name, bool $line = false): string + { + $line = $line > 0 ? "// line $line\n" : ''; + + return \sprintf('%s($context["%s"] ?? null)', $line, $name); + } + + /** + * @return string + * + * @deprecated since Twig 3.13, use createAttributeGetter() instead. + */ protected function getAttributeGetter() { - return 'twig_get_attribute($this->env, $this->source, '; + trigger_deprecation('twig/twig', '3.13', 'Method "%s()" is deprecated, use "createAttributeGetter()" instead.', __METHOD__); + + return self::createAttributeGetter(); + } + + final protected static function createAttributeGetter(): string + { + return 'CoreExtension::getAttribute($this->env, $this->source, '; + } + + /** @beforeClass */ + #[BeforeClass] + final public static function checkDataProvider(): void + { + $r = new \ReflectionMethod(static::class, 'getTests'); + if (self::class !== $r->getDeclaringClass()->getName()) { + trigger_deprecation('twig/twig', '3.13', 'Implementing "%s::getTests()" in "%s" is deprecated, implement "provideTests()" instead.', self::class, static::class); + } } } - -class_alias('Twig\Test\NodeTestCase', 'Twig_Test_NodeTestCase'); diff --git a/vendor/twig/twig/src/Token.php b/vendor/twig/twig/src/Token.php index 262fa48..823c773 100644 --- a/vendor/twig/twig/src/Token.php +++ b/vendor/twig/twig/src/Token.php @@ -13,46 +13,48 @@ namespace Twig; /** - * Represents a Token. - * * @author Fabien Potencier */ final class Token { - private $value; - private $type; - private $lineno; - - const EOF_TYPE = -1; - const TEXT_TYPE = 0; - const BLOCK_START_TYPE = 1; - const VAR_START_TYPE = 2; - const BLOCK_END_TYPE = 3; - const VAR_END_TYPE = 4; - const NAME_TYPE = 5; - const NUMBER_TYPE = 6; - const STRING_TYPE = 7; - const OPERATOR_TYPE = 8; - const PUNCTUATION_TYPE = 9; - const INTERPOLATION_START_TYPE = 10; - const INTERPOLATION_END_TYPE = 11; - const ARROW_TYPE = 12; - + public const EOF_TYPE = -1; + public const TEXT_TYPE = 0; + public const BLOCK_START_TYPE = 1; + public const VAR_START_TYPE = 2; + public const BLOCK_END_TYPE = 3; + public const VAR_END_TYPE = 4; + public const NAME_TYPE = 5; + public const NUMBER_TYPE = 6; + public const STRING_TYPE = 7; + public const OPERATOR_TYPE = 8; + public const PUNCTUATION_TYPE = 9; + public const INTERPOLATION_START_TYPE = 10; + public const INTERPOLATION_END_TYPE = 11; /** - * @param int $type The type of the token - * @param string $value The token value - * @param int $lineno The line position in the source + * @deprecated since Twig 3.21, "arrow" is now an operator */ - public function __construct($type, $value, $lineno) - { - $this->type = $type; - $this->value = $value; - $this->lineno = $lineno; + public const ARROW_TYPE = 12; + /** + * @deprecated since Twig 3.21, "spread" is now an operator + */ + public const SPREAD_TYPE = 13; + + public function __construct( + private int $type, + private $value, + private int $lineno, + ) { + if (self::ARROW_TYPE === $type) { + trigger_deprecation('twig/twig', '3.21', 'The "%s" token type is deprecated, "arrow" is now an operator.', self::ARROW_TYPE); + } + if (self::SPREAD_TYPE === $type) { + trigger_deprecation('twig/twig', '3.21', 'The "%s" token type is deprecated, "spread" is now an operator.', self::SPREAD_TYPE); + } } - public function __toString() + public function __toString(): string { - return sprintf('%s(%s)', self::typeToString($this->type, true), $this->value); + return \sprintf('%s(%s)', self::typeToString($this->type, true), $this->value); } /** @@ -65,56 +67,84 @@ final class Token * * @param array|string|int $type The type to test * @param array|string|null $values The token value - * - * @return bool */ - public function test($type, $values = null) + public function test($type, $values = null): bool { if (null === $values && !\is_int($type)) { $values = $type; $type = self::NAME_TYPE; } - return ($this->type === $type) && ( - null === $values || - (\is_array($values) && \in_array($this->value, $values)) || - $this->value == $values + if (self::ARROW_TYPE === $type) { + trigger_deprecation('twig/twig', '3.21', 'The "%s" token type is deprecated, "arrow" is now an operator.', self::typeToEnglish(self::ARROW_TYPE)); + + return self::OPERATOR_TYPE === $this->type && '=>' === $this->value; + } + if (self::SPREAD_TYPE === $type) { + trigger_deprecation('twig/twig', '3.21', 'The "%s" token type is deprecated, "spread" is now an operator.', self::typeToEnglish(self::SPREAD_TYPE)); + + return self::OPERATOR_TYPE === $this->type && '...' === $this->value; + } + + $typeMatches = $this->type === $type; + if ($typeMatches && self::PUNCTUATION_TYPE === $type && \in_array($this->value, ['(', '[', '|', '.', '?', '?:'], true) && $values) { + foreach ((array) $values as $value) { + if (\in_array($value, ['(', '[', '|', '.', '?', '?:'], true)) { + trigger_deprecation('twig/twig', '3.21', 'The "%s" token is now an "%s" token instead of a "%s" one.', $this->value, self::typeToEnglish(self::OPERATOR_TYPE), $this->toEnglish()); + + break; + } + } + } + if (!$typeMatches) { + if (self::OPERATOR_TYPE === $type && self::PUNCTUATION_TYPE === $this->type) { + if ($values) { + foreach ((array) $values as $value) { + if (\in_array($value, ['(', '[', '|', '.', '?', '?:'], true)) { + $typeMatches = true; + + break; + } + } + } else { + $typeMatches = true; + } + } + } + + return $typeMatches && ( + null === $values + || (\is_array($values) && \in_array($this->value, $values, true)) + || $this->value == $values ); } - /** - * @return int - */ - public function getLine() + public function getLine(): int { return $this->lineno; } /** - * @return int + * @deprecated since Twig 3.19 */ - public function getType() + public function getType(): int { + trigger_deprecation('twig/twig', '3.19', \sprintf('The "%s()" method is deprecated.', __METHOD__)); + return $this->type; } - /** - * @return string - */ public function getValue() { return $this->value; } - /** - * Returns the constant representation (internal) of a given type. - * - * @param int $type The type as an integer - * @param bool $short Whether to return a short representation or not - * - * @return string The string representation - */ - public static function typeToString($type, $short = false) + public function toEnglish(): string + { + return self::typeToEnglish($this->type); + } + + public static function typeToString(int $type, bool $short = false): string { switch ($type) { case self::EOF_TYPE: @@ -159,21 +189,17 @@ final class Token case self::ARROW_TYPE: $name = 'ARROW_TYPE'; break; + case self::SPREAD_TYPE: + $name = 'SPREAD_TYPE'; + break; default: - throw new \LogicException(sprintf('Token of type "%s" does not exist.', $type)); + throw new \LogicException(\sprintf('Token of type "%s" does not exist.', $type)); } return $short ? $name : 'Twig\Token::'.$name; } - /** - * Returns the English representation of a given type. - * - * @param int $type The type as an integer - * - * @return string The string representation - */ - public static function typeToEnglish($type) + public static function typeToEnglish(int $type): string { switch ($type) { case self::EOF_TYPE: @@ -204,10 +230,10 @@ final class Token return 'end of string interpolation'; case self::ARROW_TYPE: return 'arrow function'; + case self::SPREAD_TYPE: + return 'spread operator'; default: - throw new \LogicException(sprintf('Token of type "%s" does not exist.', $type)); + throw new \LogicException(\sprintf('Token of type "%s" does not exist.', $type)); } } } - -class_alias('Twig\Token', 'Twig_Token'); diff --git a/vendor/twig/twig/src/TokenParser/AbstractTokenParser.php b/vendor/twig/twig/src/TokenParser/AbstractTokenParser.php index 2c2f90b..8acaa6f 100644 --- a/vendor/twig/twig/src/TokenParser/AbstractTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/AbstractTokenParser.php @@ -11,7 +11,11 @@ namespace Twig\TokenParser; +use Twig\Lexer; +use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\Nodes; use Twig\Parser; +use Twig\Token; /** * Base class for all token parsers. @@ -25,10 +29,33 @@ abstract class AbstractTokenParser implements TokenParserInterface */ protected $parser; - public function setParser(Parser $parser) + public function setParser(Parser $parser): void { $this->parser = $parser; } -} -class_alias('Twig\TokenParser\AbstractTokenParser', 'Twig_TokenParser'); + /** + * Parses an assignment expression like "a, b". + */ + protected function parseAssignmentExpression(): Nodes + { + $stream = $this->parser->getStream(); + $targets = []; + while (true) { + $token = $stream->getCurrent(); + if ($stream->test(Token::OPERATOR_TYPE) && preg_match(Lexer::REGEX_NAME, $token->getValue())) { + // in this context, string operators are variable names + $stream->next(); + } else { + $stream->expect(Token::NAME_TYPE, null, 'Only variables can be assigned to'); + } + $targets[] = new AssignContextVariable($token->getValue(), $token->getLine()); + + if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { + break; + } + } + + return new Nodes($targets); + } +} diff --git a/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php b/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php index 879879a..5b560e7 100644 --- a/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/ApplyTokenParser.php @@ -11,8 +11,10 @@ namespace Twig\TokenParser; -use Twig\Node\Expression\TempNameExpression; +use Twig\ExpressionParser\Infix\FilterExpressionParser; +use Twig\Node\Expression\Variable\LocalVariable; use Twig\Node\Node; +use Twig\Node\Nodes; use Twig\Node\PrintNode; use Twig\Node\SetNode; use Twig\Token; @@ -22,36 +24,42 @@ use Twig\Token; * * {% apply upper %} * This text becomes uppercase - * {% endapplys %} + * {% endapply %} + * + * @internal */ final class ApplyTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { $lineno = $token->getLine(); - $name = $this->parser->getVarName(); - - $ref = new TempNameExpression($name, $lineno); - $ref->setAttribute('always_defined', true); - - $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag()); + $ref = new LocalVariable(null, $lineno); + $filter = $ref; + $op = $this->parser->getEnvironment()->getExpressionParsers()->getByClass(FilterExpressionParser::class); + while (true) { + $filter = $op->parse($this->parser, $filter, $this->parser->getCurrentToken()); + if (!$this->parser->getStream()->test(Token::OPERATOR_TYPE, '|')) { + break; + } + $this->parser->getStream()->next(); + } $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()), - ]); + return new Nodes([ + new SetNode(true, $ref, $body, $lineno), + new PrintNode($filter, $lineno), + ], $lineno); } - public function decideApplyEnd(Token $token) + public function decideApplyEnd(Token $token): bool { return $token->test('endapply'); } - public function getTag() + public function getTag(): string { return 'apply'; } diff --git a/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php b/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php index 10fdb81..86feb27 100644 --- a/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/AutoEscapeTokenParser.php @@ -14,44 +14,45 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; use Twig\Node\AutoEscapeNode; use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Node; use Twig\Token; /** * Marks a section of a template to be escaped or not. + * + * @internal */ final class AutoEscapeTokenParser extends AbstractTokenParser { - public function parse(Token $token) + 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(); + $expr = $this->parser->parseExpression(); if (!$expr instanceof ConstantExpression) { throw new SyntaxError('An escaping strategy must be a string or false.', $stream->getCurrent()->getLine(), $stream->getSourceContext()); } $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) + public function decideBlockEnd(Token $token): bool { return $token->test('endautoescape'); } - public function getTag() + public function getTag(): string { return 'autoescape'; } } - -class_alias('Twig\TokenParser\AutoEscapeTokenParser', 'Twig_TokenParser_AutoEscape'); diff --git a/vendor/twig/twig/src/TokenParser/BlockTokenParser.php b/vendor/twig/twig/src/TokenParser/BlockTokenParser.php index 449a2c0..452b323 100644 --- a/vendor/twig/twig/src/TokenParser/BlockTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/BlockTokenParser.php @@ -15,7 +15,9 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; use Twig\Node\BlockNode; use Twig\Node\BlockReferenceNode; +use Twig\Node\EmptyNode; use Twig\Node\Node; +use Twig\Node\Nodes; use Twig\Node\PrintNode; use Twig\Token; @@ -26,53 +28,50 @@ use Twig\Token; * * {% block title %}{% endblock %} - My Webpage * {% endblock %} + * + * @internal */ final class BlockTokenParser extends AbstractTokenParser { - public function parse(Token $token) + 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()); - } - $this->parser->setBlock($name, $block = new BlockNode($name, new Node([]), $lineno)); + $name = $stream->expect(Token::NAME_TYPE)->getValue(); + $this->parser->setBlock($name, $block = new BlockNode($name, new EmptyNode(), $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) { - throw new SyntaxError(sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + throw new SyntaxError(\sprintf('Expected endblock for block "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } } else { - $body = new Node([ - new PrintNode($this->parser->getExpressionParser()->parseExpression(), $lineno), + $body = new Nodes([ + new PrintNode($this->parser->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) + public function decideBlockEnd(Token $token): bool { return $token->test('endblock'); } - public function getTag() + public function getTag(): string { return 'block'; } } - -class_alias('Twig\TokenParser\BlockTokenParser', 'Twig_TokenParser_Block'); diff --git a/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php b/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php index 6575cff..df1ba38 100644 --- a/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/DeprecatedTokenParser.php @@ -11,7 +11,9 @@ namespace Twig\TokenParser; +use Twig\Error\SyntaxError; use Twig\Node\DeprecatedNode; +use Twig\Node\Node; use Twig\Token; /** @@ -20,25 +22,44 @@ use Twig\Token; * {% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' %} * {% extends 'layout.html.twig' %} * + * {% deprecated 'The "base.twig" template is deprecated, use "layout.twig" instead.' package="foo/bar" version="1.1" %} + * * @author Yonel Ceruto * - * @final + * @internal */ -class DeprecatedTokenParser extends AbstractTokenParser +final class DeprecatedTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { - $expr = $this->parser->getExpressionParser()->parseExpression(); + $stream = $this->parser->getStream(); + $expr = $this->parser->parseExpression(); + $node = new DeprecatedNode($expr, $token->getLine()); - $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); + while ($stream->test(Token::NAME_TYPE)) { + $k = $stream->getCurrent()->getValue(); + $stream->next(); + $stream->expect(Token::OPERATOR_TYPE, '='); - return new DeprecatedNode($expr, $token->getLine(), $this->getTag()); + switch ($k) { + case 'package': + $node->setNode('package', $this->parser->parseExpression()); + break; + case 'version': + $node->setNode('version', $this->parser->parseExpression()); + break; + default: + throw new SyntaxError(\sprintf('Unknown "%s" option.', $k), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + } + } + + $stream->expect(Token::BLOCK_END_TYPE); + + return $node; } - public function getTag() + public function getTag(): string { return 'deprecated'; } } - -class_alias('Twig\TokenParser\DeprecatedTokenParser', 'Twig_TokenParser_Deprecated'); diff --git a/vendor/twig/twig/src/TokenParser/DoTokenParser.php b/vendor/twig/twig/src/TokenParser/DoTokenParser.php index e5a07d6..ca9d03d 100644 --- a/vendor/twig/twig/src/TokenParser/DoTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/DoTokenParser.php @@ -12,26 +12,27 @@ namespace Twig\TokenParser; use Twig\Node\DoNode; +use Twig\Node\Node; use Twig\Token; /** * Evaluates an expression, discarding the returned value. + * + * @internal */ final class DoTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { - $expr = $this->parser->getExpressionParser()->parseExpression(); + $expr = $this->parser->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() + public function getTag(): string { return 'do'; } } - -class_alias('Twig\TokenParser\DoTokenParser', 'Twig_TokenParser_Do'); diff --git a/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php b/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php index 83a545e..fa27910 100644 --- a/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/EmbedTokenParser.php @@ -13,35 +13,38 @@ namespace Twig\TokenParser; use Twig\Node\EmbedNode; use Twig\Node\Expression\ConstantExpression; -use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\ContextVariable; +use Twig\Node\Node; use Twig\Token; /** * Embeds a template. + * + * @internal */ final class EmbedTokenParser extends IncludeTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { $stream = $this->parser->getStream(); - $parent = $this->parser->getExpressionParser()->parseExpression(); + $parent = $this->parser->parseExpression(); - list($variables, $only, $ignoreMissing) = $this->parseArguments(); + [$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()); - } elseif ($parent instanceof NameExpression) { - $parentToken = new Token(/* Token::NAME_TYPE */ 5, $parent->getAttribute('name'), $token->getLine()); + $parentToken = new Token(Token::STRING_TYPE, $parent->getAttribute('value'), $token->getLine()); + } elseif ($parent instanceof ContextVariable) { + $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); @@ -53,20 +56,18 @@ final class EmbedTokenParser extends IncludeTokenParser $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) + public function decideBlockEnd(Token $token): bool { return $token->test('endembed'); } - public function getTag() + public function getTag(): string { return 'embed'; } } - -class_alias('Twig\TokenParser\EmbedTokenParser', 'Twig_TokenParser_Embed'); diff --git a/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php b/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php index a44980f..8f64698 100644 --- a/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/ExtendsTokenParser.php @@ -13,6 +13,7 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; +use Twig\Node\EmptyNode; use Twig\Node\Node; use Twig\Token; @@ -20,10 +21,12 @@ use Twig\Token; * Extends a template by another one. * * {% extends "base.html" %} + * + * @internal */ final class ExtendsTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { $stream = $this->parser->getStream(); @@ -33,20 +36,15 @@ final class ExtendsTokenParser extends AbstractTokenParser 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()); + $this->parser->setParent($this->parser->parseExpression()); $stream->expect(Token::BLOCK_END_TYPE); - return new Node(); + return new EmptyNode($token->getLine()); } - public function getTag() + public function getTag(): string { return 'extends'; } } - -class_alias('Twig\TokenParser\ExtendsTokenParser', 'Twig_TokenParser_Extends'); diff --git a/vendor/twig/twig/src/TokenParser/FilterTokenParser.php b/vendor/twig/twig/src/TokenParser/FilterTokenParser.php deleted file mode 100644 index e57fc90..0000000 --- a/vendor/twig/twig/src/TokenParser/FilterTokenParser.php +++ /dev/null @@ -1,64 +0,0 @@ -parser->getStream(); - $lineno = $token->getLine(); - - @trigger_error(sprintf('The "filter" tag in "%s" at line %d is deprecated since Twig 2.9, use the "apply" tag instead.', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED); - - $name = $this->parser->getVarName(); - $ref = new BlockReferenceExpression(new ConstantExpression($name, $lineno), null, $lineno, $this->getTag()); - - $filter = $this->parser->getExpressionParser()->parseFilterExpressionRaw($ref, $this->getTag()); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); - - $body = $this->parser->subparse([$this, 'decideBlockEnd'], true); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); - - $block = new BlockNode($name, $body, $lineno); - $this->parser->setBlock($name, $block); - - return new PrintNode($filter, $lineno, $this->getTag()); - } - - public function decideBlockEnd(Token $token) - { - return $token->test('endfilter'); - } - - public function getTag() - { - return 'filter'; - } -} - -class_alias('Twig\TokenParser\FilterTokenParser', 'Twig_TokenParser_Filter'); diff --git a/vendor/twig/twig/src/TokenParser/FlushTokenParser.php b/vendor/twig/twig/src/TokenParser/FlushTokenParser.php index 70f4339..0d23887 100644 --- a/vendor/twig/twig/src/TokenParser/FlushTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/FlushTokenParser.php @@ -12,26 +12,27 @@ namespace Twig\TokenParser; use Twig\Node\FlushNode; +use Twig\Node\Node; use Twig\Token; /** * Flushes the output to the client. * * @see flush() + * + * @internal */ final class FlushTokenParser extends AbstractTokenParser { - public function parse(Token $token) + 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() + public function getTag(): string { return 'flush'; } } - -class_alias('Twig\TokenParser\FlushTokenParser', 'Twig_TokenParser_Flush'); diff --git a/vendor/twig/twig/src/TokenParser/ForTokenParser.php b/vendor/twig/twig/src/TokenParser/ForTokenParser.php index 34430f0..21166fc 100644 --- a/vendor/twig/twig/src/TokenParser/ForTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/ForTokenParser.php @@ -12,15 +12,11 @@ namespace Twig\TokenParser; -use Twig\Error\SyntaxError; -use Twig\Node\Expression\AssignNameExpression; -use Twig\Node\Expression\ConstantExpression; -use Twig\Node\Expression\GetAttrExpression; -use Twig\Node\Expression\NameExpression; +use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\ForElseNode; use Twig\Node\ForNode; use Twig\Node\Node; use Twig\Token; -use Twig\TokenStream; /** * Loops over each item of a sequence. @@ -30,108 +26,55 @@ use Twig\TokenStream; *
  • {{ user.username|e }}
  • * {% endfor %} * + * + * @internal */ final class ForTokenParser extends AbstractTokenParser { - public function parse(Token $token) + 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'); - $seq = $this->parser->getExpressionParser()->parseExpression(); + $targets = $this->parseAssignmentExpression(); + $stream->expect(Token::OPERATOR_TYPE, 'in'); + $seq = $this->parser->parseExpression(); - $ifexpr = null; - if ($stream->nextIf(/* Token::NAME_TYPE */ 5, 'if')) { - @trigger_error(sprintf('Using an "if" condition on "for" tag in "%s" at line %d is deprecated since Twig 2.10.0, use a "filter" filter or an "if" condition inside the "for" body instead (if your condition depends on a variable updated inside the loop).', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED); - - $ifexpr = $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); - $else = $this->parser->subparse([$this, 'decideForEnd'], true); + $elseLineno = $stream->getCurrent()->getLine(); + $stream->expect(Token::BLOCK_END_TYPE); + $else = new ForElseNode($this->parser->subparse([$this, 'decideForEnd'], true), $elseLineno); } else { $else = null; } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); if (\count($targets) > 1) { - $keyTarget = $targets->getNode(0); - $keyTarget = new AssignNameExpression($keyTarget->getAttribute('name'), $keyTarget->getTemplateLine()); - $valueTarget = $targets->getNode(1); - $valueTarget = new AssignNameExpression($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine()); + $keyTarget = $targets->getNode('0'); + $keyTarget = new AssignContextVariable($keyTarget->getAttribute('name'), $keyTarget->getTemplateLine()); + $valueTarget = $targets->getNode('1'); } else { - $keyTarget = new AssignNameExpression('_key', $lineno); - $valueTarget = $targets->getNode(0); - $valueTarget = new AssignNameExpression($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine()); + $keyTarget = new AssignContextVariable('_key', $lineno); + $valueTarget = $targets->getNode('0'); } + $valueTarget = new AssignContextVariable($valueTarget->getAttribute('name'), $valueTarget->getTemplateLine()); - if ($ifexpr) { - $this->checkLoopUsageCondition($stream, $ifexpr); - $this->checkLoopUsageBody($stream, $body); - } - - return new ForNode($keyTarget, $valueTarget, $seq, $ifexpr, $body, $else, $lineno, $this->getTag()); + return new ForNode($keyTarget, $valueTarget, $seq, null, $body, $else, $lineno); } - public function decideForFork(Token $token) + public function decideForFork(Token $token): bool { return $token->test(['else', 'endfor']); } - public function decideForEnd(Token $token) + public function decideForEnd(Token $token): bool { return $token->test('endfor'); } - // the loop variable cannot be used in the condition - private function checkLoopUsageCondition(TokenStream $stream, Node $node) - { - if ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression && 'loop' == $node->getNode('node')->getAttribute('name')) { - throw new SyntaxError('The "loop" variable cannot be used in a looping condition.', $node->getTemplateLine(), $stream->getSourceContext()); - } - - foreach ($node as $n) { - if (!$n) { - continue; - } - - $this->checkLoopUsageCondition($stream, $n); - } - } - - // check usage of non-defined loop-items - // it does not catch all problems (for instance when a for is included into another or when the variable is used in an include) - private function checkLoopUsageBody(TokenStream $stream, Node $node) - { - if ($node instanceof GetAttrExpression && $node->getNode('node') instanceof NameExpression && 'loop' == $node->getNode('node')->getAttribute('name')) { - $attribute = $node->getNode('attribute'); - if ($attribute instanceof ConstantExpression && \in_array($attribute->getAttribute('value'), ['length', 'revindex0', 'revindex', 'last'])) { - throw new SyntaxError(sprintf('The "loop.%s" variable is not defined when looping with a condition.', $attribute->getAttribute('value')), $node->getTemplateLine(), $stream->getSourceContext()); - } - } - - // should check for parent.loop.XXX usage - if ($node instanceof ForNode) { - return; - } - - foreach ($node as $n) { - if (!$n) { - continue; - } - - $this->checkLoopUsageBody($stream, $n); - } - } - - public function getTag() + public function getTag(): string { return 'for'; } } - -class_alias('Twig\TokenParser\ForTokenParser', 'Twig_TokenParser_For'); diff --git a/vendor/twig/twig/src/TokenParser/FromTokenParser.php b/vendor/twig/twig/src/TokenParser/FromTokenParser.php index dd49f2f..1c80a17 100644 --- a/vendor/twig/twig/src/TokenParser/FromTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/FromTokenParser.php @@ -11,55 +11,59 @@ namespace Twig\TokenParser; -use Twig\Node\Expression\AssignNameExpression; +use Twig\Node\Expression\Variable\AssignContextVariable; +use Twig\Node\Expression\Variable\AssignTemplateVariable; +use Twig\Node\Expression\Variable\TemplateVariable; use Twig\Node\ImportNode; +use Twig\Node\Node; use Twig\Token; /** * Imports macros. * - * {% from 'forms.html' import forms %} + * {% from 'forms.html.twig' import forms %} + * + * @internal */ final class FromTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { - $macro = $this->parser->getExpressionParser()->parseExpression(); + $macro = $this->parser->parseExpression(); $stream = $this->parser->getStream(); - $stream->expect(/* Token::NAME_TYPE */ 5, 'import'); + $stream->expect(Token::NAME_TYPE, 'import'); $targets = []; - do { - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + while (true) { + $name = $stream->expect(Token::NAME_TYPE)->getValue(); - $alias = $name; if ($stream->nextIf('as')) { - $alias = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + $alias = new AssignContextVariable($stream->expect(Token::NAME_TYPE)->getValue(), $token->getLine()); + } else { + $alias = new AssignContextVariable($name, $token->getLine()); } $targets[$name] = $alias; - if (!$stream->nextIf(/* Token::PUNCTUATION_TYPE */ 9, ',')) { + if (!$stream->nextIf(Token::PUNCTUATION_TYPE, ',')) { break; } - } while (true); + } - $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()); + $internalRef = new AssignTemplateVariable(new TemplateVariable(null, $token->getLine()), $this->parser->isMainScope()); + $node = new ImportNode($macro, $internalRef, $token->getLine()); foreach ($targets as $name => $alias) { - $this->parser->addImportedSymbol('function', $alias, 'macro_'.$name, $var); + $this->parser->addImportedSymbol('function', $alias->getAttribute('name'), 'macro_'.$name, $internalRef); } return $node; } - public function getTag() + public function getTag(): string { return 'from'; } } - -class_alias('Twig\TokenParser\FromTokenParser', 'Twig_TokenParser_From'); diff --git a/vendor/twig/twig/src/TokenParser/GuardTokenParser.php b/vendor/twig/twig/src/TokenParser/GuardTokenParser.php new file mode 100644 index 0000000..656766a --- /dev/null +++ b/vendor/twig/twig/src/TokenParser/GuardTokenParser.php @@ -0,0 +1,73 @@ +parser->getStream(); + $typeToken = $stream->expect(Token::NAME_TYPE); + if (!\in_array($typeToken->getValue(), ['function', 'filter', 'test'], true)) { + throw new SyntaxError(\sprintf('Supported guard types are function, filter and test, "%s" given.', $typeToken->getValue()), $typeToken->getLine(), $stream->getSourceContext()); + } + $method = 'get'.$typeToken->getValue(); + + $nameToken = $stream->expect(Token::NAME_TYPE); + + try { + $exists = null !== $this->parser->getEnvironment()->$method($nameToken->getValue()); + } catch (SyntaxError) { + $exists = false; + } + + $stream->expect(Token::BLOCK_END_TYPE); + if ($exists) { + $body = $this->parser->subparse([$this, 'decideGuardFork']); + } else { + $body = new EmptyNode(); + $this->parser->subparseIgnoreUnknownTwigCallables([$this, 'decideGuardFork']); + } + $else = new EmptyNode(); + if ('else' === $stream->next()->getValue()) { + $stream->expect(Token::BLOCK_END_TYPE); + $else = $this->parser->subparse([$this, 'decideGuardEnd'], true); + } + $stream->expect(Token::BLOCK_END_TYPE); + + return new Nodes([$exists ? $body : $else]); + } + + public function decideGuardFork(Token $token): bool + { + return $token->test(['else', 'endguard']); + } + + public function decideGuardEnd(Token $token): bool + { + return $token->test(['endguard']); + } + + public function getTag(): string + { + return 'guard'; + } +} diff --git a/vendor/twig/twig/src/TokenParser/IfTokenParser.php b/vendor/twig/twig/src/TokenParser/IfTokenParser.php index 8ad99f0..4e3588e 100644 --- a/vendor/twig/twig/src/TokenParser/IfTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/IfTokenParser.php @@ -15,6 +15,7 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; use Twig\Node\IfNode; use Twig\Node\Node; +use Twig\Node\Nodes; use Twig\Token; /** @@ -27,15 +28,17 @@ use Twig\Token; * {% endfor %} * * {% endif %} + * + * @internal */ final class IfTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { $lineno = $token->getLine(); - $expr = $this->parser->getExpressionParser()->parseExpression(); + $expr = $this->parser->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; @@ -44,13 +47,13 @@ final class IfTokenParser extends AbstractTokenParser 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); + $expr = $this->parser->parseExpression(); + $stream->expect(Token::BLOCK_END_TYPE); $body = $this->parser->subparse([$this, 'decideIfFork']); $tests[] = $expr; $tests[] = $body; @@ -61,29 +64,27 @@ final class IfTokenParser extends AbstractTokenParser break; default: - throw new SyntaxError(sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + throw new SyntaxError(\sprintf('Unexpected end of template. Twig was looking for the following tags "else", "elseif", or "endif" to close the "if" block started at line %d).', $lineno), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } - $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 Nodes($tests), $else, $lineno); } - public function decideIfFork(Token $token) + public function decideIfFork(Token $token): bool { return $token->test(['elseif', 'else', 'endif']); } - public function decideIfEnd(Token $token) + public function decideIfEnd(Token $token): bool { return $token->test(['endif']); } - public function getTag() + public function getTag(): string { return 'if'; } } - -class_alias('Twig\TokenParser\IfTokenParser', 'Twig_TokenParser_If'); diff --git a/vendor/twig/twig/src/TokenParser/ImportTokenParser.php b/vendor/twig/twig/src/TokenParser/ImportTokenParser.php index b5674c1..6dcb766 100644 --- a/vendor/twig/twig/src/TokenParser/ImportTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/ImportTokenParser.php @@ -11,33 +11,35 @@ namespace Twig\TokenParser; -use Twig\Node\Expression\AssignNameExpression; +use Twig\Node\Expression\Variable\AssignTemplateVariable; +use Twig\Node\Expression\Variable\TemplateVariable; use Twig\Node\ImportNode; +use Twig\Node\Node; use Twig\Token; /** * Imports macros. * - * {% import 'forms.html' as forms %} + * {% import 'forms.html.twig' as forms %} + * + * @internal */ final class ImportTokenParser extends AbstractTokenParser { - public function parse(Token $token) + 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); + $macro = $this->parser->parseExpression(); + $this->parser->getStream()->expect(Token::NAME_TYPE, 'as'); + $name = $this->parser->getStream()->expect(Token::NAME_TYPE)->getValue(); + $var = new AssignTemplateVariable(new TemplateVariable($name, $token->getLine()), $this->parser->isMainScope()); + $this->parser->getStream()->expect(Token::BLOCK_END_TYPE); + $this->parser->addImportedSymbol('template', $name); - $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()); } - public function getTag() + public function getTag(): string { return 'import'; } } - -class_alias('Twig\TokenParser\ImportTokenParser', 'Twig_TokenParser_Import'); diff --git a/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php b/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php index e1e95da..55ac151 100644 --- a/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/IncludeTokenParser.php @@ -12,57 +12,62 @@ namespace Twig\TokenParser; +use Twig\Node\Expression\AbstractExpression; use Twig\Node\IncludeNode; +use Twig\Node\Node; use Twig\Token; /** * Includes a template. * - * {% include 'header.html' %} + * {% include 'header.html.twig' %} * Body - * {% include 'footer.html' %} + * {% include 'footer.html.twig' %} + * + * @internal */ class IncludeTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { - $expr = $this->parser->getExpressionParser()->parseExpression(); + $expr = $this->parser->parseExpression(); - list($variables, $only, $ignoreMissing) = $this->parseArguments(); + [$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()); } + /** + * @return array{0: ?AbstractExpression, 1: bool, 2: bool} + */ 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')) { - $variables = $this->parser->getExpressionParser()->parseExpression(); + if ($stream->nextIf(Token::NAME_TYPE, 'with')) { + $variables = $this->parser->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]; } - public function getTag() + public function getTag(): string { return 'include'; } } - -class_alias('Twig\TokenParser\IncludeTokenParser', 'Twig_TokenParser_Include'); diff --git a/vendor/twig/twig/src/TokenParser/MacroTokenParser.php b/vendor/twig/twig/src/TokenParser/MacroTokenParser.php index d267387..38e66c8 100644 --- a/vendor/twig/twig/src/TokenParser/MacroTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/MacroTokenParser.php @@ -13,6 +13,12 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; use Twig\Node\BodyNode; +use Twig\Node\EmptyNode; +use Twig\Node\Expression\ArrayExpression; +use Twig\Node\Expression\ConstantExpression; +use Twig\Node\Expression\Unary\NegUnary; +use Twig\Node\Expression\Unary\PosUnary; +use Twig\Node\Expression\Variable\LocalVariable; use Twig\Node\MacroNode; use Twig\Node\Node; use Twig\Token; @@ -23,44 +29,95 @@ use Twig\Token; * {% macro input(name, value, type, size) %} * * {% endmacro %} + * + * @internal */ final class MacroTokenParser extends AbstractTokenParser { - public function parse(Token $token) + 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->parseDefinition(); - $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) { - throw new SyntaxError(sprintf('Expected endmacro for macro "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()); + throw new SyntaxError(\sprintf('Expected endmacro for macro "%s" (but "%s" given).', $name, $value), $stream->getCurrent()->getLine(), $stream->getSourceContext()); } } $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 EmptyNode($lineno); } - public function decideBlockEnd(Token $token) + public function decideBlockEnd(Token $token): bool { return $token->test('endmacro'); } - public function getTag() + public function getTag(): string { return 'macro'; } -} -class_alias('Twig\TokenParser\MacroTokenParser', 'Twig_TokenParser_Macro'); + private function parseDefinition(): ArrayExpression + { + $arguments = new ArrayExpression([], $this->parser->getCurrentToken()->getLine()); + $stream = $this->parser->getStream(); + $stream->expect(Token::OPERATOR_TYPE, '(', 'A list of arguments must begin with an opening parenthesis'); + while (!$stream->test(Token::PUNCTUATION_TYPE, ')')) { + if (\count($arguments)) { + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'Arguments must be separated by a comma'); + + // if the comma above was a trailing comma, early exit the argument parse loop + if ($stream->test(Token::PUNCTUATION_TYPE, ')')) { + break; + } + } + + $token = $stream->expect(Token::NAME_TYPE, null, 'An argument must be a name'); + $name = new LocalVariable($token->getValue(), $this->parser->getCurrentToken()->getLine()); + if ($token = $stream->nextIf(Token::OPERATOR_TYPE, '=')) { + $default = $this->parser->parseExpression(); + } else { + $default = new ConstantExpression(null, $this->parser->getCurrentToken()->getLine()); + $default->setAttribute('is_implicit', true); + } + + if (!$this->checkConstantExpression($default)) { + throw new SyntaxError('A default value for an argument must be a constant (a boolean, a string, a number, a sequence, or a mapping).', $token->getLine(), $stream->getSourceContext()); + } + $arguments->addElement($default, $name); + } + $stream->expect(Token::PUNCTUATION_TYPE, ')', 'A list of arguments must be closed by a parenthesis'); + + return $arguments; + } + + // checks that the node only contains "constant" elements + private function checkConstantExpression(Node $node): bool + { + if (!($node instanceof ConstantExpression || $node instanceof ArrayExpression + || $node instanceof NegUnary || $node instanceof PosUnary + )) { + return false; + } + + foreach ($node as $n) { + if (!$this->checkConstantExpression($n)) { + return false; + } + } + + return true; + } +} diff --git a/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php b/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php index 1f57987..536c14f 100644 --- a/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/SandboxTokenParser.php @@ -13,6 +13,7 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; use Twig\Node\IncludeNode; +use Twig\Node\Node; use Twig\Node\SandboxNode; use Twig\Node\TextNode; use Twig\Token; @@ -21,19 +22,23 @@ use Twig\Token; * Marks a section of a template as untrusted code that must be evaluated in the sandbox mode. * * {% sandbox %} - * {% include 'user.html' %} + * {% include 'user.html.twig' %} * {% endsandbox %} * * @see https://twig.symfony.com/doc/api.html#sandbox-extension for details + * + * @internal */ final class SandboxTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { $stream = $this->parser->getStream(); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + trigger_deprecation('twig/twig', '3.15', \sprintf('The "sandbox" tag is deprecated in "%s" at line %d.', $stream->getSourceContext()->getName(), $token->getLine())); + + $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) { @@ -48,18 +53,16 @@ final class SandboxTokenParser extends AbstractTokenParser } } - return new SandboxNode($body, $token->getLine(), $this->getTag()); + return new SandboxNode($body, $token->getLine()); } - public function decideBlockEnd(Token $token) + public function decideBlockEnd(Token $token): bool { return $token->test('endsandbox'); } - public function getTag() + public function getTag(): string { return 'sandbox'; } } - -class_alias('Twig\TokenParser\SandboxTokenParser', 'Twig_TokenParser_Sandbox'); diff --git a/vendor/twig/twig/src/TokenParser/SetTokenParser.php b/vendor/twig/twig/src/TokenParser/SetTokenParser.php index 82fee26..1aabbf5 100644 --- a/vendor/twig/twig/src/TokenParser/SetTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/SetTokenParser.php @@ -12,6 +12,8 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; +use Twig\Node\Node; +use Twig\Node\Nodes; use Twig\Node\SetNode; use Twig\Token; @@ -24,20 +26,22 @@ use Twig\Token; * {% set foo = 'foo' ~ 'bar' %} * {% set foo, bar = 'foo', 'bar' %} * {% set foo %}Some content{% endset %} + * + * @internal */ final class SetTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { $lineno = $token->getLine(); $stream = $this->parser->getStream(); - $names = $this->parser->getExpressionParser()->parseAssignmentExpression(); + $names = $this->parseAssignmentExpression(); $capture = false; - if ($stream->nextIf(/* Token::OPERATOR_TYPE */ 8, '=')) { - $values = $this->parser->getExpressionParser()->parseMultitargetExpression(); + if ($stream->nextIf(Token::OPERATOR_TYPE, '=')) { + $values = $this->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()); @@ -49,24 +53,35 @@ final class SetTokenParser extends AbstractTokenParser 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) + public function decideBlockEnd(Token $token): bool { return $token->test('endset'); } - public function getTag() + public function getTag(): string { return 'set'; } -} -class_alias('Twig\TokenParser\SetTokenParser', 'Twig_TokenParser_Set'); + private function parseMultitargetExpression(): Nodes + { + $targets = []; + while (true) { + $targets[] = $this->parser->parseExpression(); + if (!$this->parser->getStream()->nextIf(Token::PUNCTUATION_TYPE, ',')) { + break; + } + } + + return new Nodes($targets); + } +} diff --git a/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php b/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php deleted file mode 100644 index b58624d..0000000 --- a/vendor/twig/twig/src/TokenParser/SpacelessTokenParser.php +++ /dev/null @@ -1,56 +0,0 @@ - - * foo - * - * {% endspaceless %} - * {# output will be
    foo
    #} - * - * @deprecated since Twig 2.7, to be removed in 3.0 (use the "spaceless" filter with the "apply" tag instead) - */ -final class SpacelessTokenParser extends AbstractTokenParser -{ - public function parse(Token $token) - { - $stream = $this->parser->getStream(); - $lineno = $token->getLine(); - - @trigger_error(sprintf('The spaceless tag in "%s" at line %d is deprecated since Twig 2.7, use the "spaceless" filter with the "apply" tag instead.', $stream->getSourceContext()->getName(), $lineno), E_USER_DEPRECATED); - - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); - $body = $this->parser->subparse([$this, 'decideSpacelessEnd'], true); - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); - - return new SpacelessNode($body, $lineno, $this->getTag()); - } - - public function decideSpacelessEnd(Token $token) - { - return $token->test('endspaceless'); - } - - public function getTag() - { - return 'spaceless'; - } -} - -class_alias('Twig\TokenParser\SpacelessTokenParser', 'Twig_TokenParser_Spaceless'); diff --git a/vendor/twig/twig/src/TokenParser/TokenParserInterface.php b/vendor/twig/twig/src/TokenParser/TokenParserInterface.php index 6f34106..bb8db3e 100644 --- a/vendor/twig/twig/src/TokenParser/TokenParserInterface.php +++ b/vendor/twig/twig/src/TokenParser/TokenParserInterface.php @@ -26,7 +26,7 @@ interface TokenParserInterface /** * Sets the parser associated with this token parser. */ - public function setParser(Parser $parser); + public function setParser(Parser $parser): void; /** * Parses a token and returns a node. @@ -40,13 +40,7 @@ interface TokenParserInterface /** * Gets the tag name associated with this token parser. * - * @return string The tag name + * @return string */ public function getTag(); } - -class_alias('Twig\TokenParser\TokenParserInterface', 'Twig_TokenParserInterface'); - -// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name. -class_exists('Twig\Token'); -class_exists('Twig\Parser'); diff --git a/vendor/twig/twig/src/TokenParser/TypesTokenParser.php b/vendor/twig/twig/src/TokenParser/TypesTokenParser.php new file mode 100644 index 0000000..2c7b77c --- /dev/null +++ b/vendor/twig/twig/src/TokenParser/TypesTokenParser.php @@ -0,0 +1,89 @@ + + * + * @internal + */ +final class TypesTokenParser extends AbstractTokenParser +{ + public function parse(Token $token): Node + { + $stream = $this->parser->getStream(); + $types = $this->parseSimpleMappingExpression($stream); + $stream->expect(Token::BLOCK_END_TYPE); + + return new TypesNode($types, $token->getLine()); + } + + /** + * @return array + * + * @throws SyntaxError + */ + private function parseSimpleMappingExpression(TokenStream $stream): array + { + $enclosed = null !== $stream->nextIf(Token::PUNCTUATION_TYPE, '{'); + $types = []; + $first = true; + while (!($stream->test(Token::PUNCTUATION_TYPE, '}') || $stream->test(Token::BLOCK_END_TYPE))) { + if (!$first) { + $stream->expect(Token::PUNCTUATION_TYPE, ',', 'A type string must be followed by a comma'); + + // trailing ,? + if ($stream->test(Token::PUNCTUATION_TYPE, '}') || $stream->test(Token::BLOCK_END_TYPE)) { + break; + } + } + $first = false; + + $nameToken = $stream->expect(Token::NAME_TYPE); + + if ($stream->nextIf(Token::OPERATOR_TYPE, '?:')) { + $isOptional = true; + } else { + $isOptional = null !== $stream->nextIf(Token::OPERATOR_TYPE, '?'); + $stream->expect(Token::PUNCTUATION_TYPE, ':', 'A type name must be followed by a colon (:)'); + } + + $valueToken = $stream->expect(Token::STRING_TYPE); + + $types[$nameToken->getValue()] = [ + 'type' => $valueToken->getValue(), + 'optional' => $isOptional, + ]; + } + + if ($enclosed) { + $stream->expect(Token::PUNCTUATION_TYPE, '}', 'An opened mapping is not properly closed'); + } + + return $types; + } + + public function getTag(): string + { + return 'types'; + } +} diff --git a/vendor/twig/twig/src/TokenParser/UseTokenParser.php b/vendor/twig/twig/src/TokenParser/UseTokenParser.php index 266efe5..41386c8 100644 --- a/vendor/twig/twig/src/TokenParser/UseTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/UseTokenParser.php @@ -12,8 +12,10 @@ namespace Twig\TokenParser; use Twig\Error\SyntaxError; +use Twig\Node\EmptyNode; use Twig\Node\Expression\ConstantExpression; use Twig\Node\Node; +use Twig\Node\Nodes; use Twig\Token; /** @@ -27,12 +29,14 @@ use Twig\Token; * {% block content %}{% endblock %} * * @see https://twig.symfony.com/doc/templates.html#horizontal-reuse for details. + * + * @internal */ final class UseTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { - $template = $this->parser->getExpressionParser()->parseExpression(); + $template = $this->parser->parseExpression(); $stream = $this->parser->getStream(); if (!$template instanceof ConstantExpression) { @@ -41,33 +45,31 @@ final class UseTokenParser extends AbstractTokenParser $targets = []; if ($stream->nextIf('with')) { - do { - $name = $stream->expect(/* Token::NAME_TYPE */ 5)->getValue(); + while (true) { + $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; } - } while (true); + } } - $stream->expect(/* Token::BLOCK_END_TYPE */ 3); + $stream->expect(Token::BLOCK_END_TYPE); - $this->parser->addTrait(new Node(['template' => $template, 'targets' => new Node($targets)])); + $this->parser->addTrait(new Nodes(['template' => $template, 'targets' => new Nodes($targets)])); - return new Node(); + return new EmptyNode($token->getLine()); } - public function getTag() + public function getTag(): string { return 'use'; } } - -class_alias('Twig\TokenParser\UseTokenParser', 'Twig_TokenParser_Use'); diff --git a/vendor/twig/twig/src/TokenParser/WithTokenParser.php b/vendor/twig/twig/src/TokenParser/WithTokenParser.php index c184fd7..83470d8 100644 --- a/vendor/twig/twig/src/TokenParser/WithTokenParser.php +++ b/vendor/twig/twig/src/TokenParser/WithTokenParser.php @@ -11,6 +11,7 @@ namespace Twig\TokenParser; +use Twig\Node\Node; use Twig\Node\WithNode; use Twig\Token; @@ -18,38 +19,38 @@ use Twig\Token; * Creates a nested scope. * * @author Fabien Potencier + * + * @internal */ final class WithTokenParser extends AbstractTokenParser { - public function parse(Token $token) + public function parse(Token $token): Node { $stream = $this->parser->getStream(); $variables = null; $only = false; - if (!$stream->test(/* Token::BLOCK_END_TYPE */ 3)) { - $variables = $this->parser->getExpressionParser()->parseExpression(); - $only = (bool) $stream->nextIf(/* Token::NAME_TYPE */ 5, 'only'); + if (!$stream->test(Token::BLOCK_END_TYPE)) { + $variables = $this->parser->parseExpression(); + $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) + public function decideWithEnd(Token $token): bool { return $token->test('endwith'); } - public function getTag() + public function getTag(): string { return 'with'; } } - -class_alias('Twig\TokenParser\WithTokenParser', 'Twig_TokenParser_With'); diff --git a/vendor/twig/twig/src/TokenStream.php b/vendor/twig/twig/src/TokenStream.php index 3fb9e86..7ee7539 100644 --- a/vendor/twig/twig/src/TokenStream.php +++ b/vendor/twig/twig/src/TokenStream.php @@ -21,21 +21,27 @@ use Twig\Error\SyntaxError; */ final class TokenStream { - private $tokens; private $current = 0; - private $source; - public function __construct(array $tokens, Source $source = null) - { - $this->tokens = $tokens; - $this->source = $source ?: new Source('', ''); + public function __construct( + private array $tokens, + private ?Source $source = null, + ) { + if (null === $this->source) { + trigger_deprecation('twig/twig', '3.16', \sprintf('Not passing a "%s" object to "%s" constructor is deprecated.', Source::class, __CLASS__)); + + $this->source = new Source('', ''); + } } - public function __toString() + public function __toString(): string { return implode("\n", $this->tokens); } + /** + * @return void + */ public function injectTokens(array $tokens) { $this->tokens = array_merge(\array_slice($this->tokens, 0, $this->current), $tokens, \array_slice($this->tokens, $this->current)); @@ -60,24 +66,22 @@ final class TokenStream */ public function nextIf($primary, $secondary = null) { - if ($this->tokens[$this->current]->test($primary, $secondary)) { - return $this->next(); - } + return $this->tokens[$this->current]->test($primary, $secondary) ? $this->next() : null; } /** * Tests a token and returns it or throws a syntax error. */ - public function expect($type, $value = null, string $message = null): Token + public function expect($type, $value = null, ?string $message = null): Token { $token = $this->tokens[$this->current]; if (!$token->test($type, $value)) { $line = $token->getLine(); - throw new SyntaxError(sprintf('%sUnexpected token "%s"%s ("%s" expected%s).', + throw new SyntaxError(\sprintf('%sUnexpected token "%s"%s ("%s" expected%s).', $message ? $message.'. ' : '', - Token::typeToEnglish($token->getType()), - $token->getValue() ? sprintf(' of value "%s"', $token->getValue()) : '', - Token::typeToEnglish($type), $value ? sprintf(' with value "%s"', $value) : ''), + $token->toEnglish(), + $token->getValue() ? \sprintf(' of value "%s"', $token->getValue()) : '', + Token::typeToEnglish($type), $value ? \sprintf(' with value "%s"', $value) : ''), $line, $this->source ); @@ -112,7 +116,7 @@ final class TokenStream */ public function isEOF(): bool { - return /* Token::EOF_TYPE */ -1 === $this->tokens[$this->current]->getType(); + return $this->tokens[$this->current]->test(Token::EOF_TYPE); } public function getCurrent(): Token @@ -120,15 +124,8 @@ final class TokenStream return $this->tokens[$this->current]; } - /** - * Gets the source associated with this stream. - * - * @internal - */ public function getSourceContext(): Source { return $this->source; } } - -class_alias('Twig\TokenStream', 'Twig_TokenStream'); diff --git a/vendor/twig/twig/src/TwigCallableInterface.php b/vendor/twig/twig/src/TwigCallableInterface.php new file mode 100644 index 0000000..2a8ff61 --- /dev/null +++ b/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/vendor/twig/twig/src/TwigFilter.php b/vendor/twig/twig/src/TwigFilter.php index 9e7b838..dece518 100644 --- a/vendor/twig/twig/src/TwigFilter.php +++ b/vendor/twig/twig/src/TwigFilter.php @@ -17,89 +17,34 @@ use Twig\Node\Node; /** * Represents a template filter. * - * @final since Twig 2.4.0 - * * @author Fabien Potencier * * @see https://twig.symfony.com/doc/templates.html#filters */ -class TwigFilter +final class TwigFilter extends AbstractTwigCallable { - private $name; - private $callable; - private $options; - private $arguments = []; - /** - * Creates a template filter. - * - * @param string $name Name of this filter - * @param callable|null $callable A callable implementing the filter. If null, you need to overwrite the "node_class" option to customize compilation. - * @param array $options Options array + * @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 = []) { - if (__CLASS__ !== \get_class($this)) { - @trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', E_USER_DEPRECATED); - } + parent::__construct($name, $callable, $options); - $this->name = $name; - $this->callable = $callable; $this->options = array_merge([ - 'needs_environment' => false, - 'needs_context' => false, - 'is_variadic' => false, 'is_safe' => null, 'is_safe_callback' => null, 'pre_escape' => null, 'preserves_safety' => null, 'node_class' => FilterExpression::class, - 'deprecated' => false, - 'alternative' => null, - ], $options); + ], $this->options); } - public function getName() + public function getType(): string { - return $this->name; + return 'filter'; } - /** - * Returns the callable to execute for this filter. - * - * @return callable|null - */ - public function getCallable() - { - return $this->callable; - } - - public function getNodeClass() - { - return $this->options['node_class']; - } - - public function setArguments($arguments) - { - $this->arguments = $arguments; - } - - public function getArguments() - { - return $this->arguments; - } - - public function needsEnvironment() - { - return $this->options['needs_environment']; - } - - public function needsContext() - { - return $this->options['needs_context']; - } - - public function getSafe(Node $filterArgs) + public function getSafe(Node $filterArgs): ?array { if (null !== $this->options['is_safe']) { return $this->options['is_safe']; @@ -108,43 +53,22 @@ class TwigFilter if (null !== $this->options['is_safe_callback']) { return $this->options['is_safe_callback']($filterArgs); } + + return []; } - public function getPreservesSafety() + public function getPreservesSafety(): array { - return $this->options['preserves_safety']; + return $this->options['preserves_safety'] ?? []; } - public function getPreEscape() + public function getPreEscape(): ?string { return $this->options['pre_escape']; } - public function isVariadic() + public function getMinimalNumberOfRequiredArguments(): int { - return $this->options['is_variadic']; - } - - public function isDeprecated() - { - return (bool) $this->options['deprecated']; - } - - public function getDeprecatedVersion() - { - return $this->options['deprecated']; - } - - public function getAlternative() - { - return $this->options['alternative']; + return parent::getMinimalNumberOfRequiredArguments() + 1; } } - -// For Twig 1.x compatibility -class_alias('Twig\TwigFilter', 'Twig_SimpleFilter', false); - -class_alias('Twig\TwigFilter', 'Twig_Filter'); - -// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name. -class_exists('Twig\Node\Node'); diff --git a/vendor/twig/twig/src/TwigFunction.php b/vendor/twig/twig/src/TwigFunction.php index c5779af..4a10df9 100644 --- a/vendor/twig/twig/src/TwigFunction.php +++ b/vendor/twig/twig/src/TwigFunction.php @@ -17,87 +17,38 @@ use Twig\Node\Node; /** * Represents a template function. * - * @final - * * @author Fabien Potencier * * @see https://twig.symfony.com/doc/templates.html#functions */ -class TwigFunction +final class TwigFunction extends AbstractTwigCallable { - private $name; - private $callable; - private $options; - private $arguments = []; - /** - * Creates a template function. - * - * @param string $name Name of this function - * @param callable|null $callable A callable implementing the function. If null, you need to overwrite the "node_class" option to customize compilation. - * @param array $options Options array + * @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 = []) { - if (__CLASS__ !== \get_class($this)) { - @trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', E_USER_DEPRECATED); - } + parent::__construct($name, $callable, $options); - $this->name = $name; - $this->callable = $callable; $this->options = array_merge([ - 'needs_environment' => false, - 'needs_context' => false, - 'is_variadic' => false, 'is_safe' => null, 'is_safe_callback' => null, 'node_class' => FunctionExpression::class, - 'deprecated' => false, - 'alternative' => null, - ], $options); + 'parser_callable' => null, + ], $this->options); } - public function getName() + public function getType(): string { - return $this->name; + return 'function'; } - /** - * Returns the callable to execute for this function. - * - * @return callable|null - */ - public function getCallable() + public function getParserCallable(): ?callable { - return $this->callable; + return $this->options['parser_callable']; } - public function getNodeClass() - { - return $this->options['node_class']; - } - - public function setArguments($arguments) - { - $this->arguments = $arguments; - } - - public function getArguments() - { - return $this->arguments; - } - - public function needsEnvironment() - { - return $this->options['needs_environment']; - } - - public function needsContext() - { - return $this->options['needs_context']; - } - - public function getSafe(Node $functionArgs) + public function getSafe(Node $functionArgs): ?array { if (null !== $this->options['is_safe']) { return $this->options['is_safe']; @@ -109,32 +60,4 @@ class TwigFunction return []; } - - public function isVariadic() - { - return $this->options['is_variadic']; - } - - public function isDeprecated() - { - return (bool) $this->options['deprecated']; - } - - public function getDeprecatedVersion() - { - return $this->options['deprecated']; - } - - public function getAlternative() - { - return $this->options['alternative']; - } } - -// For Twig 1.x compatibility -class_alias('Twig\TwigFunction', 'Twig_SimpleFunction', false); - -class_alias('Twig\TwigFunction', 'Twig_Function'); - -// Ensure that the aliased name is loaded to keep BC for classes implementing the typehint with the old aliased name. -class_exists('Twig\Node\Node'); diff --git a/vendor/twig/twig/src/TwigTest.php b/vendor/twig/twig/src/TwigTest.php index e15da41..5e58ad8 100644 --- a/vendor/twig/twig/src/TwigTest.php +++ b/vendor/twig/twig/src/TwigTest.php @@ -16,94 +16,52 @@ use Twig\Node\Expression\TestExpression; /** * Represents a template test. * - * @final since Twig 2.4.0 - * * @author Fabien Potencier * * @see https://twig.symfony.com/doc/templates.html#test-operator */ -class TwigTest +final class TwigTest extends AbstractTwigCallable { - private $name; - private $callable; - private $options; - private $arguments = []; - /** - * Creates a template test. - * - * @param string $name Name of this test - * @param callable|null $callable A callable implementing the test. If null, you need to overwrite the "node_class" option to customize compilation. - * @param array $options Options array + * @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 = []) { - if (__CLASS__ !== \get_class($this)) { - @trigger_error('Overriding '.__CLASS__.' is deprecated since Twig 2.4.0 and the class will be final in 3.0.', E_USER_DEPRECATED); - } + parent::__construct($name, $callable, $options); - $this->name = $name; - $this->callable = $callable; $this->options = array_merge([ - 'is_variadic' => false, 'node_class' => TestExpression::class, - 'deprecated' => false, - 'alternative' => null, - ], $options); + 'one_mandatory_argument' => false, + ], $this->options); } - public function getName() + public function getType(): string { - return $this->name; + return 'test'; } - /** - * Returns the callable to execute for this test. - * - * @return callable|null - */ - public function getCallable() + public function needsCharset(): bool { - return $this->callable; + return false; } - public function getNodeClass() + public function needsEnvironment(): bool { - return $this->options['node_class']; + return false; } - public function setArguments($arguments) + public function needsContext(): bool { - $this->arguments = $arguments; + return false; } - public function getArguments() + public function hasOneMandatoryArgument(): bool { - return $this->arguments; + return (bool) $this->options['one_mandatory_argument']; } - public function isVariadic() + public function getMinimalNumberOfRequiredArguments(): int { - return $this->options['is_variadic']; - } - - public function isDeprecated() - { - return (bool) $this->options['deprecated']; - } - - public function getDeprecatedVersion() - { - return $this->options['deprecated']; - } - - public function getAlternative() - { - return $this->options['alternative']; + return parent::getMinimalNumberOfRequiredArguments() + 1; } } - -// For Twig 1.x compatibility -class_alias('Twig\TwigTest', 'Twig_SimpleTest', false); - -class_alias('Twig\TwigTest', 'Twig_Test'); diff --git a/vendor/twig/twig/src/Util/CallableArgumentsExtractor.php b/vendor/twig/twig/src/Util/CallableArgumentsExtractor.php new file mode 100644 index 0000000..d862516 --- /dev/null +++ b/vendor/twig/twig/src/Util/CallableArgumentsExtractor.php @@ -0,0 +1,219 @@ + + * + * @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 = []; + $extractedArgumentNameMap = []; + $named = false; + foreach ($arguments as $name => $node) { + if (!\is_int($name)) { + $named = true; + } 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[$normalizedName = $this->normalizeName($name)] = $node; + $extractedArgumentNameMap[$normalizedName] = $name; + } + + if (!$named && !$this->twigCallable->isVariadic()) { + $min = $this->twigCallable->getMinimalNumberOfRequiredArguments(); + if (\count($extractedArguments) < $this->rc->getReflector()->getNumberOfRequiredParameters() - $min) { + $argName = $this->toSnakeCase($this->rc->getReflector()->getParameters()[$min + \count($extractedArguments)]->getName()); + + throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $argName, $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 = []; + $callableParameterNames = []; + $missingArguments = []; + $optionalArguments = []; + $pos = 0; + foreach ($callableParameters as $callableParameter) { + $callableParameterName = $callableParameter->name; + if (\PHP_VERSION_ID >= 80000 && 'range' === $callable) { + if ('start' === $callableParameterName) { + $callableParameterName = 'low'; + } elseif ('end' === $callableParameterName) { + $callableParameterName = 'high'; + } + } + + $callableParameterNames[] = $callableParameterName; + $normalizedCallableParameterName = $this->normalizeName($callableParameterName); + + if (\array_key_exists($normalizedCallableParameterName, $extractedArguments)) { + if (\array_key_exists($pos, $extractedArguments)) { + throw new SyntaxError(\sprintf('Argument "%s" is defined twice for %s "%s".', $callableParameterName, $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".', + $callableParameterName, $this->twigCallable->getType(), $this->twigCallable->getName(), implode(', ', array_map([$this, 'toSnakeCase'], $callableParameterNames)), \count($missingArguments) > 1 ? 's' : '', implode('", "', $missingArguments) + ), $this->node->getTemplateLine(), $this->node->getSourceContext()); + } + + $arguments = array_merge($arguments, $optionalArguments); + $arguments[] = $extractedArguments[$normalizedCallableParameterName]; + unset($extractedArguments[$normalizedCallableParameterName]); + $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[] = $callableParameterName; + } else { + throw new SyntaxError(\sprintf('Value for argument "%s" is required for %s "%s".', $this->toSnakeCase($callableParameterName), $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 { + $originalKey = $extractedArgumentNameMap[$key]; + if ($originalKey !== $this->toSnakeCase($originalKey)) { + trigger_deprecation('twig/twig', '3.15', \sprintf('Using "snake_case" for variadic arguments is required for a smooth upgrade with Twig 4.0; rename "%s" to "%s" in "%s" at line %d.', $originalKey, $this->toSnakeCase($originalKey), $this->node->getSourceContext()->getName(), $this->node->getTemplateLine())); + } + $arbitraryArguments->addElement($value, new ConstantExpression($this->toSnakeCase($originalKey), $this->node->getTemplateLine())); + // I Twig 4.0, don't convert the key: + // $arbitraryArguments->addElement($value, new ConstantExpression($originalKey, $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(', ', array_map([$this, 'toSnakeCase'], $callableParameterNames)) + ), + $unknownArgument ? $unknownArgument->getTemplateLine() : $this->node->getTemplateLine(), + $unknownArgument ? $unknownArgument->getSourceContext() : $this->node->getSourceContext() + ); + } + + return $arguments; + } + + private function normalizeName(string $name): string + { + return strtolower(str_replace('_', '', $name)); + } + + private function toSnakeCase(string $name): string + { + return strtolower(preg_replace(['/([A-Z]+)([A-Z][a-z])/', '/([a-z0-9])([A-Z])/'], '\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/vendor/twig/twig/src/Util/DeprecationCollector.php b/vendor/twig/twig/src/Util/DeprecationCollector.php index d373698..0ea26ed 100644 --- a/vendor/twig/twig/src/Util/DeprecationCollector.php +++ b/vendor/twig/twig/src/Util/DeprecationCollector.php @@ -20,11 +20,9 @@ use Twig\Source; */ final class DeprecationCollector { - private $twig; - - public function __construct(Environment $twig) - { - $this->twig = $twig; + public function __construct( + private Environment $twig, + ) { } /** @@ -35,7 +33,7 @@ final class DeprecationCollector * * @return array An array of deprecations */ - public function collectDir($dir, $ext = '.twig') + public function collectDir(string $dir, string $ext = '.twig'): array { $iterator = new \RegexIterator( new \RecursiveIteratorIterator( @@ -53,13 +51,15 @@ final class DeprecationCollector * * @return array An array of deprecations */ - public function collect(\Traversable $iterator) + public function collect(\Traversable $iterator): array { $deprecations = []; set_error_handler(function ($type, $msg) use (&$deprecations) { - if (E_USER_DEPRECATED === $type) { + if (\E_USER_DEPRECATED === $type) { $deprecations[] = $msg; } + + return false; }); foreach ($iterator as $name => $contents) { @@ -75,5 +75,3 @@ final class DeprecationCollector return $deprecations; } } - -class_alias('Twig\Util\DeprecationCollector', 'Twig_Util_DeprecationCollector'); diff --git a/vendor/twig/twig/src/Util/ReflectionCallable.php b/vendor/twig/twig/src/Util/ReflectionCallable.php new file mode 100644 index 0000000..0298e29 --- /dev/null +++ b/vendor/twig/twig/src/Util/ReflectionCallable.php @@ -0,0 +1,95 @@ + + * + * @internal + */ +final class ReflectionCallable +{ + private $reflector; + private $callable; + private $name; + + public function __construct( + TwigCallableInterface $twigCallable, + ) { + $callable = $twigCallable->getCallable(); + if (\is_string($callable) && false !== $pos = strpos($callable, '::')) { + $callable = [substr($callable, 0, $pos), substr($callable, 2 + $pos)]; + } + + if (\is_array($callable) && method_exists($callable[0], $callable[1])) { + $this->reflector = $r = new \ReflectionMethod($callable[0], $callable[1]); + $this->callable = $callable; + $this->name = $r->class.'::'.$r->name; + + return; + } + + $checkVisibility = $callable instanceof \Closure; + try { + $closure = \Closure::fromCallable($callable); + } catch (\TypeError $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); + + if (str_contains($r->name, '{closure')) { + $this->callable = $callable; + $this->name = 'Closure'; + + return; + } + + if ($object = $r->getClosureThis()) { + $callable = [$object, $r->name]; + $this->name = get_debug_type($object).'::'.$r->name; + } elseif (\PHP_VERSION_ID >= 80111 && $class = $r->getClosureCalledClass()) { + $callable = [$class->name, $r->name]; + $this->name = $class->name.'::'.$r->name; + } elseif (\PHP_VERSION_ID < 80111 && $class = $r->getClosureScopeClass()) { + $callable = [\is_array($callable) ? $callable[0] : $class->name, $r->name]; + $this->name = (\is_array($callable) ? $callable[0] : $class->name).'::'.$r->name; + } else { + $callable = $this->name = $r->name; + } + + if ($checkVisibility && \is_array($callable) && method_exists(...$callable) && !(new \ReflectionMethod(...$callable))->isPublic()) { + $callable = $r->getClosure(); + } + + $this->callable = $callable; + } + + public function getReflector(): \ReflectionFunctionAbstract + { + return $this->reflector; + } + + /** + * @return callable + */ + public function getCallable() + { + return $this->callable; + } + + public function getName(): string + { + return $this->name; + } +} diff --git a/vendor/twig/twig/src/Util/TemplateDirIterator.php b/vendor/twig/twig/src/Util/TemplateDirIterator.php index 1ab0dac..d739b28 100644 --- a/vendor/twig/twig/src/Util/TemplateDirIterator.php +++ b/vendor/twig/twig/src/Util/TemplateDirIterator.php @@ -16,15 +16,21 @@ namespace Twig\Util; */ class TemplateDirIterator extends \IteratorIterator { + /** + * @return string + */ + #[\ReturnTypeWillChange] public function current() { return file_get_contents(parent::current()); } + /** + * @return string + */ + #[\ReturnTypeWillChange] public function key() { return (string) parent::key(); } } - -class_alias('Twig\Util\TemplateDirIterator', 'Twig_Util_TemplateDirIterator'); diff --git a/vendor/upstatement/routes/Routes.php b/vendor/upstatement/routes/Routes.php new file mode 100755 index 0000000..7cefa28 --- /dev/null +++ b/vendor/upstatement/routes/Routes.php @@ -0,0 +1,166 @@ +router)) { + $route = $upstatement_routes->router->match(); + + unset($upstatement_routes->router); + + if ($route && isset($route['target'])) { + if ( isset($route['params']) ) { + call_user_func($route['target'], $route['params']); + } else { + call_user_func($route['target']); + } + } + } + } + + /** + * @param string $route A string to match (ex: 'myfoo') + * @param callable $callback A function to run, examples: + * Routes::map('myfoo', 'my_callback_function'); + * Routes::map('mybaq', array($my_class, 'method')); + * Routes::map('myqux', function() { + * //stuff goes here + * }); + */ + public static function map($route, $callback, $name = '') { + global $upstatement_routes; + if (!isset($upstatement_routes->router)) { + $upstatement_routes->router = new AltoRouter(); + $site_url = get_bloginfo('url'); + $site_url_parts = explode('/', $site_url); + $site_url_parts = array_slice($site_url_parts, 3); + $base_path = implode('/', $site_url_parts); + if (!$base_path || strpos($route, $base_path) === 0) { + $base_path = '/'; + } else { + $base_path = '/' . $base_path . '/'; + } + // Clean any double slashes that have resulted + $base_path = str_replace( "//", "/", $base_path ); + $upstatement_routes->router->setBasePath($base_path); + } + $route = self::convert_route($route); + $upstatement_routes->router->map('GET|POST|PUT|DELETE', trailingslashit($route), $callback, $name); + $upstatement_routes->router->map('GET|POST|PUT|DELETE', untrailingslashit($route), $callback, $name); + } + + /** + * @return string A string in a format for AltoRouter + * ex: [:my_param] + */ + public static function convert_route($route_string) { + if (strpos($route_string, '[') > -1) { + return $route_string; + } + $route_string = preg_replace('/(:)\w+/', '/[$0]', $route_string); + $route_string = str_replace('[[', '[', $route_string); + $route_string = str_replace(']]', ']', $route_string); + $route_string = str_replace('[/:', '[:', $route_string); + $route_string = str_replace('//[', '/[', $route_string); + if ( strpos($route_string, '/') === 0 ) { + $route_string = substr($route_string, 1); + } + return $route_string; + } + + /** + * @param string $template A php file to load (ex: 'single.php') + * @param array|bool $tparams An array of data to send to the php file. Inside the php file + * this data can be accessed via: + * global $params; + * @param int $status_code A code for the status (ex: 200) + * @param WP_Query $query Use a WP_Query object in the template file instead of + * the default query + * @param int $priority The priority used by the "template_include" filter + * @return bool + */ + public static function load($template, $tparams = false, $query = false, $status_code = 200, $priority = 10) { + $fullPath = is_readable($template); + if (!$fullPath) { + $template = locate_template($template); + } + if ($tparams){ + global $params; + $params = $tparams; + } + if ($status_code) { + add_filter('status_header', function($status_header, $header, $text, $protocol) use ($status_code) { + $text = get_status_header_desc($status_code); + $header_string = "$protocol $status_code $text"; + return $header_string; + }, 10, 4 ); + if (404 != $status_code) { + add_action('parse_query', function($query) { + if ($query->is_main_query()){ + $query->is_404 = false; + } + },1); + add_action('template_redirect', function(){ + global $wp_query; + $wp_query->is_404 = false; + },1); + } + } + + if ($query) { + add_action('parse_request', function() use ($query) { + global $wp; + if ( is_callable($query) ) + $query = call_user_func($query); + + if ( is_array($query) ) + $wp->query_vars = $query; + elseif ( !empty($query) ) + parse_str($query, $wp->query_vars); + else + return true; // Could not interpret query. Let WP try. + + return false; + }); + } + if ($template) { + add_filter('template_include', function($t) use ($template) { + return $template; + }, $priority); + return true; + } + return false; + } +} + +global $upstatement_routes; +$upstatement_routes = new Routes(); + +if ( file_exists($composer_autoload = __DIR__ . '/vendor/autoload.php') + || file_exists($composer_autoload = WP_CONTENT_DIR.'/vendor/autoload.php')){ + require_once($composer_autoload); +} + diff --git a/vendor/upstatement/routes/composer.json b/vendor/upstatement/routes/composer.json new file mode 100755 index 0000000..a248eed --- /dev/null +++ b/vendor/upstatement/routes/composer.json @@ -0,0 +1,39 @@ +{ + "name": "upstatement/routes", + "description": "Manage rewrites and routes in WordPress with this dead-simple plugin", + "keywords": [ + "routes", + "routing", + "rewrite", + "redirects" + ], + "homepage": "https://www.upstatement.com", + "license": "MIT", + "authors": [ + { + "name": "Jared Novack", + "email": "jared@upstatement.com", + "homepage": "https://www.upstatement.com" + } + ], + "support": { + "issues": "https://github.com/Upstatement/routes/issues", + "wiki": "https://github.com/Upstatement/routes/wiki", + "source": "https://github.com/Upstatement/routes" + }, + "require": { + "php": ">=7.3", + "altorouter/altorouter": "^2.0.2", + "composer/installers": "^1.0 || ^2.0" + }, + "require-dev": { + "phpunit/phpunit": "5.7.16", + "wp-cli/wp-cli": "*", + "satooshi/php-coveralls": "*" + }, + "autoload": { + "psr-0": { + "Routes": "" + } + } +} diff --git a/templates/actualidad.twig b/views/templates/actualidad.twig similarity index 100% rename from templates/actualidad.twig rename to views/templates/actualidad.twig diff --git a/templates/base.twig b/views/templates/base.twig similarity index 100% rename from templates/base.twig rename to views/templates/base.twig diff --git a/templates/block-clientes.twig b/views/templates/block-clientes.twig similarity index 100% rename from templates/block-clientes.twig rename to views/templates/block-clientes.twig diff --git a/templates/block-network.twig b/views/templates/block-network.twig similarity index 100% rename from templates/block-network.twig rename to views/templates/block-network.twig diff --git a/templates/contacto.twig b/views/templates/contacto.twig similarity index 100% rename from templates/contacto.twig rename to views/templates/contacto.twig diff --git a/templates/footer.twig b/views/templates/footer.twig similarity index 100% rename from templates/footer.twig rename to views/templates/footer.twig diff --git a/templates/frontpage-actualidad.twig b/views/templates/frontpage-actualidad.twig similarity index 100% rename from templates/frontpage-actualidad.twig rename to views/templates/frontpage-actualidad.twig diff --git a/templates/frontpage-proyectos-slider.twig b/views/templates/frontpage-proyectos-slider.twig similarity index 100% rename from templates/frontpage-proyectos-slider.twig rename to views/templates/frontpage-proyectos-slider.twig diff --git a/templates/frontpage-proyectos-slider_lg.twig b/views/templates/frontpage-proyectos-slider_lg.twig similarity index 100% rename from templates/frontpage-proyectos-slider_lg.twig rename to views/templates/frontpage-proyectos-slider_lg.twig diff --git a/templates/frontpage-proyectos-slider_md.twig b/views/templates/frontpage-proyectos-slider_md.twig similarity index 100% rename from templates/frontpage-proyectos-slider_md.twig rename to views/templates/frontpage-proyectos-slider_md.twig diff --git a/templates/frontpage-proyectos.twig b/views/templates/frontpage-proyectos.twig similarity index 100% rename from templates/frontpage-proyectos.twig rename to views/templates/frontpage-proyectos.twig diff --git a/templates/frontpage-servicios.twig b/views/templates/frontpage-servicios.twig similarity index 100% rename from templates/frontpage-servicios.twig rename to views/templates/frontpage-servicios.twig diff --git a/templates/frontpage-slider.twig b/views/templates/frontpage-slider.twig similarity index 100% rename from templates/frontpage-slider.twig rename to views/templates/frontpage-slider.twig diff --git a/templates/frontpage.twig b/views/templates/frontpage.twig similarity index 100% rename from templates/frontpage.twig rename to views/templates/frontpage.twig diff --git a/templates/frontpage_post.twig b/views/templates/frontpage_post.twig similarity index 100% rename from templates/frontpage_post.twig rename to views/templates/frontpage_post.twig diff --git a/templates/gallery.twig b/views/templates/gallery.twig similarity index 100% rename from templates/gallery.twig rename to views/templates/gallery.twig diff --git a/templates/header.twig b/views/templates/header.twig similarity index 100% rename from templates/header.twig rename to views/templates/header.twig diff --git a/templates/html-header.twig b/views/templates/html-header.twig similarity index 100% rename from templates/html-header.twig rename to views/templates/html-header.twig diff --git a/templates/index.twig b/views/templates/index.twig similarity index 100% rename from templates/index.twig rename to views/templates/index.twig diff --git a/templates/menu.twig b/views/templates/menu.twig similarity index 100% rename from templates/menu.twig rename to views/templates/menu.twig diff --git a/templates/page.twig b/views/templates/page.twig similarity index 100% rename from templates/page.twig rename to views/templates/page.twig diff --git a/templates/posts-team-members.twig b/views/templates/posts-team-members.twig similarity index 100% rename from templates/posts-team-members.twig rename to views/templates/posts-team-members.twig diff --git a/templates/posts_by_project.twig b/views/templates/posts_by_project.twig similarity index 100% rename from templates/posts_by_project.twig rename to views/templates/posts_by_project.twig diff --git a/templates/posts_by_service.twig b/views/templates/posts_by_service.twig similarity index 100% rename from templates/posts_by_service.twig rename to views/templates/posts_by_service.twig diff --git a/templates/posts_featured_image.twig b/views/templates/posts_featured_image.twig similarity index 100% rename from templates/posts_featured_image.twig rename to views/templates/posts_featured_image.twig diff --git a/templates/proyectos.twig b/views/templates/proyectos.twig similarity index 100% rename from templates/proyectos.twig rename to views/templates/proyectos.twig diff --git a/templates/related.twig b/views/templates/related.twig similarity index 100% rename from templates/related.twig rename to views/templates/related.twig diff --git a/templates/related_proyectos_item-servicio-unico.twig b/views/templates/related_proyectos_item-servicio-unico.twig similarity index 100% rename from templates/related_proyectos_item-servicio-unico.twig rename to views/templates/related_proyectos_item-servicio-unico.twig diff --git a/templates/related_proyectos_item-servicio.twig b/views/templates/related_proyectos_item-servicio.twig similarity index 100% rename from templates/related_proyectos_item-servicio.twig rename to views/templates/related_proyectos_item-servicio.twig diff --git a/templates/related_proyectos_item.twig b/views/templates/related_proyectos_item.twig similarity index 100% rename from templates/related_proyectos_item.twig rename to views/templates/related_proyectos_item.twig diff --git a/templates/single.twig b/views/templates/single.twig similarity index 100% rename from templates/single.twig rename to views/templates/single.twig diff --git a/templates/solicita-presupuesto.twig b/views/templates/solicita-presupuesto.twig similarity index 100% rename from templates/solicita-presupuesto.twig rename to views/templates/solicita-presupuesto.twig diff --git a/templates/team-member.twig b/views/templates/team-member.twig similarity index 100% rename from templates/team-member.twig rename to views/templates/team-member.twig