diff --git a/.github/workflows/static-analysis.yml b/.github/workflows/static-analysis.yml index 86033a4..8489ae0 100644 --- a/.github/workflows/static-analysis.yml +++ b/.github/workflows/static-analysis.yml @@ -99,7 +99,7 @@ jobs: run: composer install --no-interaction --no-progress --prefer-dist - name: Run PHP Mess Detector - run: ./vendor/bin/phpmd src text ./phpmd.xml + run: ./vendor/bin/phpmd analyze src --format=text --ruleset=./phpmd.xml --no-progress static-analysis-composer-require-checker: name: ComposerRequireChecker diff --git a/composer.json b/composer.json index 4591622..15f69f3 100644 --- a/composer.json +++ b/composer.json @@ -44,7 +44,7 @@ "cs-fix": "phpcbf src tests", "phpstan": "phpstan analyse -c phpstan.neon", "psalm": "psalm --show-info=false", - "phpmd": "phpmd text ./phpmd.xml", + "phpmd": "phpmd analyze src --format=text --ruleset=./phpmd.xml --no-progress", "baseline": "phpstan analyse -c phpstan.neon --generate-baseline --allow-empty-baseline && psalm --set-baseline=psalm-baseline.xml", "crc": "composer-require-checker check ./composer.json", "metrics": "phpmetrics --report-html=build/metrics --exclude=Exception src", diff --git a/src/Exception/InvalidInputTypeException.php b/src/Exception/InvalidInputTypeException.php new file mode 100644 index 0000000..ca8173a --- /dev/null +++ b/src/Exception/InvalidInputTypeException.php @@ -0,0 +1,34 @@ +resolveUnionType($param, $query, $type); + return $this->resolveUnionType($param, $query, $inputAttributes, $type); } if (! $type instanceof ReflectionNamedType) { @@ -214,33 +214,68 @@ private function resolveInputParameter(ReflectionParameter $param, array $query, */ private function resolveBuiltinType(ReflectionParameter $param, array $query, array $inputAttributes, ReflectionNamedType $type): mixed { - $paramName = $param->getName(); - if ($type->getName() === 'array') { - $inputAttribute = $inputAttributes[0]->newInstance(); - if ($inputAttribute->item !== null) { - assert(class_exists($inputAttribute->item)); - $itemClass = $inputAttribute->item; - - // Check if array items are FileUpload types - if (FileUploadTypeChecker::isFileUploadType($itemClass)) { - throw new InvalidFileUploadAttributeException( - sprintf('FileUpload array parameter "%s" must use #[InputFile] attribute, not #[Input]', $paramName), - ); - } - - /** @var class-string $itemClass */ - return $this->createArrayOfInputs($paramName, $query, $itemClass); - } + return $this->getArrayParameterValue($param, $query, $inputAttributes); } // Scalar type with #[Input] + $paramName = $param->getName(); /** @psalm-suppress MixedAssignment $value */ $value = $query[$paramName] ?? $this->getDefaultValue($param); return $this->convertScalar($value, $type); } + /** + * @param Query $query + * @param InputAttributes $inputAttributes + */ + private function getArrayParameterValue(ReflectionParameter $param, array $query, array $inputAttributes): mixed + { + $paramName = $param->getName(); + $itemClass = $this->getArrayInputItemClass($paramName, $inputAttributes); + if ($itemClass !== null) { + return $this->createArrayOfInputs($paramName, $query, $itemClass); + } + + if (! array_key_exists($paramName, $query)) { + return $this->getDefaultValue($param); + } + + /** @var mixed $arrayData */ + $arrayData = $query[$paramName]; + if (! is_array($arrayData)) { + throw InvalidInputTypeException::forParameter($paramName, $arrayData); + } + + return $arrayData; + } + + /** + * @param InputAttributes $inputAttributes + * + * @return class-string|null + */ + private function getArrayInputItemClass(string $paramName, array $inputAttributes): string|null + { + $inputAttribute = $inputAttributes[0]->newInstance(); + if ($inputAttribute->item === null) { + return null; + } + + assert(class_exists($inputAttribute->item)); + /** @var class-string $itemClass */ + $itemClass = $inputAttribute->item; + + if (FileUploadTypeChecker::isFileUploadType($itemClass)) { + throw new InvalidFileUploadAttributeException( + sprintf('FileUpload array parameter "%s" must use #[InputFile] attribute, not #[Input]', $paramName), + ); + } + + return $itemClass; + } + /** * @param Query $query * @param InputAttributes $inputAttributes @@ -473,20 +508,14 @@ private function createArrayOfInputs(string $paramName, array $query, string $it $arrayData = $query[$paramName]; if (! is_array($arrayData)) { - return []; + throw InvalidInputTypeException::forParameter($paramName, $arrayData); } $result = []; /** @var mixed $itemData */ foreach ($arrayData as $key => $itemData) { if (! is_array($itemData)) { - throw new InvalidArgumentException( - sprintf( - 'Expected array for item at key "%s", got %s.', - $key, - gettype($itemData), - ), - ); + throw InvalidInputTypeException::forItem($paramName, $key, $itemData); } // Query parameters from HTTP requests have string keys @@ -498,9 +527,16 @@ private function createArrayOfInputs(string $paramName, array $query, string $it return $result; } - /** @param Query $query */ - private function resolveUnionType(ReflectionParameter $param, array $query, ReflectionUnionType $type): mixed - { + /** + * @param Query $query + * @param InputAttributes $inputAttributes + */ + private function resolveUnionType( + ReflectionParameter $param, + array $query, + array $inputAttributes, + ReflectionUnionType $type, + ): mixed { // Check if this is a file upload union type if (FileUploadTypeChecker::isValidFileUploadUnion($type)) { // This is a valid FileUpload union, handle as file upload @@ -510,9 +546,71 @@ private function resolveUnionType(ReflectionParameter $param, array $query, Refl return $this->fileUploadFactory->create($param, $query, $inputFileAttribute); } + if ($this->isNullableArrayUnion($type)) { + return $this->getNullableArrayParameterValue($param, $query, $inputAttributes); + } + // Not a FileUpload union type, handle as regular parameter $paramName = $param->getName(); return $query[$paramName] ?? $this->getDefaultValue($param); } + + private function isNullableArrayUnion(ReflectionUnionType $type): bool + { + $hasArray = false; + $hasNull = false; + + foreach ($type->getTypes() as $namedType) { + if (! $namedType instanceof ReflectionNamedType) { + return false; + } + + if ($namedType->getName() === 'array') { + $hasArray = true; + continue; + } + + if ($namedType->getName() === 'null') { + $hasNull = true; + continue; + } + + return false; + } + + return $hasArray && $hasNull; + } + + /** + * @param Query $query + * @param InputAttributes $inputAttributes + */ + private function getNullableArrayParameterValue( + ReflectionParameter $param, + array $query, + array $inputAttributes, + ): mixed { + $paramName = $param->getName(); + if (! array_key_exists($paramName, $query)) { + return $this->getDefaultValue($param); + } + + /** @var mixed $arrayData */ + $arrayData = $query[$paramName]; + if ($arrayData === null) { + return null; + } + + $itemClass = $this->getArrayInputItemClass($paramName, $inputAttributes); + if ($itemClass !== null) { + return $this->createArrayOfInputs($paramName, $query, $itemClass); + } + + if (! is_array($arrayData)) { + throw InvalidInputTypeException::forParameter($paramName, $arrayData); + } + + return $arrayData; + } } diff --git a/tests/ArrayInputTest.php b/tests/ArrayInputTest.php index 1cadc82..dacb29e 100644 --- a/tests/ArrayInputTest.php +++ b/tests/ArrayInputTest.php @@ -5,10 +5,10 @@ namespace Ray\InputQuery; use ArrayObject; -use InvalidArgumentException; use PHPUnit\Framework\TestCase; use Ray\Di\Injector; use Ray\InputQuery\Attribute\Input; +use Ray\InputQuery\Exception\InvalidInputTypeException; use Ray\InputQuery\Fake\CustomArrayObject; use Ray\InputQuery\Fake\UserInputWithAttribute; use ReflectionMethod; @@ -119,6 +119,102 @@ public function listUsers( $this->assertCount(0, $args[0]); } + public function testPlainArrayInput(): void + { + $query = ['ids' => [1, 2, 3]]; + + $controller = new class { + /** + * @param list $ids + * + * @return list + */ + public function listIds( + #[Input] + array $ids, + ): array { + return $ids; + } + }; + + $method = new ReflectionMethod($controller, 'listIds'); + $args = $this->inputQuery->getArguments($method, $query); + + $this->assertSame([1, 2, 3], $args[0]); + } + + public function testPlainArrayInputRejectsScalarValue(): void + { + $query = ['ids' => '1,2,3']; + + $controller = new class { + /** + * @param list $ids + * + * @return list + */ + public function listIds( + #[Input] + array $ids, + ): array { + return $ids; + } + }; + + $method = new ReflectionMethod($controller, 'listIds'); + + $this->assertInvalidInputTypeContext( + fn () => $this->inputQuery->getArguments($method, $query), + 'ids', + 'string', + ); + } + + public function testNullableArrayInputRejectsScalarValue(): void + { + $query = ['ids' => '1,2,3']; + + $controller = new class { + /** + * @param list|null $ids + * + * @return list|null + */ + public function listIds( + #[Input] + array|null $ids = null, + ): array|null { + return $ids; + } + }; + + $method = new ReflectionMethod($controller, 'listIds'); + + $this->assertInvalidInputTypeContext( + fn () => $this->inputQuery->getArguments($method, $query), + 'ids', + 'string', + ); + } + + public function testNativeArrayConstructorInputRejectsScalarBeforeTypeError(): void + { + $input = new class { + /** @param list $tagIds */ + public function __construct( + #[Input] + public readonly array $tagIds = [], + ) { + } + }; + + $this->assertInvalidInputTypeContext( + fn () => $this->inputQuery->newInstance($input::class, ['tagIds' => 1]), + 'tagIds', + 'int', + ); + } + public function testMissingArrayParameter(): void { $query = []; @@ -168,10 +264,12 @@ public function listUsers( }; $method = new ReflectionMethod($controller, 'listUsers'); - $args = $this->inputQuery->getArguments($method, $query); - $this->assertIsArray($args[0]); - $this->assertCount(0, $args[0]); + $this->assertInvalidInputTypeContext( + fn () => $this->inputQuery->getArguments($method, $query), + 'users', + 'string', + ); } public function testArrayWithNonArrayElements(): void @@ -199,10 +297,12 @@ public function listUsers( $method = new ReflectionMethod($controller, 'listUsers'); - $this->expectException(InvalidArgumentException::class); - $this->expectExceptionMessage('Expected array for item at key "0", got string.'); - - $this->inputQuery->getArguments($method, $query); + $this->assertInvalidInputTypeContext( + fn () => $this->inputQuery->getArguments($method, $query), + 'users', + 'string', + 0, + ); } public function testCustomArrayObjectOfInputObjects(): void @@ -271,4 +371,23 @@ public function listUsers( // Should create a regular ArrayObject with the nested query data $this->assertInstanceOf(ArrayObject::class, $args[0]); } + + /** @param callable(): mixed $callback */ + private function assertInvalidInputTypeContext( + callable $callback, + string $paramName, + string $actualType, + int|string|null $itemKey = null, + ): void { + try { + $callback(); + $this->fail('Expected InvalidInputTypeException.'); + } catch (InvalidInputTypeException $e) { + $this->assertSame('', $e->getMessage()); + $this->assertSame($paramName, $e->paramName); + $this->assertSame('array', $e->expectedType); + $this->assertSame($actualType, $e->actualType); + $this->assertSame($itemKey, $e->itemKey); + } + } } diff --git a/vendor-bin/tools/composer.lock b/vendor-bin/tools/composer.lock index a06b4f8..b30e541 100644 --- a/vendor-bin/tools/composer.lock +++ b/vendor-bin/tools/composer.lock @@ -1375,6 +1375,53 @@ }, "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": "felixfbecker/language-server-protocol", "version": "v1.5.3", @@ -1433,16 +1480,16 @@ }, { "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "version": "1.3.0", "source": { "type": "git", "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "reference": "db9508f7b1474469d9d3c53b86f817e344732678" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/db9508f7b1474469d9d3c53b86f817e344732678", + "reference": "db9508f7b1474469d9d3c53b86f817e344732678", "shasum": "" }, "require": { @@ -1452,10 +1499,10 @@ "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", + "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" }, @@ -1482,7 +1529,7 @@ ], "support": { "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "source": "https://github.com/theofidry/cpu-core-counter/tree/1.3.0" }, "funding": [ { @@ -1490,7 +1537,7 @@ "type": "github" } ], - "time": "2024-08-06T10:04:20+00:00" + "time": "2025-08-14T07:29:31+00:00" }, { "name": "kelunik/certificate", @@ -1882,31 +1929,38 @@ "source": { "type": "git", "url": "https://github.com/pdepend/pdepend.git", - "reference": "04632709b73ef136bcc5b5b32708bc130df0ca2e" + "reference": "fca1f5fb1953df6713e35e06e10644e3caff7246" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/pdepend/pdepend/zipball/04632709b73ef136bcc5b5b32708bc130df0ca2e", - "reference": "04632709b73ef136bcc5b5b32708bc130df0ca2e", + "url": "https://api.github.com/repos/pdepend/pdepend/zipball/fca1f5fb1953df6713e35e06e10644e3caff7246", + "reference": "fca1f5fb1953df6713e35e06e10644e3caff7246", "shasum": "" }, "require": { + "fidry/cpu-core-counter": "^1.1", "php": ">=8.1", "php-64bit": "*", - "symfony/config": "^5.4 || ^6.0 || ^7.0", - "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.0 || ^7.0", - "symfony/polyfill-mbstring": "^1.19" + "react/child-process": "^0.6.7", + "symfony/config": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/dependency-injection": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/filesystem": "^5.4 || ^6.0 || ^7.0 || ^8.0", + "symfony/polyfill-mbstring": "^1.19", + "symfony/yaml": "^5.4 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { "brianium/paratest": "^7.3", - "easy-doc/easy-doc": "0.0.0 || ^1.2.3", + "easy-doc/easy-doc": "^1.2.3", "friendsofphp/php-cs-fixer": "^3.57", "gregwar/rst": "^1.0.6", - "phpstan/phpstan": "~2.1.0", - "phpunit/phpunit": "^10.5.20,<10.5.32", + "phpstan/phpstan": "~2.1.32", + "phpunit/phpunit": "^10.5.20,<10.5.32 || ^11.0.0", "squizlabs/php_codesniffer": "^3.8.0" }, + "suggest": { + "ext-sockets": "Recommended on Windows for multiprocessing support" + }, + "default-branch": true, "bin": [ "bin/pdepend" ], @@ -1942,7 +1996,7 @@ "type": "tidelift" } ], - "time": "2025-03-18T18:57:34+00:00" + "time": "2026-02-10T15:29:38+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -2125,30 +2179,32 @@ "source": { "type": "git", "url": "https://github.com/phpmd/phpmd.git", - "reference": "3a8f6ed5293a244cdf4ec3a4f1ebc32db1dadf27" + "reference": "35140602568b6068884b28e04b3dcf1cde9d5ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpmd/phpmd/zipball/3a8f6ed5293a244cdf4ec3a4f1ebc32db1dadf27", - "reference": "3a8f6ed5293a244cdf4ec3a4f1ebc32db1dadf27", + "url": "https://api.github.com/repos/phpmd/phpmd/zipball/35140602568b6068884b28e04b3dcf1cde9d5ea1", + "reference": "35140602568b6068884b28e04b3dcf1cde9d5ea1", "shasum": "" }, "require": { "composer/xdebug-handler": "^3.0", "ext-xml": "*", - "pdepend/pdepend": "3.x-dev#5fddc0476146acf1455bd4cccf83f20439a0335b", - "php": "^8.1" + "pdepend/pdepend": "3.x-dev#43ad2f3435cc94ecc8e114d2d18be7a46e5ad101", + "php": "^8.1", + "symfony/console": "^6.4|^7.0|^8.0", + "symfony/yaml": "^5.4.31 || ^6.0 || ^7.0 || ^8.0" }, "require-dev": { - "brianium/paratest": "^7.3", + "brianium/paratest": "~7.3.2|^7.8.5", "easy-doc/easy-doc": "^1.3.2", "ext-json": "*", "ext-simplexml": "*", "friendsofphp/php-cs-fixer": "^3.57", "gregwar/rst": "^1.0", "mikey179/vfsstream": "^1.6.8", - "phpstan/phpstan": "~2.1.0", - "phpunit/phpunit": "^10.5.20,<10.5.32", + "phpstan/phpstan": "~2.1.44", + "phpunit/phpunit": "^10.5.62|^11.5.50", "squizlabs/php_codesniffer": "^3.8.0" }, "bin": [ @@ -2204,7 +2260,7 @@ "type": "tidelift" } ], - "time": "2025-04-07T18:47:38+00:00" + "time": "2026-04-02T14:49:19+00:00" }, { "name": "phpmetrics/phpmetrics", @@ -2657,6 +2713,231 @@ }, "time": "2024-09-11T13:17:53+00:00" }, + { + "name": "react/child-process", + "version": "v0.6.7", + "source": { + "type": "git", + "url": "https://github.com/reactphp/child-process.git", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/970f0e71945556422ee4570ccbabaedc3cf04ad3", + "reference": "970f0e71945556422ee4570ccbabaedc3cf04ad3", + "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.7" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-12-23T15:25:20+00:00" + }, + { + "name": "react/event-loop", + "version": "v1.6.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/event-loop.git", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "reference": "ba276bda6083df7e0050fd9b33f66ad7a4ac747a", + "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.6.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-11-17T20:46:25+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": "revolt/event-loop", "version": "v1.0.7", @@ -3015,34 +3296,33 @@ }, { "name": "symfony/config", - "version": "v7.3.0", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/config.git", - "reference": "ba62ae565f1327c2f6366726312ed828c85853bc" + "reference": "7e712ee3c98ec114f674adc4fbad4c2fe7526b9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/config/zipball/ba62ae565f1327c2f6366726312ed828c85853bc", - "reference": "ba62ae565f1327c2f6366726312ed828c85853bc", + "url": "https://api.github.com/repos/symfony/config/zipball/7e712ee3c98ec114f674adc4fbad4c2fe7526b9c", + "reference": "7e712ee3c98ec114f674adc4fbad4c2fe7526b9c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/filesystem": "^7.1", - "symfony/polyfill-ctype": "~1.8" + "symfony/filesystem": "^7.4|^8.0", + "symfony/polyfill-ctype": "^1.8" }, "conflict": { - "symfony/finder": "<6.4", "symfony/service-contracts": "<2.5" }, "require-dev": { - "symfony/event-dispatcher": "^6.4|^7.0", - "symfony/finder": "^6.4|^7.0", - "symfony/messenger": "^6.4|^7.0", + "symfony/event-dispatcher": "^7.4|^8.0", + "symfony/finder": "^7.4|^8.0", + "symfony/messenger": "^7.4|^8.0", "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^6.4|^7.0" + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3070,7 +3350,7 @@ "description": "Helps you find, load, combine, autofill and validate configuration values of any kind", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/config/tree/v7.3.0" + "source": "https://github.com/symfony/config/tree/v8.0.9" }, "funding": [ { @@ -3081,25 +3361,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": "2025-05-15T09:04:05+00:00" + "time": "2026-04-29T15:02:55+00:00" }, { "name": "symfony/console", - "version": "v7.3.1", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101" + "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/9e27aecde8f506ba0fd1d9989620c04a87697101", - "reference": "9e27aecde8f506ba0fd1d9989620c04a87697101", + "url": "https://api.github.com/repos/symfony/console/zipball/d7d2b64a45a89d607865927b176fa51c33ddbb58", + "reference": "d7d2b64a45a89d607865927b176fa51c33ddbb58", "shasum": "" }, "require": { @@ -3107,7 +3391,7 @@ "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^7.2" + "symfony/string": "^7.2|^8.0" }, "conflict": { "symfony/dependency-injection": "<6.4", @@ -3121,16 +3405,16 @@ }, "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" + "symfony/config": "^6.4|^7.0|^8.0", + "symfony/dependency-injection": "^6.4|^7.0|^8.0", + "symfony/event-dispatcher": "^6.4|^7.0|^8.0", + "symfony/http-foundation": "^6.4|^7.0|^8.0", + "symfony/http-kernel": "^6.4|^7.0|^8.0", + "symfony/lock": "^6.4|^7.0|^8.0", + "symfony/messenger": "^6.4|^7.0|^8.0", + "symfony/process": "^6.4|^7.0|^8.0", + "symfony/stopwatch": "^6.4|^7.0|^8.0", + "symfony/var-dumper": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3164,7 +3448,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.3.1" + "source": "https://github.com/symfony/console/tree/v7.4.9" }, "funding": [ { @@ -3175,48 +3459,49 @@ "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-27T19:55:54+00:00" + "time": "2026-04-22T15:21:55+00:00" }, { "name": "symfony/dependency-injection", - "version": "v7.3.1", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/dependency-injection.git", - "reference": "8656c4848b48784c4bb8c4ae50d2b43f832cead8" + "reference": "85d359a3f72b8e1ae1466b4330149318442e3a8c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/8656c4848b48784c4bb8c4ae50d2b43f832cead8", - "reference": "8656c4848b48784c4bb8c4ae50d2b43f832cead8", + "url": "https://api.github.com/repos/symfony/dependency-injection/zipball/85d359a3f72b8e1ae1466b4330149318442e3a8c", + "reference": "85d359a3f72b8e1ae1466b4330149318442e3a8c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.4", "psr/container": "^1.1|^2.0", "symfony/deprecation-contracts": "^2.5|^3", - "symfony/service-contracts": "^3.5", - "symfony/var-exporter": "^6.4.20|^7.2.5" + "symfony/service-contracts": "^3.6", + "symfony/var-exporter": "^7.4|^8.0" }, "conflict": { - "ext-psr": "<1.1|>=2", - "symfony/config": "<6.4", - "symfony/finder": "<6.4", - "symfony/yaml": "<6.4" + "ext-psr": "<1.1|>=2" }, "provide": { "psr/container-implementation": "1.1|2.0", "symfony/service-implementation": "1.1|2.0|3.0" }, "require-dev": { - "symfony/config": "^6.4|^7.0", - "symfony/expression-language": "^6.4|^7.0", - "symfony/yaml": "^6.4|^7.0" + "symfony/config": "^7.4|^8.0", + "symfony/expression-language": "^7.4|^8.0", + "symfony/yaml": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3244,7 +3529,7 @@ "description": "Allows you to standardize and centralize the way objects are constructed in your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/dependency-injection/tree/v7.3.1" + "source": "https://github.com/symfony/dependency-injection/tree/v8.0.9" }, "funding": [ { @@ -3255,12 +3540,16 @@ "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-24T04:04:43+00:00" + "time": "2026-04-30T18:39:49+00:00" }, { "name": "symfony/deprecation-contracts", @@ -3331,16 +3620,16 @@ }, { "name": "symfony/filesystem", - "version": "v7.3.0", + "version": "v7.4.9", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "dcd8f96bcdc0f128ec406c765cc066c6035d1be3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/dcd8f96bcdc0f128ec406c765cc066c6035d1be3", + "reference": "dcd8f96bcdc0f128ec406c765cc066c6035d1be3", "shasum": "" }, "require": { @@ -3349,7 +3638,7 @@ "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0" + "symfony/process": "^6.4|^7.0|^8.0" }, "type": "library", "autoload": { @@ -3377,7 +3666,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.3.0" + "source": "https://github.com/symfony/filesystem/tree/v7.4.9" }, "funding": [ { @@ -3388,25 +3677,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-10-25T15:15:23+00:00" + "time": "2026-04-18T13:18:21+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "reference": "141046a8f9477948ff284fa65be2095baafb94f2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/141046a8f9477948ff284fa65be2095baafb94f2", + "reference": "141046a8f9477948ff284fa65be2095baafb94f2", "shasum": "" }, "require": { @@ -3456,7 +3749,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.37.0" }, "funding": [ { @@ -3467,25 +3760,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-09-09T11:45:10+00:00" + "time": "2026-04-10T16:19:22+00:00" }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/4864388bfbd3001ce88e234fab652acd91fdc57e", + "reference": "4864388bfbd3001ce88e234fab652acd91fdc57e", "shasum": "" }, "require": { @@ -3534,7 +3831,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.37.0" }, "funding": [ { @@ -3545,16 +3842,20 @@ "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" + "time": "2026-04-26T13:13:48+00:00" }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -3615,7 +3916,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.37.0" }, "funding": [ { @@ -3626,6 +3927,10 @@ "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" @@ -3635,16 +3940,16 @@ }, { "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "version": "v1.37.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6a21eb99c6973357967f6ce3708cd55a6bec6315", + "reference": "6a21eb99c6973357967f6ce3708cd55a6bec6315", "shasum": "" }, "require": { @@ -3696,7 +4001,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.37.0" }, "funding": [ { @@ -3707,12 +4012,16 @@ "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" + "time": "2026-04-10T17:25:58+00:00" }, { "name": "symfony/polyfill-php84", @@ -3792,16 +4101,16 @@ }, { "name": "symfony/service-contracts", - "version": "v3.6.0", + "version": "v3.6.1", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/45112560a3ba2d715666a509a0bc9521d10b6c43", + "reference": "45112560a3ba2d715666a509a0bc9521d10b6c43", "shasum": "" }, "require": { @@ -3855,7 +4164,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.1" }, "funding": [ { @@ -3866,44 +4175,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": "2025-04-25T09:37:31+00:00" + "time": "2025-07-15T11:30:57+00:00" }, { "name": "symfony/string", - "version": "v7.3.0", + "version": "v8.0.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125" + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/f3570b8c61ca887a9e2938e85cb6458515d2b125", - "reference": "f3570b8c61ca887a9e2938e85cb6458515d2b125", + "url": "https://api.github.com/repos/symfony/string/zipball/ae9488f874d7603f9d2dfbf120203882b645d963", + "reference": "ae9488f874d7603f9d2dfbf120203882b645d963", "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" + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8", + "symfony/polyfill-intl-grapheme": "^1.33", + "symfony/polyfill-intl-normalizer": "^1.0", + "symfony/polyfill-mbstring": "^1.0" }, "conflict": { "symfony/translation-contracts": "<2.5" }, "require-dev": { - "symfony/emoji": "^7.1", - "symfony/error-handler": "^6.4|^7.0", - "symfony/http-client": "^6.4|^7.0", - "symfony/intl": "^6.4|^7.0", + "symfony/emoji": "^7.4|^8.0", + "symfony/http-client": "^7.4|^8.0", + "symfony/intl": "^7.4|^8.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -3942,7 +4254,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.3.0" + "source": "https://github.com/symfony/string/tree/v8.0.8" }, "funding": [ { @@ -3953,35 +4265,38 @@ "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-04-20T20:19:01+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "symfony/var-exporter", - "version": "v7.3.0", + "version": "v8.0.9", "source": { "type": "git", "url": "https://github.com/symfony/var-exporter.git", - "reference": "c9a1168891b5aaadfd6332ef44393330b3498c4c" + "reference": "24cf67be4dd0926e4413635418682f4fff831412" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/var-exporter/zipball/c9a1168891b5aaadfd6332ef44393330b3498c4c", - "reference": "c9a1168891b5aaadfd6332ef44393330b3498c4c", + "url": "https://api.github.com/repos/symfony/var-exporter/zipball/24cf67be4dd0926e4413635418682f4fff831412", + "reference": "24cf67be4dd0926e4413635418682f4fff831412", "shasum": "" }, "require": { - "php": ">=8.2", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.4" }, "require-dev": { - "symfony/property-access": "^6.4|^7.0", - "symfony/serializer": "^6.4|^7.0", - "symfony/var-dumper": "^6.4|^7.0" + "symfony/property-access": "^7.4|^8.0", + "symfony/serializer": "^7.4|^8.0", + "symfony/var-dumper": "^7.4|^8.0" }, "type": "library", "autoload": { @@ -4019,7 +4334,82 @@ "serialize" ], "support": { - "source": "https://github.com/symfony/var-exporter/tree/v7.3.0" + "source": "https://github.com/symfony/var-exporter/tree/v8.0.9" + }, + "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": "2026-04-18T13:51:42+00:00" + }, + { + "name": "symfony/yaml", + "version": "v8.0.8", + "source": { + "type": "git", + "url": "https://github.com/symfony/yaml.git", + "reference": "54174ab48c0c0f9e21512b304be17f8150ccf8f1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/yaml/zipball/54174ab48c0c0f9e21512b304be17f8150ccf8f1", + "reference": "54174ab48c0c0f9e21512b304be17f8150ccf8f1", + "shasum": "" + }, + "require": { + "php": ">=8.4", + "symfony/polyfill-ctype": "^1.8" + }, + "conflict": { + "symfony/console": "<7.4" + }, + "require-dev": { + "symfony/console": "^7.4|^8.0" + }, + "bin": [ + "Resources/bin/yaml-lint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Symfony\\Component\\Yaml\\": "" + }, + "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": "Loads and dumps YAML files", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/yaml/tree/v8.0.8" }, "funding": [ { @@ -4030,12 +4420,16 @@ "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-05-15T09:04:05+00:00" + "time": "2026-03-30T15:14:47+00:00" }, { "name": "vimeo/psalm", @@ -4226,5 +4620,5 @@ "prefer-lowest": false, "platform": {}, "platform-dev": {}, - "plugin-api-version": "2.6.0" + "plugin-api-version": "2.9.0" }