From 4dd1155b0319f9989f13e6745c522b77fe85324d Mon Sep 17 00:00:00 2001 From: Kyrian Obikwelu Date: Mon, 27 Oct 2025 12:13:38 +0100 Subject: [PATCH 1/2] feat: improve code quality and fix runtime argument passing bugs MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Add PHPStan static analysis and PHP CS Fixer for code quality - Fix FunctionValue::call method to use proper callable invocation - Correct ObjectValue built-in functions to accept $args array pattern - Fix method name inconsistencies (evaluateAsBool → asBool) across RuntimeValue classes - Add proper type validation and error handling in built-in functions - Establish consistent argument passing pattern for all RuntimeValue methods - Add GitHub Actions workflow for automated code quality checks - Support PHP 8.4 in CI/CD pipeline --- .github/workflows/test.yml | 44 +- .gitignore | 4 +- .php-cs-fixer.php | 20 + composer.json | 18 +- composer.lock | 4890 ++++++++++++++++++------- phpstan.neon | 22 + src/AST/ArrayLiteral.php | 5 +- src/AST/BinaryExpression.php | 9 +- src/AST/BreakStatement.php | 2 +- src/AST/CallExpression.php | 10 +- src/AST/CallStatement.php | 9 +- src/AST/Comment.php | 6 +- src/AST/ContinueStatement.php | 2 +- src/AST/Expression.php | 8 +- src/AST/FilterExpression.php | 12 +- src/AST/FilterStatement.php | 7 +- src/AST/FloatLiteral.php | 5 +- src/AST/ForStatement.php | 19 +- src/AST/Identifier.php | 5 +- src/AST/IfStatement.php | 13 +- src/AST/IntegerLiteral.php | 5 +- src/AST/KeywordArgumentExpression.php | 10 +- src/AST/Literal.php | 12 +- src/AST/Macro.php | 10 +- src/AST/MemberExpression.php | 7 +- src/AST/ObjectLiteral.php | 7 +- src/AST/Program.php | 5 +- src/AST/SelectExpression.php | 6 +- src/AST/SetStatement.php | 10 +- src/AST/SliceExpression.php | 9 +- src/AST/SpreadExpression.php | 6 +- src/AST/Statement.php | 5 +- src/AST/StringLiteral.php | 5 +- src/AST/TernaryExpression.php | 6 +- src/AST/TestExpression.php | 8 +- src/AST/TupleLiteral.php | 5 +- src/AST/UnaryExpression.php | 10 +- src/Core/Environment.php | 245 +- src/Core/Interpreter.php | 774 ++-- src/Core/Lexer.php | 142 +- src/Core/Parser.php | 382 +- src/Core/Token.php | 81 +- src/Core/TokenType.php | 51 +- src/Core/Utils.php | 26 +- src/Exceptions/ParserException.php | 8 +- src/Exceptions/RuntimeException.php | 8 +- src/Exceptions/SyntaxError.php | 8 +- src/Runtime/ArrayValue.php | 19 +- src/Runtime/BooleanValue.php | 6 +- src/Runtime/BreakControl.php | 6 +- src/Runtime/ContinueControl.php | 6 +- src/Runtime/FloatValue.php | 5 +- src/Runtime/FunctionValue.php | 12 +- src/Runtime/IntegerValue.php | 5 +- src/Runtime/KeywordArgumentsValue.php | 2 +- src/Runtime/NullValue.php | 8 +- src/Runtime/ObjectValue.php | 45 +- src/Runtime/RuntimeValue.php | 26 +- src/Runtime/StringValue.php | 93 +- src/Runtime/TupleValue.php | 6 +- src/Runtime/UndefinedValue.php | 8 +- src/Template.php | 12 +- tests/Datasets/End2EndDataset.php | 345 +- tests/Datasets/InterpreterDataset.php | 204 +- tests/End2EndTest.php | 5 +- tests/Pest.php | 4 +- tests/TemplateTest.php | 63 +- tests/UtilsTest.php | 19 +- 68 files changed, 5163 insertions(+), 2697 deletions(-) create mode 100644 .php-cs-fixer.php create mode 100644 phpstan.neon diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index dec5485..788452f 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -9,20 +9,13 @@ jobs: strategy: fail-fast: true matrix: - php: [ 8.1, 8.2, 8.3 ] - dependency-version: [ prefer-lowest, prefer-stable ] + php: [ 8.1, 8.2, 8.3, 8.4 ] - name: Tests on PHP ${{ matrix.php }} - ${{ matrix.dependency-version }} + name: Tests on PHP ${{ matrix.php }} steps: - name: Checkout - uses: actions/checkout@v3 - - - name: Cache dependencies - uses: actions/cache@v3 - with: - path: ~/.composer/cache/files - key: dependencies-php-${{ matrix.php }}-composer-${{ hashFiles('composer.json') }} + uses: actions/checkout@v5 - name: Setup PHP uses: shivammathur/setup-php@v2 @@ -32,7 +25,34 @@ jobs: coverage: none - name: Install Composer dependencies - run: composer update --${{ matrix.dependency-version }} --no-interaction --prefer-dist + uses: "ramsey/composer-install@v3" - name: Run tests - run: composer test \ No newline at end of file + run: composer test + + qa: + runs-on: ubuntu-latest + name: Code Quality + + steps: + - name: Checkout + uses: actions/checkout@v5 + + - name: Setup PHP + uses: shivammathur/setup-php@v2 + with: + php-version: 8.1 + extensions: dom, mbstring, zip + coverage: none + + - name: Validate composer.json and composer.lock + run: composer validate --strict + + - name: Install Composer dependencies + uses: "ramsey/composer-install@v3" + + - name: Check code style + run: vendor/bin/php-cs-fixer fix --dry-run --diff --verbose + + - name: Check code quality + run: vendor/bin/phpstan analyze \ No newline at end of file diff --git a/.gitignore b/.gitignore index cb756e9..b38c6b2 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ vendor -.idea \ No newline at end of file +.idea +.claude +.php-cs-fixer.cache \ No newline at end of file diff --git a/.php-cs-fixer.php b/.php-cs-fixer.php new file mode 100644 index 0000000..b803390 --- /dev/null +++ b/.php-cs-fixer.php @@ -0,0 +1,20 @@ +in(__DIR__ . '/src') + ->in(__DIR__ . '/tests'); + +$config = new PhpCsFixer\Config(); + +$parallelConfig = PhpCsFixer\Runner\Parallel\ParallelConfigFactory::detect(); + +return $config + ->setRules([ + '@PSR12' => true, + 'array_syntax' => ['syntax' => 'short'], + 'explicit_string_variable' => true, + ]) + ->setParallelConfig($parallelConfig) + ->setFinder($finder); \ No newline at end of file diff --git a/composer.json b/composer.json index 724d505..bb45093 100644 --- a/composer.json +++ b/composer.json @@ -7,7 +7,9 @@ }, "require-dev": { "symfony/var-dumper": "^6.3|^7.0", - "pestphp/pest": "^2.34" + "pestphp/pest": "^2.36.0|^3.5.0", + "friendsofphp/php-cs-fixer": "^3.89", + "phpstan/phpstan": "^2.1" }, "license": "MIT", "autoload": { @@ -31,6 +33,18 @@ }, "scripts": { "test": "vendor/bin/pest", - "test:coverage": "XDEBUG_MODE=coverage ./vendor/bin/pest --coverage" + "test:coverage": "XDEBUG_MODE=coverage ./vendor/bin/pest --coverage", + "cs-fix": "vendor/bin/php-cs-fixer fix", + "cs-check": "vendor/bin/php-cs-fixer fix --dry-run --diff --verbose", + "phpstan": "vendor/bin/phpstan analyze", + "phpstan-baseline": "vendor/bin/phpstan analyze --generate-baseline", + "qa": [ + "@cs-check", + "@phpstan" + ], + "qa-fix": [ + "@cs-fix", + "@phpstan" + ] } } diff --git a/composer.lock b/composer.lock index 196ffd2..814532a 100644 --- a/composer.lock +++ b/composer.lock @@ -4,21 +4,21 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "bccbcd38371c214263b25093aae8d0bd", + "content-hash": "eaa9949d6c6b19a2c2685e18485f436d", "packages": [], "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.4.3", + "version": "v7.8.4", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec" + "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/64fcfd0e28a6b8078a19dbf9127be2ee645b92ec", - "reference": "64fcfd0e28a6b8078a19dbf9127be2ee645b92ec", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/130a9bf0e269ee5f5b320108f794ad03e275cad4", + "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4", "shasum": "" }, "require": { @@ -26,31 +26,30 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.1.0", - "jean85/pretty-package-versions": "^2.0.5", - "php": "~8.2.0 || ~8.3.0", - "phpunit/php-code-coverage": "^10.1.11 || ^11.0.0", - "phpunit/php-file-iterator": "^4.1.0 || ^5.0.0", - "phpunit/php-timer": "^6.0.0 || ^7.0.0", - "phpunit/phpunit": "^10.5.9 || ^11.0.3", - "sebastian/environment": "^6.0.1 || ^7.0.0", - "symfony/console": "^6.4.3 || ^7.0.3", - "symfony/process": "^6.4.3 || ^7.0.3" + "fidry/cpu-core-counter": "^1.2.0", + "jean85/pretty-package-versions": "^2.1.1", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-timer": "^7.0.1", + "phpunit/phpunit": "^11.5.24", + "sebastian/environment": "^7.2.1", + "symfony/console": "^6.4.22 || ^7.3.0", + "symfony/process": "^6.4.20 || ^7.3.0" }, "require-dev": { "doctrine/coding-standard": "^12.0.0", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^1.10.58", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "squizlabs/php_codesniffer": "^3.9.0", - "symfony/filesystem": "^6.4.3 || ^7.0.3" + "phpstan/phpstan": "^2.1.17", + "phpstan/phpstan-deprecation-rules": "^2.0.3", + "phpstan/phpstan-phpunit": "^2.0.6", + "phpstan/phpstan-strict-rules": "^2.0.4", + "squizlabs/php_codesniffer": "^3.13.2", + "symfony/filesystem": "^6.4.13 || ^7.3.0" }, "bin": [ "bin/paratest", - "bin/paratest.bat", "bin/paratest_for_phpstorm" ], "type": "library", @@ -87,7 +86,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.4.3" + "source": "https://github.com/paratestphp/paratest/tree/v7.8.4" }, "funding": [ { @@ -99,87 +98,111 @@ "type": "paypal" } ], - "time": "2024-02-20T07:24:02+00:00" + "time": "2025-06-23T06:07:21+00:00" }, { - "name": "doctrine/deprecations", - "version": "1.1.3", + "name": "clue/ndjson-react", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab" + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", - "reference": "dfbaa3c2d2e9a9df1118213f3b8b0c597bb99fab", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0" + "php": ">=5.3", + "react/stream": "^1.2" }, "require-dev": { - "doctrine/coding-standard": "^9", - "phpstan/phpstan": "1.4.10 || 1.10.15", - "phpstan/phpstan-phpunit": "^1.0", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "psalm/plugin-phpunit": "0.18.4", - "psr/log": "^1 || ^2 || ^3", - "vimeo/psalm": "4.30.0 || 5.12.0" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Deprecations\\": "lib/Doctrine/Deprecations" + "Clue\\React\\NDJson\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering" + } + ], + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", + "keywords": [ + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" + ], "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.3" + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" }, - "time": "2024-01-30T19:34:25+00:00" + "funding": [ + { + "url": "https://clue.engineering/support", + "type": "custom" + }, + { + "url": "https://github.com/clue", + "type": "github" + } + ], + "time": "2022-12-23T10:58:28+00:00" }, { - "name": "fidry/cpu-core-counter", - "version": "1.1.0", + "name": "composer/pcre", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/f92996c4d5c1a696a6a970e20f7c4216200fcc42", - "reference": "f92996c4d5c1a696a6a970e20f7c4216200fcc42", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "fidry/makefile": "^0.2.0", - "fidry/php-cs-fixer-config": "^1.1.2", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^1.9.2", - "phpstan/phpstan-deprecation-rules": "^1.0.0", - "phpstan/phpstan-phpunit": "^1.2.2", - "phpstan/phpstan-strict-rules": "^1.4.4", - "phpunit/phpunit": "^8.5.31 || ^9.5.26", - "webmozarts/strict-phpunit": "^7.5" + "phpstan/phpstan": "^1.12 || ^2", + "phpstan/phpstan-strict-rules": "^1 || ^2", + "phpunit/phpunit": "^8 || ^9" }, "type": "library", + "extra": { + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" + "Composer\\Pcre\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -188,63 +211,68 @@ ], "authors": [ { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Tiny utility to get the number of CPU cores.", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "CPU", - "core" + "PCRE", + "preg", + "regex", + "regular expression" ], "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.1.0" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" }, "funding": [ { - "url": "https://github.com/theofidry", + "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-02-07T09:43:46+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { - "name": "filp/whoops", - "version": "2.15.4", + "name": "composer/semver", + "version": "3.4.4", "source": { "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546" + "url": "https://github.com/composer/semver.git", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/a139776fa3f5985a50b509f2a02ff0f709d2a546", - "reference": "a139776fa3f5985a50b509f2a02ff0f709d2a546", + "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", + "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", "shasum": "" }, "require": { - "php": "^5.5.9 || ^7.0 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "mockery/mockery": "^0.9 || ^1.0", - "phpunit/phpunit": "^4.8.36 || ^5.7.27 || ^6.5.14 || ^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^2.6 || ^3.0 || ^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.7-dev" + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Whoops\\": "src/Whoops/" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -253,67 +281,73 @@ ], "authors": [ { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" + "name": "Nils Adermann", + "email": "naderman@naderman.de", + "homepage": "http://www.naderman.de" + }, + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" + }, + { + "name": "Rob Bast", + "email": "rob.bast@gmail.com", + "homepage": "http://robbast.nl" } ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.15.4" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.4" }, "funding": [ { - "url": "https://github.com/denis-sokolov", + "url": "https://packagist.com", + "type": "custom" + }, + { + "url": "https://github.com/composer", "type": "github" } ], - "time": "2023-11-03T12:00:00+00:00" + "time": "2025-08-20T19:15:30+00:00" }, { - "name": "jean85/pretty-package-versions", - "version": "2.0.6", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/f9fdd29ad8e6d024f52678b570e5593759b550b4", - "reference": "f9fdd29ad8e6d024f52678b570e5593759b550b4", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "composer-runtime-api": "^2.0.0", - "php": "^7.1|^8.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", - "phpunit/phpunit": "^7.5|^8.5|^9.4", - "vimeo/psalm": "^4.3" + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-strict-rules": "^1.1", + "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "psr-4": { - "Jean85\\": "src/" + "Composer\\XdebugHandler\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -322,192 +356,163 @@ ], "authors": [ { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "A library to get pretty versions strings of installed dependencies", + "description": "Restarts a process without Xdebug.", "keywords": [ - "composer", - "package", - "release", - "versions" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.0.6" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/xdebug-handler/issues", + "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" }, - "time": "2024-03-08T09:58:59+00:00" + "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-05-06T16:37:16+00:00" }, { - "name": "myclabs/deep-copy", - "version": "1.11.1", + "name": "doctrine/deprecations", + "version": "1.1.5", "source": { "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" + "url": "https://github.com/doctrine/deprecations.git", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", - "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", "shasum": "" }, "require": { "php": "^7.1 || ^8.0" }, "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3,<3.2.2" + "phpunit/phpunit": "<=7.5 || >=13" }, "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + "doctrine/coding-standard": "^9 || ^12 || ^13", + "phpstan/phpstan": "1.4.10 || 2.1.11", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", + "psr/log": "^1 || ^2 || ^3" + }, + "suggest": { + "psr/log": "Allows logging deprecations via PSR-3 logger implementation" }, "type": "library", "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], "psr-4": { - "DeepCopy\\": "src/DeepCopy/" + "Doctrine\\Deprecations\\": "src" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], + "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", + "homepage": "https://www.doctrine-project.org/", "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" + "issues": "https://github.com/doctrine/deprecations/issues", + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2023-03-08T13:26:56+00:00" + "time": "2025-04-07T20:06:18+00:00" }, { - "name": "nikic/php-parser", - "version": "v5.0.2", + "name": "evenement/evenement", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13" + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/139676794dc1e9231bf7bcd123cfc0c99182cb13", - "reference": "139676794dc1e9231bf7bcd123cfc0c99182cb13", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", "shasum": "" }, "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" + "php": ">=7.0" }, "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^7.0 || ^8.0 || ^9.0" + "phpunit/phpunit": "^9 || ^6" }, - "bin": [ - "bin/php-parse" - ], "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.0-dev" - } - }, "autoload": { "psr-4": { - "PhpParser\\": "lib/PhpParser" + "Evenement\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Nikita Popov" + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" } ], - "description": "A PHP parser written in PHP", + "description": "Événement is a very simple event dispatching library for PHP", "keywords": [ - "parser", - "php" + "event-dispatcher", + "event-emitter" ], "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.0.2" + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" }, - "time": "2024-03-05T20:51:40+00:00" + "time": "2023-08-08T05:53:35+00:00" }, { - "name": "nunomaduro/collision", - "version": "v8.1.1", + "name": "fidry/cpu-core-counter", + "version": "1.3.0", "source": { "type": "git", - "url": "https://github.com/nunomaduro/collision.git", - "reference": "13e5d538b95a744d85f447a321ce10adb28e9af9" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/13e5d538b95a744d85f447a321ce10adb28e9af9", - "reference": "13e5d538b95a744d85f447a321ce10adb28e9af9", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { - "filp/whoops": "^2.15.4", - "nunomaduro/termwind": "^2.0.1", - "php": "^8.2.0", - "symfony/console": "^7.0.4" - }, - "conflict": { - "laravel/framework": "<11.0.0 || >=12.0.0", - "phpunit/phpunit": "<10.5.1 || >=12.0.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "larastan/larastan": "^2.9.2", - "laravel/framework": "^11.0.0", - "laravel/pint": "^1.14.0", - "laravel/sail": "^1.28.2", - "laravel/sanctum": "^4.0.0", - "laravel/tinker": "^2.9.0", - "orchestra/testbench-core": "^9.0.0", - "pestphp/pest": "^2.34.1 || ^3.0.0", - "sebastian/environment": "^6.0.1 || ^7.0.0" + "fidry/makefile": "^0.2.0", + "fidry/php-cs-fixer-config": "^1.1.2", + "phpstan/extension-installer": "^1.2.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-deprecation-rules": "^2.0.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^8.5.31 || ^9.5.26", + "webmozarts/strict-phpunit": "^7.5" }, "type": "library", - "extra": { - "laravel": { - "providers": [ - "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" - ] - }, - "branch-alias": { - "dev-8.x": "8.x-dev" - } - }, "autoload": { - "files": [ - "./src/Adapters/Phpunit/Autoload.php" - ], "psr-4": { - "NunoMaduro\\Collision\\": "src/" + "Fidry\\CpuCoreCounter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -516,90 +521,63 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "Théo FIDRY", + "email": "theo.fidry@gmail.com" } ], - "description": "Cli error handling for console/command-line PHP applications.", + "description": "Tiny utility to get the number of CPU cores.", "keywords": [ - "artisan", - "cli", - "command-line", - "console", - "error", - "handling", - "laravel", - "laravel-zero", - "php", - "symfony" + "CPU", + "core" ], "support": { - "issues": "https://github.com/nunomaduro/collision/issues", - "source": "https://github.com/nunomaduro/collision" + "issues": "https://github.com/theofidry/cpu-core-counter/issues", + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", + "url": "https://github.com/theofidry", "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" } ], - "time": "2024-03-06T16:20:09+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { - "name": "nunomaduro/termwind", - "version": "v2.0.1", + "name": "filp/whoops", + "version": "2.18.4", "source": { "type": "git", - "url": "https://github.com/nunomaduro/termwind.git", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a" + "url": "https://github.com/filp/whoops.git", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/58c4c58cf23df7f498daeb97092e34f5259feb6a", - "reference": "58c4c58cf23df7f498daeb97092e34f5259feb6a", + "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", + "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": "^8.2", - "symfony/console": "^7.0.4" + "php": "^7.1 || ^8.0", + "psr/log": "^1.0.1 || ^2.0 || ^3.0" }, "require-dev": { - "ergebnis/phpstan-rules": "^2.2.0", - "illuminate/console": "^11.0.0", - "laravel/pint": "^1.14.0", - "mockery/mockery": "^1.6.7", - "pestphp/pest": "^2.34.1", - "phpstan/phpstan": "^1.10.59", - "phpstan/phpstan-strict-rules": "^1.5.2", - "symfony/var-dumper": "^7.0.4", - "thecodingmachine/phpstan-strict-rules": "^1.0.0" + "mockery/mockery": "^1.0", + "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", + "symfony/var-dumper": "^4.0 || ^5.0" + }, + "suggest": { + "symfony/var-dumper": "Pretty print complex values better with var-dumper available", + "whoops/soap": "Formats errors as SOAP responses" }, "type": "library", "extra": { - "laravel": { - "providers": [ - "Termwind\\Laravel\\TermwindServiceProvider" - ] - }, "branch-alias": { - "dev-2.x": "2.x-dev" + "dev-master": "2.7-dev" } }, "autoload": { - "files": [ - "src/Functions.php" - ], "psr-4": { - "Termwind\\": "src/" + "Whoops\\": "src/Whoops/" } }, "notification-url": "https://packagist.org/downloads/", @@ -608,110 +586,1840 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } + "name": "Filipe Dobreira", + "homepage": "https://github.com/filp", + "role": "Developer" + } + ], + "description": "php error handling for cool kids", + "homepage": "https://filp.github.io/whoops/", + "keywords": [ + "error", + "exception", + "handling", + "library", + "throwable", + "whoops" + ], + "support": { + "issues": "https://github.com/filp/whoops/issues", + "source": "https://github.com/filp/whoops/tree/2.18.4" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-08-08T12:00:00+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.89.1", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "f34967da2866ace090a2b447de1f357356474573" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f34967da2866ace090a2b447de1f357356474573", + "reference": "f34967da2866ace090a2b447de1f357356474573", + "shasum": "" + }, + "require": { + "clue/ndjson-react": "^1.3", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.5", + "ext-filter": "*", + "ext-hash": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.3", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.6", + "react/event-loop": "^1.5", + "react/socket": "^1.16", + "react/stream": "^1.4", + "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", + "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0", + "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0", + "symfony/polyfill-mbstring": "^1.33", + "symfony/polyfill-php80": "^1.33", + "symfony/polyfill-php81": "^1.33", + "symfony/polyfill-php84": "^1.33", + "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", + "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3.1 || ^2.7", + "infection/infection": "^0.31.0", + "justinrainbow/json-schema": "^6.5", + "keradus/cli-executor": "^2.2", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.8", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", + "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", + "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" + }, + "suggest": { + "ext-dom": "For handling output formats in XML", + "ext-mbstring": "For handling non-UTF8 characters." + }, + "bin": [ + "php-cs-fixer" + ], + "type": "application", + "autoload": { + "psr-4": { + "PhpCsFixer\\": "src/" + }, + "exclude-from-classmap": [ + "src/Fixer/Internal/*" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Dariusz Rumiński", + "email": "dariusz.ruminski@gmail.com" + } + ], + "description": "A tool to automatically fix PHP code style", + "keywords": [ + "Static code analysis", + "fixer", + "standards", + "static analysis" + ], + "support": { + "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.89.1" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2025-10-24T12:05:10+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.1", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "shasum": "" + }, + "require": { + "composer-runtime-api": "^2.1.0", + "php": "^7.4|^8.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^3.2", + "jean85/composer-provided-replaced-stub-package": "^1.0", + "phpstan/phpstan": "^2.0", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", + "vimeo/psalm": "^4.3 || ^5.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "Jean85\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Alessandro Lai", + "email": "alessandro.lai85@gmail.com" + } + ], + "description": "A library to get pretty versions strings of installed dependencies", + "keywords": [ + "composer", + "package", + "release", + "versions" + ], + "support": { + "issues": "https://github.com/Jean85/pretty-package-versions/issues", + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + }, + "time": "2025-03-19T14:43:43+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.4", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "conflict": { + "doctrine/collections": "<1.6.8", + "doctrine/common": "<2.13.3 || >=3 <3.2.2" + }, + "require-dev": { + "doctrine/collections": "^1.6.8", + "doctrine/common": "^2.13.3 || ^3.2.2", + "phpspec/prophecy": "^1.10", + "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" + }, + "type": "library", + "autoload": { + "files": [ + "src/DeepCopy/deep_copy.php" + ], + "psr-4": { + "DeepCopy\\": "src/DeepCopy/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Create deep copies (clones) of your objects", + "keywords": [ + "clone", + "copy", + "duplicate", + "object", + "object graph" + ], + "support": { + "issues": "https://github.com/myclabs/DeepCopy/issues", + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-08-01T08:46:24+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.6.2", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", + "reference": "3a454ca033b9e06b63282ce19562e892747449bb", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "php": ">=7.4" + }, + "require-dev": { + "ircmaxell/php-yacc": "^0.0.7", + "phpunit/phpunit": "^9.0" + }, + "bin": [ + "bin/php-parse" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpParser\\": "lib/PhpParser" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Nikita Popov" + } + ], + "description": "A PHP parser written in PHP", + "keywords": [ + "parser", + "php" + ], + "support": { + "issues": "https://github.com/nikic/PHP-Parser/issues", + "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" + }, + "time": "2025-10-21T19:32:17+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.8.2", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", + "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.18.1", + "nunomaduro/termwind": "^2.3.1", + "php": "^8.2.0", + "symfony/console": "^7.3.0" + }, + "conflict": { + "laravel/framework": "<11.44.2 || >=13.0.0", + "phpunit/phpunit": "<11.5.15 || >=13.0.0" + }, + "require-dev": { + "brianium/paratest": "^7.8.3", + "larastan/larastan": "^3.4.2", + "laravel/framework": "^11.44.2 || ^12.18", + "laravel/pint": "^1.22.1", + "laravel/sail": "^1.43.1", + "laravel/sanctum": "^4.1.1", + "laravel/tinker": "^2.10.1", + "orchestra/testbench-core": "^9.12.0 || ^10.4", + "pestphp/pest": "^3.8.2", + "sebastian/environment": "^7.2.1 || ^8.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" + ] + }, + "branch-alias": { + "dev-8.x": "8.x-dev" + } + }, + "autoload": { + "files": [ + "./src/Adapters/Phpunit/Autoload.php" + ], + "psr-4": { + "NunoMaduro\\Collision\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Cli error handling for console/command-line PHP applications.", + "keywords": [ + "artisan", + "cli", + "command-line", + "console", + "dev", + "error", + "handling", + "laravel", + "laravel-zero", + "php", + "symfony" + ], + "support": { + "issues": "https://github.com/nunomaduro/collision/issues", + "source": "https://github.com/nunomaduro/collision" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2025-06-25T02:12:12+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.2", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "eb61920a53057a7debd718a5b89c2178032b52c0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0", + "reference": "eb61920a53057a7debd718a5b89c2178032b52c0", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.3.4" + }, + "require-dev": { + "illuminate/console": "^11.46.1", + "laravel/pint": "^1.25.1", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0 || ^3.8.4", + "phpstan/phpstan": "^1.12.32", + "phpstan/phpstan-strict-rules": "^1.6.2", + "symfony/var-dumper": "^7.3.4", + "thecodingmachine/phpstan-strict-rules": "^1.0.0" + }, + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Termwind\\Laravel\\TermwindServiceProvider" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "files": [ + "src/Functions.php" + ], + "psr-4": { + "Termwind\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "Its like Tailwind CSS, but for the console.", + "keywords": [ + "cli", + "console", + "css", + "package", + "php", + "style" + ], + "support": { + "issues": "https://github.com/nunomaduro/termwind/issues", + "source": "https://github.com/nunomaduro/termwind/tree/v2.3.2" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://github.com/xiCO2k", + "type": "github" + } + ], + "time": "2025-10-18T11:10:27+00:00" + }, + { + "name": "pestphp/pest", + "version": "v3.8.4", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "72cf695554420e21858cda831d5db193db102574" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/72cf695554420e21858cda831d5db193db102574", + "reference": "72cf695554420e21858cda831d5db193db102574", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.8.4", + "nunomaduro/collision": "^8.8.2", + "nunomaduro/termwind": "^2.3.1", + "pestphp/pest-plugin": "^3.0.0", + "pestphp/pest-plugin-arch": "^3.1.1", + "pestphp/pest-plugin-mutate": "^3.0.5", + "php": "^8.2.0", + "phpunit/phpunit": "^11.5.33" + }, + "conflict": { + "filp/whoops": "<2.16.0", + "phpunit/phpunit": ">11.5.33", + "sebastian/exporter": "<6.0.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^3.4.0", + "pestphp/pest-plugin-type-coverage": "^3.6.1", + "symfony/process": "^7.3.0" + }, + "bin": [ + "bin/pest" + ], + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Mutate\\Plugins\\Mutate", + "Pest\\Plugins\\Configuration", + "Pest\\Plugins\\Bail", + "Pest\\Plugins\\Cache", + "Pest\\Plugins\\Coverage", + "Pest\\Plugins\\Init", + "Pest\\Plugins\\Environment", + "Pest\\Plugins\\Help", + "Pest\\Plugins\\Memory", + "Pest\\Plugins\\Only", + "Pest\\Plugins\\Printer", + "Pest\\Plugins\\ProcessIsolation", + "Pest\\Plugins\\Profile", + "Pest\\Plugins\\Retry", + "Pest\\Plugins\\Snapshot", + "Pest\\Plugins\\Verbose", + "Pest\\Plugins\\Version", + "Pest\\Plugins\\Parallel" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + } + }, + "autoload": { + "files": [ + "src/Functions.php", + "src/Pest.php" + ], + "psr-4": { + "Pest\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Nuno Maduro", + "email": "enunomaduro@gmail.com" + } + ], + "description": "The elegant PHP Testing Framework.", + "keywords": [ + "framework", + "pest", + "php", + "test", + "testing", + "unit" + ], + "support": { + "issues": "https://github.com/pestphp/pest/issues", + "source": "https://github.com/pestphp/pest/tree/v3.8.4" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-08-20T19:12:42+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v3.0.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e79b26c65bc11c41093b10150c1341cc5cdbea83", + "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.2" + }, + "conflict": { + "pestphp/pest": "<3.0.0" + }, + "require-dev": { + "composer/composer": "^2.7.9", + "pestphp/pest": "^3.0.0", + "pestphp/pest-dev-tools": "^3.0.0" + }, + "type": "composer-plugin", + "extra": { + "class": "Pest\\Plugin\\Manager" + }, + "autoload": { + "psr-4": { + "Pest\\Plugin\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Pest plugin manager", + "keywords": [ + "framework", + "manager", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin/tree/v3.0.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + }, + { + "url": "https://www.patreon.com/nunomaduro", + "type": "patreon" + } + ], + "time": "2024-09-08T23:21:41+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v3.1.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/db7bd9cb1612b223e16618d85475c6f63b9c8daa", + "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa", + "shasum": "" + }, + "require": { + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "ta-tikoma/phpunit-architecture-test": "^0.8.4" + }, + "require-dev": { + "pestphp/pest": "^3.8.1", + "pestphp/pest-dev-tools": "^3.4.0" + }, + "type": "library", + "extra": { + "pest": { + "plugins": [ + "Pest\\Arch\\Plugin" + ] + } + }, + "autoload": { + "files": [ + "src/Autoload.php" + ], + "psr-4": { + "Pest\\Arch\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "The Arch plugin for Pest PHP.", + "keywords": [ + "arch", + "architecture", + "framework", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-arch/tree/v3.1.1" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2025-04-16T22:59:48+00:00" + }, + { + "name": "pestphp/pest-plugin-mutate", + "version": "v3.0.5", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-mutate.git", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^5.2.0", + "pestphp/pest-plugin": "^3.0.0", + "php": "^8.2", + "psr/simple-cache": "^3.0.0" + }, + "require-dev": { + "pestphp/pest": "^3.0.8", + "pestphp/pest-dev-tools": "^3.0.0", + "pestphp/pest-plugin-type-coverage": "^3.0.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Pest\\Mutate\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Sandro Gehri", + "email": "sandrogehri@gmail.com" + } + ], + "description": "Mutates your code to find untested cases", + "keywords": [ + "framework", + "mutate", + "mutation", + "pest", + "php", + "plugin", + "test", + "testing", + "unit" + ], + "support": { + "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v3.0.5" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/gehrisandro", + "type": "github" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-09-22T07:54:40+00:00" + }, + { + "name": "phar-io/manifest", + "version": "2.0.4", + "source": { + "type": "git", + "url": "https://github.com/phar-io/manifest.git", + "reference": "54750ef60c58e43759730615a392c31c80e23176" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", + "reference": "54750ef60c58e43759730615a392c31c80e23176", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-phar": "*", + "ext-xmlwriter": "*", + "phar-io/version": "^3.0.1", + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "support": { + "issues": "https://github.com/phar-io/manifest/issues", + "source": "https://github.com/phar-io/manifest/tree/2.0.4" + }, + "funding": [ + { + "url": "https://github.com/theseer", + "type": "github" + } + ], + "time": "2024-03-03T12:33:53+00:00" + }, + { + "name": "phar-io/version", + "version": "3.2.1", + "source": { + "type": "git", + "url": "https://github.com/phar-io/version.git", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Arne Blankerts", + "email": "arne@blankerts.de", + "role": "Developer" + }, + { + "name": "Sebastian Heuer", + "email": "sebastian@phpeople.de", + "role": "Developer" + }, + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "Developer" + } + ], + "description": "Library for handling version information and constraints", + "support": { + "issues": "https://github.com/phar-io/version/issues", + "source": "https://github.com/phar-io/version/tree/3.2.1" + }, + "time": "2022-02-21T01:04:05+00:00" + }, + { + "name": "phpdocumentor/reflection-common", + "version": "2.2.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionCommon.git", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-2.x": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "Common reflection classes used by phpdocumentor to reflect the code structure", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "FQSEN", + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", + "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + }, + "time": "2020-06-27T09:03:43+00:00" + }, + { + "name": "phpdocumentor/reflection-docblock", + "version": "5.6.3", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", + "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.1", + "ext-filter": "*", + "php": "^7.4 || ^8.0", + "phpdocumentor/reflection-common": "^2.2", + "phpdocumentor/type-resolver": "^1.7", + "phpstan/phpdoc-parser": "^1.7|^2.0", + "webmozart/assert": "^1.9.1" + }, + "require-dev": { + "mockery/mockery": "~1.3.5 || ~1.6.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-mockery": "^1.1", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^9.5", + "psalm/phar": "^5.26" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "5.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + }, + { + "name": "Jaap van Otterdijk", + "email": "opensource@ijaap.nl" + } + ], + "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "support": { + "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" + }, + "time": "2025-08-01T19:43:32+00:00" + }, + { + "name": "phpdocumentor/type-resolver", + "version": "1.10.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/TypeResolver.git", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", + "shasum": "" + }, + "require": { + "doctrine/deprecations": "^1.0", + "php": "^7.3 || ^8.0", + "phpdocumentor/reflection-common": "^2.0", + "phpstan/phpdoc-parser": "^1.18|^2.0" + }, + "require-dev": { + "ext-tokenizer": "*", + "phpbench/phpbench": "^1.2", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpunit/phpunit": "^9.5", + "rector/rector": "^0.13.9", + "vimeo/psalm": "^4.25" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-1.x": "1.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\Reflection\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Mike van Riel", + "email": "me@mikevanriel.com" + } + ], + "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "support": { + "issues": "https://github.com/phpDocumentor/TypeResolver/issues", + "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" + }, + "time": "2024-11-09T15:12:26+00:00" + }, + { + "name": "phpstan/phpdoc-parser", + "version": "2.3.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", + "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0" + }, + "require-dev": { + "doctrine/annotations": "^2.0", + "nikic/php-parser": "^5.3.0", + "php-parallel-lint/php-parallel-lint": "^1.2", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^2.0", + "phpstan/phpstan-phpunit": "^2.0", + "phpstan/phpstan-strict-rules": "^2.0", + "phpunit/phpunit": "^9.6", + "symfony/process": "^5.2" + }, + "type": "library", + "autoload": { + "psr-4": { + "PHPStan\\PhpDocParser\\": [ + "src/" + ] + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPDoc parser with support for nullable, intersection and generic types", + "support": { + "issues": "https://github.com/phpstan/phpdoc-parser/issues", + "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" + }, + "time": "2025-08-30T15:50:23+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.31", + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96", + "reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "conflict": { + "phpstan/phpstan-shim": "*" + }, + "bin": [ + "phpstan", + "phpstan.phar" + ], + "type": "library", + "autoload": { + "files": [ + "bootstrap.php" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "PHPStan - PHP Static Analysis Tool", + "keywords": [ + "dev", + "static analysis" + ], + "support": { + "docs": "https://phpstan.org/user-guide/getting-started", + "forum": "https://github.com/phpstan/phpstan/discussions", + "issues": "https://github.com/phpstan/phpstan/issues", + "security": "https://github.com/phpstan/phpstan/security/policy", + "source": "https://github.com/phpstan/phpstan-src" + }, + "funding": [ + { + "url": "https://github.com/ondrejmirtes", + "type": "github" + }, + { + "url": "https://github.com/phpstan", + "type": "github" + } + ], + "time": "2025-10-10T14:14:11+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "11.0.11", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^5.4.0", + "php": ">=8.2", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-text-template": "^4.0.1", + "sebastian/code-unit-reverse-lookup": "^4.0.1", + "sebastian/complexity": "^4.0.1", + "sebastian/environment": "^7.2.0", + "sebastian/lines-of-code": "^3.0.1", + "sebastian/version": "^5.0.2", + "theseer/tokenizer": "^1.2.3" + }, + "require-dev": { + "phpunit/phpunit": "^11.5.2" + }, + "suggest": { + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.0.x-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", + "homepage": "https://github.com/sebastianbergmann/php-code-coverage", + "keywords": [ + "coverage", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", + "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", + "type": "tidelift" + } + ], + "time": "2025-08-27T14:37:49+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "5.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", + "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "FilterIterator implementation that filters files based on a list of suffixes.", + "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", + "keywords": [ + "filesystem", + "iterator" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", + "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", + "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-27T05:02:59+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "5.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", + "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^11.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:07:44+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Simple template engine.", + "homepage": "https://github.com/sebastianbergmann/php-text-template/", + "keywords": [ + "template" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-text-template/issues", + "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", + "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } ], - "description": "Its like Tailwind CSS, but for the console.", + "time": "2024-07-03T05:08:43+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "7.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", + "shasum": "" + }, + "require": { + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "7.0-dev" + } + }, + "autoload": { + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Utility class for timing", + "homepage": "https://github.com/sebastianbergmann/php-timer/", "keywords": [ - "cli", - "console", - "css", - "package", - "php", - "style" + "timer" ], "support": { - "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.0.1" + "issues": "https://github.com/sebastianbergmann/php-timer/issues", + "security": "https://github.com/sebastianbergmann/php-timer/security/policy", + "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" }, "funding": [ { - "url": "https://www.paypal.com/paypalme/enunomaduro", + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T05:09:35+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "11.5.33", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "5965e9ff57546cb9137c0ff6aa78cb7442b05cf6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5965e9ff57546cb9137c0ff6aa78cb7442b05cf6", + "reference": "5965e9ff57546cb9137c0ff6aa78cb7442b05cf6", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.13.4", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.2", + "phpunit/php-code-coverage": "^11.0.10", + "phpunit/php-file-iterator": "^5.1.0", + "phpunit/php-invoker": "^5.0.1", + "phpunit/php-text-template": "^4.0.1", + "phpunit/php-timer": "^7.0.1", + "sebastian/cli-parser": "^3.0.2", + "sebastian/code-unit": "^3.0.3", + "sebastian/comparator": "^6.3.2", + "sebastian/diff": "^6.0.2", + "sebastian/environment": "^7.2.1", + "sebastian/exporter": "^6.3.0", + "sebastian/global-state": "^7.0.2", + "sebastian/object-enumerator": "^6.0.1", + "sebastian/type": "^5.1.3", + "sebastian/version": "^5.0.2", + "staabm/side-effects-detector": "^1.0.5" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "11.5-dev" + } + }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", + "keywords": [ + "phpunit", + "testing", + "xunit" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.33" + }, + "funding": [ + { + "url": "https://phpunit.de/sponsors.html", "type": "custom" }, { - "url": "https://github.com/nunomaduro", + "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://github.com/xiCO2k", - "type": "github" + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" + } + ], + "time": "2025-08-16T05:19:02+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/event-dispatcher", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "shasum": "" + }, + "require": { + "php": ">=7.2.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\EventDispatcher\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" + } + ], + "description": "Standard interfaces for event handling.", + "keywords": [ + "events", + "psr", + "psr-14" + ], + "support": { + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + }, + "time": "2019-01-08T18:20:26+00:00" + }, + { + "name": "psr/log", + "version": "3.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "time": "2024-03-06T16:17:14+00:00" + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.2" + }, + "time": "2024-09-11T13:17:53+00:00" }, { - "name": "pestphp/pest", - "version": "v2.34.4", + "name": "psr/simple-cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/pestphp/pest.git", - "reference": "6a1161ead830294ef8e21fab83c0bd118b0df7cc" + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/6a1161ead830294ef8e21fab83c0bd118b0df7cc", - "reference": "6a1161ead830294ef8e21fab83c0bd118b0df7cc", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", "shasum": "" }, "require": { - "brianium/paratest": "^7.3.1", - "nunomaduro/collision": "^7.10.0|^8.1.1", - "nunomaduro/termwind": "^1.15.1|^2.0.1", - "pestphp/pest-plugin": "^2.1.1", - "pestphp/pest-plugin-arch": "^2.7.0", - "php": "^8.1.0", - "phpunit/phpunit": "^10.5.13" - }, - "conflict": { - "phpunit/phpunit": ">10.5.13", - "sebastian/exporter": "<5.1.0", - "webmozart/assert": "<1.11.0" - }, - "require-dev": { - "pestphp/pest-dev-tools": "^2.16.0", - "pestphp/pest-plugin-type-coverage": "^2.8.1", - "symfony/process": "^6.4.0|^7.0.4" + "php": ">=8.0.0" }, - "bin": [ - "bin/pest" - ], "type": "library", "extra": { - "pest": { - "plugins": [ - "Pest\\Plugins\\Bail", - "Pest\\Plugins\\Cache", - "Pest\\Plugins\\Coverage", - "Pest\\Plugins\\Init", - "Pest\\Plugins\\Environment", - "Pest\\Plugins\\Help", - "Pest\\Plugins\\Memory", - "Pest\\Plugins\\Only", - "Pest\\Plugins\\Printer", - "Pest\\Plugins\\ProcessIsolation", - "Pest\\Plugins\\Profile", - "Pest\\Plugins\\Retry", - "Pest\\Plugins\\Snapshot", - "Pest\\Plugins\\Verbose", - "Pest\\Plugins\\Version", - "Pest\\Plugins\\Parallel" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] + "branch-alias": { + "dev-master": "3.0.x-dev" } }, "autoload": { - "files": [ - "src/Functions.php", - "src/Pest.php" - ], "psr-4": { - "Pest\\": "src/" + "Psr\\SimpleCache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -720,320 +2428,346 @@ ], "authors": [ { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "The elegant PHP Testing Framework.", + "description": "Common interfaces for simple caching", "keywords": [ - "framework", - "pest", - "php", - "test", - "testing", - "unit" + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" ], "support": { - "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v2.34.4" + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2024-03-14T19:44:18+00:00" + "time": "2021-10-29T13:26:27+00:00" }, { - "name": "pestphp/pest-plugin", - "version": "v2.1.1", + "name": "react/cache", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/pestphp/pest-plugin.git", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b" + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b", - "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", "shasum": "" }, "require": { - "composer-plugin-api": "^2.0.0", - "composer-runtime-api": "^2.2.2", - "php": "^8.1" - }, - "conflict": { - "pestphp/pest": "<2.2.3" + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" }, "require-dev": { - "composer/composer": "^2.5.8", - "pestphp/pest": "^2.16.0", - "pestphp/pest-dev-tools": "^2.16.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Pest\\Plugin\\Manager" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, + "type": "library", "autoload": { "psr-4": { - "Pest\\Plugin\\": "src/" + "React\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "The Pest plugin manager", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Async, Promise-based cache interface for ReactPHP", "keywords": [ - "framework", - "manager", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" + "cache", + "caching", + "promise", + "reactphp" ], "support": { - "source": "https://github.com/pestphp/pest-plugin/tree/v2.1.1" + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" }, "funding": [ { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2023-08-22T08:40:06+00:00" + "time": "2022-11-30T15:59:55+00:00" }, { - "name": "pestphp/pest-plugin-arch", - "version": "v2.7.0", + "name": "react/child-process", + "version": "v0.6.6", "source": { "type": "git", - "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda" + "url": "https://github.com/reactphp/child-process.git", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/d23b2d7498475354522c3818c42ef355dca3fcda", - "reference": "d23b2d7498475354522c3818c42ef355dca3fcda", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", "shasum": "" }, "require": { - "nunomaduro/collision": "^7.10.0|^8.1.0", - "pestphp/pest-plugin": "^2.1.1", - "php": "^8.1", - "ta-tikoma/phpunit-architecture-test": "^0.8.4" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" }, "require-dev": { - "pestphp/pest": "^2.33.0", - "pestphp/pest-dev-tools": "^2.16.0" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" }, "type": "library", - "extra": { - "pest": { - "plugins": [ - "Pest\\Arch\\Plugin" - ] - } - }, "autoload": { - "files": [ - "src/Autoload.php" - ], "psr-4": { - "Pest\\Arch\\": "src/" + "React\\ChildProcess\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], - "description": "The Arch plugin for Pest PHP.", + "authors": [ + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" + } + ], + "description": "Event-driven library for executing child processes with ReactPHP.", "keywords": [ - "arch", - "architecture", - "framework", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" + "event-driven", + "process", + "reactphp" ], "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v2.7.0" + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.6" }, "funding": [ { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2024-01-26T09:46:42+00:00" + "time": "2025-01-01T16:37:48+00:00" }, { - "name": "phar-io/manifest", - "version": "2.0.4", + "name": "react/dns", + "version": "v1.13.0", "source": { "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" + "php": ">=5.3.0", + "react/cache": "^1.0 || ^0.6 || ^0.5", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.7 || ^1.2.1" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3 || ^2", + "react/promise-timer": "^1.11" }, + "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Dns\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", + "description": "Async DNS resolver for ReactPHP", + "keywords": [ + "async", + "dns", + "dns-resolver", + "reactphp" + ], "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" }, "funding": [ { - "url": "https://github.com/theseer", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2024-03-03T12:33:53+00:00" + "time": "2024-06-13T14:18:03+00:00" }, { - "name": "phar-io/version", - "version": "3.2.1", + "name": "react/event-loop", + "version": "v1.5.0", "source": { "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=5.3.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + }, + "suggest": { + "ext-pcntl": "For signal handling support when using the StreamSelectLoop" }, "type": "library", "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\EventLoop\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Library for handling version information and constraints", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" }, - "time": "2022-02-21T01:04:05+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-13T13:48:05+00:00" }, { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", + "name": "react/promise", + "version": "v3.3.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" + "url": "https://github.com/reactphp/promise.git", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", + "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", + "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=7.1.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } + "require-dev": { + "phpstan/phpstan": "1.12.28 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" }, + "type": "library", "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "phpDocumentor\\Reflection\\": "src/" + "React\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1042,59 +2776,75 @@ ], "authors": [ { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" + "promise", + "promises" ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.3.0" }, - "time": "2020-06-27T09:03:43+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-08-19T18:57:03+00:00" }, { - "name": "phpdocumentor/reflection-docblock", - "version": "5.3.0", + "name": "react/socket", + "version": "v1.16.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170" + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/622548b623e81ca6d78b721c5e029f4ce664f170", - "reference": "622548b623e81ca6d78b721c5e029f4ce664f170", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", "shasum": "" }, "require": { - "ext-filter": "*", - "php": "^7.2 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.3", - "webmozart/assert": "^1.9.1" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/dns": "^1.13", + "react/event-loop": "^1.2", + "react/promise": "^3.2 || ^2.6 || ^1.2.1", + "react/stream": "^1.4" }, "require-dev": { - "mockery/mockery": "~1.3.2", - "psalm/phar": "^4.8" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "React\\Socket\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1103,60 +2853,73 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Jaap van Otterdijk", - "email": "account@ijaap.nl" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "keywords": [ + "Connection", + "Socket", + "async", + "reactphp", + "stream" + ], "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.3.0" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" }, - "time": "2021-10-19T17:43:47+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-07-26T10:38:09+00:00" }, { - "name": "phpdocumentor/type-resolver", - "version": "1.8.2", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "153ae662783729388a584b4361f2545e4d841e3c" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/153ae662783729388a584b4361f2545e4d841e3c", - "reference": "153ae662783729388a584b4361f2545e4d841e3c", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.13" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, "autoload": { "psr-4": { - "phpDocumentor\\Reflection\\": "src" + "React\\Stream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1165,104 +2928,130 @@ ], "authors": [ { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" + }, + { + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" + }, + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, + { + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "keywords": [ + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" + ], "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.8.2" + "issues": "https://github.com/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, - "time": "2024-02-23T11:10:43+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-11T12:45:25+00:00" }, { - "name": "phpstan/phpdoc-parser", - "version": "1.26.0", + "name": "sebastian/cli-parser", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "231e3186624c03d7e7c890ec662b81e6b0405227" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/231e3186624c03d7e7c890ec662b81e6b0405227", - "reference": "231e3186624c03d7e7c890ec662b81e6b0405227", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", + "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": ">=8.2" }, "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^4.15", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^1.5", - "phpstan/phpstan-phpunit": "^1.1", - "phpstan/phpstan-strict-rules": "^1.0", - "phpunit/phpunit": "^9.5", - "symfony/process": "^5.2" + "phpunit/phpunit": "^11.0" }, "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", + "authors": [ + { + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" + } + ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.26.0" + "issues": "https://github.com/sebastianbergmann/cli-parser/issues", + "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", + "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" }, - "time": "2024-02-23T16:05:55+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:41:36+00:00" }, { - "name": "phpunit/php-code-coverage", - "version": "10.1.14", + "name": "sebastian/code-unit", + "version": "3.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", - "reference": "e3f51450ebffe8e0efdf7346ae966a656f7d5e5b", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", + "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-text-template": "^3.0", - "sebastian/code-unit-reverse-lookup": "^3.0", - "sebastian/complexity": "^3.0", - "sebastian/environment": "^6.0", - "sebastian/lines-of-code": "^2.0", - "sebastian/version": "^4.0", - "theseer/tokenizer": "^1.2.0" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.1" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" + "phpunit/phpunit": "^11.5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "10.1-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1281,17 +3070,12 @@ "role": "lead" } ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/10.1.14" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "security": "https://github.com/sebastianbergmann/code-unit/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" }, "funding": [ { @@ -1299,27 +3083,27 @@ "type": "github" } ], - "time": "2024-03-12T15:33:41+00:00" + "time": "2025-03-19T07:56:08+00:00" }, { - "name": "phpunit/php-file-iterator", - "version": "4.1.0", + "name": "sebastian/code-unit-reverse-lookup", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", - "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", + "reference": "183a9b2632194febd219bb9246eee421dad8d45e", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { @@ -1339,20 +3123,15 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], + "description": "Looks up which function or method a line of code belongs to", + "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/4.1.0" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" }, "funding": [ { @@ -1360,36 +3139,39 @@ "type": "github" } ], - "time": "2023-08-31T06:24:48+00:00" + "time": "2024-07-03T04:45:54+00:00" }, { - "name": "phpunit/php-invoker", - "version": "4.0.0", + "name": "sebastian/comparator", + "version": "6.3.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", - "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", + "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", "shasum": "" }, "require": { - "php": ">=8.1" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/diff": "^6.0", + "sebastian/exporter": "^6.0" }, "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.4" }, "suggest": { - "ext-pcntl": "*" + "ext-bcmath": "For comparing BcMath\\Number objects" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.3-dev" } }, "autoload": { @@ -1404,51 +3186,78 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ - "process" + "comparator", + "compare", + "equality" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", + "type": "tidelift" } ], - "time": "2023-02-03T06:56:09+00:00" + "time": "2025-08-10T08:07:46+00:00" }, { - "name": "phpunit/php-text-template", - "version": "3.0.1", + "name": "sebastian/complexity", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", - "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", + "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", "shasum": "" }, "require": { - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1467,15 +3276,12 @@ "role": "lead" } ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/3.0.1" + "issues": "https://github.com/sebastianbergmann/complexity/issues", + "security": "https://github.com/sebastianbergmann/complexity/security/policy", + "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" }, "funding": [ { @@ -1483,27 +3289,28 @@ "type": "github" } ], - "time": "2023-08-31T14:07:24+00:00" + "time": "2024-07-03T04:49:50+00:00" }, { - "name": "phpunit/php-timer", - "version": "6.0.0", + "name": "sebastian/diff", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", - "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", + "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0", + "symfony/process": "^4.2 || ^5" }, "type": "library", "extra": { @@ -1523,18 +3330,25 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "timer" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + "issues": "https://github.com/sebastianbergmann/diff/issues", + "security": "https://github.com/sebastianbergmann/diff/security/policy", + "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" }, "funding": [ { @@ -1542,66 +3356,38 @@ "type": "github" } ], - "time": "2023-02-03T06:57:52+00:00" + "time": "2024-07-03T04:53:05+00:00" }, { - "name": "phpunit/phpunit", - "version": "10.5.13", + "name": "sebastian/environment", + "version": "7.2.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/20a63fc1c6db29b15da3bd02d4b6cf59900088a7", - "reference": "20a63fc1c6db29b15da3bd02d4b6cf59900088a7", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", + "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.10.1", - "phar-io/manifest": "^2.0.3", - "phar-io/version": "^3.0.2", - "php": ">=8.1", - "phpunit/php-code-coverage": "^10.1.5", - "phpunit/php-file-iterator": "^4.0", - "phpunit/php-invoker": "^4.0", - "phpunit/php-text-template": "^3.0", - "phpunit/php-timer": "^6.0", - "sebastian/cli-parser": "^2.0", - "sebastian/code-unit": "^2.0", - "sebastian/comparator": "^5.0", - "sebastian/diff": "^5.0", - "sebastian/environment": "^6.0", - "sebastian/exporter": "^5.1", - "sebastian/global-state": "^6.0.1", - "sebastian/object-enumerator": "^5.0", - "sebastian/recursion-context": "^5.0", - "sebastian/type": "^4.0", - "sebastian/version": "^4.0" + "php": ">=8.2" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" }, "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "ext-posix": "*" }, - "bin": [ - "phpunit" - ], "type": "library", "extra": { "branch-alias": { - "dev-main": "10.5-dev" + "dev-main": "7.2-dev" } }, "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], "classmap": [ "src/" ] @@ -1613,165 +3399,218 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", "keywords": [ - "phpunit", - "testing", - "xunit" + "Xdebug", + "environment", + "hhvm" ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.13" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" }, "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, { "url": "https://github.com/sebastianbergmann", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", "type": "tidelift" } ], - "time": "2024-03-12T15:37:41+00:00" + "time": "2025-05-21T11:55:47+00:00" }, { - "name": "psr/container", - "version": "2.0.2", + "name": "sebastian/exporter", + "version": "6.3.2", "source": { "type": "git", - "url": "https://github.com/php-fig/container.git", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", - "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", + "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", "shasum": "" }, "require": { - "php": ">=7.4.0" + "ext-mbstring": "*", + "php": ">=8.2", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "2.0.x-dev" + "dev-main": "6.3-dev" } }, "autoload": { - "psr-4": { - "Psr\\Container\\": "src/" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" + }, + { + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Volker Dusch", + "email": "github@wallbash.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Common Container Interface (PHP FIG PSR-11)", - "homepage": "https://github.com/php-fig/container", + "description": "Provides the functionality to export PHP variables for visualization", + "homepage": "https://www.github.com/sebastianbergmann/exporter", "keywords": [ - "PSR-11", - "container", - "container-interface", - "container-interop", - "psr" + "export", + "exporter" ], "support": { - "issues": "https://github.com/php-fig/container/issues", - "source": "https://github.com/php-fig/container/tree/2.0.2" + "issues": "https://github.com/sebastianbergmann/exporter/issues", + "security": "https://github.com/sebastianbergmann/exporter/security/policy", + "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" }, - "time": "2021-11-05T16:47:00+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", + "type": "tidelift" + } + ], + "time": "2025-09-24T06:12:51+00:00" }, { - "name": "psr/log", - "version": "3.0.0", + "name": "sebastian/global-state", + "version": "7.0.2", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", + "reference": "3be331570a721f9a4b5917f4209773de17f747d7", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-master": "3.x-dev" + "dev-main": "7.0-dev" } }, "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", "keywords": [ - "log", - "psr", - "psr-3" + "global state" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" + "issues": "https://github.com/sebastianbergmann/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" }, - "time": "2021-07-14T16:46:02+00:00" + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-07-03T04:57:36+00:00" }, { - "name": "sebastian/cli-parser", - "version": "2.0.1", + "name": "sebastian/lines-of-code", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", + "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", "shasum": "" }, "require": { - "php": ">=8.1" + "nikic/php-parser": "^5.0", + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -1790,12 +3629,12 @@ "role": "lead" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Library for counting the lines of code in PHP source code", + "homepage": "https://github.com/sebastianbergmann/lines-of-code", "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/2.0.1" + "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", + "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", + "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" }, "funding": [ { @@ -1803,32 +3642,34 @@ "type": "github" } ], - "time": "2024-03-02T07:12:49+00:00" + "time": "2024-07-03T04:58:38+00:00" }, { - "name": "sebastian/code-unit", - "version": "2.0.0", + "name": "sebastian/object-enumerator", + "version": "6.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", + "reference": "f5b498e631a74204185071eb41f33f38d64608aa", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "sebastian/object-reflector": "^4.0", + "sebastian/recursion-context": "^6.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1843,15 +3684,15 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" }, "funding": [ { @@ -1859,32 +3700,32 @@ "type": "github" } ], - "time": "2023-02-03T06:58:43+00:00" + "time": "2024-07-03T05:00:13+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", + "name": "sebastian/object-reflector", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", + "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -1902,11 +3743,12 @@ "email": "sebastian@phpunit.de" } ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" }, "funding": [ { @@ -1914,36 +3756,32 @@ "type": "github" } ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2024-07-03T05:01:32+00:00" }, { - "name": "sebastian/comparator", - "version": "5.0.1", + "name": "sebastian/recursion-context", + "version": "6.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/2db5010a484d53ebf536087a70b4a5423c102372", - "reference": "2db5010a484d53ebf536087a70b4a5423c102372", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", + "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.3" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -1965,59 +3803,61 @@ "email": "whatthejeff@gmail.com" }, { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.1" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", + "type": "tidelift" } ], - "time": "2023-08-14T13:18:12+00:00" + "time": "2025-08-13T04:42:22+00:00" }, { - "name": "sebastian/complexity", - "version": "3.2.0", + "name": "sebastian/type", + "version": "5.1.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", + "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^11.3" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -2036,46 +3876,54 @@ "role": "lead" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/3.2.0" + "issues": "https://github.com/sebastianbergmann/type/issues", + "security": "https://github.com/sebastianbergmann/type/security/policy", + "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" }, "funding": [ { "url": "https://github.com/sebastianbergmann", "type": "github" + }, + { + "url": "https://liberapay.com/sebastianbergmann", + "type": "liberapay" + }, + { + "url": "https://thanks.dev/u/gh/sebastianbergmann", + "type": "thanks_dev" + }, + { + "url": "https://tidelift.com/funding/github/packagist/sebastian/type", + "type": "tidelift" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2025-08-09T06:55:48+00:00" }, { - "name": "sebastian/diff", - "version": "5.1.1", + "name": "sebastian/version", + "version": "5.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", + "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "php": ">=8.2" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -2090,25 +3938,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1" + "issues": "https://github.com/sebastianbergmann/version/issues", + "security": "https://github.com/sebastianbergmann/version/security/policy", + "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" }, "funding": [ { @@ -2116,605 +3955,711 @@ "type": "github" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2024-10-09T05:16:32+00:00" }, { - "name": "sebastian/environment", - "version": "6.0.1", + "name": "staabm/side-effects-detector", + "version": "1.0.5", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951" + "url": "https://github.com/staabm/side-effects-detector.git", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/43c751b41d74f96cbbd4e07b7aec9675651e2951", - "reference": "43c751b41d74f96cbbd4e07b7aec9675651e2951", + "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", + "reference": "d8334211a140ce329c13726d4a715adbddd0a163", "shasum": "" }, "require": { - "php": ">=8.1" + "ext-tokenizer": "*", + "php": "^7.4 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "suggest": { - "ext-posix": "*" + "phpstan/extension-installer": "^1.4.3", + "phpstan/phpstan": "^1.12.6", + "phpunit/phpunit": "^9.6.21", + "symfony/var-dumper": "^5.4.43", + "tomasvotruba/type-coverage": "1.0.0", + "tomasvotruba/unused-public": "1.0.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, "autoload": { "classmap": [ - "src/" + "lib/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } + "MIT" ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", + "description": "A static analysis tool to detect side effects in PHP code", "keywords": [ - "Xdebug", - "environment", - "hhvm" + "static analysis" ], "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.0.1" + "issues": "https://github.com/staabm/side-effects-detector/issues", + "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/staabm", "type": "github" } ], - "time": "2023-04-11T05:39:26+00:00" + "time": "2024-10-20T05:08:20+00:00" }, { - "name": "sebastian/exporter", - "version": "5.1.2", + "name": "symfony/console", + "version": "v7.3.4", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "url": "https://github.com/symfony/console.git", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", + "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^7.2" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/dotenv": "<6.4", + "symfony/event-dispatcher": "<6.4", + "symfony/lock": "<6.4", + "symfony/process": "<6.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/event-dispatcher": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^6.4|^7.0", + "symfony/messenger": "^6.4|^7.0", + "symfony/process": "^6.4|^7.0", + "symfony/stopwatch": "^6.4|^7.0", + "symfony/var-dumper": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Console\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", + "description": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", "keywords": [ - "export", - "exporter" + "cli", + "command-line", + "console", + "terminal" ], "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "source": "https://github.com/symfony/console/tree/v7.3.4" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2025-09-22T15:31:00+00:00" }, { - "name": "sebastian/global-state", - "version": "6.0.2", + "name": "symfony/deprecation-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^10.0" + "php": ">=8.1" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "3.6-dev" } }, "autoload": { - "classmap": [ - "src/" + "files": [ + "function.php" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], + "description": "A generic function and convention to trigger deprecation notices", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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-03-02T07:19:19+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "2.0.2", + "name": "symfony/event-dispatcher", + "version": "v7.3.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", + "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "php": ">=8.2", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<6.4", + "symfony/service-contracts": "<2.5" + }, + "provide": { + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^6.4|^7.0", + "symfony/dependency-injection": "^6.4|^7.0", + "symfony/error-handler": "^6.4|^7.0", + "symfony/expression-language": "^6.4|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/2.0.2" + "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2025-08-13T11:49:31+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "5.0.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" + "psr/event-dispatcher": "^1" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "3.6-dev" } }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Symfony\\Contracts\\EventDispatcher\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "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": "2023-02-03T07:08:32+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { - "name": "sebastian/object-reflector", - "version": "3.0.0", + "name": "symfony/filesystem", + "version": "v7.3.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "url": "https://github.com/symfony/filesystem.git", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", + "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "symfony/process": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Filesystem\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Provides basic utilities for the filesystem", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "source": "https://github.com/symfony/filesystem/tree/v7.3.2" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2025-07-07T08:17:47+00:00" }, { - "name": "sebastian/recursion-context", - "version": "5.0.0", + "name": "symfony/finder", + "version": "v7.3.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "url": "https://github.com/symfony/finder.git", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", + "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.2" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "symfony/filesystem": "^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\Finder\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { - "name": "Adam Harvey", - "email": "aharvey@php.net" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", + "description": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "source": "https://github.com/symfony/finder/tree/v7.3.2" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2025-07-15T13:41:35+00:00" }, { - "name": "sebastian/type", - "version": "4.0.0", + "name": "symfony/options-resolver", + "version": "v7.3.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", + "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" + "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" + "psr-4": { + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", + "keywords": [ + "config", + "configuration", + "options" + ], "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2025-08-05T10:16:07+00:00" }, { - "name": "sebastian/version", - "version": "4.0.1", + "name": "symfony/polyfill-ctype", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=7.2" + }, + "provide": { + "ext-ctype": "*" + }, + "suggest": { + "ext-ctype": "For best performance" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "4.0-dev" + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "bootstrap.php" + ], + "psr-4": { + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Symfony polyfill for ctype functions", + "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://github.com/nicolas-grekas", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/console", - "version": "v7.0.4", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/6b099f3306f7c9c2d2786ed736d0026b2903205f", - "reference": "6b099f3306f7c9c2d2786ed736d0026b2903205f", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", + "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" + "php": ">=7.2" }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2722,24 +4667,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ - "cli", - "command-line", - "console", - "terminal" + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.0.4" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" }, "funding": [ { @@ -2750,40 +4697,53 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2025-06-27T09:58:17+00:00" }, { - "name": "symfony/finder", - "version": "v7.0.0", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", - "reference": "6e5688d69f7cfc4ed4a511e96007e06c2d34ce56", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=7.2" }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Finder\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -2792,18 +4752,26 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Finds files and directories via an intuitive fluent interface", + "description": "Symfony polyfill for intl's Normalizer class and related functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "intl", + "normalizer", + "polyfill", + "portable", + "shim" + ], "support": { - "source": "https://github.com/symfony/finder/tree/v7.0.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" }, "funding": [ { @@ -2814,41 +4782,46 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2023-10-31T17:59:56+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.29.0", + "name": "symfony/polyfill-mbstring", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/ef4d7e442ca910c4764bce785146269b30cb5fc4", - "reference": "ef4d7e442ca910c4764bce785146269b30cb5fc4", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=7.1" + "ext-iconv": "*", + "php": ">=7.2" }, "provide": { - "ext-ctype": "*" + "ext-mbstring": "*" }, "suggest": { - "ext-ctype": "For best performance" + "ext-mbstring": "For best performance" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2856,7 +4829,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -2865,24 +4838,25 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "ctype", + "mbstring", "polyfill", - "portable" + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" }, "funding": [ { @@ -2893,38 +4867,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.29.0", + "name": "symfony/polyfill-php80", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/32a9da87d7b3245e09ac426c83d334ae9f06f80f", - "reference": "32a9da87d7b3245e09ac426c83d334ae9f06f80f", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -2932,14 +4907,21 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ + { + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -2949,18 +4931,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "grapheme", - "intl", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" }, "funding": [ { @@ -2971,38 +4951,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.29.0", + "name": "symfony/polyfill-php81", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/bc45c394692b948b4d383a08d7753968bed9a83d", - "reference": "bc45c394692b948b4d383a08d7753968bed9a83d", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3010,7 +4991,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, "classmap": [ "Resources/stubs" @@ -3030,18 +5011,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "intl", - "normalizer", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" }, "funding": [ { @@ -3052,41 +5031,39 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.29.0", + "name": "symfony/polyfill-php84", + "version": "v1.33.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec" + "url": "https://github.com/symfony/polyfill-php84.git", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/9773676c8a1bb1f8d4340a62efe641cf76eda7ec", - "reference": "9773676c8a1bb1f8d4340a62efe641cf76eda7ec", + "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", + "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", "shasum": "" }, "require": { - "php": ">=7.1" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "name": "symfony/polyfill", - "url": "https://github.com/symfony/polyfill" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { @@ -3094,8 +5071,11 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Polyfill\\Php84\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -3111,17 +5091,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", "polyfill", "portable", "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.29.0" + "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" }, "funding": [ { @@ -3132,25 +5111,29 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-01-29T20:11:03+00:00" + "time": "2025-06-24T13:30:11+00:00" }, { "name": "symfony/process", - "version": "v7.0.4", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9" + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/0e7727191c3b71ebec6d529fa0e50a01ca5679e9", - "reference": "0e7727191c3b71ebec6d529fa0e50a01ca5679e9", + "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", + "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", "shasum": "" }, "require": { @@ -3182,7 +5165,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.0.4" + "source": "https://github.com/symfony/process/tree/v7.3.4" }, "funding": [ { @@ -3193,42 +5176,47 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-02-22T20:27:20+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.4.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/fe07cbc8d837f60caf7018068e350cc5163681a0", - "reference": "fe07cbc8d837f60caf7018068e350cc5163681a0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { "php": ">=8.1", - "psr/container": "^1.1|^2.0" + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "3.4-dev" - }, "thanks": { - "name": "symfony/contracts", - "url": "https://github.com/symfony/contracts" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { @@ -3264,7 +5252,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.4.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -3280,20 +5268,82 @@ "type": "tidelift" } ], - "time": "2023-12-26T14:02:43+00:00" + "time": "2025-04-25T09:37:31+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.3.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/stopwatch.git", + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "shasum": "" + }, + "require": { + "php": ">=8.2", + "symfony/service-contracts": "^2.5|^3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Stopwatch\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" + } + ], + "description": "Provides a way to profile code", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/stopwatch/tree/v7.3.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": "2025-02-24T10:49:57+00:00" }, { "name": "symfony/string", - "version": "v7.0.4", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b" + "reference": "f96476035142921000338bad71e5247fbc138872" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f5832521b998b0bec40bee688ad5de98d4cf111b", - "reference": "f5832521b998b0bec40bee688ad5de98d4cf111b", + "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", + "reference": "f96476035142921000338bad71e5247fbc138872", "shasum": "" }, "require": { @@ -3307,7 +5357,7 @@ "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/error-handler": "^6.4|^7.0", + "symfony/emoji": "^7.1", "symfony/http-client": "^6.4|^7.0", "symfony/intl": "^6.4|^7.0", "symfony/translation-contracts": "^2.5|^3.0", @@ -3350,7 +5400,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.0.4" + "source": "https://github.com/symfony/string/tree/v7.3.4" }, "funding": [ { @@ -3361,41 +5411,45 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-02-01T13:17:36+00:00" + "time": "2025-09-11T14:36:48+00:00" }, { "name": "symfony/var-dumper", - "version": "v7.0.4", + "version": "v7.3.4", "source": { "type": "git", "url": "https://github.com/symfony/var-dumper.git", - "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670" + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/e03ad7c1535e623edbb94c22cc42353e488c6670", - "reference": "e03ad7c1535e623edbb94c22cc42353e488c6670", + "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", + "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", "shasum": "" }, "require": { "php": ">=8.2", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0" }, "conflict": { "symfony/console": "<6.4" }, "require-dev": { - "ext-iconv": "*", "symfony/console": "^6.4|^7.0", "symfony/http-kernel": "^6.4|^7.0", "symfony/process": "^6.4|^7.0", "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.0.4" + "twig/twig": "^3.12" }, "bin": [ "Resources/bin/var-dump-server" @@ -3433,7 +5487,7 @@ "dump" ], "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.0.4" + "source": "https://github.com/symfony/var-dumper/tree/v7.3.4" }, "funding": [ { @@ -3444,32 +5498,36 @@ "url": "https://github.com/fabpot", "type": "github" }, + { + "url": "https://github.com/nicolas-grekas", + "type": "github" + }, { "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-02-15T11:33:06+00:00" + "time": "2025-09-11T10:12:26+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", - "version": "0.8.4", + "version": "0.8.5", "source": { "type": "git", "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", - "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636" + "reference": "cf6fb197b676ba716837c886baca842e4db29005" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/89f0dea1cb0f0d5744d3ec1764a286af5e006636", - "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/cf6fb197b676ba716837c886baca842e4db29005", + "reference": "cf6fb197b676ba716837c886baca842e4db29005", "shasum": "" }, "require": { "nikic/php-parser": "^4.18.0 || ^5.0.0", "php": "^8.1.0", "phpdocumentor/reflection-docblock": "^5.3.0", - "phpunit/phpunit": "^10.5.5 || ^11.0.0", + "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0", "symfony/finder": "^6.4.0 || ^7.0.0" }, "require-dev": { @@ -3506,9 +5564,9 @@ ], "support": { "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", - "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.4" + "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.5" }, - "time": "2024-01-05T14:10:56+00:00" + "time": "2025-04-20T20:23:40+00:00" }, { "name": "theseer/tokenizer", @@ -3562,28 +5620,28 @@ }, { "name": "webmozart/assert", - "version": "1.11.0", + "version": "1.12.0", "source": { "type": "git", "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "reference": "541057574806f942c94662b817a50f63f7345360" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/541057574806f942c94662b817a50f63f7345360", + "reference": "541057574806f942c94662b817a50f63f7345360", "shasum": "" }, "require": { "ext-ctype": "*", + "ext-date": "*", + "ext-filter": "*", "php": "^7.2 || ^8.0" }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "suggest": { + "ext-intl": "", + "ext-simplexml": "", + "ext-spl": "" }, "type": "library", "extra": { @@ -3614,19 +5672,19 @@ ], "support": { "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/webmozarts/assert/tree/1.12.0" }, - "time": "2022-06-03T18:03:27+00:00" + "time": "2025-10-20T12:43:39+00:00" } ], "aliases": [], "minimum-stability": "stable", - "stability-flags": [], + "stability-flags": {}, "prefer-stable": false, "prefer-lowest": false, "platform": { "php": "^8.1" }, - "platform-dev": [], - "plugin-api-version": "2.3.0" + "platform-dev": {}, + "plugin-api-version": "2.6.0" } diff --git a/phpstan.neon b/phpstan.neon new file mode 100644 index 0000000..75ce2dd --- /dev/null +++ b/phpstan.neon @@ -0,0 +1,22 @@ +parameters: + level: 8 + paths: + - src + excludePaths: + - tests/* + - vendor/* + + bootstrapFiles: + - src/Core/Utils.php + + ignoreErrors: + - '#Access to an undefined property.*#' + - identifier: missingType.iterableValue + + typeAliases: + MixedArray: 'array' + + parallel: + jobSize: 20 + maximumNumberOfProcesses: 32 + minimumNumberOfJobsPerProcess: 2 \ No newline at end of file diff --git a/src/AST/ArrayLiteral.php b/src/AST/ArrayLiteral.php index ce7ec39..f11df5f 100644 --- a/src/AST/ArrayLiteral.php +++ b/src/AST/ArrayLiteral.php @@ -2,15 +2,16 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** * Represents an array literal in the template. + * + * @extends Literal */ class ArrayLiteral extends Literal { - public string $type = "ArrayLiteral"; + public string $type = 'ArrayLiteral'; /** * @param Expression[] $value diff --git a/src/AST/BinaryExpression.php b/src/AST/BinaryExpression.php index 284f5b0..0f58a64 100644 --- a/src/AST/BinaryExpression.php +++ b/src/AST/BinaryExpression.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** @@ -12,12 +11,12 @@ */ class BinaryExpression extends Expression { - public string $type = "BinaryExpression"; + public string $type = 'BinaryExpression'; public function __construct( public mixed $operator, public Expression $left, - public Expression $right) - { + public Expression $right, + ) { } -} \ No newline at end of file +} diff --git a/src/AST/BreakStatement.php b/src/AST/BreakStatement.php index 9548785..7463631 100644 --- a/src/AST/BreakStatement.php +++ b/src/AST/BreakStatement.php @@ -6,5 +6,5 @@ class BreakStatement extends Statement { - public string $type = "BreakStatement"; + public string $type = 'BreakStatement'; } diff --git a/src/AST/CallExpression.php b/src/AST/CallExpression.php index ca7f45f..199dd6e 100644 --- a/src/AST/CallExpression.php +++ b/src/AST/CallExpression.php @@ -2,20 +2,18 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; class CallExpression extends Expression { - public string $type = "CallExpression"; + public string $type = 'CallExpression'; /** - * @param Expression $callee * @param Expression[] $args */ public function __construct( public Expression $callee, - public array $args) - { + public array $args, + ) { } -} \ No newline at end of file +} diff --git a/src/AST/CallStatement.php b/src/AST/CallStatement.php index f1bbc7c..7400bd5 100644 --- a/src/AST/CallStatement.php +++ b/src/AST/CallStatement.php @@ -6,12 +6,13 @@ class CallStatement extends Statement { - public string $type = "CallStatement"; + public string $type = 'CallStatement'; /** - * @param CallExpression $call * @param Expression[]|null $args - * @param Statement[] $body + * @param Statement[] $body */ - public function __construct(public CallExpression $call, public ?array $args, public array $body) {} + public function __construct(public CallExpression $call, public ?array $args, public array $body) + { + } } diff --git a/src/AST/Comment.php b/src/AST/Comment.php index 9049107..d86719f 100644 --- a/src/AST/Comment.php +++ b/src/AST/Comment.php @@ -6,7 +6,9 @@ class Comment extends Statement { - public string $type = "Comment"; + public string $type = 'Comment'; - public function __construct(public string $value) {} + public function __construct(public string $value) + { + } } diff --git a/src/AST/ContinueStatement.php b/src/AST/ContinueStatement.php index 95c2adb..16a2bf0 100644 --- a/src/AST/ContinueStatement.php +++ b/src/AST/ContinueStatement.php @@ -6,5 +6,5 @@ class ContinueStatement extends Statement { - public string $type = "ContinueStatement"; + public string $type = 'ContinueStatement'; } diff --git a/src/AST/Expression.php b/src/AST/Expression.php index 6c31e2a..06a0b09 100644 --- a/src/AST/Expression.php +++ b/src/AST/Expression.php @@ -2,12 +2,12 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** * Expressions will result in a value at runtime (unlike statements). */ -abstract class Expression extends Statement { - public string $type = "Expression"; -} \ No newline at end of file +abstract class Expression extends Statement +{ + public string $type = 'Expression'; +} diff --git a/src/AST/FilterExpression.php b/src/AST/FilterExpression.php index 9e2d60b..34b2014 100644 --- a/src/AST/FilterExpression.php +++ b/src/AST/FilterExpression.php @@ -2,21 +2,19 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** * An operation with two sides, separated by the | operator. - * Operator precedence: https://github.com/pallets/jinja/issues/379#issuecomment-168076202 + * Operator precedence: https://github.com/pallets/jinja/issues/379#issuecomment-168076202. */ class FilterExpression extends Expression { - public string $type = "FilterExpression"; + public string $type = 'FilterExpression'; public function __construct( public Expression $operand, - public Identifier|CallExpression $filter - ) - { + public Identifier|CallExpression $filter, + ) { } -} \ No newline at end of file +} diff --git a/src/AST/FilterStatement.php b/src/AST/FilterStatement.php index bd60ad3..65d0709 100644 --- a/src/AST/FilterStatement.php +++ b/src/AST/FilterStatement.php @@ -6,11 +6,12 @@ class FilterStatement extends Statement { - public string $type = "FilterStatement"; + public string $type = 'FilterStatement'; /** - * @param Identifier|CallExpression $filter * @param Statement[] $body */ - public function __construct(public Identifier|CallExpression $filter, public array $body) {} + public function __construct(public Identifier|CallExpression $filter, public array $body) + { + } } diff --git a/src/AST/FloatLiteral.php b/src/AST/FloatLiteral.php index ef439e8..0c87115 100644 --- a/src/AST/FloatLiteral.php +++ b/src/AST/FloatLiteral.php @@ -4,9 +4,12 @@ namespace Codewithkyrian\Jinja\AST; +/** + * @extends Literal + */ class FloatLiteral extends Literal { - public string $type = "FloatLiteral"; + public string $type = 'FloatLiteral'; public function __construct(float $value) { diff --git a/src/AST/ForStatement.php b/src/AST/ForStatement.php index d8ab9b5..77755b4 100644 --- a/src/AST/ForStatement.php +++ b/src/AST/ForStatement.php @@ -6,22 +6,23 @@ /** * Loop over each item in a sequence - * https://jinja.palletsprojects.com/en/3.0.x/templates/#for + * https://jinja.palletsprojects.com/en/3.0.x/templates/#for. */ class ForStatement extends Statement { - public string $type = "For"; + public string $type = 'For'; /** - * @param Identifier|TupleLiteral $loopvar The variable to loop over - * @param Expression $iterable The iterable to loop over - * @param Statement[] $body The block to render for each item in the iterable - * @param Statement[]|null $defaultBlock Optional default block to render if the iterable is empty + * @param Identifier|TupleLiteral $loopvar The variable to loop over + * @param Expression $iterable The iterable to loop over + * @param Statement[] $body The block to render for each item in the iterable + * @param Statement[]|null $defaultBlock Optional default block to render if the iterable is empty */ public function __construct( public Identifier|TupleLiteral $loopvar, public Expression|SelectExpression $iterable, - public array $body, - public ?array $defaultBlock = null - ) {} + public array $body, + public ?array $defaultBlock = null, + ) { + } } diff --git a/src/AST/Identifier.php b/src/AST/Identifier.php index 0aeda94..b113417 100644 --- a/src/AST/Identifier.php +++ b/src/AST/Identifier.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** @@ -10,9 +9,9 @@ */ class Identifier extends Expression { - public string $type = "Identifier"; + public string $type = 'Identifier'; public function __construct(public string $value) { } -} \ No newline at end of file +} diff --git a/src/AST/IfStatement.php b/src/AST/IfStatement.php index f5a836c..2be6ad0 100644 --- a/src/AST/IfStatement.php +++ b/src/AST/IfStatement.php @@ -2,23 +2,20 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; class IfStatement extends Statement { - public string $type = "If"; + public string $type = 'If'; /** - * @param Expression $test * @param Statement[] $body * @param Statement[] $alternate */ public function __construct( public Expression $test, - public array $body, - public array $alternate - ) - { + public array $body, + public array $alternate, + ) { } -} \ No newline at end of file +} diff --git a/src/AST/IntegerLiteral.php b/src/AST/IntegerLiteral.php index 4ca172c..1341a14 100644 --- a/src/AST/IntegerLiteral.php +++ b/src/AST/IntegerLiteral.php @@ -4,9 +4,12 @@ namespace Codewithkyrian\Jinja\AST; +/** + * @extends Literal + */ class IntegerLiteral extends Literal { - public string $type = "IntegerLiteral"; + public string $type = 'IntegerLiteral'; public function __construct(int $value) { diff --git a/src/AST/KeywordArgumentExpression.php b/src/AST/KeywordArgumentExpression.php index b120d99..f4044b0 100644 --- a/src/AST/KeywordArgumentExpression.php +++ b/src/AST/KeywordArgumentExpression.php @@ -2,17 +2,15 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; class KeywordArgumentExpression extends Expression { - public string $type = "KeywordArgumentExpression"; + public string $type = 'KeywordArgumentExpression'; public function __construct( public Identifier $key, - public Expression $value - ) - { + public Expression $value, + ) { } -} \ No newline at end of file +} diff --git a/src/AST/Literal.php b/src/AST/Literal.php index aaeb226..d538691 100644 --- a/src/AST/Literal.php +++ b/src/AST/Literal.php @@ -2,16 +2,22 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** * Abstract base class for all Literal expressions. * Should not be instantiated directly. + * + * @template T */ abstract class Literal extends Expression { - public string $type = "Literal"; + public string $type = 'Literal'; - public function __construct(public $value) {} + /** + * @param T $value + */ + public function __construct(public mixed $value) + { + } } diff --git a/src/AST/Macro.php b/src/AST/Macro.php index 3ea0deb..0f1e7d7 100644 --- a/src/AST/Macro.php +++ b/src/AST/Macro.php @@ -6,12 +6,14 @@ class Macro extends Statement { - public string $type = "Macro"; + public string $type = 'Macro'; /** - * @param Identifier $name The name of the macro + * @param Identifier $name The name of the macro * @param Expression[] $args The arguments of the macro - * @param Statement[] $body The body of the macro + * @param Statement[] $body The body of the macro */ - public function __construct(public Identifier $name, public array $args, public array $body) {} + public function __construct(public Identifier $name, public array $args, public array $body) + { + } } diff --git a/src/AST/MemberExpression.php b/src/AST/MemberExpression.php index 531d841..e9afd88 100644 --- a/src/AST/MemberExpression.php +++ b/src/AST/MemberExpression.php @@ -6,11 +6,12 @@ class MemberExpression extends Expression { - public string $type = "MemberExpression"; + public string $type = 'MemberExpression'; public function __construct( public Expression $object, public Expression $property, - public bool $computed - ) {} + public bool $computed, + ) { + } } diff --git a/src/AST/ObjectLiteral.php b/src/AST/ObjectLiteral.php index 23be946..650a468 100644 --- a/src/AST/ObjectLiteral.php +++ b/src/AST/ObjectLiteral.php @@ -2,20 +2,21 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; use SplObjectStorage; /** * Represents an object literal in the template. + * + * @extends Literal> */ class ObjectLiteral extends Literal { - public string $type = "ObjectLiteral"; + public string $type = 'ObjectLiteral'; /** - * @param SplObjectStorage $value + * @param SplObjectStorage $value */ public function __construct(SplObjectStorage $value) { diff --git a/src/AST/Program.php b/src/AST/Program.php index 4b30d57..60f35ad 100644 --- a/src/AST/Program.php +++ b/src/AST/Program.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** @@ -10,7 +9,7 @@ */ class Program extends Statement { - public string $type = "Program"; + public string $type = 'Program'; /** * @param Statement[] $body @@ -18,4 +17,4 @@ class Program extends Statement public function __construct(public array $body) { } -} \ No newline at end of file +} diff --git a/src/AST/SelectExpression.php b/src/AST/SelectExpression.php index e7112bd..f6b25c7 100644 --- a/src/AST/SelectExpression.php +++ b/src/AST/SelectExpression.php @@ -6,7 +6,9 @@ class SelectExpression extends Expression { - public string $type = "SelectExpression"; + public string $type = 'SelectExpression'; - public function __construct(public Expression $lhs, public Expression $test) {} + public function __construct(public Expression $lhs, public Expression $test) + { + } } diff --git a/src/AST/SetStatement.php b/src/AST/SetStatement.php index a73cc1c..619cdb5 100644 --- a/src/AST/SetStatement.php +++ b/src/AST/SetStatement.php @@ -2,21 +2,19 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; class SetStatement extends Statement { - public string $type = "Set"; + public string $type = 'Set'; /** - * @param Expression $assignee - * @param ?Expression $value * @param Statement[] $body */ public function __construct( public Expression $assignee, public ?Expression $value, - public array $body = [] - ) {} + public array $body = [], + ) { + } } diff --git a/src/AST/SliceExpression.php b/src/AST/SliceExpression.php index fb9e0aa..cab7071 100644 --- a/src/AST/SliceExpression.php +++ b/src/AST/SliceExpression.php @@ -2,17 +2,16 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; class SliceExpression extends Expression { - public string $type = "SliceExpression"; + public string $type = 'SliceExpression'; public function __construct( public ?Expression $start = null, public ?Expression $stop = null, - public ?Expression $step = null) - { + public ?Expression $step = null, + ) { } -} \ No newline at end of file +} diff --git a/src/AST/SpreadExpression.php b/src/AST/SpreadExpression.php index c2bc3fb..8214002 100644 --- a/src/AST/SpreadExpression.php +++ b/src/AST/SpreadExpression.php @@ -6,7 +6,9 @@ class SpreadExpression extends Expression { - public string $type = "SpreadExpression"; + public string $type = 'SpreadExpression'; - public function __construct(public Expression $argument) {} + public function __construct(public Expression $argument) + { + } } diff --git a/src/AST/Statement.php b/src/AST/Statement.php index 53f1bd3..a36ad0f 100644 --- a/src/AST/Statement.php +++ b/src/AST/Statement.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** @@ -10,5 +9,5 @@ */ abstract class Statement { - public string $type = "Statement"; -} \ No newline at end of file + public string $type = 'Statement'; +} diff --git a/src/AST/StringLiteral.php b/src/AST/StringLiteral.php index d31ac6d..aa7b842 100644 --- a/src/AST/StringLiteral.php +++ b/src/AST/StringLiteral.php @@ -2,15 +2,16 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** * Represents a text constant in the template. + * + * @extends Literal */ class StringLiteral extends Literal { - public string $type = "StringLiteral"; + public string $type = 'StringLiteral'; public function __construct(string $value) { diff --git a/src/AST/TernaryExpression.php b/src/AST/TernaryExpression.php index 93b8932..1d3394e 100644 --- a/src/AST/TernaryExpression.php +++ b/src/AST/TernaryExpression.php @@ -6,7 +6,9 @@ class TernaryExpression extends Expression { - public string $type = "TernaryExpression"; + public string $type = 'TernaryExpression'; - public function __construct(public Expression $condition, public Expression $ifTrue, public Expression $ifFalse) {} + public function __construct(public Expression $condition, public Expression $ifTrue, public Expression $ifFalse) + { + } } diff --git a/src/AST/TestExpression.php b/src/AST/TestExpression.php index 47fb45a..c294999 100644 --- a/src/AST/TestExpression.php +++ b/src/AST/TestExpression.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** @@ -10,13 +9,12 @@ */ class TestExpression extends Expression { - public string $type = "TestExpression"; + public string $type = 'TestExpression'; public function __construct( public Expression $operand, public bool $negate, - public Identifier $test - ) - { + public Identifier $test, + ) { } } diff --git a/src/AST/TupleLiteral.php b/src/AST/TupleLiteral.php index 4021e37..d10942e 100644 --- a/src/AST/TupleLiteral.php +++ b/src/AST/TupleLiteral.php @@ -2,15 +2,16 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; /** * Represents a tuple literal in the template. + * + * @extends Literal */ class TupleLiteral extends Literal { - public string $type = "TupleLiteral"; + public string $type = 'TupleLiteral'; /** * @param Expression[] $value diff --git a/src/AST/UnaryExpression.php b/src/AST/UnaryExpression.php index 1192911..ca6b55f 100644 --- a/src/AST/UnaryExpression.php +++ b/src/AST/UnaryExpression.php @@ -2,17 +2,15 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\AST; class UnaryExpression extends Expression { - public string $type = "UnaryExpression"; + public string $type = 'UnaryExpression'; public function __construct( public mixed $operator, - public Expression $argument - ) - { + public Expression $argument, + ) { } -} \ No newline at end of file +} diff --git a/src/Core/Environment.php b/src/Core/Environment.php index f080744..52aa45e 100644 --- a/src/Core/Environment.php +++ b/src/Core/Environment.php @@ -13,11 +13,26 @@ use Codewithkyrian\Jinja\Runtime\IntegerValue; use Codewithkyrian\Jinja\Runtime\KeywordArgumentsValue; use Codewithkyrian\Jinja\Runtime\NullValue; -use Codewithkyrian\Jinja\Runtime\NumericValue; use Codewithkyrian\Jinja\Runtime\ObjectValue; use Codewithkyrian\Jinja\Runtime\RuntimeValue; use Codewithkyrian\Jinja\Runtime\StringValue; use Codewithkyrian\Jinja\Runtime\UndefinedValue; +use Exception; + +use function array_is_list; +use function array_key_exists; +use function array_map; +use function call_user_func_array; +use function count; +use function is_array; +use function is_bool; +use function is_callable; +use function is_float; +use function is_int; +use function is_null; +use function is_string; +use function strtolower; +use function strtoupper; /** * Represents the current environment (scope) at runtime. @@ -25,120 +40,24 @@ class Environment { /** - * @var array The variables declared in this environment. - */ - protected array $variables = []; - - /** - * @var array The tests available in this environment. + * @var array the tests available in this environment */ public array $tests = []; /** - * @var ?Environment The parent environment, if any. - */ - protected ?Environment $parent = null; - - public function __construct(?Environment $parent = null) - { - $this->parent = $parent; - - $this->variables = [ - 'namespace' => new FunctionValue(function (?KeywordArgumentsValue $args = null) { - if (!$args) { - return new ObjectValue([]); - } - - return new ObjectValue($args->value); - }) - ]; - - $this->tests = [ - 'boolean' => fn(RuntimeValue $operand) => $operand->type === "BooleanValue", - 'callable' => fn(RuntimeValue $operand) => $operand instanceof FunctionValue, - 'odd' => function (RuntimeValue $operand) { - if (!($operand instanceof IntegerValue)) { - throw new RuntimeException("Cannot apply test 'odd' to type: $operand->type"); - } - return $operand->value % 2 !== 0; - }, - 'even' => function (RuntimeValue $operand) { - if (!($operand instanceof IntegerValue)) { - throw new RuntimeException("Cannot apply test 'even' to type: $operand->type"); - } - return $operand->value % 2 === 0; - }, - 'false' => fn(RuntimeValue $operand) => $operand instanceof BooleanValue && !$operand->value, - 'true' => fn(RuntimeValue $operand) => $operand instanceof BooleanValue && $operand->value, - 'null' => fn(RuntimeValue $operand) => $operand instanceof NullValue, - 'string' => fn(RuntimeValue $operand) => $operand instanceof StringValue, - 'number' => fn(RuntimeValue $operand) => $operand instanceof IntegerValue || $operand instanceof FloatValue, - 'integer' => fn(RuntimeValue $operand) => $operand instanceof IntegerValue, - 'iterable' => fn(RuntimeValue $operand) => $operand instanceof ArrayValue || $operand instanceof StringValue, - 'mapping' => fn(RuntimeValue $operand) => $operand instanceof ObjectValue, - 'lower' => fn(RuntimeValue $operand) => $operand instanceof StringValue && $operand->value === strtolower($operand->value), - 'upper' => fn(RuntimeValue $operand) => $operand instanceof StringValue && $operand->value === strtoupper($operand->value), - 'none' => fn(RuntimeValue $operand) => $operand instanceof NullValue, - 'defined' => fn(RuntimeValue $operand) => $operand instanceof UndefinedValue, - 'undefined' => fn(RuntimeValue $operand) => $operand instanceof UndefinedValue, - 'equalto' => fn(RuntimeValue $a, RuntimeValue $b) => $a->value === $b->value, - 'eq' => fn(RuntimeValue $a, RuntimeValue $b) => $a->value === $b->value, - 'ne' => fn(RuntimeValue $a, RuntimeValue $b) => $a->value !== $b->value, - ]; - } - - /** - * Set the value of a variable in the current environment. + * @var array the variables declared in this environment */ - public function set(string $name, $value): RuntimeValue - { - return $this->declareVariable($name, self::convertToRuntimeValues($value)); - } - - private function declareVariable(string $name, RuntimeValue $value): RuntimeValue - { - if (array_key_exists($name, $this->variables)) { - throw new SyntaxError("Variable already declared: $name"); - } - $this->variables[$name] = $value; - return $value; - } - - public function setVariable($name, $value) - { - $this->variables[$name] = $value; - return $value; - } + protected array $variables = []; /** - * Resolve the environment in which the variable is declared. + * @var ?Environment the parent environment, if any */ - private function resolve(string $name): Environment - { - if (array_key_exists($name, $this->variables)) { - return $this; - } - - if ($this->parent !== null) { - return $this->parent->resolve($name); - } - - throw new RuntimeException("Unknown variable: $name"); - } - - public function lookupVariable(string $name): RuntimeValue - { - try { - $environment = $this->resolve($name); - return $environment->variables[$name] ?? new UndefinedValue(); - } catch (\Exception $e) { - return new UndefinedValue(); - } - } - + protected ?Environment $parent = null; /** * Helper function to convert a PHP value to a runtime value. + * + * @return RuntimeValue */ public static function convertToRuntimeValues(mixed $input): RuntimeValue { @@ -159,9 +78,10 @@ public static function convertToRuntimeValues(mixed $input): RuntimeValue if (is_callable($input)) { return new FunctionValue(function ($args, $scope) use ($input) { - $plainArgs = array_map(fn($arg) => $arg->value, $args); - $result = call_user_func_array($input, $plainArgs); - return $this->convertToRuntimeValues($result); + $plainArgs = array_map(fn ($arg) => $arg->value, $args); + $result = call_user_func_array($input, $plainArgs); + + return self::convertToRuntimeValues($result); }); } @@ -174,6 +94,7 @@ public static function convertToRuntimeValues(mixed $input): RuntimeValue foreach ($input as $key => $value) { $convertedItems[$key] = self::convertToRuntimeValues($value); } + return new ObjectValue($convertedItems); } @@ -183,6 +104,7 @@ public static function convertToRuntimeValues(mixed $input): RuntimeValue return $arg->value; }, $args); $result = call_user_func_array($input, $plainArgs); + // Convert the result back to a runtime value return self::convertToRuntimeValues($result); }); @@ -192,6 +114,113 @@ public static function convertToRuntimeValues(mixed $input): RuntimeValue return new NullValue(); } - throw new RuntimeException("Cannot convert to runtime value: Unsupported type"); + throw new RuntimeException('Cannot convert to runtime value: Unsupported type'); + } + + public function __construct(?self $parent = null) + { + $this->parent = $parent; + + $this->variables = [ + 'namespace' => new FunctionValue(function ($args): ObjectValue { + if (empty($args)) { + return new ObjectValue([]); + } + + if (count($args) !== 1 || !($args[0] instanceof ObjectValue)) { + throw new RuntimeException('`namespace` expects either zero arguments or a single object argument'); + } + + return $args[0]; + }), + ]; + + $this->tests = [ + 'boolean' => fn (RuntimeValue $operand) => $operand instanceof BooleanValue, + 'callable' => fn (RuntimeValue $operand) => $operand instanceof FunctionValue, + 'odd' => fn (RuntimeValue $operand) => $operand instanceof IntegerValue && $operand->value % 2 !== 0, + 'even' => fn (RuntimeValue $operand) => $operand instanceof IntegerValue && $operand->value % 2 === 0, + 'false' => fn (RuntimeValue $operand) => $operand instanceof BooleanValue && !$operand->value, + 'true' => fn (RuntimeValue $operand) => $operand instanceof BooleanValue && $operand->value, + 'null' => fn (RuntimeValue $operand) => $operand instanceof NullValue, + 'string' => fn (RuntimeValue $operand) => $operand instanceof StringValue, + 'number' => fn (RuntimeValue $operand) => $operand instanceof IntegerValue || $operand instanceof FloatValue, + 'integer' => fn (RuntimeValue $operand) => $operand instanceof IntegerValue, + 'iterable' => fn (RuntimeValue $operand) => $operand instanceof ArrayValue || $operand instanceof StringValue, + 'mapping' => fn (RuntimeValue $operand) => $operand instanceof ObjectValue, + 'lower' => fn (RuntimeValue $operand) => $operand instanceof StringValue && $operand->value === strtolower($operand->value), + 'upper' => fn (RuntimeValue $operand) => $operand instanceof StringValue && $operand->value === strtoupper($operand->value), + 'none' => fn (RuntimeValue $operand) => $operand instanceof NullValue, + 'defined' => fn (RuntimeValue $operand) => $operand instanceof UndefinedValue, + 'undefined' => fn (RuntimeValue $operand) => $operand instanceof UndefinedValue, + 'equalto' => fn (RuntimeValue $a, RuntimeValue $b) => $a->value === $b->value, + 'eq' => fn (RuntimeValue $a, RuntimeValue $b) => $a->value === $b->value, + 'ne' => fn (RuntimeValue $a, RuntimeValue $b) => $a->value !== $b->value, + ]; + } + + /** + * Set the value of a variable in the current environment. + * + * @return RuntimeValue + */ + public function set(string $name, mixed $value): RuntimeValue + { + return $this->declareVariable($name, self::convertToRuntimeValues($value)); + } + + /** + * @return RuntimeValue + */ + public function setVariable(string $name, mixed $value): RuntimeValue + { + $this->variables[$name] = $value; + + return $value; + } + + /** + * @return RuntimeValue + */ + public function lookupVariable(string $name): RuntimeValue + { + try { + $environment = $this->resolve($name); + + return $environment->variables[$name] ?? new UndefinedValue(); + } catch (Exception $e) { + return new UndefinedValue(); + } + } + + /** + * @param RuntimeValue $value + * + * @return RuntimeValue + */ + private function declareVariable(string $name, RuntimeValue $value): RuntimeValue + { + if (array_key_exists($name, $this->variables)) { + throw new SyntaxError("Variable already declared: {$name}"); + } + $this->variables[$name] = $value; + + return $value; + } + + /** + * Resolve the environment in which the variable is declared. + */ + private function resolve(string $name): self + { + if (array_key_exists($name, $this->variables)) { + return $this; + } + + if ($this->parent !== null) { + return $this->parent->resolve($name); + } + + throw new RuntimeException("Unknown variable: {$name}"); } } diff --git a/src/Core/Interpreter.php b/src/Core/Interpreter.php index 50376f9..a1d0c95 100644 --- a/src/Core/Interpreter.php +++ b/src/Core/Interpreter.php @@ -50,11 +50,42 @@ use Codewithkyrian\Jinja\Runtime\StringValue; use Codewithkyrian\Jinja\Runtime\TupleValue; use Codewithkyrian\Jinja\Runtime\UndefinedValue; - +use Exception; + +use function abs; +use function array_key_exists; +use function array_keys; +use function array_map; +use function array_merge; +use function array_reverse; +use function array_slice; +use function array_unique; use function Codewithkyrian\Jinja\array_some; use function Codewithkyrian\Jinja\slice; use function Codewithkyrian\Jinja\toTitleCase; -use function PHPUnit\Framework\isNan; +use function count; +use function explode; +use function floor; +use function fmod; +use function implode; +use function in_array; +use function is_infinite; +use function is_nan; +use function is_numeric; +use function json_encode; +use function mb_str_split; +use function range; +use function sprintf; +use function str_contains; +use function str_repeat; +use function str_split; +use function strcmp; +use function strlen; +use function strtolower; +use function strtoupper; +use function trim; +use function ucfirst; +use function usort; class Interpreter { @@ -65,65 +96,80 @@ public function __construct(?Environment $env = null) $this->global = $env ?? new Environment(); } - public function run($program): RuntimeValue + /** + * @return RuntimeValue + */ + public function run(Statement $program): RuntimeValue { return $this->evaluate($program, $this->global); } - function evaluate(?Statement $statement, Environment $environment): RuntimeValue + /** + * @return RuntimeValue + */ + public function evaluate(?Statement $statement, Environment $environment): RuntimeValue { - if ($statement === null) return new UndefinedValue(); + if ($statement === null) { + return new UndefinedValue(); + } + /** + * @var array, callable(Statement, Environment): RuntimeValue> + */ $evaluators = [ - Program::class => [$this, "evalProgram"], - SetStatement::class => [$this, "evaluateSet"], - IfStatement::class => [$this, "evaluateIf"], - ForStatement::class => [$this, "evaluateFor"], - Macro::class => [$this, "evaluateMacro"], - CallStatement::class => [$this, "evaluateCallStatement"], - FilterStatement::class => [$this, "evaluateFilterStatement"], - BreakStatement::class => fn() => throw new BreakControl(), - ContinueStatement::class => fn() => throw new ContinueControl(), - IntegerLiteral::class => fn(IntegerLiteral $s) => new IntegerValue($s->value), - FloatLiteral::class => fn(FloatLiteral $s) => new FloatValue($s->value), - StringLiteral::class => fn(StringLiteral $s) => new StringValue($s->value), - ArrayLiteral::class => fn(ArrayLiteral $s) => new ArrayValue(array_map(fn($x) => $this->evaluate($x, $environment), $s->value)), - TupleLiteral::class => fn(TupleLiteral $s, Environment $environment) => new TupleValue(array_map(fn($x) => $this->evaluate($x, $environment), $s->value)), - ObjectLiteral::class => function (ObjectLiteral $s, Environment $environment): ObjectValue { + Program::class => [$this, 'evalProgram'], + SetStatement::class => [$this, 'evaluateSet'], + IfStatement::class => [$this, 'evaluateIf'], + ForStatement::class => [$this, 'evaluateFor'], + Macro::class => [$this, 'evaluateMacro'], + CallStatement::class => [$this, 'evaluateCallStatement'], + FilterStatement::class => [$this, 'evaluateFilterStatement'], + BreakStatement::class => fn () => throw new BreakControl(), + ContinueStatement::class => fn () => throw new ContinueControl(), + IntegerLiteral::class => fn (IntegerLiteral $s) => new IntegerValue($s->value), + FloatLiteral::class => fn (FloatLiteral $s) => new FloatValue($s->value), + StringLiteral::class => fn (StringLiteral $s) => new StringValue($s->value), + ArrayLiteral::class => fn (ArrayLiteral $s, Environment $env) => new ArrayValue(array_map(fn ($x) => $this->evaluate($x, $env), $s->value)), + TupleLiteral::class => fn (TupleLiteral $s, Environment $env) => new TupleValue(array_map(fn ($x) => $this->evaluate($x, $env), $s->value)), + ObjectLiteral::class => function (ObjectLiteral $s, Environment $env): ObjectValue { $mapping = []; + foreach ($s->value as $key) { - $evaluatedKey = $this->evaluate($key, $environment); + $evaluatedKey = $this->evaluate($key, $env); if (!($evaluatedKey instanceof StringValue)) { - throw new RuntimeException("Object keys must be strings"); + throw new RuntimeException('Object keys must be strings'); } - $mapping[$evaluatedKey->value] = $this->evaluate($s->value[$key], $environment); + $mapping[$evaluatedKey->value] = $this->evaluate($s->value[$key], $env); } + return new ObjectValue($mapping); }, - Identifier::class => [$this, "evaluateIdentifier"], - CallExpression::class => [$this, "evaluateCallExpression"], - MemberExpression::class => [$this, "evaluateMemberExpression"], - UnaryExpression::class => [$this, "evaluateUnaryExpression"], - BinaryExpression::class => [$this, "evaluateBinaryExpression"], - FilterExpression::class => [$this, "evaluateFilterExpression"], - TestExpression::class => [$this, "evaluateTestExpression"], - SelectExpression::class => [$this, "evaluateSelectExpression"], - TernaryExpression::class => [$this, "evaluateTernaryExpression"], - Comment::class => fn() => new NullValue(), + Identifier::class => [$this, 'evaluateIdentifier'], + CallExpression::class => [$this, 'evaluateCallExpression'], + MemberExpression::class => [$this, 'evaluateMemberExpression'], + UnaryExpression::class => [$this, 'evaluateUnaryExpression'], + BinaryExpression::class => [$this, 'evaluateBinaryExpression'], + FilterExpression::class => [$this, 'evaluateFilterExpression'], + TestExpression::class => [$this, 'evaluateTestExpression'], + SelectExpression::class => [$this, 'evaluateSelectExpression'], + TernaryExpression::class => [$this, 'evaluateTernaryExpression'], + Comment::class => fn () => new NullValue(), ]; $evaluator = $evaluators[$statement::class] ?? null; if ($evaluator === null) { - throw new RuntimeException("Unknown node type: " . $statement->type); + throw new RuntimeException('Unknown node type: ' . $statement->type); } return $evaluator($statement, $environment); } - /** * Evaluates expressions following the binary operation type. + * + * @return RuntimeValue + * * @throws SyntaxError */ private function evaluateBinaryExpression(BinaryExpression $node, Environment $environment): RuntimeValue @@ -132,10 +178,10 @@ private function evaluateBinaryExpression(BinaryExpression $node, Environment $e // Logical operators with short-circuiting handled by the evaluate function switch ($node->operator->value) { - case "and": - return $left->evaluateAsBool()->value ? $this->evaluate($node->right, $environment) : $left; - case "or": - return $left->evaluateAsBool()->value ? $left : $this->evaluate($node->right, $environment); + case 'and': + return $left->asBool()->value ? $this->evaluate($node->right, $environment) : $left; + case 'or': + return $left->asBool()->value ? $left : $this->evaluate($node->right, $environment); } // For non-short-circuit operators, evaluate the right operand @@ -143,25 +189,26 @@ private function evaluateBinaryExpression(BinaryExpression $node, Environment $e // Equality operators switch ($node->operator->value) { - case "==": + case '==': return new BooleanValue($left->value == $right->value); - case "!=": + case '!=': return new BooleanValue($left->value != $right->value); } if ($left instanceof UndefinedValue || $right instanceof UndefinedValue) { if ($right instanceof UndefinedValue && in_array($node->operator->value, ['in', 'not in'])) { // Special case: `anything in undefined` is `false` and `anything not in undefined` is `true` - return new BooleanValue($node->operator->value === "not in"); + return new BooleanValue($node->operator->value === 'not in'); } - throw new \Exception("Cannot perform operation {$node->operator->value} on undefined values"); + + throw new Exception("Cannot perform operation {$node->operator->value} on undefined values"); } if ($left instanceof NullValue || $right instanceof NullValue) { - throw new \Exception("Cannot perform operation on null values"); + throw new Exception('Cannot perform operation on null values'); } - if ($node->operator->value === "~") { + if ($node->operator->value === '~') { return new StringValue((string)$left->value . (string)$right->value); } @@ -172,21 +219,24 @@ private function evaluateBinaryExpression(BinaryExpression $node, Environment $e ) { switch ($node->operator->value) { // Arithmetic operators - case "+": - case "-": - case "*": + case '+': + case '-': + case '*': $result = match ($node->operator->value) { - "+" => $left->value + $right->value, - "-" => $left->value - $right->value, - "*" => $left->value * $right->value, + '+' => $left->value + $right->value, + '-' => $left->value - $right->value, + '*' => $left->value * $right->value, + default => throw new \RuntimeException("Unsupported operator: {$node->operator->value}"), }; + $isFloat = $left instanceof FloatValue || $right instanceof FloatValue; - return $isFloat ? new FloatValue($result) : new IntegerValue($result); - case "/": + + return $isFloat ? new FloatValue((float)$result) : new IntegerValue((int)$result); + case '/': $result = $left->value / $right->value; if (is_infinite($result) || is_nan($result)) { - throw new \RuntimeException("Invalid division result"); + throw new \RuntimeException('Invalid division result'); } $isFloatOperand = $left instanceof FloatValue || $right instanceof FloatValue; @@ -195,27 +245,27 @@ private function evaluateBinaryExpression(BinaryExpression $node, Environment $e return new FloatValue($result); } - return fmod($result, 1.0) === 0.0 ? new IntegerValue((int) $result) : new FloatValue($result); - case "%": - $result = $left->value % $right->value; + return fmod($result, 1.0) === 0.0 ? new IntegerValue((int)$result) : new FloatValue($result); + case '%': + $result = $left->value % $right->value; $isFloatOperand = $left instanceof FloatValue || $right instanceof FloatValue; return $isFloatOperand ? new FloatValue($result) : new IntegerValue($result); // Comparison operators - case "<": + case '<': return new BooleanValue($left->value < $right->value); - case ">": + case '>': return new BooleanValue($left->value > $right->value); - case ">=": + case '>=': return new BooleanValue($left->value >= $right->value); - case "<=": + case '<=': return new BooleanValue($left->value <= $right->value); } } // Array operations - if ($left instanceof ArrayValue && $right instanceof ArrayValue && $node->operator->value === "+") { + if ($left instanceof ArrayValue && $right instanceof ArrayValue && $node->operator->value === '+') { // Concatenate arrays return new ArrayValue(array_merge($left->value, $right->value)); } @@ -225,73 +275,76 @@ private function evaluateBinaryExpression(BinaryExpression $node, Environment $e foreach ($right->value as $item) { if ($item->value === $left->value) { $memberFound = true; + break; } } switch ($node->operator->value) { - case "in": + case 'in': return new BooleanValue($memberFound); - case "not in": + case 'not in': return new BooleanValue(!$memberFound); } } if ($left instanceof StringValue || $right instanceof StringValue) { // Ensure both operands are treated as strings for concatenation - if ($node->operator->value == "+") { - $leftStr = ($left instanceof StringValue) ? $left->value : (string)$left->value; + if ($node->operator->value == '+') { + $leftStr = ($left instanceof StringValue) ? $left->value : (string)$left->value; $rightStr = ($right instanceof StringValue) ? $right->value : (string)$right->value; + return new StringValue($leftStr . $rightStr); } } if ($left instanceof StringValue && $right instanceof StringValue) { switch ($node->operator->value) { - case "in": + case 'in': return new BooleanValue(str_contains($right->value, $left->value)); - case "not in": + case 'not in': return new BooleanValue(!str_contains($right->value, $left->value)); } } if ($left instanceof StringValue && $right instanceof ObjectValue) { switch ($node->operator->value) { - case "in": + case 'in': return new BooleanValue(array_key_exists($left->value, $right->value)); - case "not in": + case 'not in': return new BooleanValue(!array_key_exists($left->value, $right->value)); } } - throw new SyntaxError("Unknown operator: " . $node->operator->value); + throw new SyntaxError('Unknown operator: ' . $node->operator->value); } /** * Evaluates the arguments of a call expression. + * * @param Expression[] $args - * @param Environment $environment - * @return array> + * + * @return array{0: RuntimeValue[], 1: array>} */ private function evaluateArguments(array $args, Environment $environment): array { $positionalArguments = []; - $keywordArguments = []; + $keywordArguments = []; foreach ($args as $argument) { if ($argument instanceof SpreadExpression) { $value = $this->evaluate($argument->argument, $environment); if (!($value instanceof ArrayValue)) { - throw new \Exception("Cannot unpack non-iterable type: {$value->type}"); + throw new Exception("Cannot unpack non-iterable type: {$value->type}"); } foreach ($value->value as $item) { $positionalArguments[] = $item; } - } else if ($argument instanceof KeywordArgumentExpression) { + } elseif ($argument instanceof KeywordArgumentExpression) { $keywordArguments[$argument->key->value] = $this->evaluate($argument->value, $environment); } else { if (count($keywordArguments) > 0) { - throw new \Exception("Positional arguments must come before keyword arguments"); + throw new Exception('Positional arguments must come before keyword arguments'); } $positionalArguments[] = $this->evaluate($argument, $environment); @@ -301,316 +354,357 @@ private function evaluateArguments(array $args, Environment $environment): array return [$positionalArguments, $keywordArguments]; } + /** + * @param RuntimeValue $operand + * + * @return RuntimeValue + */ private function applyFilter(RuntimeValue $operand, Identifier|CallExpression $filter, Environment $environment): RuntimeValue { if ($filter instanceof Identifier) { if ($filter->value === 'tojson') { - return new StringValue(json_encode($operand)); + $encoded = json_encode($operand); + if ($encoded === false) { + throw new RuntimeException('Failed to encode value as JSON'); + } + + return new StringValue($encoded); } // ArrayValue filters if ($operand instanceof ArrayValue) { switch ($filter->value) { - case "list": + case 'list': return $operand; - case "first": + case 'first': return $operand->value[0]; - case "last": + case 'last': $lastIndex = count($operand->value) - 1; + return $operand->value[$lastIndex]; - case "length": + case 'length': return new IntegerValue(count($operand->value)); - case "reverse": + case 'reverse': $reversed = array_reverse($operand->value); + return new ArrayValue($reversed); - case "sort": + case 'sort': usort($operand->value, function (RuntimeValue $a, RuntimeValue $b) { if ($a->type != $b->type) { - throw new \Exception("Cannot compare different types: $a->type and $b->type"); + throw new Exception("Cannot compare different types: {$a->type} and {$b->type}"); } return match ($a->type) { - "IntegerValue" => $a->value <=> $b->value, - "FloatValue" => $a->value <=> $b->value, - "StringValue" => strcmp($a->value, $b->value), - default => throw new \Exception("Cannot compare type: $a->type"), + 'IntegerValue' => $a->value <=> $b->value, + 'FloatValue' => $a->value <=> $b->value, + 'StringValue' => strcmp($a->value, $b->value), + default => throw new Exception("Cannot compare type: {$a->type}"), }; }); + return new ArrayValue($operand->value); - case "join": - return new StringValue(implode('', $operand->value)); - case "string": - return new StringValue(json_encode($operand->value)); - case "unique": + case 'join': + return new StringValue(implode('', array_map(fn ($x) => $x->value, $operand->value))); + case 'string': + $encoded = json_encode($operand->value); + if ($encoded === false) { + throw new RuntimeException('Failed to encode value as JSON'); + } + + return new StringValue($encoded); + case 'unique': return new ArrayValue(array_unique($operand->value)); default: - throw new \Exception("Unknown ArrayValue filter: {$filter->value}"); + throw new Exception("Unknown ArrayValue filter: {$filter->value}"); } } // StringValue filters if ($operand instanceof StringValue) { return match ($filter->value) { - "length" => new IntegerValue(strlen($operand->value)), - "upper" => new StringValue(strtoupper($operand->value)), - "lower" => new StringValue(strtolower($operand->value)), - "title" => new StringValue(toTitleCase($operand->value)), - "capitalize" => new StringValue(ucfirst($operand->value)), - "trim" => new StringValue(trim($operand->value)), - "indent" => new StringValue(implode("\n", array_map( - fn($x, $i) => $i === 0 || $x === "" ? $x : " " . $x, + 'length' => new IntegerValue(strlen($operand->value)), + 'upper' => new StringValue(strtoupper($operand->value)), + 'lower' => new StringValue(strtolower($operand->value)), + 'title' => new StringValue(toTitleCase($operand->value)), + 'capitalize' => new StringValue(ucfirst($operand->value)), + 'trim' => new StringValue(trim($operand->value)), + 'indent' => new StringValue(implode("\n", array_map( + fn ($x, $i) => $i === 0 || $x === '' ? $x : ' ' . $x, explode("\n", $operand->value), - range(0, count(explode("\n", $operand->value)) - 1) + range(0, count(explode("\n", $operand->value)) - 1), ))), - "string" => $operand, - "int" => new IntegerValue((int)$operand->value), - "float" => new FloatValue((float)$operand->value), - default => throw new \Exception("Unknown StringValue filter: {$filter->value}"), + 'string' => $operand, + 'int' => new IntegerValue((int)$operand->value), + 'float' => new FloatValue((float)$operand->value), + default => throw new Exception("Unknown StringValue filter: {$filter->value}"), }; } // NumericValue filters if ($operand instanceof IntegerValue || $operand instanceof FloatValue) { return match ($filter->value) { - "abs" => $operand instanceof IntegerValue ? new IntegerValue(abs($operand->value)) : new FloatValue(abs($operand->value)), - "int" => new IntegerValue((int)floor($operand->value)), - "float" => new FloatValue((float)$operand->value), - "string" => new StringValue((string)$operand->value), - default => throw new \Exception("Unknown NumericValue filter: {$filter->value}"), + 'abs' => $operand instanceof IntegerValue + ? new IntegerValue((int)abs($operand->value)) + : new FloatValue((float)abs($operand->value)), + 'int' => new IntegerValue((int)floor($operand->value)), + 'float' => new FloatValue((float)$operand->value), + 'string' => new StringValue((string)$operand->value), + default => throw new Exception("Unknown NumericValue filter: {$filter->value}"), }; } // ObjectValue filters if ($operand instanceof ObjectValue) { switch ($filter->value) { - case "items": + case 'items': $items = []; foreach ($operand->value as $key => $value) { $items[] = new ArrayValue([new StringValue($key), $value]); } + return new ArrayValue($items); - case "length": + case 'length': return new IntegerValue(count($operand->value)); default: - throw new \Exception("Unknown ObjectValue filter: {$filter->value}"); + throw new Exception("Unknown ObjectValue filter: {$filter->value}"); } } // BooleanValue filters if ($operand instanceof BooleanValue) { switch ($filter->value) { - case "bool": + case 'bool': return $operand; - case "int": + case 'int': return new IntegerValue($operand->value ? 1 : 0); - case "float": + case 'float': return new FloatValue($operand->value ? 1.0 : 0.0); - case "string": - return new StringValue($operand->value ? "true" : "false"); + case 'string': + return new StringValue($operand->value ? 'true' : 'false'); default: - throw new \Exception("Unknown BooleanValue filter: {$filter->value}"); + throw new Exception("Unknown BooleanValue filter: {$filter->value}"); } } - throw new \Exception("Cannot apply filter {$filter->value} to type $operand->type"); + throw new Exception("Cannot apply filter {$filter->value} to type {$operand->type}"); } - if ($filter instanceof CallExpression) { - if (!($filter->callee instanceof Identifier)) { - throw new \Exception("Unknown filter: {$filter->callee->type}"); - } + if (!($filter->callee instanceof Identifier)) { + throw new Exception("Unknown filter: {$filter->callee->type}"); + } - $filterName = $filter->callee->value; + $filterName = $filter->callee->value; - if ($filterName === "tojson") { - return new StringValue(json_encode($operand)); + if ($filterName === 'tojson') { + $encoded = json_encode($operand); + if ($encoded === false) { + throw new RuntimeException('Failed to encode value as JSON'); } - if ($filterName === "join") { - if ($operand instanceof StringValue) { - $value = mb_str_split($operand->value); - } elseif ($operand instanceof ArrayValue) { - $value = $operand->value; - } else { - throw new \Exception("Cannot apply join filter to type: $operand->type"); - } + return new StringValue($encoded); + } - [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); - $separator = $args[0] ?? $kwargs["separator"] ?? new StringValue(""); + if ($filterName === 'join') { + if ($operand instanceof StringValue) { + $value = mb_str_split($operand->value); + } elseif ($operand instanceof ArrayValue) { + $value = array_map(fn ($x) => $x->value, $operand->value); + } else { + throw new Exception("Cannot apply join filter to type: {$operand->type}"); + } - if (!($separator instanceof StringValue)) { - throw new \Exception("separator must be a string"); - } + [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); + $separator = $args[0] ?? $kwargs['separator'] ?? new StringValue(''); - return new StringValue(implode($separator->value, $value)); + if (!($separator instanceof StringValue)) { + throw new Exception('separator must be a string'); } - if ($filterName === "int" || $filterName === "float") { - [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); - $default = $args[0] ?? $kwargs["default"] ?? ($filterName === "int" ? new IntegerValue(0) : new FloatValue(0.0)); + return new StringValue(implode($separator->value, $value)); + } - if ($operand instanceof IntegerValue || $operand instanceof FloatValue) { - return $operand; - } + if ($filterName === 'int' || $filterName === 'float') { + [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); + $default = $args[0] ?? $kwargs['default'] ?? null; + + if ($operand instanceof IntegerValue || $operand instanceof FloatValue) { + return $operand; + } + + if ($operand instanceof StringValue) { + if ($filterName === 'int') { + if (!is_numeric($operand->value)) { + return $default ?? new IntegerValue(0); + } - if ($operand instanceof StringValue) { - $value = $filterName === "int" ? (int)$operand->value : (float)$operand->value; + $value = (int)$operand->value; - return isNan($value) ? $default : ($filterName === "int" ? new IntegerValue($value) : new FloatValue($value)); + return new IntegerValue($value); } + $value = (float)$operand->value; - if ($operand instanceof BooleanValue) { - return $filterName === "int" ? new IntegerValue($operand->value ? 1 : 0) : new FloatValue($operand->value ? 1.0 : 0.0); + if (is_nan($value)) { + return $default ?? new FloatValue(0.0); } - throw new \Exception("Cannot apply $filterName filter to type: $operand->type"); + return new FloatValue($value); + } - if ($filterName === "default") { - [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); - $default = $args[0] ?? new StringValue(""); - $boolean = $args[1] ?? $kwargs["boolean"] ?? new BooleanValue(false); + if ($operand instanceof BooleanValue) { + return $filterName === 'int' ? new IntegerValue($operand->value ? 1 : 0) : new FloatValue($operand->value ? 1.0 : 0.0); + } - if (!($boolean instanceof BooleanValue)) { - throw new \Exception("`default` filter flag must be a boolean"); - } + throw new Exception("Cannot apply {$filterName} filter to type: {$operand->type}"); + } - if ($operand instanceof UndefinedValue || $operand instanceof NullValue || ($boolean->value && !$operand->evaluateAsBool()->value)) { - return $default; - } + if ($filterName === 'default') { + [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); + $default = $args[0] ?? new StringValue(''); + $boolean = $args[1] ?? $kwargs['boolean'] ?? new BooleanValue(false); - return $operand; + if (!($boolean instanceof BooleanValue)) { + throw new Exception('`default` filter flag must be a boolean'); } - if ($operand instanceof ArrayValue) { - switch ($filterName) { - case "selectattr": - case "rejectattr": - if (array_some($operand->value, fn($x) => !($x instanceof ObjectValue))) { - throw new \Exception("`$filterName` can only be applied to an array of objects"); - } + if ($operand instanceof UndefinedValue || $operand instanceof NullValue || ($boolean->value && !$operand->asBool()->value)) { + return $default; + } - if (array_some($filter->args, fn($x) => $x->type !== "StringLiteral")) { - throw new \Exception("The arguments of `$filterName` must be strings"); - } + return $operand; + } + + if ($operand instanceof ArrayValue) { + switch ($filterName) { + case 'selectattr': + case 'rejectattr': + if (array_some($operand->value, fn ($x) => !($x instanceof ObjectValue))) { + throw new Exception("`{$filterName}` can only be applied to an array of objects"); + } - [$attrExpr, $testNameExpr, $valueExpr] = $filter->args; + if (array_some($filter->args, fn ($x) => $x->type !== 'StringLiteral')) { + throw new Exception("The arguments of `{$filterName}` must be strings"); + } - $attr = $this->evaluate($attrExpr, $environment); - $testName = isset($testNameExpr) ? $this->evaluate($testNameExpr, $environment) : null; - $value = isset($valueExpr) ? $this->evaluate($valueExpr, $environment) : null; + $attrExpr = $filter->args[0]; + $testNameExpr = $filter->args[1] ?? null; + $valueExpr = $filter->args[2] ?? null; - if (!($attr instanceof StringValue)) { - throw new \Exception("The attribute name of `$filterName` must be a string"); - } + $attr = $this->evaluate($attrExpr, $environment); + $testName = $testNameExpr ? $this->evaluate($testNameExpr, $environment) : null; + $value = $valueExpr ? $this->evaluate($valueExpr, $environment) : null; - $testFunction = null; - if ($testName !== null) { - $test = $environment->tests[$testName->value] ?? null; - if ($test === null) { - throw new \Exception("Unknown test: " . $testName->value); - } - $testFunction = $test; - } else { - // Default to checking for truthiness if no test name is provided - $testFunction = fn($x) => $x->evaluateAsBool()->value; + if (!($attr instanceof StringValue)) { + throw new Exception("The attribute name of `{$filterName}` must be a string"); + } + + $testFunction = null; + if ($testName !== null) { + $test = $environment->tests[$testName->value] ?? null; + if ($test === null) { + throw new Exception('Unknown test: ' . $testName->value); } + $testFunction = $test; + } else { + // Default to checking for truthiness if no test name is provided + $testFunction = fn ($x) => $x->evaluateAsBool()->value; + } - $filtered = []; - foreach ($operand->value as $item) { - $attrValue = $item->value[$attr->value] ?? null; - if ($attrValue === null) { - continue; - } + $filtered = []; + foreach ($operand->value as $item) { + $attrValue = $item->value[$attr->value] ?? null; + if ($attrValue === null) { + continue; + } - $testResult = $testFunction($attrValue, $value); - $shouldInclude = ($filterName === "selectattr") ? $testResult : !$testResult; + $testResult = $testFunction($attrValue, $value); + $shouldInclude = ($filterName === 'selectattr') ? $testResult : !$testResult; - if ($shouldInclude) { - $filtered[] = $item; - } + if ($shouldInclude) { + $filtered[] = $item; } + } - return new ArrayValue($filtered); + return new ArrayValue($filtered); - case "map": - [$_, $kwargs] = $this->evaluateArguments($filter->args, $environment); + case 'map': + [$_, $kwargs] = $this->evaluateArguments($filter->args, $environment); - if (array_key_exists("attribute", $kwargs)) { - $attr = $kwargs["attribute"]; - if (!($attr instanceof StringValue)) { - throw new \Exception("attribute must be a string"); - } + if (array_key_exists('attribute', $kwargs)) { + $attr = $kwargs['attribute']; + if (!($attr instanceof StringValue)) { + throw new Exception('attribute must be a string'); + } - $defaultValue = $kwargs["default"] ?? new UndefinedValue(); + $defaultValue = $kwargs['default'] ?? new UndefinedValue(); - $mapped = array_map(function ($item) use ($attr, $defaultValue) { - if (!($item instanceof ObjectValue)) { - throw new \Exception("items in map must be an object"); - } + $mapped = array_map(function ($item) use ($attr, $defaultValue) { + if (!($item instanceof ObjectValue)) { + throw new Exception('items in map must be an object'); + } - return $item->value[$attr->value] ?? $defaultValue; - }, $operand->value); + return $item->value[$attr->value] ?? $defaultValue; + }, $operand->value); - return new ArrayValue($mapped); - } else { - throw new \Exception("`map` expressions without `attribute` set are not currently supported."); - } + return new ArrayValue($mapped); + } - default: - throw new \Exception("Unknown ArrayValue filter: $filterName"); - } + throw new Exception('`map` expressions without `attribute` set are not currently supported.'); + + default: + throw new Exception("Unknown ArrayValue filter: {$filterName}"); } + } - if ($operand instanceof StringValue) { - switch ($filterName) { - case "indent": - [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); + if ($operand instanceof StringValue) { + switch ($filterName) { + case 'indent': + [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); - $width = $args[0] ?? $kwargs["width"] ?? new IntegerValue(4); - if (!($width instanceof IntegerValue)) { - throw new \Exception("width must be a number"); - } + $width = $args[0] ?? $kwargs['width'] ?? new IntegerValue(4); + if (!($width instanceof IntegerValue)) { + throw new Exception('width must be a number'); + } - $first = $args[1] ?? $kwargs["first"] ?? new BooleanValue(false); - $blank = $args[2] ?? $kwargs["blank"] ?? new BooleanValue(false); + $first = $args[1] ?? $kwargs['first'] ?? new BooleanValue(false); + $blank = $args[2] ?? $kwargs['blank'] ?? new BooleanValue(false); - $lines = explode("\n", $operand->value); - $indent = str_repeat(" ", $width->value); - $indented = array_map(function ($x, $i) use ($first, $blank, $indent) { - return (!($first->value) && $i === 0) || (!($blank->value) && $x === "") ? $x : $indent . $x; - }, $lines, range(0, count($lines) - 1)); + $lines = explode("\n", $operand->value); + $indent = str_repeat(' ', $width->value); + $indented = array_map(function ($x, $i) use ($first, $blank, $indent) { + return (!($first->value) && $i === 0) || (!($blank->value) && $x === '') ? $x : $indent . $x; + }, $lines, range(0, count($lines) - 1)); - return new StringValue(implode("\n", $indented)); + return new StringValue(implode("\n", $indented)); - case "replace": - $replaceFn = $operand->builtins["replace"] ?? null; + case 'replace': + $replaceFn = $operand->builtins['replace'] ?? null; - if (!($replaceFn instanceof FunctionValue)) { - throw new \Exception("`replace` filter not available for type: $operand->type"); - } + if (!($replaceFn instanceof FunctionValue)) { + throw new Exception("`replace` filter not available for type: {$operand->type}"); + } - [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); + [$args, $kwargs] = $this->evaluateArguments($filter->args, $environment); - return ($replaceFn->value)([...$args, new KeywordArgumentsValue($kwargs)], $environment); + return ($replaceFn->value)([...$args, new KeywordArgumentsValue($kwargs)], $environment); - default: - throw new \Exception("Unknown StringValue filter: $filterName"); - } + default: + throw new Exception("Unknown StringValue filter: {$filterName}"); } - - throw new \Exception("Cannot apply filter \"$filterName\" to type: $operand->type"); } - throw new \Exception("Unknown filter: {$filter->type}"); + throw new Exception("Cannot apply filter \"{$filterName}\" to type: {$operand->type}"); } /** * Evaluates expressions following the filter operation type. + * + * @return RuntimeValue */ private function evaluateFilterExpression(FilterExpression $node, Environment $environment): RuntimeValue { $operand = $this->evaluate($node->operand, $environment); + return $this->applyFilter($operand, $node->filter, $environment); } @@ -624,7 +718,7 @@ private function evaluateTestExpression(TestExpression $node, Environment $envir $testFunction = $environment->tests[$node->test->value] ?? null; if ($testFunction === null) { - throw new \Exception("Unknown test: {$node->test->value}"); + throw new Exception("Unknown test: {$node->test->value}"); } $result = $testFunction($operand); @@ -634,12 +728,14 @@ private function evaluateTestExpression(TestExpression $node, Environment $envir /** * Evaluates expressions following the select operation type. + * + * @return RuntimeValue */ private function evaluateSelectExpression(SelectExpression $node, Environment $environment): RuntimeValue { $predicate = $this->evaluate($node->test, $environment); - if (!$predicate->evaluateAsBool()->value) { + if (!$predicate->asBool()->value) { return new UndefinedValue(); } @@ -648,22 +744,27 @@ private function evaluateSelectExpression(SelectExpression $node, Environment $e /** * Evaluates expressions following the unary operation type. + * + * @return RuntimeValue */ private function evaluateUnaryExpression(UnaryExpression $node, Environment $environment): RuntimeValue { $argument = $this->evaluate($node->argument, $environment); return match ($node->operator->value) { - "not" => new BooleanValue(!$argument->value), - default => throw new \Exception("Unknown operator: {$node->operator->value}"), + 'not' => new BooleanValue(!$argument->value), + default => throw new Exception("Unknown operator: {$node->operator->value}"), }; } + /** + * @return RuntimeValue + */ private function evaluateTernaryExpression(TernaryExpression $node, Environment $environment): RuntimeValue { $predicate = $this->evaluate($node->condition, $environment); - return $predicate->evaluateAsBool()->value + return $predicate->asBool()->value ? $this->evaluate($node->ifTrue, $environment) : $this->evaluate($node->ifFalse, $environment); } @@ -678,21 +779,28 @@ private function evalProgram(Program $program, Environment $environment): String */ private function evaluateBlock(array $statements, Environment $environment): StringValue { - $result = ""; + $result = ''; foreach ($statements as $statement) { $lastEvaluated = $this->evaluate($statement, $environment); if (!($lastEvaluated instanceof NullValue) && !($lastEvaluated instanceof UndefinedValue)) { - $result .= (string) $lastEvaluated->value; + $result .= (string)$lastEvaluated->value; } } + return new StringValue($result); } + /** + * @return RuntimeValue + */ private function evaluateIdentifier(Identifier $node, Environment $environment): RuntimeValue { return $environment->lookupVariable($node->value); } + /** + * @return RuntimeValue + */ private function evaluateCallExpression(CallExpression $expr, Environment $environment): RuntimeValue { [$args, $kwargs] = $this->evaluateArguments($expr->args, $environment); @@ -703,52 +811,62 @@ private function evaluateCallExpression(CallExpression $expr, Environment $envir $fn = $this->evaluate($expr->callee, $environment); if (!($fn instanceof FunctionValue)) { - throw new RuntimeException("Cannot call something that is not a function: got $fn->type"); + throw new RuntimeException("Cannot call something that is not a function: got {$fn->type}"); } return $fn->call($args, $environment); } + /** + * @param RuntimeValue $object + */ private function evaluateSliceExpression(RuntimeValue $object, SliceExpression $expr, Environment $environment): ArrayValue|StringValue { if (!($object instanceof ArrayValue || $object instanceof StringValue)) { - throw new RuntimeException("Slice object must be an array or string"); + throw new RuntimeException('Slice object must be an array or string'); } $start = $this->evaluate($expr->start, $environment); - $stop = $this->evaluate($expr->stop, $environment); - $step = $this->evaluate($expr->step, $environment); + $stop = $this->evaluate($expr->stop, $environment); + $step = $this->evaluate($expr->step, $environment); // Validate arguments if (!($start instanceof IntegerValue || $start instanceof UndefinedValue)) { - throw new RuntimeException("Slice start must be an integer or undefined"); + throw new RuntimeException('Slice start must be an integer or undefined'); } if (!($stop instanceof IntegerValue || $stop instanceof UndefinedValue)) { - throw new RuntimeException("Slice stop must be an integer or undefined"); + throw new RuntimeException('Slice stop must be an integer or undefined'); } if (!($step instanceof IntegerValue || $step instanceof UndefinedValue)) { - throw new RuntimeException("Slice step must be an integer or undefined"); + throw new RuntimeException('Slice step must be an integer or undefined'); } if ($object instanceof ArrayValue) { $sliced = slice($object->value, $start->value, $stop->value, $step->value); + return new ArrayValue($sliced); - } else { - $sliced = slice($object->value, $start->value, $stop->value, $step->value); - return new StringValue(implode("", $sliced)); } + + $sliced = slice(str_split($object->value), $start->value, $stop->value, $step->value); + + return new StringValue(implode('', $sliced)); } + /** + * @return RuntimeValue + */ private function evaluateMemberExpression(MemberExpression $expr, Environment $environment): RuntimeValue { $object = $this->evaluate($expr->object, $environment); if ($expr->computed) { - if ($expr->property->type == "SliceExpression") { - return $this->evaluateSliceExpression($object, $expr->property, $environment); - } else { - $property = $this->evaluate($expr->property, $environment); + if ($expr->property->type == 'SliceExpression') { + /** @var SliceExpression $slice */ + $slice = $expr->property; + + return $this->evaluateSliceExpression($object, $slice, $environment); } + $property = $this->evaluate($expr->property, $environment); } else { $property = new StringValue($expr->property->value); } @@ -759,10 +877,10 @@ private function evaluateMemberExpression(MemberExpression $expr, Environment $e } $value = $object->value[$property->value] ?? $object->builtins[$property->value] ?? new NullValue(); - } else if ($object instanceof ArrayValue || $object instanceof StringValue) { + } elseif ($object instanceof ArrayValue || $object instanceof StringValue) { if ($property instanceof IntegerValue) { - $index = $property->value; - $length = count($object->value); + $index = $property->value; + $length = $object instanceof ArrayValue ? count($object->value) : strlen($object->value); // Handle negative indices (Python-style) if ($index < 0) { @@ -778,7 +896,7 @@ private function evaluateMemberExpression(MemberExpression $expr, Environment $e if ($object instanceof StringValue) { $value = new StringValue($object->value[$index]); } - } else if ($property instanceof StringValue) { + } elseif ($property instanceof StringValue) { $value = $object->builtins[$property->value]; } else { throw new RuntimeException("Cannot access property with non-string/non-number: got {$property->type}"); @@ -804,10 +922,10 @@ private function evaluateSet(SetStatement $node, Environment $environment): Null throw new RuntimeException("Cannot unpack non-iterable type in set: got {$rhs->type}"); } - $assigneeValue = (array) $node->assignee->value; + $assigneeValue = (array)$node->assignee->value; if (count($assigneeValue) !== count($rhs->value)) { - throw new RuntimeException("Too " . (count($assigneeValue) > count($rhs->value) ? "few" : "many") . " items to unpack in set"); + throw new RuntimeException('Too ' . (count($assigneeValue) > count($rhs->value) ? 'few' : 'many') . ' items to unpack in set'); } foreach ($assigneeValue as $i => $identifier) { @@ -820,11 +938,11 @@ private function evaluateSet(SetStatement $node, Environment $environment): Null } elseif ($node->assignee instanceof MemberExpression) { $object = $this->evaluate($node->assignee->object, $environment); if (!($object instanceof ObjectValue)) { - throw new RuntimeException("Cannot assign to member of non-object"); + throw new RuntimeException('Cannot assign to member of non-object'); } $object->value[$node->assignee->property->value] = $rhs; } else { - throw new RuntimeException("Invalid LHS in assignment"); + throw new RuntimeException('Invalid LHS in assignment'); } return new NullValue(); @@ -833,11 +951,11 @@ private function evaluateSet(SetStatement $node, Environment $environment): Null private function evaluateIf(IfStatement $node, Environment $environment): StringValue { $test = $this->evaluate($node->test, $environment); - if ($test->evaluateAsBool()->value) { + if ($test->asBool()->value) { return $this->evaluateBlock($node->body, $environment); - } else { - return $this->evaluateBlock($node->alternate, $environment); } + + return $this->evaluateBlock($node->alternate, $environment); } private function evaluateFor(ForStatement $node, Environment $environment): StringValue @@ -847,40 +965,34 @@ private function evaluateFor(ForStatement $node, Environment $environment): Stri $iterable = $test = null; if ($node->iterable instanceof SelectExpression) { $iterable = $this->evaluate($node->iterable->lhs, $scope); - $test = $node->iterable->test; + $test = $node->iterable->test; } else { $iterable = $this->evaluate($node->iterable, $scope); } if (!($iterable instanceof ArrayValue || $iterable instanceof ObjectValue)) { - throw new RuntimeException("Expected iterable or object type in for loop: got $iterable->type"); - } - - if ($iterable instanceof ObjectValue) { - $iterable = array_keys($iterable->value); + throw new RuntimeException("Expected iterable or object type in for loop: got {$iterable->type}"); } - $items = []; + $items = []; $scopeUpdateFunctions = []; - foreach ($iterable->value as $i => $current) { + foreach ($iterable->value as $current) { $loopScope = new Environment($scope); - $current = $iterable->value[$i]; - $scopeUpdateFunction = null; if ($node->loopvar instanceof Identifier) { - $scopeUpdateFunction = fn($scope) => $scope->setVariable($node->loopvar->value, $current); + $scopeUpdateFunction = fn ($scope) => $scope->setVariable($node->loopvar->value, $current); } elseif ($node->loopvar instanceof TupleLiteral) { if (!($current instanceof ArrayValue)) { - throw new RuntimeException("Cannot unpack non-iterable type"); + throw new RuntimeException('Cannot unpack non-iterable type'); } - $loopVarLength = count((array) $node->loopvar->value); + $loopVarLength = count((array)$node->loopvar->value); $currentLength = count($current->value); if ($loopVarLength !== $currentLength) { - throw new RuntimeException(sprintf("Too %s items to unpack", $loopVarLength > $currentLength ? "few" : "many")); + throw new RuntimeException(sprintf('Too %s items to unpack', $loopVarLength > $currentLength ? 'few' : 'many')); } $scopeUpdateFunction = function ($scope) use ($node, $current) { @@ -899,33 +1011,33 @@ private function evaluateFor(ForStatement $node, Environment $environment): Stri $scopeUpdateFunction($loopScope); $testValue = $this->evaluate($test, $loopScope); - if (!$testValue->evaluateAsBool()->value) { + if (!$testValue->asBool()->value) { continue; } } - $items[] = $current; + $items[] = $current; $scopeUpdateFunctions[] = $scopeUpdateFunction; } - $result = ""; + $result = ''; $noIteration = true; - $length = count($items); + $length = count($items); - for ($i = 0; $i < $length; ++$i) { + for ($i = 0; $i < $length; $i++) { $loop = [ - "index" => new IntegerValue($i + 1), - "index0" => new IntegerValue($i), - "revindex" => new IntegerValue($length - $i), - "revindex0" => new IntegerValue($length - $i - 1), - "first" => new BooleanValue($i === 0), - "last" => new BooleanValue($i === $length - 1), - "length" => new IntegerValue($length), - "previtem" => $i > 0 ? $items[$i - 1] : new UndefinedValue(), - "nextitem" => $i < $length - 1 ? $items[$i + 1] : new UndefinedValue(), + 'index' => new IntegerValue($i + 1), + 'index0' => new IntegerValue($i), + 'revindex' => new IntegerValue($length - $i), + 'revindex0' => new IntegerValue($length - $i - 1), + 'first' => new BooleanValue($i === 0), + 'last' => new BooleanValue($i === $length - 1), + 'length' => new IntegerValue($length), + 'previtem' => $i > 0 ? $items[$i - 1] : new UndefinedValue(), + 'nextitem' => $i < $length - 1 ? $items[$i + 1] : new UndefinedValue(), ]; - $scope->setVariable("loop", new ObjectValue($loop)); + $scope->setVariable('loop', new ObjectValue($loop)); $scopeUpdateFunction = $scopeUpdateFunctions[$i]; $scopeUpdateFunction($scope); @@ -943,7 +1055,7 @@ private function evaluateFor(ForStatement $node, Environment $environment): Stri } if ($noIteration) { - $defaultEvaluated = $this->evaluateBlock($node->defaultBlock, $scope); + $defaultEvaluated = $this->evaluateBlock($node->defaultBlock ?? [], $scope); $result .= $defaultEvaluated->value; } @@ -952,9 +1064,9 @@ private function evaluateFor(ForStatement $node, Environment $environment): Stri private function evaluateMacro(Macro $node, Environment $environment): NullValue { - $environment->setVariable($node->name->value, new FunctionValue(function ($args, $scope) use ($node, $environment) { + $environment->setVariable($node->name->value, new FunctionValue(function ($args, $scope) use ($node) { $macroScope = new Environment($scope); - $args = array_slice($args, 0, -1); + $args = array_slice($args, 0, -1); $kwargs = $args[count($args) - 1]; @@ -962,9 +1074,9 @@ private function evaluateMacro(Macro $node, Environment $environment): NullValue $kwargs = null; } - for ($i = 0; $i < count($node->args); ++$i) { - $nodeArg = $node->args[$i]; - $passedArg = $args[$i]; + for ($i = 0; $i < count($node->args); $i++) { + $nodeArg = $node->args[$i]; + $passedArg = $args[$i] ?? null; if ($nodeArg instanceof Identifier) { if (!$passedArg) { @@ -991,7 +1103,7 @@ private function evaluateMacro(Macro $node, Environment $environment): NullValue private function evaluateCallStatement(CallStatement $node, Environment $environment): NullValue { - $callerFn = new FunctionValue(function ($args, $env) use ($node, $environment) { + $callerFn = new FunctionValue(function ($args, $env) use ($node) { $callerScope = new Environment($env); if ($node->args) { @@ -1008,23 +1120,27 @@ private function evaluateCallStatement(CallStatement $node, Environment $environ }); [$macroArgs, $macroKwargs] = $this->evaluateArguments($node->call->args, $environment); - $macroArgs[] = new KeywordArgumentsValue($macroKwargs); - $fn = $this->evaluate($node->call->callee, $environment); + $macroArgs[] = new KeywordArgumentsValue($macroKwargs); + $fn = $this->evaluate($node->call->callee, $environment); if (!($fn instanceof FunctionValue)) { throw new RuntimeException("Cannot call something that is not a function: got {$fn->type}"); } $newEnv = new Environment($environment); - $newEnv->setVariable("caller", $callerFn); + $newEnv->setVariable('caller', $callerFn); - return $fn->call($macroArgs, $newEnv); + $fn->call($macroArgs, $newEnv); + + return new NullValue(); } private function evaluateFilterStatement(FilterStatement $node, Environment $environment): StringValue { $rendered = $this->evaluateBlock($node->body, $environment); - return $this->applyFilter($rendered, $node->filter, $environment); + $result = $this->applyFilter($rendered, $node->filter, $environment); + + return $result instanceof StringValue ? $result : new StringValue((string)$result->value); } } diff --git a/src/Core/Lexer.php b/src/Core/Lexer.php index 9c35bfd..74001cf 100644 --- a/src/Core/Lexer.php +++ b/src/Core/Lexer.php @@ -2,20 +2,28 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Core; use Codewithkyrian\Jinja\Exceptions\SyntaxError; +use function end; +use function is_null; +use function preg_match; +use function preg_replace; +use function rtrim; +use function strlen; +use function substr; + class Lexer { /** * Preprocess a Jinja template string according to Jinja's default whitespace handling. * - * @param string $template The template string to preprocess. - * @param bool $lstripBlocks Whether to strip whitespace from the beginning of lines. - * @param bool $trimBlocks Whether to remove the first newline after template tags. - * @return string The preprocessed template string. + * @param string $template the template string to preprocess + * @param bool $lstripBlocks whether to strip whitespace from the beginning of lines + * @param bool $trimBlocks whether to remove the first newline after template tags + * + * @return string the preprocessed template string */ public static function preprocess(string $template, bool $lstripBlocks = false, bool $trimBlocks = false): string { @@ -24,35 +32,36 @@ public static function preprocess(string $template, bool $lstripBlocks = false, // Strip whitespace from block beginnings. if ($lstripBlocks) { - $template = preg_replace('/^[ \t]*({[#%-])/', '$1', $template); + $template = preg_replace('/^[ \t]*({[#%-])/', '$1', $template) ?? $template; } // Remove first newline after template tags if ($trimBlocks) { - $template = preg_replace('/([#%-]})\n/', '$1', $template); + $template = preg_replace('/([#%-]})\n/', '$1', $template) ?? $template; } // Process template string further based on options and Jinja standards - $template = preg_replace('/-%}\s*/', '%}', $template); - $template = preg_replace('/\s*{%-/', '{%', $template); - $template = preg_replace('/-}}\s*/', '}}', $template); - $template = preg_replace('/\s*{{-/', '{{', $template); - $template = preg_replace('/-#}\s*/', '#}', $template); - $template = preg_replace('/\s*{#-/', '{#', $template); + $template = preg_replace('/-%}\s*/', '%}', $template) ?? $template; + $template = preg_replace('/\s*{%-/', '{%', $template) ?? $template; + $template = preg_replace('/-}}\s*/', '}}', $template) ?? $template; + $template = preg_replace('/\s*{{-/', '{{', $template) ?? $template; + $template = preg_replace('/-#}\s*/', '#}', $template) ?? $template; + $template = preg_replace('/\s*{#-/', '{#', $template) ?? $template; // Handle the custom transformers-specific `generation` tag. // See https://github.com/huggingface/transformers/pull/30650 for more information. - $template = preg_replace('/{%\s*(end)?generation\s*%}/', '', $template); - - return $template; + return (string)(preg_replace('/{%\s*(end)?generation\s*%}/', '', $template) ?? $template); } /** * Generate a list of tokens from a source string. - * @param string $source The source string to be tokenized - * @param bool $lstripBlocks Whether to strip whitespace from the beginning of lines. - * @param bool $trimBlocks Whether to remove the first newline after template tags. + * + * @param string $source The source string to be tokenized + * @param bool $lstripBlocks whether to strip whitespace from the beginning of lines + * @param bool $trimBlocks whether to remove the first newline after template tags + * * @return Token[] + * * @throws SyntaxError */ public static function tokenize(string $source, bool $lstripBlocks = false, bool $trimBlocks = false): array @@ -62,92 +71,97 @@ public static function tokenize(string $source, bool $lstripBlocks = false, bool $src = self::preprocess($source, $lstripBlocks, $trimBlocks); - $cursorPosition = 0; + $cursorPosition = 0; $curlyBracketDepth = 0; - $srcLength = strlen($src); + $srcLength = strlen($src); - $isWord = fn($char) => preg_match('/\w/', $char) == 1; - $isInteger = fn($char) => preg_match('/[0-9]/', $char) == 1; + $isWord = fn ($char) => preg_match('/\w/', $char) == 1; + $isInteger = fn ($char) => preg_match('/[0-9]/', $char) == 1; - $consumeWhile = function (callable $predicate) use (&$src, &$cursorPosition, $srcLength) { - $str = ""; + $consumeWhile = function (callable $predicate) use (&$src, &$cursorPosition) { + $str = ''; while ($predicate($src[$cursorPosition])) { // Check for escaped characters - if ($src[$cursorPosition] === "\\") { + if ($src[$cursorPosition] === '\\') { // Consume the backslash - ++$cursorPosition; + $cursorPosition++; // Check for end of input if ($cursorPosition >= strlen($src)) { - throw new SyntaxError("Unexpected end of input"); + throw new SyntaxError('Unexpected end of input'); } // Add the escaped character - $escaped = $src[$cursorPosition++]; - $unescaped = Token::ESCAPE_CHARACTERS[$escaped] ?? throw new SyntaxError("Unexpected escaped character: $escaped"); + $escaped = $src[$cursorPosition++]; + $unescaped = Token::ESCAPE_CHARACTERS[$escaped] ?? throw new SyntaxError("Unexpected escaped character: {$escaped}"); $str .= $unescaped; // Adjust based on your ESCAPE_CHARACTERS handling + continue; } $str .= $src[$cursorPosition++]; - if ($cursorPosition >= strlen($src)) throw new SyntaxError("Unexpected end of input"); + if ($cursorPosition >= strlen($src)) { + throw new SyntaxError('Unexpected end of input'); + } } + return $str; }; - // Build each token until end of input while ($cursorPosition < $srcLength) { // First, consume all text that is outside a Jinja statement or expression $lastTokenType = end($tokens)->type ?? null; if ( - is_null($lastTokenType) - || $lastTokenType === TokenType::CloseStatement - || $lastTokenType === TokenType::CloseExpression - || $lastTokenType === TokenType::Comment + is_null($lastTokenType) || + $lastTokenType === TokenType::CloseStatement || + $lastTokenType === TokenType::CloseExpression || + $lastTokenType === TokenType::Comment ) { - $text = ""; + $text = ''; while ( - $cursorPosition < $srcLength - && !($src[$cursorPosition] === "{" && ($src[$cursorPosition + 1] === "%" || $src[$cursorPosition + 1] === "{" || $src[$cursorPosition + 1] === "#")) + $cursorPosition < $srcLength && + !($src[$cursorPosition] === '{' && ($src[$cursorPosition + 1] === '%' || $src[$cursorPosition + 1] === '{' || $src[$cursorPosition + 1] === '#')) ) { $text .= $src[$cursorPosition++]; } - if (strlen($text) > 0) { $tokens[] = new Token($text, TokenType::Text); + continue; } } // Possibly consume a comment - if ($src[$cursorPosition] === "{" && $src[$cursorPosition + 1] === "#") { + if ($src[$cursorPosition] === '{' && $src[$cursorPosition + 1] === '#') { $cursorPosition += 2; // Skip the opening {# - $comment = ""; - while ($src[$cursorPosition] !== "#" || $src[$cursorPosition + 1] !== "}") { + $comment = ''; + while ($src[$cursorPosition] !== '#' || $src[$cursorPosition + 1] !== '}') { // Check for end of input if ($cursorPosition + 2 >= $srcLength) { - throw new SyntaxError("Missing end of comment tag"); + throw new SyntaxError('Missing end of comment tag'); } $comment .= $src[$cursorPosition++]; } $tokens[] = new Token($comment, TokenType::Comment); $cursorPosition += 2; // Skip the closing #} + continue; } // Consume (and ignore) all whitespace inside Jinja statements or expressions - $consumeWhile(fn($char) => preg_match('/\s/', $char)); + $consumeWhile(fn ($char) => preg_match('/\s/', $char)); $char = $src[$cursorPosition]; // Check for unary operators - if ($char === "-" || $char === "+") { - $lastTokenType = end($tokens)->type; + if ($char === '-' || $char === '+') { + $lastToken = end($tokens); + $lastTokenType = $lastToken ? $lastToken->type : null; if ($lastTokenType === TokenType::Text || $lastTokenType === null) { - throw new SyntaxError("Unexpected character: $char"); + throw new SyntaxError("Unexpected character: {$char}"); } switch ($lastTokenType) { case TokenType::Identifier: @@ -169,9 +183,10 @@ public static function tokenize(string $source, bool $lstripBlocks = false, bool $num = $consumeWhile($isInteger); $tokens[] = new Token( - "$char$num", - strlen($num) > 0 ? TokenType::NumericLiteral : TokenType::UnaryOperator + "{$char}{$num}", + strlen($num) > 0 ? TokenType::NumericLiteral : TokenType::UnaryOperator, ); + continue 2; // Continue the outer loop. } } @@ -185,7 +200,7 @@ public static function tokenize(string $source, bool $lstripBlocks = false, bool // continue 2; // Continue the outer loop. // } - if ($sequence === "}}" && $curlyBracketDepth > 0) { + if ($sequence === '}}' && $curlyBracketDepth > 0) { continue; } $slice = substr($src, $cursorPosition, strlen($sequence)); @@ -194,32 +209,35 @@ public static function tokenize(string $source, bool $lstripBlocks = false, bool if ($type === TokenType::OpenExpression) { $curlyBracketDepth = 0; - } else if ($type === TokenType::OpenCurlyBracket) { - ++$curlyBracketDepth; - } else if ($type === TokenType::CloseCurlyBracket) { - --$curlyBracketDepth; + } elseif ($type === TokenType::OpenCurlyBracket) { + $curlyBracketDepth++; + } elseif ($type === TokenType::CloseCurlyBracket) { + $curlyBracketDepth--; } $cursorPosition += strlen($sequence); + continue 2; // Continue the outer loop. } } if ($char === "'" || $char === '"') { - ++$cursorPosition; // Skip the opening quote - $str = $consumeWhile(fn($c) => $c !== $char); + $cursorPosition++; // Skip the opening quote + $str = $consumeWhile(fn ($c) => $c !== $char); $tokens[] = new Token($str, TokenType::StringLiteral); - ++$cursorPosition; // Skip the closing quote + $cursorPosition++; // Skip the closing quote + continue; } if ($isInteger($char)) { $num = $consumeWhile($isInteger); - if ($src[$cursorPosition] === "." && $isInteger($src[$cursorPosition + 1])) { - ++$cursorPosition; // consume '.' + if ($src[$cursorPosition] === '.' && $isInteger($src[$cursorPosition + 1])) { + $cursorPosition++; // consume '.' $frac = $consumeWhile($isInteger); - $num = "$num.$frac"; + $num = "{$num}.{$frac}"; } $tokens[] = new Token($num, TokenType::NumericLiteral); + continue; } @@ -232,7 +250,7 @@ public static function tokenize(string $source, bool $lstripBlocks = false, bool } // Fallback error if character does not match any known token types - throw new SyntaxError("Unexpected character: $char"); + throw new SyntaxError("Unexpected character: {$char}"); } return $tokens; diff --git a/src/Core/Parser.php b/src/Core/Parser.php index 1d56450..5d63a33 100644 --- a/src/Core/Parser.php +++ b/src/Core/Parser.php @@ -4,27 +4,27 @@ namespace Codewithkyrian\Jinja\Core; -use SplObjectStorage; use Codewithkyrian\Jinja\AST\ArrayLiteral; use Codewithkyrian\Jinja\AST\BinaryExpression; use Codewithkyrian\Jinja\AST\BreakStatement; use Codewithkyrian\Jinja\AST\CallExpression; use Codewithkyrian\Jinja\AST\CallStatement; use Codewithkyrian\Jinja\AST\Comment; +use Codewithkyrian\Jinja\AST\ContinueStatement; +use Codewithkyrian\Jinja\AST\Expression; use Codewithkyrian\Jinja\AST\FilterExpression; +use Codewithkyrian\Jinja\AST\FilterStatement; +use Codewithkyrian\Jinja\AST\FloatLiteral; use Codewithkyrian\Jinja\AST\ForStatement; use Codewithkyrian\Jinja\AST\Identifier; use Codewithkyrian\Jinja\AST\IfStatement; +use Codewithkyrian\Jinja\AST\IntegerLiteral; use Codewithkyrian\Jinja\AST\KeywordArgumentExpression; use Codewithkyrian\Jinja\AST\Macro; use Codewithkyrian\Jinja\AST\MemberExpression; use Codewithkyrian\Jinja\AST\ObjectLiteral; use Codewithkyrian\Jinja\AST\Program; use Codewithkyrian\Jinja\AST\SelectExpression; -use Codewithkyrian\Jinja\AST\ContinueStatement; -use Codewithkyrian\Jinja\AST\FilterStatement; -use Codewithkyrian\Jinja\AST\FloatLiteral; -use Codewithkyrian\Jinja\AST\IntegerLiteral; use Codewithkyrian\Jinja\AST\SetStatement; use Codewithkyrian\Jinja\AST\SliceExpression; use Codewithkyrian\Jinja\AST\SpreadExpression; @@ -36,8 +36,13 @@ use Codewithkyrian\Jinja\AST\UnaryExpression; use Codewithkyrian\Jinja\Exceptions\ParserException; use Codewithkyrian\Jinja\Exceptions\SyntaxError; +use SplObjectStorage; -use function Codewithkyrian\Jinja\{array_every, array_some}; +use function Codewithkyrian\Jinja\array_every; +use function Codewithkyrian\Jinja\array_some; +use function count; +use function in_array; +use function str_contains; /** * Generate the Abstract Syntax Tree (AST) from a list of tokens. @@ -46,24 +51,40 @@ class Parser { /** @var Token[] */ private array $tokens; + private int $current = 0; + private Program $program; + public static function make(array $tokens): self + { + return new self($tokens); + } + public function __construct(array $tokens) { - $this->tokens = $tokens; + $this->tokens = $tokens; $this->program = new Program([]); } - public static function make(array $tokens): static + /** + * Generate the AST from the tokens. + */ + public function parse(): Program { - return new self($tokens); + while ($this->current < count($this->tokens)) { + $this->program->body[] = $this->parseAny(); + } + + return $this->program; } /** * Consume the next token if it matches the expected type, otherwise throw an error. - * @param TokenType $type The expected token type - * @param string $error The error message to throw if the token does not match the expected type + * + * @param TokenType $type The expected token type + * @param string $error The error message to throw if the token does not match the expected type + * * @return Token|mixed */ private function expect(TokenType $type, string $error) @@ -85,77 +106,67 @@ private function expectIdentifier(string $name): void { if (!$this->isIdentifier($name)) { $currentToken = $this->tokens[$this->current] ?? null; - $value = $currentToken?->value ?? "end of input"; + $value = $currentToken->value ?? 'end of input'; + throw new SyntaxError("Expected identifier {$name}, got {$value} instead"); } $this->current++; } - /** - * Generate the AST from the tokens. - */ - public function parse(): Program - { - while ($this->current < count($this->tokens)) { - $this->program->body[] = $this->parseAny(); - } - return $this->program; - } - - private function parseAny() + private function parseAny(): Comment|Statement|StringLiteral { if ($this->current >= count($this->tokens)) { - throw new ParserException("Unexpected end of input"); + throw new ParserException('Unexpected end of input'); } $token = $this->tokens[$this->current]; return match ($token->type) { - TokenType::Comment => new Comment($this->tokens[$this->current++]->value), - TokenType::Text => $this->parseText(), - TokenType::OpenStatement => $this->parseJinjaStatement(), + TokenType::Comment => new Comment($this->tokens[$this->current++]->value), + TokenType::Text => $this->parseText(), + TokenType::OpenStatement => $this->parseJinjaStatement(), TokenType::OpenExpression => $this->parseJinjaExpression(), - default => throw new ParserException("Unexpected token type: {$token->type}"), + default => throw new ParserException('Unexpected token type: ' . $token->type->name), }; } - private function not(...$types): bool + private function isNot(TokenType ...$types): bool { if ($this->current + count($types) > count($this->tokens)) { return true; // If we're past the end, we're "not" any of these types } - return array_some($types, fn($type, $i) => $type !== $this->tokens[$this->current + $i]->type); + + return array_some($types, fn ($type, $i) => $type !== $this->tokens[$this->current + $i]->type); } - private function is(...$types): bool + private function is(TokenType ...$types): bool { if ($this->current + count($types) > count($this->tokens)) { return false; // If we're past the end, we can't match any types } - return array_every($types, fn($type, $i) => $type === $this->tokens[$this->current + $i]->type); + + return array_every($types, fn ($type, $i) => $type === $this->tokens[$this->current + $i]->type); } private function isIdentifier(string ...$names): bool { - return ( - $this->current + count($names) <= count($this->tokens) && - array_every($names, fn($name, $i) => $this->tokens[$this->current + $i]->type === TokenType::Identifier && $this->tokens[$this->current + $i]->value === $name) - ); + return + $this->current + count($names) <= count($this->tokens) && + array_every($names, fn ($name, $i) => $this->tokens[$this->current + $i]->type === TokenType::Identifier && $this->tokens[$this->current + $i]->value === $name); } private function isStatement(string ...$names): bool { - return ( + return isset($this->tokens[$this->current], $this->tokens[$this->current + 1]) && $this->tokens[$this->current]->type === TokenType::OpenStatement && $this->tokens[$this->current + 1]->type === TokenType::Identifier && - in_array($this->tokens[$this->current + 1]->value, $names) - ); + in_array($this->tokens[$this->current + 1]->value, $names); } private function parseText(): StringLiteral { - return new StringLiteral($this->expect(TokenType::Text, "Expected text token")->value); + return new StringLiteral($this->expect(TokenType::Text, 'Expected text token')->value); } /** @@ -164,7 +175,7 @@ private function parseText(): StringLiteral private function parseJinjaStatement(): Statement { // Consume {% token - $this->expect(TokenType::OpenStatement, "Expected opening statement token"); + $this->expect(TokenType::OpenStatement, 'Expected opening statement token'); $token = $this->tokens[$this->current]; @@ -173,68 +184,73 @@ private function parseJinjaStatement(): Statement } switch ($token->value) { - case "set": + case 'set': $this->current++; $result = $this->parseSetStatement(); + break; - case "if": + case 'if': $this->current++; $result = $this->parseIfStatement(); - $this->expect(TokenType::OpenStatement, "Expected {% token"); - $this->expectIdentifier("endif"); - $this->expect(TokenType::CloseStatement, "Expected %} token"); + $this->expect(TokenType::OpenStatement, 'Expected {% token'); + $this->expectIdentifier('endif'); + $this->expect(TokenType::CloseStatement, 'Expected %} token'); + break; - case "macro": + case 'macro': $this->current++; $result = $this->parseMacroStatement(); - $this->expect(TokenType::OpenStatement, "Expected {% token"); - $this->expectIdentifier("endmacro"); - $this->expect(TokenType::CloseStatement, "Expected %} token"); + $this->expect(TokenType::OpenStatement, 'Expected {% token'); + $this->expectIdentifier('endmacro'); + $this->expect(TokenType::CloseStatement, 'Expected %} token'); + break; - case "for": + case 'for': $this->current++; $result = $this->parseForStatement(); - $this->expect(TokenType::OpenStatement, "Expected {% token"); - $this->expectIdentifier("endfor"); - $this->expect(TokenType::CloseStatement, "Expected %} token"); + $this->expect(TokenType::OpenStatement, 'Expected {% token'); + $this->expectIdentifier('endfor'); + $this->expect(TokenType::CloseStatement, 'Expected %} token'); break; - case "call": + case 'call': $this->current++; $result = $this->parseCallStatement(); - $this->expect(TokenType::OpenStatement, "Expected {% token"); - $this->expectIdentifier("endcall"); - $this->expect(TokenType::CloseStatement, "Expected %} token"); + $this->expect(TokenType::OpenStatement, 'Expected {% token'); + $this->expectIdentifier('endcall'); + $this->expect(TokenType::CloseStatement, 'Expected %} token'); break; - case "break": + case 'break': $this->current++; - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); $result = new BreakStatement(); + break; - case "continue": + case 'continue': $this->current++; - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); $result = new ContinueStatement(); + break; - case "filter": + case 'filter': $this->current++; $result = $this->parseFilterStatement(); - $this->expect(TokenType::OpenStatement, "Expected {% token"); - $this->expectIdentifier("endfilter"); - $this->expect(TokenType::CloseStatement, "Expected %} token"); + $this->expect(TokenType::OpenStatement, 'Expected {% token'); + $this->expectIdentifier('endfilter'); + $this->expect(TokenType::CloseStatement, 'Expected %} token'); break; @@ -248,36 +264,45 @@ private function parseJinjaStatement(): Statement private function parseJinjaExpression(): Statement { // Consume {{ }} tokens - $this->expect(TokenType::OpenExpression, "Expected opening expression token"); + $this->expect(TokenType::OpenExpression, 'Expected opening expression token'); $result = $this->parseExpression(); - $this->expect(TokenType::CloseExpression, "Expected closing expression token"); + $this->expect(TokenType::CloseExpression, 'Expected closing expression token'); return $result; } private function parseSetStatement(): Statement { - $left = $this->parseExpressionSequence(); + $left = $this->parseExpressionSequence(); $value = null; - $body = []; + $body = []; if ($this->is(TokenType::Equals)) { $this->current++; $value = $this->parseExpressionSequence(); } else { - $this->expect(TokenType::CloseStatement, "Expected %} token"); + $this->expect(TokenType::CloseStatement, 'Expected %} token'); - while (!$this->isStatement("endset")) { + while (!$this->isStatement('endset')) { $body[] = $this->parseAny(); } - $this->expect(TokenType::OpenStatement, "Expected {% token"); - $this->expectIdentifier("endset"); + $this->expect(TokenType::OpenStatement, 'Expected {% token'); + $this->expectIdentifier('endset'); + } + + if (!($left instanceof Expression)) { + throw new SyntaxError('Expected expression for set statement assignee'); + } + + if ($value !== null && !($value instanceof Expression)) { + throw new SyntaxError('Expected expression for set statement value'); } - $this->expect(TokenType::CloseStatement, "Expected %} token"); + $this->expect(TokenType::CloseStatement, 'Expected %} token'); + return new SetStatement($left, $value, $body); } @@ -285,31 +310,32 @@ private function parseIfStatement(): IfStatement { $test = $this->parseExpression(); - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); - $body = []; + $body = []; $alternate = []; // Keep parsing if body until we reach the first {% elif %} or {% else %} or {% endif %} - while (!$this->isStatement("elif", "else", "endif")) { + while (!$this->isStatement('elif', 'else', 'endif')) { $body[] = $this->parseAny(); } // Alternate branch: Check for {% elif %} or {% else %} - if ($this->isStatement("elif")) { + if ($this->isStatement('elif')) { $this->current++; // consume {% $this->current++; // consume elif $alternate[] = $this->parseIfStatement(); - } else if ($this->isStatement("else")) { + } elseif ($this->isStatement('else')) { $this->current++; // consume {% $this->current++; // consume else - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); - while (!$this->isStatement("endif")) { + while (!$this->isStatement('endif')) { $alternate[] = $this->parseAny(); } } + /* @var Expression $test */ return new IfStatement($test, $body, $alternate); } @@ -317,16 +343,16 @@ private function parseMacroStatement(): Macro { $name = $this->parsePrimaryExpression(); if (!($name instanceof Identifier)) { - throw new SyntaxError("Expected identifier following macro statement"); + throw new SyntaxError('Expected identifier following macro statement'); } $args = $this->parseArgs(); - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); $body = []; // Keep going until we hit {% endmacro %} - while (!$this->isStatement("endmacro")) { + while (!$this->isStatement('endmacro')) { $body[] = $this->parseAny(); } @@ -335,18 +361,19 @@ private function parseMacroStatement(): Macro private function parseExpressionSequence(bool $primary = false): Statement { - $fn = $primary ? [$this, 'parsePrimaryExpression'] : [$this, 'parseExpression']; + $fn = $primary ? [$this, 'parsePrimaryExpression'] : [$this, 'parseExpression']; $expressions = [$fn()]; - $isTuple = $this->is(TokenType::Comma); + $isTuple = $this->is(TokenType::Comma); while ($isTuple) { $this->current++; // consume comma $expressions[] = $fn(); - if (!$this->is(TokenType::Comma)) { + if ($this->isNot(TokenType::Comma)) { break; } } + /* @var Expression[] $expressions */ return $isTuple ? new TupleLiteral($expressions) : $expressions[0]; } @@ -360,35 +387,36 @@ private function parseForStatement(): ForStatement throw new SyntaxError("Expected identifier/tuple for the loop variable, got {$loopVariable->type} instead"); } - if (!$this->isIdentifier("in")) { - throw new SyntaxError("Expected `in` keyword following loop variable"); + if (!$this->isIdentifier('in')) { + throw new SyntaxError('Expected `in` keyword following loop variable'); } $this->current++; // consume in $iterable = $this->parseExpression(); - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); $body = []; // Keep going until we hit {% endfor or {% else %} - while (!$this->isStatement("endfor", "else")) { + while (!$this->isStatement('endfor', 'else')) { $body[] = $this->parseAny(); } $alternate = []; - if ($this->isStatement("else")) { + if ($this->isStatement('else')) { $this->current++; // consume {% $this->current++; // consume else - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); // Keep going until we hit {% endfor %} - while (!$this->isStatement("endfor")) { + while (!$this->isStatement('endfor')) { $alternate[] = $this->parseAny(); } } + /* @var Expression $iterable */ return new ForStatement($loopVariable, $iterable, $body, $alternate); } @@ -402,19 +430,20 @@ private function parseCallStatement(): Statement $callee = $this->parsePrimaryExpression(); if (!($callee instanceof Identifier)) { - throw new SyntaxError("Expected identifier following call statement"); + throw new SyntaxError('Expected identifier following call statement'); } $args = $this->parseArgs(); - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); $body = []; - while (!$this->isStatement("endcall")) { + while (!$this->isStatement('endcall')) { $body[] = $this->parseAny(); } - $expression = new CallExpression($callee, $args); + /** @var Expression[] $args */ + $expression = new CallExpression($callee, $args); return new CallStatement($expression, $args, $body); } @@ -426,89 +455,97 @@ private function parseFilterStatement(): Statement $filter = $this->parseCallExpression($filter); } - $this->expect(TokenType::CloseStatement, "Expected closing statement token"); + if (!($filter instanceof Identifier) && !($filter instanceof CallExpression)) { + throw new SyntaxError('Expected identifier or call expression for filter statement'); + } + + $this->expect(TokenType::CloseStatement, 'Expected closing statement token'); $body = []; - while (!$this->isStatement("endfilter")) { + while (!$this->isStatement('endfilter')) { $body[] = $this->parseAny(); } return new FilterStatement($filter, $body); } - private function parseExpression(): Statement + private function parseExpression(): Expression { // Choose parse function with the lowest precedence return $this->parseIfExpression(); } - private function parseIfExpression(): Statement + private function parseIfExpression(): Expression { $trueExpression = $this->parseLogicalOrExpression(); - if ($this->isIdentifier("if")) { + if ($this->isIdentifier('if')) { $this->current++; $test = $this->parseLogicalOrExpression(); - if ($this->isIdentifier("else")) { + if ($this->isIdentifier('else')) { $this->current++; // consume else $falseExpression = $this->parseIfExpression(); + return new TernaryExpression($test, $trueExpression, $falseExpression); - } else { - return new SelectExpression($trueExpression, $test); } + + return new SelectExpression($trueExpression, $test); } + return $trueExpression; } - private function parseLogicalOrExpression(): Statement + private function parseLogicalOrExpression(): Expression { $left = $this->parseLogicalAndExpression(); - while ($this->isIdentifier("or")) { + while ($this->isIdentifier('or')) { $operator = $this->tokens[$this->current]; $this->current++; $right = $this->parseLogicalAndExpression(); - $left = new BinaryExpression($operator, $left, $right); + $left = new BinaryExpression($operator, $left, $right); } return $left; } - private function parseLogicalAndExpression(): Statement + private function parseLogicalAndExpression(): Expression { $left = $this->parseLogicalNegationExpression(); - while ($this->isIdentifier("and")) { + while ($this->isIdentifier('and')) { $operator = $this->tokens[$this->current]; $this->current++; $right = $this->parseLogicalNegationExpression(); - $left = new BinaryExpression($operator, $left, $right); + $left = new BinaryExpression($operator, $left, $right); } + return $left; } - private function parseLogicalNegationExpression(): Statement + private function parseLogicalNegationExpression(): Expression { $right = null; - while ($this->isIdentifier("not")) { + while ($this->isIdentifier('not')) { $operator = $this->tokens[$this->current]; $this->current++; - $arg = $this->parseLogicalNegationExpression(); + $arg = $this->parseLogicalNegationExpression(); $right = new UnaryExpression($operator, $arg); } + return $right ?: $this->parseComparisonExpression(); } - private function parseComparisonExpression(): Statement + private function parseComparisonExpression(): Expression { $left = $this->parseAdditiveExpression(); while (true) { - if ($this->isIdentifier("not", "in")) { - $operator = new Token("not in", TokenType::Identifier); + if ($this->isIdentifier('not', 'in')) { + $operator = new Token('not in', TokenType::Identifier); $this->current += 2; - } elseif ($this->isIdentifier("in")) { + } elseif ($this->isIdentifier('in')) { $operator = $this->tokens[$this->current++]; } elseif ($this->is(TokenType::ComparisonBinaryOperator)) { $operator = $this->tokens[$this->current++]; @@ -517,25 +554,26 @@ private function parseComparisonExpression(): Statement } $right = $this->parseAdditiveExpression(); - $left = new BinaryExpression($operator, $left, $right); + $left = new BinaryExpression($operator, $left, $right); } return $left; } - private function parseAdditiveExpression(): Statement + private function parseAdditiveExpression(): Expression { $left = $this->parseMultiplicativeExpression(); while ($this->is(TokenType::AdditiveBinaryOperator)) { $operator = $this->tokens[$this->current]; $this->current++; $right = $this->parseMultiplicativeExpression(); - $left = new BinaryExpression($operator, $left, $right); + $left = new BinaryExpression($operator, $left, $right); } + return $left; } - private function parseCallMemberExpression(): Statement + private function parseCallMemberExpression(): Expression { $member = $this->parseMemberExpression($this->parsePrimaryExpression()); @@ -546,9 +584,10 @@ private function parseCallMemberExpression(): Statement return $member; } - private function parseCallExpression(Statement $callee): CallExpression + private function parseCallExpression(Expression $callee): Expression { - $expression = new CallExpression($callee, $this->parseArgs()); + $args = $this->parseArgs(); + $expression = new CallExpression($callee, $args); $expression = $this->parseMemberExpression($expression); // foo->x()->y @@ -560,18 +599,20 @@ private function parseCallExpression(Statement $callee): CallExpression } /** - * @return Statement[] + * @return Expression[] */ private function parseArgs(): array { - $this->expect(TokenType::OpenParen, "Expected opening parenthesis for arguments list"); + $this->expect(TokenType::OpenParen, 'Expected opening parenthesis for arguments list'); $args = $this->parseArgumentsList(); - $this->expect(TokenType::CloseParen, "Expected closing parenthesis for arguments list"); + $this->expect(TokenType::CloseParen, 'Expected closing parenthesis for arguments list'); + return $args; } /** - * @return Statement[] + * @return Expression[] + * * @throws SyntaxError */ private function parseArgumentsList(): array @@ -581,12 +622,12 @@ private function parseArgumentsList(): array while (!$this->is(TokenType::CloseParen)) { // unpacking *expr if ( - $this->tokens[$this->current]->type === TokenType::MultiplicativeBinaryOperator - && $this->tokens[$this->current]->value === "*" + $this->tokens[$this->current]->type === TokenType::MultiplicativeBinaryOperator && + $this->tokens[$this->current]->value === '*' ) { $this->current++; // consume asterisk $expression = $this->parseExpression(); - $argument = new SpreadExpression($expression); + $argument = new SpreadExpression($expression); } else { $argument = $this->parseExpression(); @@ -595,9 +636,9 @@ private function parseArgumentsList(): array // e.g., func(x = 5, y = a or b) $this->current++; // consume equals if (!($argument instanceof Identifier)) { - throw new SyntaxError("Expected identifier for keyword argument"); + throw new SyntaxError('Expected identifier for keyword argument'); } - $value = $this->parseExpression(); + $value = $this->parseExpression(); $argument = new KeywordArgumentExpression($argument, $value); } } @@ -611,41 +652,46 @@ private function parseArgumentsList(): array return $arguments; } - private function parseMemberExpressionArgumentsList(): Statement + private function parseMemberExpressionArgumentsList(): Expression { - $slices = []; + $slices = []; $isSlice = false; - while (!$this->is(TokenType::CloseSquareBracket)) { + while ($this->isNot(TokenType::CloseSquareBracket)) { if ($this->is(TokenType::Colon)) { $slices[] = null; $this->current++; // consume colon $isSlice = true; } else { $slices[] = $this->parseExpression(); + /* @phpstan-ignore-next-line */ if ($this->is(TokenType::Colon)) { $this->current++; // consume colon $isSlice = true; } } } + if (empty($slices)) { - throw new SyntaxError("Expected at least one argument for member/slice expression"); + throw new SyntaxError('Expected at least one argument for member/slice expression'); } if ($isSlice) { if (count($slices) > 3) { - throw new SyntaxError("Expected 0-3 arguments for slice expression"); + throw new SyntaxError('Expected 0-3 arguments for slice expression'); } return new SliceExpression(...$slices); } + if ($slices[0] === null) { + throw new SyntaxError('Invalid array access syntax - array access must contain an expression, e.g. array[0] or array["key"]'); + } + return $slices[0]; } - private function parseMemberExpression(Statement $object): Statement + private function parseMemberExpression(Expression $object): Expression { - while ($this->is(TokenType::Dot) || $this->is(TokenType::OpenSquareBracket)) { $operator = $this->tokens[$this->current]; // . or [ $this->current++; // assume operator token is consumed @@ -654,38 +700,40 @@ private function parseMemberExpression(Statement $object): Statement if ($computed) { // computed (i.e., bracket notation: obj[expr]) $property = $this->parseMemberExpressionArgumentsList(); - $this->expect(TokenType::CloseSquareBracket, "Expected closing square bracket"); + $this->expect(TokenType::CloseSquareBracket, 'Expected closing square bracket'); } else { // non-computed (i.e., dot notation: obj.expr) $property = $this->parsePrimaryExpression(); - if ($property->type !== "Identifier") { - throw new SyntaxError("Expected identifier following dot operator"); + if ($property->type !== 'Identifier') { + throw new SyntaxError('Expected identifier following dot operator'); } } $object = new MemberExpression($object, $property, $computed); } + return $object; } - private function parseMultiplicativeExpression(): Statement + private function parseMultiplicativeExpression(): Expression { $left = $this->parseTestExpression(); while ($this->is(TokenType::MultiplicativeBinaryOperator)) { $operator = $this->tokens[$this->current]; $this->current++; $right = $this->parseTestExpression(); - $left = new BinaryExpression($operator, $left, $right); + $left = new BinaryExpression($operator, $left, $right); } + return $left; } - private function parseTestExpression(): Statement + private function parseTestExpression(): Expression { $operand = $this->parseFilterExpression(); while ($this->isIdentifier('is')) { $this->current++; // consume is - $negate = $this->isIdentifier("not"); + $negate = $this->isIdentifier('not'); if ($negate) { $this->current++; // consume not @@ -694,35 +742,40 @@ private function parseTestExpression(): Statement $filter = $this->parsePrimaryExpression(); if (!($filter instanceof Identifier)) { - throw new SyntaxError("Expected identifier for the test"); + throw new SyntaxError('Expected identifier for the test'); } $operand = new TestExpression($operand, $negate, $filter); } + return $operand; } - private function parseFilterExpression(): Statement + private function parseFilterExpression(): Expression { $operand = $this->parseCallMemberExpression(); while ($this->is(TokenType::Pipe)) { $this->current++; // consume pipe $filter = $this->parsePrimaryExpression(); - if (!($filter instanceof Identifier)) { - throw new SyntaxError("Expected identifier for the filter"); - } + if ($this->is(TokenType::OpenParen)) { $filter = $this->parseCallExpression($filter); } + + if (!($filter instanceof Identifier || $filter instanceof CallExpression)) { + throw new SyntaxError('Filter must be an identifier or a function call'); + } + $operand = new FilterExpression($operand, $filter); } + return $operand; } - private function parsePrimaryExpression(): Statement + private function parsePrimaryExpression(): Expression { if ($this->current >= count($this->tokens)) { - throw new SyntaxError("Unexpected end of input"); + throw new SyntaxError('Unexpected end of input'); } $token = $this->tokens[$this->current++]; @@ -730,9 +783,9 @@ private function parsePrimaryExpression(): Statement case TokenType::NumericLiteral: $value = $token->value; - return str_contains($value, ".") - ? new FloatLiteral(floatval($value)) - : new IntegerLiteral(intval($value)); + return str_contains($value, '.') + ? new FloatLiteral((float)$value) + : new IntegerLiteral((int)$value); case TokenType::StringLiteral: $value = $token->value; @@ -746,8 +799,10 @@ private function parsePrimaryExpression(): Statement return new Identifier($token->value); case TokenType::OpenParen: + /** @var Expression $expression */ $expression = $this->parseExpressionSequence(); $this->expect(TokenType::CloseParen, "Expected closing parenthesis, got {$this->tokens[$this->current]->type->value} instead"); + return $expression; case TokenType::OpenSquareBracket: @@ -760,13 +815,15 @@ private function parsePrimaryExpression(): Statement } } $this->current++; // consume closing square bracket + return new ArrayLiteral($values); case TokenType::OpenCurlyBracket: + /** @var SplObjectStorage $values */ $values = new SplObjectStorage(); while (!$this->is(TokenType::CloseCurlyBracket)) { $key = $this->parseExpression(); - $this->expect(TokenType::Colon, "Expected colon between key and value in object literal"); + $this->expect(TokenType::Colon, 'Expected colon between key and value in object literal'); $value = $this->parseExpression(); $values->attach($key, $value); if ($this->is(TokenType::Comma)) { @@ -774,10 +831,11 @@ private function parsePrimaryExpression(): Statement } } $this->current++; // consume closing curly bracket + return new ObjectLiteral($values); default: - throw new SyntaxError("Unexpected token: {$token->type}"); + throw new SyntaxError('Unexpected token: ' . $token->type->name); } } } diff --git a/src/Core/Token.php b/src/Core/Token.php index 6731ab4..2514f59 100644 --- a/src/Core/Token.php +++ b/src/Core/Token.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Core; /** @@ -12,58 +11,58 @@ class Token { public const ORDERED_MAPPING_TABLE = [ // Control sequences - ["{%", TokenType::OpenStatement], - ["%}", TokenType::CloseStatement], - ["{{", TokenType::OpenExpression], - ["}}", TokenType::CloseExpression], + ['{%', TokenType::OpenStatement], + ['%}', TokenType::CloseStatement], + ['{{', TokenType::OpenExpression], + ['}}', TokenType::CloseExpression], // Single character tokens - ["(", TokenType::OpenParen], - [")", TokenType::CloseParen], - ["{", TokenType::OpenCurlyBracket], - ["}", TokenType::CloseCurlyBracket], - ["[", TokenType::OpenSquareBracket], - ["]", TokenType::CloseSquareBracket], - [",", TokenType::Comma], - [".", TokenType::Dot], - [":", TokenType::Colon], - ["|", TokenType::Pipe], + ['(', TokenType::OpenParen], + [')', TokenType::CloseParen], + ['{', TokenType::OpenCurlyBracket], + ['}', TokenType::CloseCurlyBracket], + ['[', TokenType::OpenSquareBracket], + [']', TokenType::CloseSquareBracket], + [',', TokenType::Comma], + ['.', TokenType::Dot], + [':', TokenType::Colon], + ['|', TokenType::Pipe], // Comparison operators - ["<=", TokenType::ComparisonBinaryOperator], - [">=", TokenType::ComparisonBinaryOperator], - ["==", TokenType::ComparisonBinaryOperator], - ["!=", TokenType::ComparisonBinaryOperator], - ["<", TokenType::ComparisonBinaryOperator], - [">", TokenType::ComparisonBinaryOperator], + ['<=', TokenType::ComparisonBinaryOperator], + ['>=', TokenType::ComparisonBinaryOperator], + ['==', TokenType::ComparisonBinaryOperator], + ['!=', TokenType::ComparisonBinaryOperator], + ['<', TokenType::ComparisonBinaryOperator], + ['>', TokenType::ComparisonBinaryOperator], // Arithmetic operators - ["+", TokenType::AdditiveBinaryOperator], - ["-", TokenType::AdditiveBinaryOperator], - ["~", TokenType::AdditiveBinaryOperator], - ["*", TokenType::MultiplicativeBinaryOperator], - ["/", TokenType::MultiplicativeBinaryOperator], - ["%", TokenType::MultiplicativeBinaryOperator], + ['+', TokenType::AdditiveBinaryOperator], + ['-', TokenType::AdditiveBinaryOperator], + ['~', TokenType::AdditiveBinaryOperator], + ['*', TokenType::MultiplicativeBinaryOperator], + ['/', TokenType::MultiplicativeBinaryOperator], + ['%', TokenType::MultiplicativeBinaryOperator], // Assignment operator - ["=", TokenType::Equals], + ['=', TokenType::Equals], ]; public const ESCAPE_CHARACTERS = [ - "n" => "\n", // New line - "t" => "\t", // Horizontal tab - "r" => "\r", // Carriage return - "b" => "\b", // Backspace - "f" => "\f", // Form feed - "v" => "\v", // Vertical tab - "'" => "'", // Single quote - '"' => '"', // Double quote - "\\" => "\\", // Backslash + 'n' => "\n", // New line + 't' => "\t", // Horizontal tab + 'r' => "\r", // Carriage return + 'b' => "\b", // Backspace + 'f' => "\f", // Form feed + 'v' => "\v", // Vertical tab + "'" => "'", // Single quote + '"' => '"', // Double quote + '\\' => '\\', // Backslash ]; - public function __construct( - public readonly string $value, - public readonly TokenType $type - ) {} + public readonly string $value, + public readonly TokenType $type, + ) { + } } diff --git a/src/Core/TokenType.php b/src/Core/TokenType.php index 597a482..98900e0 100644 --- a/src/Core/TokenType.php +++ b/src/Core/TokenType.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Core; /** @@ -10,30 +9,30 @@ */ enum TokenType: string { - case Text = "Text"; - case NumericLiteral = "NumericLiteral"; // e.g., 123 - case StringLiteral = "StringLiteral"; // 'string' - case Identifier = "Identifier"; // Variables, functions, statements, booleans, etc. - case Equals = "Equals"; // = - case OpenParen = "OpenParen"; // ( - case CloseParen = "CloseParen"; // ) - case OpenStatement = "OpenStatement"; // {% - case CloseStatement = "CloseStatement"; // %} - case OpenExpression = "OpenExpression"; // {{ - case CloseExpression = "CloseExpression"; // }} - case OpenSquareBracket = "OpenSquareBracket"; // [ - case CloseSquareBracket = "CloseSquareBracket"; // ] - case OpenCurlyBracket = "OpenCurlyBracket"; // { - case CloseCurlyBracket = "CloseCurlyBracket"; // } - case Comma = "Comma"; // , - case Dot = "Dot"; // . - case Colon = "Colon"; // : - case Pipe = "Pipe"; // | + case Text = 'Text'; + case NumericLiteral = 'NumericLiteral'; // e.g., 123 + case StringLiteral = 'StringLiteral'; // 'string' + case Identifier = 'Identifier'; // Variables, functions, statements, booleans, etc. + case Equals = 'Equals'; // = + case OpenParen = 'OpenParen'; // ( + case CloseParen = 'CloseParen'; // ) + case OpenStatement = 'OpenStatement'; // {% + case CloseStatement = 'CloseStatement'; // %} + case OpenExpression = 'OpenExpression'; // {{ + case CloseExpression = 'CloseExpression'; // }} + case OpenSquareBracket = 'OpenSquareBracket'; // [ + case CloseSquareBracket = 'CloseSquareBracket'; // ] + case OpenCurlyBracket = 'OpenCurlyBracket'; // { + case CloseCurlyBracket = 'CloseCurlyBracket'; // } + case Comma = 'Comma'; // , + case Dot = 'Dot'; // . + case Colon = 'Colon'; // : + case Pipe = 'Pipe'; // | - case CallOperator = "CallOperator"; // () - case AdditiveBinaryOperator = "AdditiveBinaryOperator"; // + - ~ - case MultiplicativeBinaryOperator = "MultiplicativeBinaryOperator"; // * / % - case ComparisonBinaryOperator = "ComparisonBinaryOperator"; // < > <= >= == != - case UnaryOperator = "UnaryOperato"; // ! - + - case Comment = "Comment"; // # + case CallOperator = 'CallOperator'; // () + case AdditiveBinaryOperator = 'AdditiveBinaryOperator'; // + - ~ + case MultiplicativeBinaryOperator = 'MultiplicativeBinaryOperator'; // * / % + case ComparisonBinaryOperator = 'ComparisonBinaryOperator'; // < > <= >= == != + case UnaryOperator = 'UnaryOperato'; // ! - + + case Comment = 'Comment'; // # } diff --git a/src/Core/Utils.php b/src/Core/Utils.php index f0fd513..02723bc 100644 --- a/src/Core/Utils.php +++ b/src/Core/Utils.php @@ -4,6 +4,13 @@ namespace Codewithkyrian\Jinja; +use function count; +use function is_null; +use function max; +use function min; +use function strtolower; +use function ucwords; + function array_some(array $array, callable $callback): bool { foreach ($array as $key => $value) { @@ -11,6 +18,7 @@ function array_some(array $array, callable $callback): bool return true; } } + return false; } @@ -21,6 +29,7 @@ function array_every(array $array, callable $callback): bool return false; } } + return true; } @@ -31,29 +40,30 @@ function toTitleCase(string $str): string /** * Function that mimics Python's array slicing. - * @param array $array The array to slice. - * @param ?int $start The start index of the slice. Defaults to 0. - * @param ?int $stop The last index of the slice. Defaults to the length of the array - * @param int $step The step value of the slice. Defaults to 1. - * @return array + * + * @param array $array the array to slice + * @param ?int $start The start index of the slice. Defaults to 0. + * @param ?int $stop The last index of the slice. Defaults to the length of the array + * @param int $step The step value of the slice. Defaults to 1. */ function slice(array $array, ?int $start = null, ?int $stop = null, ?int $step = null): array { $step ??= 1; - $length = count($array); + $length = count($array); $direction = $step >= 0 ? 1 : -1; if ($direction >= 0) { $start = is_null($start) ? 0 : ($start < 0 ? max($length + $start, 0) : min($start, $length)); - $stop = is_null($stop) ? $length : ($stop < 0 ? max($length + $stop, 0) : min($stop, $length)); + $stop = is_null($stop) ? $length : ($stop < 0 ? max($length + $stop, 0) : min($stop, $length)); } else { $start = is_null($start) ? $length - 1 : ($start < 0 ? max($length + $start, -1) : min($start, $length - 1)); - $stop = is_null($stop) ? -1 : ($stop < -1 ? max($length + $stop, -1) : min($stop, $length - 1)); + $stop = is_null($stop) ? -1 : ($stop < -1 ? max($length + $stop, -1) : min($stop, $length - 1)); } $result = []; for ($i = $start; $direction * $i < $direction * $stop; $i += $step) { $result[] = $array[$i]; } + return $result; } diff --git a/src/Exceptions/ParserException.php b/src/Exceptions/ParserException.php index 4236f7d..9fe5fc7 100644 --- a/src/Exceptions/ParserException.php +++ b/src/Exceptions/ParserException.php @@ -2,10 +2,10 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Exceptions; -class ParserException extends \Exception -{ +use Exception; -} \ No newline at end of file +class ParserException extends Exception +{ +} diff --git a/src/Exceptions/RuntimeException.php b/src/Exceptions/RuntimeException.php index 3903962..2cfcd38 100644 --- a/src/Exceptions/RuntimeException.php +++ b/src/Exceptions/RuntimeException.php @@ -2,10 +2,10 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Exceptions; -class RuntimeException extends \Exception -{ +use Exception; -} \ No newline at end of file +class RuntimeException extends Exception +{ +} diff --git a/src/Exceptions/SyntaxError.php b/src/Exceptions/SyntaxError.php index 0f94143..17e8821 100644 --- a/src/Exceptions/SyntaxError.php +++ b/src/Exceptions/SyntaxError.php @@ -2,10 +2,10 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Exceptions; -class SyntaxError extends \Exception -{ +use Exception; -} \ No newline at end of file +class SyntaxError extends Exception +{ +} diff --git a/src/Runtime/ArrayValue.php b/src/Runtime/ArrayValue.php index 8794d2b..3a15412 100644 --- a/src/Runtime/ArrayValue.php +++ b/src/Runtime/ArrayValue.php @@ -4,9 +4,14 @@ namespace Codewithkyrian\Jinja\Runtime; +use function count; + +/** + * @extends RuntimeValue>> + */ class ArrayValue extends RuntimeValue { - public string $type = "ArrayValue"; + public string $type = 'ArrayValue'; public function __construct(array $value = []) { @@ -14,8 +19,18 @@ public function __construct(array $value = []) $this->builtins['length'] = new IntegerValue(count($this->value)); } - public function evaluateAsBool(): BooleanValue + public function asBool(): BooleanValue { return new BooleanValue(count($this->value) > 0); } + + public function __toString(): string + { + $encoded = json_encode($this->value, JSON_PRETTY_PRINT); + if ($encoded === false) { + return '[unable to encode array]'; + } + + return $encoded; + } } diff --git a/src/Runtime/BooleanValue.php b/src/Runtime/BooleanValue.php index bf42ec4..99af31a 100644 --- a/src/Runtime/BooleanValue.php +++ b/src/Runtime/BooleanValue.php @@ -2,12 +2,14 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Runtime; +/** + * @extends RuntimeValue + */ class BooleanValue extends RuntimeValue { - public string $type = "BooleanValue"; + public string $type = 'BooleanValue'; public function __construct(bool $value) { diff --git a/src/Runtime/BreakControl.php b/src/Runtime/BreakControl.php index 5204972..94b07b4 100644 --- a/src/Runtime/BreakControl.php +++ b/src/Runtime/BreakControl.php @@ -4,7 +4,11 @@ namespace Codewithkyrian\Jinja\Runtime; +use Exception; + /** * Control flow exception thrown when a break statement is encountered in a loop. */ -class BreakControl extends \Exception {} +class BreakControl extends Exception +{ +} diff --git a/src/Runtime/ContinueControl.php b/src/Runtime/ContinueControl.php index 1227ba3..a980069 100644 --- a/src/Runtime/ContinueControl.php +++ b/src/Runtime/ContinueControl.php @@ -4,7 +4,11 @@ namespace Codewithkyrian\Jinja\Runtime; +use Exception; + /** * Control flow exception thrown when a continue statement is encountered in a loop. */ -class ContinueControl extends \Exception {} +class ContinueControl extends Exception +{ +} diff --git a/src/Runtime/FloatValue.php b/src/Runtime/FloatValue.php index eb2a9bb..db4e77c 100644 --- a/src/Runtime/FloatValue.php +++ b/src/Runtime/FloatValue.php @@ -4,9 +4,12 @@ namespace Codewithkyrian\Jinja\Runtime; +/** + * @extends RuntimeValue + */ class FloatValue extends RuntimeValue { - public string $type = "FloatValue"; + public string $type = 'FloatValue'; public function __construct(float $value) { diff --git a/src/Runtime/FunctionValue.php b/src/Runtime/FunctionValue.php index aa4b7c3..0ad0dfb 100644 --- a/src/Runtime/FunctionValue.php +++ b/src/Runtime/FunctionValue.php @@ -6,17 +6,25 @@ use Codewithkyrian\Jinja\Core\Environment; +use function call_user_func_array; + +/** + * @extends RuntimeValue>, Environment): RuntimeValue> + */ class FunctionValue extends RuntimeValue { - public string $type = "FunctionValue"; + public string $type = 'FunctionValue'; public function __construct(callable $value) { parent::__construct($value); } + /** + * @return RuntimeValue + */ public function call(array $args, Environment $env): RuntimeValue { - return call_user_func_array($this->value, [...$args, $env]); + return ($this->value)($args, $env); } } diff --git a/src/Runtime/IntegerValue.php b/src/Runtime/IntegerValue.php index 7e3c9c0..a373701 100644 --- a/src/Runtime/IntegerValue.php +++ b/src/Runtime/IntegerValue.php @@ -4,9 +4,12 @@ namespace Codewithkyrian\Jinja\Runtime; +/** + * @extends RuntimeValue + */ class IntegerValue extends RuntimeValue { - public string $type = "IntegerValue"; + public string $type = 'IntegerValue'; public function __construct(int $value) { diff --git a/src/Runtime/KeywordArgumentsValue.php b/src/Runtime/KeywordArgumentsValue.php index 3cb1fdc..f419982 100644 --- a/src/Runtime/KeywordArgumentsValue.php +++ b/src/Runtime/KeywordArgumentsValue.php @@ -9,5 +9,5 @@ */ class KeywordArgumentsValue extends ObjectValue { - public string $type = "KeywordArgumentsValue"; + public string $type = 'KeywordArgumentsValue'; } diff --git a/src/Runtime/NullValue.php b/src/Runtime/NullValue.php index 8510d73..285d9f8 100644 --- a/src/Runtime/NullValue.php +++ b/src/Runtime/NullValue.php @@ -2,12 +2,14 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Runtime; +/** + * @extends RuntimeValue + */ class NullValue extends RuntimeValue { - public string $type = "NullValue"; + public string $type = 'NullValue'; public function __construct() { @@ -16,6 +18,6 @@ public function __construct() public function jsonSerialize(): mixed { - return "null"; + return 'null'; } } diff --git a/src/Runtime/ObjectValue.php b/src/Runtime/ObjectValue.php index b4bd328..2c99db7 100644 --- a/src/Runtime/ObjectValue.php +++ b/src/Runtime/ObjectValue.php @@ -2,33 +2,64 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Runtime; +use RuntimeException; + +/** + * @extends RuntimeValue>> + */ class ObjectValue extends RuntimeValue { - public string $type = "ObjectValue"; + public string $type = 'ObjectValue'; public function __construct(array $value) { parent::__construct($value); $this->builtins = [ - "get" => new FunctionValue(function (StringValue $key, $defaultValue = null) { - return $this->value[$key->value] ?? $defaultValue ?? new NullValue(); + 'get' => new FunctionValue(function ($args) { + $key = $args[0]; + $defaultValue = $args[1] ?? new NullValue(); + + if (!($key instanceof StringValue)) { + throw new RuntimeException("Object key must be a string: got {$key->type}"); + } + + return $this->value[$key->value] ?? $defaultValue; }), - "items" => new FunctionValue(function () { + 'items' => new FunctionValue(function () { $items = []; foreach ($this->value as $key => $value) { $items[] = new ArrayValue([new StringValue($key), $value]); } return new ArrayValue($items); - }) + }), + 'keys' => new FunctionValue(function () { + $keys = []; + foreach ($this->value as $key => $value) { + $keys[] = new StringValue($key); + } + return new ArrayValue($keys); + }), + 'values' => new FunctionValue(function () { + return new ArrayValue(array_values($this->value)); + }), ]; } - public function evaluateAsBool(): BooleanValue + public function asBool(): BooleanValue { return new BooleanValue(!empty($this->value)); } + + public function __toString(): string + { + $encoded = json_encode($this->value, JSON_PRETTY_PRINT); + if ($encoded === false) { + return '[unable to encode object]'; + } + + return $encoded; + } } diff --git a/src/Runtime/RuntimeValue.php b/src/Runtime/RuntimeValue.php index 491879f..a111c50 100644 --- a/src/Runtime/RuntimeValue.php +++ b/src/Runtime/RuntimeValue.php @@ -2,35 +2,47 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Runtime; use JsonSerializable; +use Stringable; -abstract class RuntimeValue implements JsonSerializable +/** + * @template TValue + */ +abstract class RuntimeValue implements JsonSerializable, Stringable { - public string $type = "RuntimeValue"; + public string $type = 'RuntimeValue'; /** * A collection of built-in functions for this type. */ public array $builtins = []; + /** + * @param TValue $value + */ public function __construct( - public $value - ) {} + public mixed $value, + ) { + } /** * Determines truthiness or falsiness of the runtime value. * This function should be overridden by subclasses if it has custom truthiness criteria. */ - public function evaluateAsBool(): BooleanValue + public function asBool(): BooleanValue { - return new BooleanValue(!!$this->value); + return new BooleanValue((bool)$this->value); } public function jsonSerialize(): mixed { return $this->value; } + + public function __toString(): string + { + return (string) $this->value; + } } diff --git a/src/Runtime/StringValue.php b/src/Runtime/StringValue.php index 3c461c6..473b14a 100644 --- a/src/Runtime/StringValue.php +++ b/src/Runtime/StringValue.php @@ -2,31 +2,48 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Runtime; -use function Codewithkyrian\Jinja\toTitleCase; +use Exception; +use function array_map; +use function Codewithkyrian\Jinja\toTitleCase; +use function count; +use function explode; +use function ltrim; +use function rtrim; +use function str_ends_with; +use function str_replace; +use function str_starts_with; +use function strlen; +use function strtolower; +use function strtoupper; +use function trim; +use function ucfirst; + +/** + * @extends RuntimeValue + */ class StringValue extends RuntimeValue { - public string $type = "StringValue"; + public string $type = 'StringValue'; public function __construct(string $value) { parent::__construct($value); $this->builtins = [ - "upper" => new FunctionValue(fn() => new StringValue(strtoupper($this->value))), - "lower" => new FunctionValue(fn() => new StringValue(strtolower($this->value))), - "strip" => new FunctionValue(fn() => new StringValue(trim($this->value))), - "title" => new FunctionValue(fn() => new StringValue(toTitleCase($this->value))), - "capitalize" => new FunctionValue(fn() => new StringValue(ucfirst($this->value))), - "length" => new IntegerValue(strlen($this->value)), - "rstrip" => new FunctionValue(fn() => new StringValue(rtrim($this->value))), - "lstrip" => new FunctionValue(fn() => new StringValue(ltrim($this->value))), - "startswith" => new FunctionValue(function ($args) { + 'upper' => new FunctionValue(fn () => new StringValue(strtoupper($this->value))), + 'lower' => new FunctionValue(fn () => new StringValue(strtolower($this->value))), + 'strip' => new FunctionValue(fn () => new StringValue(trim($this->value))), + 'title' => new FunctionValue(fn () => new StringValue(toTitleCase($this->value))), + 'capitalize' => new FunctionValue(fn () => new StringValue(ucfirst($this->value))), + 'length' => new IntegerValue(strlen($this->value)), + 'rstrip' => new FunctionValue(fn () => new StringValue(rtrim($this->value))), + 'lstrip' => new FunctionValue(fn () => new StringValue(ltrim($this->value))), + 'startswith' => new FunctionValue(function ($args) { if (count($args) === 0) { - throw new \Exception("startswith() requires at least one argument"); + throw new Exception('startswith() requires at least one argument'); } $prefix = $args[0]; @@ -38,7 +55,7 @@ public function __construct(string $value) if ($prefix instanceof ArrayValue) { foreach ($prefix->value as $item) { if (!($item instanceof StringValue)) { - throw new \Exception("startswith() tuple elements must be strings"); + throw new Exception('startswith() tuple elements must be strings'); } if (str_starts_with($this->value, $item->value)) { @@ -49,11 +66,11 @@ public function __construct(string $value) return new BooleanValue(false); } - throw new \Exception("startswith() must be a string or tuple of strings"); + throw new Exception('startswith() must be a string or tuple of strings'); }), - "endswith" => new FunctionValue(function ($args) { + 'endswith' => new FunctionValue(function ($args) { if (count($args) === 0) { - throw new \Exception("endswith() requires at least one argument"); + throw new Exception('endswith() requires at least one argument'); } $suffix = $args[0]; @@ -64,7 +81,7 @@ public function __construct(string $value) if ($suffix instanceof ArrayValue) { foreach ($suffix->value as $item) { if (!($item instanceof StringValue)) { - throw new \Exception("endswith() tuple elements must be strings"); + throw new Exception('endswith() tuple elements must be strings'); } if (str_ends_with($this->value, $item->value)) { @@ -75,59 +92,47 @@ public function __construct(string $value) return new BooleanValue(false); } - throw new \Exception("endswith() must be a string or tuple of strings"); + throw new Exception('endswith() must be a string or tuple of strings'); }), - "split" => new FunctionValue(function ($args) { + 'split' => new FunctionValue(function ($args) { $separator = $args[0] ?? new NullValue(); if (!($separator instanceof StringValue || $separator instanceof NullValue)) { - throw new \Exception("split() separator must be a string or null"); + throw new Exception('split() separator must be a string or null'); } $maxsplit = $args[1] ?? new IntegerValue(-1); if (!($maxsplit instanceof IntegerValue)) { - throw new \Exception("split() maxsplit must be an integer"); + throw new Exception('split() maxsplit must be an integer'); } + $separatorValue = $separator instanceof NullValue ? '' : $separator->value; + $separatorValue = $separatorValue ?: ' '; + return new ArrayValue( array_map( - fn($item) => new StringValue($item), - explode($separator->value, $this->value, $maxsplit->value) - ) + fn ($item) => new StringValue($item), + explode($separatorValue, $this->value, $maxsplit->value), + ), ); }), - "replace" => new FunctionValue(function ($args) { + 'replace' => new FunctionValue(function ($args) { if (count($args) < 2) { - throw new \Exception("replace() requires at least two arguments"); + throw new Exception('replace() requires at least two arguments'); } $oldValue = $args[0]; $newValue = $args[1]; if (!($oldValue instanceof StringValue)) { - throw new \Exception("replace() old value must be a string"); + throw new Exception('replace() old value must be a string'); } if (!($newValue instanceof StringValue)) { - throw new \Exception("replace() new value must be a string"); + throw new Exception('replace() new value must be a string'); } return new StringValue(str_replace($oldValue->value, $newValue->value, $this->value)); }), - "items" => new FunctionValue(function () { - return new ArrayValue( - array_map( - fn($value, $key) => new ArrayValue([new StringValue($key), $value]), - $this->value, - array_keys($this->value) - ) - ); - }), - "keys" => new FunctionValue(function () { - return new ArrayValue(array_keys($this->value)); - }), - "values" => new FunctionValue(function () { - return new ArrayValue(array_values($this->value)); - }), ]; } } diff --git a/src/Runtime/TupleValue.php b/src/Runtime/TupleValue.php index 64c0f4f..76545d3 100644 --- a/src/Runtime/TupleValue.php +++ b/src/Runtime/TupleValue.php @@ -2,9 +2,9 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Runtime; -class TupleValue extends ArrayValue { - public string $type = "TupleValue"; +class TupleValue extends ArrayValue +{ + public string $type = 'TupleValue'; } diff --git a/src/Runtime/UndefinedValue.php b/src/Runtime/UndefinedValue.php index 48f247e..d4d3cd0 100644 --- a/src/Runtime/UndefinedValue.php +++ b/src/Runtime/UndefinedValue.php @@ -2,12 +2,14 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja\Runtime; +/** + * @extends RuntimeValue + */ class UndefinedValue extends RuntimeValue { - public string $type = "UndefinedValue"; + public string $type = 'UndefinedValue'; public function __construct() { @@ -16,6 +18,6 @@ public function __construct() public function jsonSerialize(): mixed { - return "null"; + return 'null'; } } diff --git a/src/Template.php b/src/Template.php index 2b4891d..ac1b217 100644 --- a/src/Template.php +++ b/src/Template.php @@ -2,7 +2,6 @@ declare(strict_types=1); - namespace Codewithkyrian\Jinja; use Codewithkyrian\Jinja\AST\Program; @@ -20,19 +19,20 @@ class Template /** * The constructor takes a template string, tokenizes it, parses it into a program structure. * - * @param string $template The template string. + * @param string $template the template string */ public function __construct(string $template, bool $lstripBlocks = true, bool $trimBlocks = true) { - $tokens = Lexer::tokenize($template, lstripBlocks: $lstripBlocks, trimBlocks: $trimBlocks); + $tokens = Lexer::tokenize($template, lstripBlocks: $lstripBlocks, trimBlocks: $trimBlocks); $this->parsed = Parser::make($tokens)->parse(); } /** * Renders the template with the provided items as variables. * - * @param ?array $items Associative array of user-defined variables. - * @return string The rendered template. + * @param ?array $items associative array of user-defined variables + * + * @return string the rendered template */ public function render(?array $items): string { @@ -46,7 +46,7 @@ public function render(?array $items): string $env->set('True', true); $env->set('none', null); $env->set('None', null); - $env->set('raise_exception', fn(string $args) => throw new RuntimeException($args)); + $env->set('raise_exception', fn (string $args) => throw new RuntimeException($args)); $env->set('range', range(...)); // Add user-defined variables diff --git a/tests/Datasets/End2EndDataset.php b/tests/Datasets/End2EndDataset.php index 284493e..cd8ba21 100644 --- a/tests/Datasets/End2EndDataset.php +++ b/tests/Datasets/End2EndDataset.php @@ -2,7 +2,6 @@ declare(strict_types=1); - const EXAMPLE_CHAT = [ ['role' => 'user', 'content' => 'Hello, how are you?'], ['role' => 'assistant', 'content' => "I'm doing great. How can I help you today?"], @@ -15,19 +14,19 @@ ]; // Since PHP doesn't support the spread operator for arrays in the way JavaScript does, use array_merge for $EXAMPLE_CHAT_WITH_SYSTEM -$EXAMPLE_CHAT_WITH_SYSTEM = array_merge([ +$EXAMPLE_CHAT_WITH_SYSTEM = \array_merge([ ['role' => 'system', 'content' => 'You are a friendly chatbot who always responds in the style of a pirate'], ], EXAMPLE_CHAT); const EXAMPLE_FUNCTION_CALLING = [ [ - 'role' => 'assistant', - 'content' => null, + 'role' => 'assistant', + 'content' => null, 'tool_calls' => [ [ - 'type' => 'function', + 'type' => 'function', 'function' => [ - 'name' => 'get_current_weather', + 'name' => 'get_current_weather', 'arguments' => "{\n \"location\": \"Hanoi\"\n}", ], ], @@ -38,13 +37,13 @@ const EXAMPLE_FUNCTION_SPEC = [ [ - 'name' => 'get_stock_price', + 'name' => 'get_stock_price', 'description' => 'Get the current stock price', - 'parameters' => [ - 'type' => 'object', + 'parameters' => [ + 'type' => 'object', 'properties' => [ 'symbol' => [ - 'type' => 'string', + 'type' => 'string', 'description' => 'The stock symbol, e.g. AAPL, GOOG', ], ], @@ -52,17 +51,17 @@ ], ], [ - 'name' => 'check_word_anagram', + 'name' => 'check_word_anagram', 'description' => 'Check if two words are anagrams of each other', - 'parameters' => [ - 'type' => 'object', + 'parameters' => [ + 'type' => 'object', 'properties' => [ 'word1' => [ - 'type' => 'string', + 'type' => 'string', 'description' => 'The first word', ], 'word2' => [ - 'type' => 'string', + 'type' => 'string', 'description' => 'The second word', ], ], @@ -72,250 +71,248 @@ ]; $EXAMPLE_FUNCTION_CALLING_WITH_SYSTEM = [ - ['role' => 'functions', 'content' => json_encode(EXAMPLE_FUNCTION_SPEC, JSON_PRETTY_PRINT)], + ['role' => 'functions', 'content' => \json_encode(EXAMPLE_FUNCTION_SPEC, \JSON_PRETTY_PRINT)], ['role' => 'system', 'content' => 'You are a helpful assistant with access to functions. Use them if required.'], ['role' => 'user', 'content' => 'Hi, can you tell me the current stock price of AAPL?'], ]; - // Defined in https://github.com/huggingface/transformers // Keys correspond to `model_type` in the transformers repo. dataset('defaultTemplates', [ '_base' => [ - 'chat_template' => "{% for message in messages %}{{'' + message['role'] + '\\n' + message['content'] + '' + '\\n'}}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, + 'chatTemplate' => "{% for message in messages %}{{'' + message['role'] + '\\n' + message['content'] + '' + '\\n'}}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, 'add_generation_prompt' => false, ], 'target' => "user\nHello, how are you?\nassistant\nI'm doing great. How can I help you today?\nuser\nI'd like to show off how chat templating works!\n", ], 'blenderbot' => [ - 'chat_template' => "{% for message in messages %}{% if message['role'] == 'user' %}{{ ' ' }}{% endif %}{{ message['content'] }}{% if not loop.last %}{{ ' ' }}{% endif %}{% endfor %}{{ eos_token }}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'eos_token' => "", + 'chatTemplate' => "{% for message in messages %}{% if message['role'] == 'user' %}{{ ' ' }}{% endif %}{{ message['content'] }}{% if not loop.last %}{{ ' ' }}{% endif %}{% endfor %}{{ eos_token }}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'eos_token' => '', ], 'target' => " Hello, how are you? I'm doing great. How can I help you today? I'd like to show off how chat templating works!", ], 'blenderbot_small' => [ - 'chat_template' => "{% for message in messages %}{% if message['role'] == 'user' %}{{ ' ' }}{% endif %}{{ message['content'] }}{% if not loop.last %}{{ ' ' }}{% endif %}{% endfor %}{{ eos_token }}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'eos_token' => "", + 'chatTemplate' => "{% for message in messages %}{% if message['role'] == 'user' %}{{ ' ' }}{% endif %}{{ message['content'] }}{% if not loop.last %}{{ ' ' }}{% endif %}{% endfor %}{{ eos_token }}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'eos_token' => '', ], 'target' => " Hello, how are you? I'm doing great. How can I help you today? I'd like to show off how chat templating works!", ], 'bloom' => [ - 'chat_template' => "{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'eos_token' => "", + 'chatTemplate' => '{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}', + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'eos_token' => '', ], 'target' => "Hello, how are you?I'm doing great. How can I help you today?I'd like to show off how chat templating works!", ], 'gpt_neox' => [ - 'chat_template' => "{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'eos_token' => "", + 'chatTemplate' => '{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}', + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'eos_token' => '', ], 'target' => "Hello, how are you?I'm doing great. How can I help you today?I'd like to show off how chat templating works!", ], 'gpt2' => [ - 'chat_template' => "{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'eos_token' => "", + 'chatTemplate' => '{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}', + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'eos_token' => '', ], 'target' => "Hello, how are you?I'm doing great. How can I help you today?I'd like to show off how chat templating works!", ], 'llama' => [ - 'chat_template' => "{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% elif USE_DEFAULT_PROMPT == true and not '<>' in messages[0]['content'] %}{% set loop_messages = messages %}{% set system_message = 'DEFAULT_SYSTEM_MESSAGE' %}{% else %}{% set loop_messages = messages %}{% set system_message = false %}{% endif %}{% for message in loop_messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if loop.index0 == 0 and system_message != false %}{% set content = '<>\\n' + system_message + '\\n<>\\n\\n' + message['content'] %}{% else %}{% set content = message['content'] %}{% endif %}{% if message['role'] == 'user' %}{{ bos_token + '[INST] ' + content.strip() + ' [/INST]' }}{% elif message['role'] == 'system' %}{{ '<>\\n' + content.strip() + '\\n<>\\n\\n' }}{% elif message['role'] == 'assistant' %}{{ ' ' + content.strip() + ' ' + eos_token }}{% endif %}{% endfor %}", - 'data' => [ - 'messages' => $EXAMPLE_CHAT_WITH_SYSTEM, - 'bos_token' => "", - 'eos_token' => "", + 'chatTemplate' => "{% if messages[0]['role'] == 'system' %}{% set loop_messages = messages[1:] %}{% set system_message = messages[0]['content'] %}{% elif USE_DEFAULT_PROMPT == true and not '<>' in messages[0]['content'] %}{% set loop_messages = messages %}{% set system_message = 'DEFAULT_SYSTEM_MESSAGE' %}{% else %}{% set loop_messages = messages %}{% set system_message = false %}{% endif %}{% for message in loop_messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if loop.index0 == 0 and system_message != false %}{% set content = '<>\\n' + system_message + '\\n<>\\n\\n' + message['content'] %}{% else %}{% set content = message['content'] %}{% endif %}{% if message['role'] == 'user' %}{{ bos_token + '[INST] ' + content.strip() + ' [/INST]' }}{% elif message['role'] == 'system' %}{{ '<>\\n' + content.strip() + '\\n<>\\n\\n' }}{% elif message['role'] == 'assistant' %}{{ ' ' + content.strip() + ' ' + eos_token }}{% endif %}{% endfor %}", + 'data' => [ + 'messages' => $EXAMPLE_CHAT_WITH_SYSTEM, + 'bos_token' => '', + 'eos_token' => '', 'USE_DEFAULT_PROMPT' => true, ], 'target' => "[INST] <>\nYou are a friendly chatbot who always responds in the style of a pirate\n<>\n\nHello, how are you? [/INST] I'm doing great. How can I help you today? [INST] I'd like to show off how chat templating works! [/INST]", ], 'whisper' => [ - 'chat_template' => "{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'eos_token' => "", + 'chatTemplate' => '{% for message in messages %}{{ message.content }}{{ eos_token }}{% endfor %}', + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'eos_token' => '', ], 'target' => "Hello, how are you?I'm doing great. How can I help you today?I'd like to show off how chat templating works!", - ] + ], ]); - -/** +/* * Custom templates that are not defined in the transformers' repo. * Keys are repo ids on the Hugging Face Hub (https://hf.co/models) */ dataset('customTemplates', [ - "HuggingFaceH4/zephyr-7b-beta (add_generation_prompt=false)" => [ - 'chat_template' => "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'system' %}\n{{ '\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'assistant' %}\n{{ '\n' + message['content'] + eos_token }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '' }}\n{% endif %}\n{% endfor %}", - 'data' => [ - 'messages' => $EXAMPLE_CHAT_WITH_SYSTEM, - 'eos_token' => "", + 'HuggingFaceH4/zephyr-7b-beta (add_generation_prompt=false)' => [ + 'chatTemplate' => "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'system' %}\n{{ '\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'assistant' %}\n{{ '\n' + message['content'] + eos_token }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '' }}\n{% endif %}\n{% endfor %}", + 'data' => [ + 'messages' => $EXAMPLE_CHAT_WITH_SYSTEM, + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "\nYou are a friendly chatbot who always responds in the style of a pirate\n\nHello, how are you?\n\nI'm doing great. How can I help you today?\n\nI'd like to show off how chat templating works!\n", ], - "HuggingFaceH4/zephyr-7b-beta (add_generation_prompt=true)" => [ - 'chat_template' => "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '\\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'system' %}\n{{ '\\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'assistant' %}\n{{ '\\n' + message['content'] + eos_token }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '' }}\n{% endif %}\n{% endfor %}", - 'data' => [ + 'HuggingFaceH4/zephyr-7b-beta (add_generation_prompt=true)' => [ + 'chatTemplate' => "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '\\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'system' %}\n{{ '\\n' + message['content'] + eos_token }}\n{% elif message['role'] == 'assistant' %}\n{{ '\\n' + message['content'] + eos_token }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '' }}\n{% endif %}\n{% endfor %}", + 'data' => [ 'messages' => [ - ['role' => "system", 'content' => "You are a friendly chatbot who always responds in the style of a pirate"], - ['role' => "user", 'content' => "How many helicopters can a human eat in one sitting?"], + ['role' => 'system', 'content' => 'You are a friendly chatbot who always responds in the style of a pirate'], + ['role' => 'user', 'content' => 'How many helicopters can a human eat in one sitting?'], ], - 'eos_token' => "", + 'eos_token' => '', 'add_generation_prompt' => true, ], 'target' => "\nYou are a friendly chatbot who always responds in the style of a pirate\n\nHow many helicopters can a human eat in one sitting?\n\n", ], - "HuggingFaceH4/zephyr-7b-gemma-v0.1" => [ - 'chat_template' => "{% if messages[0]['role'] == 'user' or messages[0]['role'] == 'system' %}{{ bos_token }}{% endif %}{% for message in messages %}{{ '' + message['role'] + '\\n' + message['content'] + '' + '\\n' }}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% elif messages[-1]['role'] == 'assistant' %}{{ eos_token }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'HuggingFaceH4/zephyr-7b-gemma-v0.1' => [ + 'chatTemplate' => "{% if messages[0]['role'] == 'user' or messages[0]['role'] == 'system' %}{{ bos_token }}{% endif %}{% for message in messages %}{{ '' + message['role'] + '\\n' + message['content'] + '' + '\\n' }}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% elif messages[-1]['role'] == 'assistant' %}{{ eos_token }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "user\nHello, how are you?\nassistant\nI'm doing great. How can I help you today?\nuser\nI'd like to show off how chat templating works!\n", ], - "mistralai/Mistral-7B-Instruct-v0.1" => [ - 'chat_template' => "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token + ' ' }}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'mistralai/Mistral-7B-Instruct-v0.1' => [ + 'chatTemplate' => "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token + ' ' }}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', ], 'target' => "[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today? [INST] I'd like to show off how chat templating works! [/INST]", ], - "mistralai/Mixtral-8x7B-Instruct-v0.1" => [ - 'chat_template' => "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token}}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'mistralai/Mixtral-8x7B-Instruct-v0.1' => [ + 'chatTemplate' => "{{ bos_token }}{% for message in messages %}{% if (message['role'] == 'user') != (loop.index0 % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if message['role'] == 'user' %}{{ '[INST] ' + message['content'] + ' [/INST]' }}{% elif message['role'] == 'assistant' %}{{ message['content'] + eos_token}}{% else %}{{ raise_exception('Only user and assistant roles are supported!') }}{% endif %}{% endfor %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', ], - 'target' => "[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today?[INST] I'd like to show off how chat templating works! [/INST]" + 'target' => "[INST] Hello, how are you? [/INST]I'm doing great. How can I help you today?[INST] I'd like to show off how chat templating works! [/INST]", ], - "cognitivecomputations_dolphin_2_5_mixtral_8x7b" => [ - 'chat_template' => "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'' + message['role'] + '\\n' + message['content'] + '' + '\\n'}}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'cognitivecomputations_dolphin_2_5_mixtral_8x7b' => [ + 'chatTemplate' => "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% for message in messages %}{{'' + message['role'] + '\\n' + message['content'] + '' + '\\n'}}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', ], 'target' => "user\nHello, how are you?\nassistant\nI'm doing great. How can I help you today?\nuser\nI'd like to show off how chat templating works!\n", ], - "openchat_openchat_3_5_0106" => [ - 'chat_template' => "{{ bos_token }}{% for message in messages %}{{ 'GPT4 Correct ' + message['role'].title() + ': ' + message['content'] + ''}}{% endfor %}{% if add_generation_prompt %}{{ 'GPT4 Correct Assistant:' }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'openchat_openchat_3_5_0106' => [ + 'chatTemplate' => "{{ bos_token }}{% for message in messages %}{{ 'GPT4 Correct ' + message['role'].title() + ': ' + message['content'] + ''}}{% endfor %}{% if add_generation_prompt %}{{ 'GPT4 Correct Assistant:' }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "GPT4 Correct User: Hello, how are you?GPT4 Correct Assistant: I'm doing great. How can I help you today?GPT4 Correct User: I'd like to show off how chat templating works!", ], - "upstage_SOLAR_10_7B_Instruct_v1_0" => [ - 'chat_template' => "{% for message in messages %}{% if message['role'] == 'system' %}{% if message['content']%}{{'### System:\n' + message['content']+'\n\n'}}{% endif %}{% elif message['role'] == 'user' %}{{'### User:\n' + message['content']+'\n\n'}}{% elif message['role'] == 'assistant' %}{{'### Assistant:\n' + message['content']}}{% endif %}{% if loop.last and add_generation_prompt %}{{ '### Assistant:\n' }}{% endif %}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'upstage_SOLAR_10_7B_Instruct_v1_0' => [ + 'chatTemplate' => "{% for message in messages %}{% if message['role'] == 'system' %}{% if message['content']%}{{'### System:\n' + message['content']+'\n\n'}}{% endif %}{% elif message['role'] == 'user' %}{{'### User:\n' + message['content']+'\n\n'}}{% elif message['role'] == 'assistant' %}{{'### Assistant:\n' + message['content']}}{% endif %}{% if loop.last and add_generation_prompt %}{{ '### Assistant:\n' }}{% endif %}{% endfor %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "### User:\nHello, how are you?\n\n### Assistant:\nI'm doing great. How can I help you today?### User:\nI'd like to show off how chat templating works!\n\n", ], - "codellama_CodeLlama_70b_Instruct_hf" => [ - 'chat_template' => "{% if messages[0]['role'] == 'system' %}{% set user_index = 1 %}{% else %}{% set user_index = 0 %}{% endif %}{% for message in messages %}{% if (message['role'] == 'user') != ((loop.index0 + user_index) % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if loop.index0 == 0 %}{{ '' }}{% endif %}{% set content = 'Source: ' + message['role'] + '\\n\\n ' + message['content'] | trim %}{{ content + ' ' }}{% endfor %}{{'Source: assistant\\nDestination: user\\n\\n '}}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'codellama_CodeLlama_70b_Instruct_hf' => [ + 'chatTemplate' => "{% if messages[0]['role'] == 'system' %}{% set user_index = 1 %}{% else %}{% set user_index = 0 %}{% endif %}{% for message in messages %}{% if (message['role'] == 'user') != ((loop.index0 + user_index) % 2 == 0) %}{{ raise_exception('Conversation roles must alternate user/assistant/user/assistant/...') }}{% endif %}{% if loop.index0 == 0 %}{{ '' }}{% endif %}{% set content = 'Source: ' + message['role'] + '\\n\\n ' + message['content'] | trim %}{{ content + ' ' }}{% endfor %}{{'Source: assistant\\nDestination: user\\n\\n '}}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', ], 'target' => "Source: user\n\n Hello, how are you? Source: assistant\n\n I'm doing great. How can I help you today? Source: user\n\n I'd like to show off how chat templating works! Source: assistant\nDestination: user\n\n ", ], - "Deci/DeciLM-7B-instruct" => [ - 'chat_template' => "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '### User:\n' + message['content'] }}\n{% elif message['role'] == 'system' %}\n{{ '### System:\n' + message['content'] }}\n{% elif message['role'] == 'assistant' %}\n{{ '### Assistant:\n' + message['content'] }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '### Assistant:' }}\n{% endif %}\n{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'Deci/DeciLM-7B-instruct' => [ + 'chatTemplate' => "{% for message in messages %}\n{% if message['role'] == 'user' %}\n{{ '### User:\n' + message['content'] }}\n{% elif message['role'] == 'system' %}\n{{ '### System:\n' + message['content'] }}\n{% elif message['role'] == 'assistant' %}\n{{ '### Assistant:\n' + message['content'] }}\n{% endif %}\n{% if loop.last and add_generation_prompt %}\n{{ '### Assistant:' }}\n{% endif %}\n{% endfor %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "### User:\nHello, how are you?\n### Assistant:\nI'm doing great. How can I help you today?\n### User:\nI'd like to show off how chat templating works!\n", ], - "Qwen/Qwen1.5-72B-Chat" => [ - 'chat_template' => "{% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ 'system\nYou are a helpful assistant\n' }}{% endif %}{{'' + message['role'] + '\n' + message['content']}}{% if (loop.last and add_generation_prompt) or not loop.last %}{{ '' + '\n'}}{% endif %}{% endfor %}{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}{{ 'assistant\n' }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'Qwen/Qwen1.5-72B-Chat' => [ + 'chatTemplate' => "{% for message in messages %}{% if loop.first and messages[0]['role'] != 'system' %}{{ 'system\nYou are a helpful assistant\n' }}{% endif %}{{'' + message['role'] + '\n' + message['content']}}{% if (loop.last and add_generation_prompt) or not loop.last %}{{ '' + '\n'}}{% endif %}{% endfor %}{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}{{ 'assistant\n' }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "system\nYou are a helpful assistant\nuser\nHello, how are you?\nassistant\nI'm doing great. How can I help you today?\nuser\nI'd like to show off how chat templating works!", ], - "deepseek-ai/deepseek-llm-7b-chat" => [ - 'chat_template' => "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{{ bos_token }}{% for message in messages %}{% if message['role'] == 'user' %}{{ 'User: ' + message['content'] + '\n\n' }}{% elif message['role'] == 'assistant' %}{{ 'Assistant: ' + message['content'] + eos_token }}{% elif message['role'] == 'system' %}{{ message['content'] + '\n\n' }}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ 'Assistant:' }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "<|begin▁of▁sentence|>", - 'eos_token' => "<|end▁of▁sentence|>", + 'deepseek-ai/deepseek-llm-7b-chat' => [ + 'chatTemplate' => "{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{{ bos_token }}{% for message in messages %}{% if message['role'] == 'user' %}{{ 'User: ' + message['content'] + '\n\n' }}{% elif message['role'] == 'assistant' %}{{ 'Assistant: ' + message['content'] + eos_token }}{% elif message['role'] == 'system' %}{{ message['content'] + '\n\n' }}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ 'Assistant:' }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '<|begin▁of▁sentence|>', + 'eos_token' => '<|end▁of▁sentence|>', ], 'target' => "<|begin▁of▁sentence|>User: Hello, how are you?\n\nAssistant: I'm doing great. How can I help you today?<|end▁of▁sentence|>User: I'd like to show off how chat templating works!\n\n", ], - "h2oai/h2o-danube-1.8b-chat" => [ - 'chat_template' => "{% for message in messages %}{% if message['role'] == 'user' %}{{ '<|prompt|>' + message['content'] + eos_token }}{% elif message['role'] == 'system' %}{{ '<|system|>' + message['content'] + eos_token }}{% elif message['role'] == 'assistant' %}{{ '<|answer|>' + message['content'] + eos_token }}{% endif %}{% if loop.last and add_generation_prompt %}{{ '<|answer|>' }}{% endif %}{% endfor %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'h2oai/h2o-danube-1.8b-chat' => [ + 'chatTemplate' => "{% for message in messages %}{% if message['role'] == 'user' %}{{ '<|prompt|>' + message['content'] + eos_token }}{% elif message['role'] == 'system' %}{{ '<|system|>' + message['content'] + eos_token }}{% elif message['role'] == 'assistant' %}{{ '<|answer|>' + message['content'] + eos_token }}{% endif %}{% if loop.last and add_generation_prompt %}{{ '<|answer|>' }}{% endif %}{% endfor %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], - 'target' => "<|prompt|>Hello, how are you?<|answer|>I'm doing great. How can I help you today?<|prompt|>I'd like to show off how chat templating works!" + 'target' => "<|prompt|>Hello, how are you?<|answer|>I'm doing great. How can I help you today?<|prompt|>I'd like to show off how chat templating works!", ], - "internlm/internlm2-chat-7b" => [ - 'chat_template' => "{% if messages[0]['role'] == 'user' or messages[0]['role'] == 'system' %}{{ bos_token }}{% endif %}{% for message in messages %}{{ '' + message['role'] + '\\n' + message['content'] + '' + '\\n' }}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% elif messages[-1]['role'] == 'assistant' %}{{ eos_token }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'internlm/internlm2-chat-7b' => [ + 'chatTemplate' => "{% if messages[0]['role'] == 'user' or messages[0]['role'] == 'system' %}{{ bos_token }}{% endif %}{% for message in messages %}{{ '' + message['role'] + '\\n' + message['content'] + '' + '\\n' }}{% endfor %}{% if add_generation_prompt %}{{ 'assistant\\n' }}{% elif messages[-1]['role'] == 'assistant' %}{{ eos_token }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "user\nHello, how are you?\nassistant\nI'm doing great. How can I help you today?\nuser\nI'd like to show off how chat templating works!\n", ], - "TheBloke/deepseek-coder-33B-instruct-AWQ" => [ - 'chat_template' => "{%- set found_item = false -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'system' -%}\n {%- set found_item = true -%}\n {%- endif -%}\n{%- endfor -%}\n{%- if not found_item -%}\n{{'You are an AI programming assistant, utilizing the Deepseek Coder model, developed by Deepseek Company, and you only answer questions related to computer science. For politically sensitive questions, security and privacy issues, and other non-computer science questions, you will refuse to answer.\\n'}}\n{%- endif %}\n{%- for message in messages %}\n {%- if message['role'] == 'system' %}\n{{ message['content'] }}\n {%- else %}\n {%- if message['role'] == 'user' %}\n{{'### Instruction:\\n' + message['content'] + '\\n'}}\n {%- else %}\n{{'### Response:\\n' + message['content'] + '\\n\\n'}}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{{'### Response:\\n'}}\n", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "<|begin▁of▁sentence|>", - 'eos_token' => "<|EOT|>", + 'TheBloke/deepseek-coder-33B-instruct-AWQ' => [ + 'chatTemplate' => "{%- set found_item = false -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'system' -%}\n {%- set found_item = true -%}\n {%- endif -%}\n{%- endfor -%}\n{%- if not found_item -%}\n{{'You are an AI programming assistant, utilizing the Deepseek Coder model, developed by Deepseek Company, and you only answer questions related to computer science. For politically sensitive questions, security and privacy issues, and other non-computer science questions, you will refuse to answer.\\n'}}\n{%- endif %}\n{%- for message in messages %}\n {%- if message['role'] == 'system' %}\n{{ message['content'] }}\n {%- else %}\n {%- if message['role'] == 'user' %}\n{{'### Instruction:\\n' + message['content'] + '\\n'}}\n {%- else %}\n{{'### Response:\\n' + message['content'] + '\\n\\n'}}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{{'### Response:\\n'}}\n", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '<|begin▁of▁sentence|>', + 'eos_token' => '<|EOT|>', ], 'target' => "You are an AI programming assistant, utilizing the Deepseek Coder model, developed by Deepseek Company, and you only answer questions related to computer science. For politically sensitive questions, security and privacy issues, and other non-computer science questions, you will refuse to answer.\n### Instruction:\nHello, how are you?\n### Response:\nI'm doing great. How can I help you today?\n\n### Instruction:\nI'd like to show off how chat templating works!\n### Response:\n", ], - "ericzzz/falcon-rw-1b-chat" => [ - 'chat_template' => "{% for message in messages %}{% if loop.index > 1 and loop.previtem['role'] != 'assistant' %}{{ ' ' }}{% endif %}{% if message['role'] == 'system' %}{{ '[SYS] ' + message['content'].strip() }}{% elif message['role'] == 'user' %}{{ '[INST] ' + message['content'].strip() }}{% elif message['role'] == 'assistant' %}{{ '[RESP] ' + message['content'] + eos_token }}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ ' [RESP] ' }}{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "<|endoftext|>", - 'eos_token' => "<|endoftext|>", + 'ericzzz/falcon-rw-1b-chat' => [ + 'chatTemplate' => "{% for message in messages %}{% if loop.index > 1 and loop.previtem['role'] != 'assistant' %}{{ ' ' }}{% endif %}{% if message['role'] == 'system' %}{{ '[SYS] ' + message['content'].strip() }}{% elif message['role'] == 'user' %}{{ '[INST] ' + message['content'].strip() }}{% elif message['role'] == 'assistant' %}{{ '[RESP] ' + message['content'] + eos_token }}{% endif %}{% endfor %}{% if add_generation_prompt %}{{ ' [RESP] ' }}{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '<|endoftext|>', + 'eos_token' => '<|endoftext|>', 'add_generation_prompt' => false, ], 'target' => "[INST] Hello, how are you? [RESP] I'm doing great. How can I help you today?<|endoftext|>[INST] I'd like to show off how chat templating works!", ], // "abacusai/Smaug-34B-v0.1" => [ - // 'chat_template' => "{%- for idx in range(0, messages|length) -%}\n{%- if messages[idx]['role'] == 'user' -%}\n{%- if idx > 1 -%}\n{{- bos_token + '[INST] ' + messages[idx]['content'] + ' [/INST]' -}}\n{%- else -%}\n{{- messages[idx]['content'] + ' [/INST]' -}}\n{%- endif -%}\n{% elif messages[idx]['role'] == 'system' %}\n{{- '[INST] <>\\n' + messages[idx]['content'] + '\\n<>\\n\\n' -}}\n{%- elif messages[idx]['role'] == 'assistant' -%}\n{{- ' ' + messages[idx]['content'] + ' ' + eos_token -}}\n{% endif %}\n{% endfor %}", + // 'chatTemplate' => "{%- for idx in range(0, messages|length) -%}\n{%- if messages[idx]['role'] == 'user' -%}\n{%- if idx > 1 -%}\n{{- bos_token + '[INST] ' + messages[idx]['content'] + ' [/INST]' -}}\n{%- else -%}\n{{- messages[idx]['content'] + ' [/INST]' -}}\n{%- endif -%}\n{% elif messages[idx]['role'] == 'system' %}\n{{- '[INST] <>\\n' + messages[idx]['content'] + '\\n<>\\n\\n' -}}\n{%- elif messages[idx]['role'] == 'assistant' -%}\n{{- ' ' + messages[idx]['content'] + ' ' + eos_token -}}\n{% endif %}\n{% endfor %}", // 'data' => [ // 'messages' => EXAMPLE_CHAT, // 'bos_token' => "", @@ -323,33 +320,33 @@ // ], // 'target' => "Hello, how are you? [/INST] I'm doing great. How can I help you today? [INST] I'd like to show off how chat templating works! [/INST]" // ], - "maywell/Synatra-Mixtral-8x7B" => [ - 'chat_template' => "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n{% for message in messages %}{% if message['role'] == 'user' %}### Instruction:\n{{ message['content']|trim -}}{% if not loop.last %}{% endif %}\n{% elif message['role'] == 'assistant' %}### Response:\n{{ message['content']|trim -}}{% if not loop.last %}{% endif %}\n{% elif message['role'] == 'system' %}{{ message['content']|trim -}}{% if not loop.last %}{% endif %}\n{% endif %}\n{% endfor %}\n{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}\n### Response:\n{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "", - 'eos_token' => "", + 'maywell/Synatra-Mixtral-8x7B' => [ + 'chatTemplate' => "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n{% for message in messages %}{% if message['role'] == 'user' %}### Instruction:\n{{ message['content']|trim -}}{% if not loop.last %}{% endif %}\n{% elif message['role'] == 'assistant' %}### Response:\n{{ message['content']|trim -}}{% if not loop.last %}{% endif %}\n{% elif message['role'] == 'system' %}{{ message['content']|trim -}}{% if not loop.last %}{% endif %}\n{% endif %}\n{% endfor %}\n{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}\n### Response:\n{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => "Below is an instruction that describes a task. Write a response that appropriately completes the request.\n\n### Instruction:\nHello, how are you?### Response:\nI'm doing great. How can I help you today?### Instruction:\nI'd like to show off how chat templating works!", ], - "deepseek-ai/deepseek-coder-33b-instruct" => [ - 'chat_template' => "{% if not add_generation_prompt is defined %}\n{% set add_generation_prompt = false %}\n{% endif %}\n{%- set ns = namespace(found=false) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'system' -%}\n {%- set ns.found = true -%}\n {%- endif -%}\n{%- endfor -%}\n{{bos_token}}{%- if not ns.found -%}\n{{'You are an AI programming assistant, utilizing the Deepseek Coder model, developed by Deepseek Company, and you only answer questions related to computer science. For politically sensitive questions, security and privacy issues, and other non-computer science questions, you will refuse to answer\\n'}}\n{%- endif %}\n{%- for message in messages %}\n {%- if message['role'] == 'system' %}\n{{ message['content'] }}\n {%- else %}\n {%- if message['role'] == 'user' %}\n{{'### Instruction:\\n' + message['content'] + '\\n'}}\n {%- else %}\n{{'### Response:\\n' + message['content'] + '\\n<|EOT|>\\n'}}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{% if add_generation_prompt %}\n{{'### Response:'}}\n{% endif %}", - 'data' => [ - 'messages' => EXAMPLE_CHAT, - 'bos_token' => "<|begin▁of▁sentence|>", - 'eos_token' => "<|EOT|>", + 'deepseek-ai/deepseek-coder-33b-instruct' => [ + 'chatTemplate' => "{% if not add_generation_prompt is defined %}\n{% set add_generation_prompt = false %}\n{% endif %}\n{%- set ns = namespace(found=false) -%}\n{%- for message in messages -%}\n {%- if message['role'] == 'system' -%}\n {%- set ns.found = true -%}\n {%- endif -%}\n{%- endfor -%}\n{{bos_token}}{%- if not ns.found -%}\n{{'You are an AI programming assistant, utilizing the Deepseek Coder model, developed by Deepseek Company, and you only answer questions related to computer science. For politically sensitive questions, security and privacy issues, and other non-computer science questions, you will refuse to answer\\n'}}\n{%- endif %}\n{%- for message in messages %}\n {%- if message['role'] == 'system' %}\n{{ message['content'] }}\n {%- else %}\n {%- if message['role'] == 'user' %}\n{{'### Instruction:\\n' + message['content'] + '\\n'}}\n {%- else %}\n{{'### Response:\\n' + message['content'] + '\\n<|EOT|>\\n'}}\n {%- endif %}\n {%- endif %}\n{%- endfor %}\n{% if add_generation_prompt %}\n{{'### Response:'}}\n{% endif %}", + 'data' => [ + 'messages' => EXAMPLE_CHAT, + 'bos_token' => '<|begin▁of▁sentence|>', + 'eos_token' => '<|EOT|>', ], 'target' => "<|begin▁of▁sentence|>You are an AI programming assistant, utilizing the Deepseek Coder model, developed by Deepseek Company, and you only answer questions related to computer science. For politically sensitive questions, security and privacy issues, and other non-computer science questions, you will refuse to answer\n### Instruction:\nHello, how are you?\n### Response:\nI'm doing great. How can I help you today?\n<|EOT|>\n### Instruction:\nI'd like to show off how chat templating works!\n", ], - "meetkai/functionary-medium-v2.2" => [ - 'chat_template' => << [ + 'chatTemplate' => <<' + message['role'] + '\n<|recipient|>all\n<|content|>' + message['content'] + '\n' }}{% elif message['role'] == 'tool' %}\n{{ '<|from|>' + message['name'] + '\n<|recipient|>all\n<|content|>' + message['content'] + '\n' }}{% else %}\n{% set contain_content='no'%}\n{% if message['content'] is not none %}\n{{ '<|from|>assistant\n<|recipient|>all\n<|content|>' + message['content'] }}{% set contain_content='yes'%}\n{% endif %}\n{% if 'tool_calls' in message and message['tool_calls'] is not none %}\n{% for tool_call in message['tool_calls'] %}\n{% set prompt='<|from|>assistant\n<|recipient|>' + tool_call['function']['name'] + '\n<|content|>' + tool_call['function']['arguments'] %}\n{% if loop.index == 1 and contain_content == "no" %}\n{{ prompt }}{% else %}\n{{ '\n' + prompt}}{% endif %}\n{% endfor %}\n{% endif %}\n{{ '<|stop|>\n' }}{% endif %}\n{% endfor %}\n{% if add_generation_prompt %}{{ '<|from|>assistant\n<|recipient|>' }}{% endif %} END, 'data' => [ - 'messages' => EXAMPLE_FUNCTION_CALLING, - 'bos_token' => "", - 'eos_token' => "", + 'messages' => EXAMPLE_FUNCTION_CALLING, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], 'target' => <<< END @@ -357,7 +354,7 @@ END, ], // "fireworks-ai/firefunction-v1" => [ - // 'chat_template' => <<< END + // 'chatTemplate' => <<< END //{%- set message_roles = ['SYSTEM', 'FUNCTIONS', 'USER', 'ASSISTANT', 'TOOL'] -%}\n{%- set ns = namespace(seen_non_system=false, messages=messages, content='', functions=[]) -%}\n{{ bos_token }}\n{#- Basic consistency checks -#}\n{%- if not ns.messages -%}\n {{ raise_exception('No messages') }}\n{%- endif -%}\n{%- if ns.messages[0]['role'] | upper != 'SYSTEM' -%}\n {%- set ns.messages = [{'role': 'SYSTEM', 'content': 'You are a helpful assistant with access to functions. Use them if required.'}] + ns.messages -%}\n{%- endif -%}\n{%- if ns.messages | length < 2 or ns.messages[0]['role'] | upper != 'SYSTEM' or ns.messages[1]['role'] | upper != 'FUNCTIONS' -%}\n {{ raise_exception('Expected either "functions" or ["system", "functions"] as the first messages') }}\n{%- endif -%}\n{%- for message in ns.messages -%}\n {%- set role = message['role'] | upper -%}\n {#- Validation -#}\n {%- if role not in message_roles -%}\n {{ raise_exception('Invalid role ' + message['role'] + '. Only ' + message_roles + ' are supported.') }}\n {%- endif -%}\n {%- set ns.content = message['content'] if message.get('content') else '' -%}\n {#- Move tool calls inside the content -#}\n {%- if 'tool_calls' in message -%}\n {%- for call in message['tool_calls'] -%}\n {%- set ns.content = ns.content + '{"name": "' + call['function']['name'] + '", "arguments": ' + call['function']['arguments'] + '}' -%}\n {%- endfor -%}\n {%- endif -%}\n {%- if role == 'ASSISTANT' and '' not in ns.content -%}\n {%- set ns.content = '' + ns.content -%}\n {%- endif -%}\n {%- if role == 'ASSISTANT' -%}\n {%- set ns.content = ns.content + eos_token -%}\n {%- endif -%}\n {{ role }}: {{ ns.content }}{{ '\\n\\n' }}\n{%- endfor -%}\nASSISTANT:{{ ' ' }}\n //END, // 'data' => [ @@ -370,12 +367,12 @@ //SYSTEM: You are a helpful assistant with access to functions. Use them if required.\n\nFUNCTIONS: [\n {\n "name": "get_stock_price",\n "description": "Get the current stock price",\n "parameters": {\n "type": "object",\n "properties": {\n "symbol": {\n "type": "string",\n "description": "The stock symbol, e.g. AAPL, GOOG"\n }\n },\n "required": [\n "symbol"\n ]\n }\n },\n {\n "name": "check_word_anagram",\n "description": "Check if two words are anagrams of each other",\n "parameters": {\n "type": "object",\n "properties": {\n "word1": {\n "type": "string",\n "description": "The first word"\n },\n "word2": {\n "type": "string",\n "description": "The second word"\n }\n },\n "required": [\n "word1",\n "word2"\n ]\n }\n }\n]\n\nSYSTEM: You are a helpful assistant with access to functions. Use them if required.\n\nUSER: Hi, can you tell me the current stock price of AAPL?\n\nASSISTANT: //END, // ], - "maywell/PiVoT-MoE" => [ - 'chat_template' => "{{ (messages|selectattr('role', 'equalto', 'system')|list|last).content|trim if (messages|selectattr('role', 'equalto', 'system')|list) else '' }}{% for message in messages %}{% if message['role'] == 'system' %}{{ message['content']|trim }}{% elif message['role'] == 'user' %}### Instruction: {{ message['content']|trim }}{% elif message['role'] == 'assistant' %}### Response: {{ message['content']|trim }}{% elif message['role'] == 'user_context' %}### Input: {{ message['content']|trim }}{% endif %}{% if not loop.last %}\n{% endif %}{% endfor %}{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}### Response:{% endif %}", - 'data' => [ - 'messages' => $EXAMPLE_CHAT_WITH_SYSTEM, - 'bos_token' => "", - 'eos_token' => "", + 'maywell/PiVoT-MoE' => [ + 'chatTemplate' => "{{ (messages|selectattr('role', 'equalto', 'system')|list|last).content|trim if (messages|selectattr('role', 'equalto', 'system')|list) else '' }}{% for message in messages %}{% if message['role'] == 'system' %}{{ message['content']|trim }}{% elif message['role'] == 'user' %}### Instruction: {{ message['content']|trim }}{% elif message['role'] == 'assistant' %}### Response: {{ message['content']|trim }}{% elif message['role'] == 'user_context' %}### Input: {{ message['content']|trim }}{% endif %}{% if not loop.last %}\n{% endif %}{% endfor %}{% if add_generation_prompt and messages[-1]['role'] != 'assistant' %}### Response:{% endif %}", + 'data' => [ + 'messages' => $EXAMPLE_CHAT_WITH_SYSTEM, + 'bos_token' => '', + 'eos_token' => '', 'add_generation_prompt' => false, ], // NOTE=> There is a bug in the model's chat template which causes the system prompt diff --git a/tests/Datasets/InterpreterDataset.php b/tests/Datasets/InterpreterDataset.php index b6acac3..a01c302 100644 --- a/tests/Datasets/InterpreterDataset.php +++ b/tests/Datasets/InterpreterDataset.php @@ -2,163 +2,161 @@ declare(strict_types=1); -const EXAMPLE_IF_TEMPLATE = "
\n {% if True %}\n yay\n {% endif %}\n
"; -const EXAMPLE_FOR_TEMPLATE = "{% for item in seq %}\n {{ item }}\n{% endfor %}"; -const EXAMPLE_FOR_TEMPLATE_2 = "{% for item in seq -%}\n {{ item }}\n{% endfor %}"; -const EXAMPLE_FOR_TEMPLATE_3 = "{% for item in seq %}\n {{ item }}\n{%- endfor %}"; -const EXAMPLE_FOR_TEMPLATE_4 = "{% for item in seq -%}\n {{ item }}\n{%- endfor %}"; -const EXAMPLE_COMMENT_TEMPLATE = " {# comment #}\n {# {% if true %} {% endif %} #}\n"; +const EXAMPLE_IF_TEMPLATE = "
\n {% if True %}\n yay\n {% endif %}\n
"; +const EXAMPLE_FOR_TEMPLATE = "{% for item in seq %}\n {{ item }}\n{% endfor %}"; +const EXAMPLE_FOR_TEMPLATE_2 = "{% for item in seq -%}\n {{ item }}\n{% endfor %}"; +const EXAMPLE_FOR_TEMPLATE_3 = "{% for item in seq %}\n {{ item }}\n{%- endfor %}"; +const EXAMPLE_FOR_TEMPLATE_4 = "{% for item in seq -%}\n {{ item }}\n{%- endfor %}"; +const EXAMPLE_COMMENT_TEMPLATE = " {# comment #}\n {# {% if true %} {% endif %} #}\n"; const EXAMPLE_OBJECT_LITERAL_TEMPLATE = "{% set obj = { 'key1': 'value1', 'key2': 'value2' } %}{{ obj.key1 }} - {{ obj.key2 }}"; -const EXAMPLE_OBJECT_GET = "{% set obj = { 'key1': 'value1', 'key2': 'value2' } %}{{ obj.get('key1') }} - {{ obj.get('key3', 'default') }}"; +const EXAMPLE_OBJECT_GET = "{% set obj = { 'key1': 'value1', 'key2': 'value2' } %}{{ obj.get('key1') }} - {{ obj.get('key3', 'default') }}"; dataset('interpreterTestData', [ // If tests 'if (no strip or trim)' => [ - 'template' => EXAMPLE_IF_TEMPLATE, - 'data' => [], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => "
\n \n yay\n \n
", + 'template' => EXAMPLE_IF_TEMPLATE, + 'data' => [], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => "
\n \n yay\n \n
", ], // 'if (strip blocks only)' => [ // 'template' => EXAMPLE_IF_TEMPLATE, // 'data' => [], -// 'lstrip_blocks' => true, -// 'trim_blocks' => false, +// 'lstripBlocks' => true, +// 'trimBlocks' => false, // 'target' => "
\n\n yay\n\n
", // ], 'if (trim blocks only)' => [ - 'template' => EXAMPLE_IF_TEMPLATE, - 'data' => [], - 'lstrip_blocks' => false, - 'trim_blocks' => true, - 'target' => "
\n yay\n
", + 'template' => EXAMPLE_IF_TEMPLATE, + 'data' => [], + 'lstripBlocks' => false, + 'trimBlocks' => true, + 'target' => "
\n yay\n
", ], // 'if (strip and trim blocks)' => [ // 'template' => EXAMPLE_IF_TEMPLATE, // 'data' => [], -// 'lstrip_blocks' => true, -// 'trim_blocks' => true, +// 'lstripBlocks' => true, +// 'trimBlocks' => true, // 'target' => "
\n yay\n
", // ], - // For tests - 'for (no strip or trim)' => [ - 'template' => EXAMPLE_FOR_TEMPLATE, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => "\n 1\n\n 2\n\n 3\n\n 4\n\n 5\n\n 6\n\n 7\n\n 8\n\n 9\n", + 'for (no strip or trim)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => "\n 1\n\n 2\n\n 3\n\n 4\n\n 5\n\n 6\n\n 7\n\n 8\n\n 9\n", ], - 'for (lstrip blocks only)' => [ - 'template' => EXAMPLE_FOR_TEMPLATE, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => true, - 'trim_blocks' => false, - 'target' => "\n 1\n\n 2\n\n 3\n\n 4\n\n 5\n\n 6\n\n 7\n\n 8\n\n 9\n", + 'for (lstrip blocks only)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => true, + 'trimBlocks' => false, + 'target' => "\n 1\n\n 2\n\n 3\n\n 4\n\n 5\n\n 6\n\n 7\n\n 8\n\n 9\n", ], - 'for (trim blocks only)' => [ - 'template' => EXAMPLE_FOR_TEMPLATE, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => false, - 'trim_blocks' => true, - 'target' => " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n", + 'for (trim blocks only)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => false, + 'trimBlocks' => true, + 'target' => " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n", ], - 'for (strip and trim blocks)' => [ - 'template' => EXAMPLE_FOR_TEMPLATE, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => true, - 'trim_blocks' => true, - 'target' => " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n", + 'for (strip and trim blocks)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => true, + 'trimBlocks' => true, + 'target' => " 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9\n", ], - 'for (single line output with no strip or trim)' => [ - 'template' => EXAMPLE_FOR_TEMPLATE_2, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => "1\n2\n3\n4\n5\n6\n7\n8\n9\n", + 'for (single line output with no strip or trim)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE_2, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => "1\n2\n3\n4\n5\n6\n7\n8\n9\n", ], - 'for (single line output, no strip or trim 2)' => [ - 'template' => EXAMPLE_FOR_TEMPLATE_3, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => "\n 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9", + 'for (single line output, no strip or trim 2)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE_3, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => "\n 1\n 2\n 3\n 4\n 5\n 6\n 7\n 8\n 9", ], - 'for (single line output, trim only)' => [ - 'template' => EXAMPLE_FOR_TEMPLATE_3, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => false, - 'trim_blocks' => true, - 'target' => " 1 2 3 4 5 6 7 8 9", + 'for (single line output, trim only)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE_3, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => false, + 'trimBlocks' => true, + 'target' => ' 1 2 3 4 5 6 7 8 9', ], - 'for (single line output, no strip or trim 3)' => [ - 'template' => - EXAMPLE_FOR_TEMPLATE_4, - 'data' => ['seq' => range(1, 9)], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => "123456789", + 'for (single line output, no strip or trim 3)' => [ + 'template' => EXAMPLE_FOR_TEMPLATE_4, + 'data' => ['seq' => \range(1, 9)], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => '123456789', ], // Comment tests 'comment (basic)' => [ - 'template' => EXAMPLE_COMMENT_TEMPLATE, - 'data' => [], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => " \n ", + 'template' => EXAMPLE_COMMENT_TEMPLATE, + 'data' => [], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => " \n ", ], // 'comment (lstrip only)' => [ // 'template' => EXAMPLE_COMMENT_TEMPLATE, // 'data' => [], -// 'lstrip_blocks' => true, -// 'trim_blocks' => false, +// 'lstripBlocks' => true, +// 'trimBlocks' => false, // 'target' => "\n", // ], // 'comment (trim only)' => [ // 'template' => EXAMPLE_COMMENT_TEMPLATE, // 'data' => [], -// 'lstrip_blocks' => false, -// 'trim_blocks' => true, +// 'lstripBlocks' => false, +// 'trimBlocks' => true, // 'target' => " ", // ], // 'comment (lstrip and trim)' => [ // 'template' => EXAMPLE_COMMENT_TEMPLATE, // 'data' => [], -// 'lstrip_blocks' => true, -// 'trim_blocks' => true, +// 'lstripBlocks' => true, +// 'trimBlocks' => true, // 'target' => "", // ], // Object literal tests 'object literal (no strip or trim)' => [ - 'template' => EXAMPLE_OBJECT_LITERAL_TEMPLATE, - 'data' => [], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => "value1 - value2", + 'template' => EXAMPLE_OBJECT_LITERAL_TEMPLATE, + 'data' => [], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => 'value1 - value2', ], 'object literal (strip and trim)' => [ - 'template' => EXAMPLE_OBJECT_LITERAL_TEMPLATE, - 'data' => [], - 'lstrip_blocks' => true, - 'trim_blocks' => true, - 'target' => "value1 - value2", + 'template' => EXAMPLE_OBJECT_LITERAL_TEMPLATE, + 'data' => [], + 'lstripBlocks' => true, + 'trimBlocks' => true, + 'target' => 'value1 - value2', ], 'object get method (no strip or trim)' => [ - 'template' => EXAMPLE_OBJECT_GET, - 'data' => [], - 'lstrip_blocks' => false, - 'trim_blocks' => false, - 'target' => "value1 - default", + 'template' => EXAMPLE_OBJECT_GET, + 'data' => [], + 'lstripBlocks' => false, + 'trimBlocks' => false, + 'target' => 'value1 - default', ], 'object get method (strip and trim)' => [ - 'template' => EXAMPLE_OBJECT_GET, - 'data' => [], - 'lstrip_blocks' => true, - 'trim_blocks' => true, - 'target' => "value1 - default", + 'template' => EXAMPLE_OBJECT_GET, + 'data' => [], + 'lstripBlocks' => true, + 'trimBlocks' => true, + 'target' => 'value1 - default', ], -]); \ No newline at end of file +]); diff --git a/tests/End2EndTest.php b/tests/End2EndTest.php index bf15ee7..072fdbf 100644 --- a/tests/End2EndTest.php +++ b/tests/End2EndTest.php @@ -2,7 +2,6 @@ declare(strict_types=1); - use Codewithkyrian\Jinja\Template; test('Default Templates', function ($chatTemplate, $data, $target) { @@ -11,8 +10,8 @@ expect($template->render($data))->toBe($target); })->with('defaultTemplates'); -test('Custom Templates',function ($chatTemplate, $data, $target) { +test('Custom Templates', function ($chatTemplate, $data, $target) { $template = new Template($chatTemplate); expect($template->render($data))->toBe($target); -})->with('customTemplates'); \ No newline at end of file +})->with('customTemplates'); diff --git a/tests/Pest.php b/tests/Pest.php index a814366..174d7fd 100644 --- a/tests/Pest.php +++ b/tests/Pest.php @@ -1 +1,3 @@ - Lexer::tokenize($text))->toThrow(SyntaxError::class); + $text = '{{ variable'; + expect(fn () => Lexer::tokenize($text))->toThrow(SyntaxError::class); }); test('Unclosed string literal', function () { $text = "{{ 'unclosed string }}"; - expect(fn() => Lexer::tokenize($text))->toThrow(SyntaxError::class); + expect(fn () => Lexer::tokenize($text))->toThrow(SyntaxError::class); }); test('Unexpected character', function () { - $text = "{{ invalid ! invalid }}"; - expect(fn() => Lexer::tokenize($text))->toThrow(SyntaxError::class); + $text = '{{ invalid ! invalid }}'; + expect(fn () => Lexer::tokenize($text))->toThrow(SyntaxError::class); }); test('Invalid quote character', function () { $text = "{{ \u2018text\u2019 }}"; - expect(fn() => Lexer::tokenize($text))->toThrow(SyntaxError::class); + expect(fn () => Lexer::tokenize($text))->toThrow(SyntaxError::class); }); }); describe('Parsing Errors', function () { // Parsing errors test('Unclosed statement', function () { - $text = "{{ variable }}{{"; + $text = '{{ variable }}{{'; $tokens = Lexer::tokenize($text); - expect(fn() => Parser::make($tokens)->parse())->toThrow(SyntaxError::class); + expect(fn () => Parser::make($tokens)->parse())->toThrow(SyntaxError::class); }); test('Unclosed expression', function () { - $text = "{% if condition %}\n Content"; + $text = "{% if condition %}\n Content"; $tokens = Lexer::tokenize($text); - expect(fn() => Parser::make($tokens)->parse())->toThrow(ParserException::class); + expect(fn () => Parser::make($tokens)->parse())->toThrow(ParserException::class); }); test('Unmatched control structure', function () { - $text = "{% if condition %}\n Content\n{% endif %}\n{% endfor %}"; + $text = "{% if condition %}\n Content\n{% endif %}\n{% endfor %}"; $tokens = Lexer::tokenize($text); - expect(fn() => Parser::make($tokens)->parse())->toThrow(SyntaxError::class); + expect(fn () => Parser::make($tokens)->parse())->toThrow(SyntaxError::class); }); // test('Missing variable in for loop', function () { @@ -66,9 +65,9 @@ // }); test('Invalid variable name', function () { - $text = "{{ 1variable }}"; + $text = '{{ 1variable }}'; $tokens = Lexer::tokenize($text); - expect(fn() => Parser::make($tokens)->parse())->toThrow(ParserException::class); + expect(fn () => Parser::make($tokens)->parse())->toThrow(ParserException::class); }); // test('Invalid control structure usage', function () { @@ -81,39 +80,39 @@ describe('Runtime Errors', function () { // Runtime errors test('Undefined function call', function () { - $env = new Environment(); + $env = new Environment(); $interpreter = new Interpreter($env); - $tokens = Lexer::tokenize("{{ undefined_function() }}"); - $ast = Parser::make($tokens)->parse(); - expect(fn() => $interpreter->run($ast))->toThrow(RuntimeException::class); + $tokens = Lexer::tokenize('{{ undefined_function() }}'); + $ast = Parser::make($tokens)->parse(); + expect(fn () => $interpreter->run($ast))->toThrow(RuntimeException::class); }); test('Incorrect function call', function () { $env = new Environment(); - $env->set("true", true); + $env->set('true', true); $interpreter = new Interpreter($env); - $tokens = Lexer::tokenize("{{ true() }}"); - $ast = Parser::make($tokens)->parse(); - expect(fn() => $interpreter->run($ast))->toThrow(RuntimeException::class); + $tokens = Lexer::tokenize('{{ true() }}'); + $ast = Parser::make($tokens)->parse(); + expect(fn () => $interpreter->run($ast))->toThrow(RuntimeException::class); }); test('Looping over non-iterable', function () { - $env = new Environment(); + $env = new Environment(); $interpreter = new Interpreter($env); - $env->set("non_iterable", 10); + $env->set('non_iterable', 10); - $tokens = Lexer::tokenize("{% for item in non_iterable %}{{ item }}{% endfor %}"); - $ast = Parser::make($tokens)->parse(); - expect(fn() => $interpreter->run($ast))->toThrow(RuntimeException::class); + $tokens = Lexer::tokenize('{% for item in non_iterable %}{{ item }}{% endfor %}'); + $ast = Parser::make($tokens)->parse(); + expect(fn () => $interpreter->run($ast))->toThrow(RuntimeException::class); }); test('Invalid variable assignment', function () { - $env = new Environment(); + $env = new Environment(); $interpreter = new Interpreter($env); - $tokens = Lexer::tokenize("{% set 42 = variable %}"); - $ast = Parser::make($tokens)->parse(); - expect(fn() => $interpreter->run($ast))->toThrow(RuntimeException::class); + $tokens = Lexer::tokenize('{% set 42 = variable %}'); + $ast = Parser::make($tokens)->parse(); + expect(fn () => $interpreter->run($ast))->toThrow(RuntimeException::class); }); }); diff --git a/tests/UtilsTest.php b/tests/UtilsTest.php index 217a16b..868eb14 100644 --- a/tests/UtilsTest.php +++ b/tests/UtilsTest.php @@ -1,34 +1,38 @@ array = range(0, 9); + $this->array = \range(0, 9); }); it('slices the whole array with [:]', function () { expect(slice($this->array))->toEqual($this->array); }); - it('slices the array with [start:end]', function () { expect(slice($this->array, 0, 3))->toEqual([0, 1, 2]) ->and(slice($this->array, 0, 0))->toEqual([]) ->and(slice($this->array, 0, 100))->toEqual($this->array) - ->and(slice($this->array, 100, 100))->toEqual([]); + ->and(slice($this->array, 100, 100))->toEqual([]) + ; }); it('slices the array with [start:end:step]', function () { expect(slice($this->array, 1, 4, 2))->toEqual([1, 3]) ->and(slice($this->array, 1, 8, 3))->toEqual([1, 4, 7]) - ->and(slice($this->array, 1, 8, 10))->toEqual([1]); + ->and(slice($this->array, 1, 8, 10))->toEqual([1]) + ; }); // Add similar tests for other cases it('slices with negative start index', function () { expect(slice($this->array, -3))->toEqual([7, 8, 9]) - ->and(slice($this->array, -10))->toEqual($this->array); + ->and(slice($this->array, -10))->toEqual($this->array) + ; }); it('slices with negative start and end index', function () { @@ -36,9 +40,10 @@ ->and(slice($this->array, -1, -1))->toEqual([]) ->and(slice($this->array, -3, -5))->toEqual([]) ->and(slice($this->array, -100, -90))->toEqual([]) - ->and(slice($this->array, -100, -1))->toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]); + ->and(slice($this->array, -100, -1))->toEqual([0, 1, 2, 3, 4, 5, 6, 7, 8]) + ; }); it('slices with negative start, end, and step', function () { expect(slice($this->array, -3, -1, 2))->toEqual([7]); -}); \ No newline at end of file +}); From 41837214693629dba5281d5fcf4c68f297724421 Mon Sep 17 00:00:00 2001 From: Kyrian Obikwelu Date: Mon, 27 Oct 2025 12:26:54 +0100 Subject: [PATCH 2/2] chore: update .gitignore and remove composer.lock --- .github/workflows/test.yml | 2 +- .gitignore | 3 +- composer.lock | 5690 ------------------------------------ 3 files changed, 3 insertions(+), 5692 deletions(-) delete mode 100644 composer.lock diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 788452f..430b8b3 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -1,6 +1,6 @@ name: tests -on: [ 'push', 'pull_request' ] +on: pull_request jobs: test: diff --git a/.gitignore b/.gitignore index b38c6b2..0c653ab 100644 --- a/.gitignore +++ b/.gitignore @@ -1,4 +1,5 @@ vendor .idea .claude -.php-cs-fixer.cache \ No newline at end of file +.php-cs-fixer.cache +composer.lock \ No newline at end of file diff --git a/composer.lock b/composer.lock deleted file mode 100644 index 814532a..0000000 --- a/composer.lock +++ /dev/null @@ -1,5690 +0,0 @@ -{ - "_readme": [ - "This file locks the dependencies of your project to a known state", - "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", - "This file is @generated automatically" - ], - "content-hash": "eaa9949d6c6b19a2c2685e18485f436d", - "packages": [], - "packages-dev": [ - { - "name": "brianium/paratest", - "version": "v7.8.4", - "source": { - "type": "git", - "url": "https://github.com/paratestphp/paratest.git", - "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/130a9bf0e269ee5f5b320108f794ad03e275cad4", - "reference": "130a9bf0e269ee5f5b320108f794ad03e275cad4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.2.0", - "jean85/pretty-package-versions": "^2.1.1", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^11.0.10", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-timer": "^7.0.1", - "phpunit/phpunit": "^11.5.24", - "sebastian/environment": "^7.2.1", - "symfony/console": "^6.4.22 || ^7.3.0", - "symfony/process": "^6.4.20 || ^7.3.0" - }, - "require-dev": { - "doctrine/coding-standard": "^12.0.0", - "ext-pcov": "*", - "ext-posix": "*", - "phpstan/phpstan": "^2.1.17", - "phpstan/phpstan-deprecation-rules": "^2.0.3", - "phpstan/phpstan-phpunit": "^2.0.6", - "phpstan/phpstan-strict-rules": "^2.0.4", - "squizlabs/php_codesniffer": "^3.13.2", - "symfony/filesystem": "^6.4.13 || ^7.3.0" - }, - "bin": [ - "bin/paratest", - "bin/paratest_for_phpstorm" - ], - "type": "library", - "autoload": { - "psr-4": { - "ParaTest\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Brian Scaturro", - "email": "scaturrob@gmail.com", - "role": "Developer" - }, - { - "name": "Filippo Tessarotto", - "email": "zoeslam@gmail.com", - "role": "Developer" - } - ], - "description": "Parallel testing for PHP", - "homepage": "https://github.com/paratestphp/paratest", - "keywords": [ - "concurrent", - "parallel", - "phpunit", - "testing" - ], - "support": { - "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.8.4" - }, - "funding": [ - { - "url": "https://github.com/sponsors/Slamdunk", - "type": "github" - }, - { - "url": "https://paypal.me/filippotessarotto", - "type": "paypal" - } - ], - "time": "2025-06-23T06:07:21+00:00" - }, - { - "name": "clue/ndjson-react", - "version": "v1.3.0", - "source": { - "type": "git", - "url": "https://github.com/clue/reactphp-ndjson.git", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", - "shasum": "" - }, - "require": { - "php": ">=5.3", - "react/stream": "^1.2" - }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", - "react/event-loop": "^1.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Clue\\React\\NDJson\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering" - } - ], - "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", - "homepage": "https://github.com/clue/reactphp-ndjson", - "keywords": [ - "NDJSON", - "json", - "jsonlines", - "newline", - "reactphp", - "streaming" - ], - "support": { - "issues": "https://github.com/clue/reactphp-ndjson/issues", - "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" - }, - "funding": [ - { - "url": "https://clue.engineering/support", - "type": "custom" - }, - { - "url": "https://github.com/clue", - "type": "github" - } - ], - "time": "2022-12-23T10:58:28+00:00" - }, - { - "name": "composer/pcre", - "version": "3.3.2", - "source": { - "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<1.11.10" - }, - "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" - }, - "type": "library", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - }, - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Pcre\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - } - ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", - "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" - ], - "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" - }, - "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-11-12T16:29:46+00:00" - }, - { - "name": "composer/semver", - "version": "3.4.4", - "source": { - "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "reference": "198166618906cb2de69b95d7d47e5fa8aa1b2b95", - "shasum": "" - }, - "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Composer\\Semver\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nils Adermann", - "email": "naderman@naderman.de", - "homepage": "http://www.naderman.de" - }, - { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" - }, - { - "name": "Rob Bast", - "email": "rob.bast@gmail.com", - "homepage": "http://robbast.nl" - } - ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", - "keywords": [ - "semantic", - "semver", - "validation", - "versioning" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.4" - }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - } - ], - "time": "2025-08-20T19:15:30+00:00" - }, - { - "name": "composer/xdebug-handler", - "version": "3.0.5", - "source": { - "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", - "shasum": "" - }, - "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" - }, - "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Composer\\XdebugHandler\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" - } - ], - "description": "Restarts a process without Xdebug.", - "keywords": [ - "Xdebug", - "performance" - ], - "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/xdebug-handler/issues", - "source": "https://github.com/composer/xdebug-handler/tree/3.0.5" - }, - "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-05-06T16:37:16+00:00" - }, - { - "name": "doctrine/deprecations", - "version": "1.1.5", - "source": { - "type": "git", - "url": "https://github.com/doctrine/deprecations.git", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "phpunit/phpunit": "<=7.5 || >=13" - }, - "require-dev": { - "doctrine/coding-standard": "^9 || ^12 || ^13", - "phpstan/phpstan": "1.4.10 || 2.1.11", - "phpstan/phpstan-phpunit": "^1.0 || ^2", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", - "psr/log": "^1 || ^2 || ^3" - }, - "suggest": { - "psr/log": "Allows logging deprecations via PSR-3 logger implementation" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Deprecations\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A small layer on top of trigger_error(E_USER_DEPRECATED) or PSR-3 logging with options to disable all deprecations or selectively for packages.", - "homepage": "https://www.doctrine-project.org/", - "support": { - "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.5" - }, - "time": "2025-04-07T20:06:18+00:00" - }, - { - "name": "evenement/evenement", - "version": "v3.0.2", - "source": { - "type": "git", - "url": "https://github.com/igorw/evenement.git", - "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", - "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", - "shasum": "" - }, - "require": { - "php": ">=7.0" - }, - "require-dev": { - "phpunit/phpunit": "^9 || ^6" - }, - "type": "library", - "autoload": { - "psr-4": { - "Evenement\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" - } - ], - "description": "Événement is a very simple event dispatching library for PHP", - "keywords": [ - "event-dispatcher", - "event-emitter" - ], - "support": { - "issues": "https://github.com/igorw/evenement/issues", - "source": "https://github.com/igorw/evenement/tree/v3.0.2" - }, - "time": "2023-08-08T05:53:35+00:00" - }, - { - "name": "fidry/cpu-core-counter", - "version": "1.3.0", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", - "reference": "db9508f7b1474469d9d3c53b86f817e344732678", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "require-dev": { - "fidry/makefile": "^0.2.0", - "fidry/php-cs-fixer-config": "^1.1.2", - "phpstan/extension-installer": "^1.2.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-deprecation-rules": "^2.0.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^8.5.31 || ^9.5.26", - "webmozarts/strict-phpunit": "^7.5" - }, - "type": "library", - "autoload": { - "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" - } - ], - "description": "Tiny utility to get the number of CPU cores.", - "keywords": [ - "CPU", - "core" - ], - "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" - }, - "funding": [ - { - "url": "https://github.com/theofidry", - "type": "github" - } - ], - "time": "2025-08-14T07:29:31+00:00" - }, - { - "name": "filp/whoops", - "version": "2.18.4", - "source": { - "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "reference": "d2102955e48b9fd9ab24280a7ad12ed552752c4d", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" - }, - "require-dev": { - "mockery/mockery": "^1.0", - "phpunit/phpunit": "^7.5.20 || ^8.5.8 || ^9.3.3", - "symfony/var-dumper": "^4.0 || ^5.0" - }, - "suggest": { - "symfony/var-dumper": "Pretty print complex values better with var-dumper available", - "whoops/soap": "Formats errors as SOAP responses" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.7-dev" - } - }, - "autoload": { - "psr-4": { - "Whoops\\": "src/Whoops/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" - } - ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", - "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" - ], - "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.4" - }, - "funding": [ - { - "url": "https://github.com/denis-sokolov", - "type": "github" - } - ], - "time": "2025-08-08T12:00:00+00:00" - }, - { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.89.1", - "source": { - "type": "git", - "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "f34967da2866ace090a2b447de1f357356474573" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/f34967da2866ace090a2b447de1f357356474573", - "reference": "f34967da2866ace090a2b447de1f357356474573", - "shasum": "" - }, - "require": { - "clue/ndjson-react": "^1.3", - "composer/semver": "^3.4", - "composer/xdebug-handler": "^3.0.5", - "ext-filter": "*", - "ext-hash": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "fidry/cpu-core-counter": "^1.3", - "php": "^7.4 || ^8.0", - "react/child-process": "^0.6.6", - "react/event-loop": "^1.5", - "react/socket": "^1.16", - "react/stream": "^1.4", - "sebastian/diff": "^4.0.6 || ^5.1.1 || ^6.0.2 || ^7.0", - "symfony/console": "^5.4.47 || ^6.4.24 || ^7.0", - "symfony/event-dispatcher": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/filesystem": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/finder": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/options-resolver": "^5.4.45 || ^6.4.24 || ^7.0", - "symfony/polyfill-mbstring": "^1.33", - "symfony/polyfill-php80": "^1.33", - "symfony/polyfill-php81": "^1.33", - "symfony/polyfill-php84": "^1.33", - "symfony/process": "^5.4.47 || ^6.4.24 || ^7.2", - "symfony/stopwatch": "^5.4.45 || ^6.4.24 || ^7.0" - }, - "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.7", - "infection/infection": "^0.31.0", - "justinrainbow/json-schema": "^6.5", - "keradus/cli-executor": "^2.2", - "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.8", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.25 || ^10.5.53 || ^11.5.34", - "symfony/var-dumper": "^5.4.48 || ^6.4.24 || ^7.3.2", - "symfony/yaml": "^5.4.45 || ^6.4.24 || ^7.3.2" - }, - "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." - }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", - "autoload": { - "psr-4": { - "PhpCsFixer\\": "src/" - }, - "exclude-from-classmap": [ - "src/Fixer/Internal/*" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" - } - ], - "description": "A tool to automatically fix PHP code style", - "keywords": [ - "Static code analysis", - "fixer", - "standards", - "static analysis" - ], - "support": { - "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.89.1" - }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2025-10-24T12:05:10+00:00" - }, - { - "name": "jean85/pretty-package-versions", - "version": "2.1.1", - "source": { - "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", - "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", - "shasum": "" - }, - "require": { - "composer-runtime-api": "^2.1.0", - "php": "^7.4|^8.0" - }, - "require-dev": { - "friendsofphp/php-cs-fixer": "^3.2", - "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^2.0", - "phpunit/phpunit": "^7.5|^8.5|^9.6", - "rector/rector": "^2.0", - "vimeo/psalm": "^4.3 || ^5.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "Jean85\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" - } - ], - "description": "A library to get pretty versions strings of installed dependencies", - "keywords": [ - "composer", - "package", - "release", - "versions" - ], - "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" - }, - "time": "2025-03-19T14:43:43+00:00" - }, - { - "name": "myclabs/deep-copy", - "version": "1.13.4", - "source": { - "type": "git", - "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "reference": "07d290f0c47959fd5eed98c95ee5602db07e0b6a", - "shasum": "" - }, - "require": { - "php": "^7.1 || ^8.0" - }, - "conflict": { - "doctrine/collections": "<1.6.8", - "doctrine/common": "<2.13.3 || >=3 <3.2.2" - }, - "require-dev": { - "doctrine/collections": "^1.6.8", - "doctrine/common": "^2.13.3 || ^3.2.2", - "phpspec/prophecy": "^1.10", - "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" - }, - "type": "library", - "autoload": { - "files": [ - "src/DeepCopy/deep_copy.php" - ], - "psr-4": { - "DeepCopy\\": "src/DeepCopy/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "Create deep copies (clones) of your objects", - "keywords": [ - "clone", - "copy", - "duplicate", - "object", - "object graph" - ], - "support": { - "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.4" - }, - "funding": [ - { - "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", - "type": "tidelift" - } - ], - "time": "2025-08-01T08:46:24+00:00" - }, - { - "name": "nikic/php-parser", - "version": "v5.6.2", - "source": { - "type": "git", - "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/3a454ca033b9e06b63282ce19562e892747449bb", - "reference": "3a454ca033b9e06b63282ce19562e892747449bb", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-json": "*", - "ext-tokenizer": "*", - "php": ">=7.4" - }, - "require-dev": { - "ircmaxell/php-yacc": "^0.0.7", - "phpunit/phpunit": "^9.0" - }, - "bin": [ - "bin/php-parse" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "PhpParser\\": "lib/PhpParser" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Nikita Popov" - } - ], - "description": "A PHP parser written in PHP", - "keywords": [ - "parser", - "php" - ], - "support": { - "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.6.2" - }, - "time": "2025-10-21T19:32:17+00:00" - }, - { - "name": "nunomaduro/collision", - "version": "v8.8.2", - "source": { - "type": "git", - "url": "https://github.com/nunomaduro/collision.git", - "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", - "reference": "60207965f9b7b7a4ce15a0f75d57f9dadb105bdb", - "shasum": "" - }, - "require": { - "filp/whoops": "^2.18.1", - "nunomaduro/termwind": "^2.3.1", - "php": "^8.2.0", - "symfony/console": "^7.3.0" - }, - "conflict": { - "laravel/framework": "<11.44.2 || >=13.0.0", - "phpunit/phpunit": "<11.5.15 || >=13.0.0" - }, - "require-dev": { - "brianium/paratest": "^7.8.3", - "larastan/larastan": "^3.4.2", - "laravel/framework": "^11.44.2 || ^12.18", - "laravel/pint": "^1.22.1", - "laravel/sail": "^1.43.1", - "laravel/sanctum": "^4.1.1", - "laravel/tinker": "^2.10.1", - "orchestra/testbench-core": "^9.12.0 || ^10.4", - "pestphp/pest": "^3.8.2", - "sebastian/environment": "^7.2.1 || ^8.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" - ] - }, - "branch-alias": { - "dev-8.x": "8.x-dev" - } - }, - "autoload": { - "files": [ - "./src/Adapters/Phpunit/Autoload.php" - ], - "psr-4": { - "NunoMaduro\\Collision\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Cli error handling for console/command-line PHP applications.", - "keywords": [ - "artisan", - "cli", - "command-line", - "console", - "dev", - "error", - "handling", - "laravel", - "laravel-zero", - "php", - "symfony" - ], - "support": { - "issues": "https://github.com/nunomaduro/collision/issues", - "source": "https://github.com/nunomaduro/collision" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], - "time": "2025-06-25T02:12:12+00:00" - }, - { - "name": "nunomaduro/termwind", - "version": "v2.3.2", - "source": { - "type": "git", - "url": "https://github.com/nunomaduro/termwind.git", - "reference": "eb61920a53057a7debd718a5b89c2178032b52c0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/eb61920a53057a7debd718a5b89c2178032b52c0", - "reference": "eb61920a53057a7debd718a5b89c2178032b52c0", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": "^8.2", - "symfony/console": "^7.3.4" - }, - "require-dev": { - "illuminate/console": "^11.46.1", - "laravel/pint": "^1.25.1", - "mockery/mockery": "^1.6.12", - "pestphp/pest": "^2.36.0 || ^3.8.4", - "phpstan/phpstan": "^1.12.32", - "phpstan/phpstan-strict-rules": "^1.6.2", - "symfony/var-dumper": "^7.3.4", - "thecodingmachine/phpstan-strict-rules": "^1.0.0" - }, - "type": "library", - "extra": { - "laravel": { - "providers": [ - "Termwind\\Laravel\\TermwindServiceProvider" - ] - }, - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "files": [ - "src/Functions.php" - ], - "psr-4": { - "Termwind\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Its like Tailwind CSS, but for the console.", - "keywords": [ - "cli", - "console", - "css", - "package", - "php", - "style" - ], - "support": { - "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.2" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://github.com/xiCO2k", - "type": "github" - } - ], - "time": "2025-10-18T11:10:27+00:00" - }, - { - "name": "pestphp/pest", - "version": "v3.8.4", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest.git", - "reference": "72cf695554420e21858cda831d5db193db102574" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest/zipball/72cf695554420e21858cda831d5db193db102574", - "reference": "72cf695554420e21858cda831d5db193db102574", - "shasum": "" - }, - "require": { - "brianium/paratest": "^7.8.4", - "nunomaduro/collision": "^8.8.2", - "nunomaduro/termwind": "^2.3.1", - "pestphp/pest-plugin": "^3.0.0", - "pestphp/pest-plugin-arch": "^3.1.1", - "pestphp/pest-plugin-mutate": "^3.0.5", - "php": "^8.2.0", - "phpunit/phpunit": "^11.5.33" - }, - "conflict": { - "filp/whoops": "<2.16.0", - "phpunit/phpunit": ">11.5.33", - "sebastian/exporter": "<6.0.0", - "webmozart/assert": "<1.11.0" - }, - "require-dev": { - "pestphp/pest-dev-tools": "^3.4.0", - "pestphp/pest-plugin-type-coverage": "^3.6.1", - "symfony/process": "^7.3.0" - }, - "bin": [ - "bin/pest" - ], - "type": "library", - "extra": { - "pest": { - "plugins": [ - "Pest\\Mutate\\Plugins\\Mutate", - "Pest\\Plugins\\Configuration", - "Pest\\Plugins\\Bail", - "Pest\\Plugins\\Cache", - "Pest\\Plugins\\Coverage", - "Pest\\Plugins\\Init", - "Pest\\Plugins\\Environment", - "Pest\\Plugins\\Help", - "Pest\\Plugins\\Memory", - "Pest\\Plugins\\Only", - "Pest\\Plugins\\Printer", - "Pest\\Plugins\\ProcessIsolation", - "Pest\\Plugins\\Profile", - "Pest\\Plugins\\Retry", - "Pest\\Plugins\\Snapshot", - "Pest\\Plugins\\Verbose", - "Pest\\Plugins\\Version", - "Pest\\Plugins\\Parallel" - ] - }, - "phpstan": { - "includes": [ - "extension.neon" - ] - } - }, - "autoload": { - "files": [ - "src/Functions.php", - "src/Pest.php" - ], - "psr-4": { - "Pest\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "The elegant PHP Testing Framework.", - "keywords": [ - "framework", - "pest", - "php", - "test", - "testing", - "unit" - ], - "support": { - "issues": "https://github.com/pestphp/pest/issues", - "source": "https://github.com/pestphp/pest/tree/v3.8.4" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2025-08-20T19:12:42+00:00" - }, - { - "name": "pestphp/pest-plugin", - "version": "v3.0.0", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin.git", - "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e79b26c65bc11c41093b10150c1341cc5cdbea83", - "reference": "e79b26c65bc11c41093b10150c1341cc5cdbea83", - "shasum": "" - }, - "require": { - "composer-plugin-api": "^2.0.0", - "composer-runtime-api": "^2.2.2", - "php": "^8.2" - }, - "conflict": { - "pestphp/pest": "<3.0.0" - }, - "require-dev": { - "composer/composer": "^2.7.9", - "pestphp/pest": "^3.0.0", - "pestphp/pest-dev-tools": "^3.0.0" - }, - "type": "composer-plugin", - "extra": { - "class": "Pest\\Plugin\\Manager" - }, - "autoload": { - "psr-4": { - "Pest\\Plugin\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "The Pest plugin manager", - "keywords": [ - "framework", - "manager", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin/tree/v3.0.0" - }, - "funding": [ - { - "url": "https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=66BYDWAT92N6L", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - }, - { - "url": "https://www.patreon.com/nunomaduro", - "type": "patreon" - } - ], - "time": "2024-09-08T23:21:41+00:00" - }, - { - "name": "pestphp/pest-plugin-arch", - "version": "v3.1.1", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin-arch.git", - "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/db7bd9cb1612b223e16618d85475c6f63b9c8daa", - "reference": "db7bd9cb1612b223e16618d85475c6f63b9c8daa", - "shasum": "" - }, - "require": { - "pestphp/pest-plugin": "^3.0.0", - "php": "^8.2", - "ta-tikoma/phpunit-architecture-test": "^0.8.4" - }, - "require-dev": { - "pestphp/pest": "^3.8.1", - "pestphp/pest-dev-tools": "^3.4.0" - }, - "type": "library", - "extra": { - "pest": { - "plugins": [ - "Pest\\Arch\\Plugin" - ] - } - }, - "autoload": { - "files": [ - "src/Autoload.php" - ], - "psr-4": { - "Pest\\Arch\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "The Arch plugin for Pest PHP.", - "keywords": [ - "arch", - "architecture", - "framework", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin-arch/tree/v3.1.1" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2025-04-16T22:59:48+00:00" - }, - { - "name": "pestphp/pest-plugin-mutate", - "version": "v3.0.5", - "source": { - "type": "git", - "url": "https://github.com/pestphp/pest-plugin-mutate.git", - "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/pestphp/pest-plugin-mutate/zipball/e10dbdc98c9e2f3890095b4fe2144f63a5717e08", - "reference": "e10dbdc98c9e2f3890095b4fe2144f63a5717e08", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.2.0", - "pestphp/pest-plugin": "^3.0.0", - "php": "^8.2", - "psr/simple-cache": "^3.0.0" - }, - "require-dev": { - "pestphp/pest": "^3.0.8", - "pestphp/pest-dev-tools": "^3.0.0", - "pestphp/pest-plugin-type-coverage": "^3.0.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Pest\\Mutate\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Sandro Gehri", - "email": "sandrogehri@gmail.com" - } - ], - "description": "Mutates your code to find untested cases", - "keywords": [ - "framework", - "mutate", - "mutation", - "pest", - "php", - "plugin", - "test", - "testing", - "unit" - ], - "support": { - "source": "https://github.com/pestphp/pest-plugin-mutate/tree/v3.0.5" - }, - "funding": [ - { - "url": "https://www.paypal.com/paypalme/enunomaduro", - "type": "custom" - }, - { - "url": "https://github.com/gehrisandro", - "type": "github" - }, - { - "url": "https://github.com/nunomaduro", - "type": "github" - } - ], - "time": "2024-09-22T07:54:40+00:00" - }, - { - "name": "phar-io/manifest", - "version": "2.0.4", - "source": { - "type": "git", - "url": "https://github.com/phar-io/manifest.git", - "reference": "54750ef60c58e43759730615a392c31c80e23176" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/manifest/zipball/54750ef60c58e43759730615a392c31c80e23176", - "reference": "54750ef60c58e43759730615a392c31c80e23176", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-phar": "*", - "ext-xmlwriter": "*", - "phar-io/version": "^3.0.1", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Component for reading phar.io manifest information from a PHP Archive (PHAR)", - "support": { - "issues": "https://github.com/phar-io/manifest/issues", - "source": "https://github.com/phar-io/manifest/tree/2.0.4" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:33:53+00:00" - }, - { - "name": "phar-io/version", - "version": "3.2.1", - "source": { - "type": "git", - "url": "https://github.com/phar-io/version.git", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phar-io/version/zipball/4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "reference": "4f7fd7836c6f332bb2933569e566a0d6c4cbed74", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - }, - { - "name": "Sebastian Heuer", - "email": "sebastian@phpeople.de", - "role": "Developer" - }, - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "Developer" - } - ], - "description": "Library for handling version information and constraints", - "support": { - "issues": "https://github.com/phar-io/version/issues", - "source": "https://github.com/phar-io/version/tree/3.2.1" - }, - "time": "2022-02-21T01:04:05+00:00" - }, - { - "name": "phpdocumentor/reflection-common", - "version": "2.2.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionCommon.git", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionCommon/zipball/1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "reference": "1d01c49d4ed62f25aa84a747ad35d5a16924662b", - "shasum": "" - }, - "require": { - "php": "^7.2 || ^8.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-2.x": "2.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "Common reflection classes used by phpdocumentor to reflect the code structure", - "homepage": "http://www.phpdoc.org", - "keywords": [ - "FQSEN", - "phpDocumentor", - "phpdoc", - "reflection", - "static analysis" - ], - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionCommon/issues", - "source": "https://github.com/phpDocumentor/ReflectionCommon/tree/2.x" - }, - "time": "2020-06-27T09:03:43+00:00" - }, - { - "name": "phpdocumentor/reflection-docblock", - "version": "5.6.3", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/94f8051919d1b0369a6bcc7931d679a511c03fe9", - "reference": "94f8051919d1b0369a6bcc7931d679a511c03fe9", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.1", - "ext-filter": "*", - "php": "^7.4 || ^8.0", - "phpdocumentor/reflection-common": "^2.2", - "phpdocumentor/type-resolver": "^1.7", - "phpstan/phpdoc-parser": "^1.7|^2.0", - "webmozart/assert": "^1.9.1" - }, - "require-dev": { - "mockery/mockery": "~1.3.5 || ~1.6.0", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-mockery": "^1.1", - "phpstan/phpstan-webmozart-assert": "^1.2", - "phpunit/phpunit": "^9.5", - "psalm/phar": "^5.26" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "5.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - }, - { - "name": "Jaap van Otterdijk", - "email": "opensource@ijaap.nl" - } - ], - "description": "With this component, a library can provide support for annotations via DocBlocks or otherwise retrieve information that is embedded in a DocBlock.", - "support": { - "issues": "https://github.com/phpDocumentor/ReflectionDocBlock/issues", - "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.3" - }, - "time": "2025-08-01T19:43:32+00:00" - }, - { - "name": "phpdocumentor/type-resolver", - "version": "1.10.0", - "source": { - "type": "git", - "url": "https://github.com/phpDocumentor/TypeResolver.git", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/TypeResolver/zipball/679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "reference": "679e3ce485b99e84c775d28e2e96fade9a7fb50a", - "shasum": "" - }, - "require": { - "doctrine/deprecations": "^1.0", - "php": "^7.3 || ^8.0", - "phpdocumentor/reflection-common": "^2.0", - "phpstan/phpdoc-parser": "^1.18|^2.0" - }, - "require-dev": { - "ext-tokenizer": "*", - "phpbench/phpbench": "^1.2", - "phpstan/extension-installer": "^1.1", - "phpstan/phpstan": "^1.8", - "phpstan/phpstan-phpunit": "^1.1", - "phpunit/phpunit": "^9.5", - "rector/rector": "^0.13.9", - "vimeo/psalm": "^4.25" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-1.x": "1.x-dev" - } - }, - "autoload": { - "psr-4": { - "phpDocumentor\\Reflection\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Mike van Riel", - "email": "me@mikevanriel.com" - } - ], - "description": "A PSR-5 based resolver of Class names, Types and Structural Element Names", - "support": { - "issues": "https://github.com/phpDocumentor/TypeResolver/issues", - "source": "https://github.com/phpDocumentor/TypeResolver/tree/1.10.0" - }, - "time": "2024-11-09T15:12:26+00:00" - }, - { - "name": "phpstan/phpdoc-parser", - "version": "2.3.0", - "source": { - "type": "git", - "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/1e0cd5370df5dd2e556a36b9c62f62e555870495", - "reference": "1e0cd5370df5dd2e556a36b9c62f62e555870495", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "doctrine/annotations": "^2.0", - "nikic/php-parser": "^5.3.0", - "php-parallel-lint/php-parallel-lint": "^1.2", - "phpstan/extension-installer": "^1.0", - "phpstan/phpstan": "^2.0", - "phpstan/phpstan-phpunit": "^2.0", - "phpstan/phpstan-strict-rules": "^2.0", - "phpunit/phpunit": "^9.6", - "symfony/process": "^5.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPStan\\PhpDocParser\\": [ - "src/" - ] - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPDoc parser with support for nullable, intersection and generic types", - "support": { - "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/2.3.0" - }, - "time": "2025-08-30T15:50:23+00:00" - }, - { - "name": "phpstan/phpstan", - "version": "2.1.31", - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/ead89849d879fe203ce9292c6ef5e7e76f867b96", - "reference": "ead89849d879fe203ce9292c6ef5e7e76f867b96", - "shasum": "" - }, - "require": { - "php": "^7.4|^8.0" - }, - "conflict": { - "phpstan/phpstan-shim": "*" - }, - "bin": [ - "phpstan", - "phpstan.phar" - ], - "type": "library", - "autoload": { - "files": [ - "bootstrap.php" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "PHPStan - PHP Static Analysis Tool", - "keywords": [ - "dev", - "static analysis" - ], - "support": { - "docs": "https://phpstan.org/user-guide/getting-started", - "forum": "https://github.com/phpstan/phpstan/discussions", - "issues": "https://github.com/phpstan/phpstan/issues", - "security": "https://github.com/phpstan/phpstan/security/policy", - "source": "https://github.com/phpstan/phpstan-src" - }, - "funding": [ - { - "url": "https://github.com/ondrejmirtes", - "type": "github" - }, - { - "url": "https://github.com/phpstan", - "type": "github" - } - ], - "time": "2025-10-10T14:14:11+00:00" - }, - { - "name": "phpunit/php-code-coverage", - "version": "11.0.11", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", - "reference": "4f7722aa9a7b76aa775e2d9d4e95d1ea16eeeef4", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-libxml": "*", - "ext-xmlwriter": "*", - "nikic/php-parser": "^5.4.0", - "php": ">=8.2", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-text-template": "^4.0.1", - "sebastian/code-unit-reverse-lookup": "^4.0.1", - "sebastian/complexity": "^4.0.1", - "sebastian/environment": "^7.2.0", - "sebastian/lines-of-code": "^3.0.1", - "sebastian/version": "^5.0.2", - "theseer/tokenizer": "^1.2.3" - }, - "require-dev": { - "phpunit/phpunit": "^11.5.2" - }, - "suggest": { - "ext-pcov": "PHP extension that provides line coverage", - "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "11.0.x-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that provides collection, processing, and rendering functionality for PHP code coverage information.", - "homepage": "https://github.com/sebastianbergmann/php-code-coverage", - "keywords": [ - "coverage", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "security": "https://github.com/sebastianbergmann/php-code-coverage/security/policy", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/11.0.11" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage", - "type": "tidelift" - } - ], - "time": "2025-08-27T14:37:49+00:00" - }, - { - "name": "phpunit/php-file-iterator", - "version": "5.1.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-file-iterator.git", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/118cfaaa8bc5aef3287bf315b6060b1174754af6", - "reference": "118cfaaa8bc5aef3287bf315b6060b1174754af6", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "FilterIterator implementation that filters files based on a list of suffixes.", - "homepage": "https://github.com/sebastianbergmann/php-file-iterator/", - "keywords": [ - "filesystem", - "iterator" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-file-iterator/issues", - "security": "https://github.com/sebastianbergmann/php-file-iterator/security/policy", - "source": "https://github.com/sebastianbergmann/php-file-iterator/tree/5.1.0" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-08-27T05:02:59+00:00" - }, - { - "name": "phpunit/php-invoker", - "version": "5.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-invoker.git", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/c1ca3814734c07492b3d4c5f794f4b0995333da2", - "reference": "c1ca3814734c07492b3d4c5f794f4b0995333da2", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "ext-pcntl": "*", - "phpunit/phpunit": "^11.0" - }, - "suggest": { - "ext-pcntl": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Invoke callables with a timeout", - "homepage": "https://github.com/sebastianbergmann/php-invoker/", - "keywords": [ - "process" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-invoker/issues", - "security": "https://github.com/sebastianbergmann/php-invoker/security/policy", - "source": "https://github.com/sebastianbergmann/php-invoker/tree/5.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:07:44+00:00" - }, - { - "name": "phpunit/php-text-template", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-text-template.git", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "reference": "3e0404dc6b300e6bf56415467ebcb3fe4f33e964", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Simple template engine.", - "homepage": "https://github.com/sebastianbergmann/php-text-template/", - "keywords": [ - "template" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-text-template/issues", - "security": "https://github.com/sebastianbergmann/php-text-template/security/policy", - "source": "https://github.com/sebastianbergmann/php-text-template/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:08:43+00:00" - }, - { - "name": "phpunit/php-timer", - "version": "7.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/php-timer.git", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "reference": "3b415def83fbcb41f991d9ebf16ae4ad8b7837b3", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Utility class for timing", - "homepage": "https://github.com/sebastianbergmann/php-timer/", - "keywords": [ - "timer" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/php-timer/issues", - "security": "https://github.com/sebastianbergmann/php-timer/security/policy", - "source": "https://github.com/sebastianbergmann/php-timer/tree/7.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:09:35+00:00" - }, - { - "name": "phpunit/phpunit", - "version": "11.5.33", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "5965e9ff57546cb9137c0ff6aa78cb7442b05cf6" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/5965e9ff57546cb9137c0ff6aa78cb7442b05cf6", - "reference": "5965e9ff57546cb9137c0ff6aa78cb7442b05cf6", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-json": "*", - "ext-libxml": "*", - "ext-mbstring": "*", - "ext-xml": "*", - "ext-xmlwriter": "*", - "myclabs/deep-copy": "^1.13.4", - "phar-io/manifest": "^2.0.4", - "phar-io/version": "^3.2.1", - "php": ">=8.2", - "phpunit/php-code-coverage": "^11.0.10", - "phpunit/php-file-iterator": "^5.1.0", - "phpunit/php-invoker": "^5.0.1", - "phpunit/php-text-template": "^4.0.1", - "phpunit/php-timer": "^7.0.1", - "sebastian/cli-parser": "^3.0.2", - "sebastian/code-unit": "^3.0.3", - "sebastian/comparator": "^6.3.2", - "sebastian/diff": "^6.0.2", - "sebastian/environment": "^7.2.1", - "sebastian/exporter": "^6.3.0", - "sebastian/global-state": "^7.0.2", - "sebastian/object-enumerator": "^6.0.1", - "sebastian/type": "^5.1.3", - "sebastian/version": "^5.0.2", - "staabm/side-effects-detector": "^1.0.5" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "11.5-dev" - } - }, - "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", - "keywords": [ - "phpunit", - "testing", - "xunit" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "security": "https://github.com/sebastianbergmann/phpunit/security/policy", - "source": "https://github.com/sebastianbergmann/phpunit/tree/11.5.33" - }, - "funding": [ - { - "url": "https://phpunit.de/sponsors.html", - "type": "custom" - }, - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", - "type": "tidelift" - } - ], - "time": "2025-08-16T05:19:02+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/event-dispatcher", - "version": "1.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", - "shasum": "" - }, - "require": { - "php": ">=7.2.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\EventDispatcher\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" - } - ], - "description": "Standard interfaces for event handling.", - "keywords": [ - "events", - "psr", - "psr-14" - ], - "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" - }, - "time": "2019-01-08T18:20:26+00:00" - }, - { - "name": "psr/log", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" - }, - "time": "2024-09-11T13:17:53+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": "react/cache", - "version": "v1.2.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/cache.git", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/promise": "^3.0 || ^2.0 || ^1.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async, Promise-based cache interface for ReactPHP", - "keywords": [ - "cache", - "caching", - "promise", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/cache/issues", - "source": "https://github.com/reactphp/cache/tree/v1.2.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2022-11-30T15:59:55+00:00" - }, - { - "name": "react/child-process", - "version": "v0.6.6", - "source": { - "type": "git", - "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/event-loop": "^1.2", - "react/stream": "^1.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/socket": "^1.16", - "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\ChildProcess\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Event-driven library for executing child processes with ReactPHP.", - "keywords": [ - "event-driven", - "process", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-01-01T16:37:48+00:00" - }, - { - "name": "react/dns", - "version": "v1.13.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "shasum": "" - }, - "require": { - "php": ">=5.3.0", - "react/cache": "^1.0 || ^0.6 || ^0.5", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.7 || ^1.2.1" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3 || ^2", - "react/promise-timer": "^1.11" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Dns\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async DNS resolver for ReactPHP", - "keywords": [ - "async", - "dns", - "dns-resolver", - "reactphp" - ], - "support": { - "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2024-06-13T14:18:03+00:00" - }, - { - "name": "react/event-loop", - "version": "v1.5.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "shasum": "" - }, - "require": { - "php": ">=5.3.0" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" - }, - "suggest": { - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\EventLoop\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", - "keywords": [ - "asynchronous", - "event-loop" - ], - "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2023-11-13T13:48:05+00:00" - }, - { - "name": "react/promise", - "version": "v3.3.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "23444f53a813a3296c1368bb104793ce8d88f04a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/23444f53a813a3296c1368bb104793ce8d88f04a", - "reference": "23444f53a813a3296c1368bb104793ce8d88f04a", - "shasum": "" - }, - "require": { - "php": ">=7.1.0" - }, - "require-dev": { - "phpstan/phpstan": "1.12.28 || 1.4.10", - "phpunit/phpunit": "^9.6 || ^7.5" - }, - "type": "library", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "React\\Promise\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", - "keywords": [ - "promise", - "promises" - ], - "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.3.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-08-19T18:57:03+00:00" - }, - { - "name": "react/socket", - "version": "v1.16.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.0", - "react/dns": "^1.13", - "react/event-loop": "^1.2", - "react/promise": "^3.2 || ^2.6 || ^1.2.1", - "react/stream": "^1.4" - }, - "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3.3 || ^2", - "react/promise-stream": "^1.4", - "react/promise-timer": "^1.11" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Socket\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" - ], - "support": { - "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2024-07-26T10:38:09+00:00" - }, - { - "name": "react/stream", - "version": "v1.4.0", - "source": { - "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", - "shasum": "" - }, - "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" - }, - "require-dev": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" - }, - "type": "library", - "autoload": { - "psr-4": { - "React\\Stream\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" - }, - { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" - }, - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, - { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" - } - ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", - "keywords": [ - "event-driven", - "io", - "non-blocking", - "pipe", - "reactphp", - "readable", - "stream", - "writable" - ], - "support": { - "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.4.0" - }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2024-06-11T12:45:25+00:00" - }, - { - "name": "sebastian/cli-parser", - "version": "3.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/15c5dd40dc4f38794d383bb95465193f5e0ae180", - "reference": "15c5dd40dc4f38794d383bb95465193f5e0ae180", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", - "support": { - "issues": "https://github.com/sebastianbergmann/cli-parser/issues", - "security": "https://github.com/sebastianbergmann/cli-parser/security/policy", - "source": "https://github.com/sebastianbergmann/cli-parser/tree/3.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:41:36+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "3.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "reference": "54391c61e4af8078e5b276ab082b6d3c54c9ad64", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit/issues", - "security": "https://github.com/sebastianbergmann/code-unit/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit/tree/3.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2025-03-19T07:56:08+00:00" - }, - { - "name": "sebastian/code-unit-reverse-lookup", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/183a9b2632194febd219bb9246eee421dad8d45e", - "reference": "183a9b2632194febd219bb9246eee421dad8d45e", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", - "support": { - "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", - "security": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/security/policy", - "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:45:54+00:00" - }, - { - "name": "sebastian/comparator", - "version": "6.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/85c77556683e6eee4323e4c5468641ca0237e2e8", - "reference": "85c77556683e6eee4323e4c5468641ca0237e2e8", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/diff": "^6.0", - "sebastian/exporter": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.4" - }, - "suggest": { - "ext-bcmath": "For comparing BcMath\\Number objects" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.3-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" - } - ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/6.3.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator", - "type": "tidelift" - } - ], - "time": "2025-08-10T08:07:46+00:00" - }, - { - "name": "sebastian/complexity", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/ee41d384ab1906c68852636b6de493846e13e5a0", - "reference": "ee41d384ab1906c68852636b6de493846e13e5a0", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", - "support": { - "issues": "https://github.com/sebastianbergmann/complexity/issues", - "security": "https://github.com/sebastianbergmann/complexity/security/policy", - "source": "https://github.com/sebastianbergmann/complexity/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:49:50+00:00" - }, - { - "name": "sebastian/diff", - "version": "6.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/b4ccd857127db5d41a5b676f24b51371d76d8544", - "reference": "b4ccd857127db5d41a5b676f24b51371d76d8544", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0", - "symfony/process": "^4.2 || ^5" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" - } - ], - "description": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", - "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/diff/issues", - "security": "https://github.com/sebastianbergmann/diff/security/policy", - "source": "https://github.com/sebastianbergmann/diff/tree/6.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:53:05+00:00" - }, - { - "name": "sebastian/environment", - "version": "7.2.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/a5c75038693ad2e8d4b6c15ba2403532647830c4", - "reference": "a5c75038693ad2e8d4b6c15ba2403532647830c4", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "suggest": { - "ext-posix": "*" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.2-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/7.2.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/environment", - "type": "tidelift" - } - ], - "time": "2025-05-21T11:55:47+00:00" - }, - { - "name": "sebastian/exporter", - "version": "6.3.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/70a298763b40b213ec087c51c739efcaa90bcd74", - "reference": "70a298763b40b213ec087c51c739efcaa90bcd74", - "shasum": "" - }, - "require": { - "ext-mbstring": "*", - "php": ">=8.2", - "sebastian/recursion-context": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.3-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Volker Dusch", - "email": "github@wallbash.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - }, - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/6.3.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/exporter", - "type": "tidelift" - } - ], - "time": "2025-09-24T06:12:51+00:00" - }, - { - "name": "sebastian/global-state", - "version": "7.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/3be331570a721f9a4b5917f4209773de17f747d7", - "reference": "3be331570a721f9a4b5917f4209773de17f747d7", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" - }, - "require-dev": { - "ext-dom": "*", - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "7.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], - "support": { - "issues": "https://github.com/sebastianbergmann/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/7.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:57:36+00:00" - }, - { - "name": "sebastian/lines-of-code", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "reference": "d36ad0d782e5756913e42ad87cb2890f4ffe467a", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^5.0", - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", - "support": { - "issues": "https://github.com/sebastianbergmann/lines-of-code/issues", - "security": "https://github.com/sebastianbergmann/lines-of-code/security/policy", - "source": "https://github.com/sebastianbergmann/lines-of-code/tree/3.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T04:58:38+00:00" - }, - { - "name": "sebastian/object-enumerator", - "version": "6.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/f5b498e631a74204185071eb41f33f38d64608aa", - "reference": "f5b498e631a74204185071eb41f33f38d64608aa", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "sebastian/object-reflector": "^4.0", - "sebastian/recursion-context": "^6.0" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/6.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:00:13+00:00" - }, - { - "name": "sebastian/object-reflector", - "version": "4.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "reference": "6e1a43b411b2ad34146dee7524cb13a068bb35f9", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - } - ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", - "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "security": "https://github.com/sebastianbergmann/object-reflector/security/policy", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/4.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-07-03T05:01:32+00:00" - }, - { - "name": "sebastian/recursion-context", - "version": "6.0.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/f6458abbf32a6c8174f8f26261475dc133b3d9dc", - "reference": "f6458abbf32a6c8174f8f26261475dc133b3d9dc", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "6.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" - }, - { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" - }, - { - "name": "Adam Harvey", - "email": "aharvey@php.net" - } - ], - "description": "Provides functionality to recursively process PHP variables", - "homepage": "https://github.com/sebastianbergmann/recursion-context", - "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "security": "https://github.com/sebastianbergmann/recursion-context/security/policy", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/6.0.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/recursion-context", - "type": "tidelift" - } - ], - "time": "2025-08-13T04:42:22+00:00" - }, - { - "name": "sebastian/type", - "version": "5.1.3", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/f77d2d4e78738c98d9a68d2596fe5e8fa380f449", - "reference": "f77d2d4e78738c98d9a68d2596fe5e8fa380f449", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "phpunit/phpunit": "^11.3" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", - "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "security": "https://github.com/sebastianbergmann/type/security/policy", - "source": "https://github.com/sebastianbergmann/type/tree/5.1.3" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - }, - { - "url": "https://liberapay.com/sebastianbergmann", - "type": "liberapay" - }, - { - "url": "https://thanks.dev/u/gh/sebastianbergmann", - "type": "thanks_dev" - }, - { - "url": "https://tidelift.com/funding/github/packagist/sebastian/type", - "type": "tidelift" - } - ], - "time": "2025-08-09T06:55:48+00:00" - }, - { - "name": "sebastian/version", - "version": "5.0.2", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c687e3387b99f5b03b6caa64c74b63e2936ff874", - "reference": "c687e3387b99f5b03b6caa64c74b63e2936ff874", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" - } - ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", - "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "security": "https://github.com/sebastianbergmann/version/security/policy", - "source": "https://github.com/sebastianbergmann/version/tree/5.0.2" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-10-09T05:16:32+00:00" - }, - { - "name": "staabm/side-effects-detector", - "version": "1.0.5", - "source": { - "type": "git", - "url": "https://github.com/staabm/side-effects-detector.git", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163", - "reference": "d8334211a140ce329c13726d4a715adbddd0a163", - "shasum": "" - }, - "require": { - "ext-tokenizer": "*", - "php": "^7.4 || ^8.0" - }, - "require-dev": { - "phpstan/extension-installer": "^1.4.3", - "phpstan/phpstan": "^1.12.6", - "phpunit/phpunit": "^9.6.21", - "symfony/var-dumper": "^5.4.43", - "tomasvotruba/type-coverage": "1.0.0", - "tomasvotruba/unused-public": "1.0.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "lib/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "description": "A static analysis tool to detect side effects in PHP code", - "keywords": [ - "static analysis" - ], - "support": { - "issues": "https://github.com/staabm/side-effects-detector/issues", - "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5" - }, - "funding": [ - { - "url": "https://github.com/staabm", - "type": "github" - } - ], - "time": "2024-10-20T05:08:20+00:00" - }, - { - "name": "symfony/console", - "version": "v7.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", - "reference": "2b9c5fafbac0399a20a2e82429e2bd735dcfb7db", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/dotenv": "<6.4", - "symfony/event-dispatcher": "<6.4", - "symfony/lock": "<6.4", - "symfony/process": "<6.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/stopwatch": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], - "support": { - "source": "https://github.com/symfony/console/tree/v7.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-09-22T15:31:00+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/event-dispatcher", - "version": "v7.3.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/b7dc69e71de420ac04bc9ab830cf3ffebba48191", - "reference": "b7dc69e71de420ac04bc9ab830cf3ffebba48191", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/event-dispatcher-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/dependency-injection": "<6.4", - "symfony/service-contracts": "<2.5" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^6.4|^7.0", - "symfony/dependency-injection": "^6.4|^7.0", - "symfony/error-handler": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v7.3.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-08-13T11:49:31+00:00" - }, - { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/event-dispatcher": "^1" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/event-dispatcher-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/filesystem", - "version": "v7.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/edcbb768a186b5c3f25d0643159a787d3e63b7fd", - "reference": "edcbb768a186b5c3f25d0643159a787d3e63b7fd", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" - }, - "require-dev": { - "symfony/process": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-07T08:17:47+00:00" - }, - { - "name": "symfony/finder", - "version": "v7.3.2", - "source": { - "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/2a6614966ba1074fa93dae0bc804227422df4dfe", - "reference": "2a6614966ba1074fa93dae0bc804227422df4dfe", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "require-dev": { - "symfony/filesystem": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/finder/tree/v7.3.2" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-07-15T13:41:35+00:00" - }, - { - "name": "symfony/options-resolver", - "version": "v7.3.3", - "source": { - "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/0ff2f5c3df08a395232bbc3c2eb7e84912df911d", - "reference": "0ff2f5c3df08a395232bbc3c2eb7e84912df911d", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], - "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.3.3" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-08-05T10:16:07+00:00" - }, - { - "name": "symfony/polyfill-ctype", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "provide": { - "ext-ctype": "*" - }, - "suggest": { - "ext-ctype": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for ctype functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], - "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/380872130d3a5dd3ace2f4010d95125fde5d5c70", - "reference": "380872130d3a5dd3ace2f4010d95125fde5d5c70", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's grapheme_* functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-27T09:58:17+00:00" - }, - { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-mbstring", - "version": "v1.33.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" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill for the Mbstring extension", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-12-23T08:48:59+00:00" - }, - { - "name": "symfony/polyfill-php80", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-01-02T08:10:11+00:00" - }, - { - "name": "symfony/polyfill-php81", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2024-09-09T11:45:10+00:00" - }, - { - "name": "symfony/polyfill-php84", - "version": "v1.33.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/polyfill-php84.git", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php84/zipball/d8ced4d875142b6a7426000426b8abc631d6b191", - "reference": "d8ced4d875142b6a7426000426b8abc631d6b191", - "shasum": "" - }, - "require": { - "php": ">=7.2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, - "autoload": { - "files": [ - "bootstrap.php" - ], - "psr-4": { - "Symfony\\Polyfill\\Php84\\": "" - }, - "classmap": [ - "Resources/stubs" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Symfony polyfill backporting some PHP 8.4+ features to lower PHP versions", - "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], - "support": { - "source": "https://github.com/symfony/polyfill-php84/tree/v1.33.0" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-06-24T13:30:11+00:00" - }, - { - "name": "symfony/process", - "version": "v7.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/f24f8f316367b30810810d4eb30c543d7003ff3b", - "reference": "f24f8f316367b30810810d4eb30c543d7003ff3b", - "shasum": "" - }, - "require": { - "php": ">=8.2" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Process\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Executes commands in sub-processes", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/process/tree/v7.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-09-11T10:12:26+00:00" - }, - { - "name": "symfony/service-contracts", - "version": "v3.6.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" - }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" - } - }, - "autoload": { - "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.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": "2025-04-25T09:37:31+00:00" - }, - { - "name": "symfony/stopwatch", - "version": "v7.3.0", - "source": { - "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/service-contracts": "^2.5|^3" - }, - "type": "library", - "autoload": { - "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides a way to profile code", - "homepage": "https://symfony.com", - "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.3.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": "2025-02-24T10:49:57+00:00" - }, - { - "name": "symfony/string", - "version": "v7.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "f96476035142921000338bad71e5247fbc138872" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f96476035142921000338bad71e5247fbc138872", - "reference": "f96476035142921000338bad71e5247fbc138872", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/emoji": "^7.1", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" - }, - "type": "library", - "autoload": { - "files": [ - "Resources/functions.php" - ], - "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" - } - ], - "description": "Provides an object-oriented API to strings and deals with bytes, UTF-8 code points and grapheme clusters in a unified way", - "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], - "support": { - "source": "https://github.com/symfony/string/tree/v7.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-09-11T14:36:48+00:00" - }, - { - "name": "symfony/var-dumper", - "version": "v7.3.4", - "source": { - "type": "git", - "url": "https://github.com/symfony/var-dumper.git", - "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/var-dumper/zipball/b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", - "reference": "b8abe7daf2730d07dfd4b2ee1cecbf0dd2fbdabb", - "shasum": "" - }, - "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/console": "<6.4" - }, - "require-dev": { - "symfony/console": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/process": "^6.4|^7.0", - "symfony/uid": "^6.4|^7.0", - "twig/twig": "^3.12" - }, - "bin": [ - "Resources/bin/var-dump-server" - ], - "type": "library", - "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": "Provides mechanisms for walking through any arbitrary PHP variable", - "homepage": "https://symfony.com", - "keywords": [ - "debug", - "dump" - ], - "support": { - "source": "https://github.com/symfony/var-dumper/tree/v7.3.4" - }, - "funding": [ - { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", - "type": "github" - }, - { - "url": "https://github.com/nicolas-grekas", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" - } - ], - "time": "2025-09-11T10:12:26+00:00" - }, - { - "name": "ta-tikoma/phpunit-architecture-test", - "version": "0.8.5", - "source": { - "type": "git", - "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", - "reference": "cf6fb197b676ba716837c886baca842e4db29005" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/cf6fb197b676ba716837c886baca842e4db29005", - "reference": "cf6fb197b676ba716837c886baca842e4db29005", - "shasum": "" - }, - "require": { - "nikic/php-parser": "^4.18.0 || ^5.0.0", - "php": "^8.1.0", - "phpdocumentor/reflection-docblock": "^5.3.0", - "phpunit/phpunit": "^10.5.5 || ^11.0.0 || ^12.0.0", - "symfony/finder": "^6.4.0 || ^7.0.0" - }, - "require-dev": { - "laravel/pint": "^1.13.7", - "phpstan/phpstan": "^1.10.52" - }, - "type": "library", - "autoload": { - "psr-4": { - "PHPUnit\\Architecture\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Ni Shi", - "email": "futik0ma011@gmail.com" - }, - { - "name": "Nuno Maduro", - "email": "enunomaduro@gmail.com" - } - ], - "description": "Methods for testing application architecture", - "keywords": [ - "architecture", - "phpunit", - "stucture", - "test", - "testing" - ], - "support": { - "issues": "https://github.com/ta-tikoma/phpunit-architecture-test/issues", - "source": "https://github.com/ta-tikoma/phpunit-architecture-test/tree/0.8.5" - }, - "time": "2025-04-20T20:23:40+00:00" - }, - { - "name": "theseer/tokenizer", - "version": "1.2.3", - "source": { - "type": "git", - "url": "https://github.com/theseer/tokenizer.git", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theseer/tokenizer/zipball/737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "reference": "737eda637ed5e28c3413cb1ebe8bb52cbf1ca7a2", - "shasum": "" - }, - "require": { - "ext-dom": "*", - "ext-tokenizer": "*", - "ext-xmlwriter": "*", - "php": "^7.2 || ^8.0" - }, - "type": "library", - "autoload": { - "classmap": [ - "src/" - ] - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "BSD-3-Clause" - ], - "authors": [ - { - "name": "Arne Blankerts", - "email": "arne@blankerts.de", - "role": "Developer" - } - ], - "description": "A small library for converting tokenized PHP source code into XML and potentially other formats", - "support": { - "issues": "https://github.com/theseer/tokenizer/issues", - "source": "https://github.com/theseer/tokenizer/tree/1.2.3" - }, - "funding": [ - { - "url": "https://github.com/theseer", - "type": "github" - } - ], - "time": "2024-03-03T12:36:25+00:00" - }, - { - "name": "webmozart/assert", - "version": "1.12.0", - "source": { - "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "541057574806f942c94662b817a50f63f7345360" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/541057574806f942c94662b817a50f63f7345360", - "reference": "541057574806f942c94662b817a50f63f7345360", - "shasum": "" - }, - "require": { - "ext-ctype": "*", - "ext-date": "*", - "ext-filter": "*", - "php": "^7.2 || ^8.0" - }, - "suggest": { - "ext-intl": "", - "ext-simplexml": "", - "ext-spl": "" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.10-dev" - } - }, - "autoload": { - "psr-4": { - "Webmozart\\Assert\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" - } - ], - "description": "Assertions to validate method input/output with nice error messages.", - "keywords": [ - "assert", - "check", - "validate" - ], - "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.12.0" - }, - "time": "2025-10-20T12:43:39+00:00" - } - ], - "aliases": [], - "minimum-stability": "stable", - "stability-flags": {}, - "prefer-stable": false, - "prefer-lowest": false, - "platform": { - "php": "^8.1" - }, - "platform-dev": {}, - "plugin-api-version": "2.6.0" -}