From 71c4a93a4d09ec7155974126437b2db1a54856e5 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 8 May 2025 09:27:08 +0800 Subject: [PATCH 01/59] init --- composer.lock | 6812 +++++++++++++++++ src/OpenApi/Annotations/Headers.php | 24 + src/OpenApi/Annotations/OpenApi.php | 16 + src/OpenApi/Annotations/RequestBody.php | 20 + src/OpenApi/Annotations/Response.php | 19 + src/OpenApi/Annotations/Route.php | 24 + src/OpenApi/Annotations/Summary.php | 21 + src/OpenApi/Annotations/Tag.php | 26 + src/OpenApi/Collections/OpenApiCollection.php | 28 + .../Collections/ParameterCollection.php | 28 + src/OpenApi/Enum/ContentTypeEnum.php | 10 + src/OpenApi/Enum/MethodEnum.php | 9 + src/OpenApi/Enum/ParameterTypeEnum.php | 10 + src/OpenApi/Formatter/FormatterInterface.php | 12 + src/OpenApi/Formatter/Json.php | 18 + src/OpenApi/Formatter/Yam.php | 0 src/OpenApi/Handler/HandleInterface.php | 12 + src/OpenApi/Handler/Handler.php | 178 + src/OpenApi/Handler/ParserPartaker.php | 191 + src/OpenApi/Handler/SerializeHandler.php | 152 + src/OpenApi/Storage/OpenAPI/ApiInfo.php | 20 + src/OpenApi/Storage/OpenAPI/Contact.php | 20 + .../Storage/OpenAPI/ContentProperties.php | 27 + src/OpenApi/Storage/OpenAPI/Method/GET.php | 13 + src/OpenApi/Storage/OpenAPI/Method/Method.php | 71 + .../OpenAPI/Method/MethodInterface.php | 10 + src/OpenApi/Storage/OpenAPI/Method/POST.php | 13 + src/OpenApi/Storage/OpenAPI/OpenAPI.php | 67 + .../Storage/OpenAPI/ParameterStorage.php | 73 + .../Storage/OpenAPI/RequestBodyStorage.php | 27 + .../Storage/OpenAPI/ResponseStorage.php | 29 + src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 233 + .../Storage/OpenAPI/ServersStorage.php | 19 + src/OpenApi/Storage/OpenAPI/TagStorage.php | 16 + src/OpenApi/Storage/StorageInterface.php | 9 + src/OpenApi/Storage/TreeNode.php | 47 + src/Resolvers/PropertyTypeDocResolver.php | 18 +- .../Collections/ConstructDataCollection.php | 2 - 38 files changed, 8313 insertions(+), 11 deletions(-) create mode 100644 composer.lock create mode 100755 src/OpenApi/Annotations/Headers.php create mode 100755 src/OpenApi/Annotations/OpenApi.php create mode 100755 src/OpenApi/Annotations/RequestBody.php create mode 100644 src/OpenApi/Annotations/Response.php create mode 100755 src/OpenApi/Annotations/Route.php create mode 100755 src/OpenApi/Annotations/Summary.php create mode 100755 src/OpenApi/Annotations/Tag.php create mode 100644 src/OpenApi/Collections/OpenApiCollection.php create mode 100755 src/OpenApi/Collections/ParameterCollection.php create mode 100755 src/OpenApi/Enum/ContentTypeEnum.php create mode 100755 src/OpenApi/Enum/MethodEnum.php create mode 100755 src/OpenApi/Enum/ParameterTypeEnum.php create mode 100755 src/OpenApi/Formatter/FormatterInterface.php create mode 100755 src/OpenApi/Formatter/Json.php create mode 100755 src/OpenApi/Formatter/Yam.php create mode 100755 src/OpenApi/Handler/HandleInterface.php create mode 100755 src/OpenApi/Handler/Handler.php create mode 100755 src/OpenApi/Handler/ParserPartaker.php create mode 100755 src/OpenApi/Handler/SerializeHandler.php create mode 100755 src/OpenApi/Storage/OpenAPI/ApiInfo.php create mode 100755 src/OpenApi/Storage/OpenAPI/Contact.php create mode 100755 src/OpenApi/Storage/OpenAPI/ContentProperties.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/GET.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/Method.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/POST.php create mode 100755 src/OpenApi/Storage/OpenAPI/OpenAPI.php create mode 100755 src/OpenApi/Storage/OpenAPI/ParameterStorage.php create mode 100755 src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php create mode 100755 src/OpenApi/Storage/OpenAPI/ResponseStorage.php create mode 100755 src/OpenApi/Storage/OpenAPI/SchemaStorage.php create mode 100755 src/OpenApi/Storage/OpenAPI/ServersStorage.php create mode 100755 src/OpenApi/Storage/OpenAPI/TagStorage.php create mode 100755 src/OpenApi/Storage/StorageInterface.php create mode 100755 src/OpenApi/Storage/TreeNode.php diff --git a/composer.lock b/composer.lock new file mode 100644 index 0000000..1825245 --- /dev/null +++ b/composer.lock @@ -0,0 +1,6812 @@ +{ + "_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": "e2fb9f91440aed5f8990e559a3d1b4be", + "packages": [ + { + "name": "carbonphp/carbon-doctrine-types", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon-doctrine-types.git", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon-doctrine-types/zipball/18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "reference": "18ba5ddfec8976260ead6e866180bd5d2f71aa1d", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "conflict": { + "doctrine/dbal": "<4.0.0 || >=5.0.0" + }, + "require-dev": { + "doctrine/dbal": "^4.0.0", + "nesbot/carbon": "^2.71.0 || ^3.0.0", + "phpunit/phpunit": "^10.3" + }, + "type": "library", + "autoload": { + "psr-4": { + "Carbon\\Doctrine\\": "src/Carbon/Doctrine/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "KyleKatarn", + "email": "kylekatarnls@gmail.com" + } + ], + "description": "Types to use Carbon in Doctrine", + "keywords": [ + "carbon", + "date", + "datetime", + "doctrine", + "time" + ], + "support": { + "issues": "https://github.com/CarbonPHP/carbon-doctrine-types/issues", + "source": "https://github.com/CarbonPHP/carbon-doctrine-types/tree/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon", + "type": "open_collective" + }, + { + "url": "https://tidelift.com/funding/github/packagist/nesbot/carbon", + "type": "tidelift" + } + ], + "time": "2024-02-09T16:56:22+00:00" + }, + { + "name": "doctrine/deprecations", + "version": "1.1.4", + "source": { + "type": "git", + "url": "https://github.com/doctrine/deprecations.git", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", + "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", + "shasum": "" + }, + "require": { + "php": "^7.1 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^9 || ^12", + "phpstan/phpstan": "1.4.10 || 2.0.3", + "phpstan/phpstan-phpunit": "^1.0 || ^2", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "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.4" + }, + "time": "2024-12-07T21:18:45+00:00" + }, + { + "name": "doctrine/inflector", + "version": "2.0.10", + "source": { + "type": "git", + "url": "https://github.com/doctrine/inflector.git", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/inflector/zipball/5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "reference": "5817d0659c5b50c9b950feb9af7b9668e2c436bc", + "shasum": "" + }, + "require": { + "php": "^7.2 || ^8.0" + }, + "require-dev": { + "doctrine/coding-standard": "^11.0", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-phpunit": "^1.1", + "phpstan/phpstan-strict-rules": "^1.3", + "phpunit/phpunit": "^8.5 || ^9.5", + "vimeo/psalm": "^4.25 || ^5.4" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Inflector\\": "lib/Doctrine/Inflector" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Inflector is a small library that can perform string manipulations with regard to upper/lowercase and singular/plural forms of words.", + "homepage": "https://www.doctrine-project.org/projects/inflector.html", + "keywords": [ + "inflection", + "inflector", + "lowercase", + "manipulation", + "php", + "plural", + "singular", + "strings", + "uppercase", + "words" + ], + "support": { + "issues": "https://github.com/doctrine/inflector/issues", + "source": "https://github.com/doctrine/inflector/tree/2.0.10" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Finflector", + "type": "tidelift" + } + ], + "time": "2024-02-18T20:23:39+00:00" + }, + { + "name": "fakerphp/faker", + "version": "v1.24.1", + "source": { + "type": "git", + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "shasum": "" + }, + "require": { + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" + }, + "require-dev": { + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" + }, + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." + }, + "type": "library", + "autoload": { + "psr-4": { + "Faker\\": "src/Faker/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "François Zaninotto" + } + ], + "description": "Faker is a PHP library that generates fake data for you.", + "keywords": [ + "data", + "faker", + "fixtures" + ], + "support": { + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" + }, + "time": "2024-11-21T13:46:39+00:00" + }, + { + "name": "illuminate/collections", + "version": "v10.48.28", + "source": { + "type": "git", + "url": "https://github.com/illuminate/collections.git", + "reference": "48de3d6bc6aa779112ddcb608a3a96fc975d89d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/collections/zipball/48de3d6bc6aa779112ddcb608a3a96fc975d89d8", + "reference": "48de3d6bc6aa779112ddcb608a3a96fc975d89d8", + "shasum": "" + }, + "require": { + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "php": "^8.1" + }, + "suggest": { + "symfony/var-dumper": "Required to use the dump method (^6.2)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Collections package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-11-21T14:02:44+00:00" + }, + { + "name": "illuminate/conditionable", + "version": "v10.48.28", + "source": { + "type": "git", + "url": "https://github.com/illuminate/conditionable.git", + "reference": "3ee34ac306fafc2a6f19cd7cd68c9af389e432a5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/conditionable/zipball/3ee34ac306fafc2a6f19cd7cd68c9af389e432a5", + "reference": "3ee34ac306fafc2a6f19cd7cd68c9af389e432a5", + "shasum": "" + }, + "require": { + "php": "^8.0.2" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Conditionable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-11-21T14:02:44+00:00" + }, + { + "name": "illuminate/contracts", + "version": "v10.48.28", + "source": { + "type": "git", + "url": "https://github.com/illuminate/contracts.git", + "reference": "f90663a69f926105a70b78060a31f3c64e2d1c74" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/contracts/zipball/f90663a69f926105a70b78060a31f3c64e2d1c74", + "reference": "f90663a69f926105a70b78060a31f3c64e2d1c74", + "shasum": "" + }, + "require": { + "php": "^8.1", + "psr/container": "^1.1.1|^2.0.1", + "psr/simple-cache": "^1.0|^2.0|^3.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Contracts\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Contracts package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-11-21T14:02:44+00:00" + }, + { + "name": "illuminate/macroable", + "version": "v10.48.28", + "source": { + "type": "git", + "url": "https://github.com/illuminate/macroable.git", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/macroable/zipball/dff667a46ac37b634dcf68909d9d41e94dc97c27", + "reference": "dff667a46ac37b634dcf68909d9d41e94dc97c27", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Macroable package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2023-06-05T12:46:42+00:00" + }, + { + "name": "illuminate/support", + "version": "v10.48.28", + "source": { + "type": "git", + "url": "https://github.com/illuminate/support.git", + "reference": "6d09b480d34846245d9288f4dcefb17a73ce6e6a" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/illuminate/support/zipball/6d09b480d34846245d9288f4dcefb17a73ce6e6a", + "reference": "6d09b480d34846245d9288f4dcefb17a73ce6e6a", + "shasum": "" + }, + "require": { + "doctrine/inflector": "^2.0", + "ext-ctype": "*", + "ext-filter": "*", + "ext-mbstring": "*", + "illuminate/collections": "^10.0", + "illuminate/conditionable": "^10.0", + "illuminate/contracts": "^10.0", + "illuminate/macroable": "^10.0", + "nesbot/carbon": "^2.67", + "php": "^8.1", + "voku/portable-ascii": "^2.0" + }, + "conflict": { + "tightenco/collect": "<5.5.33" + }, + "suggest": { + "illuminate/filesystem": "Required to use the composer class (^10.0).", + "league/commonmark": "Required to use Str::markdown() and Stringable::markdown() (^2.6).", + "ramsey/uuid": "Required to use Str::uuid() (^4.7).", + "symfony/process": "Required to use the composer class (^6.2).", + "symfony/uid": "Required to use Str::ulid() (^6.2).", + "symfony/var-dumper": "Required to use the dd function (^6.2).", + "vlucas/phpdotenv": "Required to use the Env class and env helper (^5.4.1)." + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "10.x-dev" + } + }, + "autoload": { + "files": [ + "helpers.php" + ], + "psr-4": { + "Illuminate\\Support\\": "" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Taylor Otwell", + "email": "taylor@laravel.com" + } + ], + "description": "The Illuminate Support package.", + "homepage": "https://laravel.com", + "support": { + "issues": "https://github.com/laravel/framework/issues", + "source": "https://github.com/laravel/framework" + }, + "time": "2024-12-10T14:47:55+00:00" + }, + { + "name": "nesbot/carbon", + "version": "2.73.0", + "source": { + "type": "git", + "url": "https://github.com/CarbonPHP/carbon.git", + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/CarbonPHP/carbon/zipball/9228ce90e1035ff2f0db84b40ec2e023ed802075", + "reference": "9228ce90e1035ff2f0db84b40ec2e023ed802075", + "shasum": "" + }, + "require": { + "carbonphp/carbon-doctrine-types": "*", + "ext-json": "*", + "php": "^7.1.8 || ^8.0", + "psr/clock": "^1.0", + "symfony/polyfill-mbstring": "^1.0", + "symfony/polyfill-php80": "^1.16", + "symfony/translation": "^3.4 || ^4.0 || ^5.0 || ^6.0" + }, + "provide": { + "psr/clock-implementation": "1.0" + }, + "require-dev": { + "doctrine/dbal": "^2.0 || ^3.1.4 || ^4.0", + "doctrine/orm": "^2.7 || ^3.0", + "friendsofphp/php-cs-fixer": "^3.0", + "kylekatarnls/multi-tester": "^2.0", + "ondrejmirtes/better-reflection": "<6", + "phpmd/phpmd": "^2.9", + "phpstan/extension-installer": "^1.0", + "phpstan/phpstan": "^0.12.99 || ^1.7.14", + "phpunit/php-file-iterator": "^2.0.5 || ^3.0.6", + "phpunit/phpunit": "^7.5.20 || ^8.5.26 || ^9.5.20", + "squizlabs/php_codesniffer": "^3.4" + }, + "bin": [ + "bin/carbon" + ], + "type": "library", + "extra": { + "laravel": { + "providers": [ + "Carbon\\Laravel\\ServiceProvider" + ] + }, + "phpstan": { + "includes": [ + "extension.neon" + ] + }, + "branch-alias": { + "dev-2.x": "2.x-dev", + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Carbon\\": "src/Carbon/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Brian Nesbitt", + "email": "brian@nesbot.com", + "homepage": "https://markido.com" + }, + { + "name": "kylekatarnls", + "homepage": "https://github.com/kylekatarnls" + } + ], + "description": "An API extension for DateTime that supports 281 different languages.", + "homepage": "https://carbon.nesbot.com", + "keywords": [ + "date", + "datetime", + "time" + ], + "support": { + "docs": "https://carbon.nesbot.com/docs", + "issues": "https://github.com/briannesbitt/Carbon/issues", + "source": "https://github.com/briannesbitt/Carbon" + }, + "funding": [ + { + "url": "https://github.com/sponsors/kylekatarnls", + "type": "github" + }, + { + "url": "https://opencollective.com/Carbon#sponsor", + "type": "opencollective" + }, + { + "url": "https://tidelift.com/subscription/pkg/packagist-nesbot-carbon?utm_source=packagist-nesbot-carbon&utm_medium=referral&utm_campaign=readme", + "type": "tidelift" + } + ], + "time": "2025-01-08T20:10:23+00:00" + }, + { + "name": "nikic/php-parser", + "version": "v5.4.0", + "source": { + "type": "git", + "url": "https://github.com/nikic/PHP-Parser.git", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", + "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "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.0-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.4.0" + }, + "time": "2024-12-30T11:07:19+00:00" + }, + { + "name": "phpdocumentor/reflection", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/Reflection.git", + "reference": "bb4dea805a645553d6d989b23dad9f8041f39502" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/bb4dea805a645553d6d989b23dad9f8041f39502", + "reference": "bb4dea805a645553d6d989b23dad9f8041f39502", + "shasum": "" + }, + "require": { + "nikic/php-parser": "~4.18 || ^5.0", + "php": "8.1.*|8.2.*|8.3.*|8.4.*", + "phpdocumentor/reflection-common": "^2.1", + "phpdocumentor/reflection-docblock": "^5", + "phpdocumentor/type-resolver": "^1.2", + "symfony/polyfill-php80": "^1.28", + "webmozart/assert": "^1.7" + }, + "require-dev": { + "dealerdirect/phpcodesniffer-composer-installer": "^1.0", + "doctrine/coding-standard": "^12.0", + "mikey179/vfsstream": "~1.2", + "mockery/mockery": "~1.6.0", + "phpspec/prophecy-phpunit": "^2.0", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.8", + "phpstan/phpstan-webmozart-assert": "^1.2", + "phpunit/phpunit": "^10.0", + "psalm/phar": "^5.24", + "rector/rector": "^1.0.0", + "squizlabs/php_codesniffer": "^3.8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-5.x": "5.3.x-dev", + "dev-6.x": "6.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "phpDocumentor\\": "src/phpDocumentor" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "description": "Reflection library to do Static Analysis for PHP Projects", + "homepage": "http://www.phpdoc.org", + "keywords": [ + "phpDocumentor", + "phpdoc", + "reflection", + "static analysis" + ], + "support": { + "issues": "https://github.com/phpDocumentor/Reflection/issues", + "source": "https://github.com/phpDocumentor/Reflection/tree/6.1.0" + }, + "time": "2024-11-22T15:11:54+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.1", + "source": { + "type": "git", + "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "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.1" + }, + "time": "2024-12-07T09:39:29+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.1.0", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpdoc-parser.git", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "reference": "9b30d6fd026b2c132b3985ce6b23bec09ab3aa68", + "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.1.0" + }, + "time": "2025-02-19T13:28:12+00:00" + }, + { + "name": "psr/clock", + "version": "1.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/clock.git", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/clock/zipball/e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "reference": "e41a24703d4560fd0acb709162f73b8adfc3aa0d", + "shasum": "" + }, + "require": { + "php": "^7.0 || ^8.0" + }, + "type": "library", + "autoload": { + "psr-4": { + "Psr\\Clock\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for reading the clock.", + "homepage": "https://github.com/php-fig/clock", + "keywords": [ + "clock", + "now", + "psr", + "psr-20", + "time" + ], + "support": { + "issues": "https://github.com/php-fig/clock/issues", + "source": "https://github.com/php-fig/clock/tree/1.0.0" + }, + "time": "2022-11-25T14:36:26+00:00" + }, + { + "name": "psr/container", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/php-fig/container.git", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/container/zipball/c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "reference": "c71ecc56dfe541dbd90c5360474fbc405f8d5963", + "shasum": "" + }, + "require": { + "php": ">=7.4.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Container\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common Container Interface (PHP FIG PSR-11)", + "homepage": "https://github.com/php-fig/container", + "keywords": [ + "PSR-11", + "container", + "container-interface", + "container-interop", + "psr" + ], + "support": { + "issues": "https://github.com/php-fig/container/issues", + "source": "https://github.com/php-fig/container/tree/2.0.2" + }, + "time": "2021-11-05T16:47:00+00:00" + }, + { + "name": "psr/simple-cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/simple-cache.git", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/simple-cache/zipball/764e0b3939f5ca87cb904f570ef9be2d78a07865", + "reference": "764e0b3939f5ca87cb904f570ef9be2d78a07865", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\SimpleCache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interfaces for simple caching", + "keywords": [ + "cache", + "caching", + "psr", + "psr-16", + "simple-cache" + ], + "support": { + "source": "https://github.com/php-fig/simple-cache/tree/3.0.0" + }, + "time": "2021-10-29T13:26:27+00:00" + }, + { + "name": "symfony/deprecation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/deprecation-contracts.git", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-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.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/polyfill-mbstring", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", + "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "shasum": "" + }, + "require": { + "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.31.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-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php80", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "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.31.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-09T11:45:10+00:00" + }, + { + "name": "symfony/translation", + "version": "v6.4.19", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation.git", + "reference": "3b9bf9f33997c064885a7bfc126c14b9daa0e00e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation/zipball/3b9bf9f33997c064885a7bfc126c14b9daa0e00e", + "reference": "3b9bf9f33997c064885a7bfc126c14b9daa0e00e", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", + "autoload": { + "files": [ + "Resources/functions.php" + ], + "psr-4": { + "Symfony\\Component\\Translation\\": "" + }, + "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 to internationalize your application", + "homepage": "https://symfony.com", + "support": { + "source": "https://github.com/symfony/translation/tree/v6.4.19" + }, + "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-13T10:18:43+00:00" + }, + { + "name": "symfony/translation-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", + "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.5-dev" + } + }, + "autoload": { + "psr-4": { + "Symfony\\Contracts\\Translation\\": "" + }, + "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 translation", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], + "support": { + "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "voku/portable-ascii", + "version": "2.0.3", + "source": { + "type": "git", + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "shasum": "" + }, + "require": { + "php": ">=7.0.0" + }, + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" + }, + "type": "library", + "autoload": { + "psr-4": { + "voku\\": "src/voku/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" + } + ], + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", + "keywords": [ + "ascii", + "clean", + "php" + ], + "support": { + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + }, + "funding": [ + { + "url": "https://www.paypal.me/moelleken", + "type": "custom" + }, + { + "url": "https://github.com/voku", + "type": "github" + }, + { + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "type": "tidelift" + } + ], + "time": "2024-11-21T01:49:47+00:00" + }, + { + "name": "webmozart/assert", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "shasum": "" + }, + "require": { + "ext-ctype": "*", + "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" + }, + "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.11.0" + }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.4.8", + "source": { + "type": "git", + "url": "https://github.com/paratestphp/paratest.git", + "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/cf16fcbb9b8107a7df6b97e497fc91e819774d8b", + "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^1.2.0", + "jean85/pretty-package-versions": "^2.0.6", + "php": "~8.2.0 || ~8.3.0 || ~8.4.0", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-timer": "^6.0.0", + "phpunit/phpunit": "^10.5.36", + "sebastian/environment": "^6.1.0", + "symfony/console": "^6.4.7 || ^7.1.5", + "symfony/process": "^6.4.7 || ^7.1.5" + }, + "require-dev": { + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "phpstan/phpstan": "^1.12.6", + "phpstan/phpstan-deprecation-rules": "^1.2.1", + "phpstan/phpstan-phpunit": "^1.4.0", + "phpstan/phpstan-strict-rules": "^1.6.1", + "squizlabs/php_codesniffer": "^3.10.3", + "symfony/filesystem": "^6.4.3 || ^7.1.5" + }, + "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.4.8" + }, + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2024-10-15T12:45:19+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.3", + "source": { + "type": "git", + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "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.3" + }, + "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-09-19T14:15:21+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/annotations", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/doctrine/annotations.git", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", + "shasum": "" + }, + "require": { + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" + }, + "require-dev": { + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.10.28", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "keywords": [ + "annotations", + "docblock", + "parser" + ], + "support": { + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.2" + }, + "time": "2024-09-05T10:17:24+00:00" + }, + { + "name": "doctrine/lexer", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "shasum": "" + }, + "require": { + "php": "^8.1" + }, + "require-dev": { + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" + }, + "type": "library", + "autoload": { + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "keywords": [ + "annotations", + "docblock", + "lexer", + "parser", + "php" + ], + "support": { + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" + }, + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+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.2.0", + "source": { + "type": "git", + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "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": "^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" + }, + "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.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "filp/whoops", + "version": "2.17.0", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "075bc0c26631110584175de6523ab3f1652eb28e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e", + "reference": "075bc0c26631110584175de6523ab3f1652eb28e", + "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.17.0" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-01-25T12:00:00+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.70.2", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "1ca468270efbb75ce0c7566a79cca8ea2888584d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1ca468270efbb75ce0c7566a79cca8ea2888584d", + "reference": "1ca468270efbb75ce0c7566a79cca8ea2888584d", + "shasum": "" + }, + "require": { + "clue/ndjson-react": "^1.0", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.2", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.5", + "react/event-loop": "^1.0", + "react/promise": "^2.0 || ^3.0", + "react/socket": "^1.0", + "react/stream": "^1.0", + "sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0", + "symfony/console": "^5.4 || ^6.4 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", + "symfony/finder": "^5.4 || ^6.4 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.4 || ^7.0", + "symfony/polyfill-mbstring": "^1.31", + "symfony/polyfill-php80": "^1.31", + "symfony/polyfill-php81": "^1.31", + "symfony/process": "^5.4 || ^6.4 || ^7.2", + "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3.1 || ^2.5", + "infection/infection": "^0.29.10", + "justinrainbow/json-schema": "^5.3 || ^6.0", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.7", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.0", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.0" + }, + "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.70.2" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2025-03-03T21:07:23+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.0.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "shasum": "" + }, + "require": { + "php": "^5.3|^7.0|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + }, + "time": "2020-07-09T08:09:16+00:00" + }, + { + "name": "jean85/pretty-package-versions", + "version": "2.1.0", + "source": { + "type": "git", + "url": "https://github.com/Jean85/pretty-package-versions.git", + "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "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": "^1.4", + "phpunit/phpunit": "^7.5|^8.5|^9.6", + "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.0" + }, + "time": "2024-11-18T16:19:46+00:00" + }, + { + "name": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" + } + ], + "description": "Mockery is a simple yet flexible PHP mock object framework", + "homepage": "https://github.com/mockery/mockery", + "keywords": [ + "BDD", + "TDD", + "library", + "mock", + "mock objects", + "mockery", + "stub", + "test", + "test double", + "testing" + ], + "support": { + "docs": "https://docs.mockery.io/", + "issues": "https://github.com/mockery/mockery/issues", + "rss": "https://github.com/mockery/mockery/releases.atom", + "security": "https://github.com/mockery/mockery/security/advisories", + "source": "https://github.com/mockery/mockery" + }, + "time": "2024-05-16T03:13:13+00:00" + }, + { + "name": "myclabs/deep-copy", + "version": "1.13.0", + "source": { + "type": "git", + "url": "https://github.com/myclabs/DeepCopy.git", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", + "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "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.0" + }, + "funding": [ + { + "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy", + "type": "tidelift" + } + ], + "time": "2025-02-12T12:17:51+00:00" + }, + { + "name": "nunomaduro/collision", + "version": "v8.5.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/collision.git", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", + "reference": "f5c101b929c958e849a633283adff296ed5f38f5", + "shasum": "" + }, + "require": { + "filp/whoops": "^2.16.0", + "nunomaduro/termwind": "^2.1.0", + "php": "^8.2.0", + "symfony/console": "^7.1.5" + }, + "conflict": { + "laravel/framework": "<11.0.0 || >=12.0.0", + "phpunit/phpunit": "<10.5.1 || >=12.0.0" + }, + "require-dev": { + "larastan/larastan": "^2.9.8", + "laravel/framework": "^11.28.0", + "laravel/pint": "^1.18.1", + "laravel/sail": "^1.36.0", + "laravel/sanctum": "^4.0.3", + "laravel/tinker": "^2.10.0", + "orchestra/testbench-core": "^9.5.3", + "pestphp/pest": "^2.36.0 || ^3.4.0", + "sebastian/environment": "^6.1.0 || ^7.2.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", + "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": "2024-10-15T16:06:32+00:00" + }, + { + "name": "nunomaduro/termwind", + "version": "v2.3.0", + "source": { + "type": "git", + "url": "https://github.com/nunomaduro/termwind.git", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": "^8.2", + "symfony/console": "^7.1.8" + }, + "require-dev": { + "illuminate/console": "^11.33.2", + "laravel/pint": "^1.18.2", + "mockery/mockery": "^1.6.12", + "pestphp/pest": "^2.36.0", + "phpstan/phpstan": "^1.12.11", + "phpstan/phpstan-strict-rules": "^1.6.1", + "symfony/var-dumper": "^7.1.8", + "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.0" + }, + "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": "2024-11-21T10:39:51+00:00" + }, + { + "name": "pestphp/pest", + "version": "v2.36.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest.git", + "reference": "f8c88bd14dc1772bfaf02169afb601ecdf2724cd" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest/zipball/f8c88bd14dc1772bfaf02169afb601ecdf2724cd", + "reference": "f8c88bd14dc1772bfaf02169afb601ecdf2724cd", + "shasum": "" + }, + "require": { + "brianium/paratest": "^7.3.1", + "nunomaduro/collision": "^7.11.0|^8.4.0", + "nunomaduro/termwind": "^1.16.0|^2.1.0", + "pestphp/pest-plugin": "^2.1.1", + "pestphp/pest-plugin-arch": "^2.7.0", + "php": "^8.1.0", + "phpunit/phpunit": "^10.5.36" + }, + "conflict": { + "filp/whoops": "<2.16.0", + "phpunit/phpunit": ">10.5.36", + "sebastian/exporter": "<5.1.0", + "webmozart/assert": "<1.11.0" + }, + "require-dev": { + "pestphp/pest-dev-tools": "^2.17.0", + "pestphp/pest-plugin-type-coverage": "^2.8.7", + "symfony/process": "^6.4.0|^7.1.5" + }, + "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" + ] + } + }, + "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/v2.36.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-10-15T15:30:56+00:00" + }, + { + "name": "pestphp/pest-plugin", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin.git", + "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin/zipball/e05d2859e08c2567ee38ce8b005d044e72648c0b", + "reference": "e05d2859e08c2567ee38ce8b005d044e72648c0b", + "shasum": "" + }, + "require": { + "composer-plugin-api": "^2.0.0", + "composer-runtime-api": "^2.2.2", + "php": "^8.1" + }, + "conflict": { + "pestphp/pest": "<2.2.3" + }, + "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" + }, + "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/v2.1.1" + }, + "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": "2023-08-22T08:40:06+00:00" + }, + { + "name": "pestphp/pest-plugin-arch", + "version": "v2.7.0", + "source": { + "type": "git", + "url": "https://github.com/pestphp/pest-plugin-arch.git", + "reference": "d23b2d7498475354522c3818c42ef355dca3fcda" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/pestphp/pest-plugin-arch/zipball/d23b2d7498475354522c3818c42ef355dca3fcda", + "reference": "d23b2d7498475354522c3818c42ef355dca3fcda", + "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" + }, + "require-dev": { + "pestphp/pest": "^2.33.0", + "pestphp/pest-dev-tools": "^2.16.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/v2.7.0" + }, + "funding": [ + { + "url": "https://www.paypal.com/paypalme/enunomaduro", + "type": "custom" + }, + { + "url": "https://github.com/nunomaduro", + "type": "github" + } + ], + "time": "2024-01-26T09:46:42+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": "phpbench/container", + "version": "2.2.2", + "source": { + "type": "git", + "url": "https://github.com/phpbench/container.git", + "reference": "a59b929e00b87b532ca6d0edd8eca0967655af33" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpbench/container/zipball/a59b929e00b87b532ca6d0edd8eca0967655af33", + "reference": "a59b929e00b87b532ca6d0edd8eca0967655af33", + "shasum": "" + }, + "require": { + "psr/container": "^1.0|^2.0", + "symfony/options-resolver": "^4.2 || ^5.0 || ^6.0 || ^7.0" + }, + "require-dev": { + "friendsofphp/php-cs-fixer": "^2.16", + "phpstan/phpstan": "^0.12.52", + "phpunit/phpunit": "^8" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.x-dev" + } + }, + "autoload": { + "psr-4": { + "PhpBench\\DependencyInjection\\": "lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Leech", + "email": "daniel@dantleech.com" + } + ], + "description": "Simple, configurable, service container.", + "support": { + "issues": "https://github.com/phpbench/container/issues", + "source": "https://github.com/phpbench/container/tree/2.2.2" + }, + "time": "2023-10-30T13:38:26+00:00" + }, + { + "name": "phpbench/phpbench", + "version": "1.4.0", + "source": { + "type": "git", + "url": "https://github.com/phpbench/phpbench.git", + "reference": "4248817222514421cba466bfa7adc7d8932345d4" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/4248817222514421cba466bfa7adc7d8932345d4", + "reference": "4248817222514421cba466bfa7adc7d8932345d4", + "shasum": "" + }, + "require": { + "doctrine/annotations": "^2.0", + "ext-dom": "*", + "ext-json": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-spl": "*", + "ext-tokenizer": "*", + "php": "^8.1", + "phpbench/container": "^2.2", + "psr/log": "^1.1 || ^2.0 || ^3.0", + "seld/jsonlint": "^1.1", + "symfony/console": "^6.1 || ^7.0", + "symfony/filesystem": "^6.1 || ^7.0", + "symfony/finder": "^6.1 || ^7.0", + "symfony/options-resolver": "^6.1 || ^7.0", + "symfony/process": "^6.1 || ^7.0", + "webmozart/glob": "^4.6" + }, + "require-dev": { + "dantleech/invoke": "^2.0", + "ergebnis/composer-normalize": "^2.39", + "friendsofphp/php-cs-fixer": "^3.0", + "jangregor/phpstan-prophecy": "^1.0", + "phpspec/prophecy": "dev-master", + "phpstan/extension-installer": "^1.1", + "phpstan/phpstan": "^1.0", + "phpstan/phpstan-phpunit": "^1.0", + "phpunit/phpunit": "^10.4 || ^11.0", + "rector/rector": "^1.2", + "symfony/error-handler": "^6.1 || ^7.0", + "symfony/var-dumper": "^6.1 || ^7.0" + }, + "suggest": { + "ext-xdebug": "For Xdebug profiling extension." + }, + "bin": [ + "bin/phpbench" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.2-dev" + } + }, + "autoload": { + "files": [ + "lib/Report/Func/functions.php" + ], + "psr-4": { + "PhpBench\\": "lib/", + "PhpBench\\Extensions\\XDebug\\": "extensions/xdebug/lib/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Daniel Leech", + "email": "daniel@dantleech.com" + } + ], + "description": "PHP Benchmarking Framework", + "keywords": [ + "benchmarking", + "optimization", + "performance", + "profiling", + "testing" + ], + "support": { + "issues": "https://github.com/phpbench/phpbench/issues", + "source": "https://github.com/phpbench/phpbench/tree/1.4.0" + }, + "funding": [ + { + "url": "https://github.com/dantleech", + "type": "github" + } + ], + "time": "2025-01-26T19:54:45+00:00" + }, + { + "name": "phpstan/phpstan", + "version": "2.1.6", + "source": { + "type": "git", + "url": "https://github.com/phpstan/phpstan.git", + "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", + "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", + "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-02-19T15:46:42+00:00" + }, + { + "name": "phpunit/php-code-coverage", + "version": "10.1.16", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-code-coverage.git", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/7e308268858ed6baedc8704a304727d20bc07c77", + "reference": "7e308268858ed6baedc8704a304727d20bc07c77", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-libxml": "*", + "ext-xmlwriter": "*", + "nikic/php-parser": "^4.19.1 || ^5.1.0", + "php": ">=8.1", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-text-template": "^3.0.1", + "sebastian/code-unit-reverse-lookup": "^3.0.0", + "sebastian/complexity": "^3.2.0", + "sebastian/environment": "^6.1.0", + "sebastian/lines-of-code": "^2.0.2", + "sebastian/version": "^4.0.1", + "theseer/tokenizer": "^1.2.3" + }, + "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" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.1.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/10.1.16" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-08-22T04:31:57+00:00" + }, + { + "name": "phpunit/php-file-iterator", + "version": "4.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-file-iterator.git", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/a95037b6d9e608ba092da1b23931e537cadc3c3c", + "reference": "a95037b6d9e608ba092da1b23931e537cadc3c3c", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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/4.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T06:24:48+00:00" + }, + { + "name": "phpunit/php-invoker", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-invoker.git", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "reference": "f5e568ba02fa5ba0ddd0f618391d5a9ea50b06d7", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "ext-pcntl": "*", + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-pcntl": "*" + }, + "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": "Invoke callables with a timeout", + "homepage": "https://github.com/sebastianbergmann/php-invoker/", + "keywords": [ + "process" + ], + "support": { + "issues": "https://github.com/sebastianbergmann/php-invoker/issues", + "source": "https://github.com/sebastianbergmann/php-invoker/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:56:09+00:00" + }, + { + "name": "phpunit/php-text-template", + "version": "3.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-text-template.git", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "reference": "0c7b06ff49e3d5072f057eb1fa59258bf287a748", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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/3.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-08-31T14:07:24+00:00" + }, + { + "name": "phpunit/php-timer", + "version": "6.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/php-timer.git", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "reference": "e2a2d67966e740530f4a3343fe2e030ffdc1161d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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", + "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", + "source": "https://github.com/sebastianbergmann/php-timer/tree/6.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:57:52+00:00" + }, + { + "name": "phpunit/phpunit", + "version": "10.5.36", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/phpunit.git", + "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", + "reference": "aa0a8ce701ea7ee314b0dfaa8970dc94f3f8c870", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-json": "*", + "ext-libxml": "*", + "ext-mbstring": "*", + "ext-xml": "*", + "ext-xmlwriter": "*", + "myclabs/deep-copy": "^1.12.0", + "phar-io/manifest": "^2.0.4", + "phar-io/version": "^3.2.1", + "php": ">=8.1", + "phpunit/php-code-coverage": "^10.1.16", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-invoker": "^4.0.0", + "phpunit/php-text-template": "^3.0.1", + "phpunit/php-timer": "^6.0.0", + "sebastian/cli-parser": "^2.0.1", + "sebastian/code-unit": "^2.0.0", + "sebastian/comparator": "^5.0.2", + "sebastian/diff": "^5.1.1", + "sebastian/environment": "^6.1.0", + "sebastian/exporter": "^5.1.2", + "sebastian/global-state": "^6.0.2", + "sebastian/object-enumerator": "^5.0.0", + "sebastian/recursion-context": "^5.0.0", + "sebastian/type": "^4.0.0", + "sebastian/version": "^4.0.1" + }, + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.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/10.5.36" + }, + "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", + "type": "tidelift" + } + ], + "time": "2024-10-08T15:36:51+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+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": "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.2.0", + "source": { + "type": "git", + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "shasum": "" + }, + "require": { + "php": ">=7.1.0" + }, + "require-dev": { + "phpstan/phpstan": "1.10.39 || 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.2.0" + }, + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-05-24T10:39:05+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": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:58:43+00:00" + }, + { + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + } + ], + "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", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T06:59:15+00:00" + }, + { + "name": "sebastian/comparator", + "version": "5.0.3", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "shasum": "" + }, + "require": { + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.5" + }, + "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" + }, + { + "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/5.0.3" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-10-18T14:56:07+00:00" + }, + { + "name": "sebastian/complexity", + "version": "3.2.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.2-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/3.2.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:37:17+00:00" + }, + { + "name": "sebastian/diff", + "version": "5.1.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" + }, + "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" + }, + { + "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/5.1.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:15:17+00:00" + }, + { + "name": "sebastian/environment", + "version": "6.1.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "suggest": { + "ext-posix": "*" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "6.1-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/6.1.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-23T08:47:14+00:00" + }, + { + "name": "sebastian/exporter", + "version": "5.1.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "shasum": "" + }, + "require": { + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "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" + }, + { + "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/5.1.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:17:12+00:00" + }, + { + "name": "sebastian/global-state", + "version": "6.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.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": "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/6.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:19:19+00:00" + }, + { + "name": "sebastian/lines-of-code", + "version": "2.0.2", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "shasum": "" + }, + "require": { + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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/2.0.2" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-12-21T08:38:20+00:00" + }, + { + "name": "sebastian/object-enumerator", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + } + ], + "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", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:08:32+00:00" + }, + { + "name": "sebastian/object-reflector", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + } + ], + "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", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:06:18+00:00" + }, + { + "name": "sebastian/recursion-context", + "version": "5.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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" + }, + { + "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", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:05:40+00:00" + }, + { + "name": "sebastian/type", + "version": "4.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.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": "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", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-03T07:10:45+00:00" + }, + { + "name": "sebastian/version", + "version": "4.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "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 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", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2023-02-07T11:34:05+00:00" + }, + { + "name": "seld/jsonlint", + "version": "1.11.0", + "source": { + "type": "git", + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "shasum": "" + }, + "require": { + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + }, + "bin": [ + "bin/jsonlint" + ], + "type": "library", + "autoload": { + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" + } + ], + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], + "support": { + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" + }, + "funding": [ + { + "url": "https://github.com/Seldaek", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" + } + ], + "time": "2024-07-11T14:55:45+00:00" + }, + { + "name": "symfony/console", + "version": "v7.2.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "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" + }, + "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.2.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-11T03:49:26+00:00" + }, + { + "name": "symfony/event-dispatcher", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", + "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "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.2.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-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", + "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "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.5-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.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/filesystem", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/filesystem.git", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "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.2.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-10-25T15:15:23+00:00" + }, + { + "name": "symfony/finder", + "version": "v7.2.2", + "source": { + "type": "git", + "url": "https://github.com/symfony/finder.git", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", + "reference": "87a71856f2f56e4100373e92529eed3171695cfb", + "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.2.2" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-12-30T19:00:17+00:00" + }, + { + "name": "symfony/options-resolver", + "version": "v7.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/options-resolver.git", + "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "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.2.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-11-20T11:17:29+00:00" + }, + { + "name": "symfony/polyfill-ctype", + "version": "v1.31.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.31.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-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.31.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "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.31.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-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.31.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.31.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-09T11:45:10+00:00" + }, + { + "name": "symfony/polyfill-php81", + "version": "v1.31.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.31.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-09T11:45:10+00:00" + }, + { + "name": "symfony/process", + "version": "v7.2.4", + "source": { + "type": "git", + "url": "https://github.com/symfony/process.git", + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "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.2.4" + }, + "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-05T08:33:46+00:00" + }, + { + "name": "symfony/service-contracts", + "version": "v3.5.1", + "source": { + "type": "git", + "url": "https://github.com/symfony/service-contracts.git", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "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.5-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.5.1" + }, + "funding": [ + { + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" + } + ], + "time": "2024-09-25T14:20:29+00:00" + }, + { + "name": "symfony/stopwatch", + "version": "v7.2.4", + "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.2.4" + }, + "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.2.0", + "source": { + "type": "git", + "url": "https://github.com/symfony/string.git", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", + "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "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/error-handler": "^6.4|^7.0", + "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.2.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-11-13T13:31:26+00:00" + }, + { + "name": "ta-tikoma/phpunit-architecture-test", + "version": "0.8.4", + "source": { + "type": "git", + "url": "https://github.com/ta-tikoma/phpunit-architecture-test.git", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/ta-tikoma/phpunit-architecture-test/zipball/89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "reference": "89f0dea1cb0f0d5744d3ec1764a286af5e006636", + "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", + "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.4" + }, + "time": "2024-01-05T14:10:56+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/glob", + "version": "4.7.0", + "source": { + "type": "git", + "url": "https://github.com/webmozarts/glob.git", + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/webmozarts/glob/zipball/8a2842112d6916e61e0e15e316465b611f3abc17", + "reference": "8a2842112d6916e61e0e15e316465b611f3abc17", + "shasum": "" + }, + "require": { + "php": "^7.3 || ^8.0.0" + }, + "require-dev": { + "phpunit/phpunit": "^9.5", + "symfony/filesystem": "^5.3" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "4.1-dev" + } + }, + "autoload": { + "psr-4": { + "Webmozart\\Glob\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" + } + ], + "description": "A PHP implementation of Ant's glob.", + "support": { + "issues": "https://github.com/webmozarts/glob/issues", + "source": "https://github.com/webmozarts/glob/tree/4.7.0" + }, + "time": "2024-03-07T20:33:40+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" +} diff --git a/src/OpenApi/Annotations/Headers.php b/src/OpenApi/Annotations/Headers.php new file mode 100755 index 0000000..d5255f0 --- /dev/null +++ b/src/OpenApi/Annotations/Headers.php @@ -0,0 +1,24 @@ +'1'],['test-header'=>'test']] + * withOutHeaders: ['test-header','company-id'] + */ + public function __construct( + public array $headers = [], + public array $withOutHeaders = [] + ) { + } +} diff --git a/src/OpenApi/Annotations/OpenApi.php b/src/OpenApi/Annotations/OpenApi.php new file mode 100755 index 0000000..20f77e2 --- /dev/null +++ b/src/OpenApi/Annotations/OpenApi.php @@ -0,0 +1,16 @@ +method->name; + } +} diff --git a/src/OpenApi/Annotations/Summary.php b/src/OpenApi/Annotations/Summary.php new file mode 100755 index 0000000..ca893ce --- /dev/null +++ b/src/OpenApi/Annotations/Summary.php @@ -0,0 +1,21 @@ +value, $this->description); + } +} diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php new file mode 100644 index 0000000..82a119d --- /dev/null +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -0,0 +1,28 @@ + $parameters, */ + public array $parameters = [], + /** @var array $requestBody, */ + public array $requestBody = [], + /** @var array $responses, */ + public array $response = [], + + ){ + } + +} \ No newline at end of file diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php new file mode 100755 index 0000000..3625566 --- /dev/null +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -0,0 +1,28 @@ + $children */ + public array $children = [], + ){ + } +} diff --git a/src/OpenApi/Enum/ContentTypeEnum.php b/src/OpenApi/Enum/ContentTypeEnum.php new file mode 100755 index 0000000..cada37e --- /dev/null +++ b/src/OpenApi/Enum/ContentTypeEnum.php @@ -0,0 +1,10 @@ +headerParameterStorages->addHeaderProperties($name, $description, $example); + return $this; + } + + /** + * Undocumented function + */ + public function getOpenAPI(): OpenAPI + { + return self::$OpenAPI; + } + + /** + * 增加类前缀标识 + * + * @return $this + */ + public function withControllerPrefix(string $value): self + { + $this->controllerPrefix = $value; + return $this; + } + + /** + * 增加类后缀标识 + * + * @return $this + */ + public function withControllerSuffix(string $value): self + { + $this->controllerSuffix = $value; + return $this; + } + + public function handleByAutoLoad(): void + { + $classMap = require base_path('vendor/composer/autoload_classmap.php'); + $appClassMap = array_keys(array_filter($classMap, function ($key) { + return (str_starts_with($key, 'App\\') && str_ends_with($key, 'Controller')) + || (str_starts_with($key, 'April\\') && str_ends_with($key, 'Controller')); + }, ARRAY_FILTER_USE_KEY)); + + foreach ($appClassMap as $className) { + $this->createOpenAPIByClass($className); + } + } + + /** + * 解析Controller文件 + * + * @param array $folders 文件路径 => 命名空间 + * @return $this + */ + public function handleByFolders(array $folders): self + { + + foreach ($folders as $folder => $namespace) { + + if (! is_dir($folder)) { + continue; + } + + foreach (scandir($folder) as $file) { + + $path = $folder . '/' . $file; + if ($file == '.' || $file == '..' || strpos($file, '.') === 0) { + continue; + } + + if (is_dir($path)) { + $this->handleByFolders([$path => $namespace . '\\' . $file]); + + continue; + } + + if (pathinfo($file, PATHINFO_EXTENSION) !== 'php') { + continue; + } + + $fileName = $this->controllerPrefix . trim(substr($file, 0, strpos($file, '.'))) . $this->controllerSuffix; + $className = $namespace ? $namespace . '\\' . $fileName : $fileName; + + if (! class_exists($className)) { + include_once $folder . '/' . $file; + ob_clean(); // 清除一些引入进来的莫名其妙输出文件 + if (! class_exists($className)) { + continue; + } + } + + $this->createOpenAPIByClass($className); + } + } + + return $this; + } + + /** + * 根据类信息构建Schema + * @throws Exception + */ + public function buildSchemaByClass(string $className, ?string $group = null): SchemaStorage + { + $schema = new SchemaStorage(); + + if (! $className) { + return $schema; + } + + $ParserPartaker = new ParserPartaker(); + $ParserPartaker->addNode($className, $group, null); + + $tree = $ParserPartaker->getTree(); + + $schema->createTree($tree->getChildren()); + + return $schema; + } + + public function output(string $path): bool + { + return true; + } + + /** + * @throws JsonException + */ + public function toString(): string + { + return json_encode(self::$OpenAPI, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); + } +} diff --git a/src/OpenApi/Handler/ParserPartaker.php b/src/OpenApi/Handler/ParserPartaker.php new file mode 100755 index 0000000..3efdd85 --- /dev/null +++ b/src/OpenApi/Handler/ParserPartaker.php @@ -0,0 +1,191 @@ +tree = new TreeNode(); + } + + public function addNode(string $className, ?string $group = null, ?TreeNode $childTree = null) + { + + $classRefection = new ReflectionClass($className); + + foreach ($classRefection->getProperties() as $property) { + + if ($property->isProtected()) { + continue; + } + + if ($group && class_exists(Group::class)) { + $attributes = $property->getAttributes(Group::class); + if (! $attributes) { + continue; + } + + $groups = $attributes[0]->newInstance(); + if (! in_array($group, $groups->names)) { + continue; + } + } + + $tree = new TreeNode($property); + if ($childTree) { + $childTree->addChildren($tree); + } else { + $this->tree->addChildren($tree); + } + + // // debug + // $docComment = <<getDocComment(); + $varMatch = getVarByDocComment($docComment); + if (! $varMatch) { + continue; + } + + /** + * var array<*,{string-class}> + * var {string-class}[] + * var {string-class} + */ + if ( + ! preg_match('#array<\S+,(\S+)>#', $varMatch[1], $arrayMatch) + && ! preg_match('#(\S+)\[\]#', $varMatch[1], $arrayMatch) + && ! preg_match('#(\S+)#', $varMatch[1], $arrayMatch) + ) { + continue; + } + + $listClass = trim($arrayMatch[1]); + if (in_array(strtolower($listClass), $this->ignoreConst)) { + continue; + } + + try { + $listClass = $this->getFullClassName($property, $listClass); + } catch (\Throwable $th) { + continue; + } + + // 添加子级类信息 + if ($listClass != $className) { + $this->addNode($listClass, $group, $tree); + } + } + } + + public function getFullClassName(ReflectionProperty $property, string $className): string + { + // 当前类命名空间 + $selfNamespaceName = $property->getDeclaringClass()->getNamespaceName(); + // 直接匹配类 + if (class_exists($className)) { + return $className; + } + // 判断是否是同一命名空间下的类 + elseif (class_exists($selfNamespaceName . '\\' . $className)) { + return $selfNamespaceName . '\\' . $className; + } + // 判断是否是引用类 + else { + // 获取引入文件 + $importClass = $this->parseUseStatements($property->getDeclaringClass()); + if (isset($importClass[$className])) { + return $importClass[$className]; + } + } + + throw new Exception('not find class ' . $className); + } + + /** + * 获取引入文件 + */ + private function parseUseStatements(ReflectionClass $reflectionClass): array + { + + $content = file_get_contents($reflectionClass->getFileName()); + preg_match_all('/^\s*use[\s+](.*);$/m', $content, $matches); + $classNames = []; + foreach ($matches[1] as $fullClassName) { + $parts = explode('\\', $fullClassName); + $classNames[end($parts)] = $fullClassName; + } + + return $classNames; + } + + /** + * Undocumented function + * + * @return TreeNode + */ + public function getTree() + { + return $this->tree; + } +} diff --git a/src/OpenApi/Handler/SerializeHandler.php b/src/OpenApi/Handler/SerializeHandler.php new file mode 100755 index 0000000..396fbba --- /dev/null +++ b/src/OpenApi/Handler/SerializeHandler.php @@ -0,0 +1,152 @@ +getAttributes(Tag::class); + /** @var Tag $tagDoc */ + $tagDoc = isset($tagDoc[0]) ? $tagDoc[0]->newInstance() : null; + if ($tagDoc) { + self::$OpenAPI->addTag($tagDoc->buildTagStorage()); + } + + foreach ($classRefection->getMethods() as $item) { + + $methodAttributes = $item->getAttributes(); + + if (! $methodAttributes) { + continue; + } + + $routeDoc = $summaryDoc = $requestBodyDoc = $responseDoc = $headersDoc = null; + $instances = [ + Route::class => &$routeDoc, + Summary::class => &$summaryDoc, + RequestBody::class => &$requestBodyDoc, + Response::class => &$responseDoc, + Headers::class => &$headersDoc, + ]; + foreach ($methodAttributes as $methodAttribute) { + $name = $methodAttribute->getName(); + if (isset($instances[$name])) { + $instances[$name] = $methodAttribute->newInstance(); + } + } + + if (! $routeDoc || ! $summaryDoc) { + continue; + } + + $returnClass = $classRefection->getMethod($item->name)->getReturnType(); + $responseClass = match(true){ + $responseDoc => $responseDoc->className, + $returnClass && $returnClass instanceof Serialize::class =>$returnClass, + default => null, + }; + + new OpenApiCollection( + controllerClass: $className, + methodName: $item->getName(), + summary:$summaryDoc, + route: $routeDoc, + headers: $headersDoc, + attributes: $methodAttributes, + requestBody: $this->buildRequestBodyParameterCollections($requestBodyDoc->className), + response: $requestBodyDoc ? $this->buildRequestBodyParameterCollections($responseClass) : [], + ); + } + } + + /** + * @param string $className + * @return array + */ + public function buildRequestBodyParameterCollections(string $className): array + { + $serializeContext = ContextFactory::build($className); + $serializeContext->from(); + $properties = $serializeContext->getChooseSerializeContext()->getProperties(); + + $vols = []; + foreach ($properties as $property){ + $vol = new ParameterCollection( + name:$property->getInputName(), + descriptions: '', + type: $property->getType()?->kind->name ?: 'STRING', + required: !$property->getDataCollection()->isNullable(), + ); + + if($property->getChildren()){ + $vol->children = $this->buildRequestBodyParameterCollections($property->getChildren()->get) + } + } + + return $vols; + } + + /** + * @param string $className + * @return array + */ + public function buildResponseParameterCollections(string $className): array + { + $serializeContext = ContextFactory::build($className); + $serializeContext->from(); + $properties = $serializeContext->getChooseSerializeContext()->getProperties(); + + $vols = []; + foreach ($properties as $property){ + + if(!$property->getInputName()){ + continue; + } + + $vols[] = new ParameterCollection( + name:$property->getInputName(), + descriptions: '', + type: $property->getType()?->kind->name ?: 'STRING', + required: !$property->getDataCollection()->isNullable(), + ); + } + + return $vols; + } +} diff --git a/src/OpenApi/Storage/OpenAPI/ApiInfo.php b/src/OpenApi/Storage/OpenAPI/ApiInfo.php new file mode 100755 index 0000000..f30a72e --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/ApiInfo.php @@ -0,0 +1,20 @@ + $parameters */ + public array|stdClass $parameters = new stdClass(), + public RequestBodyStorage|stdClass $requestBody = new stdClass(), + /** @var array 返回信息 */ + public array|stdClass $responses = new stdClass(), + ) { + } + + public function withTags(array $tags): Method + { + $this->tags = $tags; + + return $this; + } + + public function withParameters(array $parameters): static + { + $this->parameters = $parameters; + + return $this; + } + + public function addParameters(array $parameters): void + { + $this->parameters[] = $parameters; + } + + /** + * @return $this + */ + public function withRequestBody(RequestBodyStorage $body): static + { + $this->requestBody = $body; + + return $this; + } + + /** + * @param int $code + * @param ResponseStorage $response + * @return $this + */ + public function addResponse(int $code, ResponseStorage $response): static + { + + $this->responses = new stdClass(); // 去除初始化 + $this->responses[$code] = $response; + + return $this; + } +} diff --git a/src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php b/src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php new file mode 100755 index 0000000..d04a7b5 --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php @@ -0,0 +1,10 @@ + 请求api的标识符 */ + public array $tags; + + /** @var array 具体api配置 */ + public array $paths = []; + + public function withApiInfo(ApiInfo $apiInfo): self + { + $this->info = $apiInfo; + return $this; + } + + public function withServers(array $servers): self + { + $this->servers = $servers; + return $this; + } + + public function withTags(array $tags): self + { + $this->tags = $tags; + return $this; + } + + public function addTag(TagStorage $tag): void + { + $this->tags[] = $tag; + } + + public function withPaths(array $paths): self + { + $this->paths = $paths; + return $this; + } + + public function addPath(string $url, MethodInterface $method): self + { + $this->paths[$url][$method->getName()] = $method; + return $this; + } +} diff --git a/src/OpenApi/Storage/OpenAPI/ParameterStorage.php b/src/OpenApi/Storage/OpenAPI/ParameterStorage.php new file mode 100755 index 0000000..6479298 --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/ParameterStorage.php @@ -0,0 +1,73 @@ +data[] = [ + 'in' => 'query', + 'name' => $name, + 'description' => $description, + 'schema' => ['type' => $type], + 'example' => $example, + 'required' => $required, + ]; + } + + public function addHeaderProperties(string $name, string $description, string $example): void + { + $this->data[] = [ + 'in' => 'header', + 'name' => $name, + 'description' => $description, + 'schema' => ['type' => 'string'], + 'example' => $example, + 'required' => false, + ]; + } + + public function deleteHeadersProperties(array $names = []): void + { + if (! $names) { + return; + } + + foreach ($this->data as $key => ['in' => $type,'name' => $headerName]) { + if ($type === 'header' && in_array($headerName, $names, true)) { + unset($this->data[$key]); + break; + } + } + } + + public function getData(): array + { + return $this->data; + } +} diff --git a/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php new file mode 100755 index 0000000..689a7a0 --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php @@ -0,0 +1,27 @@ + 参数类型 */ + public array $content = [] + ) { + $this->content[$this->contentType->value]['schema'] = []; + } + + public function withParameter(SchemaStorage $schema): void + { + $this->content[$this->contentType->value]['schema'] = $schema->getData(); + } +} diff --git a/src/OpenApi/Storage/OpenAPI/ResponseStorage.php b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php new file mode 100755 index 0000000..565d23e --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php @@ -0,0 +1,29 @@ +content[$this->contentType]['schema'] = []; + } + + public function withParameter(SchemaStorage $schema): void + { + $this->content[$this->contentType]['schema'] = $schema->getData(); + } +} diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php new file mode 100755 index 0000000..0dd80b5 --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -0,0 +1,233 @@ + 'object', + 'properties' => [], + // 'required' => [], + ]; + + public function getData(): SchemaStorage|array + { + return $this->data; + } + + /** + * Undocumented function + * + * @param array $tree + * @param mixed|null $node + * @return void + * @throws Exception + */ + public function createTree(array $tree, mixed &$node = null): void + { + if ($node === null) { + $node = &$this->data; + } + + foreach ($tree as $item) { + + $parameter = $this->parserParameter($item->getValue()); + if ($parameter->ignore) { + continue; + } + + $node['properties'][$parameter->name] = [ + 'type' => $parameter->type, + 'description' => $parameter->value, + 'example' => $parameter->example, + ]; + + if ($parameter->required) { + $node['required'][] = $parameter->name; + } + + if ($item->getChildren()) { + // list对象 + if ($parameter->type === 'array') { + $node['properties'][$parameter->name]['items'] = [ + 'type' => 'object', + 'properties' => [], + // 'required' => [], + ]; + $tree = &$node['properties'][$parameter->name]['items']; + } + // 单个对象 + elseif ($parameter->type === 'object') { + $node['properties'][$parameter->name] = [ + 'type' => 'object', + 'properties' => [], + // 'required' => [], + ]; + $tree = &$node['properties'][$parameter->name]; + } + + $this->createTree($item->getChildren(), $tree); + } + } + } + + public function addProperties(string $name, string $type, string $description, string $example, bool $required = false): void + { + $this->data['properties'][$name] = [ + 'type' => $type, + 'description' => $description, + 'example' => $example, + ]; + + if ($required) { + $this->data['required'][] = $name; + } + } + + private function parserParameter(ReflectionProperty $prop): Parameter + { + + try { + + $parameterAttributes = $prop->getAttributes(Parameter::class); + + /** @var Parameter $parameter */ + $parameter = isset($parameterAttributes[0]) ? $parameterAttributes[0]->newInstance() : new Parameter(); + + // 转换输入名称 + if (class_exists(GetAlias::class) && $prop->getAttributes(GetAlias::class)) { + $getAsName = $prop->getAttributes(GetAlias::class)[0]->newInstance(); + $parameter->name = $getAsName->name; + } elseif (class_exists(SetAlisa::class) && $prop->getAttributes(SetAlisa::class)) { + $setAsName = $prop->getAttributes(SetAlisa::class)[0]->newInstance(); + $parameter->name = $setAsName->name; + } else { + $parameter->name = $prop->name; + } + + $docComment = $prop->getDocComment(); + + if (! $docComment) { + return $parameter; + } + + // 获取 类型/说明 @var {type} {name} or @var {type} + $varMatch = getVarByDocComment($docComment); + + // 获取 例值 @example {value} + preg_match('/@example\s*(\S+)/', $docComment, $exampleMatch); + + // 获取 是否必填 @requires true / @required true + preg_match('/@(?:requires|required)\s*(\S+)/i', $docComment, $requiredMatch); + + if ($requiredMatch) { + $parameter->required = $requiredMatch[1] == 'true' ? true : false; + } elseif (class_exists(NotBlank::class) && ($prop->getAttributes(NotBlank::class) ?? [])) { + $parameter->required = true; + } + + if ($varMatch) { + $parameter->type = $this->getType($varMatch[1]); + $parameter->value = $varMatch[2] ?? ''; + + $className = str_replace(['[', ']'], '', $varMatch[1]); + if (($parameter->type == 'object' || $parameter->type == 'array') + && ! in_array($className, ['int', 'float', 'bool', 'array', 'string', 'boolean', 'integer']) + ) { + try { + $fullClassName = (new ParserPartaker())->getFullClassName($prop, $className); + } catch (\Throwable $th) { + throw new Exception( + sprintf('%s not find from %s', $className, $prop->getDeclaringClass()->getName()), + $th->getCode(), + $th + ); + } + $reflectionClass = new ReflectionClass($fullClassName); + if ($reflectionClass->isEnum()) { + // object转string + $parameter->type = $parameter->type == 'object' ? 'string' : $parameter->type; + foreach ($reflectionClass->getConstants() as $enum) { + $parameter->value .= isset($enum->name) ? ' ' . $enum->name . ',' : ''; + } + $parameter->value = rtrim($parameter->value, ','); + } + } + } + // 获取忽略 + if (str_contains($docComment, '@ignore')) { + $parameter->ignore = true; + } + + if ($exampleMatch) { + $parameter->example = $exampleMatch[1]; + if (strpos($exampleMatch['1'], '[') === 0 || strpos($exampleMatch['1'], '{') === 0) { + $example = json_decode($exampleMatch[1], true); + if ($exampleMatch[1] && json_last_error()) { + throw new Exception('example JSON 错误 from ' . $prop->getName() . ' in ' . $prop->getDeclaringClass()->getName()); + } + $parameter->example = $example; + } + } + + return $parameter; + } catch (\Throwable $th) { + + echo '参数解析失败了' . PHP_EOL; + throw new Exception($th->getMessage(), $th->getCode(), $th); + } + } + + private function getType(string $type): string + { + + /** + * 不包含一下信息的 + * var array<*,string-class> + * var string-class[] + * var string-class + */ + if ( + ! preg_match('/array<\S+,(\S+)>/', $type, $arrayMatch) + && ! preg_match('/(\S+)\[\]/', $type, $arrayMatch) + && ! preg_match('/(\S+)/', $type, $arrayMatch) + ) { + return 'string'; + } + + // 取出对应值 + $stringClass = strtolower(trim($arrayMatch[1])); + + if (stripos($type, '[]') !== false) { + return 'array'; + } + + if (in_array($stringClass, ['int', 'float', 'bool', 'array', 'string', 'boolean', 'integer'])) { + + $stringClass = $stringClass == 'array' ? 'string' : $stringClass; + $stringClass = $stringClass == 'int' ? 'integer' : $stringClass; + $stringClass = $stringClass == 'bool' ? 'boolean' : $stringClass; + + return $stringClass; + } + + if (stripos($type, '<') === false && stripos($type, '[') === false) { + return 'object'; + } + + return 'array'; + } +} diff --git a/src/OpenApi/Storage/OpenAPI/ServersStorage.php b/src/OpenApi/Storage/OpenAPI/ServersStorage.php new file mode 100755 index 0000000..db2f062 --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/ServersStorage.php @@ -0,0 +1,19 @@ +variables = $this->variables ?: new stdClass(); + } +} diff --git a/src/OpenApi/Storage/OpenAPI/TagStorage.php b/src/OpenApi/Storage/OpenAPI/TagStorage.php new file mode 100755 index 0000000..3651691 --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/TagStorage.php @@ -0,0 +1,16 @@ +setValue($value); + } + + public function addChildren(TreeNode $child): void + { + $this->children[] = $child; + } + + public function setValue($value): void + { + $this->value = $value; + } + + public function getValue(): mixed + { + return $this->value; + } + + /** + * @return TreeNode[] + */ + public function getChildren(): array + { + return $this->children; + } +} diff --git a/src/Resolvers/PropertyTypeDocResolver.php b/src/Resolvers/PropertyTypeDocResolver.php index 2cdd054..bdf59cd 100644 --- a/src/Resolvers/PropertyTypeDocResolver.php +++ b/src/Resolvers/PropertyTypeDocResolver.php @@ -22,19 +22,19 @@ public function resolve(Type $type): array protected function resolveArrayType(Array_ $type): array { - $valueType = $type->getValueType(); + $valueTypes = $type->getValueType(); - if ($valueType instanceof Object_) { - $className = ltrim($valueType->getFqsen()?->__toString(), '\\'); - } elseif ($valueType instanceof AggregatedType) { + if ($valueTypes instanceof Object_) { + $className = ltrim($valueTypes->getFqsen()?->__toString(), '\\'); + } elseif ($valueTypes instanceof AggregatedType) { $className = []; - foreach ($valueType as $type) { - $className[] = ltrim($type instanceof Object_ - ? $type->getFqsen()?->__toString() - : (string)$valueType, '\\'); + foreach ($valueTypes as $valueType) { + $className[] = ltrim($valueType instanceof Object_ + ? $valueType->getFqsen()?->__toString() + : (string)$valueTypes, '\\'); } } else { - $className = ltrim((string)$valueType, '\\'); + $className = ltrim((string)$valueTypes, '\\'); } return [ diff --git a/src/Support/Collections/ConstructDataCollection.php b/src/Support/Collections/ConstructDataCollection.php index f428ba2..de1e4c8 100644 --- a/src/Support/Collections/ConstructDataCollection.php +++ b/src/Support/Collections/ConstructDataCollection.php @@ -4,8 +4,6 @@ class ConstructDataCollection { - // private array $tranFromResolvers = []; - public function __construct( public readonly string $name, public readonly bool $isPromoted, From 578e3be407b14f4f57f6d7ef45fcb80db52bd580 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 6 Jun 2025 16:12:39 +0800 Subject: [PATCH 02/59] ref v2 --- src/Enums/TypeKindEnum.php | 15 +- src/OpenApi/Annotations/RequestBody.php | 2 +- src/OpenApi/Annotations/Response.php | 6 +- src/OpenApi/Annotations/Route.php | 5 - src/OpenApi/Annotations/Tag.php | 4 - src/OpenApi/Collections/OpenApiCollection.php | 160 ++++++++- .../Collections/ParameterCollection.php | 16 +- src/OpenApi/Enum/MethodEnum.php | 14 +- src/OpenApi/Handler/Handler.php | 41 +-- src/OpenApi/Handler/SerializeHandler.php | 152 --------- src/OpenApi/Handler/api.php | 314 ++++++++++++++++++ src/OpenApi/OpenApi.php | 89 +++++ src/OpenApi/Storage/OpenAPI/Method/Delete.php | 9 + src/OpenApi/Storage/OpenAPI/Method/GET.php | 13 - src/OpenApi/Storage/OpenAPI/Method/Get.php | 9 + src/OpenApi/Storage/OpenAPI/Method/Method.php | 20 +- .../OpenAPI/Method/MethodInterface.php | 1 - src/OpenApi/Storage/OpenAPI/Method/POST.php | 13 - src/OpenApi/Storage/OpenAPI/Method/Post.php | 9 + src/OpenApi/Storage/OpenAPI/Method/Put.php | 9 + src/OpenApi/Storage/OpenAPI/OpenAPI.php | 20 +- .../Storage/OpenAPI/RequestBodyStorage.php | 19 +- .../Storage/OpenAPI/ResponseStorage.php | 28 +- src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 185 ++--------- tests/Openapi/ArrayOpenApi.php | 66 ++++ 25 files changed, 782 insertions(+), 437 deletions(-) delete mode 100755 src/OpenApi/Handler/SerializeHandler.php create mode 100644 src/OpenApi/Handler/api.php create mode 100755 src/OpenApi/OpenApi.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/Delete.php delete mode 100755 src/OpenApi/Storage/OpenAPI/Method/GET.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/Get.php delete mode 100755 src/OpenApi/Storage/OpenAPI/Method/POST.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/Post.php create mode 100755 src/OpenApi/Storage/OpenAPI/Method/Put.php create mode 100644 tests/Openapi/ArrayOpenApi.php diff --git a/src/Enums/TypeKindEnum.php b/src/Enums/TypeKindEnum.php index b56752f..ef13933 100644 --- a/src/Enums/TypeKindEnum.php +++ b/src/Enums/TypeKindEnum.php @@ -2,7 +2,8 @@ namespace Astral\Serialize\Enums; -use http\Exception\RuntimeException; + +use RuntimeException; enum TypeKindEnum { @@ -68,4 +69,16 @@ public static function getNameTo(string $type, ?string $className = null): self default => throw new RuntimeException("not found type $type"), }; } + + public function getOpenApiName(): string + { + return match ($this) { + self::INT => 'integer', + self::FLOAT => 'number', + self::BOOLEAN => 'boolean', + self::OBJECT, self::CLASS_OBJECT => 'object', + self::ARRAY, self::COLLECT_SINGLE_OBJECT , self::COLLECT_UNION_OBJECT => 'array', + default => 'string', + }; + } } diff --git a/src/OpenApi/Annotations/RequestBody.php b/src/OpenApi/Annotations/RequestBody.php index 5b5dd4d..71e5a96 100755 --- a/src/OpenApi/Annotations/RequestBody.php +++ b/src/OpenApi/Annotations/RequestBody.php @@ -14,7 +14,7 @@ public function __construct( /** @var class-string $className */ public string $className = '', public ContentTypeEnum $contentType = ContentTypeEnum::JSON, - public ?string $group = null + public array|null $group = null ){ } } diff --git a/src/OpenApi/Annotations/Response.php b/src/OpenApi/Annotations/Response.php index 03830b2..6f9376a 100644 --- a/src/OpenApi/Annotations/Response.php +++ b/src/OpenApi/Annotations/Response.php @@ -11,9 +11,9 @@ class Response { public function __construct( /** @var class-string $className */ - public string $className, - public ?string $group = null, - public ?int $code = 200 + public string $className, + public ?int $code = 200, + public ?array $groups = null, ){ } } diff --git a/src/OpenApi/Annotations/Route.php b/src/OpenApi/Annotations/Route.php index 80cc709..3d5ec9c 100755 --- a/src/OpenApi/Annotations/Route.php +++ b/src/OpenApi/Annotations/Route.php @@ -16,9 +16,4 @@ public function __construct( public array $attributes = [], ) { } - - public function getMethod(): string - { - return 'April\\ApiDoc\\Storage\\OpenAPI\\Method\\' . $this->method->name; - } } diff --git a/src/OpenApi/Annotations/Tag.php b/src/OpenApi/Annotations/Tag.php index 7ff4165..79769b1 100755 --- a/src/OpenApi/Annotations/Tag.php +++ b/src/OpenApi/Annotations/Tag.php @@ -19,8 +19,4 @@ public function __construct( ) { } - public function buildTagStorage(): TagStorage - { - return new TagStorage($this->value, $this->description); - } } diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index 82a119d..c09b698 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -2,27 +2,171 @@ namespace Astral\Serialize\OpenApi\Collections; +use Astral\Serialize\Enums\TypeKindEnum; use Astral\Serialize\OpenApi\Annotations\Headers; +use Astral\Serialize\OpenApi\Annotations\RequestBody; +use Astral\Serialize\OpenApi\Annotations\Response; use Astral\Serialize\OpenApi\Annotations\Route; use Astral\Serialize\OpenApi\Annotations\Summary; +use Astral\Serialize\OpenApi\Annotations\Tag; +use Astral\Serialize\OpenApi\Enum\ContentTypeEnum; +use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Method; +use Astral\Serialize\OpenApi\Storage\OpenAPI\RequestBodyStorage; +use Astral\Serialize\OpenApi\Storage\OpenAPI\ResponseStorage; +use Astral\Serialize\OpenApi\Storage\OpenAPI\SchemaStorage; +use Astral\Serialize\Serialize; +use Astral\Serialize\Support\Factories\ContextFactory; +use Psr\SimpleCache\InvalidArgumentException; +use ReflectionMethod; class OpenApiCollection { public function __construct( public string $controllerClass, public string $methodName, + public reflectionMethod $reflectionMethod, + public Tag $tag, public Summary $summary, public Route $route, - public Headers $headers, + public Headers|null $headers, public array $attributes, - /** @var array $parameters, */ - public array $parameters = [], - /** @var array $requestBody, */ - public array $requestBody = [], - /** @var array $responses, */ - public array $response = [], - + public RequestBody|null $requestBody, + public Response|null $response, ){ } + /** + * @throws InvalidArgumentException + */ + public function build() : Method + { + $methodClass = $this->route->method->value; + /** @var Method $openAPIMethod */ + $openAPIMethod = new $methodClass( + tags:[$this->tag->value ?: ''], + summary:$this->summary->value, + description:$this->summary->description ?: '' + ); + + $openAPIMethod->withRequestBody($this->requestBody !== null ? $this->buildRequestBodyByAttribute() : $this->buildRequestBodyByParameters()); + $openAPIMethod->addResponse(200, $this->buildResponse()); + + return $openAPIMethod; + } + + /** + * @throws InvalidArgumentException + */ + public function buildRequestBodyByAttribute(): RequestBodyStorage + { + $openAPIRequestBody = new RequestBodyStorage($this->requestBody->contentType); + $schemaStorage = (new SchemaStorage())->build($this->buildRequestBodyParameterCollections($this->requestBody->className,$this->requestBody->group),$n); + $openAPIRequestBody->withParameter($schemaStorage); + return $openAPIRequestBody; + } + + /** + * @throws InvalidArgumentException + */ + public function buildRequestBodyByParameters(): RequestBodyStorage + { + $openAPIRequestBody = new RequestBodyStorage(ContentTypeEnum::JSON); + $methodParam = $this->reflectionMethod->getParameters()[0] ?? null; + $requestBodyClass = $methodParam ? $methodParam->getType()?->getName() : ''; + if (is_subclass_of($requestBodyClass, Serialize::class)) { + $schemaStorage = (new SchemaStorage())->build($this->buildRequestBodyParameterCollections($requestBodyClass),$node); + $openAPIRequestBody->withParameter($schemaStorage); + } + + return $openAPIRequestBody; + } + + /** + * @throws InvalidArgumentException + */ + public function buildResponse(): ResponseStorage + { + $responseStorage = new ResponseStorage(); + $schemaStorage = (new SchemaStorage())->build($this->buildResponseParameterCollections()); + $responseStorage->withParameter($schemaStorage); + return $responseStorage; + } + + /** + * @param string $className + * @param array $groups + * @return array + * @throws InvalidArgumentException + */ + public function buildRequestBodyParameterCollections(string $className, array $groups = ['default']): array + { + $serializeContext = ContextFactory::build($className); + $serializeContext->from(); + $properties = $serializeContext->getGroupCollection()->getProperties(); + + $vols = []; + foreach ($properties as $property){ + $vol = new ParameterCollection( + name: current($property->getInputNamesByGroups($groups,$className)), + descriptions: '', + type: current($property->getTypes())->kind ?: TypeKindEnum::STRING, + required: !$property->isNullable(), + ignore: false, + ); + + if($property->getChildren()){ + foreach ($property->getChildren() as $children){ + $vol->children[] = $this->buildRequestBodyParameterCollections($children->getClassName()); + } + } + + $vols[] = $vol; + } + + return $vols; + } + + /** + * @return array + * @throws InvalidArgumentException + */ + public function buildResponseParameterCollections(): array + { + $returnClass = $this->reflectionMethod->getReturnType(); + $responseClass = match(true){ + $this->response !== null => $this->response->className, + $returnClass && is_subclass_of($returnClass,Serialize::class) => $returnClass, + default => null, + }; + + if(!$responseClass){ + return []; + } + + $groups = $this->response ? $this->response->groups : ['default']; + $serializeContext = ContextFactory::build($responseClass); + $serializeContext->from(); + $properties = $serializeContext->getGroupCollection()->getProperties(); + + $vols = []; + foreach ($properties as $property){ + $vol = new ParameterCollection( + name:current($property->getOutNamesByGroups($groups,$responseClass)), + descriptions: '', + type: current($property->getTypes())->kind ?: TypeKindEnum::STRING, + required: !$property->isNullable(), + ignore: false, + ); + + if($property->getChildren()){ + foreach ($property->getChildren() as $children){ + $vol->children[] = $this->buildRequestBodyParameterCollections($children->getClassName()); + } + } + + $vols[] = $vol; + } + + return $vols; + } } \ No newline at end of file diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php index 3625566..f161b6d 100755 --- a/src/OpenApi/Collections/ParameterCollection.php +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -4,25 +4,25 @@ namespace Astral\Serialize\OpenApi\Collections; +use Astral\Serialize\Enums\TypeKindEnum; use Attribute; class ParameterCollection { public function __construct( /** @var string 元素变量名 */ - public string $name, + public string $name, /** @var string descriptions */ - public string $descriptions = '', - /** @var string 元素类型 */ - public string $type = 'string', + public string $descriptions = '', + public TypeKindEnum $type = TypeKindEnum::STRING, /** @var mixed 示例值 */ - public mixed $example = '', + public mixed $example = '', /** @var bool 是否必填 */ - public bool $required = false, + public bool $required = false, /** @var bool 是否忽略显示 */ - public bool $ignore = false, + public bool $ignore = false, /** @var array $children */ - public array $children = [], + public array $children = [], ){ } } diff --git a/src/OpenApi/Enum/MethodEnum.php b/src/OpenApi/Enum/MethodEnum.php index 8dd5471..e5a2156 100755 --- a/src/OpenApi/Enum/MethodEnum.php +++ b/src/OpenApi/Enum/MethodEnum.php @@ -2,8 +2,16 @@ namespace Astral\Serialize\OpenApi\Enum; -enum MethodEnum +use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Delete; +use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Get; +use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Post; +use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Put; + +enum MethodEnum:string { - case POST; - case GET; + case POST = Post::class; + case GET = Get::class; + case PUT = Put::class; + case DELETE = Delete::class; + } diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index 2b6f380..f547914 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -5,6 +5,7 @@ namespace Astral\Serialize\OpenApi\Handler; use Astral\Serialize\OpenApi\Collections\OpenApiCollection; +use Astral\Serialize\OpenApi\Storage\OpenAPI\ApiInfo; use Astral\Serialize\OpenApi\Storage\OpenAPI\OpenAPI; use Astral\Serialize\OpenApi\Storage\OpenAPI\ParameterStorage; use Astral\Serialize\OpenApi\Storage\OpenAPI\SchemaStorage; @@ -22,7 +23,7 @@ abstract class Handler implements HandleInterface public function __construct( protected readonly ParameterStorage $headerParameterStorages = new ParameterStorage() ) { - self::$OpenAPI ?: self::$OpenAPI = new OpenAPI(); + self::$OpenAPI ??= (new OpenAPI())->withApiInfo(new ApiInfo('API 接口','')); } /** @@ -32,7 +33,7 @@ public function __construct( * @throws ReflectionException * @throws Exception */ - abstract public function createOpenAPIByClass(string $className): void; + abstract public function buildByClass(string $className): void; /** * 向全局头部参数存储中添加一个新的头部参数。 @@ -78,24 +79,28 @@ public function withControllerSuffix(string $value): self return $this; } + /** + * @throws ReflectionException + */ public function handleByAutoLoad(): void { - $classMap = require base_path('vendor/composer/autoload_classmap.php'); - $appClassMap = array_keys(array_filter($classMap, function ($key) { + $classMap = require dir('vendor/composer/autoload_classmap.php'); + $appClassMap = array_keys(array_filter($classMap, static function ($key) { return (str_starts_with($key, 'App\\') && str_ends_with($key, 'Controller')) || (str_starts_with($key, 'April\\') && str_ends_with($key, 'Controller')); }, ARRAY_FILTER_USE_KEY)); foreach ($appClassMap as $className) { - $this->createOpenAPIByClass($className); + $this->buildByClass($className); } } /** * 解析Controller文件 * - * @param array $folders 文件路径 => 命名空间 + * @param array $folders 文件路径 => 命名空间 * @return $this + * @throws ReflectionException */ public function handleByFolders(array $folders): self { @@ -109,7 +114,7 @@ public function handleByFolders(array $folders): self foreach (scandir($folder) as $file) { $path = $folder . '/' . $file; - if ($file == '.' || $file == '..' || strpos($file, '.') === 0) { + if ($file === '.' || $file === '..' || str_starts_with($file, '.')) { continue; } @@ -134,34 +139,14 @@ public function handleByFolders(array $folders): self } } - $this->createOpenAPIByClass($className); + $this->buildByClass($className); } } return $this; } - /** - * 根据类信息构建Schema - * @throws Exception - */ - public function buildSchemaByClass(string $className, ?string $group = null): SchemaStorage - { - $schema = new SchemaStorage(); - - if (! $className) { - return $schema; - } - - $ParserPartaker = new ParserPartaker(); - $ParserPartaker->addNode($className, $group, null); - $tree = $ParserPartaker->getTree(); - - $schema->createTree($tree->getChildren()); - - return $schema; - } public function output(string $path): bool { diff --git a/src/OpenApi/Handler/SerializeHandler.php b/src/OpenApi/Handler/SerializeHandler.php deleted file mode 100755 index 396fbba..0000000 --- a/src/OpenApi/Handler/SerializeHandler.php +++ /dev/null @@ -1,152 +0,0 @@ -getAttributes(Tag::class); - /** @var Tag $tagDoc */ - $tagDoc = isset($tagDoc[0]) ? $tagDoc[0]->newInstance() : null; - if ($tagDoc) { - self::$OpenAPI->addTag($tagDoc->buildTagStorage()); - } - - foreach ($classRefection->getMethods() as $item) { - - $methodAttributes = $item->getAttributes(); - - if (! $methodAttributes) { - continue; - } - - $routeDoc = $summaryDoc = $requestBodyDoc = $responseDoc = $headersDoc = null; - $instances = [ - Route::class => &$routeDoc, - Summary::class => &$summaryDoc, - RequestBody::class => &$requestBodyDoc, - Response::class => &$responseDoc, - Headers::class => &$headersDoc, - ]; - foreach ($methodAttributes as $methodAttribute) { - $name = $methodAttribute->getName(); - if (isset($instances[$name])) { - $instances[$name] = $methodAttribute->newInstance(); - } - } - - if (! $routeDoc || ! $summaryDoc) { - continue; - } - - $returnClass = $classRefection->getMethod($item->name)->getReturnType(); - $responseClass = match(true){ - $responseDoc => $responseDoc->className, - $returnClass && $returnClass instanceof Serialize::class =>$returnClass, - default => null, - }; - - new OpenApiCollection( - controllerClass: $className, - methodName: $item->getName(), - summary:$summaryDoc, - route: $routeDoc, - headers: $headersDoc, - attributes: $methodAttributes, - requestBody: $this->buildRequestBodyParameterCollections($requestBodyDoc->className), - response: $requestBodyDoc ? $this->buildRequestBodyParameterCollections($responseClass) : [], - ); - } - } - - /** - * @param string $className - * @return array - */ - public function buildRequestBodyParameterCollections(string $className): array - { - $serializeContext = ContextFactory::build($className); - $serializeContext->from(); - $properties = $serializeContext->getChooseSerializeContext()->getProperties(); - - $vols = []; - foreach ($properties as $property){ - $vol = new ParameterCollection( - name:$property->getInputName(), - descriptions: '', - type: $property->getType()?->kind->name ?: 'STRING', - required: !$property->getDataCollection()->isNullable(), - ); - - if($property->getChildren()){ - $vol->children = $this->buildRequestBodyParameterCollections($property->getChildren()->get) - } - } - - return $vols; - } - - /** - * @param string $className - * @return array - */ - public function buildResponseParameterCollections(string $className): array - { - $serializeContext = ContextFactory::build($className); - $serializeContext->from(); - $properties = $serializeContext->getChooseSerializeContext()->getProperties(); - - $vols = []; - foreach ($properties as $property){ - - if(!$property->getInputName()){ - continue; - } - - $vols[] = new ParameterCollection( - name:$property->getInputName(), - descriptions: '', - type: $property->getType()?->kind->name ?: 'STRING', - required: !$property->getDataCollection()->isNullable(), - ); - } - - return $vols; - } -} diff --git a/src/OpenApi/Handler/api.php b/src/OpenApi/Handler/api.php new file mode 100644 index 0000000..db0c773 --- /dev/null +++ b/src/OpenApi/Handler/api.php @@ -0,0 +1,314 @@ +headerParameterStorages) { + $this->headerParameterStorages = new ParameterStorage(); + } + + // 添加头部参数的属性 + $this->headerParameterStorages->addHeaderProperties($name, $description, $example); + + return $this; + } + + /** + * Undocumented function + */ + public function getOpenAPI(): OpenAPI + { + return self::$OpenAPI; + } + + /** + * 增加类前缀标识 + * + * @return $this + */ + public function withControllerPrefix(string $value): self + { + $this->controllerPrefix = $value; + + return $this; + } + + /** + * 增加类后缀标识 + * + * @return $this + */ + public function withControllerSuffix(string $value): self + { + $this->controllerSuffix = $value; + + return $this; + } + + /** + * 是否开启注解异常信息 + * + * @return $this + */ + public function enableException(bool $bool = true): self + { + $this->_isIgnoreException = $bool; + + return $this; + } + + /** + * 解析Controller文件 + * + * @param array $folders 文件路径 => 命名空间 + * @return $this + */ + public function handleByFolders(array $folders): self + { + + foreach ($folders as $folder => $namespace) { + + if (! is_dir($folder)) { + continue; + } + + foreach (scandir($folder) as $file) { + + $path = $folder.'/'.$file; + if ($file == '.' || $file == '..' || strpos($file, '.') === 0) { + continue; + } + + if (is_dir($path)) { + $this->handleByFolders([$path => $namespace.'\\'.$file]); + + continue; + } + + if (pathinfo($file, PATHINFO_EXTENSION) != 'php') { + continue; + } + + $fileName = $this->controllerPrefix.trim(substr($file, 0, strpos($file, '.'))).$this->controllerSuffix; + $className = $namespace ? $namespace.'\\'.$fileName : $fileName; + + if (! class_exists($className)) { + include_once $folder.'/'.$file; + ob_clean(); // 清除一些引入进来的莫名其妙输出文件 + if (! class_exists($className)) { + continue; + } + } + + $this->createOpenAPIByClass($className); + } + } + + return $this; + } + + /** + * 构建OpenApi结构文档 + * + * @param class-string $className + */ + public function createOpenAPIByClass($className): void + { + // try { + + $classRefection = new ReflectionClass($className); + + $tagDoc = $classRefection->getAttributes(Tag::class); + /** @var Tag */ + $tagDoc = isset($tagDoc[0]) ? $tagDoc[0]->newInstance() : null; + if ($tagDoc) { + self::$OpenAPI->addTag($tagDoc->buildTagStorage()); + } + + foreach ($classRefection->getMethods() as $item) { + + /** @var ReflectionMethod */ + $reflectionMethod = $classRefection->getMethod($item->name); + $methodAttributes = $reflectionMethod->getAttributes(); + + if (! $methodAttributes) { + continue; + } + + $routeDoc = null; + $summaryDoc = null; + $requestBodyDoc = null; + $responseDoc = null; + foreach ($methodAttributes as $methodAttribute) { + switch ($methodAttribute) { + case $methodAttribute->getName() == Route::class: + /** @var Route */ + $routeDoc = $methodAttribute->newInstance(); + break; + case $methodAttribute->getName() == Summary::class: + /** @var Summary */ + $summaryDoc = $methodAttribute->newInstance(); + break; + case $methodAttribute->getName() == RequestBody::class: + /** @var RequestBody */ + $requestBodyDoc = $methodAttribute->newInstance(); + break; + case $methodAttribute->getName() == Response::class: + /** @var Response */ + $responseDoc = $methodAttribute->newInstance(); + break; + } + } + + if (! $routeDoc || ! $summaryDoc) { + continue; + } + + $methodClass = $routeDoc->getMethod(); + /** @var MethodInterface|Method| */ + $openAPIMethod = new $methodClass($summaryDoc->value, $summaryDoc->description ?: '', [$tagDoc->value ?: '']); + + // 统一header头 + if ($this->headerParameterStorages) { + $openAPIMethod->withParameters($this->headerParameterStorages->getData()); + } + + if ($requestBodyDoc) { + $openAPIRequestBody = new OpenAPIRequestBody($requestBodyDoc->contentType); + $requestBodySchema = $this->buildSchemaByClass($requestBodyDoc->className); + $openAPIRequestBody->withParameter($requestBodySchema); + $openAPIMethod->withRequestBody($openAPIRequestBody); + } else { + $methodParam = $reflectionMethod->getParameters(); + if (isset($methodParam[0]) && ($requestBodyClass = $methodParam[0]->gettype()->getName()) !== Request::class) { + $openAPIRequestBody = new OpenAPIRequestBody(ContentTypeEnum::JSON); + $requestBodySchema = $this->buildSchemaByClass($requestBodyClass); + $openAPIRequestBody->withParameter($requestBodySchema); + $openAPIMethod->withRequestBody($openAPIRequestBody); + } + } + + if ($responseDoc) { + if (! class_exists($responseDoc->className)) { + throw new Exception( + sprintf('Class "%s" does not exist in "%s" from action "%s"', + $responseDoc->className, + $reflectionMethod->getFileName(), + $reflectionMethod->getName()) + ); + } + + $openApiResponse = new OpenAPIResponse(); + $openApiResponse->description = '成功'; + $openApiResponse->withParameter($this->buildSchemaByClass($responseDoc->className)); + $openAPIMethod->addResponse($responseDoc->code, $openApiResponse); + + } else { + /** @var ReflectionType */ + $returnClass = $classRefection->getMethod($item->name)->getReturnType(); + if ($returnClass && class_exists($returnClass->getName())) { + $openApiResponse = new OpenAPIResponse(); + $openApiResponse->description = '成功'; + $openApiResponse->withParameter($this->buildSchemaByClass($returnClass->getName())); + $openAPIMethod->addResponse(200, $openApiResponse); + } + } + + // /** @var Params[] */ + // $paramsDoc = $reflectionMethod->getAttributes(Params::class); + // if ($paramsDoc) { + // $Parameter = new Parameter(); + // foreach ($paramsDoc as $v) { + // $Parameter->addProperties($v->name, $v->type, $v->value, $v->example, $v->required); + // } + + // $openAPIMethod->withParameters($Parameter->getData()); + // } + + // /** @var RequestValue[] */ + // $requestValuesDoc = $reflectionMethod->getAttributes(RequestValue::class); + // if ($requestValuesDoc && $openAPIRequestBody) { + // foreach ($requestValuesDoc as $v) { + // $requestBodySchema->addProperties($v->name, $v->type, $v->value, $v->example, $v->required); + // } + + // $openAPIRequestBody->withParameter($requestBodySchema); + // $openAPIMethod->withRequestBody($openAPIRequestBody); + // } + + self::$OpenAPI->addPath($routeDoc->route, $openAPIMethod); + } + // } catch (Throwable $th) { + // if ($this->_isIgnoreException) { + // echo '解析参数异常:'.PHP_EOL; + // exit(highlight_string(var_export($th, true))); + // } + + // } + + } + + /** + * 根据类信息构建Schema + */ + public function buildSchemaByClass(string $className): SchemaStorage + { + $schema = new SchemaStorage(); + + if (! $className) { + return $schema; + } + + $ParserPartaker = new ParserPartaker(); + $ParserPartaker->addNode($className, null); + + $tree = $ParserPartaker->getTree(); + + $schema->createTree($tree->getChildren()); + + return $schema; + } + + public function output(string $path): bool + { + return true; + } + + public function toString(): string + { + return json_encode(self::$OpenAPI, JSON_UNESCAPED_UNICODE); + } +} diff --git a/src/OpenApi/OpenApi.php b/src/OpenApi/OpenApi.php new file mode 100755 index 0000000..bd15630 --- /dev/null +++ b/src/OpenApi/OpenApi.php @@ -0,0 +1,89 @@ +getAttributes(Tag::class); + /** @var Tag $tagDoc */ + $tagDoc = isset($tagDoc[0]) ? $tagDoc[0]->newInstance() : null; + + if ($tagDoc) { + self::$OpenAPI->addTag(new TagStorage($tagDoc->value, $tagDoc->description)); + } + + foreach ($classRefection->getMethods() as $item) { + + $methodAttributes = $item->getAttributes(); + + if (! $methodAttributes) { + continue; + } + + $routeDoc = $summaryDoc = $requestBodyDoc = $responseDoc = $headersDoc = null; + $instances = [ + Route::class => &$routeDoc, + Summary::class => &$summaryDoc, + RequestBody::class => &$requestBodyDoc, + Response::class => &$responseDoc, + Headers::class => &$headersDoc, + ]; + + foreach ($methodAttributes as $methodAttribute) { + $name = $methodAttribute->getName(); + if (array_key_exists($name,$instances)) { + $instances[$name] = $methodAttribute->newInstance(); + } + } + + if (! $routeDoc || ! $summaryDoc) { + continue; + } + + $openApiCollection = new OpenApiCollection( + controllerClass: $className, + methodName: $item->getName(), + reflectionMethod: $item, + tag: $tagDoc, + summary: $summaryDoc, + route: $routeDoc, + headers: $headersDoc, + attributes: $methodAttributes, + requestBody: $requestBodyDoc, + response: $responseDoc, + ); + + + self::$OpenAPI->addPath($openApiCollection); + } + } + + + + +} diff --git a/src/OpenApi/Storage/OpenAPI/Method/Delete.php b/src/OpenApi/Storage/OpenAPI/Method/Delete.php new file mode 100755 index 0000000..c3995fa --- /dev/null +++ b/src/OpenApi/Storage/OpenAPI/Method/Delete.php @@ -0,0 +1,9 @@ + $parameters */ - public array|stdClass $parameters = new stdClass(), - public RequestBodyStorage|stdClass $requestBody = new stdClass(), - /** @var array 返回信息 */ + /** @var array $parameters */ + public array $parameters = [], + public array|stdClass $requestBody = new stdClass(), + /** @var array $responses */ public array|stdClass $responses = new stdClass(), ) { } @@ -50,8 +47,7 @@ public function addParameters(array $parameters): void */ public function withRequestBody(RequestBodyStorage $body): static { - $this->requestBody = $body; - + $this->requestBody = $body->getData(); return $this; } @@ -63,8 +59,8 @@ public function withRequestBody(RequestBodyStorage $body): static public function addResponse(int $code, ResponseStorage $response): static { - $this->responses = new stdClass(); // 去除初始化 - $this->responses[$code] = $response; + $this->responses = []; + $this->responses[$code] = $response->getData(); return $this; } diff --git a/src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php b/src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php index d04a7b5..1d4d7c6 100755 --- a/src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php +++ b/src/OpenApi/Storage/OpenAPI/Method/MethodInterface.php @@ -6,5 +6,4 @@ interface MethodInterface { - public function getName(): string; } diff --git a/src/OpenApi/Storage/OpenAPI/Method/POST.php b/src/OpenApi/Storage/OpenAPI/Method/POST.php deleted file mode 100755 index 7a3cc44..0000000 --- a/src/OpenApi/Storage/OpenAPI/Method/POST.php +++ /dev/null @@ -1,13 +0,0 @@ - 请求api的标识符 */ + /** @var array */ public array $tags; - /** @var array 具体api配置 */ + /** @var array */ public array $paths = []; public function withApiInfo(ApiInfo $apiInfo): self @@ -59,9 +57,11 @@ public function withPaths(array $paths): self return $this; } - public function addPath(string $url, MethodInterface $method): self + public function addPath(OpenApiCollection $openApiCollection): self { - $this->paths[$url][$method->getName()] = $method; + $this->paths[$openApiCollection->route->route][strtolower($openApiCollection->route->method->name)] = $openApiCollection->build(); return $this; } + + } diff --git a/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php index 689a7a0..5b74f85 100755 --- a/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php +++ b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php @@ -12,16 +12,27 @@ */ class RequestBodyStorage implements StorageInterface { + public array $parameters; + public function __construct( public ContentTypeEnum $contentType = ContentTypeEnum::JSON, - /** @var array 参数类型 */ - public array $content = [] ) { - $this->content[$this->contentType->value]['schema'] = []; } public function withParameter(SchemaStorage $schema): void { - $this->content[$this->contentType->value]['schema'] = $schema->getData(); + $this->parameters = $schema->getData(); + } + + public function getData(): array + { + return [ + 'required' => true, + 'content' => [ + $this->contentType->value => [ + 'schema' => $this->parameters + ] + ] + ]; } } diff --git a/src/OpenApi/Storage/OpenAPI/ResponseStorage.php b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php index 565d23e..d74c959 100755 --- a/src/OpenApi/Storage/OpenAPI/ResponseStorage.php +++ b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php @@ -6,24 +6,32 @@ use Astral\Serialize\OpenApi\Storage\StorageInterface; -/** - * 参数配置 - */ class ResponseStorage implements StorageInterface { - /** @var RequestBodyStorage 参数类型 */ - public RequestBodyStorage $content; + public array $parameter; public function __construct( public string $contentType = 'application/json', - public string $description = '', - public ?string $group = null + public string $description = '成功', + public string|null $groups = null, ) { - $this->content[$this->contentType]['schema'] = []; } - public function withParameter(SchemaStorage $schema): void + public function withParameter(SchemaStorage $schema): static { - $this->content[$this->contentType]['schema'] = $schema->getData(); + $this->parameter = $schema->getData(); + return $this; + } + + public function getData(): array + { + return [ + 'description' => $this->description, + 'content' => [ + $this->contentType => [ + 'schema' => $this->parameter + ] + ] + ]; } } diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index 0dd80b5..135dc5f 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -4,13 +4,9 @@ namespace Astral\Serialize\OpenApi\Storage\OpenAPI; -use Astral\Serialize\OpenApi\Annotations\Parameter; -use Astral\Serialize\OpenApi\Handler\ParserPartaker; +use Astral\Serialize\OpenApi\Collections\ParameterCollection; use Astral\Serialize\OpenApi\Storage\StorageInterface; -use Astral\Serialize\OpenApi\Storage\TreeNode; use Exception; -use ReflectionClass; -use ReflectionProperty; /** * 文档开发者介绍 @@ -20,7 +16,6 @@ class SchemaStorage implements StorageInterface private array|SchemaStorage $data = [ 'type' => 'object', 'properties' => [], - // 'required' => [], ]; public function getData(): SchemaStorage|array @@ -31,12 +26,11 @@ public function getData(): SchemaStorage|array /** * Undocumented function * - * @param array $tree + * @param array $tree * @param mixed|null $node - * @return void - * @throws Exception + * @return SchemaStorage */ - public function createTree(array $tree, mixed &$node = null): void + public function build(array $tree, mixed &$node = null): static { if ($node === null) { $node = &$this->data; @@ -44,44 +38,46 @@ public function createTree(array $tree, mixed &$node = null): void foreach ($tree as $item) { - $parameter = $this->parserParameter($item->getValue()); - if ($parameter->ignore) { + if ($item->ignore) { continue; } - $node['properties'][$parameter->name] = [ - 'type' => $parameter->type, - 'description' => $parameter->value, - 'example' => $parameter->example, + $node['properties'][$item->name] = [ + 'type' => strtolower($item->type->getOpenApiName()), + 'description' => $item->descriptions, + 'example' => $item->example, ]; - if ($parameter->required) { - $node['required'][] = $parameter->name; + if ($item->required) { + $node['required'][] = $item->name; } - if ($item->getChildren()) { + if ($item->children) { // list对象 - if ($parameter->type === 'array') { - $node['properties'][$parameter->name]['items'] = [ + if ($item->type->isCollect()) { + $node['properties'][$item->name]['items'] = [ 'type' => 'object', 'properties' => [], - // 'required' => [], ]; - $tree = &$node['properties'][$parameter->name]['items']; + $tree = &$node['properties'][$item->name]['items']; } // 单个对象 - elseif ($parameter->type === 'object') { - $node['properties'][$parameter->name] = [ + elseif ($item->type->existsCollectClass()) { + $node['properties'][$item->name] = [ 'type' => 'object', 'properties' => [], - // 'required' => [], ]; - $tree = &$node['properties'][$parameter->name]; + $tree = &$node['properties'][$item->name]; + } + + foreach ($item->children as $v){ + $this->build($v, $tree); } - $this->createTree($item->getChildren(), $tree); } } + + return $this; } public function addProperties(string $name, string $type, string $description, string $example, bool $required = false): void @@ -97,137 +93,4 @@ public function addProperties(string $name, string $type, string $description, s } } - private function parserParameter(ReflectionProperty $prop): Parameter - { - - try { - - $parameterAttributes = $prop->getAttributes(Parameter::class); - - /** @var Parameter $parameter */ - $parameter = isset($parameterAttributes[0]) ? $parameterAttributes[0]->newInstance() : new Parameter(); - - // 转换输入名称 - if (class_exists(GetAlias::class) && $prop->getAttributes(GetAlias::class)) { - $getAsName = $prop->getAttributes(GetAlias::class)[0]->newInstance(); - $parameter->name = $getAsName->name; - } elseif (class_exists(SetAlisa::class) && $prop->getAttributes(SetAlisa::class)) { - $setAsName = $prop->getAttributes(SetAlisa::class)[0]->newInstance(); - $parameter->name = $setAsName->name; - } else { - $parameter->name = $prop->name; - } - - $docComment = $prop->getDocComment(); - - if (! $docComment) { - return $parameter; - } - - // 获取 类型/说明 @var {type} {name} or @var {type} - $varMatch = getVarByDocComment($docComment); - - // 获取 例值 @example {value} - preg_match('/@example\s*(\S+)/', $docComment, $exampleMatch); - - // 获取 是否必填 @requires true / @required true - preg_match('/@(?:requires|required)\s*(\S+)/i', $docComment, $requiredMatch); - - if ($requiredMatch) { - $parameter->required = $requiredMatch[1] == 'true' ? true : false; - } elseif (class_exists(NotBlank::class) && ($prop->getAttributes(NotBlank::class) ?? [])) { - $parameter->required = true; - } - - if ($varMatch) { - $parameter->type = $this->getType($varMatch[1]); - $parameter->value = $varMatch[2] ?? ''; - - $className = str_replace(['[', ']'], '', $varMatch[1]); - if (($parameter->type == 'object' || $parameter->type == 'array') - && ! in_array($className, ['int', 'float', 'bool', 'array', 'string', 'boolean', 'integer']) - ) { - try { - $fullClassName = (new ParserPartaker())->getFullClassName($prop, $className); - } catch (\Throwable $th) { - throw new Exception( - sprintf('%s not find from %s', $className, $prop->getDeclaringClass()->getName()), - $th->getCode(), - $th - ); - } - $reflectionClass = new ReflectionClass($fullClassName); - if ($reflectionClass->isEnum()) { - // object转string - $parameter->type = $parameter->type == 'object' ? 'string' : $parameter->type; - foreach ($reflectionClass->getConstants() as $enum) { - $parameter->value .= isset($enum->name) ? ' ' . $enum->name . ',' : ''; - } - $parameter->value = rtrim($parameter->value, ','); - } - } - } - // 获取忽略 - if (str_contains($docComment, '@ignore')) { - $parameter->ignore = true; - } - - if ($exampleMatch) { - $parameter->example = $exampleMatch[1]; - if (strpos($exampleMatch['1'], '[') === 0 || strpos($exampleMatch['1'], '{') === 0) { - $example = json_decode($exampleMatch[1], true); - if ($exampleMatch[1] && json_last_error()) { - throw new Exception('example JSON 错误 from ' . $prop->getName() . ' in ' . $prop->getDeclaringClass()->getName()); - } - $parameter->example = $example; - } - } - - return $parameter; - } catch (\Throwable $th) { - - echo '参数解析失败了' . PHP_EOL; - throw new Exception($th->getMessage(), $th->getCode(), $th); - } - } - - private function getType(string $type): string - { - - /** - * 不包含一下信息的 - * var array<*,string-class> - * var string-class[] - * var string-class - */ - if ( - ! preg_match('/array<\S+,(\S+)>/', $type, $arrayMatch) - && ! preg_match('/(\S+)\[\]/', $type, $arrayMatch) - && ! preg_match('/(\S+)/', $type, $arrayMatch) - ) { - return 'string'; - } - - // 取出对应值 - $stringClass = strtolower(trim($arrayMatch[1])); - - if (stripos($type, '[]') !== false) { - return 'array'; - } - - if (in_array($stringClass, ['int', 'float', 'bool', 'array', 'string', 'boolean', 'integer'])) { - - $stringClass = $stringClass == 'array' ? 'string' : $stringClass; - $stringClass = $stringClass == 'int' ? 'integer' : $stringClass; - $stringClass = $stringClass == 'bool' ? 'boolean' : $stringClass; - - return $stringClass; - } - - if (stripos($type, '<') === false && stripos($type, '[') === false) { - return 'object'; - } - - return 'array'; - } } diff --git a/tests/Openapi/ArrayOpenApi.php b/tests/Openapi/ArrayOpenApi.php new file mode 100644 index 0000000..b903429 --- /dev/null +++ b/tests/Openapi/ArrayOpenApi.php @@ -0,0 +1,66 @@ +buildByClass(TestOpenApiController::class); + $res = $api->toString(); + print_r($res); + +}); From 3e0d541f880a6d9b3f99a4f7239b1d67b9a84f5b Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 6 Jun 2025 18:02:16 +0800 Subject: [PATCH 03/59] ref v2 --- src/Enums/TypeKindEnum.php | 12 -- src/OpenApi/Collections/OpenApiCollection.php | 5 +- .../Collections/ParameterCollection.php | 3 +- src/OpenApi/Enum/ParameterTypeEnum.php | 41 +++++ src/OpenApi/Handler/Handler.php | 140 +++++++++--------- tests/Openapi/ArrayOpenApi.php | 9 +- 6 files changed, 117 insertions(+), 93 deletions(-) diff --git a/src/Enums/TypeKindEnum.php b/src/Enums/TypeKindEnum.php index ef13933..438a013 100644 --- a/src/Enums/TypeKindEnum.php +++ b/src/Enums/TypeKindEnum.php @@ -69,16 +69,4 @@ public static function getNameTo(string $type, ?string $className = null): self default => throw new RuntimeException("not found type $type"), }; } - - public function getOpenApiName(): string - { - return match ($this) { - self::INT => 'integer', - self::FLOAT => 'number', - self::BOOLEAN => 'boolean', - self::OBJECT, self::CLASS_OBJECT => 'object', - self::ARRAY, self::COLLECT_SINGLE_OBJECT , self::COLLECT_UNION_OBJECT => 'array', - default => 'string', - }; - } } diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index c09b698..c08787c 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -10,6 +10,7 @@ use Astral\Serialize\OpenApi\Annotations\Summary; use Astral\Serialize\OpenApi\Annotations\Tag; use Astral\Serialize\OpenApi\Enum\ContentTypeEnum; +use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Method; use Astral\Serialize\OpenApi\Storage\OpenAPI\RequestBodyStorage; use Astral\Serialize\OpenApi\Storage\OpenAPI\ResponseStorage; @@ -109,7 +110,7 @@ public function buildRequestBodyParameterCollections(string $className, array $g $vol = new ParameterCollection( name: current($property->getInputNamesByGroups($groups,$className)), descriptions: '', - type: current($property->getTypes())->kind ?: TypeKindEnum::STRING, + type: ParameterTypeEnum::getByTypes($property->getTypes()), required: !$property->isNullable(), ignore: false, ); @@ -153,7 +154,7 @@ public function buildResponseParameterCollections(): array $vol = new ParameterCollection( name:current($property->getOutNamesByGroups($groups,$responseClass)), descriptions: '', - type: current($property->getTypes())->kind ?: TypeKindEnum::STRING, + type: ParameterTypeEnum::getByTypes($property->getTypes()), required: !$property->isNullable(), ignore: false, ); diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php index f161b6d..e9049c5 100755 --- a/src/OpenApi/Collections/ParameterCollection.php +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -5,6 +5,7 @@ namespace Astral\Serialize\OpenApi\Collections; use Astral\Serialize\Enums\TypeKindEnum; +use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; use Attribute; class ParameterCollection @@ -14,7 +15,7 @@ public function __construct( public string $name, /** @var string descriptions */ public string $descriptions = '', - public TypeKindEnum $type = TypeKindEnum::STRING, + public ParameterTypeEnum $type = ParameterTypeEnum::STRING, /** @var mixed 示例值 */ public mixed $example = '', /** @var bool 是否必填 */ diff --git a/src/OpenApi/Enum/ParameterTypeEnum.php b/src/OpenApi/Enum/ParameterTypeEnum.php index a055d28..b45e74a 100755 --- a/src/OpenApi/Enum/ParameterTypeEnum.php +++ b/src/OpenApi/Enum/ParameterTypeEnum.php @@ -2,9 +2,50 @@ namespace Astral\Serialize\OpenApi\Enum; +use Astral\Serialize\Enums\TypeKindEnum; +use Astral\Serialize\Support\Collections\TypeCollection; + enum ParameterTypeEnum: string { case ARRAY = 'array'; case STRING = 'string'; case OBJECT = 'object'; + case BOOLEAN = 'boolean'; + case INTEGER = 'integer'; + case NUMBER = 'number'; + case ONE_OF = 'oneOf'; + case ANY_OF = 'anyOf'; + case ALL_OF = 'allOf'; + + /** + * @param TypeCollection[] $types + */ + public static function getByTypes(array $types): ParameterTypeEnum + { + + $count = count($types); + + if($count === 1){ + $type = current($types)->kind; + return match (true){ + $type === TypeKindEnum::INT => self::INTEGER, + $type === TypeKindEnum::FLOAT => self::NUMBER, + $type === TypeKindEnum::BOOLEAN => self::BOOLEAN, + $type === TypeKindEnum::OBJECT, $type === TypeKindEnum::CLASS_OBJECT => self::OBJECT, + $type === TypeKindEnum::ARRAY, $type === TypeKindEnum::COLLECT_SINGLE_OBJECT , $type === TypeKindEnum::COLLECT_UNION_OBJECT => self::ARRAY, + default => self::STRING, + }; + } + + $hasUnion = false; + foreach ($types as $type){ + if($type->kind === TypeKindEnum::COLLECT_UNION_OBJECT){ + $hasUnion = true; + } + } + + return $hasUnion ? self::ANY_OF : self::ONE_OF; + + + } } diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index f547914..5c5e155 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -16,9 +16,7 @@ abstract class Handler implements HandleInterface { /** @var OpenAPI */ - protected static OpenAPI $OpenAPI; - private string $controllerPrefix = ''; - private string $controllerSuffix = ''; + public static OpenAPI $OpenAPI; public function __construct( protected readonly ParameterStorage $headerParameterStorages = new ParameterStorage() @@ -50,100 +48,98 @@ public function addGlobalHeader(string $name, string $example = '', string $desc } /** - * Undocumented function - */ - public function getOpenAPI(): OpenAPI + * 遍历整个项目根目录,自动扫描所有 PHP 文件, + * 如果文件内容中包含 "Astral\Serialize\OpenApi\Annotations", + * 则认为它是需要处理的 Controller,进而调用 buildByClass。 + * + * @return $this + * @throws ReflectionException + */ + public function handleByFolders(): self { - return self::$OpenAPI; - } + // 1. 取项目根目录 + $projectRoot = getcwd(); - /** - * 增加类前缀标识 - * - * @return $this - */ - public function withControllerPrefix(string $value): self - { - $this->controllerPrefix = $value; - return $this; - } + if ($projectRoot === false) { + return $this; + } - /** - * 增加类后缀标识 - * - * @return $this - */ - public function withControllerSuffix(string $value): self - { - $this->controllerSuffix = $value; - return $this; - } + // 2. 默认根命名空间留空(根据项目实际情况可改为 "App"、"App\\Http\\Controllers" 等) + // 如果你希望扫描时拼接命名空间前缀,可以在这里做修改。 + $rootNamespace = ''; - /** - * @throws ReflectionException - */ - public function handleByAutoLoad(): void - { - $classMap = require dir('vendor/composer/autoload_classmap.php'); - $appClassMap = array_keys(array_filter($classMap, static function ($key) { - return (str_starts_with($key, 'App\\') && str_ends_with($key, 'Controller')) - || (str_starts_with($key, 'April\\') && str_ends_with($key, 'Controller')); - }, ARRAY_FILTER_USE_KEY)); + // 3. 调用内部递归方法开始扫描 + $this->scanFolderRecursively($projectRoot, $rootNamespace); - foreach ($appClassMap as $className) { - $this->buildByClass($className); - } + return $this; } /** - * 解析Controller文件 + * 递归扫描指定目录下的所有子目录和文件。 + * @param string $folder 要扫描的文件夹路径 + * @param string $namespace 该文件夹对应的 PHP 命名空间前缀 * - * @param array $folders 文件路径 => 命名空间 - * @return $this * @throws ReflectionException */ - public function handleByFolders(array $folders): self + protected function scanFolderRecursively(string $folder, string $namespace): void { + // 如果不是目录,跳过 + if (! is_dir($folder)) { + return; + } - foreach ($folders as $folder => $namespace) { - - if (! is_dir($folder)) { + // 遍历当前文件夹下的所有内容 + foreach (scandir($folder) as $file) { + // 跳过 . 和 .. ,以及隐藏文件夹/文件 + if ($file === '.' || $file === '..' || str_starts_with($file, '.')) { continue; } - foreach (scandir($folder) as $file) { + $path = $folder . DIRECTORY_SEPARATOR . $file; - $path = $folder . '/' . $file; - if ($file === '.' || $file === '..' || str_starts_with($file, '.')) { - continue; - } + // 如果是子目录,则递归,并拼接命名空间 + if (is_dir($path)) { - if (is_dir($path)) { - $this->handleByFolders([$path => $namespace . '\\' . $file]); + // 例如,如果当前命名空间是 "App": + // 子目录 "Http" 则新的命名空间为 "App\Http" + $newNamespace = $namespace !== '' ? ($namespace . '\\' . $file) : $file; + $this->scanFolderRecursively($path, $newNamespace); + continue; + } - continue; - } + // 只处理 .php 文件 + if (pathinfo($file, PATHINFO_EXTENSION) !== 'php') { + continue; + } - if (pathinfo($file, PATHINFO_EXTENSION) !== 'php') { - continue; - } + // 读取文件内容,检查是否包含 Annotations 关键字 + $fileContent = @file_get_contents($path); + if ($fileContent === false) { + continue; + } + + // 如果文件中没有引入 Astral\Serialize\OpenApi\Annotations,就跳过 + if (!str_contains($fileContent, 'Astral\\Serialize\\OpenApi\\Annotations')) { + continue; + } - $fileName = $this->controllerPrefix . trim(substr($file, 0, strpos($file, '.'))) . $this->controllerSuffix; - $className = $namespace ? $namespace . '\\' . $fileName : $fileName; + // 计算类名:去掉 .php 之后,将命名空间前缀 + 文件名 组成完整类名 + $baseName = substr($file, 0, -4); // 去掉 ".php" + $className = $namespace !== '' ? ($namespace . '\\' . $baseName) : $baseName; + // 如果类尚未加载,则尝试 include + if (! class_exists($className)) { + include_once $path; + @ob_clean(); if (! class_exists($className)) { - include_once $folder . '/' . $file; - ob_clean(); // 清除一些引入进来的莫名其妙输出文件 - if (! class_exists($className)) { - continue; - } + // 如果 include 后仍然不存在该类,跳过 + continue; } - - $this->buildByClass($className); } - } - return $this; + // 调用子类实现的 buildByClass + $this->buildByClass($className); + } } @@ -158,6 +154,6 @@ public function output(string $path): bool */ public function toString(): string { - return json_encode(self::$OpenAPI, JSON_THROW_ON_ERROR | JSON_UNESCAPED_UNICODE); + return json_encode(self::$OpenAPI, JSON_THROW_ON_ERROR); } } diff --git a/tests/Openapi/ArrayOpenApi.php b/tests/Openapi/ArrayOpenApi.php index b903429..87ea591 100644 --- a/tests/Openapi/ArrayOpenApi.php +++ b/tests/Openapi/ArrayOpenApi.php @@ -57,10 +57,7 @@ public function one(TestOpenApiRequest $request): TestOpenApiResponse // it('test openapi build by class', function () { - - $api = new \Astral\Serialize\OpenApi\OpenApi(); - $api->buildByClass(TestOpenApiController::class); - $res = $api->toString(); - print_r($res); - + $api = new \Astral\Serialize\OpenApi\OpenApi(); + $api->buildByClass(TestOpenApiController::class); + $res = $api->toString(); }); From ab7f5d1102dcd12a53d666427e90d88b531a5ba1 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 12 Jun 2025 15:21:16 +0800 Subject: [PATCH 04/59] RC1 --- src/OpenApi/Collections/OpenApiCollection.php | 10 +- .../ParameterChildrenCollection.php | 17 ++ .../Collections/ParameterCollection.php | 17 +- src/OpenApi/Enum/ParameterTypeEnum.php | 50 ++++++ src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 162 ++++++++++++------ .../Openapi/{ArrayOpenApi.php => OpenApi.php} | 20 +-- 6 files changed, 209 insertions(+), 67 deletions(-) create mode 100644 src/OpenApi/Collections/ParameterChildrenCollection.php rename tests/Openapi/{ArrayOpenApi.php => OpenApi.php} (62%) diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index c08787c..984fe8e 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -105,11 +105,14 @@ public function buildRequestBodyParameterCollections(string $className, array $g $serializeContext->from(); $properties = $serializeContext->getGroupCollection()->getProperties(); + $vols = []; foreach ($properties as $property){ $vol = new ParameterCollection( + className: $className, name: current($property->getInputNamesByGroups($groups,$className)), descriptions: '', + types: $property->getTypes(), type: ParameterTypeEnum::getByTypes($property->getTypes()), required: !$property->isNullable(), ignore: false, @@ -117,7 +120,8 @@ public function buildRequestBodyParameterCollections(string $className, array $g if($property->getChildren()){ foreach ($property->getChildren() as $children){ - $vol->children[] = $this->buildRequestBodyParameterCollections($children->getClassName()); + $className = $children->getClassName(); + $vol->children[$className] = $this->buildRequestBodyParameterCollections($className); } } @@ -144,7 +148,7 @@ public function buildResponseParameterCollections(): array return []; } - $groups = $this->response ? $this->response->groups : ['default']; + $groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default']; $serializeContext = ContextFactory::build($responseClass); $serializeContext->from(); $properties = $serializeContext->getGroupCollection()->getProperties(); @@ -152,8 +156,10 @@ public function buildResponseParameterCollections(): array $vols = []; foreach ($properties as $property){ $vol = new ParameterCollection( + className: $responseClass, name:current($property->getOutNamesByGroups($groups,$responseClass)), descriptions: '', + types: $property->getTypes(), type: ParameterTypeEnum::getByTypes($property->getTypes()), required: !$property->isNullable(), ignore: false, diff --git a/src/OpenApi/Collections/ParameterChildrenCollection.php b/src/OpenApi/Collections/ParameterChildrenCollection.php new file mode 100644 index 0000000..879b804 --- /dev/null +++ b/src/OpenApi/Collections/ParameterChildrenCollection.php @@ -0,0 +1,17 @@ + $children */ + public array $children = [], + ) + { + } +} \ No newline at end of file diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php index e9049c5..027c803 100755 --- a/src/OpenApi/Collections/ParameterCollection.php +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -6,24 +6,37 @@ use Astral\Serialize\Enums\TypeKindEnum; use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; +use Astral\Serialize\Support\Collections\TypeCollection; use Attribute; class ParameterCollection { public function __construct( + public string $className, /** @var string 元素变量名 */ public string $name, /** @var string descriptions */ public string $descriptions = '', - public ParameterTypeEnum $type = ParameterTypeEnum::STRING, + /** @var TypeCollection[] $types */ + public array $types, + public ParameterTypeEnum $type, /** @var mixed 示例值 */ public mixed $example = '', /** @var bool 是否必填 */ public bool $required = false, /** @var bool 是否忽略显示 */ public bool $ignore = false, - /** @var array $children */ + /** @var array $children */ public array $children = [], ){ } + + public function addChildren(array $collections,ParameterTypeEnum $type = ParameterTypeEnum::STRING): void + { + $children = new ParameterChildrenCollection(); + $children->type = $type; + $children->children = $collections; + $this->children[] = $children; + } } + diff --git a/src/OpenApi/Enum/ParameterTypeEnum.php b/src/OpenApi/Enum/ParameterTypeEnum.php index b45e74a..72631e2 100755 --- a/src/OpenApi/Enum/ParameterTypeEnum.php +++ b/src/OpenApi/Enum/ParameterTypeEnum.php @@ -17,6 +17,56 @@ enum ParameterTypeEnum: string case ANY_OF = 'anyOf'; case ALL_OF = 'allOf'; + public function isObject(): bool + { + return $this === self::OBJECT; + } + + public function isArray(): bool + { + return $this === self::ARRAY; + } + + public function isOf(): bool + { + return $this === self::ONE_OF || $this === self::ANY_OF || $this === self::ALL_OF; + } + + public static function getBaseEnumByTypeKindEnum(TypeCollection $collection, string $className = null): ?ParameterTypeEnum + { + return match (true){ + $collection->kind === TypeKindEnum::STRING && !$className => self::STRING, + $collection->kind === TypeKindEnum::INT && !$className => self::INTEGER, + $collection->kind === TypeKindEnum::FLOAT && !$className => self::NUMBER, + $collection->kind === TypeKindEnum::BOOLEAN && !$className=> self::BOOLEAN, + in_array($collection->kind, [TypeKindEnum::CLASS_OBJECT, TypeKindEnum::OBJECT], true) && $className === $collection->className => self::OBJECT, + in_array($collection->kind, [TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true) && $className === $collection->className => self::ARRAY, + default => null, + }; + } + + /** + * @param TypeCollection[] $types + * @param string $className + * @return ParameterTypeEnum|null + */ + + public static function getArrayAndObjectEnumBy(array $types, string $className): ?ParameterTypeEnum + { + + foreach ($types as $collection){ + if($className === $collection->className && in_array($collection->kind, [TypeKindEnum::CLASS_OBJECT, TypeKindEnum::OBJECT], true)){ + return self::OBJECT; + } + + if( $className === $collection->className && in_array($collection->kind, [TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true)){ + return self::ARRAY; + } + } + + return null; + } + /** * @param TypeCollection[] $types */ diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index 135dc5f..bbfeb8b 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -5,92 +5,154 @@ namespace Astral\Serialize\OpenApi\Storage\OpenAPI; use Astral\Serialize\OpenApi\Collections\ParameterCollection; +use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; use Astral\Serialize\OpenApi\Storage\StorageInterface; -use Exception; /** - * 文档开发者介绍 + * OpenAPI Schema 数据存储和构建器 + * 用于生成符合 OpenAPI 规范的 Schema 结构 */ class SchemaStorage implements StorageInterface { - private array|SchemaStorage $data = [ + /** + * Schema 数据结构 + */ + private array $data = [ 'type' => 'object', 'properties' => [], ]; - public function getData(): SchemaStorage|array + /** + * 获取构建的 Schema 数据 + */ + public function getData(): array { return $this->data; } /** - * Undocumented function + * 构建 OpenAPI Schema 数据结构 * - * @param array $tree - * @param mixed|null $node - * @return SchemaStorage + * @param array $parameterTree 参数集合树 + * @param array|null $currentNode 当前构建节点的引用 + * @return static */ - public function build(array $tree, mixed &$node = null): static + public function build(array $parameterTree, array &$currentNode = null): static { - if ($node === null) { - $node = &$this->data; + if ($currentNode === null) { + $currentNode = &$this->data; } - foreach ($tree as $item) { + foreach ($parameterTree as $parameter) { + - if ($item->ignore) { + // 跳过被标记为忽略的参数 + if ($parameter->ignore) { continue; } - $node['properties'][$item->name] = [ - 'type' => strtolower($item->type->getOpenApiName()), - 'description' => $item->descriptions, - 'example' => $item->example, - ]; + // 构建基础属性 Schema + $this->buildBasicPropertySchema($parameter, $currentNode); - if ($item->required) { - $node['required'][] = $item->name; + // oneOf/anyOf/allOf 格式 + if($parameter->type->isOf()){ + $this->buildOfProperties($parameter, $currentNode); + } + // 处理嵌套子属性 + else if ($parameter->children) { + $this->buildNestedProperties($parameter, $currentNode); } + } - if ($item->children) { - // list对象 - if ($item->type->isCollect()) { - $node['properties'][$item->name]['items'] = [ - 'type' => 'object', - 'properties' => [], - ]; - $tree = &$node['properties'][$item->name]['items']; - } - // 单个对象 - elseif ($item->type->existsCollectClass()) { - $node['properties'][$item->name] = [ - 'type' => 'object', - 'properties' => [], - ]; - $tree = &$node['properties'][$item->name]; - } + return $this; + } + + /** + * 构建基础属性的 Schema 结构 + */ + private function buildBasicPropertySchema(ParameterCollection $parameter, array &$currentNode): void + { + $propertyName = $parameter->name; + + $currentNode['properties'][$propertyName] = [ + 'type' => $parameter->type->value, + 'description' => $parameter->descriptions, + 'example' => $parameter->example, + ]; - foreach ($item->children as $v){ - $this->build($v, $tree); + // 添加必填字段标记 + if ($parameter->required) { + $currentNode['required'][] = $propertyName; + } + } + + /** + * 构建 oneOf/anyOf/allOf 属性结构 + */ + public function buildOfProperties(ParameterCollection $topParameter, array &$currentNode): void + { + $propertyName = $topParameter->name; + // 重构属性结构为 oneOf/anyOf/allOf 格式 + $node = &$currentNode['properties'][$propertyName][$topParameter->type->value]; + + $i = 0; + foreach ($topParameter->types as $kindType){ + $type = ParameterTypeEnum::getBaseEnumByTypeKindEnum($kindType); + if($type){ + $node[$i] = ['type'=> $type]; + $i++; + } + } + + if($topParameter->children){ + foreach ($topParameter->children as $className => $child){ + $type = ParameterTypeEnum::getArrayAndObjectEnumBy($topParameter->types,$className); + if($type->isObject()){ + $node[$i] = ['type'=>'object','properties' => []]; + $childNode = &$node[$i]; + $i++; + }else if($type->isArray()){ + $node[$i] = ['type'=>'array','items'=> ['type'=>'object','properties' => []]]; + $childNode = &$node[$i]['items']; + $i++; } + $this->build($child,$childNode); } } - return $this; } - public function addProperties(string $name, string $type, string $description, string $example, bool $required = false): void + /** + * 构建嵌套属性结构 + */ + private function buildNestedProperties(ParameterCollection $topParameter, array &$currentNode): void { - $this->data['properties'][$name] = [ - 'type' => $type, - 'description' => $description, - 'example' => $example, - ]; + $propertyName = $topParameter->name; + $nestedNode = null; + + if ($topParameter->type->isArray()) { + // 数组类型:创建 items 结构 + $currentNode['properties'][$propertyName]['items'] = [ + 'type' => 'object', + 'properties' => [], + ]; + $nestedNode = &$currentNode['properties'][$propertyName]['items']; + } elseif ($topParameter->type->isObject()) { + // 对象类型:重构为嵌套对象结构 + $currentNode['properties'][$propertyName] = [ + 'type' => 'object', + 'properties' => [], + 'description' => $topParameter->descriptions, + ]; + $nestedNode = &$currentNode['properties'][$propertyName]; + } - if ($required) { - $this->data['required'][] = $name; + // 递归构建子属性 + if ($nestedNode !== null) { + foreach ($topParameter->children as $childParameter) { + $this->build($childParameter, $nestedNode); + } } } - -} +} \ No newline at end of file diff --git a/tests/Openapi/ArrayOpenApi.php b/tests/Openapi/OpenApi.php similarity index 62% rename from tests/Openapi/ArrayOpenApi.php rename to tests/Openapi/OpenApi.php index 87ea591..579ad22 100644 --- a/tests/Openapi/ArrayOpenApi.php +++ b/tests/Openapi/OpenApi.php @@ -9,7 +9,8 @@ class OtherOpenApiArrayNestedOne public string $name_one; public int $id_one; - public OtherOpenApiArrayNestedTwo $otherNestedTwo; + public OtherOpenApiArrayNestedTwo $object_test; + } class OtherOpenApiArrayNestedTwo @@ -22,10 +23,10 @@ class OtherOpenApiArrayNestedTwo class TestOpenApiRequest extends Serialize { public string $name; - public int $id; + public int|float|string $id; - /** @var OtherOpenApiArrayNestedOne[] $otherNestedOne */ - public array $otherNestedOne; + /** @var OtherOpenApiArrayNestedOne[]|OtherOpenApiArrayNestedTwo[]|string[] $any_array */ + public array $any_array; } class TestOpenApiResponse extends Serialize @@ -39,18 +40,10 @@ class TestOpenApiController{ #[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法一')] #[\Astral\Serialize\OpenApi\Annotations\Route('/test/one-action')] - public function one(TestOpenApiRequest $request): TestOpenApiResponse + public function one(TestOpenApiRequest $request) { return new TestOpenApiResponse(); } - - -// #[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法二')] -// #[\Astral\Serialize\OpenApi\Annotations\Route('test/two-action')] -// #[\Astral\Serialize\OpenApi\Annotations\Response(TestOpenApiResponse::class)] -// public function one2(TestOpenApiRequest $request): void -// { -// } } }); @@ -60,4 +53,5 @@ public function one(TestOpenApiRequest $request): TestOpenApiResponse $api = new \Astral\Serialize\OpenApi\OpenApi(); $api->buildByClass(TestOpenApiController::class); $res = $api->toString(); + var_dump($res); }); From 5e1c8583f6f79df84de6ae0cf61254bca21ca087 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 12 Jun 2025 15:37:28 +0800 Subject: [PATCH 05/59] fix phpstan RC2 --- src/OpenApi/Annotations/RequestBody.php | 2 +- src/OpenApi/Collections/OpenApiCollection.php | 14 +- .../ParameterChildrenCollection.php | 17 - .../Collections/ParameterCollection.php | 15 +- src/OpenApi/Enum/ParameterTypeEnum.php | 12 +- src/OpenApi/Handler/Handler.php | 7 +- src/OpenApi/Handler/ParserPartaker.php | 191 ----------- src/OpenApi/Handler/api.php | 314 ------------------ 8 files changed, 16 insertions(+), 556 deletions(-) delete mode 100644 src/OpenApi/Collections/ParameterChildrenCollection.php delete mode 100755 src/OpenApi/Handler/ParserPartaker.php delete mode 100644 src/OpenApi/Handler/api.php diff --git a/src/OpenApi/Annotations/RequestBody.php b/src/OpenApi/Annotations/RequestBody.php index 71e5a96..9323f6d 100755 --- a/src/OpenApi/Annotations/RequestBody.php +++ b/src/OpenApi/Annotations/RequestBody.php @@ -11,7 +11,7 @@ class RequestBody { public function __construct( - /** @var class-string $className */ + /** @var class-string|string $className */ public string $className = '', public ContentTypeEnum $contentType = ContentTypeEnum::JSON, public array|null $group = null diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index 984fe8e..1449a95 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -19,6 +19,7 @@ use Astral\Serialize\Support\Factories\ContextFactory; use Psr\SimpleCache\InvalidArgumentException; use ReflectionMethod; +use ReflectionNamedType; class OpenApiCollection { @@ -73,7 +74,8 @@ public function buildRequestBodyByParameters(): RequestBodyStorage { $openAPIRequestBody = new RequestBodyStorage(ContentTypeEnum::JSON); $methodParam = $this->reflectionMethod->getParameters()[0] ?? null; - $requestBodyClass = $methodParam ? $methodParam->getType()?->getName() : ''; + $type = $methodParam?->getType(); + $requestBodyClass = $type instanceof ReflectionNamedType ? $type->getName() : ''; if (is_subclass_of($requestBodyClass, Serialize::class)) { $schemaStorage = (new SchemaStorage())->build($this->buildRequestBodyParameterCollections($requestBodyClass),$node); $openAPIRequestBody->withParameter($schemaStorage); @@ -96,7 +98,7 @@ public function buildResponse(): ResponseStorage /** * @param string $className * @param array $groups - * @return array + * @return array * @throws InvalidArgumentException */ public function buildRequestBodyParameterCollections(string $className, array $groups = ['default']): array @@ -111,9 +113,9 @@ public function buildRequestBodyParameterCollections(string $className, array $g $vol = new ParameterCollection( className: $className, name: current($property->getInputNamesByGroups($groups,$className)), - descriptions: '', types: $property->getTypes(), type: ParameterTypeEnum::getByTypes($property->getTypes()), + descriptions: '', required: !$property->isNullable(), ignore: false, ); @@ -132,7 +134,7 @@ className: $className, } /** - * @return array + * @return array * @throws InvalidArgumentException */ public function buildResponseParameterCollections(): array @@ -157,10 +159,10 @@ public function buildResponseParameterCollections(): array foreach ($properties as $property){ $vol = new ParameterCollection( className: $responseClass, - name:current($property->getOutNamesByGroups($groups,$responseClass)), - descriptions: '', + name: current($property->getOutNamesByGroups($groups,$responseClass)), types: $property->getTypes(), type: ParameterTypeEnum::getByTypes($property->getTypes()), + descriptions: '', required: !$property->isNullable(), ignore: false, ); diff --git a/src/OpenApi/Collections/ParameterChildrenCollection.php b/src/OpenApi/Collections/ParameterChildrenCollection.php deleted file mode 100644 index 879b804..0000000 --- a/src/OpenApi/Collections/ParameterChildrenCollection.php +++ /dev/null @@ -1,17 +0,0 @@ - $children */ - public array $children = [], - ) - { - } -} \ No newline at end of file diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php index 027c803..a6d3cc6 100755 --- a/src/OpenApi/Collections/ParameterCollection.php +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -13,30 +13,17 @@ class ParameterCollection { public function __construct( public string $className, - /** @var string 元素变量名 */ public string $name, - /** @var string descriptions */ - public string $descriptions = '', /** @var TypeCollection[] $types */ public array $types, public ParameterTypeEnum $type, - /** @var mixed 示例值 */ + public string $descriptions = '', public mixed $example = '', - /** @var bool 是否必填 */ public bool $required = false, - /** @var bool 是否忽略显示 */ public bool $ignore = false, /** @var array $children */ public array $children = [], ){ } - - public function addChildren(array $collections,ParameterTypeEnum $type = ParameterTypeEnum::STRING): void - { - $children = new ParameterChildrenCollection(); - $children->type = $type; - $children->children = $collections; - $this->children[] = $children; - } } diff --git a/src/OpenApi/Enum/ParameterTypeEnum.php b/src/OpenApi/Enum/ParameterTypeEnum.php index 72631e2..c9cb3d4 100755 --- a/src/OpenApi/Enum/ParameterTypeEnum.php +++ b/src/OpenApi/Enum/ParameterTypeEnum.php @@ -32,15 +32,13 @@ public function isOf(): bool return $this === self::ONE_OF || $this === self::ANY_OF || $this === self::ALL_OF; } - public static function getBaseEnumByTypeKindEnum(TypeCollection $collection, string $className = null): ?ParameterTypeEnum + public static function getBaseEnumByTypeKindEnum(TypeCollection $collection): ?ParameterTypeEnum { return match (true){ - $collection->kind === TypeKindEnum::STRING && !$className => self::STRING, - $collection->kind === TypeKindEnum::INT && !$className => self::INTEGER, - $collection->kind === TypeKindEnum::FLOAT && !$className => self::NUMBER, - $collection->kind === TypeKindEnum::BOOLEAN && !$className=> self::BOOLEAN, - in_array($collection->kind, [TypeKindEnum::CLASS_OBJECT, TypeKindEnum::OBJECT], true) && $className === $collection->className => self::OBJECT, - in_array($collection->kind, [TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true) && $className === $collection->className => self::ARRAY, + $collection->kind === TypeKindEnum::STRING => self::STRING, + $collection->kind === TypeKindEnum::INT => self::INTEGER, + $collection->kind === TypeKindEnum::FLOAT => self::NUMBER, + $collection->kind === TypeKindEnum::BOOLEAN => self::BOOLEAN, default => null, }; } diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index 5c5e155..2fe8b09 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -129,12 +129,7 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi // 如果类尚未加载,则尝试 include if (! class_exists($className)) { - include_once $path; - @ob_clean(); - if (! class_exists($className)) { - // 如果 include 后仍然不存在该类,跳过 - continue; - } + continue; } // 调用子类实现的 buildByClass diff --git a/src/OpenApi/Handler/ParserPartaker.php b/src/OpenApi/Handler/ParserPartaker.php deleted file mode 100755 index 3efdd85..0000000 --- a/src/OpenApi/Handler/ParserPartaker.php +++ /dev/null @@ -1,191 +0,0 @@ -tree = new TreeNode(); - } - - public function addNode(string $className, ?string $group = null, ?TreeNode $childTree = null) - { - - $classRefection = new ReflectionClass($className); - - foreach ($classRefection->getProperties() as $property) { - - if ($property->isProtected()) { - continue; - } - - if ($group && class_exists(Group::class)) { - $attributes = $property->getAttributes(Group::class); - if (! $attributes) { - continue; - } - - $groups = $attributes[0]->newInstance(); - if (! in_array($group, $groups->names)) { - continue; - } - } - - $tree = new TreeNode($property); - if ($childTree) { - $childTree->addChildren($tree); - } else { - $this->tree->addChildren($tree); - } - - // // debug - // $docComment = <<getDocComment(); - $varMatch = getVarByDocComment($docComment); - if (! $varMatch) { - continue; - } - - /** - * var array<*,{string-class}> - * var {string-class}[] - * var {string-class} - */ - if ( - ! preg_match('#array<\S+,(\S+)>#', $varMatch[1], $arrayMatch) - && ! preg_match('#(\S+)\[\]#', $varMatch[1], $arrayMatch) - && ! preg_match('#(\S+)#', $varMatch[1], $arrayMatch) - ) { - continue; - } - - $listClass = trim($arrayMatch[1]); - if (in_array(strtolower($listClass), $this->ignoreConst)) { - continue; - } - - try { - $listClass = $this->getFullClassName($property, $listClass); - } catch (\Throwable $th) { - continue; - } - - // 添加子级类信息 - if ($listClass != $className) { - $this->addNode($listClass, $group, $tree); - } - } - } - - public function getFullClassName(ReflectionProperty $property, string $className): string - { - // 当前类命名空间 - $selfNamespaceName = $property->getDeclaringClass()->getNamespaceName(); - // 直接匹配类 - if (class_exists($className)) { - return $className; - } - // 判断是否是同一命名空间下的类 - elseif (class_exists($selfNamespaceName . '\\' . $className)) { - return $selfNamespaceName . '\\' . $className; - } - // 判断是否是引用类 - else { - // 获取引入文件 - $importClass = $this->parseUseStatements($property->getDeclaringClass()); - if (isset($importClass[$className])) { - return $importClass[$className]; - } - } - - throw new Exception('not find class ' . $className); - } - - /** - * 获取引入文件 - */ - private function parseUseStatements(ReflectionClass $reflectionClass): array - { - - $content = file_get_contents($reflectionClass->getFileName()); - preg_match_all('/^\s*use[\s+](.*);$/m', $content, $matches); - $classNames = []; - foreach ($matches[1] as $fullClassName) { - $parts = explode('\\', $fullClassName); - $classNames[end($parts)] = $fullClassName; - } - - return $classNames; - } - - /** - * Undocumented function - * - * @return TreeNode - */ - public function getTree() - { - return $this->tree; - } -} diff --git a/src/OpenApi/Handler/api.php b/src/OpenApi/Handler/api.php deleted file mode 100644 index db0c773..0000000 --- a/src/OpenApi/Handler/api.php +++ /dev/null @@ -1,314 +0,0 @@ -headerParameterStorages) { - $this->headerParameterStorages = new ParameterStorage(); - } - - // 添加头部参数的属性 - $this->headerParameterStorages->addHeaderProperties($name, $description, $example); - - return $this; - } - - /** - * Undocumented function - */ - public function getOpenAPI(): OpenAPI - { - return self::$OpenAPI; - } - - /** - * 增加类前缀标识 - * - * @return $this - */ - public function withControllerPrefix(string $value): self - { - $this->controllerPrefix = $value; - - return $this; - } - - /** - * 增加类后缀标识 - * - * @return $this - */ - public function withControllerSuffix(string $value): self - { - $this->controllerSuffix = $value; - - return $this; - } - - /** - * 是否开启注解异常信息 - * - * @return $this - */ - public function enableException(bool $bool = true): self - { - $this->_isIgnoreException = $bool; - - return $this; - } - - /** - * 解析Controller文件 - * - * @param array $folders 文件路径 => 命名空间 - * @return $this - */ - public function handleByFolders(array $folders): self - { - - foreach ($folders as $folder => $namespace) { - - if (! is_dir($folder)) { - continue; - } - - foreach (scandir($folder) as $file) { - - $path = $folder.'/'.$file; - if ($file == '.' || $file == '..' || strpos($file, '.') === 0) { - continue; - } - - if (is_dir($path)) { - $this->handleByFolders([$path => $namespace.'\\'.$file]); - - continue; - } - - if (pathinfo($file, PATHINFO_EXTENSION) != 'php') { - continue; - } - - $fileName = $this->controllerPrefix.trim(substr($file, 0, strpos($file, '.'))).$this->controllerSuffix; - $className = $namespace ? $namespace.'\\'.$fileName : $fileName; - - if (! class_exists($className)) { - include_once $folder.'/'.$file; - ob_clean(); // 清除一些引入进来的莫名其妙输出文件 - if (! class_exists($className)) { - continue; - } - } - - $this->createOpenAPIByClass($className); - } - } - - return $this; - } - - /** - * 构建OpenApi结构文档 - * - * @param class-string $className - */ - public function createOpenAPIByClass($className): void - { - // try { - - $classRefection = new ReflectionClass($className); - - $tagDoc = $classRefection->getAttributes(Tag::class); - /** @var Tag */ - $tagDoc = isset($tagDoc[0]) ? $tagDoc[0]->newInstance() : null; - if ($tagDoc) { - self::$OpenAPI->addTag($tagDoc->buildTagStorage()); - } - - foreach ($classRefection->getMethods() as $item) { - - /** @var ReflectionMethod */ - $reflectionMethod = $classRefection->getMethod($item->name); - $methodAttributes = $reflectionMethod->getAttributes(); - - if (! $methodAttributes) { - continue; - } - - $routeDoc = null; - $summaryDoc = null; - $requestBodyDoc = null; - $responseDoc = null; - foreach ($methodAttributes as $methodAttribute) { - switch ($methodAttribute) { - case $methodAttribute->getName() == Route::class: - /** @var Route */ - $routeDoc = $methodAttribute->newInstance(); - break; - case $methodAttribute->getName() == Summary::class: - /** @var Summary */ - $summaryDoc = $methodAttribute->newInstance(); - break; - case $methodAttribute->getName() == RequestBody::class: - /** @var RequestBody */ - $requestBodyDoc = $methodAttribute->newInstance(); - break; - case $methodAttribute->getName() == Response::class: - /** @var Response */ - $responseDoc = $methodAttribute->newInstance(); - break; - } - } - - if (! $routeDoc || ! $summaryDoc) { - continue; - } - - $methodClass = $routeDoc->getMethod(); - /** @var MethodInterface|Method| */ - $openAPIMethod = new $methodClass($summaryDoc->value, $summaryDoc->description ?: '', [$tagDoc->value ?: '']); - - // 统一header头 - if ($this->headerParameterStorages) { - $openAPIMethod->withParameters($this->headerParameterStorages->getData()); - } - - if ($requestBodyDoc) { - $openAPIRequestBody = new OpenAPIRequestBody($requestBodyDoc->contentType); - $requestBodySchema = $this->buildSchemaByClass($requestBodyDoc->className); - $openAPIRequestBody->withParameter($requestBodySchema); - $openAPIMethod->withRequestBody($openAPIRequestBody); - } else { - $methodParam = $reflectionMethod->getParameters(); - if (isset($methodParam[0]) && ($requestBodyClass = $methodParam[0]->gettype()->getName()) !== Request::class) { - $openAPIRequestBody = new OpenAPIRequestBody(ContentTypeEnum::JSON); - $requestBodySchema = $this->buildSchemaByClass($requestBodyClass); - $openAPIRequestBody->withParameter($requestBodySchema); - $openAPIMethod->withRequestBody($openAPIRequestBody); - } - } - - if ($responseDoc) { - if (! class_exists($responseDoc->className)) { - throw new Exception( - sprintf('Class "%s" does not exist in "%s" from action "%s"', - $responseDoc->className, - $reflectionMethod->getFileName(), - $reflectionMethod->getName()) - ); - } - - $openApiResponse = new OpenAPIResponse(); - $openApiResponse->description = '成功'; - $openApiResponse->withParameter($this->buildSchemaByClass($responseDoc->className)); - $openAPIMethod->addResponse($responseDoc->code, $openApiResponse); - - } else { - /** @var ReflectionType */ - $returnClass = $classRefection->getMethod($item->name)->getReturnType(); - if ($returnClass && class_exists($returnClass->getName())) { - $openApiResponse = new OpenAPIResponse(); - $openApiResponse->description = '成功'; - $openApiResponse->withParameter($this->buildSchemaByClass($returnClass->getName())); - $openAPIMethod->addResponse(200, $openApiResponse); - } - } - - // /** @var Params[] */ - // $paramsDoc = $reflectionMethod->getAttributes(Params::class); - // if ($paramsDoc) { - // $Parameter = new Parameter(); - // foreach ($paramsDoc as $v) { - // $Parameter->addProperties($v->name, $v->type, $v->value, $v->example, $v->required); - // } - - // $openAPIMethod->withParameters($Parameter->getData()); - // } - - // /** @var RequestValue[] */ - // $requestValuesDoc = $reflectionMethod->getAttributes(RequestValue::class); - // if ($requestValuesDoc && $openAPIRequestBody) { - // foreach ($requestValuesDoc as $v) { - // $requestBodySchema->addProperties($v->name, $v->type, $v->value, $v->example, $v->required); - // } - - // $openAPIRequestBody->withParameter($requestBodySchema); - // $openAPIMethod->withRequestBody($openAPIRequestBody); - // } - - self::$OpenAPI->addPath($routeDoc->route, $openAPIMethod); - } - // } catch (Throwable $th) { - // if ($this->_isIgnoreException) { - // echo '解析参数异常:'.PHP_EOL; - // exit(highlight_string(var_export($th, true))); - // } - - // } - - } - - /** - * 根据类信息构建Schema - */ - public function buildSchemaByClass(string $className): SchemaStorage - { - $schema = new SchemaStorage(); - - if (! $className) { - return $schema; - } - - $ParserPartaker = new ParserPartaker(); - $ParserPartaker->addNode($className, null); - - $tree = $ParserPartaker->getTree(); - - $schema->createTree($tree->getChildren()); - - return $schema; - } - - public function output(string $path): bool - { - return true; - } - - public function toString(): string - { - return json_encode(self::$OpenAPI, JSON_UNESCAPED_UNICODE); - } -} From 71a0e29895d261c644820c35ae177e9733913532 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 12 Jun 2025 15:40:13 +0800 Subject: [PATCH 06/59] fix phpstan RC2 --- composer.lock | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/composer.lock b/composer.lock index 1825245..efb4237 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "e2fb9f91440aed5f8990e559a3d1b4be", + "content-hash": "7d1eb67f33b4afb14170c3b9812e0f20", "packages": [ { "name": "carbonphp/carbon-doctrine-types", From baf40079d7227a31ec43aefa62b7977cc2dce82b Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 12 Jun 2025 16:03:45 +0800 Subject: [PATCH 07/59] fix phpstan RC2 --- src/OpenApi/OpenApi.php | 28 ++++++++----------- src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 1 + 2 files changed, 12 insertions(+), 17 deletions(-) diff --git a/src/OpenApi/OpenApi.php b/src/OpenApi/OpenApi.php index bd15630..c6b887a 100755 --- a/src/OpenApi/OpenApi.php +++ b/src/OpenApi/OpenApi.php @@ -45,13 +45,12 @@ public function buildByClass(string $className): void continue; } - $routeDoc = $summaryDoc = $requestBodyDoc = $responseDoc = $headersDoc = null; $instances = [ - Route::class => &$routeDoc, - Summary::class => &$summaryDoc, - RequestBody::class => &$requestBodyDoc, - Response::class => &$responseDoc, - Headers::class => &$headersDoc, + Route::class => null, + Summary::class => null, + RequestBody::class => null, + Response::class => null, + Headers::class => null, ]; foreach ($methodAttributes as $methodAttribute) { @@ -61,7 +60,7 @@ public function buildByClass(string $className): void } } - if (! $routeDoc || ! $summaryDoc) { + if ($instances[Route::class] === null || $instances[Summary::class] === null) { continue; } @@ -70,20 +69,15 @@ public function buildByClass(string $className): void methodName: $item->getName(), reflectionMethod: $item, tag: $tagDoc, - summary: $summaryDoc, - route: $routeDoc, - headers: $headersDoc, + summary: $instances[Summary::class], + route: $instances[Route::class], + headers: $instances[Headers::class], attributes: $methodAttributes, - requestBody: $requestBodyDoc, - response: $responseDoc, + requestBody: $instances[RequestBody::class], + response: $instances[Response::class], ); - self::$OpenAPI->addPath($openApiCollection); } } - - - - } diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index bbfeb8b..f5e0ab8 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -35,6 +35,7 @@ public function getData(): array * * @param array $parameterTree 参数集合树 * @param array|null $currentNode 当前构建节点的引用 + * @param-out array $currentNode 当前构建节点的引用 * @return static */ public function build(array $parameterTree, array &$currentNode = null): static From 7f6e7324812ae2e052f90498f3254fddba87e83f Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 12 Jun 2025 16:20:04 +0800 Subject: [PATCH 08/59] =?UTF-8?q?composer=20=E5=9B=BA=E5=AE=9A8.1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- composer.json | 3 + composer.lock | 581 ++++++++++++++++++++++++++------------------------ 2 files changed, 300 insertions(+), 284 deletions(-) diff --git a/composer.json b/composer.json index 5e14d29..7a18e2b 100755 --- a/composer.json +++ b/composer.json @@ -41,6 +41,9 @@ "config": { "allow-plugins": { "pestphp/pest-plugin": true + }, + "platform": { + "php": "8.1.32" } } } diff --git a/composer.lock b/composer.lock index efb4237..b453161 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "7d1eb67f33b4afb14170c3b9812e0f20", + "content-hash": "a5a7da27a45194d7e04046a062ca1490", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -77,26 +77,29 @@ }, { "name": "doctrine/deprecations", - "version": "1.1.4", + "version": "1.1.5", "source": { "type": "git", "url": "https://github.com/doctrine/deprecations.git", - "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9" + "reference": "459c2f5dd3d6a4633d3b5f46ee2b1c40f57d3f38" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/deprecations/zipball/31610dbb31faa98e6b5447b62340826f54fbc4e9", - "reference": "31610dbb31faa98e6b5447b62340826f54fbc4e9", + "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", - "phpstan/phpstan": "1.4.10 || 2.0.3", + "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.5", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.6 || ^10.5 || ^11.5 || ^12", "psr/log": "^1 || ^2 || ^3" }, "suggest": { @@ -116,9 +119,9 @@ "homepage": "https://www.doctrine-project.org/", "support": { "issues": "https://github.com/doctrine/deprecations/issues", - "source": "https://github.com/doctrine/deprecations/tree/1.1.4" + "source": "https://github.com/doctrine/deprecations/tree/1.1.5" }, - "time": "2024-12-07T21:18:45+00:00" + "time": "2025-04-07T20:06:18+00:00" }, { "name": "doctrine/inflector", @@ -649,16 +652,16 @@ }, { "name": "nikic/php-parser", - "version": "v5.4.0", + "version": "v5.5.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494" + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/447a020a1f875a434d62f2a401f53b82a396e494", - "reference": "447a020a1f875a434d62f2a401f53b82a396e494", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9", + "reference": "ae59794362fe85e051a58ad36b289443f57be7a9", "shasum": "" }, "require": { @@ -701,25 +704,26 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v5.4.0" + "source": "https://github.com/nikic/PHP-Parser/tree/v5.5.0" }, - "time": "2024-12-30T11:07:19+00:00" + "time": "2025-05-31T08:24:38+00:00" }, { "name": "phpdocumentor/reflection", - "version": "6.1.0", + "version": "6.3.0", "source": { "type": "git", "url": "https://github.com/phpDocumentor/Reflection.git", - "reference": "bb4dea805a645553d6d989b23dad9f8041f39502" + "reference": "d91b3270832785602adcc24ae2d0974ba99a8ff8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/bb4dea805a645553d6d989b23dad9f8041f39502", - "reference": "bb4dea805a645553d6d989b23dad9f8041f39502", + "url": "https://api.github.com/repos/phpDocumentor/Reflection/zipball/d91b3270832785602adcc24ae2d0974ba99a8ff8", + "reference": "d91b3270832785602adcc24ae2d0974ba99a8ff8", "shasum": "" }, "require": { + "composer-runtime-api": "^2", "nikic/php-parser": "~4.18 || ^5.0", "php": "8.1.*|8.2.*|8.3.*|8.4.*", "phpdocumentor/reflection-common": "^2.1", @@ -730,7 +734,8 @@ }, "require-dev": { "dealerdirect/phpcodesniffer-composer-installer": "^1.0", - "doctrine/coding-standard": "^12.0", + "doctrine/coding-standard": "^13.0", + "eliashaeussler/phpunit-attributes": "^1.7", "mikey179/vfsstream": "~1.2", "mockery/mockery": "~1.6.0", "phpspec/prophecy-phpunit": "^2.0", @@ -738,7 +743,7 @@ "phpstan/phpstan": "^1.8", "phpstan/phpstan-webmozart-assert": "^1.2", "phpunit/phpunit": "^10.0", - "psalm/phar": "^5.24", + "psalm/phar": "^6.0", "rector/rector": "^1.0.0", "squizlabs/php_codesniffer": "^3.8" }, @@ -750,6 +755,9 @@ } }, "autoload": { + "files": [ + "src/php-parser/Modifiers.php" + ], "psr-4": { "phpDocumentor\\": "src/phpDocumentor" } @@ -768,9 +776,9 @@ ], "support": { "issues": "https://github.com/phpDocumentor/Reflection/issues", - "source": "https://github.com/phpDocumentor/Reflection/tree/6.1.0" + "source": "https://github.com/phpDocumentor/Reflection/tree/6.3.0" }, - "time": "2024-11-22T15:11:54+00:00" + "time": "2025-06-06T13:39:18+00:00" }, { "name": "phpdocumentor/reflection-common", @@ -827,16 +835,16 @@ }, { "name": "phpdocumentor/reflection-docblock", - "version": "5.6.1", + "version": "5.6.2", "source": { "type": "git", "url": "https://github.com/phpDocumentor/ReflectionDocBlock.git", - "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8" + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", - "reference": "e5e784149a09bd69d9a5e3b01c5cbd2e2bd653d8", + "url": "https://api.github.com/repos/phpDocumentor/ReflectionDocBlock/zipball/92dde6a5919e34835c506ac8c523ef095a95ed62", + "reference": "92dde6a5919e34835c506ac8c523ef095a95ed62", "shasum": "" }, "require": { @@ -885,9 +893,9 @@ "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.1" + "source": "https://github.com/phpDocumentor/ReflectionDocBlock/tree/5.6.2" }, - "time": "2024-12-07T09:39:29+00:00" + "time": "2025-04-13T19:20:35+00:00" }, { "name": "phpdocumentor/type-resolver", @@ -1148,16 +1156,16 @@ }, { "name": "symfony/deprecation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/deprecation-contracts.git", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6" + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", - "reference": "74c71c939a79f7d5bf3c1ce9f5ea37ba0114c6f6", + "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/63afe740e99a13ba87ec199bb07bbdee937a5b62", + "reference": "63afe740e99a13ba87ec199bb07bbdee937a5b62", "shasum": "" }, "require": { @@ -1170,7 +1178,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -1195,7 +1203,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.6.0" }, "funding": [ { @@ -1211,23 +1219,24 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/polyfill-mbstring", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341" + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/85181ba99b2345b0ef10ce42ecac37612d9fd341", - "reference": "85181ba99b2345b0ef10ce42ecac37612d9fd341", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { @@ -1275,7 +1284,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -1291,20 +1300,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { "name": "symfony/polyfill-php80", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8" + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", - "reference": "60328e362d4c2c802a54fcbf04f9d3fb892b4cf8", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { @@ -1355,7 +1364,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" }, "funding": [ { @@ -1371,20 +1380,20 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { "name": "symfony/translation", - "version": "v6.4.19", + "version": "v6.4.22", "source": { "type": "git", "url": "https://github.com/symfony/translation.git", - "reference": "3b9bf9f33997c064885a7bfc126c14b9daa0e00e" + "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/3b9bf9f33997c064885a7bfc126c14b9daa0e00e", - "reference": "3b9bf9f33997c064885a7bfc126c14b9daa0e00e", + "url": "https://api.github.com/repos/symfony/translation/zipball/7e3b3b7146c6fab36ddff304a8041174bf6e17ad", + "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad", "shasum": "" }, "require": { @@ -1450,7 +1459,7 @@ "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/translation/tree/v6.4.19" + "source": "https://github.com/symfony/translation/tree/v6.4.22" }, "funding": [ { @@ -1466,20 +1475,20 @@ "type": "tidelift" } ], - "time": "2025-02-13T10:18:43+00:00" + "time": "2025-05-29T07:06:44+00:00" }, { "name": "symfony/translation-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/translation-contracts.git", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c" + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/4667ff3bd513750603a09c8dedbea942487fb07c", - "reference": "4667ff3bd513750603a09c8dedbea942487fb07c", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", "shasum": "" }, "require": { @@ -1492,7 +1501,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -1528,7 +1537,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" }, "funding": [ { @@ -1544,7 +1553,7 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-27T08:32:26+00:00" }, { "name": "voku/portable-ascii", @@ -1682,16 +1691,16 @@ "packages-dev": [ { "name": "brianium/paratest", - "version": "v7.4.8", + "version": "v7.3.1", "source": { "type": "git", "url": "https://github.com/paratestphp/paratest.git", - "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b" + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/cf16fcbb9b8107a7df6b97e497fc91e819774d8b", - "reference": "cf16fcbb9b8107a7df6b97e497fc91e819774d8b", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", "shasum": "" }, "require": { @@ -1699,30 +1708,32 @@ "ext-pcre": "*", "ext-reflection": "*", "ext-simplexml": "*", - "fidry/cpu-core-counter": "^1.2.0", - "jean85/pretty-package-versions": "^2.0.6", - "php": "~8.2.0 || ~8.3.0 || ~8.4.0", - "phpunit/php-code-coverage": "^10.1.16", + "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", + "jean85/pretty-package-versions": "^2.0.5", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "phpunit/php-code-coverage": "^10.1.7", "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0.0", - "phpunit/phpunit": "^10.5.36", - "sebastian/environment": "^6.1.0", - "symfony/console": "^6.4.7 || ^7.1.5", - "symfony/process": "^6.4.7 || ^7.1.5" + "phpunit/php-timer": "^6.0", + "phpunit/phpunit": "^10.4.2", + "sebastian/environment": "^6.0.1", + "symfony/console": "^6.3.4 || ^7.0.0", + "symfony/process": "^6.3.4 || ^7.0.0" }, "require-dev": { "doctrine/coding-standard": "^12.0.0", "ext-pcov": "*", "ext-posix": "*", - "phpstan/phpstan": "^1.12.6", - "phpstan/phpstan-deprecation-rules": "^1.2.1", - "phpstan/phpstan-phpunit": "^1.4.0", - "phpstan/phpstan-strict-rules": "^1.6.1", - "squizlabs/php_codesniffer": "^3.10.3", - "symfony/filesystem": "^6.4.3 || ^7.1.5" + "infection/infection": "^0.27.6", + "phpstan/phpstan": "^1.10.40", + "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpstan/phpstan-strict-rules": "^1.5.2", + "squizlabs/php_codesniffer": "^3.7.2", + "symfony/filesystem": "^6.3.1 || ^7.0.0" }, "bin": [ "bin/paratest", + "bin/paratest.bat", "bin/paratest_for_phpstorm" ], "type": "library", @@ -1759,7 +1770,7 @@ ], "support": { "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.4.8" + "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" }, "funding": [ { @@ -1771,7 +1782,7 @@ "type": "paypal" } ], - "time": "2024-10-15T12:45:19+00:00" + "time": "2023-10-31T09:24:17+00:00" }, { "name": "clue/ndjson-react", @@ -2326,16 +2337,16 @@ }, { "name": "filp/whoops", - "version": "2.17.0", + "version": "2.18.2", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e" + "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/075bc0c26631110584175de6523ab3f1652eb28e", - "reference": "075bc0c26631110584175de6523ab3f1652eb28e", + "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3", "shasum": "" }, "require": { @@ -2385,7 +2396,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.17.0" + "source": "https://github.com/filp/whoops/tree/2.18.2" }, "funding": [ { @@ -2393,20 +2404,20 @@ "type": "github" } ], - "time": "2025-01-25T12:00:00+00:00" + "time": "2025-06-11T20:42:19+00:00" }, { "name": "friendsofphp/php-cs-fixer", - "version": "v3.70.2", + "version": "v3.75.0", "source": { "type": "git", "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "1ca468270efbb75ce0c7566a79cca8ea2888584d" + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/1ca468270efbb75ce0c7566a79cca8ea2888584d", - "reference": "1ca468270efbb75ce0c7566a79cca8ea2888584d", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c", + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c", "shasum": "" }, "require": { @@ -2414,6 +2425,7 @@ "composer/semver": "^3.4", "composer/xdebug-handler": "^3.0.3", "ext-filter": "*", + "ext-hash": "*", "ext-json": "*", "ext-tokenizer": "*", "fidry/cpu-core-counter": "^1.2", @@ -2436,18 +2448,18 @@ "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.5", - "infection/infection": "^0.29.10", - "justinrainbow/json-schema": "^5.3 || ^6.0", + "facile-it/paraunit": "^1.3.1 || ^2.6", + "infection/infection": "^0.29.14", + "justinrainbow/json-schema": "^5.3 || ^6.2", "keradus/cli-executor": "^2.1", "mikey179/vfsstream": "^1.6.12", "php-coveralls/php-coveralls": "^2.7", "php-cs-fixer/accessible-object": "^1.1", "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.7", - "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.0", - "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.0" + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" }, "suggest": { "ext-dom": "For handling output formats in XML", @@ -2488,7 +2500,7 @@ ], "support": { "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.70.2" + "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.75.0" }, "funding": [ { @@ -2496,24 +2508,24 @@ "type": "github" } ], - "time": "2025-03-03T21:07:23+00:00" + "time": "2025-03-31T18:40:42+00:00" }, { "name": "hamcrest/hamcrest-php", - "version": "v2.0.1", + "version": "v2.1.1", "source": { "type": "git", "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3" + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", - "reference": "8c3d0a3f6af734494ad8f6fbbee0ba92422859f3", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", "shasum": "" }, "require": { - "php": "^5.3|^7.0|^8.0" + "php": "^7.4|^8.0" }, "replace": { "cordoval/hamcrest-php": "*", @@ -2521,8 +2533,8 @@ "kodova/hamcrest-php": "*" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0" + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" }, "type": "library", "extra": { @@ -2545,22 +2557,22 @@ ], "support": { "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.0.1" + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" }, - "time": "2020-07-09T08:09:16+00:00" + "time": "2025-04-30T06:54:44+00:00" }, { "name": "jean85/pretty-package-versions", - "version": "2.1.0", + "version": "2.1.1", "source": { "type": "git", "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10" + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", - "reference": "3c4e5f62ba8d7de1734312e4fff32f67a8daaf10", + "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", + "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", "shasum": "" }, "require": { @@ -2570,8 +2582,9 @@ "require-dev": { "friendsofphp/php-cs-fixer": "^3.2", "jean85/composer-provided-replaced-stub-package": "^1.0", - "phpstan/phpstan": "^1.4", + "phpstan/phpstan": "^2.0", "phpunit/phpunit": "^7.5|^8.5|^9.6", + "rector/rector": "^2.0", "vimeo/psalm": "^4.3 || ^5.0" }, "type": "library", @@ -2604,9 +2617,9 @@ ], "support": { "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.0" + "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" }, - "time": "2024-11-18T16:19:46+00:00" + "time": "2025-03-19T14:43:43+00:00" }, { "name": "mockery/mockery", @@ -2693,16 +2706,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.13.0", + "version": "1.13.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414" + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/024473a478be9df5fdaca2c793f2232fe788e414", - "reference": "024473a478be9df5fdaca2c793f2232fe788e414", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/1720ddd719e16cf0db4eb1c6eca108031636d46c", + "reference": "1720ddd719e16cf0db4eb1c6eca108031636d46c", "shasum": "" }, "require": { @@ -2741,7 +2754,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.13.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.13.1" }, "funding": [ { @@ -2749,42 +2762,44 @@ "type": "tidelift" } ], - "time": "2025-02-12T12:17:51+00:00" + "time": "2025-04-29T12:36:36+00:00" }, { "name": "nunomaduro/collision", - "version": "v8.5.0", + "version": "v7.12.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/collision.git", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5" + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/collision/zipball/f5c101b929c958e849a633283adff296ed5f38f5", - "reference": "f5c101b929c958e849a633283adff296ed5f38f5", + "url": "https://api.github.com/repos/nunomaduro/collision/zipball/995245421d3d7593a6960822063bdba4f5d7cf1a", + "reference": "995245421d3d7593a6960822063bdba4f5d7cf1a", "shasum": "" }, "require": { - "filp/whoops": "^2.16.0", - "nunomaduro/termwind": "^2.1.0", - "php": "^8.2.0", - "symfony/console": "^7.1.5" + "filp/whoops": "^2.17.0", + "nunomaduro/termwind": "^1.17.0", + "php": "^8.1.0", + "symfony/console": "^6.4.17" }, "conflict": { - "laravel/framework": "<11.0.0 || >=12.0.0", - "phpunit/phpunit": "<10.5.1 || >=12.0.0" + "laravel/framework": ">=11.0.0" }, "require-dev": { - "larastan/larastan": "^2.9.8", - "laravel/framework": "^11.28.0", - "laravel/pint": "^1.18.1", - "laravel/sail": "^1.36.0", - "laravel/sanctum": "^4.0.3", - "laravel/tinker": "^2.10.0", - "orchestra/testbench-core": "^9.5.3", - "pestphp/pest": "^2.36.0 || ^3.4.0", - "sebastian/environment": "^6.1.0 || ^7.2.0" + "brianium/paratest": "^7.4.8", + "laravel/framework": "^10.48.29", + "laravel/pint": "^1.21.2", + "laravel/sail": "^1.41.0", + "laravel/sanctum": "^3.3.3", + "laravel/tinker": "^2.10.1", + "nunomaduro/larastan": "^2.10.0", + "orchestra/testbench-core": "^8.35.0", + "pestphp/pest": "^2.36.0", + "phpunit/phpunit": "^10.5.36", + "sebastian/environment": "^6.1.0", + "spatie/laravel-ignition": "^2.9.1" }, "type": "library", "extra": { @@ -2792,9 +2807,6 @@ "providers": [ "NunoMaduro\\Collision\\Adapters\\Laravel\\CollisionServiceProvider" ] - }, - "branch-alias": { - "dev-8.x": "8.x-dev" } }, "autoload": { @@ -2846,35 +2858,36 @@ "type": "patreon" } ], - "time": "2024-10-15T16:06:32+00:00" + "time": "2025-03-14T22:35:49+00:00" }, { "name": "nunomaduro/termwind", - "version": "v2.3.0", + "version": "v1.17.0", "source": { "type": "git", "url": "https://github.com/nunomaduro/termwind.git", - "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda" + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/52915afe6a1044e8b9cee1bcff836fb63acf9cda", - "reference": "52915afe6a1044e8b9cee1bcff836fb63acf9cda", + "url": "https://api.github.com/repos/nunomaduro/termwind/zipball/5369ef84d8142c1d87e4ec278711d4ece3cbf301", + "reference": "5369ef84d8142c1d87e4ec278711d4ece3cbf301", "shasum": "" }, "require": { "ext-mbstring": "*", - "php": "^8.2", - "symfony/console": "^7.1.8" + "php": "^8.1", + "symfony/console": "^6.4.15" }, "require-dev": { - "illuminate/console": "^11.33.2", + "illuminate/console": "^10.48.24", + "illuminate/support": "^10.48.24", "laravel/pint": "^1.18.2", - "mockery/mockery": "^1.6.12", "pestphp/pest": "^2.36.0", + "pestphp/pest-plugin-mock": "2.0.0", "phpstan/phpstan": "^1.12.11", "phpstan/phpstan-strict-rules": "^1.6.1", - "symfony/var-dumper": "^7.1.8", + "symfony/var-dumper": "^6.4.15", "thecodingmachine/phpstan-strict-rules": "^1.0.0" }, "type": "library", @@ -2883,9 +2896,6 @@ "providers": [ "Termwind\\Laravel\\TermwindServiceProvider" ] - }, - "branch-alias": { - "dev-2.x": "2.x-dev" } }, "autoload": { @@ -2917,7 +2927,7 @@ ], "support": { "issues": "https://github.com/nunomaduro/termwind/issues", - "source": "https://github.com/nunomaduro/termwind/tree/v2.3.0" + "source": "https://github.com/nunomaduro/termwind/tree/v1.17.0" }, "funding": [ { @@ -2933,7 +2943,7 @@ "type": "github" } ], - "time": "2024-11-21T10:39:51+00:00" + "time": "2024-11-21T10:36:35+00:00" }, { "name": "pestphp/pest", @@ -3356,16 +3366,16 @@ }, { "name": "phpbench/phpbench", - "version": "1.4.0", + "version": "1.4.1", "source": { "type": "git", "url": "https://github.com/phpbench/phpbench.git", - "reference": "4248817222514421cba466bfa7adc7d8932345d4" + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpbench/phpbench/zipball/4248817222514421cba466bfa7adc7d8932345d4", - "reference": "4248817222514421cba466bfa7adc7d8932345d4", + "url": "https://api.github.com/repos/phpbench/phpbench/zipball/78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", + "reference": "78cd98a9aa34e0f8f80ca01972a8b88d2c30194b", "shasum": "" }, "require": { @@ -3442,7 +3452,7 @@ ], "support": { "issues": "https://github.com/phpbench/phpbench/issues", - "source": "https://github.com/phpbench/phpbench/tree/1.4.0" + "source": "https://github.com/phpbench/phpbench/tree/1.4.1" }, "funding": [ { @@ -3450,20 +3460,20 @@ "type": "github" } ], - "time": "2025-01-26T19:54:45+00:00" + "time": "2025-03-12T08:01:40+00:00" }, { "name": "phpstan/phpstan", - "version": "2.1.6", + "version": "2.1.17", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c" + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", - "reference": "6eaec7c6c9e90dcfe46ad1e1ffa5171e2dab641c", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053", + "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053", "shasum": "" }, "require": { @@ -3508,7 +3518,7 @@ "type": "github" } ], - "time": "2025-02-19T15:46:42+00:00" + "time": "2025-05-21T20:55:28+00:00" }, { "name": "phpunit/php-code-coverage", @@ -5589,46 +5599,47 @@ }, { "name": "symfony/console", - "version": "v7.2.1", + "version": "v6.4.22", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3" + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/fefcc18c0f5d0efe3ab3152f15857298868dc2c3", - "reference": "fefcc18c0f5d0efe3ab3152f15857298868dc2c3", + "url": "https://api.github.com/repos/symfony/console/zipball/7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", "symfony/polyfill-mbstring": "~1.0", "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^6.4|^7.0" + "symfony/string": "^5.4|^6.0|^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" + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.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/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^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/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -5662,7 +5673,7 @@ "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v7.2.1" + "source": "https://github.com/symfony/console/tree/v6.4.22" }, "funding": [ { @@ -5678,28 +5689,28 @@ "type": "tidelift" } ], - "time": "2024-12-11T03:49:26+00:00" + "time": "2025-05-07T07:05:04+00:00" }, { "name": "symfony/event-dispatcher", - "version": "v7.2.0", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1" + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/910c5db85a5356d0fea57680defec4e99eb9c8c1", - "reference": "910c5db85a5356d0fea57680defec4e99eb9c8c1", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/event-dispatcher-contracts": "^2.5|^3" }, "conflict": { - "symfony/dependency-injection": "<6.4", + "symfony/dependency-injection": "<5.4", "symfony/service-contracts": "<2.5" }, "provide": { @@ -5708,13 +5719,13 @@ }, "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/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^6.4|^7.0" + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -5742,7 +5753,7 @@ "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.2.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" }, "funding": [ { @@ -5758,20 +5769,20 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { "name": "symfony/event-dispatcher-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f" + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/7642f5e970b672283b7823222ae8ef8bbc160b9f", - "reference": "7642f5e970b672283b7823222ae8ef8bbc160b9f", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { @@ -5785,7 +5796,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -5818,7 +5829,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { @@ -5834,29 +5845,29 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { "name": "symfony/filesystem", - "version": "v7.2.0", + "version": "v6.4.13", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb" + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/b8dce482de9d7c9fe2891155035a7248ab5c7fdb", - "reference": "b8dce482de9d7c9fe2891155035a7248ab5c7fdb", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "symfony/process": "^6.4|^7.0" + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", "autoload": { @@ -5884,7 +5895,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v7.2.0" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -5900,27 +5911,27 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:15:23+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { "name": "symfony/finder", - "version": "v7.2.2", + "version": "v6.4.17", "source": { "type": "git", "url": "https://github.com/symfony/finder.git", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb" + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/87a71856f2f56e4100373e92529eed3171695cfb", - "reference": "87a71856f2f56e4100373e92529eed3171695cfb", + "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1" }, "require-dev": { - "symfony/filesystem": "^6.4|^7.0" + "symfony/filesystem": "^6.0|^7.0" }, "type": "library", "autoload": { @@ -5948,7 +5959,7 @@ "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/finder/tree/v7.2.2" + "source": "https://github.com/symfony/finder/tree/v6.4.17" }, "funding": [ { @@ -5964,24 +5975,24 @@ "type": "tidelift" } ], - "time": "2024-12-30T19:00:17+00:00" + "time": "2024-12-29T13:51:37+00:00" }, { "name": "symfony/options-resolver", - "version": "v7.2.0", + "version": "v6.4.16", "source": { "type": "git", "url": "https://github.com/symfony/options-resolver.git", - "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50" + "reference": "368128ad168f20e22c32159b9f761e456cec0c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/7da8fbac9dcfef75ffc212235d76b2754ce0cf50", - "reference": "7da8fbac9dcfef75ffc212235d76b2754ce0cf50", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/368128ad168f20e22c32159b9f761e456cec0c78", + "reference": "368128ad168f20e22c32159b9f761e456cec0c78", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", @@ -6015,7 +6026,7 @@ "options" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v7.2.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.16" }, "funding": [ { @@ -6031,11 +6042,11 @@ "type": "tidelift" } ], - "time": "2024-11-20T11:17:29+00:00" + "time": "2024-11-20T10:57:02+00:00" }, { "name": "symfony/polyfill-ctype", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-ctype.git", @@ -6094,7 +6105,7 @@ "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -6114,7 +6125,7 @@ }, { "name": "symfony/polyfill-intl-grapheme", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-grapheme.git", @@ -6172,7 +6183,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" }, "funding": [ { @@ -6192,7 +6203,7 @@ }, { "name": "symfony/polyfill-intl-normalizer", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-intl-normalizer.git", @@ -6253,7 +6264,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" }, "funding": [ { @@ -6273,7 +6284,7 @@ }, { "name": "symfony/polyfill-php81", - "version": "v1.31.0", + "version": "v1.32.0", "source": { "type": "git", "url": "https://github.com/symfony/polyfill-php81.git", @@ -6329,7 +6340,7 @@ "shim" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.31.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" }, "funding": [ { @@ -6349,20 +6360,20 @@ }, { "name": "symfony/process", - "version": "v7.2.4", + "version": "v6.4.20", "source": { "type": "git", "url": "https://github.com/symfony/process.git", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf" + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", - "reference": "d8f411ff3c7ddc4ae9166fb388d1190a2df5b5cf", + "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", "shasum": "" }, "require": { - "php": ">=8.2" + "php": ">=8.1" }, "type": "library", "autoload": { @@ -6390,7 +6401,7 @@ "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v7.2.4" + "source": "https://github.com/symfony/process/tree/v6.4.20" }, "funding": [ { @@ -6406,20 +6417,20 @@ "type": "tidelift" } ], - "time": "2025-02-05T08:33:46+00:00" + "time": "2025-03-10T17:11:00+00:00" }, { "name": "symfony/service-contracts", - "version": "v3.5.1", + "version": "v3.6.0", "source": { "type": "git", "url": "https://github.com/symfony/service-contracts.git", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0" + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/e53260aabf78fb3d63f8d79d69ece59f80d5eda0", - "reference": "e53260aabf78fb3d63f8d79d69ece59f80d5eda0", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { @@ -6437,7 +6448,7 @@ "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.5-dev" + "dev-main": "3.6-dev" } }, "autoload": { @@ -6473,7 +6484,7 @@ "standards" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.5.1" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -6489,24 +6500,24 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:20:29+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { "name": "symfony/stopwatch", - "version": "v7.2.4", + "version": "v6.4.19", "source": { "type": "git", "url": "https://github.com/symfony/stopwatch.git", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd" + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", - "reference": "5a49289e2b308214c8b9c2fda4ea454d8b8ad7cd", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/service-contracts": "^2.5|^3" }, "type": "library", @@ -6535,7 +6546,7 @@ "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v7.2.4" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.19" }, "funding": [ { @@ -6551,24 +6562,24 @@ "type": "tidelift" } ], - "time": "2025-02-24T10:49:57+00:00" + "time": "2025-02-21T10:06:30+00:00" }, { "name": "symfony/string", - "version": "v7.2.0", + "version": "v6.4.21", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82" + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/446e0d146f991dde3e73f45f2c97a9faad773c82", - "reference": "446e0d146f991dde3e73f45f2c97a9faad773c82", + "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", "shasum": "" }, "require": { - "php": ">=8.2", + "php": ">=8.1", "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-intl-grapheme": "~1.0", "symfony/polyfill-intl-normalizer": "~1.0", @@ -6578,12 +6589,11 @@ "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/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^6.4|^7.0" + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { @@ -6622,7 +6632,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v7.2.0" + "source": "https://github.com/symfony/string/tree/v6.4.21" }, "funding": [ { @@ -6638,27 +6648,27 @@ "type": "tidelift" } ], - "time": "2024-11-13T13:31:26+00:00" + "time": "2025-04-18T15:23:29+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": { @@ -6695,9 +6705,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", @@ -6808,5 +6818,8 @@ "php": "^8.1" }, "platform-dev": {}, + "platform-overrides": { + "php": "8.1.32" + }, "plugin-api-version": "2.6.0" } From 7197714e009d1789eafde8a32c1a14ba19d888ce Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 12 Jun 2025 16:23:41 +0800 Subject: [PATCH 09/59] change SerializeBench --- benchmarks/SerializeBench.php | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/benchmarks/SerializeBench.php b/benchmarks/SerializeBench.php index 14e395b..448358c 100644 --- a/benchmarks/SerializeBench.php +++ b/benchmarks/SerializeBench.php @@ -83,7 +83,7 @@ public function setupObject(): void Revs(500), Iterations(5), BeforeMethods([ 'setupObjectCreation']), - Assert('mode(variant.time.avg) < 140 microseconds +/- 5%') + Assert('mode(variant.time.avg) < 120 microseconds +/- 5%') ] public function benchObjectCreation(): void { @@ -94,7 +94,7 @@ public function benchObjectCreation(): void Revs(5000), Iterations(5), BeforeMethods([ 'setupObjectCreation']), - Assert('mode(variant.time.avg) < 350 microseconds +/- 5%') + Assert('mode(variant.time.avg) < 300 microseconds +/- 5%') ] public function benchObjectCreationWithoutCache(): void { @@ -106,7 +106,7 @@ public function benchObjectCreationWithoutCache(): void Revs(500), Iterations(5), BeforeMethods(['setupObject']), - Assert('mode(variant.time.avg) < 80 microseconds +/- 5%') + Assert('mode(variant.time.avg) < 65 microseconds +/- 5%') ] public function benchObjectToArray(): void { @@ -117,7 +117,7 @@ public function benchObjectToArray(): void Revs(5000), Iterations(5), BeforeMethods(['setupObject']), - Assert('mode(variant.time.avg) < 270 microseconds +/- 5%') + Assert('mode(variant.time.avg) < 230 microseconds +/- 5%') ] public function benchObjectToArrayWithoutCache(): void { From 0e636399b066d3b5586599cf0d8f4efb5620f206 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 12 Jun 2025 16:43:42 +0800 Subject: [PATCH 10/59] phpstan fix --- src/Annotations/Input/InputDateFormat.php | 6 +----- src/Annotations/Output/OutputDateFormat.php | 18 +++++------------- 2 files changed, 6 insertions(+), 18 deletions(-) diff --git a/src/Annotations/Input/InputDateFormat.php b/src/Annotations/Input/InputDateFormat.php index 1a2b326..9d8b046 100755 --- a/src/Annotations/Input/InputDateFormat.php +++ b/src/Annotations/Input/InputDateFormat.php @@ -8,9 +8,7 @@ use Astral\Serialize\Support\Collections\DataCollection; use Astral\Serialize\Support\Context\InputValueContext; use Attribute; -use DateInvalidTimeZoneException; use DateTime; -use DateTimeImmutable; use DateTimeInterface; use DateTimeZone; @@ -30,9 +28,7 @@ public function match(mixed $value, DataCollection $collection, InputValueContex return is_string($value) || is_numeric($value); } - /** - * @throws DateInvalidTimeZoneException - */ + public function resolve(mixed $value, DataCollection $collection, InputValueContext $context): string|DateTime { diff --git a/src/Annotations/Output/OutputDateFormat.php b/src/Annotations/Output/OutputDateFormat.php index a727247..965bdf9 100755 --- a/src/Annotations/Output/OutputDateFormat.php +++ b/src/Annotations/Output/OutputDateFormat.php @@ -8,11 +8,10 @@ use Astral\Serialize\Support\Collections\DataCollection; use Astral\Serialize\Support\Context\OutContext; use Attribute; -use DateInvalidTimeZoneException; -use DateMalformedStringException; use DateTime; use DateTimeInterface; use DateTimeZone; +use Exception; /** * toArray 输出值为 固定日期格式 默认 YYYY-MM-DD HH:ii:ss的日期格式 @@ -31,19 +30,12 @@ public function match(mixed $value, DataCollection $collection, OutContext $cont return is_string($value) || is_numeric($value) || is_subclass_of($value, DateTimeInterface::class); } - /** - * @throws DateMalformedStringException - * @throws DateInvalidTimeZoneException - */ + public function resolve(mixed $value, DataCollection $collection, OutContext $context): string|DateTime|null { return $this->formatValue($value); } - /** - * @throws DateMalformedStringException - * @throws DateInvalidTimeZoneException - */ private function formatValue(mixed $value): ?string { $timezone = $this->timezone ? new DateTimeZone($this->timezone) : null; @@ -57,7 +49,7 @@ private function formatValue(mixed $value): ?string } /** - * @throws DateMalformedStringException + * @throws Exception */ private function formatDateTime(DateTimeInterface $dateTime, ?DateTimeZone $timezone): string { @@ -69,7 +61,7 @@ private function formatDateTime(DateTimeInterface $dateTime, ?DateTimeZone $time } /** - * @throws DateMalformedStringException + * @throws Exception */ private function formatTimestamp(int $timestamp, ?DateTimeZone $timezone): string { @@ -82,7 +74,7 @@ private function formatTimestamp(int $timestamp, ?DateTimeZone $timezone): strin } /** - * @throws DateMalformedStringException + * @throws Exception */ private function formatStringDate(string $value, ?DateTimeZone $timezone): string { From 8b5359c5841d9986521763d5df3abc21b81da4c5 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 13 Jun 2025 10:59:39 +0800 Subject: [PATCH 11/59] add openapi test --- src/OpenApi/Collections/OpenApiCollection.php | 75 +++++-------------- src/OpenApi/Handler/Handler.php | 2 - .../Storage/OpenAPI/ResponseStorage.php | 2 +- tests/Openapi/OpenApi.php | 63 +++++++++++++--- 4 files changed, 75 insertions(+), 67 deletions(-) diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index 1449a95..48633a8 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -2,7 +2,6 @@ namespace Astral\Serialize\OpenApi\Collections; -use Astral\Serialize\Enums\TypeKindEnum; use Astral\Serialize\OpenApi\Annotations\Headers; use Astral\Serialize\OpenApi\Annotations\RequestBody; use Astral\Serialize\OpenApi\Annotations\Response; @@ -52,7 +51,6 @@ public function build() : Method $openAPIMethod->withRequestBody($this->requestBody !== null ? $this->buildRequestBodyByAttribute() : $this->buildRequestBodyByParameters()); $openAPIMethod->addResponse(200, $this->buildResponse()); - return $openAPIMethod; } @@ -62,7 +60,7 @@ public function build() : Method public function buildRequestBodyByAttribute(): RequestBodyStorage { $openAPIRequestBody = new RequestBodyStorage($this->requestBody->contentType); - $schemaStorage = (new SchemaStorage())->build($this->buildRequestBodyParameterCollections($this->requestBody->className,$this->requestBody->group),$n); + $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($this->requestBody->className,$this->requestBody->group),$n); $openAPIRequestBody->withParameter($schemaStorage); return $openAPIRequestBody; } @@ -77,7 +75,7 @@ public function buildRequestBodyByParameters(): RequestBodyStorage $type = $methodParam?->getType(); $requestBodyClass = $type instanceof ReflectionNamedType ? $type->getName() : ''; if (is_subclass_of($requestBodyClass, Serialize::class)) { - $schemaStorage = (new SchemaStorage())->build($this->buildRequestBodyParameterCollections($requestBodyClass),$node); + $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($requestBodyClass),$node); $openAPIRequestBody->withParameter($schemaStorage); } @@ -89,9 +87,23 @@ public function buildRequestBodyByParameters(): RequestBodyStorage */ public function buildResponse(): ResponseStorage { + $returnClass = $this->reflectionMethod->getReturnType(); + $returnClass = $returnClass instanceof ReflectionNamedType ? $returnClass->getName() : null; + $responseClass = match(true){ + $this->response !== null => $this->response->className, + $returnClass && is_subclass_of($returnClass,Serialize::class) => $returnClass, + default => null, + }; + $responseStorage = new ResponseStorage(); - $schemaStorage = (new SchemaStorage())->build($this->buildResponseParameterCollections()); - $responseStorage->withParameter($schemaStorage); + + + if($responseClass) { + $groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default']; + $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($responseClass, $groups)); + $responseStorage->withParameter($schemaStorage); + } + return $responseStorage; } @@ -101,13 +113,12 @@ public function buildResponse(): ResponseStorage * @return array * @throws InvalidArgumentException */ - public function buildRequestBodyParameterCollections(string $className, array $groups = ['default']): array + public function buildParameterCollections(string $className, array $groups = ['default']): array { $serializeContext = ContextFactory::build($className); $serializeContext->from(); $properties = $serializeContext->getGroupCollection()->getProperties(); - $vols = []; foreach ($properties as $property){ $vol = new ParameterCollection( @@ -123,53 +134,7 @@ className: $className, if($property->getChildren()){ foreach ($property->getChildren() as $children){ $className = $children->getClassName(); - $vol->children[$className] = $this->buildRequestBodyParameterCollections($className); - } - } - - $vols[] = $vol; - } - - return $vols; - } - - /** - * @return array - * @throws InvalidArgumentException - */ - public function buildResponseParameterCollections(): array - { - $returnClass = $this->reflectionMethod->getReturnType(); - $responseClass = match(true){ - $this->response !== null => $this->response->className, - $returnClass && is_subclass_of($returnClass,Serialize::class) => $returnClass, - default => null, - }; - - if(!$responseClass){ - return []; - } - - $groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default']; - $serializeContext = ContextFactory::build($responseClass); - $serializeContext->from(); - $properties = $serializeContext->getGroupCollection()->getProperties(); - - $vols = []; - foreach ($properties as $property){ - $vol = new ParameterCollection( - className: $responseClass, - name: current($property->getOutNamesByGroups($groups,$responseClass)), - types: $property->getTypes(), - type: ParameterTypeEnum::getByTypes($property->getTypes()), - descriptions: '', - required: !$property->isNullable(), - ignore: false, - ); - - if($property->getChildren()){ - foreach ($property->getChildren() as $children){ - $vol->children[] = $this->buildRequestBodyParameterCollections($children->getClassName()); + $vol->children[$className] = $this->buildParameterCollections($className); } } diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index 2fe8b09..7a23515 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -137,8 +137,6 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi } } - - public function output(string $path): bool { return true; diff --git a/src/OpenApi/Storage/OpenAPI/ResponseStorage.php b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php index d74c959..578edd4 100755 --- a/src/OpenApi/Storage/OpenAPI/ResponseStorage.php +++ b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php @@ -8,7 +8,7 @@ class ResponseStorage implements StorageInterface { - public array $parameter; + public array $parameter = []; public function __construct( public string $contentType = 'application/json', diff --git a/tests/Openapi/OpenApi.php b/tests/Openapi/OpenApi.php index 579ad22..ec64356 100644 --- a/tests/Openapi/OpenApi.php +++ b/tests/Openapi/OpenApi.php @@ -2,7 +2,7 @@ use Astral\Serialize\Serialize; -beforeAll(function () { +beforeAll(static function () { class OtherOpenApiArrayNestedOne { @@ -31,7 +31,7 @@ class TestOpenApiRequest extends Serialize class TestOpenApiResponse extends Serialize { - public string $name; + public ?string $name; public int $id; } @@ -40,18 +40,63 @@ class TestOpenApiController{ #[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法一')] #[\Astral\Serialize\OpenApi\Annotations\Route('/test/one-action')] - public function one(TestOpenApiRequest $request) + public function one(TestOpenApiRequest $request): TestOpenApiResponse { return new TestOpenApiResponse(); } + } }); -// -it('test openapi build by class', function () { - $api = new \Astral\Serialize\OpenApi\OpenApi(); +test('OpenAPI structure is correct', function () { + + $api = new \Astral\Serialize\OpenApi\OpenApi(); $api->buildByClass(TestOpenApiController::class); - $res = $api->toString(); - var_dump($res); -}); + + $openApi = $api::$OpenAPI; + + // 顶层结构断言 + expect($openApi->openapi)->toBe('3.1.1') + ->and($openApi->info->title)->toBe('API 接口') + ->and($openApi->info->version)->toBe('1.0.0') + ->and($openApi->tags[0]->name)->toBe('接口测试'); + + // 路径是否存在 + $paths = $openApi->paths; + expect($paths)->toHaveKey('/test/one-action'); + + // 方法与标签断言 + $post = $paths['/test/one-action']['post']; + expect($post->summary)->toBe('测试方法一') + ->and($post->tags)->toContain('接口测试'); + + // 请求体断言 + $requestBody = $post->requestBody; + expect($requestBody['required'])->toBeTrue(); + $schema = $requestBody['content']['application/json']['schema']; + + // 请求字段存在 + expect($schema['properties'])->toHaveKeys(['name', 'id', 'any_array']); + + // id 字段是 oneOf 并包含 string, integer, number + $idOneOf = $schema['properties']['id']['oneOf']; + $types = array_map(static fn($item) => $item['type']->value, $idOneOf); + expect($types)->toMatchArray(['string', 'integer', 'number']); + + // any_array 是 oneOf 并包含至少一个 array 类型 + $anyArray = $schema['properties']['any_array']; + expect($anyArray['type'])->toBe('oneOf') + ->and($anyArray['oneOf'])->toBeArray() + ->and($anyArray['oneOf'][0]['type'])->toBe('array'); + + // 响应体 200 是否定义成功 + $response200 = $post->responses[200]; + expect($response200['description'])->toBe('成功'); + + $schema = $response200['content']['application/json']['schema']; + expect($schema['properties'])->toHaveKeys(['name', 'id']) + ->and($schema['required'])->toHaveCount(1) + ->and($schema['required'][0])->toBeString('id'); + +}); \ No newline at end of file From 49d5adf410457ef9ed778006bc5862ed50d48a20 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 13 Jun 2025 11:49:37 +0800 Subject: [PATCH 12/59] add openapi test --- tests/Openapi/OpenApi.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/Openapi/OpenApi.php b/tests/Openapi/OpenApi.php index ec64356..6b25c81 100644 --- a/tests/Openapi/OpenApi.php +++ b/tests/Openapi/OpenApi.php @@ -1,5 +1,6 @@ buildByClass(TestOpenApiController::class); $openApi = $api::$OpenAPI; From 8a8eb3e5c2067675be64b662a7576b3e0388b9cc Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 13 Jun 2025 16:27:44 +0800 Subject: [PATCH 13/59] add customerRoute --- .openapi.php | 12 +++++ src/{OpenApi => }/OpenApi.php | 12 +++-- src/OpenApi/Handler/Handler.php | 2 +- .../Storage/OpenAPI/RequestBodyStorage.php | 3 +- tests/Openapi/OpenApi.php | 3 +- tests/Openapi/RouteOrewriteOpenApi.php | 46 +++++++++++++++++++ 6 files changed, 70 insertions(+), 8 deletions(-) create mode 100644 .openapi.php rename src/{OpenApi => }/OpenApi.php (89%) create mode 100644 tests/Openapi/RouteOrewriteOpenApi.php diff --git a/.openapi.php b/.openapi.php new file mode 100644 index 0000000..2c2c114 --- /dev/null +++ b/.openapi.php @@ -0,0 +1,12 @@ + 'API Docs', + 'description' => 'API Docs description.', + 'headers' => [ + + ], + 'service' => [ + + ], +]; diff --git a/src/OpenApi/OpenApi.php b/src/OpenApi.php similarity index 89% rename from src/OpenApi/OpenApi.php rename to src/OpenApi.php index c6b887a..6372dc8 100755 --- a/src/OpenApi/OpenApi.php +++ b/src/OpenApi.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Astral\Serialize\OpenApi; +namespace Astral\Serialize; use Astral\Serialize\OpenApi\Annotations\Headers; use Astral\Serialize\OpenApi\Annotations\RequestBody; @@ -53,13 +53,17 @@ public function buildByClass(string $className): void Headers::class => null, ]; + foreach ($methodAttributes as $methodAttribute) { - $name = $methodAttribute->getName(); - if (array_key_exists($name,$instances)) { - $instances[$name] = $methodAttribute->newInstance(); + $inst = $methodAttribute->newInstance(); + foreach (array_keys($instances) as $anchorClass) { + if ($inst instanceof $anchorClass) { + $instances[$anchorClass] = $inst; + } } } + if ($instances[Route::class] === null || $instances[Summary::class] === null) { continue; } diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index 7a23515..c109963 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -21,7 +21,7 @@ abstract class Handler implements HandleInterface public function __construct( protected readonly ParameterStorage $headerParameterStorages = new ParameterStorage() ) { - self::$OpenAPI ??= (new OpenAPI())->withApiInfo(new ApiInfo('API 接口','')); + self::$OpenAPI ??= (new OpenAPI())->withApiInfo(new ApiInfo('API Doc','')); } /** diff --git a/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php index 5b74f85..1444711 100755 --- a/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php +++ b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php @@ -6,13 +6,14 @@ use Astral\Serialize\OpenApi\Enum\ContentTypeEnum; use Astral\Serialize\OpenApi\Storage\StorageInterface; +use stdClass; /** * 参数配置 */ class RequestBodyStorage implements StorageInterface { - public array $parameters; + public array|stdClass $parameters = []; public function __construct( public ContentTypeEnum $contentType = ContentTypeEnum::JSON, diff --git a/tests/Openapi/OpenApi.php b/tests/Openapi/OpenApi.php index 6b25c81..e93ddaf 100644 --- a/tests/Openapi/OpenApi.php +++ b/tests/Openapi/OpenApi.php @@ -1,6 +1,6 @@ openapi)->toBe('3.1.1') - ->and($openApi->info->title)->toBe('API 接口') ->and($openApi->info->version)->toBe('1.0.0') ->and($openApi->tags[0]->name)->toBe('接口测试'); diff --git a/tests/Openapi/RouteOrewriteOpenApi.php b/tests/Openapi/RouteOrewriteOpenApi.php new file mode 100644 index 0000000..4ad287f --- /dev/null +++ b/tests/Openapi/RouteOrewriteOpenApi.php @@ -0,0 +1,46 @@ +buildByClass(TestCustomerRouteController::class); + + $openApi = $api::$OpenAPI; + + // 路径是否存在 + $paths = $openApi->paths; + expect($paths)->toHaveKey('/test/customer-route'); + +}); \ No newline at end of file From 2797635d9440859a8c5e8dadd1626a16d09170ea Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 13 Jun 2025 18:08:31 +0800 Subject: [PATCH 14/59] add config --- src/OpenApi/Handler/Config.php | 48 +++++++++++++++++++++++++++++++++ src/OpenApi/Handler/Handler.php | 15 +++++++++++ 2 files changed, 63 insertions(+) create mode 100755 src/OpenApi/Handler/Config.php diff --git a/src/OpenApi/Handler/Config.php b/src/OpenApi/Handler/Config.php new file mode 100755 index 0000000..42d22bb --- /dev/null +++ b/src/OpenApi/Handler/Config.php @@ -0,0 +1,48 @@ +withApiInfo(new ApiInfo('API Doc','')); } + public function rootPath(): string + { + return dirname(__DIR__, 3); + } + + public function config() + { + $path = $this->rootPath().'/.openapi.php'; + if(is_file($path)){ + return include $path; + } + + return include dirname(__DIR__, 3).'/.openapi.php'; + } + /** * 构建OpenApi结构文档 * From b3bfaa41ba5a68961eeb2a9422295be64de90159 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Mon, 16 Jun 2025 15:11:41 +0800 Subject: [PATCH 15/59] add config --- .openapi.php | 13 +++++++++++- src/OpenApi.php | 2 -- src/OpenApi/Handler/Config.php | 15 +++++++------ src/OpenApi/Handler/HandleInterface.php | 1 - src/OpenApi/Handler/Handler.php | 21 +++++++++++-------- src/OpenApi/Storage/OpenAPI/OpenAPI.php | 2 +- .../Storage/OpenAPI/ServersStorage.php | 13 ++++++++++-- 7 files changed, 45 insertions(+), 22 deletions(-) diff --git a/.openapi.php b/.openapi.php index 2c2c114..9b018aa 100644 --- a/.openapi.php +++ b/.openapi.php @@ -1,12 +1,23 @@ 'API Docs', + 'description' => 'API Docs description.', + + /** + * 向全局头部参数存储中添加一个的头部参数。 + * @param string $name + * @param string $example + * @param string $description + */ 'headers' => [ ], - 'service' => [ + 'service' => [ + new ServersStorage('http://127.0.0.1','默认环境'), ], ]; diff --git a/src/OpenApi.php b/src/OpenApi.php index 6372dc8..1fa4e92 100755 --- a/src/OpenApi.php +++ b/src/OpenApi.php @@ -53,7 +53,6 @@ public function buildByClass(string $className): void Headers::class => null, ]; - foreach ($methodAttributes as $methodAttribute) { $inst = $methodAttribute->newInstance(); foreach (array_keys($instances) as $anchorClass) { @@ -63,7 +62,6 @@ public function buildByClass(string $className): void } } - if ($instances[Route::class] === null || $instances[Summary::class] === null) { continue; } diff --git a/src/OpenApi/Handler/Config.php b/src/OpenApi/Handler/Config.php index 42d22bb..802826c 100755 --- a/src/OpenApi/Handler/Config.php +++ b/src/OpenApi/Handler/Config.php @@ -15,8 +15,7 @@ class Config { - - public static $config; + public static array $config; public static function rootPath(): string { @@ -29,12 +28,11 @@ public static function build() return self::$config; } + self::$config = include dirname(__DIR__, 3).'/.openapi.php'; + $path = self::rootPath().'/.openapi.php'; if(is_file($path)){ - self::$config = include $path; - } - else{ - self::$config = include dirname(__DIR__, 3).'/.openapi.php'; + self::$config = array_merge(self::$config,include $path); } return self::$config; @@ -45,4 +43,9 @@ public static function get($key) return self::build()[$key] ?? ''; } + public static function has($key): bool + { + return isset(self::build()[$key]); + } + } diff --git a/src/OpenApi/Handler/HandleInterface.php b/src/OpenApi/Handler/HandleInterface.php index ee9f5ba..302a2a5 100755 --- a/src/OpenApi/Handler/HandleInterface.php +++ b/src/OpenApi/Handler/HandleInterface.php @@ -6,7 +6,6 @@ interface HandleInterface { - public function output(string $path): bool; public function toString(): string; } diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index b88a982..d08e7cf 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -15,13 +15,22 @@ abstract class Handler implements HandleInterface { - /** @var OpenAPI */ - public static OpenAPI $OpenAPI; + /** @var OpenAPI|null */ + public static ?OpenAPI $OpenAPI = null; public function __construct( protected readonly ParameterStorage $headerParameterStorages = new ParameterStorage() ) { - self::$OpenAPI ??= (new OpenAPI())->withApiInfo(new ApiInfo('API Doc','')); + + self::$OpenAPI ??= (new OpenAPI()) + ->withApiInfo(new ApiInfo(Config::get('title'), Config::get('description'))) + ->withServers(Config::get('service')); + + if(Config::has('headers')) { + foreach (Config::get('headers') as $header) { + $this->headerParameterStorages->addHeaderProperties($header['name'], $header['description'], $header['example']); + } + } } public function rootPath(): string @@ -114,7 +123,6 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi // 如果是子目录,则递归,并拼接命名空间 if (is_dir($path)) { - // 例如,如果当前命名空间是 "App": // 子目录 "Http" 则新的命名空间为 "App\Http" $newNamespace = $namespace !== '' ? ($namespace . '\\' . $file) : $file; @@ -152,11 +160,6 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi } } - public function output(string $path): bool - { - return true; - } - /** * @throws JsonException */ diff --git a/src/OpenApi/Storage/OpenAPI/OpenAPI.php b/src/OpenApi/Storage/OpenAPI/OpenAPI.php index 11e066c..8ad8567 100755 --- a/src/OpenApi/Storage/OpenAPI/OpenAPI.php +++ b/src/OpenApi/Storage/OpenAPI/OpenAPI.php @@ -10,7 +10,6 @@ class OpenAPI implements StorageInterface { - public string $openapi = '3.1.1'; public ApiInfo $info; @@ -34,6 +33,7 @@ public function withApiInfo(ApiInfo $apiInfo): self return $this; } + public function withServers(array $servers): self { $this->servers = $servers; diff --git a/src/OpenApi/Storage/OpenAPI/ServersStorage.php b/src/OpenApi/Storage/OpenAPI/ServersStorage.php index db2f062..93cd827 100755 --- a/src/OpenApi/Storage/OpenAPI/ServersStorage.php +++ b/src/OpenApi/Storage/OpenAPI/ServersStorage.php @@ -12,8 +12,17 @@ class ServersStorage implements StorageInterface public function __construct( public string $url, public string $description, - public array|stdClass|null $variables = null + public array|stdClass $variables = new stdClass(), ) { - $this->variables = $this->variables ?: new stdClass(); + + } + + public function addVariable(string $name, $description, $default = ''): static + { + $this->variables = $this->variables instanceof stdClass::class ? [] : $this->variables; + + $this->variables[$name] = ['default' => $default, 'description'=> $description]; + + return $this; } } From 60ec382d69ed3b8d7feff58bc8689accd90793a6 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Mon, 16 Jun 2025 15:15:27 +0800 Subject: [PATCH 16/59] add config --- src/OpenApi/Storage/OpenAPI/ServersStorage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenApi/Storage/OpenAPI/ServersStorage.php b/src/OpenApi/Storage/OpenAPI/ServersStorage.php index 93cd827..cfab890 100755 --- a/src/OpenApi/Storage/OpenAPI/ServersStorage.php +++ b/src/OpenApi/Storage/OpenAPI/ServersStorage.php @@ -19,7 +19,7 @@ public function __construct( public function addVariable(string $name, $description, $default = ''): static { - $this->variables = $this->variables instanceof stdClass::class ? [] : $this->variables; + $this->variables = $this->variables instanceof stdClass ? [] : $this->variables; $this->variables[$name] = ['default' => $default, 'description'=> $description]; From f84b001f8061d2e31b01eb1356dd9c6e3a70fdfb Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Wed, 18 Jun 2025 10:14:20 +0800 Subject: [PATCH 17/59] add openapi Optional values by enums --- composer.json | 6 +- composer.lock | 4178 ++++++++--------- docs/zh/README.md | 52 + src/OpenApi/Annotations/RequestBody.php | 2 +- src/OpenApi/Bin/openapi | 10 + src/OpenApi/Collections/OpenApiCollection.php | 4 +- src/OpenApi/Enum/ParameterTypeEnum.php | 14 +- src/OpenApi/Handler/Handler.php | 15 - src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 27 +- src/OpenApi/Web/Index.php | 12 + 10 files changed, 2209 insertions(+), 2111 deletions(-) create mode 100644 src/OpenApi/Bin/openapi create mode 100644 src/OpenApi/Web/Index.php diff --git a/composer.json b/composer.json index 7a18e2b..a221a3d 100755 --- a/composer.json +++ b/composer.json @@ -22,7 +22,8 @@ "phpdocumentor/reflection-docblock": "^5.5", "phpdocumentor/reflection": "^6.0", "fakerphp/faker": "^1.23", - "psr/simple-cache": "^3.0" + "psr/simple-cache": "^3.0", + "symfony/console": "^6.4" }, "require-dev" : { "phpstan/phpstan": "^2.0.2", @@ -38,6 +39,9 @@ "Astral\\Benchmarks\\": "benchmarks/" } }, + "bin": [ + "src/OpenApi/bin/art" + ], "config": { "allow-plugins": { "pestphp/pest-plugin": true diff --git a/composer.lock b/composer.lock index b453161..80ba5cc 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "a5a7da27a45194d7e04046a062ca1490", + "content-hash": "6a9a46d6b2d0f3759c5c4de4bcc10d39", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -1154,6 +1154,100 @@ }, "time": "2021-10-29T13:26:27+00:00" }, + { + "name": "symfony/console", + "version": "v6.4.22", + "source": { + "type": "git", + "url": "https://github.com/symfony/console.git", + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/symfony/console/zipball/7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", + "shasum": "" + }, + "require": { + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" + }, + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^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/v6.4.22" + }, + "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-05-07T07:05:04+00:00" + }, { "name": "symfony/deprecation-contracts", "version": "v3.6.0", @@ -1222,28 +1316,27 @@ "time": "2024-09-25T14:21:43+00:00" }, { - "name": "symfony/polyfill-mbstring", + "name": "symfony/polyfill-ctype", "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "ext-iconv": "*", "php": ">=7.2" }, "provide": { - "ext-mbstring": "*" + "ext-ctype": "*" }, "suggest": { - "ext-mbstring": "For best performance" + "ext-ctype": "For best performance" }, "type": "library", "extra": { @@ -1257,7 +1350,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" + "Symfony\\Polyfill\\Ctype\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -1266,25 +1359,24 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", "keywords": [ "compatibility", - "mbstring", + "ctype", "polyfill", - "portable", - "shim" + "portable" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -1300,25 +1392,28 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/polyfill-php80", + "name": "symfony/polyfill-intl-grapheme", "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { "php": ">=7.2" }, + "suggest": { + "ext-intl": "For best performance" + }, "type": "library", "extra": { "thanks": { @@ -1331,21 +1426,14 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ "MIT" ], "authors": [ - { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, { "name": "Nicolas Grekas", "email": "p@tchwork.com" @@ -1355,16 +1443,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "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-php80/tree/v1.32.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" }, "funding": [ { @@ -1380,66 +1470,44 @@ "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/translation", - "version": "v6.4.22", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/7e3b3b7146c6fab36ddff304a8041174bf6e17ad", - "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" - }, - "conflict": { - "symfony/config": "<5.4", - "symfony/console": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<5.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<5.4", - "symfony/yaml": "<5.4" - }, - "provide": { - "symfony/translation-implementation": "2.3|3.0" + "php": ">=7.2" }, - "require-dev": { - "nikic/php-parser": "^4.18|^5.0", - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/finder": "^5.4|^6.0|^7.0", - "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/intl": "^5.4|^6.0|^7.0", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^5.4|^6.0|^7.0" + "suggest": { + "ext-intl": "For best performance" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { "files": [ - "Resources/functions.php" + "bootstrap.php" ], "psr-4": { - "Symfony\\Component\\Translation\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1448,18 +1516,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": "Provides tools to internationalize your application", + "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/translation/tree/v6.4.22" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" }, "funding": [ { @@ -1475,42 +1551,46 @@ "type": "tidelift" } ], - "time": "2025-05-29T07:06:44+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/translation-contracts", - "version": "v3.6.0", + "name": "symfony/polyfill-mbstring", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { - "php": ">=8.1" + "ext-iconv": "*", + "php": ">=7.2" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" + "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\\Contracts\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + "Symfony\\Polyfill\\Mbstring\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1526,18 +1606,17 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to translation", + "description": "Symfony polyfill for the Mbstring extension", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "mbstring", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -1553,36 +1632,42 @@ "type": "tidelift" } ], - "time": "2024-09-27T08:32:26+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "voku/portable-ascii", - "version": "2.0.3", + "name": "symfony/polyfill-php80", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { - "php": ">=7.0.0" - }, - "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" - }, - "suggest": { - "ext-intl": "Use Intl for transliterator_transliterate() support" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "voku\\": "src/voku/" - } + "Symfony\\Polyfill\\Php80\\": "" + }, + "classmap": [ + "Resources/stubs" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1590,80 +1675,84 @@ ], "authors": [ { - "name": "Lars Moelleken", - "homepage": "https://www.moelleken.org/" + "name": "Ion Bazan", + "email": "ion.bazan@gmail.com" + }, + { + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", - "homepage": "https://github.com/voku/portable-ascii", + "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", + "homepage": "https://symfony.com", "keywords": [ - "ascii", - "clean", - "php" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/voku", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://opencollective.com/portable-ascii", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-21T01:49:47+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "webmozart/assert", - "version": "1.11.0", + "name": "symfony/service-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" + "php": ">=8.1", + "psr/container": "^1.1|^2.0", + "symfony/deprecation-contracts": "^2.5|^3" }, "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" - }, - "require-dev": { - "phpunit/phpunit": "^8.5.13" + "ext-psr": "<1.1|>=2" }, "type": "library", "extra": { + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, "branch-alias": { - "dev-master": "1.10-dev" + "dev-main": "3.6-dev" } }, "autoload": { "psr-4": { - "Webmozart\\Assert\\": "src/" - } + "Symfony\\Contracts\\Service\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1671,78 +1760,85 @@ ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Assertions to validate method input/output with nice error messages.", + "description": "Generic abstractions related to writing services", + "homepage": "https://symfony.com", "keywords": [ - "assert", - "check", - "validate" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, - "time": "2022-06-03T18:03:27+00:00" - } - ], - "packages-dev": [ + "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": "brianium/paratest", - "version": "v7.3.1", + "name": "symfony/string", + "version": "v6.4.21", "source": { "type": "git", - "url": "https://github.com/paratestphp/paratest.git", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" + "url": "https://github.com/symfony/string.git", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", + "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-simplexml": "*", - "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", - "jean85/pretty-package-versions": "^2.0.5", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "phpunit/php-code-coverage": "^10.1.7", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0", - "phpunit/phpunit": "^10.4.2", - "sebastian/environment": "^6.0.1", - "symfony/console": "^6.3.4 || ^7.0.0", - "symfony/process": "^6.3.4 || ^7.0.0" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-intl-grapheme": "~1.0", + "symfony/polyfill-intl-normalizer": "~1.0", + "symfony/polyfill-mbstring": "~1.0" + }, + "conflict": { + "symfony/translation-contracts": "<2.5" }, "require-dev": { - "doctrine/coding-standard": "^12.0.0", - "ext-pcov": "*", - "ext-posix": "*", - "infection/infection": "^0.27.6", - "phpstan/phpstan": "^1.10.40", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "squizlabs/php_codesniffer": "^3.7.2", - "symfony/filesystem": "^6.3.1 || ^7.0.0" + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, - "bin": [ - "bin/paratest", - "bin/paratest.bat", - "bin/paratest_for_phpstorm" - ], "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "ParaTest\\": [ - "src/" - ] - } + "Symfony\\Component\\String\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1750,67 +1846,102 @@ ], "authors": [ { - "name": "Brian Scaturro", - "email": "scaturrob@gmail.com", - "role": "Developer" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" }, { - "name": "Filippo Tessarotto", - "email": "zoeslam@gmail.com", - "role": "Developer" + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "Parallel testing for PHP", - "homepage": "https://github.com/paratestphp/paratest", + "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": [ - "concurrent", - "parallel", - "phpunit", - "testing" + "grapheme", + "i18n", + "string", + "unicode", + "utf-8", + "utf8" ], "support": { - "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" + "source": "https://github.com/symfony/string/tree/v6.4.21" }, "funding": [ { - "url": "https://github.com/sponsors/Slamdunk", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://paypal.me/filippotessarotto", - "type": "paypal" + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2023-10-31T09:24:17+00:00" + "time": "2025-04-18T15:23:29+00:00" }, { - "name": "clue/ndjson-react", - "version": "v1.3.0", + "name": "symfony/translation", + "version": "v6.4.22", "source": { "type": "git", - "url": "https://github.com/clue/reactphp-ndjson.git", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" + "url": "https://github.com/symfony/translation.git", + "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", + "url": "https://api.github.com/repos/symfony/translation/zipball/7e3b3b7146c6fab36ddff304a8041174bf6e17ad", + "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad", "shasum": "" }, "require": { - "php": ">=5.3", - "react/stream": "^1.2" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" + }, + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" + }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" }, "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", - "react/event-loop": "^1.2" + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "Clue\\React\\NDJson\\": "src/" - } + "Symfony\\Component\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1818,76 +1949,69 @@ ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "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" - ], + "description": "Provides tools to internationalize your application", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/clue/reactphp-ndjson/issues", - "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" + "source": "https://github.com/symfony/translation/tree/v6.4.22" }, "funding": [ { - "url": "https://clue.engineering/support", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/clue", + "url": "https://github.com/fabpot", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "type": "tidelift" } ], - "time": "2022-12-23T10:58:28+00:00" + "time": "2025-05-29T07:06:44+00:00" }, { - "name": "composer/pcre", - "version": "3.3.2", + "name": "symfony/translation-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", "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" + "php": ">=8.1" }, "type": "library", "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] + "thanks": { + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" }, "branch-alias": { - "dev-main": "3.x-dev" + "dev-main": "3.6-dev" } }, "autoload": { "psr-4": { - "Composer\\Pcre\\": "src" - } + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1895,68 +2019,70 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Nicolas Grekas", + "email": "p@tchwork.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "description": "Generic abstractions related to translation", + "homepage": "https://symfony.com", "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" }, "funding": [ { - "url": "https://packagist.com", + "url": "https://symfony.com/sponsor", "type": "custom" }, { - "url": "https://github.com/composer", + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-11-12T16:29:46+00:00" + "time": "2024-09-27T08:32:26+00:00" }, { - "name": "composer/semver", - "version": "3.4.3", + "name": "voku/portable-ascii", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "php": ">=7.0.0" }, "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" }, + "type": "library", "autoload": { "psr-4": { - "Composer\\Semver\\": "src" + "voku\\": "src/voku/" } }, "notification-url": "https://packagist.org/downloads/", @@ -1965,77 +2091,79 @@ ], "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" + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" } ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", "keywords": [ - "semantic", - "semver", - "validation", - "versioning" + "ascii", + "clean", + "php" ], "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { - "url": "https://packagist.com", + "url": "https://www.paypal.me/moelleken", "type": "custom" }, { - "url": "https://github.com/composer", + "url": "https://github.com/voku", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", "type": "tidelift" } ], - "time": "2024-09-19T14:15:21+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { - "name": "composer/xdebug-handler", - "version": "3.0.5", + "name": "webmozart/assert", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" }, "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + "phpunit/phpunit": "^8.5.13" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.10-dev" + } + }, "autoload": { "psr-4": { - "Composer\\XdebugHandler\\": "src" + "Webmozart\\Assert\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2044,71 +2172,77 @@ ], "authors": [ { - "name": "John Stevenson", - "email": "john-stevenson@blueyonder.co.uk" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Restarts a process without Xdebug.", + "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ - "Xdebug", - "performance" + "assert", + "check", + "validate" ], "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" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.0" }, - "funding": [ - { - "url": "https://packagist.com", - "type": "custom" - }, - { - "url": "https://github.com/composer", - "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/composer/composer", - "type": "tidelift" - } - ], - "time": "2024-05-06T16:37:16+00:00" - }, + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ { - "name": "doctrine/annotations", - "version": "2.0.2", + "name": "brianium/paratest", + "version": "v7.3.1", "source": { "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" + "url": "https://github.com/paratestphp/paratest.git", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", - "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", "shasum": "" }, "require": { - "doctrine/lexer": "^2 || ^3", - "ext-tokenizer": "*", - "php": "^7.2 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", + "jean85/pretty-package-versions": "^2.0.5", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "phpunit/php-code-coverage": "^10.1.7", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-timer": "^6.0", + "phpunit/phpunit": "^10.4.2", + "sebastian/environment": "^6.0.1", + "symfony/console": "^6.3.4 || ^7.0.0", + "symfony/process": "^6.3.4 || ^7.0.0" }, "require-dev": { - "doctrine/cache": "^2.0", - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.10.28", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6.4 || ^7", - "vimeo/psalm": "^4.30 || ^5.14" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "infection/infection": "^0.27.6", + "phpstan/phpstan": "^1.10.40", + "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpstan/phpstan-strict-rules": "^1.5.2", + "squizlabs/php_codesniffer": "^3.7.2", + "symfony/filesystem": "^6.3.1 || ^7.0.0" }, + "bin": [ + "bin/paratest", + "bin/paratest.bat", + "bin/paratest_for_phpstorm" + ], "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + "ParaTest\\": [ + "src/" + ] } }, "notification-url": "https://packagist.org/downloads/", @@ -2117,67 +2251,66 @@ ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" }, { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" } ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", "keywords": [ - "annotations", - "docblock", - "parser" + "concurrent", + "parallel", + "phpunit", + "testing" ], "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.2" + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" }, - "time": "2024-09-05T10:17:24+00:00" + "funding": [ + { + "url": "https://github.com/sponsors/Slamdunk", + "type": "github" + }, + { + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" + } + ], + "time": "2023-10-31T09:24:17+00:00" }, { - "name": "doctrine/lexer", - "version": "3.0.1", + "name": "clue/ndjson-react", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", "shasum": "" }, "require": { - "php": "^8.1" + "php": ">=5.3", + "react/stream": "^1.2" }, "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.21" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" }, "type": "library", "autoload": { "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" + "Clue\\React\\NDJson\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2186,71 +2319,75 @@ ], "authors": [ { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" + "name": "Christian Lück", + "email": "christian@clue.engineering" } ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", + "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", + "homepage": "https://github.com/clue/reactphp-ndjson", "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" + "NDJSON", + "json", + "jsonlines", + "newline", + "reactphp", + "streaming" ], "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.1" + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" }, "funding": [ { - "url": "https://www.doctrine-project.org/sponsorship.html", + "url": "https://clue.engineering/support", "type": "custom" }, { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" + "url": "https://github.com/clue", + "type": "github" } ], - "time": "2024-02-05T11:56:58+00:00" + "time": "2022-12-23T10:58:28+00:00" }, { - "name": "evenement/evenement", - "version": "v3.0.2", + "name": "composer/pcre", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/igorw/evenement.git", - "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", - "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { - "php": ">=7.0" + "php": "^7.4 || ^8.0" + }, + "conflict": { + "phpstan/phpstan": "<1.11.10" }, "require-dev": { - "phpunit/phpunit": "^9 || ^6" + "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": { - "Evenement\\": "src/" + "Composer\\Pcre\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2259,53 +2396,68 @@ ], "authors": [ { - "name": "Igor Wiedler", - "email": "igor@wiedler.ch" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Événement is a very simple event dispatching library for PHP", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "event-dispatcher", - "event-emitter" + "PCRE", + "preg", + "regex", + "regular expression" ], "support": { - "issues": "https://github.com/igorw/evenement/issues", - "source": "https://github.com/igorw/evenement/tree/v3.0.2" + "issues": "https://github.com/composer/pcre/issues", + "source": "https://github.com/composer/pcre/tree/3.3.2" }, - "time": "2023-08-08T05:53:35+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-11-12T16:29:46+00:00" }, { - "name": "fidry/cpu-core-counter", - "version": "1.2.0", + "name": "composer/semver", + "version": "3.4.3", "source": { "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { - "php": "^7.2 || ^8.0" + "php": "^5.3.2 || ^7.0 || ^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": "^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.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2314,63 +2466,77 @@ ], "authors": [ { - "name": "Théo FIDRY", - "email": "theo.fidry@gmail.com" + "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": "Tiny utility to get the number of CPU cores.", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "CPU", - "core" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/theofidry/cpu-core-counter/issues", - "source": "https://github.com/theofidry/cpu-core-counter/tree/1.2.0" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" }, "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-08-06T10:04:20+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { - "name": "filp/whoops", - "version": "2.18.2", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/filp/whoops.git", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "php": "^7.1 || ^8.0", - "psr/log": "^1.0.1 || ^2.0 || ^3.0" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "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" + "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": "2.7-dev" - } - }, "autoload": { "psr-4": { - "Whoops\\": "src/Whoops/" + "Composer\\XdebugHandler\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2379,103 +2545,72 @@ ], "authors": [ { - "name": "Filipe Dobreira", - "homepage": "https://github.com/filp", - "role": "Developer" + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "php error handling for cool kids", - "homepage": "https://filp.github.io/whoops/", + "description": "Restarts a process without Xdebug.", "keywords": [ - "error", - "exception", - "handling", - "library", - "throwable", - "whoops" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.2" + "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://github.com/denis-sokolov", + "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": "2025-06-11T20:42:19+00:00" + "time": "2024-05-06T16:37:16+00:00" }, { - "name": "friendsofphp/php-cs-fixer", - "version": "v3.75.0", + "name": "doctrine/annotations", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", - "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c" + "url": "https://github.com/doctrine/annotations.git", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c", - "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { - "clue/ndjson-react": "^1.0", - "composer/semver": "^3.4", - "composer/xdebug-handler": "^3.0.3", - "ext-filter": "*", - "ext-hash": "*", - "ext-json": "*", + "doctrine/lexer": "^2 || ^3", "ext-tokenizer": "*", - "fidry/cpu-core-counter": "^1.2", - "php": "^7.4 || ^8.0", - "react/child-process": "^0.6.5", - "react/event-loop": "^1.0", - "react/promise": "^2.0 || ^3.0", - "react/socket": "^1.0", - "react/stream": "^1.0", - "sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0", - "symfony/console": "^5.4 || ^6.4 || ^7.0", - "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", - "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", - "symfony/finder": "^5.4 || ^6.4 || ^7.0", - "symfony/options-resolver": "^5.4 || ^6.4 || ^7.0", - "symfony/polyfill-mbstring": "^1.31", - "symfony/polyfill-php80": "^1.31", - "symfony/polyfill-php81": "^1.31", - "symfony/process": "^5.4 || ^6.4 || ^7.2", - "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "facile-it/paraunit": "^1.3.1 || ^2.6", - "infection/infection": "^0.29.14", - "justinrainbow/json-schema": "^5.3 || ^6.2", - "keradus/cli-executor": "^2.1", - "mikey179/vfsstream": "^1.6.12", - "php-coveralls/php-coveralls": "^2.7", - "php-cs-fixer/accessible-object": "^1.1", - "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", - "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", - "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", - "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", - "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.10.28", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" }, "suggest": { - "ext-dom": "For handling output formats in XML", - "ext-mbstring": "For handling non-UTF8 characters." + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, - "bin": [ - "php-cs-fixer" - ], - "type": "application", + "type": "library", "autoload": { "psr-4": { - "PhpCsFixer\\": "src/" - }, - "exclude-from-classmap": [ - "src/Fixer/Internal/*" - ] + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2483,119 +2618,140 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, { - "name": "Dariusz Rumiński", - "email": "dariusz.ruminski@gmail.com" + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "A tool to automatically fix PHP code style", + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ - "Static code analysis", - "fixer", - "standards", - "static analysis" + "annotations", + "docblock", + "parser" ], "support": { - "issues": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/issues", - "source": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer/tree/v3.75.0" + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "funding": [ - { - "url": "https://github.com/keradus", - "type": "github" - } - ], - "time": "2025-03-31T18:40:42+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { - "name": "hamcrest/hamcrest-php", - "version": "v2.1.1", + "name": "doctrine/lexer", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/hamcrest/hamcrest-php.git", - "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", - "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": "^7.4|^8.0" - }, - "replace": { - "cordoval/hamcrest-php": "*", - "davedevelopment/hamcrest-php": "*", - "kodova/hamcrest-php": "*" + "php": "^8.1" }, "require-dev": { - "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", - "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "2.1-dev" - } - }, "autoload": { - "classmap": [ - "hamcrest" - ] + "psr-4": { + "Doctrine\\Common\\Lexer\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], - "description": "This is the PHP port of Hamcrest Matchers", + "authors": [ + { + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" + } + ], + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ - "test" + "annotations", + "docblock", + "lexer", + "parser", + "php" ], "support": { - "issues": "https://github.com/hamcrest/hamcrest-php/issues", - "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, - "time": "2025-04-30T06:54:44+00:00" + "funding": [ + { + "url": "https://www.doctrine-project.org/sponsorship.html", + "type": "custom" + }, + { + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" + } + ], + "time": "2024-02-05T11:56:58+00:00" }, { - "name": "jean85/pretty-package-versions", - "version": "2.1.1", + "name": "evenement/evenement", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/Jean85/pretty-package-versions.git", - "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a" + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Jean85/pretty-package-versions/zipball/4d7aa5dab42e2a76d99559706022885de0e18e1a", - "reference": "4d7aa5dab42e2a76d99559706022885de0e18e1a", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", "shasum": "" }, "require": { - "composer-runtime-api": "^2.1.0", - "php": "^7.4|^8.0" + "php": ">=7.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" + "phpunit/phpunit": "^9 || ^6" }, "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.x-dev" - } - }, "autoload": { "psr-4": { - "Jean85\\": "src/" + "Evenement\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2604,81 +2760,426 @@ ], "authors": [ { - "name": "Alessandro Lai", - "email": "alessandro.lai85@gmail.com" + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" } ], - "description": "A library to get pretty versions strings of installed dependencies", + "description": "Événement is a very simple event dispatching library for PHP", "keywords": [ - "composer", - "package", - "release", - "versions" + "event-dispatcher", + "event-emitter" ], "support": { - "issues": "https://github.com/Jean85/pretty-package-versions/issues", - "source": "https://github.com/Jean85/pretty-package-versions/tree/2.1.1" + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.2" }, - "time": "2025-03-19T14:43:43+00:00" + "time": "2023-08-08T05:53:35+00:00" }, { - "name": "mockery/mockery", - "version": "1.6.12", + "name": "fidry/cpu-core-counter", + "version": "1.2.0", "source": { "type": "git", - "url": "https://github.com/mockery/mockery.git", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", - "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", "shasum": "" }, "require": { - "hamcrest/hamcrest-php": "^2.0.1", - "lib-pcre": ">=7.0", - "php": ">=7.3" - }, - "conflict": { - "phpunit/phpunit": "<8.0" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^8.5 || ^9.6.17", - "symplify/easy-coding-standard": "^12.1.14" + "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" }, "type": "library", "autoload": { - "files": [ - "library/helpers.php", - "library/Mockery.php" - ], "psr-4": { - "Mockery\\": "library/Mockery" + "Fidry\\CpuCoreCounter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Pádraic Brady", - "email": "padraic.brady@gmail.com", - "homepage": "https://github.com/padraic", - "role": "Author" - }, - { - "name": "Dave Marshall", - "email": "dave.marshall@atstsolutions.co.uk", - "homepage": "https://davedevelopment.co.uk", - "role": "Developer" - }, - { - "name": "Nathanael Esayeas", - "email": "nathanael.esayeas@protonmail.com", - "homepage": "https://github.com/ghostwriter", - "role": "Lead Developer" + "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.2.0" + }, + "funding": [ + { + "url": "https://github.com/theofidry", + "type": "github" + } + ], + "time": "2024-08-06T10:04:20+00:00" + }, + { + "name": "filp/whoops", + "version": "2.18.2", + "source": { + "type": "git", + "url": "https://github.com/filp/whoops.git", + "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "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.2" + }, + "funding": [ + { + "url": "https://github.com/denis-sokolov", + "type": "github" + } + ], + "time": "2025-06-11T20:42:19+00:00" + }, + { + "name": "friendsofphp/php-cs-fixer", + "version": "v3.75.0", + "source": { + "type": "git", + "url": "https://github.com/PHP-CS-Fixer/PHP-CS-Fixer.git", + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/PHP-CS-Fixer/PHP-CS-Fixer/zipball/399a128ff2fdaf4281e4e79b755693286cdf325c", + "reference": "399a128ff2fdaf4281e4e79b755693286cdf325c", + "shasum": "" + }, + "require": { + "clue/ndjson-react": "^1.0", + "composer/semver": "^3.4", + "composer/xdebug-handler": "^3.0.3", + "ext-filter": "*", + "ext-hash": "*", + "ext-json": "*", + "ext-tokenizer": "*", + "fidry/cpu-core-counter": "^1.2", + "php": "^7.4 || ^8.0", + "react/child-process": "^0.6.5", + "react/event-loop": "^1.0", + "react/promise": "^2.0 || ^3.0", + "react/socket": "^1.0", + "react/stream": "^1.0", + "sebastian/diff": "^4.0 || ^5.1 || ^6.0 || ^7.0", + "symfony/console": "^5.4 || ^6.4 || ^7.0", + "symfony/event-dispatcher": "^5.4 || ^6.4 || ^7.0", + "symfony/filesystem": "^5.4 || ^6.4 || ^7.0", + "symfony/finder": "^5.4 || ^6.4 || ^7.0", + "symfony/options-resolver": "^5.4 || ^6.4 || ^7.0", + "symfony/polyfill-mbstring": "^1.31", + "symfony/polyfill-php80": "^1.31", + "symfony/polyfill-php81": "^1.31", + "symfony/process": "^5.4 || ^6.4 || ^7.2", + "symfony/stopwatch": "^5.4 || ^6.4 || ^7.0" + }, + "require-dev": { + "facile-it/paraunit": "^1.3.1 || ^2.6", + "infection/infection": "^0.29.14", + "justinrainbow/json-schema": "^5.3 || ^6.2", + "keradus/cli-executor": "^2.1", + "mikey179/vfsstream": "^1.6.12", + "php-coveralls/php-coveralls": "^2.7", + "php-cs-fixer/accessible-object": "^1.1", + "php-cs-fixer/phpunit-constraint-isidenticalstring": "^1.6", + "php-cs-fixer/phpunit-constraint-xmlmatchesxsd": "^1.6", + "phpunit/phpunit": "^9.6.22 || ^10.5.45 || ^11.5.12", + "symfony/var-dumper": "^5.4.48 || ^6.4.18 || ^7.2.3", + "symfony/yaml": "^5.4.45 || ^6.4.18 || ^7.2.3" + }, + "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.75.0" + }, + "funding": [ + { + "url": "https://github.com/keradus", + "type": "github" + } + ], + "time": "2025-03-31T18:40:42+00:00" + }, + { + "name": "hamcrest/hamcrest-php", + "version": "v2.1.1", + "source": { + "type": "git", + "url": "https://github.com/hamcrest/hamcrest-php.git", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/hamcrest/hamcrest-php/zipball/f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "reference": "f8b1c0173b22fa6ec77a81fe63e5b01eba7e6487", + "shasum": "" + }, + "require": { + "php": "^7.4|^8.0" + }, + "replace": { + "cordoval/hamcrest-php": "*", + "davedevelopment/hamcrest-php": "*", + "kodova/hamcrest-php": "*" + }, + "require-dev": { + "phpunit/php-file-iterator": "^1.4 || ^2.0 || ^3.0", + "phpunit/phpunit": "^4.8.36 || ^5.7 || ^6.5 || ^7.0 || ^8.0 || ^9.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "2.1-dev" + } + }, + "autoload": { + "classmap": [ + "hamcrest" + ] + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "description": "This is the PHP port of Hamcrest Matchers", + "keywords": [ + "test" + ], + "support": { + "issues": "https://github.com/hamcrest/hamcrest-php/issues", + "source": "https://github.com/hamcrest/hamcrest-php/tree/v2.1.1" + }, + "time": "2025-04-30T06:54:44+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": "mockery/mockery", + "version": "1.6.12", + "source": { + "type": "git", + "url": "https://github.com/mockery/mockery.git", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/mockery/mockery/zipball/1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "reference": "1f4efdd7d3beafe9807b08156dfcb176d18f1699", + "shasum": "" + }, + "require": { + "hamcrest/hamcrest-php": "^2.0.1", + "lib-pcre": ">=7.0", + "php": ">=7.3" + }, + "conflict": { + "phpunit/phpunit": "<8.0" + }, + "require-dev": { + "phpunit/phpunit": "^8.5 || ^9.6.17", + "symplify/easy-coding-standard": "^12.1.14" + }, + "type": "library", + "autoload": { + "files": [ + "library/helpers.php", + "library/Mockery.php" + ], + "psr-4": { + "Mockery\\": "library/Mockery" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "BSD-3-Clause" + ], + "authors": [ + { + "name": "Pádraic Brady", + "email": "padraic.brady@gmail.com", + "homepage": "https://github.com/padraic", + "role": "Author" + }, + { + "name": "Dave Marshall", + "email": "dave.marshall@atstsolutions.co.uk", + "homepage": "https://davedevelopment.co.uk", + "role": "Developer" + }, + { + "name": "Nathanael Esayeas", + "email": "nathanael.esayeas@protonmail.com", + "homepage": "https://github.com/ghostwriter", + "role": "Lead Developer" } ], "description": "Mockery is a simple yet flexible PHP mock object framework", @@ -3881,317 +4382,93 @@ "sebastian/object-enumerator": "^5.0.0", "sebastian/recursion-context": "^5.0.0", "sebastian/type": "^4.0.0", - "sebastian/version": "^4.0.1" - }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" - }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.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/10.5.36" - }, - "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", - "type": "tidelift" - } - ], - "time": "2024-10-08T15:36:51+00:00" - }, - { - "name": "psr/cache", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Cache\\": "src/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for caching libraries", - "keywords": [ - "cache", - "psr", - "psr-6" - ], - "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" - }, - "time": "2021-02-03T23:26:27+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": "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" + "sebastian/version": "^4.0.1" }, - "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" }, + "bin": [ + "phpunit" + ], "type": "library", - "autoload": { - "psr-4": { - "React\\Cache\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "10.5-dev" } }, + "autoload": { + "files": [ + "src/Framework/Assert/Functions.php" + ], + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "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/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Async, Promise-based cache interface for ReactPHP", + "description": "The PHP Unit Testing framework.", + "homepage": "https://phpunit.de/", "keywords": [ - "cache", - "caching", - "promise", - "reactphp" + "phpunit", + "testing", + "xunit" ], "support": { - "issues": "https://github.com/reactphp/cache/issues", - "source": "https://github.com/reactphp/cache/tree/v1.2.0" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/10.5.36" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://phpunit.de/sponsors.html", + "type": "custom" + }, + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit", + "type": "tidelift" } ], - "time": "2022-11-30T15:59:55+00:00" + "time": "2024-10-08T15:36:51+00:00" }, { - "name": "react/child-process", - "version": "v0.6.6", + "name": "psr/cache", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", "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" + "php": ">=8.0.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "React\\ChildProcess\\": "src/" + "Psr\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4200,73 +4477,47 @@ ], "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/" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "Event-driven library for executing child processes with ReactPHP.", + "description": "Common interface for caching libraries", "keywords": [ - "event-driven", - "process", - "reactphp" + "cache", + "psr", + "psr-6" ], "support": { - "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + "source": "https://github.com/php-fig/cache/tree/3.0.0" }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2025-01-01T16:37:48+00:00" + "time": "2021-02-03T23:26:27+00:00" }, { - "name": "react/dns", - "version": "v1.13.0", + "name": "psr/event-dispatcher", + "version": "1.0.0", "source": { "type": "git", - "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + "url": "https://github.com/php-fig/event-dispatcher.git", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", + "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", "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" + "php": ">=7.2.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, "autoload": { "psr-4": { - "React\\Dns\\": "src/" + "Psr\\EventDispatcher\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4275,145 +4526,97 @@ ], "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/" + "name": "PHP-FIG", + "homepage": "http://www.php-fig.org/" } ], - "description": "Async DNS resolver for ReactPHP", + "description": "Standard interfaces for event handling.", "keywords": [ - "async", - "dns", - "dns-resolver", - "reactphp" + "events", + "psr", + "psr-14" ], "support": { - "issues": "https://github.com/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" + "issues": "https://github.com/php-fig/event-dispatcher/issues", + "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2024-06-13T14:18:03+00:00" + "time": "2019-01-08T18:20:26+00:00" }, { - "name": "react/event-loop", - "version": "v1.5.0", + "name": "psr/log", + "version": "3.0.2", "source": { "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "url": "https://github.com/php-fig/log.git", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", "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" + "php": ">=8.0.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, "autoload": { "psr-4": { - "React\\EventLoop\\": "src/" + "Psr\\Log\\": "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/" - }, + ], + "authors": [ { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" } ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", "keywords": [ - "asynchronous", - "event-loop" + "log", + "psr", + "psr-3" ], "support": { - "issues": "https://github.com/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "source": "https://github.com/php-fig/log/tree/3.0.2" }, - "funding": [ - { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" - } - ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2024-09-11T13:17:53+00:00" }, { - "name": "react/promise", - "version": "v3.2.0", + "name": "react/cache", + "version": "v1.2.0", "source": { "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + "url": "https://github.com/reactphp/cache.git", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", + "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", "shasum": "" }, "require": { - "php": ">=7.1.0" + "php": ">=5.3.0", + "react/promise": "^3.0 || ^2.0 || ^1.1" }, "require-dev": { - "phpstan/phpstan": "1.10.39 || 1.4.10", - "phpunit/phpunit": "^9.6 || ^7.5" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, "type": "library", "autoload": { - "files": [ - "src/functions_include.php" - ], "psr-4": { - "React\\Promise\\": "src/" + "React\\Cache\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4421,11 +4624,6 @@ "MIT" ], "authors": [ - { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" - }, { "name": "Christian Lück", "email": "christian@clue.engineering", @@ -4436,20 +4634,27 @@ "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 lightweight implementation of CommonJS Promises/A for PHP", + "description": "Async, Promise-based cache interface for ReactPHP", "keywords": [ + "cache", + "caching", "promise", - "promises" + "reactphp" ], "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.2.0" + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" }, "funding": [ { @@ -4457,40 +4662,37 @@ "type": "open_collective" } ], - "time": "2024-05-24T10:39:05+00:00" + "time": "2022-11-30T15:59:55+00:00" }, { - "name": "react/socket", - "version": "v1.16.0", + "name": "react/child-process", + "version": "v0.6.6", "source": { "type": "git", - "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + "url": "https://github.com/reactphp/child-process.git", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "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/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" + "react/socket": "^1.16", + "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" }, "type": "library", "autoload": { "psr-4": { - "React\\Socket\\": "src/" + "React\\ChildProcess\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4519,17 +4721,15 @@ "homepage": "https://cboden.dev/" } ], - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", + "description": "Event-driven library for executing child processes with ReactPHP.", "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" + "event-driven", + "process", + "reactphp" ], "support": { - "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.6" }, "funding": [ { @@ -4537,35 +4737,37 @@ "type": "open_collective" } ], - "time": "2024-07-26T10:38:09+00:00" + "time": "2025-01-01T16:37:48+00:00" }, { - "name": "react/stream", - "version": "v1.4.0", + "name": "react/dns", + "version": "v1.13.0", "source": { "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", "shasum": "" }, "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" + "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": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + "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\\Stream\\": "src/" + "React\\Dns\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4594,411 +4796,340 @@ "homepage": "https://cboden.dev/" } ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "description": "Async DNS resolver for 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": "2.0.1", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.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/2.0.1" - }, - "funding": [ - { - "url": "https://github.com/sebastianbergmann", - "type": "github" - } - ], - "time": "2024-03-02T07:12:49+00:00" - }, - { - "name": "sebastian/code-unit", - "version": "2.0.0", - "source": { - "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", - "shasum": "" - }, - "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.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" - } + "async", + "dns", + "dns-resolver", + "reactphp" ], - "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", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2023-02-03T06:58:43+00:00" + "time": "2024-06-13T14:18:03+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", + "name": "react/event-loop", + "version": "v1.5.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=5.3.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.0-dev" - } + "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": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "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": "Looks up which function or method a line of code belongs to", - "homepage": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", + "keywords": [ + "asynchronous", + "event-loop" + ], "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/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2023-11-13T13:48:05+00:00" }, { - "name": "sebastian/comparator", - "version": "5.0.3", + "name": "react/promise", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "files": [ + "src/functions_include.php" + ], + "psr-4": { + "React\\Promise\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" }, { - "name": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Volker Dusch", - "email": "github@wallbash.com" + "name": "Cees-Jan Kiewiet", + "email": "reactphp@ceesjankiewiet.nl", + "homepage": "https://wyrihaximus.net/" }, { - "name": "Bernhard Schussek", - "email": "bschussek@2bepublished.at" + "name": "Chris Boden", + "email": "cboden@gmail.com", + "homepage": "https://cboden.dev/" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "comparator", - "compare", - "equality" + "promise", + "promises" ], "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.3" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2024-05-24T10:39:05+00:00" }, { - "name": "sebastian/complexity", - "version": "3.2.0", + "name": "react/socket", + "version": "v1.16.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.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": { - "phpunit/phpunit": "^10.0" + "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-main": "3.2-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Socket\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "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": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "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/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/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2024-07-26T10:38:09+00:00" }, { - "name": "sebastian/diff", - "version": "5.1.1", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "shasum": "" }, "require": { - "php": ">=8.1" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.1-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Stream\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "name": "Christian Lück", + "email": "christian@clue.engineering", + "homepage": "https://clue.engineering/" }, { - "name": "Kore Nordmann", - "email": "mail@kore-nordmann.de" + "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": "Diff implementation", - "homepage": "https://github.com/sebastianbergmann/diff", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", "keywords": [ - "diff", - "udiff", - "unidiff", - "unified diff" + "event-driven", + "io", + "non-blocking", + "pipe", + "reactphp", + "readable", + "stream", + "writable" ], "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/reactphp/stream/issues", + "source": "https://github.com/reactphp/stream/tree/v1.4.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", - "type": "github" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2024-06-11T12:45:25+00:00" }, { - "name": "sebastian/environment", - "version": "6.1.0", + "name": "sebastian/cli-parser", + "version": "2.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", "shasum": "" }, "require": { @@ -5007,13 +5138,10 @@ "require-dev": { "phpunit/phpunit": "^10.0" }, - "suggest": { - "ext-posix": "*" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -5028,20 +5156,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides functionality to handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], + "description": "Library for parsing CLI options", + "homepage": "https://github.com/sebastianbergmann/cli-parser", "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.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/2.0.1" }, "funding": [ { @@ -5049,26 +5173,24 @@ "type": "github" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2024-03-02T07:12:49+00:00" }, { - "name": "sebastian/exporter", - "version": "5.1.2", + "name": "sebastian/code-unit", + "version": "2.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" @@ -5076,7 +5198,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -5091,35 +5213,15 @@ "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" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], + "description": "Collection of value objects that represent the PHP code units", + "homepage": "https://github.com/sebastianbergmann/code-unit", "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" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { @@ -5127,35 +5229,32 @@ "type": "github" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { - "name": "sebastian/global-state", - "version": "6.0.2", + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "php": ">=8.1" }, "require-dev": { - "ext-dom": "*", "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -5173,15 +5272,11 @@ "email": "sebastian@phpunit.de" } ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], + "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/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { @@ -5189,33 +5284,36 @@ "type": "github" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "2.0.2", + "name": "sebastian/comparator", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.5" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -5230,16 +5328,32 @@ "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": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "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/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" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" }, "funding": [ { @@ -5247,26 +5361,25 @@ "type": "github" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2024-10-18T14:56:07+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "5.0.0", + "name": "sebastian/complexity", + "version": "3.2.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "shasum": "" }, "require": { - "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" @@ -5274,7 +5387,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "3.2-dev" } }, "autoload": { @@ -5289,14 +5402,16 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Traverses array structures and object graphs to enumerate all referenced objects", - "homepage": "https://github.com/sebastianbergmann/object-enumerator/", + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", - "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" + "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" }, "funding": [ { @@ -5304,32 +5419,33 @@ "type": "github" } ], - "time": "2023-02-03T07:08:32+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { - "name": "sebastian/object-reflector", - "version": "3.0.0", + "name": "sebastian/diff", + "version": "5.1.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -5345,13 +5461,24 @@ { "name": "Sebastian Bergmann", "email": "sebastian@phpunit.de" + }, + { + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Allows reflection of object attributes, including inherited and non-public ones", - "homepage": "https://github.com/sebastianbergmann/object-reflector/", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", + "keywords": [ + "diff", + "udiff", + "unidiff", + "unified diff" + ], "support": { - "issues": "https://github.com/sebastianbergmann/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "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" }, "funding": [ { @@ -5359,20 +5486,20 @@ "type": "github" } ], - "time": "2023-02-03T07:06:18+00:00" + "time": "2024-03-02T07:15:17+00:00" }, { - "name": "sebastian/recursion-context", - "version": "5.0.0", + "name": "sebastian/environment", + "version": "6.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { @@ -5381,10 +5508,13 @@ "require-dev": { "phpunit/phpunit": "^10.0" }, + "suggest": { + "ext-posix": "*" + }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -5400,21 +5530,19 @@ { "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", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], "support": { - "issues": "https://github.com/sebastianbergmann/recursion-context/issues", - "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" + "issues": "https://github.com/sebastianbergmann/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -5422,24 +5550,26 @@ "type": "github" } ], - "time": "2023-02-03T07:05:40+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { - "name": "sebastian/type", - "version": "4.0.0", + "name": "sebastian/exporter", + "version": "5.1.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", "shasum": "" }, "require": { - "php": ">=8.1" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "phpunit/phpunit": "^10.0" @@ -5447,7 +5577,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -5462,15 +5592,35 @@ "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": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Collection of value objects that represent the types of the PHP type system", - "homepage": "https://github.com/sebastianbergmann/type", + "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/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "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" }, "funding": [ { @@ -5478,29 +5628,35 @@ "type": "github" } ], - "time": "2023-02-03T07:10:45+00:00" + "time": "2024-03-02T07:17:12+00:00" }, { - "name": "sebastian/version", - "version": "4.0.1", + "name": "sebastian/global-state", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" + }, + "require-dev": { + "ext-dom": "*", + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "4.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -5515,15 +5671,18 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library that helps with managing the version number of Git-hosted PHP projects", - "homepage": "https://github.com/sebastianbergmann/version", + "description": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "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" }, "funding": [ { @@ -5531,478 +5690,379 @@ "type": "github" } ], - "time": "2023-02-07T11:34:05+00:00" + "time": "2024-03-02T07:19:19+00:00" }, { - "name": "seld/jsonlint", - "version": "1.11.0", + "name": "sebastian/lines-of-code", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0 || ^8.0" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" }, "require-dev": { - "phpstan/phpstan": "^1.11", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + "phpunit/phpunit": "^10.0" }, - "bin": [ - "bin/jsonlint" - ], "type": "library", - "autoload": { - "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" + "extra": { + "branch-alias": { + "dev-main": "2.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "JSON Linter", - "keywords": [ - "json", - "linter", - "parser", - "validator" - ], + "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/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" + "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" }, "funding": [ { - "url": "https://github.com/Seldaek", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", - "type": "tidelift" } ], - "time": "2024-07-11T14:55:45+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { - "name": "symfony/console", - "version": "v6.4.22", + "name": "sebastian/object-enumerator", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", - "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^7.0" + "phpunit/phpunit": "^10.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Console\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Eases the creation of beautiful and testable command line interfaces", - "homepage": "https://symfony.com", - "keywords": [ - "cli", - "command-line", - "console", - "terminal" - ], + "description": "Traverses array structures and object graphs to enumerate all referenced objects", + "homepage": "https://github.com/sebastianbergmann/object-enumerator/", "support": { - "source": "https://github.com/symfony/console/tree/v6.4.22" + "issues": "https://github.com/sebastianbergmann/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2025-05-07T07:05:04+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v6.4.13", + "name": "sebastian/object-reflector", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", - "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/service-contracts": "<2.5" - }, - "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" + "php": ">=8.1" }, "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "phpunit/phpunit": "^10.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", - "homepage": "https://symfony.com", + "description": "Allows reflection of object attributes, including inherited and non-public ones", + "homepage": "https://github.com/sebastianbergmann/object-reflector/", "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" + "issues": "https://github.com/sebastianbergmann/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "name": "sebastian/recursion-context", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/event-dispatcher": "^1" + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "5.0-dev" } }, "autoload": { - "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" - } + "classmap": [ + "src/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" + }, + { + "name": "Adam Harvey", + "email": "aharvey@php.net" } ], - "description": "Generic abstractions related to dispatching event", - "homepage": "https://symfony.com", - "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { - "name": "symfony/filesystem", - "version": "v6.4.13", + "name": "sebastian/type", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" + "php": ">=8.1" }, "require-dev": { - "symfony/process": "^5.4|^6.4|^7.0" + "phpunit/phpunit": "^10.0" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Filesystem\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides basic utilities for the filesystem", - "homepage": "https://symfony.com", + "description": "Collection of value objects that represent the types of the PHP type system", + "homepage": "https://github.com/sebastianbergmann/type", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.4.13" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { - "name": "symfony/finder", - "version": "v6.4.17", + "name": "sebastian/version", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", - "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "shasum": "" }, "require": { "php": ">=8.1" }, - "require-dev": { - "symfony/filesystem": "^6.0|^7.0" - }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "4.0-dev" + } + }, "autoload": { - "psr-4": { - "Symfony\\Component\\Finder\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "src/" ] }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Finds files and directories via an intuitive fluent interface", - "homepage": "https://symfony.com", + "description": "Library that helps with managing the version number of Git-hosted PHP projects", + "homepage": "https://github.com/sebastianbergmann/version", "support": { - "source": "https://github.com/symfony/finder/tree/v6.4.17" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sebastianbergmann", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2024-12-29T13:51:37+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { - "name": "symfony/options-resolver", - "version": "v6.4.16", + "name": "seld/jsonlint", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "368128ad168f20e22c32159b9f761e456cec0c78" + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/368128ad168f20e22c32159b9f761e456cec0c78", - "reference": "368128ad168f20e22c32159b9f761e456cec0c78", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3" + "php": "^5.3 || ^7.0 || ^8.0" + }, + "require-dev": { + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, + "bin": [ + "bin/jsonlint" + ], "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6010,77 +6070,78 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" } ], - "description": "Provides an improved replacement for the array_replace PHP function", - "homepage": "https://symfony.com", + "description": "JSON Linter", "keywords": [ - "config", - "configuration", - "options" + "json", + "linter", + "parser", + "validator" ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.4.16" + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/Seldaek", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", "type": "tidelift" } ], - "time": "2024-11-20T10:57:02+00:00" + "time": "2024-07-11T14:55:45+00:00" }, { - "name": "symfony/polyfill-ctype", - "version": "v1.32.0", + "name": "symfony/event-dispatcher", + "version": "v6.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/service-contracts": "<2.5" }, "provide": { - "ext-ctype": "*" + "psr/event-dispatcher-implementation": "1.0", + "symfony/event-dispatcher-implementation": "2.0|3.0" }, - "suggest": { - "ext-ctype": "For best performance" + "require-dev": { + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" - } + "Symfony\\Component\\EventDispatcher\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6088,24 +6149,18 @@ ], "authors": [ { - "name": "Gert de Pagter", - "email": "BackEndTea@gmail.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for ctype functions", + "description": "Provides tools that allow your application components to communicate with each other by dispatching events and listening to them", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "ctype", - "polyfill", - "portable" - ], "support": { - "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" }, "funding": [ { @@ -6121,41 +6176,39 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", - "version": "v1.32.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=8.1", + "psr/event-dispatcher": "^1" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Grapheme\\": "" + "Symfony\\Contracts\\EventDispatcher\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -6172,18 +6225,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's grapheme_* functions", + "description": "Generic abstractions related to dispatching event", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "grapheme", - "intl", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" + "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" }, "funding": [ { @@ -6199,44 +6252,37 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "name": "symfony/filesystem", + "version": "v6.4.13", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "url": "https://github.com/symfony/filesystem.git", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, - "suggest": { - "ext-intl": "For best performance" + "require-dev": { + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } - }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "Symfony\\Component\\Filesystem\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6245,26 +6291,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "funding": [ { @@ -6280,41 +6318,35 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.32.0", + "name": "symfony/finder", + "version": "v6.4.17", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "url": "https://github.com/symfony/finder.git", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=8.1" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Component\\Finder\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6323,24 +6355,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Finds files and directories via an intuitive fluent interface", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + "source": "https://github.com/symfony/finder/tree/v6.4.17" }, "funding": [ { @@ -6356,29 +6382,30 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-29T13:51:37+00:00" }, { - "name": "symfony/process", - "version": "v6.4.20", + "name": "symfony/options-resolver", + "version": "v6.4.16", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "368128ad168f20e22c32159b9f761e456cec0c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", - "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/368128ad168f20e22c32159b9f761e456cec0c78", + "reference": "368128ad168f20e22c32159b9f761e456cec0c78", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Component\\OptionsResolver\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6398,10 +6425,15 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "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/process/tree/v6.4.20" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.16" }, "funding": [ { @@ -6417,46 +6449,41 @@ "type": "tidelift" } ], - "time": "2025-03-10T17:11:00+00:00" + "time": "2024-11-20T10:57:02+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.6.0", + "name": "symfony/polyfill-php81", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" - }, - "conflict": { - "ext-psr": "<1.1|>=2" + "php": ">=7.2" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Contracts\\Service\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, - "exclude-from-classmap": [ - "/Test/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6473,18 +6500,16 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to writing services", + "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" }, "funding": [ { @@ -6500,30 +6525,29 @@ "type": "tidelift" } ], - "time": "2025-04-25T09:37:31+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/stopwatch", - "version": "v6.4.19", + "name": "symfony/process", + "version": "v6.4.20", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" + "url": "https://github.com/symfony/process.git", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", - "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/service-contracts": "^2.5|^3" + "php": ">=8.1" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6543,10 +6567,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides a way to profile code", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/stopwatch/tree/v6.4.19" + "source": "https://github.com/symfony/process/tree/v6.4.20" }, "funding": [ { @@ -6562,46 +6586,30 @@ "type": "tidelift" } ], - "time": "2025-02-21T10:06:30+00:00" + "time": "2025-03-10T17:11:00+00:00" }, { - "name": "symfony/string", - "version": "v6.4.21", + "name": "symfony/stopwatch", + "version": "v6.4.19", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" + "url": "https://github.com/symfony/stopwatch.git", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" - }, - "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { - "files": [ - "Resources/functions.php" - ], "psr-4": { - "Symfony\\Component\\String\\": "" + "Symfony\\Component\\Stopwatch\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6613,26 +6621,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.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", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", - "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" - ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.21" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.19" }, "funding": [ { @@ -6648,7 +6648,7 @@ "type": "tidelift" } ], - "time": "2025-04-18T15:23:29+00:00" + "time": "2025-02-21T10:06:30+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", diff --git a/docs/zh/README.md b/docs/zh/README.md index cadea6d..60576c3 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -95,6 +95,58 @@ class User extends Serialize { } ``` +## 生成openapi文档 + +#### 创建Request文件 +```php +use Astral\Serialize\Serialize; + +class UserAddRequest extends Serialize { + public string $name; + public int $id; +} + +class UserDetailRequest extends Serialize { + public int $id; +} +``` + +#### 创建Repose文件 +```php +use Astral\Serialize\Serialize; + +class UserDto extends Serialize { + public string $name, + public int $id; +} +``` + +#### 创建Controller文件 +```php +use Astral\Serialize\Serialize; +use Astral\Serialize\OpenApi\Enum\MethodEnum; + +#[\Astral\Serialize\OpenApi\Annotations\Tag('用户模块管理')] +class UserController { + + #[\Astral\Serialize\OpenApi\Annotations\Summary('创建用户')] + #[\Astral\Serialize\OpenApi\Annotations\Route('/user/create')] + #[\Astral\Serialize\OpenApi\Annotations\RequestBody(UserAddRequest::class)] + #[\Astral\Serialize\OpenApi\Annotations\Response(UserDto::class)] + public function create() + { + return new UserDto(); + } + + #[\Astral\Serialize\OpenApi\Annotations\Summary('用户详情')] + #[\Astral\Serialize\OpenApi\Annotations\Route(route:'/user/detail', method: MethodEnum::GET)] + public function detail(UserDetailRequest $request): UserDto + { + return new UserDto(); + } +} +``` + ## DTO 转换 ### 类型转换 diff --git a/src/OpenApi/Annotations/RequestBody.php b/src/OpenApi/Annotations/RequestBody.php index 9323f6d..89e7ea4 100755 --- a/src/OpenApi/Annotations/RequestBody.php +++ b/src/OpenApi/Annotations/RequestBody.php @@ -14,7 +14,7 @@ public function __construct( /** @var class-string|string $className */ public string $className = '', public ContentTypeEnum $contentType = ContentTypeEnum::JSON, - public array|null $group = null + public array|null $groups = null ){ } } diff --git a/src/OpenApi/Bin/openapi b/src/OpenApi/Bin/openapi new file mode 100644 index 0000000..faf8312 --- /dev/null +++ b/src/OpenApi/Bin/openapi @@ -0,0 +1,10 @@ +#!/usr/bin/env php +run(); \ No newline at end of file diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index 48633a8..046a0b3 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -60,7 +60,7 @@ public function build() : Method public function buildRequestBodyByAttribute(): RequestBodyStorage { $openAPIRequestBody = new RequestBodyStorage($this->requestBody->contentType); - $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($this->requestBody->className,$this->requestBody->group),$n); + $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($this->requestBody->className,$this->requestBody->groups)); $openAPIRequestBody->withParameter($schemaStorage); return $openAPIRequestBody; } @@ -75,7 +75,7 @@ public function buildRequestBodyByParameters(): RequestBodyStorage $type = $methodParam?->getType(); $requestBodyClass = $type instanceof ReflectionNamedType ? $type->getName() : ''; if (is_subclass_of($requestBodyClass, Serialize::class)) { - $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($requestBodyClass),$node); + $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($requestBodyClass)); $openAPIRequestBody->withParameter($schemaStorage); } diff --git a/src/OpenApi/Enum/ParameterTypeEnum.php b/src/OpenApi/Enum/ParameterTypeEnum.php index c9cb3d4..a5a06bb 100755 --- a/src/OpenApi/Enum/ParameterTypeEnum.php +++ b/src/OpenApi/Enum/ParameterTypeEnum.php @@ -17,6 +17,7 @@ enum ParameterTypeEnum: string case ANY_OF = 'anyOf'; case ALL_OF = 'allOf'; + public function isObject(): bool { return $this === self::OBJECT; @@ -35,7 +36,7 @@ public function isOf(): bool public static function getBaseEnumByTypeKindEnum(TypeCollection $collection): ?ParameterTypeEnum { return match (true){ - $collection->kind === TypeKindEnum::STRING => self::STRING, + $collection->kind === TypeKindEnum::STRING, $collection->kind === TypeKindEnum::ENUM => self::STRING, $collection->kind === TypeKindEnum::INT => self::INTEGER, $collection->kind === TypeKindEnum::FLOAT => self::NUMBER, $collection->kind === TypeKindEnum::BOOLEAN => self::BOOLEAN, @@ -65,6 +66,17 @@ public static function getArrayAndObjectEnumBy(array $types, string $className): return null; } + public static function hasEnum(array $types): bool + { + foreach ($types as $type){ + if($type->kind === TypeKindEnum::ENUM){ + return true; + } + } + + return false; + } + /** * @param TypeCollection[] $types */ diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index d08e7cf..c6f506d 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -33,21 +33,6 @@ public function __construct( } } - public function rootPath(): string - { - return dirname(__DIR__, 3); - } - - public function config() - { - $path = $this->rootPath().'/.openapi.php'; - if(is_file($path)){ - return include $path; - } - - return include dirname(__DIR__, 3).'/.openapi.php'; - } - /** * 构建OpenApi结构文档 * diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index f5e0ab8..29002e4 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -4,6 +4,7 @@ namespace Astral\Serialize\OpenApi\Storage\OpenAPI; +use Astral\Serialize\Enums\TypeKindEnum; use Astral\Serialize\OpenApi\Collections\ParameterCollection; use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; use Astral\Serialize\OpenApi\Storage\StorageInterface; @@ -77,7 +78,7 @@ private function buildBasicPropertySchema(ParameterCollection $parameter, array $currentNode['properties'][$propertyName] = [ 'type' => $parameter->type->value, - 'description' => $parameter->descriptions, + 'description' => $this->getDescriptions($parameter), 'example' => $parameter->example, ]; @@ -144,7 +145,7 @@ private function buildNestedProperties(ParameterCollection $topParameter, array $currentNode['properties'][$propertyName] = [ 'type' => 'object', 'properties' => [], - 'description' => $topParameter->descriptions, + 'description' => $this->getDescriptions($topParameter), ]; $nestedNode = &$currentNode['properties'][$propertyName]; } @@ -156,4 +157,26 @@ private function buildNestedProperties(ParameterCollection $topParameter, array } } } + + public function getDescriptions(ParameterCollection $parameter):string + { + if(!ParameterTypeEnum::hasEnum($parameter->types)){ + return $parameter->descriptions; + } + + $descriptions = $parameter->descriptions; + + $names = []; + foreach ($parameter->types as $type) { + if (TypeKindEnum::ENUM === $type->kind && enum_exists($type->className)) { + foreach ($type->className::cases() as $case) { + $names[$case->name] = $case->name; + } + } + } + + $descriptions .= ' Optional values: :' . implode('、', $names); + + return $descriptions; + } } \ No newline at end of file diff --git a/src/OpenApi/Web/Index.php b/src/OpenApi/Web/Index.php new file mode 100644 index 0000000..eecbb44 --- /dev/null +++ b/src/OpenApi/Web/Index.php @@ -0,0 +1,12 @@ +handleByFolders()->toString(); +} catch (JsonException|ReflectionException $e) { + throw new RuntimeException($e->getMessage(),$e->getCode(),$e); +} \ No newline at end of file From 613aa9d5da331182daf0b6dfa4e19bf06f6b3a6c Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Wed, 18 Jun 2025 17:05:35 +0800 Subject: [PATCH 18/59] fix tests --- composer.json | 3 - src/OpenApi/Enum/ParameterTypeEnum.php | 20 +++- src/OpenApi/Handler/Config.php | 2 +- src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 8 +- tests/Openapi/EnumOpenApiTest.php | 99 +++++++++++++++++++ .../Openapi/{OpenApi.php => OpenApiTest.php} | 2 +- ...enApi.php => RouteOrewriteOpenApiTest.php} | 2 +- 7 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 tests/Openapi/EnumOpenApiTest.php rename tests/Openapi/{OpenApi.php => OpenApiTest.php} (97%) rename tests/Openapi/{RouteOrewriteOpenApi.php => RouteOrewriteOpenApiTest.php} (94%) diff --git a/composer.json b/composer.json index a221a3d..54983e1 100755 --- a/composer.json +++ b/composer.json @@ -39,9 +39,6 @@ "Astral\\Benchmarks\\": "benchmarks/" } }, - "bin": [ - "src/OpenApi/bin/art" - ], "config": { "allow-plugins": { "pestphp/pest-plugin": true diff --git a/src/OpenApi/Enum/ParameterTypeEnum.php b/src/OpenApi/Enum/ParameterTypeEnum.php index a5a06bb..29bf76a 100755 --- a/src/OpenApi/Enum/ParameterTypeEnum.php +++ b/src/OpenApi/Enum/ParameterTypeEnum.php @@ -98,14 +98,32 @@ public static function getByTypes(array $types): ParameterTypeEnum } $hasUnion = false; + $unionTypes = []; foreach ($types as $type){ + + if($type->kind === TypeKindEnum::ENUM){ + $unionTypes[TypeKindEnum::STRING->name] = $type; + }else{ + $unionTypes[$type->kind->name] = $type; + } + if($type->kind === TypeKindEnum::COLLECT_UNION_OBJECT){ $hasUnion = true; } } - return $hasUnion ? self::ANY_OF : self::ONE_OF; + if(!$hasUnion && count($unionTypes) === 1){ + $type = current($types)->kind; + return match (true){ + $type === TypeKindEnum::INT => self::INTEGER, + $type === TypeKindEnum::FLOAT => self::NUMBER, + $type === TypeKindEnum::BOOLEAN => self::BOOLEAN, + in_array($type, [TypeKindEnum::OBJECT, TypeKindEnum::CLASS_OBJECT, TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true) => self::ONE_OF, + default => self::STRING, + }; + } + return $hasUnion ? self::ANY_OF : self::ONE_OF; } } diff --git a/src/OpenApi/Handler/Config.php b/src/OpenApi/Handler/Config.php index 802826c..c4a21e5 100755 --- a/src/OpenApi/Handler/Config.php +++ b/src/OpenApi/Handler/Config.php @@ -15,7 +15,7 @@ class Config { - public static array $config; + public static ?array $config = null; public static function rootPath(): string { diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index 29002e4..e0868d0 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -98,10 +98,12 @@ public function buildOfProperties(ParameterCollection $topParameter, array &$cur $node = &$currentNode['properties'][$propertyName][$topParameter->type->value]; $i = 0; + $addedTypes = []; foreach ($topParameter->types as $kindType){ $type = ParameterTypeEnum::getBaseEnumByTypeKindEnum($kindType); - if($type){ - $node[$i] = ['type'=> $type]; + if ($type && !in_array($type->value, $addedTypes, true)) { + $node[$i] = ['type' => $type->value]; + $addedTypes[] = $type->value; $i++; } } @@ -175,7 +177,7 @@ public function getDescriptions(ParameterCollection $parameter):string } } - $descriptions .= ' Optional values: :' . implode('、', $names); + $descriptions .= 'Optional values:' . implode('、', $names); return $descriptions; } diff --git a/tests/Openapi/EnumOpenApiTest.php b/tests/Openapi/EnumOpenApiTest.php new file mode 100644 index 0000000..7cc5a67 --- /dev/null +++ b/tests/Openapi/EnumOpenApiTest.php @@ -0,0 +1,99 @@ +buildByClass(OpenapiEnumController::class); + + $openApi = $api::$OpenAPI; + + + $paths = $openApi->paths; + $post = $paths['/test/enum-action']['post']; + $requestBody = $post->requestBody; + $schema = $requestBody['content']['application/json']['schema']; + + expect($schema['properties']) + ->toHaveKeys([ + 'test_enum', + 'test_string_enum', + 'test_string_2_enum', + 'test_one_of_enum', + ]) + ->and($schema['properties']['test_enum'])->toMatchArray([ + 'type' => 'string', + 'description' => 'Optional values:ENUM_1、ENUM_2', + 'example' => '', + ]) + ->and($schema['properties']['test_string_enum'])->toMatchArray([ + 'type' => 'string', + 'description' => 'Optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', + 'example' => '', + ]) + ->and($schema['properties']['test_string_2_enum'])->toMatchArray([ + 'type' => 'string', + 'description' => 'Optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', + 'example' => '', + ]) + ->and($schema['properties']['test_one_of_enum'])->toMatchArray([ + 'type' => 'oneOf', + 'description' => 'Optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', + 'example' => '', + ]) + ->and($schema['properties']['test_one_of_enum']['oneOf'])->toBeArray()->toHaveCount(2) + ->and($schema['properties']['test_one_of_enum']['oneOf'][0])->toMatchArray(['type' => 'string']) + ->and($schema['properties']['test_one_of_enum']['oneOf'][1])->toMatchArray(['type' => 'integer']) + ->and($schema['required'])->toBeArray()->toEqualCanonicalizing([ + 'test_enum', + 'test_string_enum', + 'test_string_2_enum', + 'test_one_of_enum', + ]); + +}); \ No newline at end of file diff --git a/tests/Openapi/OpenApi.php b/tests/Openapi/OpenApiTest.php similarity index 97% rename from tests/Openapi/OpenApi.php rename to tests/Openapi/OpenApiTest.php index e93ddaf..15c6db9 100644 --- a/tests/Openapi/OpenApi.php +++ b/tests/Openapi/OpenApiTest.php @@ -81,7 +81,7 @@ public function one(TestOpenApiRequest $request): TestOpenApiResponse // id 字段是 oneOf 并包含 string, integer, number $idOneOf = $schema['properties']['id']['oneOf']; - $types = array_map(static fn($item) => $item['type']->value, $idOneOf); + $types = array_map(static fn($item) => $item['type'], $idOneOf); expect($types)->toMatchArray(['string', 'integer', 'number']); // any_array 是 oneOf 并包含至少一个 array 类型 diff --git a/tests/Openapi/RouteOrewriteOpenApi.php b/tests/Openapi/RouteOrewriteOpenApiTest.php similarity index 94% rename from tests/Openapi/RouteOrewriteOpenApi.php rename to tests/Openapi/RouteOrewriteOpenApiTest.php index 4ad287f..cffae5a 100644 --- a/tests/Openapi/RouteOrewriteOpenApi.php +++ b/tests/Openapi/RouteOrewriteOpenApiTest.php @@ -32,7 +32,7 @@ public function one(TestCustomerRouteRequest $request): void }); -test('OpenAPI structure is correct', function () { +test('OpenAPI customer route', function () { $api = new OpenApi(); $api->buildByClass(TestCustomerRouteController::class); From 2e99b8483c6911856d5531e0fa559022132ee8eb Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 09:58:45 +0800 Subject: [PATCH 19/59] add openapiAnnotation --- src/OpenApi/Annotations/OpenApi.php | 6 +- src/OpenApi/Collections/OpenApiCollection.php | 14 +++- .../Collections/ParameterCollection.php | 20 ++--- src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 12 +-- src/Support/Collections/DataCollection.php | 2 + tests/Openapi/AnnotationOpenApiTest.php | 75 +++++++++++++++++++ tests/Openapi/EnumOpenApiTest.php | 10 +-- 7 files changed, 115 insertions(+), 24 deletions(-) create mode 100644 tests/Openapi/AnnotationOpenApiTest.php diff --git a/src/OpenApi/Annotations/OpenApi.php b/src/OpenApi/Annotations/OpenApi.php index 20f77e2..d10c6a3 100755 --- a/src/OpenApi/Annotations/OpenApi.php +++ b/src/OpenApi/Annotations/OpenApi.php @@ -6,11 +6,13 @@ use Attribute; -#[Attribute(Attribute::TARGET_METHOD)] +#[Attribute(Attribute::TARGET_PROPERTY)] class OpenApi { + public function __construct( - public string $descriptions, + public string $description = '', + public string $example = '', ) { } } diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index 046a0b3..d33e3c9 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -3,6 +3,7 @@ namespace Astral\Serialize\OpenApi\Collections; use Astral\Serialize\OpenApi\Annotations\Headers; +use Astral\Serialize\OpenApi\Annotations\OpenApi; use Astral\Serialize\OpenApi\Annotations\RequestBody; use Astral\Serialize\OpenApi\Annotations\Response; use Astral\Serialize\OpenApi\Annotations\Route; @@ -126,7 +127,7 @@ className: $className, name: current($property->getInputNamesByGroups($groups,$className)), types: $property->getTypes(), type: ParameterTypeEnum::getByTypes($property->getTypes()), - descriptions: '', + openApiAnnotation: $this->getOpenApiAnnotation($property->getAttributes()), required: !$property->isNullable(), ignore: false, ); @@ -143,4 +144,15 @@ className: $className, return $vols; } + + public function getOpenApiAnnotation(array $attributes): OpenApi|null + { + foreach ($attributes as $attribute){ + if($attribute->getName() === OpenApi::class){ + return $attribute->newInstance(); + } + } + + return null; + } } \ No newline at end of file diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php index a6d3cc6..405c7b3 100755 --- a/src/OpenApi/Collections/ParameterCollection.php +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -4,25 +4,25 @@ namespace Astral\Serialize\OpenApi\Collections; -use Astral\Serialize\Enums\TypeKindEnum; +use Astral\Serialize\OpenApi\Annotations\OpenApi; use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; use Astral\Serialize\Support\Collections\TypeCollection; use Attribute; +use ReflectionAttribute; class ParameterCollection { public function __construct( - public string $className, - public string $name, + public string $className, + public string $name, /** @var TypeCollection[] $types */ - public array $types, - public ParameterTypeEnum $type, - public string $descriptions = '', - public mixed $example = '', - public bool $required = false, - public bool $ignore = false, + public array $types, + public ParameterTypeEnum $type, + public OpenApi|null $openApiAnnotation = null, + public bool $required = false, + public bool $ignore = false, /** @var array $children */ - public array $children = [], + public array $children = [], ){ } } diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index e0868d0..6a792c1 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -5,6 +5,7 @@ namespace Astral\Serialize\OpenApi\Storage\OpenAPI; use Astral\Serialize\Enums\TypeKindEnum; +use Astral\Serialize\OpenApi\Annotations\OpenApi; use Astral\Serialize\OpenApi\Collections\ParameterCollection; use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; use Astral\Serialize\OpenApi\Storage\StorageInterface; @@ -79,7 +80,7 @@ private function buildBasicPropertySchema(ParameterCollection $parameter, array $currentNode['properties'][$propertyName] = [ 'type' => $parameter->type->value, 'description' => $this->getDescriptions($parameter), - 'example' => $parameter->example, + 'example' => $parameter->openApiAnnotation?->example, ]; // 添加必填字段标记 @@ -162,12 +163,11 @@ private function buildNestedProperties(ParameterCollection $topParameter, array public function getDescriptions(ParameterCollection $parameter):string { + $description = $parameter->openApiAnnotation?->description ?? ''; if(!ParameterTypeEnum::hasEnum($parameter->types)){ - return $parameter->descriptions; + return $description; } - $descriptions = $parameter->descriptions; - $names = []; foreach ($parameter->types as $type) { if (TypeKindEnum::ENUM === $type->kind && enum_exists($type->className)) { @@ -177,8 +177,8 @@ public function getDescriptions(ParameterCollection $parameter):string } } - $descriptions .= 'Optional values:' . implode('、', $names); + $description .= ($description ? ' ' : '').'optional values:' . implode('、', $names); - return $descriptions; + return $description; } } \ No newline at end of file diff --git a/src/Support/Collections/DataCollection.php b/src/Support/Collections/DataCollection.php index 2f074e6..ff089b2 100644 --- a/src/Support/Collections/DataCollection.php +++ b/src/Support/Collections/DataCollection.php @@ -3,6 +3,7 @@ namespace Astral\Serialize\Support\Collections; use InvalidArgumentException; +use ReflectionAttribute; use ReflectionProperty; class DataCollection @@ -13,6 +14,7 @@ public function __construct( private readonly string $name, private readonly bool $isNullable, private readonly bool $isReadonly, + /** @var ReflectionAttribute[] */ private readonly array $attributes, private readonly mixed $defaultValue, private readonly ReflectionProperty $property, diff --git a/tests/Openapi/AnnotationOpenApiTest.php b/tests/Openapi/AnnotationOpenApiTest.php new file mode 100644 index 0000000..13993cd --- /dev/null +++ b/tests/Openapi/AnnotationOpenApiTest.php @@ -0,0 +1,75 @@ +buildByClass(AnnotationOpenApiController::class); + + $openApi = $api::$OpenAPI; + $paths = $openApi->paths; + $post = $paths['/test/description-action']['post']; + $requestBody = $post->requestBody; + $schema = $requestBody['content']['application/json']['schema']; + + expect(array_keys($schema['properties']))->toMatchArray([ + 'test_enum', + 'test_object', + 'test_example', + ]); + + $enumProp = $schema['properties']['test_enum']; + expect($enumProp['type'])->toBe('string') + ->and($enumProp['description'])->toBe('description test enum optional values:ENUM_1、ENUM_2') + ->and($enumProp['example'])->toBe(''); + + $objProp = $schema['properties']['test_object']; + expect($objProp['type'])->toBe('object') + ->and($objProp['description'])->toBe('this is object description') + ->and($objProp['example'])->toBe('1'); + + $exProp = $schema['properties']['test_example']; + expect($exProp['type'])->toBe('string') + ->and($exProp['description'])->toBe('') + ->and($exProp['example'])->toBe('abc') + ->and($schema['required'])->toMatchArray([ + 'test_enum', + 'test_object', + 'test_example', + ]); +}); diff --git a/tests/Openapi/EnumOpenApiTest.php b/tests/Openapi/EnumOpenApiTest.php index 7cc5a67..706986a 100644 --- a/tests/Openapi/EnumOpenApiTest.php +++ b/tests/Openapi/EnumOpenApiTest.php @@ -25,6 +25,7 @@ enum OpenapiUnionEnum class OpenapiEnumRequest extends Serialize { public OpenapiEnum $test_enum; + public OpenapiEnum|OpenapiUnionEnum $test_string_enum; public OpenapiEnum|OpenapiUnionEnum|string $test_string_2_enum; @@ -41,7 +42,6 @@ class OpenapiEnumController{ public function one(OpenapiEnumRequest $request): void { } - } }); @@ -68,22 +68,22 @@ public function one(OpenapiEnumRequest $request): void ]) ->and($schema['properties']['test_enum'])->toMatchArray([ 'type' => 'string', - 'description' => 'Optional values:ENUM_1、ENUM_2', + 'description' => 'optional values:ENUM_1、ENUM_2', 'example' => '', ]) ->and($schema['properties']['test_string_enum'])->toMatchArray([ 'type' => 'string', - 'description' => 'Optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', + 'description' => 'optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', 'example' => '', ]) ->and($schema['properties']['test_string_2_enum'])->toMatchArray([ 'type' => 'string', - 'description' => 'Optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', + 'description' => 'optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', 'example' => '', ]) ->and($schema['properties']['test_one_of_enum'])->toMatchArray([ 'type' => 'oneOf', - 'description' => 'Optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', + 'description' => 'optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', 'example' => '', ]) ->and($schema['properties']['test_one_of_enum']['oneOf'])->toBeArray()->toHaveCount(2) From 74d2d0d9bb8ce957006901c32f5800b9db708886 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 09:59:08 +0800 Subject: [PATCH 20/59] add openapiAnnotation --- src/OpenApi/Collections/ParameterCollection.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php index 405c7b3..2e2b901 100755 --- a/src/OpenApi/Collections/ParameterCollection.php +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -7,8 +7,6 @@ use Astral\Serialize\OpenApi\Annotations\OpenApi; use Astral\Serialize\OpenApi\Enum\ParameterTypeEnum; use Astral\Serialize\Support\Collections\TypeCollection; -use Attribute; -use ReflectionAttribute; class ParameterCollection { From 6265c712c1a75e52d14d466021ab3c4e5200f1f3 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 10:01:05 +0800 Subject: [PATCH 21/59] add openapiAnnotation --- src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index 6a792c1..ea2ff5a 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -163,7 +163,7 @@ private function buildNestedProperties(ParameterCollection $topParameter, array public function getDescriptions(ParameterCollection $parameter):string { - $description = $parameter->openApiAnnotation?->description ?? ''; + $description = $parameter->openApiAnnotation->description ?? ''; if(!ParameterTypeEnum::hasEnum($parameter->types)){ return $description; } From fbe8af054c58a8586f34b35a11bd88494076357a Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 15:23:32 +0800 Subject: [PATCH 22/59] test docs --- .gitbook.yaml | 7 ++ README.md | 2 +- docs/en/SUMMARY.md | 0 ...73\345\236\213\350\275\254\346\215\242.md" | 70 ++++++++++++++ ...53\351\200\237\345\274\200\345\247\213.md" | 94 +++++++++++++++++++ 5 files changed, 172 insertions(+), 1 deletion(-) create mode 100644 .gitbook.yaml create mode 100644 docs/en/SUMMARY.md create mode 100644 "docs/zh/DTO \350\275\254\346\215\242/\347\261\273\345\236\213\350\275\254\346\215\242.md" create mode 100644 "docs/zh/\345\277\253\351\200\237\345\274\200\345\247\213.md" diff --git a/.gitbook.yaml b/.gitbook.yaml new file mode 100644 index 0000000..27bb333 --- /dev/null +++ b/.gitbook.yaml @@ -0,0 +1,7 @@ +root: docs +structure: + langs: + - label: 简体中文 + path: zh + - label: English + path: en \ No newline at end of file diff --git a/README.md b/README.md index 34176d8..20a1a89 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ # Languages - [Complete documen-English](./docs/en/README.md) -- [完整文档-中文](./docs/zh/README.md) +- [完整文档-中文](https://astrals-organization.gitbook.io/php-serialize) # php-serialize An advanced PHP serialization tool leveraging attributes for flexible object-to-array and JSON conversion. Supports property aliases, type conversions, and nested object handling. Ideal for APIs, data persistence, and configuration management. diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md new file mode 100644 index 0000000..e69de29 diff --git "a/docs/zh/DTO \350\275\254\346\215\242/\347\261\273\345\236\213\350\275\254\346\215\242.md" "b/docs/zh/DTO \350\275\254\346\215\242/\347\261\273\345\236\213\350\275\254\346\215\242.md" new file mode 100644 index 0000000..9fca977 --- /dev/null +++ "b/docs/zh/DTO \350\275\254\346\215\242/\347\261\273\345\236\213\350\275\254\346\215\242.md" @@ -0,0 +1,70 @@ +### 类型转换 + +#### 基本类型转换 + +##### 方式一:构造函数属性提升 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public function __construct( + public string $username, + public int $score, + public float $balance, + public bool $isActive + ) {} +} +``` + +##### 方式二:传统属性定义 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public string $username; + public int $score; + public float $balance; + public bool $isActive; +} + +// 两种方式都支持相同的类型转换 +$profile = Profile::from([ + 'username' => 123, // 整数转换为字符串 + 'score' => '100', // 字符串转换为整数 + 'balance' => '99.99', // 字符串转换为浮点数 + 'isActive' => 1 // 数字转换为布尔值 +]); + +// 转换为数组 +$profileArray = $profile->toArray(); +``` + +##### 方式三:只读属性 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public readonly string $username; + public readonly int $score; + public readonly float $balance; + public readonly bool $isActive; + + // 手动初始化 + public function __construct( + string $username, + int $score, + float $balance, + bool $isActive + ) { + $this->username = $username; + $this->score = $score; + $this->balance = $balance; + $this->isActive = $isActive; + } +} +``` + +无论使用哪种方式,`Serialize` 类都能正常工作,并提供相同的类型转换和序列化功能。 \ No newline at end of file diff --git "a/docs/zh/\345\277\253\351\200\237\345\274\200\345\247\213.md" "b/docs/zh/\345\277\253\351\200\237\345\274\200\345\247\213.md" new file mode 100644 index 0000000..9a2108e --- /dev/null +++ "b/docs/zh/\345\277\253\351\200\237\345\274\200\345\247\213.md" @@ -0,0 +1,94 @@ +## 快速开始 + +### 安装 + +使用 Composer 安装: + +```bash +composer require astral/serialize +``` + +### 基本用法 + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public string $name, + public int $age +} + +// 从数组创建对象 +$user = User::from([ + 'name' => '张三', + 'age' => 30 +]); + +// 访问对象属性 +echo $user->name; // 输出: 张三 +echo $user->age; // 输出: 30 + +// 转换为数组 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'age' => 30 +// ] +``` + +### 其他特性 + +#### **不可变性**:只读属性在构造后无法修改 + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public function __construct( + public readonly string $name, + public readonly int $age + ) {} +} + +$user = User::from([ + 'name' => '张三', + 'age' => 30 +]); + +try { + $user->name = '李四'; // 编译时错误:无法修改只读属性 +} catch (Error $e) { + echo "只读属性不能被重新赋值"; +} +``` + +#### **类型安全的初始化** + +```php +$user = User::from([ + 'name' => 123, // 整数会被转换为字符串 + 'age' => '35' // 字符串会被转换为整数 +]); + +echo $user->name; // 输出: "123" +echo $user->age; // 输出: 35 +``` + +#### **构造函数初始化** + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public function __construct( + public readonly string $name, + public readonly int $age + ) { + // 可以在构造函数中添加额外的验证或处理逻辑 + if (strlen($name) < 2) { + throw new \InvalidArgumentException('名称太短'); + } + } +} +``` \ No newline at end of file From c1b25b96749b45d68a3330446716a1996a684c61 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 15:25:00 +0800 Subject: [PATCH 23/59] test docs --- .gitbook.yaml => docs/.gitbook.yaml | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename .gitbook.yaml => docs/.gitbook.yaml (100%) diff --git a/.gitbook.yaml b/docs/.gitbook.yaml similarity index 100% rename from .gitbook.yaml rename to docs/.gitbook.yaml From 829ecb697bc7b599c1257520f3e32f4d621bafcd Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 15:27:33 +0800 Subject: [PATCH 24/59] test docs --- docs/en/SUMMARY.md | 8 ++++++++ docs/zh/SUMMARY.md | 5 +++++ .../zh/base-mapper.md | 0 .../zh/getting-started.md | 0 4 files changed, 13 insertions(+) create mode 100644 docs/zh/SUMMARY.md rename "docs/zh/DTO \350\275\254\346\215\242/\347\261\273\345\236\213\350\275\254\346\215\242.md" => docs/zh/base-mapper.md (100%) rename "docs/zh/\345\277\253\351\200\237\345\274\200\345\247\213.md" => docs/zh/getting-started.md (100%) diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index e69de29..35d38d0 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -0,0 +1,8 @@ +# Summary + +* [介绍](README.md) +* [快速开始](getting-started.md) +* [进阶使用](advanced/index.md) + * [配置](advanced/config.md) + * [插件](advanced/plugins.md) +* [常见问题](faq.md) \ No newline at end of file diff --git a/docs/zh/SUMMARY.md b/docs/zh/SUMMARY.md new file mode 100644 index 0000000..0673a8d --- /dev/null +++ b/docs/zh/SUMMARY.md @@ -0,0 +1,5 @@ +# Summary + +* [介绍](README.md) +* [快速开始](getting-started.md) +* [进阶使用](base-mapper.md) \ No newline at end of file diff --git "a/docs/zh/DTO \350\275\254\346\215\242/\347\261\273\345\236\213\350\275\254\346\215\242.md" b/docs/zh/base-mapper.md similarity index 100% rename from "docs/zh/DTO \350\275\254\346\215\242/\347\261\273\345\236\213\350\275\254\346\215\242.md" rename to docs/zh/base-mapper.md diff --git "a/docs/zh/\345\277\253\351\200\237\345\274\200\345\247\213.md" b/docs/zh/getting-started.md similarity index 100% rename from "docs/zh/\345\277\253\351\200\237\345\274\200\345\247\213.md" rename to docs/zh/getting-started.md From 0d0d785e1da612ef48e07f14e9009871019863e7 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 15:31:18 +0800 Subject: [PATCH 25/59] test docs --- docs/.gitbook.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/.gitbook.yaml b/docs/.gitbook.yaml index 27bb333..a5d997c 100644 --- a/docs/.gitbook.yaml +++ b/docs/.gitbook.yaml @@ -1,4 +1,4 @@ -root: docs +root: ./ structure: langs: - label: 简体中文 From 05aa13f75c4d0bf7b5e6301b38673c767f33124e Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 15:53:03 +0800 Subject: [PATCH 26/59] add config --- docs/en/README.md | 1377 -------------------------------------------- docs/en/SUMMARY.md | 8 - 2 files changed, 1385 deletions(-) delete mode 100644 docs/en/README.md delete mode 100644 docs/en/SUMMARY.md diff --git a/docs/en/README.md b/docs/en/README.md deleted file mode 100644 index 4fe1acb..0000000 --- a/docs/en/README.md +++ /dev/null @@ -1,1377 +0,0 @@ -# Astral Serialize Documentation - -## Quick Start - -### Installation - -Install using Composer: - -```bash -composer require astral/serialize -``` - -### Basic Usage - -```php -use Astral\Serialize\Serialize; - -class User extends Serialize { - public string $name, - public int $age -} - -// Create object from array -$user = User::from([ - 'name' => 'John Doe', - 'age' => 30 -]); - -// Access object properties -echo $user->name; // Output: John Doe -echo $user->age; // Output: 30 - -// Convert to array -$userArray = $user->toArray(); -// $userArray contents: -// [ -// 'name' => 'John Doe', -// 'age' => 30 -// ] -``` - -#### Other Features - -1. **Immutability**: Read-only properties cannot be modified after construction - -```php -use Astral\Serialize\Serialize; - -class User extends Serialize { - public function __construct( - public readonly string $name, - public readonly int $age - ) {} -} - -$user = User::from([ - 'name' => 'John Doe', - 'age' => 30 -]); - -try { - $user->name = 'Jane Doe'; // Compile-time error: cannot modify read-only property -} catch (Error $e) { - echo "Read-only properties cannot be reassigned"; -} -``` - -2. **Type-Safe Initialization** - -```php -$user = User::from([ - 'name' => 123, // Integer will be converted to string - 'age' => '35' // String will be converted to integer -]); - -echo $user->name; // Output: "123" -echo $user->age; // Output: 35 -``` - -3. **Constructor Initialization** - -```php -use Astral\Serialize\Serialize; - -class User extends Serialize { - public function __construct( - public readonly string $name, - public readonly int $age - ) { - // Can add additional validation or processing logic in the constructor - if (strlen($name) < 2) { - throw new \InvalidArgumentException('Name is too short'); - } - } -} -``` - -## DTO Conversion - -### Type Conversion - -#### Basic Type Conversion - -##### Method One: Constructor Property Promotion - -```php -use Astral\Serialize\Serialize; - -class Profile extends Serialize { - public function __construct( - public string $username, - public int $score, - public float $balance, - public bool $isActive - ) {} -} -``` - -##### Method Two: Traditional Property Definition - -```php -use Astral\Serialize\Serialize; - -class Profile extends Serialize { - public string $username; - public int $score; - public float $balance; - public bool $isActive; -} - -// Both methods support the same type conversion -$profile = Profile::from([ - 'username' => 123, // Integer converted to string - 'score' => '100', // String converted to integer - 'balance' => '99.99', // String converted to float - 'isActive' => 1 // Number converted to boolean -]); - -// Convert to array -$profileArray = $profile->toArray(); -``` - -##### Method Three: Read-Only Properties - -```php -use Astral\Serialize\Serialize; - -class Profile extends Serialize { - public readonly string $username; - public readonly int $score; - public readonly float $balance; - public readonly bool $isActive; - - // Manual initialization - public function __construct( - string $username, - int $score, - float $balance, - bool $isActive - ) { - $this->username = $username; - $this->score = $score; - $this->balance = $balance; - $this->isActive = $isActive; - } -} -``` - -Regardless of the method used, the `Serialize` class will work normally and provide the same type conversion and serialization functionality. - -#### Enum Conversion - -Enum conversion provides a powerful and flexible enum handling mechanism, supporting multiple enum types and conversion scenarios. - -- Supports enums with `tryFrom()` and `cases()` methods -- Automatically converts strings to enum instances during input -- Automatically converts enums to strings (enum names) during output -- Provides flexible and safe enum handling mechanisms - -##### Regular Enum - -```php -enum UserRole { - case ADMIN; - case EDITOR; - case VIEWER; -} - -class ComplexUser extends Serialize { - - public UserRole $role; - - // Supports multiple enum types - public UserStatus|UserRole $mixedStatus; -} - -$complexUser = ComplexUser::from([ - 'role' => 'ADMIN', // Automatically converted to UserRole::ADMIN - 'mixedStatus' => 'ACTIVE' // Can be UserStatus or UserRole -]); - -echo $complexUser->role; // Returns UserRole enum instance - -$complexUserArray = $complexUser->toArray(); -// $complexUserArray contents: -// [ -// 'role' => 'ADMIN', -// 'mixedStatus' => 'ACTIVE' -// ] -``` - -##### Backed Enum - -```php -use Astral\Serialize\Serialize; - -// BackedEnum -enum UserStatus: string { - case ACTIVE = 'active'; - case INACTIVE = 'inactive'; - case SUSPENDED = 'suspended'; -} - -// Define a user class with enum -class User extends Serialize { - public string $name; - - // Supports UnitEnum and BackedEnum - public UserStatus $status; - - // Supports multiple enum types - public UserStatus|string $alternateStatus; -} - -// Create user object -$user = User::from([ - 'name' => 'John Doe', - 'status' => 'active', // Automatically converted to UserStatus::ACTIVE - 'alternateStatus' => 'inactive' // Supports string or enum values -]); - -var_dump($user->status); // Output: UserStatus::ACTIVE - -// Convert to array -$userArray = $user->toArray(); -// $userArray contents: -// [ -// 'name' => 'John Doe', -// 'status' => 'ACTIVE', // Output enum name -// 'alternateStatus' => 'INACTIVE' -// ] -``` - -#### Null Value Conversion Rules Detailed Example - -When the property is not a nullable type (`?type`), `null` values will be automatically converted based on the target type: - -```php -use Astral\Serialize\Serialize; - -class NullConversionProfile extends Serialize { - public string $username; - public int $score; - public float $balance; - public array $tags; - public object $metadata; -} - -// Null value conversion example -$profile = NullConversionProfile::from([ - 'username' => null, // Converted to empty string '' - 'score' => null, // Converted to 0 - 'balance' => null, // Converted to 0.0 - 'tags' => null, // Converted to empty array [] - 'metadata' => null // Converted to empty object new stdClass() -]); - -// Verify conversion results -echo $profile->username; // Output: ""(empty string) -echo $profile->score; // Output: 0 -echo $profile->balance; // Output: 0.0 -var_dump($profile->tags); // Output: array(0) {} -var_dump($profile->metadata); // Output: object(stdClass)#123 (0) {} - -// Special handling for boolean values -try { - NullConversionProfile::from([ - 'isActive' => null // This will throw a type error - ]); -} catch (\TypeError $e) { - echo "Boolean type does not support null values: " . $e->getMessage(); -} -``` - -#### Nullable Type Solution - -For scenarios requiring `null` acceptance, use nullable types: - -```php -use Astral\Serialize\Serialize; - -class FlexibleProfile extends Serialize { - public function __construct( - public ?string $username, - public ?int $score, - public ?object $metadata, - public ?array $tags - ) {} -} - -// Create an object with null values -$profile = FlexibleProfile::from([ - 'username' => null, // Allows null - 'score' => null, // Allows null - 'metadata' => null, // Allows null - 'tags' => null // Allows null -]); - -// Convert to array -$profileArray = $profile->toArray(); -// $profileArray contents: -// [ -// 'username' => null, -// 'score' => null, -// 'metadata' => null, -// 'tags' => null -// ] - -// Validate nullable type behavior -echo $profile->username; // Output null -``` - -#### Union Types - -1. Can mix basic and object types -2. Object Hierarchy Matching - - For multiple object types, the most matching type will be selected - - Supports intelligent matching of inheritance hierarchy -3. Dynamic Type Handling - - Automatically handles input of different types - - Provides more flexible data modeling - -```php -use Astral\Serialize\Serialize; - -// Define a base user class -class User extends Serialize { - public string $name; - public int $age; -} - -// Define an admin user class -class AdminUser extends User { - public string $role; -} - -class FlexibleData extends Serialize { - // Supports integer or string type identifiers - public int|string $flexibleId; - - // Supports user object or integer identifier - public User|int $userIdentifier; - - // Supports complex union types - public AdminUser|User|int $complexIdentifier; -} - -// Scenario 1: Use integer as flexibleId -$data1 = FlexibleData::from( - flexibleId : 123, - userIdentifier : 456, - complexIdentifier : 789 -); - -$data1Array = $data1->toArray(); -// $data1Array contents: -// [ -// 'flexibleId' => 123, -// 'userIdentifier' => 456, -// 'complexIdentifier' => 789 -// ] - -// Scenario 2: Use string as flexibleId -$data2 = FlexibleData::from( - flexibleId : 'ABC123', - userIdentifier : [ - 'name' => 'John', - 'age' => 30 - ], - complexIdentifier : [ - 'name' => 'Jane', - 'age' => 25 - ] -); - -echo $data2->userIdentifier; // Output User object -echo $data2->complexIdentifier; // Output User object - -$data2Array = $data2->toArray(); -// $data2Array contents: -// [ -// 'flexibleId' => 'ABC123', -// 'userIdentifier' => User Object ( -// ['name' => 'John', 'age' => 30] -// ), -// 'complexIdentifier' => User Object ( -// ['name' => 'Jane', 'age' => 25] -// ) -// ] - -// Scenario 3: Use admin user -$data3 = FlexibleData::from( - flexibleId : 'USER001', - userIdentifier : [ - 'name' => 'Bob', - 'age' => 35, - 'role' => 'admin' - ], - complexIdentifier : [ - 'name' => 'Alice', - 'age' => 40, - 'role' => 'super_admin' - ] -); - -echo $data2->userIdentifier; // Output User object -echo $data2->complexIdentifier; // Output AdminUser object - -$data3Array = $data3->toArray(); -// $data3Array contents: -// [ -// 'flexibleId' => 'USER001', -// 'userIdentifier' => User Object ( -// ['name' => 'Bob', 'age' => 35] -// ), -// 'complexIdentifier' => AdminUser Object ( -// ['name' => 'Alice', 'age' => 40, 'role' => 'super_admin'] -// ) -// ] -``` - -#### Array Object Conversion - -##### phpDoc Definition - -```php -use Astral\Serialize\Serialize; - -// Define basic array types -class ArrayOne extends Serialize { - public string $type = 'one'; - public string $name; -} - -class ArrayTwo extends Serialize { - public string $type = 'two'; - public string $code; -} - -class MultiArraySerialize extends Serialize { - // Scenario 1: Mixed type array - /** @var (ArrayOne|ArrayTwo)[] */ - public array $mixedTypeArray; - - // Scenario 2: Multiple type arrays - /** @var ArrayOne[]|ArrayTwo[] */ - public array $multiTypeArray; - - // Scenario 3: Key-value mixed type - /** @var array(string, ArrayOne|ArrayTwo) */ - public array $keyValueMixedArray; -} - -// Scenario 1: Mixed type array -$data1 = MultiArraySerialize::from( - mixedTypeArray : [ - ['name' => 'John'], // Convert to ArrayOne object - ['code' => 'ABC123'], // Convert to ArrayTwo object - ['name' => 'Jane'], // Convert to ArrayOne object - ['code' => 'DEF456'] // Convert to ArrayTwo object - ] -); - -$data1Array = $data1->toArray(); -// $data1Array contents: -// [ -// 'mixedTypeArray' => [ -// [0] => ArrayOne Object -// ( -// ['name' => 'John', 'type' => 'one'], -// ) -// [1] => ArrayTwo Object -// ( -// ['code' => 'ABC123', 'type' => 'two'], -// ) -// [2] => ArrayOne Object -// ( -// ['name' => 'Jane', 'type' => 'one'], -// ) -// [3] => ArrayTwo Object -// ( -// ['code' => 'DEF456', 'type' => 'two'], -// ) -// ] -// ] - -// Scenario 2: Multiple type arrays -$data2 = MultiArraySerialize::from( - multiTypeArray:[ - ['name' => 'Bob'], // Convert to ArrayOne object - ['name' => 'Alice'], // Convert to ArrayOne object - ['code' => 'GHI789'] // Convert to ArrayTwo object - ] -); - -$data2Array = $data2->toArray(); -// $data2Array contents: -// [ -// 'multiTypeArray' => [ -// ArrayOne Object ( -// ['name' => 'Bob', 'type' => 'one'] -// ), -// ArrayOne Object ( -// ['name' => 'Alice', 'type' => 'one'] -// ), -// ArrayTwo Object ( -// ['code' => 'GHI789', 'type' => 'two'] -// ) -// ] -// ] - -// Scenario 3: Key-value mixed type -$data3 = MultiArraySerialize::from( - keyValueMixedArray: [ - 'user1' => ['name' => 'John'], // Convert to ArrayOne object - 'system1' => ['code' => 'ABC123'], // Convert to ArrayTwo object - 'user2' => ['name' => 'Jane'] // Convert to ArrayOne object - ] -); - -$data3Array = $data3->toArray(); -// $data3Array contents: -// [ -// 'keyValueMixedArray' => [ -// 'user1' => ArrayOne Object ( -// ['name' => 'John', 'type' => 'one'] -// ), -// 'system1' => ArrayTwo Object ( -// ['code' => 'ABC123', 'type' => 'two'] -// ), -// 'user2' => ArrayOne Object ( -// ['name' => 'Jane', 'type' => 'one'] -// ) -// ] -// ] - -// Scenario 4: Handling unmatched cases -$data4 = MultiArraySerialize::from( - mixedTypeArray : [ - ['unknown' => 'data1'], - ['another' => 'data2'] - ] -); - -$data4Array = $data4->toArray(); -// $data4Array contents: -// [ -// 'mixedTypeArray' => [ -// ['unknown' => 'data1'], -// ['another' => 'data2'] -// ] -// ] -``` - -### Annotation Class Usage - -#### Property Grouping - -Property grouping provides a flexible way to control the input and output behavior of properties, allowing fine-grained data conversion management in different scenarios. - -##### Basic Usage - -Use the `#[Groups]` annotation on properties to specify the groups they belong to. - -```php -use Astral\Serialize\Attributes\Groups; -use Astral\Serialize\Serialize; - -class User extends Serialize { - - #[Groups('update','detail')] - public string $id; - - #[Groups('create', 'update', 'detail')] - public string $name; - - #[Groups('create','detail')] - public string $username; - - #[Groups('other')] - public string $sensitiveData; - - // Properties without a specified group will be in the default group - public string $noGroupInfo; - - // Constructor parameters also support grouping - public function __construct( - #[Groups('create','detail')] - public readonly string $email, - - #[Groups('update','detail')] - public readonly int $score - ) {} -} - -// Use default group to display all information -$user1 = User::from( - id:1, - name: 'Jane', - score: 100, - username: 'username', - email: 'jane@example.com', - sensitiveData:'Confidential info', - noGroupInfo:'Default group info' -); - -// Use default group toArray, display all information -$defaultArray = $user1->toArray(); -// $defaultArray contents: -// [ -// 'id' => '1', -// 'name' => 'Jane', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'jane@example.com', -// 'sensitiveData' => 'Confidential info', -// 'noGroupInfo' => 'Default group info' -// ] - -// Use create group to create user, only accept data with create group -$user2 = User::setGroups(['create'])->from( - id:1, - name: 'Jane', - score: 100, - username: 'username', - email: 'jane@example.com', - sensitiveData:'Confidential info', - noGroupInfo:'Default group info' -); - -// Use create group toArray -$createArray = $user2->toArray(); -// $createArray contents: -// [ -// 'name' => 'Jane', -// 'username' => 'username', -// 'email' => 'jane@example.com', -// ] - -// Use update group to update user, only accept data with update group -$user3 = User::setGroups(['update'])->from( - id:1, - name: 'Jane', - score: 100, - username: 'username', - email: 'jane@example.com', - sensitiveData:'Confidential info', - noGroupInfo:'Default group info' -); - -// Use update group toArray -$updateArray = $user3->toArray(); -// $updateArray contents: -// [ -// 'id' => '1', -// 'name' => 'Jane', -// 'score' => 100, -// ] - -// Use detail and other groups to display user, accept data with detail and other groups -$user4 = User::setGroups(['detail','other'])->from( - id:1, - name: 'Jane', - score: 100, - username: 'username', - email: 'jane@example.com', - sensitiveData:'Confidential info', - noGroupInfo:'Default group info' -); - -// Use multiple groups toArray -$multiGroupArray = $user4->toArray(); -// $multiGroupArray contents: -// [ -// 'id' => '1', -// 'name' => 'Jane', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'jane@example.com', -// 'sensitiveData' => 'Confidential info', -// ] -``` - -##### Nested Class Group Display - -```php -class ComplexUser extends Serialize { - - public string $name; - - public int $sex; - - public ComplexNestedInfo $info; -} - -class ComplexNestedInfo extends Serialize { - - #[Groups(ComplexAUser::class)] - public float $money; - - public string $currency; -} - -// ComplexNestedInfo will hide currency -$adminUser = ComplexUser::from( - name: 'John', - sex: 1, - info: [ - 'money' => 100.00, - 'currency' => 'USD' - ] -); - -// Output data -$adminUserArray = $adminUser->toArray(); -// $adminUserArray contents: -// [ -// 'name' => 'John', -// 'sex' => 1, -// 'info' => ComplexNestedInfo Object ([ -// 'money' => 100.00 -// ]) -// ] -``` - -#### Name Mapping - -##### Basic Usage - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Serialize; - -class User extends Serialize { - // Use different property name for input - #[InputName('user_name')] - public string $name; - - // Use different property name for output - #[OutputName('user_id')] - public int $id; - - // Support different input and output names - #[InputName('register_time')] - #[OutputName('registeredAt')] - public DateTime $createdAt; -} - -// Use input data with different names -$user = User::from([ - 'user_name' => 'John', // Mapped to $name - 'id' => 123, // Remains unchanged - 'register_time' => '2023-01-01 10:00:00' // Mapped to $createdAt -]); - -// Output data -$userArray = $user->toArray(); -// $userArray contents: -// [ -// 'name' => 'John', -// 'user_id' => 123, -// 'registeredAt' => '2023-01-01 10:00:00' -// ] -``` - -##### Multiple Input/Output Name Handling - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Serialize; - -class MultiOutputUser extends Serialize { - // Multiple output names - #[OutputName('user_id')] - #[OutputName('id')] - #[OutputName('userId')] - public int $id; - - // Multiple input names, first matching name will be used - #[InputName('user_name')] - #[InputName('other_name')] - #[InputName('userName')] - public int $name; -} - -// Scenario 1: Use first matching input name -$user1 = MultiOutputUser::from([ - 'user_name' => 'John' // Use 'user_name' -]); -echo $user1->name; // Output 'John' - -// Scenario 2: Use second matching input name -$user2 = MultiOutputUser::from([ - 'other_name' => 'Jane' // Use 'other_name' -]); -echo $user2->name; // Output 'Jane' - -// Scenario 3: Use last input name -$user3 = MultiOutputUser::from([ - 'userName' => 'Bob' // Use 'userName' -]); -echo $user3->name; // Output 'Bob' - -// Scenario 4: Multiple inputs, first matching name used -$user4 = MultiOutputUser::from([ - 'userName' => 'Bob', - 'other_name' => 'Jane', - 'user_name' => 'John', -]); -echo $user4->name; // Output 'John' - -// Create user object -$user = MultiOutputUser::from([ - 'id' => 123, - 'name' => 'John' -]); - -// Convert to array -$userArray = $user->toArray(); -// $userArray contents: -// [ -// 'user_id' => 123, -// 'id' => 123, -// 'userId' => 123, -// ] -``` - -#### Complex Mapping Scenarios - -```php -use Astral\Serialize\Serialize; - -class ComplexUser extends Serialize { - // Name mapping for nested objects - #[InputName('user_profile')] - public UserProfile $profile; - - // Name mapping for array elements - #[InputName('user_tags')] - public array $tags; -} - -// Handle complex input structures -$complexUser = ComplexUser::from([ - 'user_profile' => [ - 'nickname' => 'John', - 'age' => 25 - ], - 'user_tags' => ['developer', 'programmer'] -]); - -// Convert to standard array -$complexUserArray = $complexUser->toArray(); -// $complexUserArray contents: -// [ -// 'profile' => UserProfile Object ([ -// 'nickname' => 'John', -// 'age' => 25 -// ]), -// 'tags' => ['developer', 'programmer'] -// ] -``` - -##### Mapper Mapping - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Support\Mappers\{ - CamelCaseMapper, - SnakeCaseMapper, - PascalCaseMapper, - KebabCaseMapper -}; -use Astral\Serialize\Serialize; - -class User extends Serialize { - // Directly specify mapping names - #[InputName('user_name')] - #[OutputName('userName')] - public string $name; - - // Use mapper for style conversion - #[InputName(CamelCaseMapper::class)] - #[OutputName(SnakeCaseMapper::class)] - public int $userId; - - // Support multiple mappings and groups - #[InputName('email', groups: 'profile')] - #[OutputName('userEmail', groups: 'api')] - public string $email; -} - -// Use different mapping strategies -$user = User::from([ - 'user_name' => 'John', // Mapped to $name - 'user_id' => 123, // Use CamelCaseMapper conversion - 'email' => 'user@example.com' // Only effective in 'profile' group -]); - -// Output with different mappings -$userArray = $user->toArray( - inputGroups: ['profile'], // Use only input mappings in 'profile' group - outputGroups: ['api'] // Use only output mappings in 'api' group -); -``` - -##### Global Class Mapping - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Support\Mappers\{ - CamelCaseMapper, - SnakeCaseMapper, - PascalCaseMapper, - KebabCaseMapper -}; -use Astral\Serialize\Serialize; - -#[InputName(SnakeCaseMapper::class)] -#[OutputName(CamelCaseMapper::class)] -class GlobalMappedUser extends Serialize { - // Class-level mapping automatically applies to all properties - public string $firstName; - public string $lastName; - public int $userId; - public DateTime $registeredAt; -} - -// Use global mapping -$user = GlobalMappedUser::from([ - 'first_name' => 'John', // From snake_case to firstName - 'last_name' => 'Doe', // From snake_case to lastName - 'user_id' => 123, // From snake_case to userId - 'registered_at' => '2023-01-01' // From snake_case to registeredAt -]); - -// Output will be converted to camelCase -$userArray = $user->toArray(); -// $userArray contents: -// [ -// 'firstName' => 'John', -// 'lastName' => 'Doe', -// 'userId' => 123, -// 'registeredAt' => '2023-01-01' -// ] -``` - -###### Property Mapping Takes Precedence Over Class-Level Mapping - -```php -#[InputName(SnakeCaseMapper::class)] -class PartialOverrideUser extends Serialize { - #[InputName(PascalCaseMapper::class)] - public string $userName; // Prioritize PascalCase mapping - - public string $userEmail; // Continue using class-level global mapping -} - -$partialUser = PartialOverrideUser::from([ - 'User_name' => 'John', // Use snake_case mapping - 'UserName' => 'Jane', // Use PascalCase mapping - 'user_email' => 'user@example.com' // Use snake_case mapping -]); - -$partialUser->toArray(); -// $partialUser contents: -// [ -// 'userName' => 'Jane', -// 'userEmail' => 'user@example.com', -// ] -``` - -###### Global Class Mapping with Groups - -Needs to be used in conjunction with `Groups` annotation - -```php -use Astral\Serialize\Attributes\Groups; -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Support\Mappers\{ - CamelCaseMapper, - SnakeCaseMapper, - PascalCaseMapper, - KebabCaseMapper -}; -use Astral\Serialize\Serialize; - -#[InputName(SnakeCaseMapper::class, groups: 'external')] -#[InputName(CamelCaseMapper::class, groups: 'api')] -#[OutputName(PascalCaseMapper::class, groups: ['external','api'])] -class ComplexMappedUser extends Serialize { - - #[Groups('external', 'api')] - public string $firstName; - - #[Groups('external', 'api')] - public string $lastName; - - #[InputName('full_name', groups: 'special')] - #[OutputName('userEmail', groups: 'api')] - #[Groups('external', 'api')] - public string $fullName; -} - -// Use admin group -$complexUser = ComplexMappedUser::setGroup('external')->from( - first_name: 'John', - last_name: 'Doe', - full_name: 'John Doe' -); - -$complexUser = $complexUser->toArray(); -// $complexUser contents: -// [ -// 'FirstName' => 'John', -// 'LastName' => 'Doe', -// 'FullName' => 'John Doe', -// ] - -// If specific OutputName/InputName is familiar, attribute rules take priority -// Use public group -$complexUser = ComplexMappedUser::setGroup('api')->from( - first_name: 'John', - last_name: 'Doe', - full_name: 'John Doe' -); - -$complexUser = $complexUser->toArray(); -// $complexUser contents: -// [ -// 'firstName' => 'John', -// 'lastName' => 'Doe', -// 'userEmail' => 'John Doe', -// ] -``` - -#### Custom Mapper - -```php -// Custom mapper needs to extend NameMapper and implement resolve -class CustomMapper implements NameMapper { - public function resolve(string $name): string { - // Implement custom naming conversion logic - return str_replace('user', 'customer', $name); - } -} - -class AdvancedUser extends Serialize { - #[InputName(CustomMapper::class)] - public string $name; -} -``` - -#### Field Ignoring - -1. **Security Control** - - Prevent accidental leakage of sensitive information - - Fine-grained control of data input and output - -2. **Data Filtering** - - Filter fields based on different scenarios - - Customize data views for different APIs or user roles - -3. **Performance Optimization** - - Reduce serialization overhead for unnecessary fields - - Streamline data transmission - -##### Basic Usage - -```php -use Astral\Serialize\Attributes\InputIgnore; -use Astral\Serialize\Attributes\OutputIgnore; -use Astral\Serialize\Serialize; - -class User extends Serialize { - - public string $name; - - // Fields ignored during input - #[InputIgnore] - public string $internalId; - - // Fields ignored during output - #[OutputIgnore] - public string $tempData; -} - -// Create user object -$user = User::from([ - 'name' => 'John', - 'internalId' => 'secret123', // This field will be ignored - 'tempData' => 'temporary' // This field will be ignored -]); - -echo $user->internalId; // This will output '' - -// Convert to array -$userArray = $user->toArray(); -// $userArray contents: -// [ -// 'name' => 'John', -// 'internalId' => '', -// ] -``` - -##### Group Ignoring - -Group ignoring needs to be used in conjunction with the Groups annotation - -```php -use Astral\Serialize\Attributes\Input\InputIgnore; -use Astral\Serialize\Attributes\Output\OutputIgnore; -use Astral\Serialize\Serialize; -use Astral\Serialize\Attributes\Groups; - -class ComplexUser extends Serialize { - - #[Groups('admin','public')] - #[InputIgnore('admin')] - public string $name; - - #[Groups('admin','public')] - #[OutputIgnore('public')] - public string $secretKey; - - #[Groups('admin','public')] - #[InputIgnore('admin')] - #[OutputIgnore('public')] - public string $sensitiveInfo; - - #[InputIgnore] - public string $globalInputIgnore; - - #[OutputIgnore] - public string $globalOutputIgnore; -} - -// Default group -$complexUser = ComplexUser::from([ - 'name' => 'John', - 'secretKey' => 'confidential', - 'sensitiveInfo' => 'Sensitive information', - 'globalInputIgnore' => 'Global input ignore', - 'globalOutputIgnore' => 'Global output ignore' -]); - -echo $complexUser->globalInputIgnore; // Output '' -echo $complexUser->globalOutputIgnore; // Output 'Global output ignore' - -$complexUser = $complexUser->toArray(); -// $complexUser contents: -// [ -// 'name' => 'John', -// 'secretKey' => 'confidential', -// 'sensitiveInfo' => 'Sensitive information', -// 'globalInputIgnore' => '', -// ] - -// Use admin group -$complexUser = ComplexUser::setGroups('admin')->from([ - 'name' => 'John', - 'secretKey' => 'confidential', - 'sensitiveInfo' => 'Sensitive information', - 'globalInputIgnore' => 'Global input ignore', - 'globalOutputIgnore' => 'Global output ignore' -]); - -$complexUser = $complexUser->toArray(); -// $complexUser contents: -// [ -// 'name' => '', -// 'secretKey' => 'confidential', -// 'globalInputIgnore' => '', -// ] - -// Use public group -$complexUser = ComplexUser::setGroups('public')->from([ - 'name' => 'John', - 'secretKey' => 'confidential', - 'sensitiveInfo' => 'Sensitive information', - 'globalInputIgnore' => 'Global input ignore', - 'globalOutputIgnore' => 'Global output ignore' -]); - -$complexUser = $complexUser->toArray(); -// $complexUser contents: -// [ -// 'name' => 'John', -// 'globalInputIgnore' => '', -// ] -``` - -### Nested Object Mocking - -#### Basic Usage - -```php -class ComplexUserFaker extends Serialize { - #[FakerObject(UserProfile::class)] - public UserProfile $profile; -} -``` - -#### Demonstration Example - -```php -use Astral\Serialize\Serialize; -use Astral\Serialize\Attributes\FakerObject; -use Astral\Serialize\Attributes\FakerCollection; - -class UserProfile extends Serialize { - public string $nickname; - public int $age; - public string $email; - public string $avatar; -} - -class UserTag extends Serialize { - public string $name; - public string $color; -} - -class ComplexUserFaker extends Serialize { - #[FakerObject(UserProfile::class)] - public UserProfile $profile; - - #[FakerObject(UserTag::class)] - public UserTag|UserProfile $primaryTag; -} - -$complexUserFaker = ComplexUserFaker::faker(); - -$complexUserFakerArray = $complexUserFaker->toArray(); -// $complexUserFakerArray contents: -// [ -// 'profile' => UserProfile Object ( -// ['nickname' => 'RandomNickname', 'age' => 28, 'email' => 'random.user@example.com', 'avatar' => 'https://example.com/avatars/random-avatar.jpg'] -// ), -// 'primaryTag' => UserTag Object ( -// ['name' => 'Developer', 'color' => '#007bff'] -// ) -// ] -``` - -### Faker Class Method Mocking - -```php -class UserService { - public function generateUserData(): array { - return ['name' => 'Generated User']; - } -} - -class UserFaker extends Serialize { - #[FakerMethod(UserService::class, 'generateUserData')] - public array $userData; -} -``` - -#### Complete Example - -```php -use Astral\Serialize\Serialize; -use Astral\Serialize\Attributes\Faker\FakerMethod; -use Astral\Serialize\Attributes\Faker\FakerObject; -use Astral\Serialize\Attributes\Faker\FakerCollection; - -// User Profile Class -class UserProfile extends Serialize { - public string $nickname; - public int $age; - public string $email; - public array $types = ['type1' => 'money', 'type2' => 'score']; -} - -// User Service Class, providing data generation methods -class UserService { - public function generateUserData(): array { - return [ - 'name' => 'Generated User', - 'email' => 'generated.user@example.com', - 'age' => 30 - ]; - } - - public function generateUserProfile(UserProfile $user): UserProfile { - return $user; - } - - public function generateUserList(int $count): array { - $users = []; - for ($i = 0; $i < $count; $i++) { - $users[] = [ - 'name' => "User {$i}", - 'email' => "user{$i}@example.com" - ]; - } - return $users; - } -} - -// Faker Method Mocking Example -class UserFaker extends Serialize { - // Use method to generate simple data - #[FakerMethod(UserService::class, 'generateUserData')] - public array $userData; - - // Use method to generate object - #[FakerMethod(UserService::class, 'generateUserProfile')] - public UserProfile $userProfile; - - // Get specific attribute - #[FakerMethod(UserService::class, 'generateUserProfile', returnType: 'age')] - public int $age; - - // Get specific attribute with multi-level access using [.] - #[FakerMethod(UserService::class, 'generateUserProfile', returnType: 'types.type2')] - public string $type2; - - // Pass parameters - #[FakerMethod(UserService::class, 'generateUserList', params: ['count' => 3])] - public array $userList; -} - -// Generate mock data -$userFaker = UserFaker::faker(); - -// Convert to array -$userFakerArray = $userFaker->toArray(); -// $userFakerArray contents: -// [ -// 'userData' => [ -// 'name' => 'Generated User', -// 'email' => 'generated.user@example.com', -// 'age' => 30 -// ], -// 'userProfile' => UserProfile Object ( -// [ -// 'nickname' => 'GeneratedNickname', -// 'age' => 25, // Randomly generated -// 'email' => 'profile@example.com' -// 'types' => ['type1' => 'money', 'type2' => 'score'] -// ] -// ), -// 'age' => 99 , // Randomly generated -// 'type2' => 'score', -// 'userList' => [ -// ['name' => 'User 0', 'email' => 'user0@example.com'], -// ['name' => 'User 1', 'email' => 'user1@example.com'], -// ['name' => 'User 2', 'email' => 'user2@example.com'] -// ] -// ] diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md deleted file mode 100644 index 35d38d0..0000000 --- a/docs/en/SUMMARY.md +++ /dev/null @@ -1,8 +0,0 @@ -# Summary - -* [介绍](README.md) -* [快速开始](getting-started.md) -* [进阶使用](advanced/index.md) - * [配置](advanced/config.md) - * [插件](advanced/plugins.md) -* [常见问题](faq.md) \ No newline at end of file From dab46008b7e285198a96668430df21402a8d87c1 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 15:54:24 +0800 Subject: [PATCH 27/59] add config --- docs/.gitbook.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/docs/.gitbook.yaml b/docs/.gitbook.yaml index a5d997c..4f8fe5f 100644 --- a/docs/.gitbook.yaml +++ b/docs/.gitbook.yaml @@ -1,7 +1,4 @@ root: ./ structure: - langs: - - label: 简体中文 - path: zh - - label: English - path: en \ No newline at end of file + readme: ./zh/README.md + summary: ./zh/SUMMARY.md \ No newline at end of file From d974fdec5c8dee72dd4055107d8b17ff95e238a1 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 15:59:41 +0800 Subject: [PATCH 28/59] gitbook test --- docs/en/.gitbook.yaml | 4 + docs/en/README.md | 1647 +++++++++++++++++++++++++++++++++++ docs/en/SUMMARY.md | 5 + docs/en/base-mapper.md | 70 ++ docs/en/getting-started.md | 94 ++ docs/{ => zh}/.gitbook.yaml | 0 6 files changed, 1820 insertions(+) create mode 100644 docs/en/.gitbook.yaml create mode 100644 docs/en/README.md create mode 100644 docs/en/SUMMARY.md create mode 100644 docs/en/base-mapper.md create mode 100644 docs/en/getting-started.md rename docs/{ => zh}/.gitbook.yaml (100%) diff --git a/docs/en/.gitbook.yaml b/docs/en/.gitbook.yaml new file mode 100644 index 0000000..f455ec9 --- /dev/null +++ b/docs/en/.gitbook.yaml @@ -0,0 +1,4 @@ +root: ./ +structure: + readme: ./en/README.md + summary: ./en/SUMMARY.md \ No newline at end of file diff --git a/docs/en/README.md b/docs/en/README.md new file mode 100644 index 0000000..60576c3 --- /dev/null +++ b/docs/en/README.md @@ -0,0 +1,1647 @@ +# Astral Serialize 文档 + +## 快速开始 + +### 安装 + +使用 Composer 安装: + +```bash +composer require astral/serialize +``` + +### 基本用法 + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public string $name, + public int $age +} + +// 从数组创建对象 +$user = User::from([ + 'name' => '张三', + 'age' => 30 +]); + +// 访问对象属性 +echo $user->name; // 输出: 张三 +echo $user->age; // 输出: 30 + +// 转换为数组 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'age' => 30 +// ] +``` + +#### 其他特性 + +1. **不可变性**:只读属性在构造后无法修改 + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public function __construct( + public readonly string $name, + public readonly int $age + ) {} +} + +$user = User::from([ + 'name' => '张三', + 'age' => 30 +]); + +try { + $user->name = '李四'; // 编译时错误:无法修改只读属性 +} catch (Error $e) { + echo "只读属性不能被重新赋值"; +} +``` + +2. **类型安全的初始化** + +```php +$user = User::from([ + 'name' => 123, // 整数会被转换为字符串 + 'age' => '35' // 字符串会被转换为整数 +]); + +echo $user->name; // 输出: "123" +echo $user->age; // 输出: 35 +``` + +3. **构造函数初始化** + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public function __construct( + public readonly string $name, + public readonly int $age + ) { + // 可以在构造函数中添加额外的验证或处理逻辑 + if (strlen($name) < 2) { + throw new \InvalidArgumentException('名称太短'); + } + } +} +``` + +## 生成openapi文档 + +#### 创建Request文件 +```php +use Astral\Serialize\Serialize; + +class UserAddRequest extends Serialize { + public string $name; + public int $id; +} + +class UserDetailRequest extends Serialize { + public int $id; +} +``` + +#### 创建Repose文件 +```php +use Astral\Serialize\Serialize; + +class UserDto extends Serialize { + public string $name, + public int $id; +} +``` + +#### 创建Controller文件 +```php +use Astral\Serialize\Serialize; +use Astral\Serialize\OpenApi\Enum\MethodEnum; + +#[\Astral\Serialize\OpenApi\Annotations\Tag('用户模块管理')] +class UserController { + + #[\Astral\Serialize\OpenApi\Annotations\Summary('创建用户')] + #[\Astral\Serialize\OpenApi\Annotations\Route('/user/create')] + #[\Astral\Serialize\OpenApi\Annotations\RequestBody(UserAddRequest::class)] + #[\Astral\Serialize\OpenApi\Annotations\Response(UserDto::class)] + public function create() + { + return new UserDto(); + } + + #[\Astral\Serialize\OpenApi\Annotations\Summary('用户详情')] + #[\Astral\Serialize\OpenApi\Annotations\Route(route:'/user/detail', method: MethodEnum::GET)] + public function detail(UserDetailRequest $request): UserDto + { + return new UserDto(); + } +} +``` + +## DTO 转换 + +### 类型转换 + +#### 基本类型转换 + +##### 方式一:构造函数属性提升 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public function __construct( + public string $username, + public int $score, + public float $balance, + public bool $isActive + ) {} +} +``` + +##### 方式二:传统属性定义 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public string $username; + public int $score; + public float $balance; + public bool $isActive; +} + +// 两种方式都支持相同的类型转换 +$profile = Profile::from([ + 'username' => 123, // 整数转换为字符串 + 'score' => '100', // 字符串转换为整数 + 'balance' => '99.99', // 字符串转换为浮点数 + 'isActive' => 1 // 数字转换为布尔值 +]); + +// 转换为数组 +$profileArray = $profile->toArray(); +``` + +##### 方式三:只读属性 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public readonly string $username; + public readonly int $score; + public readonly float $balance; + public readonly bool $isActive; + + // 手动初始化 + public function __construct( + string $username, + int $score, + float $balance, + bool $isActive + ) { + $this->username = $username; + $this->score = $score; + $this->balance = $balance; + $this->isActive = $isActive; + } +} +``` + +无论使用哪种方式,`Serialize` 类都能正常工作,并提供相同的类型转换和序列化功能。 + +#### 枚举转换 + +枚举转换提供了强大且灵活的枚举处理机制,支持多种枚举类型和转换场景。 + +- 支持 `tryFrom()` 和 `cases()` 方法的枚举类型 +- 输入时自动将字符串转换为枚举实例 +- 输出时自动将枚举转换为字符串(枚举名称) +- 提供灵活且安全的枚举处理机制 + +##### 普通枚举 + +```php +enum UserRole { + case ADMIN; + case EDITOR; + case VIEWER; +} + +class ComplexUser extends Serialize { + + public UserRole $role; + + // 支持多种枚举类型 + public UserStatus|UserRole $mixedStatus; +} + +$complexUser = ComplexUser::from([ + 'role' => 'ADMIN', // 自动转换为 UserRole::ADMIN + 'mixedStatus' => 'ACTIVE' // 可以是 UserStatus 或 UserRole +]); + +echo $complexUser->role; // 返回 UserRole枚举实例 + +$complexUserArray = $complexUser->toArray(); +// $complexUserArray 的内容: +// [ +// 'role' => 'ADMIN', +// 'mixedStatus' => 'ACTIVE' +// ] +``` + +##### 回退枚举 + +```php +use Astral\Serialize\Serialize; + +// BackedEnum +enum UserStatus: string { + case ACTIVE = 'active'; + case INACTIVE = 'inactive'; + case SUSPENDED = 'suspended'; +} + +// 定义带有枚举的用户类 +class User extends Serialize { + public string $name; + + // 支持 UnitEnum 和 BackedEnum + public UserStatus $status; + + // 支持多枚举类型 + public UserStatus|string $alternateStatus; +} + +// 创建用户对象 +$user = User::from([ + 'name' => '张三', + 'status' => 'active', // 自动转换为 UserStatus::ACTIVE + 'alternateStatus' => 'inactive' // 支持字符串或枚举值 +]); + +var_dump($user->status); // 输出: UserStatus::ACTIVE + +// 转换为数组 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'status' => 'ACTIVE', // 输出枚举名称 +// 'alternateStatus' => 'INACTIVE' +// ] +``` + +#### Null 值转换规则详细示例 + +当属性不是可空类型(`?type`)时,`null` 值会根据目标类型自动转换: + +```php +use Astral\Serialize\Serialize; + +class NullConversionProfile extends Serialize { + public string $username; + public int $score; + public float $balance; + public array $tags; + public object $metadata; +} + +// Null 值转换示例 +$profile = NullConversionProfile::from([ + 'username' => null, // 转换为空字符串 '' + 'score' => null, // 转换为 0 + 'balance' => null, // 转换为 0.0 + 'tags' => null, // 转换为空数组 [] + 'metadata' => null // 转换为空对象 new stdClass() +]); + +// 验证转换结果 +echo $profile->username; // 输出: ""(空字符串) +echo $profile->score; // 输出: 0 +echo $profile->balance; // 输出: 0.0 +var_dump($profile->tags); // 输出: array(0) {} +var_dump($profile->metadata); // 输出: object(stdClass)#123 (0) {} + +// 布尔值的特殊处理 +try { + NullConversionProfile::from([ + 'isActive' => null // 这将抛出类型错误 + ]); +} catch (\TypeError $e) { + echo "布尔类型不支持 null 值:" . $e->getMessage(); +} +``` + +#### 可空类型的方案 + +对于需要接受 `null` 的场景,使用可空类型: + +```php +use Astral\Serialize\Serialize; + +class FlexibleProfile extends Serialize { + public function __construct( + public ?string $username, + public ?int $score, + public ?object $metadata, + public ?array $tags + ) {} +} + +// 创建包含 null 值的对象 +$profile = FlexibleProfile::from([ + 'username' => null, // 允许 null + 'score' => null, // 允许 null + 'metadata' => null, // 允许 null + 'tags' => null // 允许 null +]); + +// 转换为数组 +$profileArray = $profile->toArray(); +// $profileArray 的内容: +// [ +// 'username' => null, +// 'score' => null, +// 'metadata' => null, +// 'tags' => null +// ] + +// 验证可空类型的行为 +echo $profile->username; // 输出 null +``` + +#### 联合类型 + +1. 可以混合使用基本类型和对象类型 +2. 对象层级匹配 + 对于多个对象类型,会选择最匹配的类型 + 支持继承层级的智能匹配 +3. 动态类型处理 + 自动处理不同类型的输入 + 提供更加灵活的数据建模方式 + +```php +use Astral\Serialize\Serialize; + +// 定义一个基础用户类 +class User extends Serialize { + public string $name; + public int $age; +} + +// 定义一个管理员用户类 +class AdminUser extends User { + public string $role; +} + +class FlexibleData extends Serialize { + // 支持整数或字符串类型的标识符 + public int|string $flexibleId; + + // 支持用户对象或整数标识符 + public User|int $userIdentifier; + + // 支持多种复杂的联合类型 + public AdminUser|User|int $complexIdentifier; +} + +// 场景1:使用整数作为 flexibleId +$data1 = FlexibleData::from([ + 'flexibleId' => 123, + 'userIdentifier' => 456, + 'complexIdentifier' => 789 +]); + +$data1Array = $data1->toArray(); +// $data1Array 的内容: +// [ +// 'flexibleId' => 123, +// 'userIdentifier' => 456, +// 'complexIdentifier' => 789 +// ] + +// 场景2:使用字符串作为 flexibleId +$data2 = FlexibleData::from([ + 'flexibleId' => 'ABC123', + 'userIdentifier' => [ + 'name' => '张三', + 'age' => 30 + ], + 'complexIdentifier' => [ + 'name' => '李四', + 'age' => 25 + ] +]); + +echo $data2->userIdentifier; // 输出 User 对象 +echo $data2->complexIdentifier; // 输出 User 对象 + +$data2Array = $data2->toArray(); +// $data2Array 的内容: +// [ +// 'flexibleId' => 'ABC123', +// 'userIdentifier' => User Object ( +// ['name' => '张三', 'age' => 30] +// ), +// 'complexIdentifier' => User Object ( +// ['name' => '李四', 'age' => 25] +// ) +// ] + +// 场景3:使用管理员用户 +$data3 = FlexibleData::from([ + 'flexibleId' => 'USER001', + 'userIdentifier' => [ + 'name' => '王五', + 'age' => 35, + 'role' => 'admin' + ], + 'complexIdentifier' => [ + 'name' => '赵六', + 'age' => 40, + 'role' => 'super_admin' + ] +]); + +echo $data2->userIdentifier; // 输出 User 对象 +echo $data2->complexIdentifier; // 输出 AdminUser 对象 + +$data3Array = $data3->toArray(); +// $data3Array 的内容: +// [ +// 'flexibleId' => 'USER001', +// 'userIdentifier' => User Object ( +// ['name' => '王五', 'age' => 35] +// ), +// 'complexIdentifier' => AdminUser Object ( +// ['name' => '赵六', 'age' => 40, 'role' => 'super_admin'] +// ) +// ] +``` + +#### 数组对象转换 + +##### phpDoc定义 + +```php +use Astral\Serialize\Serialize; + +// 定义基础数组类型 +class ArrayOne extends Serialize { + public string $type = 'one'; + public string $name; +} + +class ArrayTwo extends Serialize { + public string $type = 'two'; + public string $code; +} + +class MultiArraySerialize extends Serialize { + // 场景1:混合类型数组 + /** @var (ArrayOne|ArrayTwo)[] */ + public array $mixedTypeArray; + + // 场景2:多类型数组 + /** @var ArrayOne[]|ArrayTwo[] */ + public array $multiTypeArray; + + // 场景3:键值对混合类型 + /** @var array(string, ArrayOne|ArrayTwo) */ + public array $keyValueMixedArray; +} + +// 场景1:混合类型数组 +$data1 = MultiArraySerialize::from( + mixedTypeArray : [ + ['name' => '张三'], // 转化 ArrayOne 对象 + ['code' => 'ABC123'], // 转化 ArrayTwo 对象 + ['name' => '李四'], // 转化 ArrayOne 对象 + ['code' => 'DEF456'] // 转化 ArrayTwo 对象 + ] +); + +$data1Array = $data1->toArray(); +// $data1Array 的内容: +// [ +// 'mixedTypeArray' => [ +// [0] => ArrayOne Object +// ( +// ['name' => '张三', 'type' => 'one'], +// ) +// [1] => ArrayTwo Object +// ( +// ['code' => 'ABC123', 'type' => 'two'], +// ) +// [2] => ArrayOne Object +// ( +// ['name' => '李四', 'type' => 'one'], +// ) +// [3] => ArrayTwo Object +// ( +// ['code' => 'DEF456', 'type' => 'two'], +// ) +// ] +// ] + +// 场景2:多类型数组 +$data2 = MultiArraySerialize::from( + multiTypeArray:[ + ['name' => '王五'], // 转化 ArrayOne 对象 + ['name' => '赵六'], // 转化 ArrayOne 对象 + ['code' => 'GHI789'] // 转化 ArrayTwo 对象 + ] +); + +$data2Array = $data2->toArray(); +// $data2Array 的内容: +// [ +// 'multiTypeArray' => [ +// ArrayOne Object ( +// ['name' => '王五', 'type' => 'one'] +// ), +// ArrayOne Object ( +// ['name' => '赵六', 'type' => 'one'] +// ), +// ArrayTwo Object ( +// ['code' => 'GHI789', 'type' => 'two'] +// ) +// ] +// ] + +// 场景3:键值对混合类型 +$data3 = MultiArraySerialize::from( + keyValueMixedArray: [ + 'user1' => ['name' => '张三'], // 转化 ArrayOne 对象 + 'system1' => ['code' => 'ABC123'], // 转化 ArrayTwo 对象 + 'user2' => ['name' => '李四'] // 转化 ArrayOne 对象 + ] +); + +$data3Array = $data3->toArray(); +// $data3Array 的内容: +// [ +// 'keyValueMixedArray' => [ +// 'user1' => ArrayOne Object ( +// ['name' => '张三', 'type' => 'one'] +// ), +// 'system1' => ArrayTwo Object ( +// ['code' => 'ABC123', 'type' => 'two'] +// ), +// 'user2' => ArrayOne Object ( +// ['name' => '李四', 'type' => 'one'] +// ) +// ] +// ] + +// 场景4:无法匹配时的处理 +$data4 = MultiArraySerialize::from( + mixedTypeArray : [ + ['unknown' => 'data1'], + ['another' => 'data2'] + ] +); + +$data4Array = $data4->toArray(); +// $data4Array 的内容: +// [ +// 'mixedTypeArray' => [ +// ['unknown' => 'data1'], +// ['another' => 'data2'] +// ] +// ] +``` + +### 注解类使用 + +#### 属性分组 + +属性分组提供了一种灵活的方式来控制属性的输入和输出行为,允许在不同场景下精细地管理数据转换。 + +##### 基本用法 + +在属性上使用 `#[Groups]` 注解来指定属性所属的分组。 + +```php +use Astral\Serialize\Attributes\Groups; +use Astral\Serialize\Serialize; + +class User extends Serialize { + + #[Groups('update','detail')] + public string $id; + + #[Groups('create', 'update', 'detail')] + public string $name; + + #[Groups('create','detail')] + public string $username; + + #[Groups('other')] + public string $sensitiveData; + + // 没有指定Group 的属性将会被默认分组在default分组中 + public string $noGroupInfo; + + // 构造函数参数也支持分组 + public function __construct( + #[Groups('create','detail')] + public readonly string $email, + + #[Groups('update','detail')] + public readonly int $score + ) {} +} + + + +// 使用 默认分组展示所有信息 +$user1 = User::from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用默认分组 toArray,展示所有信息 +$defaultArray = $user1->toArray(); +// $defaultArray 的内容: +// [ +// 'id' => '1', +// 'name' => '李四', +// 'username' => 'username', +// 'score' => 100, +// 'email' => 'zhangsan@example.com', +// 'sensitiveData' => '机密信息', +// 'noGroupInfo' => '默认分组信息' +// ] + +// 指定分组内容输入 +$defaultArray = $user1->withGroups('create')->toArray(); +// 输出内容 +// [ +// 'name' => '李四', +// 'username' => 'username', +// 'email' => 'zhangsan@example.com', +// ] + +$defaultArray = $user1->withGroups(['detail','other'])->toArray(); +// 输出内容 +// [ +// 'id' => '1', +// 'name' => '李四', +// 'username' => 'username', +// 'score' => 100, +// 'email' => 'zhangsan@example.com', +// 'sensitiveData' => '机密信息', +// ] + + +// 使用 create 分组创建用户 只会接受group为create的数据信息 +$user2 = User::setGroups(['create'])->from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用 create 分组 toArray +$createArray = $user2->toArray(); +// $createArray 的内容: +// [ +// 'name' => '李四', +// 'username' => 'username', +// 'email' => 'zhangsan@example.com', +// ] + +// 使用 update 分组更新用户 只会接受group为update的数据信息 +$user3 = User::setGroups(['update'])->from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用 update 分组 toArray +$updateArray = $user3->toArray(); +// $updateArray 的内容: +// [ +// 'id' => '1', +// 'name' => '李四', +// 'score' => 100, +// ] + +// 使用 detail 和 other 展示用户 会接受group为detail和other的数据信息 +$user4 = User::setGroups(['detail','other'])->from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用多个分组 toArray +$multiGroupArray = $user4->toArray(); +// $multiGroupArray 的内容: +// [ +// 'id' => '1', +// 'name' => '李四', +// 'username' => 'username', +// 'score' => 100, +// 'email' => 'zhangsan@example.com', +// 'sensitiveData' => '机密信息', +// ] +``` + +##### 嵌套类指定Group类展示 + +```php +class ComplexUser extends Serialize { + + public string $name; + + public int $sex; + + public ComplexNestedInfo $info; +} + +class ComplexNestedInfo extends Serialize { + + #[Groups(ComplexAUser::class)] + public float $money; + + public string $currency; +} + +// ComplexNestedInfo 会自动隐藏currency +$adminUser = ComplexUser::from( + name: '张三', + sex: 1, + info: [ + 'money' => 100.00, + 'currency' => 'CNY' + ]; +); + +// 输出数据 +$adminUserArray = $adminUser->toArray(); +// $adminUserArray 的内容: +// [ +// 'name' => '张三', +// 'sex' => 1, +// 'info' => ComplexNestedInfo Object ([ +// 'money' => 100.00 +// ]) +// ] +``` + +#### 名称映射 + +##### 基础使用 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Serialize; + +class User extends Serialize { + // 输入时使用不同的属性名 + #[InputName('user_name')] + public string $name; + + // 输出时使用不同的属性名 + #[OutputName('user_id')] + public int $id; + + // 同时支持输入和输出不同名称 + #[InputName('register_time')] + #[OutputName('registeredAt')] + public DateTime $createdAt; +} + +// 使用不同名称的输入数据 +$user = User::from([ + 'user_name' => '张三', // 映射到 $name + 'id' => 123, // 保持不变 + 'register_time' => '2023-01-01 10:00:00' // 映射到 $createdAt +]); + +// 输出数据 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'user_id' => 123, +// 'registeredAt' => '2023-01-01 10:00:00' +// ] +``` + +##### 多输入/输出名称处理 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Serialize; + +class MultiOutputUser extends Serialize { + // 多个输出名称 + #[OutputName('user_id')] + #[OutputName('id')] + #[OutputName('userId')] + public int $id; + + // 多个输出名称 按照声明顺序取地一个匹配的name + #[InputName('user_name')] + #[InputName('other_name')] + #[InputName('userName')] + public int $name; + +} + +// 场景1:使用第一个匹配的输入名称 +$user1 = MultiInputUser::from([ + 'user_name' => '张三' // 使用 'user_name' +]); +echo $user1->name; // 输出 '张三' + +// 场景2:使用第二个匹配的输入名称 +$user2 = MultiInputUser::from([ + 'other_name' => '李四' // 使用 'other_name' +]); +echo $user2->name; // 输出 '李四' + +// 场景3:使用最后的输入名称 +$user3 = MultiInputUser::from([ + 'userName' => '王五' // 使用 'userName' +]); +echo $user3->name; // 输出 '王五' + +// 场景4:传入多个的时候 按照声明顺序取地一个匹配的name +$user4 = MultiInputUser::from([ + 'userName' => '王五', + 'other_name' => '李四', + 'user_name' => '张三', +]); +echo $user4->name; // 输出 '张三' + +// 创建用户对象 +$user = MultiOutputUser::from([ + 'id' => 123, + 'name' => '张三' +]); + +// 转换为数组 +// tips: 因为id 有多个outputname 所以输出了 ['user_id','id','userId'] +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'user_id' => 123, +// 'id' => 123, +// 'userId' => 123, +// ] +``` + +##### 复杂映射场景 + +```php +use Astral\Serialize\Serialize; + +class ComplexUser extends Serialize { + // 嵌套对象的名称映射 + #[InputName('user_profile')] + public UserProfile $profile; + + // 数组元素的名称映射 + #[InputName('user_tags')] + public array $tags; +} + +// 处理复杂的输入结构 +$complexUser = ComplexUser::from([ + 'user_profile' => [ + 'nickname' => '小明', + 'age' => 25 + ], + 'user_tags' => ['developer', 'programmer'] +]); + +// 转换为标准数组 +$complexUserArray = $complexUser->toArray(); +// $complexUserArray 的内容: +// [ +// 'profile' => UserProfile Object ([ +// 'nickname' => '小明', +// 'age' => 25 +// ]), +// 'tags' => ['developer', 'programmer'] +// ] +``` + +##### Mapper映射 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Support\Mappers\{ + CamelCaseMapper, + SnakeCaseMapper, + PascalCaseMapper, + KebabCaseMapper +}; +use Astral\Serialize\Serialize; + +#[Groups('profile','api')] +class User extends Serialize { + // 直接指定映射名称 + #[InputName('user_name', groups: ['profile','api'])] + #[OutputName('userName', groups: ['profile','api'])] + public string $name; + + // 使用映射器进行风格转换 + #[InputName(CamelCaseMapper::class, groups: ['profile','api'])] + #[OutputName(SnakeCaseMapper::class, groups: ['profile','api'])] + public int $userId; + + // 支持多个映射和分组 + #[InputName('profile-email', groups: 'profile')] + #[OutputName('userEmail', groups: 'profile')] + public string $email; +} + +// 使用不同的映射策略 +$user = User::setGroups('profile')::from([ + 'user_name' => '张三', // 映射到 $name + 'userId' => 123, // 使用 CamelCaseMapper 转换 + 'profile-email' => 'user@example.com' // 仅在 'profile' 分组生效 +]); + +// 输出时应用不同的映射 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'userName' => '张三', +// 'user_id' => '三', +// 'userEmail' => user@example.com, +// ] +``` + +##### 全局类映射 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Support\Mappers\{ + CamelCaseMapper, + SnakeCaseMapper, + PascalCaseMapper, + KebabCaseMapper +}; +use Astral\Serialize\Serialize; + +#[InputName(SnakeCaseMapper::class)] +#[OutputName(CamelCaseMapper::class)] +class GlobalMappedUser extends Serialize { + // 类级别的映射会自动应用到所有属性 + public string $firstName; + public string $lastName; + public int $userId; + public DateTime $registeredAt; +} + +// 使用全局映射 +$user = GlobalMappedUser::from([ + 'first_name' => '张', // 从蛇形映射到 firstName + 'last_name' => '三', // 从蛇形映射到 lastName + 'user_id' => 123, // 从蛇形映射到 userId + 'registered_at' => '2023-01-01' // 从蛇形映射到 registeredAt +]); + +// 输出时会转换为驼峰命名 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'firstName' => '张', +// 'lastName' => '三', +// 'userId' => 123, +// 'registeredAt' => '2023-01-01' +// ] +``` + +###### 属性映射大于类级映射 + +```php + +#[InputName(SnakeCaseMapper::class)] +class PartialOverrideUser extends Serialize { + #[InputName(PascalCaseMapper::class)] + public string $userName; // 优先使用帕斯卡命名映射 + + public string $userEmail; // 继续使用类级别的全局映射 +} + +$partialUser = PartialOverrideUser::from([ + 'User_name' => '张三', // 使用蛇形映射 + 'UserName' => '李四', // 使用帕斯卡映射 + 'user_email' => 'user@example.com' // 使用蛇形映射 +]); + +$partialUser->toArray(); +// $partialUser 的内容: +// [ +// 'userName' => '李四', +// 'userEmail' => 'user@example.com', +// ] +``` + +###### 全局类映射的分组使用 + +需要搭配`Groups`注解一起使用 + +```php +use Astral\Serialize\Attributes\Groups; +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Support\Mappers\{ + CamelCaseMapper, + SnakeCaseMapper, + PascalCaseMapper, + KebabCaseMapper +}; +use Astral\Serialize\Serialize; + + +#[InputName(SnakeCaseMapper::class, groups: 'external')] +#[InputName(CamelCaseMapper::class, groups: 'api')] +#[OutputName(PascalCaseMapper::class, groups: ['external','api'])] +class ComplexMappedUser extends Serialize { + + #[Groups('external', 'api')] + public string $firstName; + + #[Groups('external', 'api')] + public string $lastName; + + + #[InputName('full_name', groups: 'special')] + #[OutputName('userEmail', groups: 'api')] + #[Groups('external', 'api')] + public string $fullName; +} + +// 使用admin分组 +$complexUser = ComplexMappedUser::setGroup('external')->from( + first_name :'张', + last_name :'三' + full_name: '张三' +); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'FirstName' => '张', +// 'LastName' => '三', +// 'FullName' => 张三, +// ] + +// 如果熟悉指定了OutputName/InputName 则属性规则优先 +// 使用public分组 +$complexUser = ComplexMappedUser::setGroup('api')->from( + first_name :'张', + last_name :'三' + full_name: '张三' +); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'FirstName' => '张', +// 'LastName' => '三', +// 'userEmail' => 张三, +// ] +``` + +#### 自定义映射器 + +```php +// 自定义映射器 需要继承NameMapper 并实现 resolve +class CustomMapper implements NameMapper { + public function resolve(string $name): string { + // 实现自定义的命名转换逻辑 + return str_replace('user', 'customer', $name); + } +} + +class AdvancedUser extends Serialize { + #[InputName(CustomMapper::class)] + public string $name; +} +``` + +#### 字段忽略 + +1. **安全性控制** + - 防止敏感信息的意外泄露 + - 精细控制数据的输入和输出 + +2. **数据过滤** + - 根据不同场景过滤字段 + - 为不同的 API 或用户角色定制数据视图 + +3. **性能优化** + - 减少不必要字段的序列化开销 + - 精简数据传输 + +##### 基础使用 + +```php +use Astral\Serialize\Attributes\InputIgnore; +use Astral\Serialize\Attributes\OutputIgnore; +use Astral\Serialize\Serialize; + + +class User extends Serialize { + + public string $name; + + // 输入时忽略的字段 + #[InputIgnore] + public string $internalId; + + // 输出时忽略的字段 + #[OutputIgnore] + public string $tempData; +} + +// 创建用户对象 +$user = User::from([ + 'name' => '张三', + 'internalId' => 'secret123', // 这个字段会被忽略 + 'tempData' => 'temporary' // 这个字段会被忽略 +]); + +echo $user->internalId; // 这里会输出 '' + +// 转换为数组 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'internalId' => '', +// ] +``` + +##### 分组忽略 + +忽略分组需要搭配Groups注解一起使用 + +```php +use Astral\Serialize\Attributes\Input\InputIgnore; +use Astral\Serialize\Attributes\Output\OutputIgnore; +use Astral\Serialize\Serialize; +use Astral\Serialize\Attributes\Groups; + +class ComplexUser extends Serialize { + + #[Groups('admin','public')] + #[InputIgnore('admin')] + public string $name; + + #[Groups('admin','public')] + #[OutputIgnore('public')] + public string $secretKey; + + #[Groups('admin','public')] + #[InputIgnore('admin')] + #[OutputIgnore('public')] + public string $sensitiveInfo; + + #[InputIgnore] + public string $globalInputIgnore; + + #[OutputIgnore] + public string $globalOutputIgnore; +} + +// 默认分组 +$complexUser = ComplexUser::from([ + 'name' => '张三', + 'secretKey' => 'confidential', + 'sensitiveInfo' => '机密信息', + 'globalInputIgnore' => '全局输入忽略', + 'globalOutputIgnore' => '全局输出忽略' +]); + +echo $complexUser->globalInputIgnore; // 输出 ‘’ +echo $complexUser->globalOutputIgnore; // 输出 ‘全局输出忽略’ + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'name' => '张三', +// 'secretKey' => 'confidential', +// 'sensitiveInfo' => '机密信息', +// 'globalInputIgnore' => '', +// ] + + +// 使用admin分组 +$complexUser = ComplexUser::setGroups('admin')->from([ + 'name' => '张三', + 'secretKey' => 'confidential', + 'sensitiveInfo' => '机密信息' + 'globalInputIgnore' => '全局输入忽略', + 'globalOutputIgnore' => '全局输出忽略' +]); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'name' => '', +// 'secretKey' => 'confidential', +// 'globalInputIgnore' => '', +// ] + +// 使用public分组 +$complexUser = ComplexUser::setGroups('public')->from([ + 'name' => '张三', + 'secretKey' => 'confidential', + 'sensitiveInfo' => '机密信息' + 'globalInputIgnore' => '全局输入忽略', + 'globalOutputIgnore' => '全局输出忽略' +]); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'name' => '张三', +/// 'globalInputIgnore' => '', +// ] +``` + +#### 时间转换 + +1. 格式灵活性 + 支持多种输入和输出时间格式 + 可以轻松处理不同地区的日期表示 +2. 时区处理 + 支持在不同时区间转换 + 自动处理时间的时区偏移 +3. 类型安全 + 自动将字符串转换为 DateTime 对象 + 保证类型的一致性和正确性 + +##### 基础使用 + +```php +use Astral\Serialize\Attributes\Input\InputDateFormat; +use Astral\Serialize\Attributes\Output\OutputDateFormat; +use Astral\Serialize\Serialize; + +class TimeExample extends Serialize { + + // 输入时间格式转换 + #[InputDateFormat('Y-m-d')] + public DateTime $dateTime; + + // 输入时间格式转换 + #[InputDateFormat('Y-m-d')] + public string $dateDateString; + + // 输出时间格式转换 + #[OutputDateFormat('Y/m/d H:i')] + public DateTime|string $processedAt; + + + // 支持多种时间格式 + #[InputDateFormat('Y-m-d H:i:s')] + #[OutputDateFormat('Y-m-d')] + public string $createdAt; +} + +// 创建订单对象 +$order = TimeExample::from([ + 'dateTime' => new DateTime('2023-08-11'), // 输入格式:Y-m-d + 'dateDateString' => '2023-08-15', // 输入格式:Y-m-d + 'processedAt' => '2023-08-16 14:30', // 输入默认格式 也支持DateTime对象 + 'createdAt' => '2023-08-16 14:45:30' // 输入格式:Y-m-d H:i:s +]); + +// 转换为数组 +$orderArray = $order->toArray(); +// $orderArray 的内容: +// [ +// 'dateTime' => '2023-08-11', +// ’dateDateString' => '2023-08-15', +// 'processedAt' => '2023/08/16 14:30', +// 'createdAt' => '2023-08-16' +// ] +``` + +##### 带时区的时间转换 + +```php + +use Astral\Serialize\Attributes\Input\InputDateFormat; +use Astral\Serialize\Attributes\Output\OutputDateFormat; +use Astral\Serialize\Serialize; + +class AdvancedTimeUser extends Serialize { + // 支持时区转换 + #[InputDateFormat('Y-m-d H:i:s', timezone: 'UTC')] + #[OutputDateFormat('Y-m-d H:i:s', timezone: 'Asia/Shanghai')] + public DateTime $registeredAt; + + // 支持不同地区的时间格式 + #[InputDateFormat('d/m/Y')] // 英国格式 + #[OutputDateFormat('Y-m-d')] // 标准格式 + public DateTime $birthDate; +} + +// 使用高级时间转换 +$advancedUser = AdvancedTimeUser::from([ + 'registeredAt' => '2023-08-16 10:00:00', // UTC 时间 + 'birthDate' => '15/08/1990' // 英国日期格式 +]); + +$advancedUserArray = $advancedUser->toArray(); +// $advancedUserArray 的内容: +// [ +// 'registeredAt' => '2023-08-16 18:00:00', // 转换为上海时区 +// 'birthDate' => '1990-08-15' +// ] +``` + +## Faker + +### 简单属性模拟 + +```php +class UserFaker extends Serialize { + #[FakerValue('name')] + public string $name; + + #[FakerValue('email')] + public string $email; + + #[FakerValue('uuid')] + public string $userId; + + #[FakerValue('phoneNumber')] + public string $phone; + + #[FakerValue('age')] + public int $age; + + #[FakerValue('boolean')] + public bool $isActive; +} + +$user = UserFaker::faker(); + +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// "name" => "John Doe" +// "email" => "john.doe@example.com" +// "userId" => "550e8400-e29b-41d4-a716-446655440000" +// "phone" => "+1-555-123-4567" +// "age" => 35 +// "isActive" => true +// ] +``` + +### 集合模拟 + +```php + +class UserProfile extends Serialize { + public string $nickname; + public int $age; + public string $email; + public string $avatar; +} + +class UserListFaker extends Serialize { + #[FakerCollection(['name', 'email'], num: 3)] + public array $users; + + #[FakerCollection(UserProfile::class, num: 2)] + public array $profiles; +} + +$userList = UserListFaker::faker(); + +$complexUserListFaker = UserListFaker::faker(); + +$complexUserListFakerArray = $complexUserListFaker->toArray(); +// $complexUserListFakerArray 的内容: +// [ +// 'profile' => [ +// [0] => UserProfile Object ( +// [ +// 'nickname' => 'RandomNickname', +// 'age' => 28, 'email' => 'random.user@example.com', +// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' +// ], +// ), +// [1] => UserProfile Object ( +// [ +// 'nickname' => 'RandomNickname', +// 'age' => 28, 'email' => 'random.user@example.com', +// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' +// ], +// ) +// ], +// 'users' => [ +// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] +// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] +// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] +// ] +// ] +``` + +### 嵌套对象模拟 + +#### 基本用法 + +```php +class ComplexUserFaker extends Serialize { + #[FakerObject(UserProfile::class)] + public UserProfile $profile; +} +``` + +#### 演示实例 + +```php +use Astral\Serialize\Serialize; +use Astral\Serialize\Attributes\FakerObject; +use Astral\Serialize\Attributes\FakerCollection; + +class UserProfile extends Serialize { + public string $nickname; + public int $age; + public string $email; + public string $avatar; +} + +class UserTag extends Serialize { + public string $name; + public string $color; +} + +class ComplexUserFaker extends Serialize { + #[FakerObject(UserProfile::class)] + public UserProfile $profile; + + #[FakerObject(UserTag::class)] + public UserTag|UserProfile $primaryTag; + +} + +$complexUserFaker = ComplexUserFaker::faker(); + +$complexUserFakerArray = $complexUserFaker->toArray(); +// $complexUserFakerArray 的内容: +// [ +// 'profile' => UserProfile Object ( +// ['nickname' => 'RandomNickname', 'age' => 28, 'email' => 'random.user@example.com', 'avatar' => 'https://example.com/avatars/random-avatar.jpg'] +// ), +// 'primaryTag' => UserTag Object ( +// ['name' => 'Developer', 'color' => '#007bff'] +// ) +// ] +``` + +### Faker类方法模拟 + +```php +class UserService { + public function generateUserData(): array { + return ['name' => 'Generated User']; + } +} + +class UserFaker extends Serialize { + #[FakerMethod(UserService::class, 'generateUserData')] + public array $userData; +} +``` + +#### 完整的示例 + +```php +use Astral\Serialize\Serialize; +use Astral\Serialize\Attributes\Faker\FakerMethod; +use Astral\Serialize\Attributes\Faker\FakerObject; +use Astral\Serialize\Attributes\Faker\FakerCollection; + +// 用户配置文件类 +class UserProfile extends Serialize { + public string $nickname; + public int $age; + public string $email; + public array $types = ['type1' => 'money', 'type2' => 'score']; +} + +// 用户服务类,提供数据生成方法 +class UserService { + public function generateUserData(): array { + return [ + 'name' => 'Generated User', + 'email' => 'generated.user@example.com', + 'age' => 30 + ]; + } + + public function generateUserProfile(UserProfile $user): UserProfile { + return $user; + } + + public function generateUserList(int $count): array { + $users = []; + for ($i = 0; $i < $count; $i++) { + $users[] = [ + 'name' => "User {$i}", + 'email' => "user{$i}@example.com" + ]; + } + return $users; + } +} + +// Faker 方法模拟示例 +class UserFaker extends Serialize { + // 使用方法生成简单数据 + #[FakerMethod(UserService::class, 'generateUserData')] + public array $userData; + + // 使用方法生成对象 + #[FakerMethod(UserService::class, 'generateUserProfile')] + public UserProfile $userProfile; + + // 获取指定属性 + #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'age')] + public int $age; + + // 获取指定属性 多级可以使用[.]链接 + #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'types.type2')] + public string $type2; + + // 传入参数 + #[FakerMethod(UserService::class, 'generateUserList',params:['count'=> 3])] + public array $userList; +} + +// 生成模拟数据 +$userFaker = UserFaker::faker(); + +// 转换为数组 +$userFakerArray = $userFaker->toArray(); +// $userFakerArray 的内容: +// [ +// 'userData' => [ +// 'name' => 'Generated User', +// 'email' => 'generated.user@example.com', +// 'age' => 30 +// ], +// 'userProfile' => UserProfile Object ( +// [ +// 'nickname' => 'GeneratedNickname', +// 'age' => 25, // 随机生成 +// 'email' => 'profile@example.com' +// 'types' => ['type1' => 'money', 'type2' => 'score'] +// ] +// ), +// 'age' => 99 , // 随机生成 +// 'type2' => 'score', +// 'userList' => [ +// ['name' => 'User 0', 'email' => 'user0@example.com'], +// ['name' => 'User 1', 'email' => 'user1@example.com'], +// ['name' => 'User 2', 'email' => 'user2@example.com'] +// ] +// ] +``` diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md new file mode 100644 index 0000000..0673a8d --- /dev/null +++ b/docs/en/SUMMARY.md @@ -0,0 +1,5 @@ +# Summary + +* [介绍](README.md) +* [快速开始](getting-started.md) +* [进阶使用](base-mapper.md) \ No newline at end of file diff --git a/docs/en/base-mapper.md b/docs/en/base-mapper.md new file mode 100644 index 0000000..9fca977 --- /dev/null +++ b/docs/en/base-mapper.md @@ -0,0 +1,70 @@ +### 类型转换 + +#### 基本类型转换 + +##### 方式一:构造函数属性提升 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public function __construct( + public string $username, + public int $score, + public float $balance, + public bool $isActive + ) {} +} +``` + +##### 方式二:传统属性定义 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public string $username; + public int $score; + public float $balance; + public bool $isActive; +} + +// 两种方式都支持相同的类型转换 +$profile = Profile::from([ + 'username' => 123, // 整数转换为字符串 + 'score' => '100', // 字符串转换为整数 + 'balance' => '99.99', // 字符串转换为浮点数 + 'isActive' => 1 // 数字转换为布尔值 +]); + +// 转换为数组 +$profileArray = $profile->toArray(); +``` + +##### 方式三:只读属性 + +```php +use Astral\Serialize\Serialize; + +class Profile extends Serialize { + public readonly string $username; + public readonly int $score; + public readonly float $balance; + public readonly bool $isActive; + + // 手动初始化 + public function __construct( + string $username, + int $score, + float $balance, + bool $isActive + ) { + $this->username = $username; + $this->score = $score; + $this->balance = $balance; + $this->isActive = $isActive; + } +} +``` + +无论使用哪种方式,`Serialize` 类都能正常工作,并提供相同的类型转换和序列化功能。 \ No newline at end of file diff --git a/docs/en/getting-started.md b/docs/en/getting-started.md new file mode 100644 index 0000000..9a2108e --- /dev/null +++ b/docs/en/getting-started.md @@ -0,0 +1,94 @@ +## 快速开始 + +### 安装 + +使用 Composer 安装: + +```bash +composer require astral/serialize +``` + +### 基本用法 + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public string $name, + public int $age +} + +// 从数组创建对象 +$user = User::from([ + 'name' => '张三', + 'age' => 30 +]); + +// 访问对象属性 +echo $user->name; // 输出: 张三 +echo $user->age; // 输出: 30 + +// 转换为数组 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'age' => 30 +// ] +``` + +### 其他特性 + +#### **不可变性**:只读属性在构造后无法修改 + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public function __construct( + public readonly string $name, + public readonly int $age + ) {} +} + +$user = User::from([ + 'name' => '张三', + 'age' => 30 +]); + +try { + $user->name = '李四'; // 编译时错误:无法修改只读属性 +} catch (Error $e) { + echo "只读属性不能被重新赋值"; +} +``` + +#### **类型安全的初始化** + +```php +$user = User::from([ + 'name' => 123, // 整数会被转换为字符串 + 'age' => '35' // 字符串会被转换为整数 +]); + +echo $user->name; // 输出: "123" +echo $user->age; // 输出: 35 +``` + +#### **构造函数初始化** + +```php +use Astral\Serialize\Serialize; + +class User extends Serialize { + public function __construct( + public readonly string $name, + public readonly int $age + ) { + // 可以在构造函数中添加额外的验证或处理逻辑 + if (strlen($name) < 2) { + throw new \InvalidArgumentException('名称太短'); + } + } +} +``` \ No newline at end of file diff --git a/docs/.gitbook.yaml b/docs/zh/.gitbook.yaml similarity index 100% rename from docs/.gitbook.yaml rename to docs/zh/.gitbook.yaml From 8ddc8bff1dce1351792318e721274f331e6d0892 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 16:00:30 +0800 Subject: [PATCH 29/59] gitbook test --- docs/en/.gitbook.yaml | 4 ++-- docs/zh/.gitbook.yaml | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/en/.gitbook.yaml b/docs/en/.gitbook.yaml index f455ec9..692a078 100644 --- a/docs/en/.gitbook.yaml +++ b/docs/en/.gitbook.yaml @@ -1,4 +1,4 @@ root: ./ structure: - readme: ./en/README.md - summary: ./en/SUMMARY.md \ No newline at end of file + readme: README.md + summary: SUMMARY.md \ No newline at end of file diff --git a/docs/zh/.gitbook.yaml b/docs/zh/.gitbook.yaml index 4f8fe5f..692a078 100644 --- a/docs/zh/.gitbook.yaml +++ b/docs/zh/.gitbook.yaml @@ -1,4 +1,4 @@ root: ./ structure: - readme: ./zh/README.md - summary: ./zh/SUMMARY.md \ No newline at end of file + readme: README.md + summary: SUMMARY.md \ No newline at end of file From 3c5fceac3af1d1905f228be74fc933fcd0aa96cb Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 16:06:18 +0800 Subject: [PATCH 30/59] gitbook test --- docs/en/README.md | 1070 +++++++++++++++++--------------------------- docs/en/SUMMARY.md | 4 +- 2 files changed, 402 insertions(+), 672 deletions(-) diff --git a/docs/en/README.md b/docs/en/README.md index 60576c3..4cc0adf 100644 --- a/docs/en/README.md +++ b/docs/en/README.md @@ -1,16 +1,16 @@ -# Astral Serialize 文档 +# Astral Serialize Documentation -## 快速开始 +## Quick Start -### 安装 +### Installation -使用 Composer 安装: +Install using Composer: ```bash composer require astral/serialize ``` -### 基本用法 +### Basic Usage ```php use Astral\Serialize\Serialize; @@ -20,28 +20,28 @@ class User extends Serialize { public int $age } -// 从数组创建对象 +// Create object from array $user = User::from([ - 'name' => '张三', + 'name' => 'John Doe', 'age' => 30 ]); -// 访问对象属性 -echo $user->name; // 输出: 张三 -echo $user->age; // 输出: 30 +// Access object properties +echo $user->name; // Output: John Doe +echo $user->age; // Output: 30 -// 转换为数组 +// Convert to array $userArray = $user->toArray(); -// $userArray 的内容: +// $userArray contents: // [ -// 'name' => '张三', +// 'name' => 'John Doe', // 'age' => 30 // ] ``` -#### 其他特性 +### Other Features -1. **不可变性**:只读属性在构造后无法修改 +1. **Immutability**: Read-only properties cannot be modified after construction ```php use Astral\Serialize\Serialize; @@ -54,30 +54,30 @@ class User extends Serialize { } $user = User::from([ - 'name' => '张三', + 'name' => 'John Doe', 'age' => 30 ]); try { - $user->name = '李四'; // 编译时错误:无法修改只读属性 + $user->name = 'Jane Doe'; // Compile-time error: cannot modify read-only property } catch (Error $e) { - echo "只读属性不能被重新赋值"; + echo "Read-only properties cannot be reassigned"; } ``` -2. **类型安全的初始化** +2. **Type-Safe Initialization** ```php $user = User::from([ - 'name' => 123, // 整数会被转换为字符串 - 'age' => '35' // 字符串会被转换为整数 + 'name' => 123, // Integer will be converted to string + 'age' => '35' // String will be converted to integer ]); -echo $user->name; // 输出: "123" -echo $user->age; // 输出: 35 +echo $user->name; // Output: "123" +echo $user->age; // Output: 35 ``` -3. **构造函数初始化** +3. **Constructor Initialization** ```php use Astral\Serialize\Serialize; @@ -87,73 +87,21 @@ class User extends Serialize { public readonly string $name, public readonly int $age ) { - // 可以在构造函数中添加额外的验证或处理逻辑 + // Can add additional validation or processing logic in the constructor if (strlen($name) < 2) { - throw new \InvalidArgumentException('名称太短'); + throw new \InvalidArgumentException('Name is too short'); } } } ``` -## 生成openapi文档 +## DTO Conversion -#### 创建Request文件 -```php -use Astral\Serialize\Serialize; - -class UserAddRequest extends Serialize { - public string $name; - public int $id; -} - -class UserDetailRequest extends Serialize { - public int $id; -} -``` - -#### 创建Repose文件 -```php -use Astral\Serialize\Serialize; - -class UserDto extends Serialize { - public string $name, - public int $id; -} -``` +### Type Conversion -#### 创建Controller文件 -```php -use Astral\Serialize\Serialize; -use Astral\Serialize\OpenApi\Enum\MethodEnum; - -#[\Astral\Serialize\OpenApi\Annotations\Tag('用户模块管理')] -class UserController { - - #[\Astral\Serialize\OpenApi\Annotations\Summary('创建用户')] - #[\Astral\Serialize\OpenApi\Annotations\Route('/user/create')] - #[\Astral\Serialize\OpenApi\Annotations\RequestBody(UserAddRequest::class)] - #[\Astral\Serialize\OpenApi\Annotations\Response(UserDto::class)] - public function create() - { - return new UserDto(); - } - - #[\Astral\Serialize\OpenApi\Annotations\Summary('用户详情')] - #[\Astral\Serialize\OpenApi\Annotations\Route(route:'/user/detail', method: MethodEnum::GET)] - public function detail(UserDetailRequest $request): UserDto - { - return new UserDto(); - } -} -``` - -## DTO 转换 - -### 类型转换 +#### Basic Type Conversion -#### 基本类型转换 - -##### 方式一:构造函数属性提升 +##### Method One: Constructor Property Promotion ```php use Astral\Serialize\Serialize; @@ -168,7 +116,7 @@ class Profile extends Serialize { } ``` -##### 方式二:传统属性定义 +##### Method Two: Traditional Property Definition ```php use Astral\Serialize\Serialize; @@ -180,19 +128,19 @@ class Profile extends Serialize { public bool $isActive; } -// 两种方式都支持相同的类型转换 +// Both methods support the same type conversion $profile = Profile::from([ - 'username' => 123, // 整数转换为字符串 - 'score' => '100', // 字符串转换为整数 - 'balance' => '99.99', // 字符串转换为浮点数 - 'isActive' => 1 // 数字转换为布尔值 + 'username' => 123, // Integer converted to string + 'score' => '100', // String converted to integer + 'balance' => '99.99', // String converted to float + 'isActive' => 1 // Number converted to boolean ]); -// 转换为数组 +// Convert to array $profileArray = $profile->toArray(); ``` -##### 方式三:只读属性 +##### Method Three: Read-Only Properties ```php use Astral\Serialize\Serialize; @@ -203,7 +151,7 @@ class Profile extends Serialize { public readonly float $balance; public readonly bool $isActive; - // 手动初始化 + // Manual initialization public function __construct( string $username, int $score, @@ -218,18 +166,18 @@ class Profile extends Serialize { } ``` -无论使用哪种方式,`Serialize` 类都能正常工作,并提供相同的类型转换和序列化功能。 +Regardless of the method used, the `Serialize` class will work normally and provide the same type conversion and serialization functionality. -#### 枚举转换 +#### Enum Conversion -枚举转换提供了强大且灵活的枚举处理机制,支持多种枚举类型和转换场景。 +Enum conversion provides a powerful and flexible enum handling mechanism, supporting multiple enum types and conversion scenarios. -- 支持 `tryFrom()` 和 `cases()` 方法的枚举类型 -- 输入时自动将字符串转换为枚举实例 -- 输出时自动将枚举转换为字符串(枚举名称) -- 提供灵活且安全的枚举处理机制 +- Supports enums with `tryFrom()` and `cases()` methods +- Automatically converts strings to enum instances during input +- Automatically converts enums to strings (enum names) during output +- Provides flexible and safe enum handling mechanisms -##### 普通枚举 +##### Regular Enum ```php enum UserRole { @@ -242,26 +190,26 @@ class ComplexUser extends Serialize { public UserRole $role; - // 支持多种枚举类型 + // Supports multiple enum types public UserStatus|UserRole $mixedStatus; } $complexUser = ComplexUser::from([ - 'role' => 'ADMIN', // 自动转换为 UserRole::ADMIN - 'mixedStatus' => 'ACTIVE' // 可以是 UserStatus 或 UserRole + 'role' => 'ADMIN', // Automatically converted to UserRole::ADMIN + 'mixedStatus' => 'ACTIVE' // Can be UserStatus or UserRole ]); -echo $complexUser->role; // 返回 UserRole枚举实例 +echo $complexUser->role; // Returns UserRole enum instance $complexUserArray = $complexUser->toArray(); -// $complexUserArray 的内容: +// $complexUserArray contents: // [ // 'role' => 'ADMIN', // 'mixedStatus' => 'ACTIVE' // ] ``` -##### 回退枚举 +##### Backed Enum ```php use Astral\Serialize\Serialize; @@ -273,39 +221,39 @@ enum UserStatus: string { case SUSPENDED = 'suspended'; } -// 定义带有枚举的用户类 +// Define a user class with enum class User extends Serialize { public string $name; - // 支持 UnitEnum 和 BackedEnum + // Supports UnitEnum and BackedEnum public UserStatus $status; - // 支持多枚举类型 + // Supports multiple enum types public UserStatus|string $alternateStatus; } -// 创建用户对象 +// Create user object $user = User::from([ - 'name' => '张三', - 'status' => 'active', // 自动转换为 UserStatus::ACTIVE - 'alternateStatus' => 'inactive' // 支持字符串或枚举值 + 'name' => 'John Doe', + 'status' => 'active', // Automatically converted to UserStatus::ACTIVE + 'alternateStatus' => 'inactive' // Supports string or enum values ]); -var_dump($user->status); // 输出: UserStatus::ACTIVE +var_dump($user->status); // Output: UserStatus::ACTIVE -// 转换为数组 +// Convert to array $userArray = $user->toArray(); -// $userArray 的内容: +// $userArray contents: // [ -// 'name' => '张三', -// 'status' => 'ACTIVE', // 输出枚举名称 +// 'name' => 'John Doe', +// 'status' => 'ACTIVE', // Output enum name // 'alternateStatus' => 'INACTIVE' // ] ``` -#### Null 值转换规则详细示例 +#### Null Value Conversion Rules Detailed Example -当属性不是可空类型(`?type`)时,`null` 值会根据目标类型自动转换: +When the property is not a nullable type (`?type`), `null` values will be automatically converted based on the target type: ```php use Astral\Serialize\Serialize; @@ -318,35 +266,35 @@ class NullConversionProfile extends Serialize { public object $metadata; } -// Null 值转换示例 +// Null value conversion example $profile = NullConversionProfile::from([ - 'username' => null, // 转换为空字符串 '' - 'score' => null, // 转换为 0 - 'balance' => null, // 转换为 0.0 - 'tags' => null, // 转换为空数组 [] - 'metadata' => null // 转换为空对象 new stdClass() + 'username' => null, // Converted to empty string '' + 'score' => null, // Converted to 0 + 'balance' => null, // Converted to 0.0 + 'tags' => null, // Converted to empty array [] + 'metadata' => null // Converted to empty object new stdClass() ]); -// 验证转换结果 -echo $profile->username; // 输出: ""(空字符串) -echo $profile->score; // 输出: 0 -echo $profile->balance; // 输出: 0.0 -var_dump($profile->tags); // 输出: array(0) {} -var_dump($profile->metadata); // 输出: object(stdClass)#123 (0) {} +// Verify conversion results +echo $profile->username; // Output: ""(empty string) +echo $profile->score; // Output: 0 +echo $profile->balance; // Output: 0.0 +var_dump($profile->tags); // Output: array(0) {} +var_dump($profile->metadata); // Output: object(stdClass)#123 (0) {} -// 布尔值的特殊处理 +// Special handling for boolean values try { NullConversionProfile::from([ - 'isActive' => null // 这将抛出类型错误 + 'isActive' => null // This will throw a type error ]); } catch (\TypeError $e) { - echo "布尔类型不支持 null 值:" . $e->getMessage(); + echo "Boolean type does not support null values: " . $e->getMessage(); } ``` -#### 可空类型的方案 +#### Nullable Type Solution -对于需要接受 `null` 的场景,使用可空类型: +For scenarios requiring `null` acceptance, use nullable types: ```php use Astral\Serialize\Serialize; @@ -360,17 +308,17 @@ class FlexibleProfile extends Serialize { ) {} } -// 创建包含 null 值的对象 +// Create an object with null values $profile = FlexibleProfile::from([ - 'username' => null, // 允许 null - 'score' => null, // 允许 null - 'metadata' => null, // 允许 null - 'tags' => null // 允许 null + 'username' => null, // Allows null + 'score' => null, // Allows null + 'metadata' => null, // Allows null + 'tags' => null // Allows null ]); -// 转换为数组 +// Convert to array $profileArray = $profile->toArray(); -// $profileArray 的内容: +// $profileArray contents: // [ // 'username' => null, // 'score' => null, @@ -378,127 +326,127 @@ $profileArray = $profile->toArray(); // 'tags' => null // ] -// 验证可空类型的行为 -echo $profile->username; // 输出 null +// Validate nullable type behavior +echo $profile->username; // Output null ``` -#### 联合类型 +#### Union Types -1. 可以混合使用基本类型和对象类型 -2. 对象层级匹配 - 对于多个对象类型,会选择最匹配的类型 - 支持继承层级的智能匹配 -3. 动态类型处理 - 自动处理不同类型的输入 - 提供更加灵活的数据建模方式 +1. Can mix basic and object types +2. Object Hierarchy Matching + - For multiple object types, the most matching type will be selected + - Supports intelligent matching of inheritance hierarchy +3. Dynamic Type Handling + - Automatically handles input of different types + - Provides more flexible data modeling ```php use Astral\Serialize\Serialize; -// 定义一个基础用户类 +// Define a base user class class User extends Serialize { public string $name; public int $age; } -// 定义一个管理员用户类 +// Define an admin user class class AdminUser extends User { public string $role; } class FlexibleData extends Serialize { - // 支持整数或字符串类型的标识符 + // Supports integer or string type identifiers public int|string $flexibleId; - // 支持用户对象或整数标识符 + // Supports user object or integer identifier public User|int $userIdentifier; - // 支持多种复杂的联合类型 + // Supports complex union types public AdminUser|User|int $complexIdentifier; } -// 场景1:使用整数作为 flexibleId -$data1 = FlexibleData::from([ - 'flexibleId' => 123, - 'userIdentifier' => 456, - 'complexIdentifier' => 789 -]); +// Scenario 1: Use integer as flexibleId +$data1 = FlexibleData::from( + flexibleId : 123, + userIdentifier : 456, + complexIdentifier : 789 +); $data1Array = $data1->toArray(); -// $data1Array 的内容: +// $data1Array contents: // [ // 'flexibleId' => 123, // 'userIdentifier' => 456, // 'complexIdentifier' => 789 // ] -// 场景2:使用字符串作为 flexibleId -$data2 = FlexibleData::from([ - 'flexibleId' => 'ABC123', - 'userIdentifier' => [ - 'name' => '张三', +// Scenario 2: Use string as flexibleId +$data2 = FlexibleData::from( + flexibleId : 'ABC123', + userIdentifier : [ + 'name' => 'John', 'age' => 30 ], - 'complexIdentifier' => [ - 'name' => '李四', + complexIdentifier : [ + 'name' => 'Jane', 'age' => 25 ] -]); +); -echo $data2->userIdentifier; // 输出 User 对象 -echo $data2->complexIdentifier; // 输出 User 对象 +echo $data2->userIdentifier; // Output User object +echo $data2->complexIdentifier; // Output User object $data2Array = $data2->toArray(); -// $data2Array 的内容: +// $data2Array contents: // [ // 'flexibleId' => 'ABC123', // 'userIdentifier' => User Object ( -// ['name' => '张三', 'age' => 30] +// ['name' => 'John', 'age' => 30] // ), // 'complexIdentifier' => User Object ( -// ['name' => '李四', 'age' => 25] +// ['name' => 'Jane', 'age' => 25] // ) // ] -// 场景3:使用管理员用户 -$data3 = FlexibleData::from([ - 'flexibleId' => 'USER001', - 'userIdentifier' => [ - 'name' => '王五', +// Scenario 3: Use admin user +$data3 = FlexibleData::from( + flexibleId : 'USER001', + userIdentifier : [ + 'name' => 'Bob', 'age' => 35, 'role' => 'admin' ], - 'complexIdentifier' => [ - 'name' => '赵六', + complexIdentifier : [ + 'name' => 'Alice', 'age' => 40, 'role' => 'super_admin' ] -]); +); -echo $data2->userIdentifier; // 输出 User 对象 -echo $data2->complexIdentifier; // 输出 AdminUser 对象 +echo $data2->userIdentifier; // Output User object +echo $data2->complexIdentifier; // Output AdminUser object $data3Array = $data3->toArray(); -// $data3Array 的内容: +// $data3Array contents: // [ // 'flexibleId' => 'USER001', // 'userIdentifier' => User Object ( -// ['name' => '王五', 'age' => 35] +// ['name' => 'Bob', 'age' => 35] // ), // 'complexIdentifier' => AdminUser Object ( -// ['name' => '赵六', 'age' => 40, 'role' => 'super_admin'] +// ['name' => 'Alice', 'age' => 40, 'role' => 'super_admin'] // ) // ] ``` -#### 数组对象转换 +#### Array Object Conversion -##### phpDoc定义 +##### phpDoc Definition ```php use Astral\Serialize\Serialize; -// 定义基础数组类型 +// Define basic array types class ArrayOne extends Serialize { public string $type = 'one'; public string $name; @@ -510,36 +458,36 @@ class ArrayTwo extends Serialize { } class MultiArraySerialize extends Serialize { - // 场景1:混合类型数组 + // Scenario 1: Mixed type array /** @var (ArrayOne|ArrayTwo)[] */ public array $mixedTypeArray; - // 场景2:多类型数组 + // Scenario 2: Multiple type arrays /** @var ArrayOne[]|ArrayTwo[] */ public array $multiTypeArray; - // 场景3:键值对混合类型 + // Scenario 3: Key-value mixed type /** @var array(string, ArrayOne|ArrayTwo) */ public array $keyValueMixedArray; } -// 场景1:混合类型数组 +// Scenario 1: Mixed type array $data1 = MultiArraySerialize::from( mixedTypeArray : [ - ['name' => '张三'], // 转化 ArrayOne 对象 - ['code' => 'ABC123'], // 转化 ArrayTwo 对象 - ['name' => '李四'], // 转化 ArrayOne 对象 - ['code' => 'DEF456'] // 转化 ArrayTwo 对象 + ['name' => 'John'], // Convert to ArrayOne object + ['code' => 'ABC123'], // Convert to ArrayTwo object + ['name' => 'Jane'], // Convert to ArrayOne object + ['code' => 'DEF456'] // Convert to ArrayTwo object ] ); $data1Array = $data1->toArray(); -// $data1Array 的内容: +// $data1Array contents: // [ // 'mixedTypeArray' => [ // [0] => ArrayOne Object // ( -// ['name' => '张三', 'type' => 'one'], +// ['name' => 'John', 'type' => 'one'], // ) // [1] => ArrayTwo Object // ( @@ -547,7 +495,7 @@ $data1Array = $data1->toArray(); // ) // [2] => ArrayOne Object // ( -// ['name' => '李四', 'type' => 'one'], +// ['name' => 'Jane', 'type' => 'one'], // ) // [3] => ArrayTwo Object // ( @@ -556,24 +504,24 @@ $data1Array = $data1->toArray(); // ] // ] -// 场景2:多类型数组 +// Scenario 2: Multiple type arrays $data2 = MultiArraySerialize::from( multiTypeArray:[ - ['name' => '王五'], // 转化 ArrayOne 对象 - ['name' => '赵六'], // 转化 ArrayOne 对象 - ['code' => 'GHI789'] // 转化 ArrayTwo 对象 + ['name' => 'Bob'], // Convert to ArrayOne object + ['name' => 'Alice'], // Convert to ArrayOne object + ['code' => 'GHI789'] // Convert to ArrayTwo object ] ); $data2Array = $data2->toArray(); -// $data2Array 的内容: +// $data2Array contents: // [ // 'multiTypeArray' => [ // ArrayOne Object ( -// ['name' => '王五', 'type' => 'one'] +// ['name' => 'Bob', 'type' => 'one'] // ), // ArrayOne Object ( -// ['name' => '赵六', 'type' => 'one'] +// ['name' => 'Alice', 'type' => 'one'] // ), // ArrayTwo Object ( // ['code' => 'GHI789', 'type' => 'two'] @@ -581,32 +529,32 @@ $data2Array = $data2->toArray(); // ] // ] -// 场景3:键值对混合类型 +// Scenario 3: Key-value mixed type $data3 = MultiArraySerialize::from( keyValueMixedArray: [ - 'user1' => ['name' => '张三'], // 转化 ArrayOne 对象 - 'system1' => ['code' => 'ABC123'], // 转化 ArrayTwo 对象 - 'user2' => ['name' => '李四'] // 转化 ArrayOne 对象 + 'user1' => ['name' => 'John'], // Convert to ArrayOne object + 'system1' => ['code' => 'ABC123'], // Convert to ArrayTwo object + 'user2' => ['name' => 'Jane'] // Convert to ArrayOne object ] ); $data3Array = $data3->toArray(); -// $data3Array 的内容: +// $data3Array contents: // [ // 'keyValueMixedArray' => [ // 'user1' => ArrayOne Object ( -// ['name' => '张三', 'type' => 'one'] +// ['name' => 'John', 'type' => 'one'] // ), // 'system1' => ArrayTwo Object ( // ['code' => 'ABC123', 'type' => 'two'] // ), // 'user2' => ArrayOne Object ( -// ['name' => '李四', 'type' => 'one'] +// ['name' => 'Jane', 'type' => 'one'] // ) // ] // ] -// 场景4:无法匹配时的处理 +// Scenario 4: Handling unmatched cases $data4 = MultiArraySerialize::from( mixedTypeArray : [ ['unknown' => 'data1'], @@ -615,7 +563,7 @@ $data4 = MultiArraySerialize::from( ); $data4Array = $data4->toArray(); -// $data4Array 的内容: +// $data4Array contents: // [ // 'mixedTypeArray' => [ // ['unknown' => 'data1'], @@ -624,15 +572,15 @@ $data4Array = $data4->toArray(); // ] ``` -### 注解类使用 +### Annotation Class Usage -#### 属性分组 +#### Property Grouping -属性分组提供了一种灵活的方式来控制属性的输入和输出行为,允许在不同场景下精细地管理数据转换。 +Property grouping provides a flexible way to control the input and output behavior of properties, allowing fine-grained data conversion management in different scenarios. -##### 基本用法 +##### Basic Usage -在属性上使用 `#[Groups]` 注解来指定属性所属的分组。 +Use the `#[Groups]` annotation on properties to specify the groups they belong to. ```php use Astral\Serialize\Attributes\Groups; @@ -652,10 +600,10 @@ class User extends Serialize { #[Groups('other')] public string $sensitiveData; - // 没有指定Group 的属性将会被默认分组在default分组中 + // Properties without a specified group will be in the default group public string $noGroupInfo; - // 构造函数参数也支持分组 + // Constructor parameters also support grouping public function __construct( #[Groups('create','detail')] public readonly string $email, @@ -665,118 +613,95 @@ class User extends Serialize { ) {} } - - -// 使用 默认分组展示所有信息 +// Use default group to display all information $user1 = User::from( id:1, - name: '李四', + name: 'Jane', score: 100, username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' + email: 'jane@example.com', + sensitiveData:'Confidential info', + noGroupInfo:'Default group info' ); -// 使用默认分组 toArray,展示所有信息 +// Use default group toArray, display all information $defaultArray = $user1->toArray(); -// $defaultArray 的内容: +// $defaultArray contents: // [ // 'id' => '1', -// 'name' => '李四', +// 'name' => 'Jane', // 'username' => 'username', // 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// 'noGroupInfo' => '默认分组信息' +// 'email' => 'jane@example.com', +// 'sensitiveData' => 'Confidential info', +// 'noGroupInfo' => 'Default group info' // ] -// 指定分组内容输入 -$defaultArray = $user1->withGroups('create')->toArray(); -// 输出内容 -// [ -// 'name' => '李四', -// 'username' => 'username', -// 'email' => 'zhangsan@example.com', -// ] - -$defaultArray = $user1->withGroups(['detail','other'])->toArray(); -// 输出内容 -// [ -// 'id' => '1', -// 'name' => '李四', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// ] - - -// 使用 create 分组创建用户 只会接受group为create的数据信息 +// Use create group to create user, only accept data with create group $user2 = User::setGroups(['create'])->from( id:1, - name: '李四', + name: 'Jane', score: 100, username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' + email: 'jane@example.com', + sensitiveData:'Confidential info', + noGroupInfo:'Default group info' ); -// 使用 create 分组 toArray +// Use create group toArray $createArray = $user2->toArray(); -// $createArray 的内容: +// $createArray contents: // [ -// 'name' => '李四', +// 'name' => 'Jane', // 'username' => 'username', -// 'email' => 'zhangsan@example.com', +// 'email' => 'jane@example.com', // ] -// 使用 update 分组更新用户 只会接受group为update的数据信息 +// Use update group to update user, only accept data with update group $user3 = User::setGroups(['update'])->from( id:1, - name: '李四', + name: 'Jane', score: 100, username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' + email: 'jane@example.com', + sensitiveData:'Confidential info', + noGroupInfo:'Default group info' ); -// 使用 update 分组 toArray +// Use update group toArray $updateArray = $user3->toArray(); -// $updateArray 的内容: +// $updateArray contents: // [ // 'id' => '1', -// 'name' => '李四', +// 'name' => 'Jane', // 'score' => 100, // ] -// 使用 detail 和 other 展示用户 会接受group为detail和other的数据信息 +// Use detail and other groups to display user, accept data with detail and other groups $user4 = User::setGroups(['detail','other'])->from( id:1, - name: '李四', + name: 'Jane', score: 100, username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' + email: 'jane@example.com', + sensitiveData:'Confidential info', + noGroupInfo:'Default group info' ); -// 使用多个分组 toArray +// Use multiple groups toArray $multiGroupArray = $user4->toArray(); -// $multiGroupArray 的内容: +// $multiGroupArray contents: // [ // 'id' => '1', -// 'name' => '李四', +// 'name' => 'Jane', // 'username' => 'username', // 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', +// 'email' => 'jane@example.com', +// 'sensitiveData' => 'Confidential info', // ] ``` -##### 嵌套类指定Group类展示 +##### Nested Class Group Display ```php class ComplexUser extends Serialize { @@ -796,21 +721,21 @@ class ComplexNestedInfo extends Serialize { public string $currency; } -// ComplexNestedInfo 会自动隐藏currency +// ComplexNestedInfo will hide currency $adminUser = ComplexUser::from( - name: '张三', + name: 'John', sex: 1, info: [ 'money' => 100.00, - 'currency' => 'CNY' - ]; + 'currency' => 'USD' + ] ); -// 输出数据 +// Output data $adminUserArray = $adminUser->toArray(); -// $adminUserArray 的内容: +// $adminUserArray contents: // [ -// 'name' => '张三', +// 'name' => 'John', // 'sex' => 1, // 'info' => ComplexNestedInfo Object ([ // 'money' => 100.00 @@ -818,9 +743,9 @@ $adminUserArray = $adminUser->toArray(); // ] ``` -#### 名称映射 +#### Name Mapping -##### 基础使用 +##### Basic Usage ```php use Astral\Serialize\Attributes\InputName; @@ -828,38 +753,38 @@ use Astral\Serialize\Attributes\OutputName; use Astral\Serialize\Serialize; class User extends Serialize { - // 输入时使用不同的属性名 + // Use different property name for input #[InputName('user_name')] public string $name; - // 输出时使用不同的属性名 + // Use different property name for output #[OutputName('user_id')] public int $id; - // 同时支持输入和输出不同名称 + // Support different input and output names #[InputName('register_time')] #[OutputName('registeredAt')] public DateTime $createdAt; } -// 使用不同名称的输入数据 +// Use input data with different names $user = User::from([ - 'user_name' => '张三', // 映射到 $name - 'id' => 123, // 保持不变 - 'register_time' => '2023-01-01 10:00:00' // 映射到 $createdAt + 'user_name' => 'John', // Mapped to $name + 'id' => 123, // Remains unchanged + 'register_time' => '2023-01-01 10:00:00' // Mapped to $createdAt ]); -// 输出数据 +// Output data $userArray = $user->toArray(); -// $userArray 的内容: +// $userArray contents: // [ -// 'name' => '张三', +// 'name' => 'John', // 'user_id' => 123, // 'registeredAt' => '2023-01-01 10:00:00' // ] ``` -##### 多输入/输出名称处理 +##### Multiple Input/Output Name Handling ```php use Astral\Serialize\Attributes\InputName; @@ -867,56 +792,54 @@ use Astral\Serialize\Attributes\OutputName; use Astral\Serialize\Serialize; class MultiOutputUser extends Serialize { - // 多个输出名称 + // Multiple output names #[OutputName('user_id')] #[OutputName('id')] #[OutputName('userId')] public int $id; - // 多个输出名称 按照声明顺序取地一个匹配的name + // Multiple input names, first matching name will be used #[InputName('user_name')] #[InputName('other_name')] #[InputName('userName')] public int $name; - } -// 场景1:使用第一个匹配的输入名称 -$user1 = MultiInputUser::from([ - 'user_name' => '张三' // 使用 'user_name' +// Scenario 1: Use first matching input name +$user1 = MultiOutputUser::from([ + 'user_name' => 'John' // Use 'user_name' ]); -echo $user1->name; // 输出 '张三' +echo $user1->name; // Output 'John' -// 场景2:使用第二个匹配的输入名称 -$user2 = MultiInputUser::from([ - 'other_name' => '李四' // 使用 'other_name' +// Scenario 2: Use second matching input name +$user2 = MultiOutputUser::from([ + 'other_name' => 'Jane' // Use 'other_name' ]); -echo $user2->name; // 输出 '李四' +echo $user2->name; // Output 'Jane' -// 场景3:使用最后的输入名称 -$user3 = MultiInputUser::from([ - 'userName' => '王五' // 使用 'userName' +// Scenario 3: Use last input name +$user3 = MultiOutputUser::from([ + 'userName' => 'Bob' // Use 'userName' ]); -echo $user3->name; // 输出 '王五' +echo $user3->name; // Output 'Bob' -// 场景4:传入多个的时候 按照声明顺序取地一个匹配的name -$user4 = MultiInputUser::from([ - 'userName' => '王五', - 'other_name' => '李四', - 'user_name' => '张三', +// Scenario 4: Multiple inputs, first matching name used +$user4 = MultiOutputUser::from([ + 'userName' => 'Bob', + 'other_name' => 'Jane', + 'user_name' => 'John', ]); -echo $user4->name; // 输出 '张三' +echo $user4->name; // Output 'John' -// 创建用户对象 +// Create user object $user = MultiOutputUser::from([ 'id' => 123, - 'name' => '张三' + 'name' => 'John' ]); -// 转换为数组 -// tips: 因为id 有多个outputname 所以输出了 ['user_id','id','userId'] +// Convert to array $userArray = $user->toArray(); -// $userArray 的内容: +// $userArray contents: // [ // 'user_id' => 123, // 'id' => 123, @@ -924,43 +847,43 @@ $userArray = $user->toArray(); // ] ``` -##### 复杂映射场景 +#### Complex Mapping Scenarios ```php use Astral\Serialize\Serialize; class ComplexUser extends Serialize { - // 嵌套对象的名称映射 + // Name mapping for nested objects #[InputName('user_profile')] public UserProfile $profile; - // 数组元素的名称映射 + // Name mapping for array elements #[InputName('user_tags')] public array $tags; } -// 处理复杂的输入结构 +// Handle complex input structures $complexUser = ComplexUser::from([ 'user_profile' => [ - 'nickname' => '小明', + 'nickname' => 'John', 'age' => 25 ], 'user_tags' => ['developer', 'programmer'] ]); -// 转换为标准数组 +// Convert to standard array $complexUserArray = $complexUser->toArray(); -// $complexUserArray 的内容: +// $complexUserArray contents: // [ // 'profile' => UserProfile Object ([ -// 'nickname' => '小明', +// 'nickname' => 'John', // 'age' => 25 // ]), // 'tags' => ['developer', 'programmer'] // ] ``` -##### Mapper映射 +##### Mapper Mapping ```php use Astral\Serialize\Attributes\InputName; @@ -973,42 +896,38 @@ use Astral\Serialize\Support\Mappers\{ }; use Astral\Serialize\Serialize; -#[Groups('profile','api')] class User extends Serialize { - // 直接指定映射名称 - #[InputName('user_name', groups: ['profile','api'])] - #[OutputName('userName', groups: ['profile','api'])] + // Directly specify mapping names + #[InputName('user_name')] + #[OutputName('userName')] public string $name; - // 使用映射器进行风格转换 - #[InputName(CamelCaseMapper::class, groups: ['profile','api'])] - #[OutputName(SnakeCaseMapper::class, groups: ['profile','api'])] + // Use mapper for style conversion + #[InputName(CamelCaseMapper::class)] + #[OutputName(SnakeCaseMapper::class)] public int $userId; - // 支持多个映射和分组 - #[InputName('profile-email', groups: 'profile')] - #[OutputName('userEmail', groups: 'profile')] + // Support multiple mappings and groups + #[InputName('email', groups: 'profile')] + #[OutputName('userEmail', groups: 'api')] public string $email; } -// 使用不同的映射策略 -$user = User::setGroups('profile')::from([ - 'user_name' => '张三', // 映射到 $name - 'userId' => 123, // 使用 CamelCaseMapper 转换 - 'profile-email' => 'user@example.com' // 仅在 'profile' 分组生效 +// Use different mapping strategies +$user = User::from([ + 'user_name' => 'John', // Mapped to $name + 'user_id' => 123, // Use CamelCaseMapper conversion + 'email' => 'user@example.com' // Only effective in 'profile' group ]); -// 输出时应用不同的映射 -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'userName' => '张三', -// 'user_id' => '三', -// 'userEmail' => user@example.com, -// ] +// Output with different mappings +$userArray = $user->toArray( + inputGroups: ['profile'], // Use only input mappings in 'profile' group + outputGroups: ['api'] // Use only output mappings in 'api' group +); ``` - -##### 全局类映射 + +##### Global Class Mapping ```php use Astral\Serialize\Attributes\InputName; @@ -1024,61 +943,60 @@ use Astral\Serialize\Serialize; #[InputName(SnakeCaseMapper::class)] #[OutputName(CamelCaseMapper::class)] class GlobalMappedUser extends Serialize { - // 类级别的映射会自动应用到所有属性 + // Class-level mapping automatically applies to all properties public string $firstName; public string $lastName; public int $userId; public DateTime $registeredAt; } -// 使用全局映射 +// Use global mapping $user = GlobalMappedUser::from([ - 'first_name' => '张', // 从蛇形映射到 firstName - 'last_name' => '三', // 从蛇形映射到 lastName - 'user_id' => 123, // 从蛇形映射到 userId - 'registered_at' => '2023-01-01' // 从蛇形映射到 registeredAt + 'first_name' => 'John', // From snake_case to firstName + 'last_name' => 'Doe', // From snake_case to lastName + 'user_id' => 123, // From snake_case to userId + 'registered_at' => '2023-01-01' // From snake_case to registeredAt ]); -// 输出时会转换为驼峰命名 +// Output will be converted to camelCase $userArray = $user->toArray(); -// $userArray 的内容: +// $userArray contents: // [ -// 'firstName' => '张', -// 'lastName' => '三', +// 'firstName' => 'John', +// 'lastName' => 'Doe', // 'userId' => 123, // 'registeredAt' => '2023-01-01' // ] ``` -###### 属性映射大于类级映射 +###### Property Mapping Takes Precedence Over Class-Level Mapping ```php - #[InputName(SnakeCaseMapper::class)] class PartialOverrideUser extends Serialize { #[InputName(PascalCaseMapper::class)] - public string $userName; // 优先使用帕斯卡命名映射 + public string $userName; // Prioritize PascalCase mapping - public string $userEmail; // 继续使用类级别的全局映射 + public string $userEmail; // Continue using class-level global mapping } $partialUser = PartialOverrideUser::from([ - 'User_name' => '张三', // 使用蛇形映射 - 'UserName' => '李四', // 使用帕斯卡映射 - 'user_email' => 'user@example.com' // 使用蛇形映射 + 'User_name' => 'John', // Use snake_case mapping + 'UserName' => 'Jane', // Use PascalCase mapping + 'user_email' => 'user@example.com' // Use snake_case mapping ]); $partialUser->toArray(); -// $partialUser 的内容: +// $partialUser contents: // [ -// 'userName' => '李四', +// 'userName' => 'Jane', // 'userEmail' => 'user@example.com', // ] ``` -###### 全局类映射的分组使用 +###### Global Class Mapping with Groups -需要搭配`Groups`注解一起使用 +Needs to be used in conjunction with `Groups` annotation ```php use Astral\Serialize\Attributes\Groups; @@ -1092,7 +1010,6 @@ use Astral\Serialize\Support\Mappers\{ }; use Astral\Serialize\Serialize; - #[InputName(SnakeCaseMapper::class, groups: 'external')] #[InputName(CamelCaseMapper::class, groups: 'api')] #[OutputName(PascalCaseMapper::class, groups: ['external','api'])] @@ -1104,52 +1021,51 @@ class ComplexMappedUser extends Serialize { #[Groups('external', 'api')] public string $lastName; - #[InputName('full_name', groups: 'special')] #[OutputName('userEmail', groups: 'api')] #[Groups('external', 'api')] public string $fullName; } -// 使用admin分组 +// Use admin group $complexUser = ComplexMappedUser::setGroup('external')->from( - first_name :'张', - last_name :'三' - full_name: '张三' + first_name: 'John', + last_name: 'Doe', + full_name: 'John Doe' ); $complexUser = $complexUser->toArray(); -// $complexUser 的内容: +// $complexUser contents: // [ -// 'FirstName' => '张', -// 'LastName' => '三', -// 'FullName' => 张三, +// 'FirstName' => 'John', +// 'LastName' => 'Doe', +// 'FullName' => 'John Doe', // ] -// 如果熟悉指定了OutputName/InputName 则属性规则优先 -// 使用public分组 +// If specific OutputName/InputName is familiar, attribute rules take priority +// Use public group $complexUser = ComplexMappedUser::setGroup('api')->from( - first_name :'张', - last_name :'三' - full_name: '张三' + first_name: 'John', + last_name: 'Doe', + full_name: 'John Doe' ); $complexUser = $complexUser->toArray(); -// $complexUser 的内容: +// $complexUser contents: // [ -// 'FirstName' => '张', -// 'LastName' => '三', -// 'userEmail' => 张三, +// 'firstName' => 'John', +// 'lastName' => 'Doe', +// 'userEmail' => 'John Doe', // ] ``` -#### 自定义映射器 +#### Custom Mapper ```php -// 自定义映射器 需要继承NameMapper 并实现 resolve +// Custom mapper needs to extend NameMapper and implement resolve class CustomMapper implements NameMapper { public function resolve(string $name): string { - // 实现自定义的命名转换逻辑 + // Implement custom naming conversion logic return str_replace('user', 'customer', $name); } } @@ -1160,62 +1076,61 @@ class AdvancedUser extends Serialize { } ``` -#### 字段忽略 +#### Field Ignoring -1. **安全性控制** - - 防止敏感信息的意外泄露 - - 精细控制数据的输入和输出 +1. **Security Control** + - Prevent accidental leakage of sensitive information + - Fine-grained control of data input and output -2. **数据过滤** - - 根据不同场景过滤字段 - - 为不同的 API 或用户角色定制数据视图 +2. **Data Filtering** + - Filter fields based on different scenarios + - Customize data views for different APIs or user roles -3. **性能优化** - - 减少不必要字段的序列化开销 - - 精简数据传输 +3. **Performance Optimization** + - Reduce serialization overhead for unnecessary fields + - Streamline data transmission -##### 基础使用 +##### Basic Usage ```php use Astral\Serialize\Attributes\InputIgnore; use Astral\Serialize\Attributes\OutputIgnore; use Astral\Serialize\Serialize; - class User extends Serialize { public string $name; - // 输入时忽略的字段 + // Fields ignored during input #[InputIgnore] public string $internalId; - // 输出时忽略的字段 + // Fields ignored during output #[OutputIgnore] public string $tempData; } -// 创建用户对象 +// Create user object $user = User::from([ - 'name' => '张三', - 'internalId' => 'secret123', // 这个字段会被忽略 - 'tempData' => 'temporary' // 这个字段会被忽略 + 'name' => 'John', + 'internalId' => 'secret123', // This field will be ignored + 'tempData' => 'temporary' // This field will be ignored ]); -echo $user->internalId; // 这里会输出 '' +echo $user->internalId; // This will output '' -// 转换为数组 +// Convert to array $userArray = $user->toArray(); -// $userArray 的内容: +// $userArray contents: // [ -// 'name' => '张三', +// 'name' => 'John', // 'internalId' => '', // ] ``` -##### 分组忽略 +##### Group Ignoring -忽略分组需要搭配Groups注解一起使用 +Group ignoring needs to be used in conjunction with the Groups annotation ```php use Astral\Serialize\Attributes\Input\InputIgnore; @@ -1241,251 +1156,68 @@ class ComplexUser extends Serialize { #[InputIgnore] public string $globalInputIgnore; - #[OutputIgnore] + #[OutputIgnore] public string $globalOutputIgnore; } -// 默认分组 +// Default group $complexUser = ComplexUser::from([ - 'name' => '张三', + 'name' => 'John', 'secretKey' => 'confidential', - 'sensitiveInfo' => '机密信息', - 'globalInputIgnore' => '全局输入忽略', - 'globalOutputIgnore' => '全局输出忽略' + 'sensitiveInfo' => 'Sensitive information', + 'globalInputIgnore' => 'Global input ignore', + 'globalOutputIgnore' => 'Global output ignore' ]); -echo $complexUser->globalInputIgnore; // 输出 ‘’ -echo $complexUser->globalOutputIgnore; // 输出 ‘全局输出忽略’ +echo $complexUser->globalInputIgnore; // Output '' +echo $complexUser->globalOutputIgnore; // Output 'Global output ignore' $complexUser = $complexUser->toArray(); -// $complexUser 的内容: +// $complexUser contents: // [ -// 'name' => '张三', +// 'name' => 'John', // 'secretKey' => 'confidential', -// 'sensitiveInfo' => '机密信息', +// 'sensitiveInfo' => 'Sensitive information', // 'globalInputIgnore' => '', // ] - -// 使用admin分组 +// Use admin group $complexUser = ComplexUser::setGroups('admin')->from([ - 'name' => '张三', + 'name' => 'John', 'secretKey' => 'confidential', - 'sensitiveInfo' => '机密信息' - 'globalInputIgnore' => '全局输入忽略', - 'globalOutputIgnore' => '全局输出忽略' + 'sensitiveInfo' => 'Sensitive information', + 'globalInputIgnore' => 'Global input ignore', + 'globalOutputIgnore' => 'Global output ignore' ]); $complexUser = $complexUser->toArray(); -// $complexUser 的内容: +// $complexUser contents: // [ // 'name' => '', // 'secretKey' => 'confidential', -// 'globalInputIgnore' => '', +// 'globalInputIgnore' => '', // ] -// 使用public分组 +// Use public group $complexUser = ComplexUser::setGroups('public')->from([ - 'name' => '张三', + 'name' => 'John', 'secretKey' => 'confidential', - 'sensitiveInfo' => '机密信息' - 'globalInputIgnore' => '全局输入忽略', - 'globalOutputIgnore' => '全局输出忽略' + 'sensitiveInfo' => 'Sensitive information', + 'globalInputIgnore' => 'Global input ignore', + 'globalOutputIgnore' => 'Global output ignore' ]); $complexUser = $complexUser->toArray(); -// $complexUser 的内容: -// [ -// 'name' => '张三', -/// 'globalInputIgnore' => '', -// ] -``` - -#### 时间转换 - -1. 格式灵活性 - 支持多种输入和输出时间格式 - 可以轻松处理不同地区的日期表示 -2. 时区处理 - 支持在不同时区间转换 - 自动处理时间的时区偏移 -3. 类型安全 - 自动将字符串转换为 DateTime 对象 - 保证类型的一致性和正确性 - -##### 基础使用 - -```php -use Astral\Serialize\Attributes\Input\InputDateFormat; -use Astral\Serialize\Attributes\Output\OutputDateFormat; -use Astral\Serialize\Serialize; - -class TimeExample extends Serialize { - - // 输入时间格式转换 - #[InputDateFormat('Y-m-d')] - public DateTime $dateTime; - - // 输入时间格式转换 - #[InputDateFormat('Y-m-d')] - public string $dateDateString; - - // 输出时间格式转换 - #[OutputDateFormat('Y/m/d H:i')] - public DateTime|string $processedAt; - - - // 支持多种时间格式 - #[InputDateFormat('Y-m-d H:i:s')] - #[OutputDateFormat('Y-m-d')] - public string $createdAt; -} - -// 创建订单对象 -$order = TimeExample::from([ - 'dateTime' => new DateTime('2023-08-11'), // 输入格式:Y-m-d - 'dateDateString' => '2023-08-15', // 输入格式:Y-m-d - 'processedAt' => '2023-08-16 14:30', // 输入默认格式 也支持DateTime对象 - 'createdAt' => '2023-08-16 14:45:30' // 输入格式:Y-m-d H:i:s -]); - -// 转换为数组 -$orderArray = $order->toArray(); -// $orderArray 的内容: -// [ -// 'dateTime' => '2023-08-11', -// ’dateDateString' => '2023-08-15', -// 'processedAt' => '2023/08/16 14:30', -// 'createdAt' => '2023-08-16' -// ] -``` - -##### 带时区的时间转换 - -```php - -use Astral\Serialize\Attributes\Input\InputDateFormat; -use Astral\Serialize\Attributes\Output\OutputDateFormat; -use Astral\Serialize\Serialize; - -class AdvancedTimeUser extends Serialize { - // 支持时区转换 - #[InputDateFormat('Y-m-d H:i:s', timezone: 'UTC')] - #[OutputDateFormat('Y-m-d H:i:s', timezone: 'Asia/Shanghai')] - public DateTime $registeredAt; - - // 支持不同地区的时间格式 - #[InputDateFormat('d/m/Y')] // 英国格式 - #[OutputDateFormat('Y-m-d')] // 标准格式 - public DateTime $birthDate; -} - -// 使用高级时间转换 -$advancedUser = AdvancedTimeUser::from([ - 'registeredAt' => '2023-08-16 10:00:00', // UTC 时间 - 'birthDate' => '15/08/1990' // 英国日期格式 -]); - -$advancedUserArray = $advancedUser->toArray(); -// $advancedUserArray 的内容: +// $complexUser contents: // [ -// 'registeredAt' => '2023-08-16 18:00:00', // 转换为上海时区 -// 'birthDate' => '1990-08-15' +// 'name' => 'John', +// 'globalInputIgnore' => '', // ] ``` -## Faker +### Nested Object Mocking -### 简单属性模拟 - -```php -class UserFaker extends Serialize { - #[FakerValue('name')] - public string $name; - - #[FakerValue('email')] - public string $email; - - #[FakerValue('uuid')] - public string $userId; - - #[FakerValue('phoneNumber')] - public string $phone; - - #[FakerValue('age')] - public int $age; - - #[FakerValue('boolean')] - public bool $isActive; -} - -$user = UserFaker::faker(); - -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// "name" => "John Doe" -// "email" => "john.doe@example.com" -// "userId" => "550e8400-e29b-41d4-a716-446655440000" -// "phone" => "+1-555-123-4567" -// "age" => 35 -// "isActive" => true -// ] -``` - -### 集合模拟 - -```php - -class UserProfile extends Serialize { - public string $nickname; - public int $age; - public string $email; - public string $avatar; -} - -class UserListFaker extends Serialize { - #[FakerCollection(['name', 'email'], num: 3)] - public array $users; - - #[FakerCollection(UserProfile::class, num: 2)] - public array $profiles; -} - -$userList = UserListFaker::faker(); - -$complexUserListFaker = UserListFaker::faker(); - -$complexUserListFakerArray = $complexUserListFaker->toArray(); -// $complexUserListFakerArray 的内容: -// [ -// 'profile' => [ -// [0] => UserProfile Object ( -// [ -// 'nickname' => 'RandomNickname', -// 'age' => 28, 'email' => 'random.user@example.com', -// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' -// ], -// ), -// [1] => UserProfile Object ( -// [ -// 'nickname' => 'RandomNickname', -// 'age' => 28, 'email' => 'random.user@example.com', -// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' -// ], -// ) -// ], -// 'users' => [ -// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] -// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] -// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] -// ] -// ] -``` - -### 嵌套对象模拟 - -#### 基本用法 +#### Basic Usage ```php class ComplexUserFaker extends Serialize { @@ -1494,7 +1226,7 @@ class ComplexUserFaker extends Serialize { } ``` -#### 演示实例 +#### Demonstration Example ```php use Astral\Serialize\Serialize; @@ -1519,13 +1251,12 @@ class ComplexUserFaker extends Serialize { #[FakerObject(UserTag::class)] public UserTag|UserProfile $primaryTag; - } $complexUserFaker = ComplexUserFaker::faker(); $complexUserFakerArray = $complexUserFaker->toArray(); -// $complexUserFakerArray 的内容: +// $complexUserFakerArray contents: // [ // 'profile' => UserProfile Object ( // ['nickname' => 'RandomNickname', 'age' => 28, 'email' => 'random.user@example.com', 'avatar' => 'https://example.com/avatars/random-avatar.jpg'] @@ -1536,7 +1267,7 @@ $complexUserFakerArray = $complexUserFaker->toArray(); // ] ``` -### Faker类方法模拟 +### Faker Class Method Mocking ```php class UserService { @@ -1551,7 +1282,7 @@ class UserFaker extends Serialize { } ``` -#### 完整的示例 +#### Complete Example ```php use Astral\Serialize\Serialize; @@ -1559,7 +1290,7 @@ use Astral\Serialize\Attributes\Faker\FakerMethod; use Astral\Serialize\Attributes\Faker\FakerObject; use Astral\Serialize\Attributes\Faker\FakerCollection; -// 用户配置文件类 +// User Profile Class class UserProfile extends Serialize { public string $nickname; public int $age; @@ -1567,7 +1298,7 @@ class UserProfile extends Serialize { public array $types = ['type1' => 'money', 'type2' => 'score']; } -// 用户服务类,提供数据生成方法 +// User Service Class, providing data generation methods class UserService { public function generateUserData(): array { return [ @@ -1593,35 +1324,35 @@ class UserService { } } -// Faker 方法模拟示例 +// Faker Method Mocking Example class UserFaker extends Serialize { - // 使用方法生成简单数据 + // Use method to generate simple data #[FakerMethod(UserService::class, 'generateUserData')] public array $userData; - // 使用方法生成对象 + // Use method to generate object #[FakerMethod(UserService::class, 'generateUserProfile')] public UserProfile $userProfile; - // 获取指定属性 - #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'age')] + // Get specific attribute + #[FakerMethod(UserService::class, 'generateUserProfile', returnType: 'age')] public int $age; - // 获取指定属性 多级可以使用[.]链接 - #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'types.type2')] + // Get specific attribute with multi-level access using [.] + #[FakerMethod(UserService::class, 'generateUserProfile', returnType: 'types.type2')] public string $type2; - // 传入参数 - #[FakerMethod(UserService::class, 'generateUserList',params:['count'=> 3])] + // Pass parameters + #[FakerMethod(UserService::class, 'generateUserList', params: ['count' => 3])] public array $userList; } -// 生成模拟数据 +// Generate mock data $userFaker = UserFaker::faker(); -// 转换为数组 +// Convert to array $userFakerArray = $userFaker->toArray(); -// $userFakerArray 的内容: +// $userFakerArray contents: // [ // 'userData' => [ // 'name' => 'Generated User', @@ -1631,12 +1362,12 @@ $userFakerArray = $userFaker->toArray(); // 'userProfile' => UserProfile Object ( // [ // 'nickname' => 'GeneratedNickname', -// 'age' => 25, // 随机生成 +// 'age' => 25, // Randomly generated // 'email' => 'profile@example.com' // 'types' => ['type1' => 'money', 'type2' => 'score'] // ] // ), -// 'age' => 99 , // 随机生成 +// 'age' => 99 , // Randomly generated // 'type2' => 'score', // 'userList' => [ // ['name' => 'User 0', 'email' => 'user0@example.com'], @@ -1644,4 +1375,3 @@ $userFakerArray = $userFaker->toArray(); // ['name' => 'User 2', 'email' => 'user2@example.com'] // ] // ] -``` diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index 0673a8d..1f4abc1 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -1,5 +1,5 @@ # Summary * [介绍](README.md) -* [快速开始](getting-started.md) -* [进阶使用](base-mapper.md) \ No newline at end of file +* [Quick Start](getting-started.md) +* [Type Conversion](base-mapper.md) \ No newline at end of file From e684ad1f541ba4b4166fbf328507fa1dd557bfde Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 16:12:56 +0800 Subject: [PATCH 31/59] gitbook test --- docs/en/SUMMARY.md | 2 +- docs/en/description.md | 16 ++++++++++++++++ docs/zh/SUMMARY.md | 2 +- docs/zh/description.md | 16 ++++++++++++++++ 4 files changed, 34 insertions(+), 2 deletions(-) create mode 100644 docs/en/description.md create mode 100644 docs/zh/description.md diff --git a/docs/en/SUMMARY.md b/docs/en/SUMMARY.md index 1f4abc1..3ecce96 100644 --- a/docs/en/SUMMARY.md +++ b/docs/en/SUMMARY.md @@ -1,5 +1,5 @@ # Summary -* [介绍](README.md) +* [介绍](description.md) * [Quick Start](getting-started.md) * [Type Conversion](base-mapper.md) \ No newline at end of file diff --git a/docs/en/description.md b/docs/en/description.md new file mode 100644 index 0000000..aac5fa9 --- /dev/null +++ b/docs/en/description.md @@ -0,0 +1,16 @@ +# php-serialize + +**php-serialize** is a powerful attribute-based serialization library for PHP (requires **PHP ≥ 8.1**). +It allows you to map objects to arrays/JSON and **automatically generate OpenAPI documentation** based on the same attributes. + +> 🚀 Unified solution for API data serialization and documentation generation. + +## ✨ Features + +- 🏷️ Property aliasing with +- 🔄 Automatic type casting (e.g. `DateTime ↔ string`) +- 🔁 Deep object nesting support +- ❌ Skip/exclude fields with +- 🧩 Recursive DTO serialization +- 🧬 **Auto-generate OpenAPI schema** using object definitions +- ⚙️ Framework-agnostic — works with Laravel, Symfony, etc. \ No newline at end of file diff --git a/docs/zh/SUMMARY.md b/docs/zh/SUMMARY.md index 0673a8d..95a486f 100644 --- a/docs/zh/SUMMARY.md +++ b/docs/zh/SUMMARY.md @@ -1,5 +1,5 @@ # Summary -* [介绍](README.md) +* [介绍](description.md) * [快速开始](getting-started.md) * [进阶使用](base-mapper.md) \ No newline at end of file diff --git a/docs/zh/description.md b/docs/zh/description.md new file mode 100644 index 0000000..9b10cc7 --- /dev/null +++ b/docs/zh/description.md @@ -0,0 +1,16 @@ +# php-serialize + +**php-serialize** 是一个功能强大的基于属性(attribute)的 PHP 序列化库(需要 **PHP ≥ 8.1**)。 +它允许你将对象映射为数组或 JSON,并且可以基于相同的属性 **自动生成 OpenAPI 文档**。 + +> 🚀 统一解决方案,支持 API 数据序列化和文档生成。 + +## ✨ 功能特色 + +- 🏷️ 属性别名映射 +- 🔄 自动类型转换(例如 `DateTime ↔ string`) +- 🔁 支持深度对象嵌套 +- ❌ 支持跳过/排除字段 +- 🧩 递归 DTO(数据传输对象)序列化 +- 🧬 **基于对象定义自动生成 OpenAPI schema** +- ⚙️ 与框架无关 — 兼容 Laravel、Symfony 等框架 \ No newline at end of file From 9d9d261d1965c44f72dcea5a0da4003c8dcf670a Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 16:22:05 +0800 Subject: [PATCH 32/59] gitbook test --- docs/zh/README.md | 81 ----------------- docs/zh/SUMMARY.md | 11 ++- docs/zh/mapper/array-mapper.md | 132 ++++++++++++++++++++++++++++ docs/zh/{ => mapper}/base-mapper.md | 0 docs/zh/mapper/enum-mapper.md | 82 +++++++++++++++++ docs/zh/mapper/null-mapper.md | 78 ++++++++++++++++ docs/zh/mapper/union-mapper.md | 108 +++++++++++++++++++++++ 7 files changed, 410 insertions(+), 82 deletions(-) create mode 100644 docs/zh/mapper/array-mapper.md rename docs/zh/{ => mapper}/base-mapper.md (100%) create mode 100644 docs/zh/mapper/enum-mapper.md create mode 100644 docs/zh/mapper/null-mapper.md create mode 100644 docs/zh/mapper/union-mapper.md diff --git a/docs/zh/README.md b/docs/zh/README.md index 60576c3..43d49ac 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -220,88 +220,7 @@ class Profile extends Serialize { 无论使用哪种方式,`Serialize` 类都能正常工作,并提供相同的类型转换和序列化功能。 -#### 枚举转换 -枚举转换提供了强大且灵活的枚举处理机制,支持多种枚举类型和转换场景。 - -- 支持 `tryFrom()` 和 `cases()` 方法的枚举类型 -- 输入时自动将字符串转换为枚举实例 -- 输出时自动将枚举转换为字符串(枚举名称) -- 提供灵活且安全的枚举处理机制 - -##### 普通枚举 - -```php -enum UserRole { - case ADMIN; - case EDITOR; - case VIEWER; -} - -class ComplexUser extends Serialize { - - public UserRole $role; - - // 支持多种枚举类型 - public UserStatus|UserRole $mixedStatus; -} - -$complexUser = ComplexUser::from([ - 'role' => 'ADMIN', // 自动转换为 UserRole::ADMIN - 'mixedStatus' => 'ACTIVE' // 可以是 UserStatus 或 UserRole -]); - -echo $complexUser->role; // 返回 UserRole枚举实例 - -$complexUserArray = $complexUser->toArray(); -// $complexUserArray 的内容: -// [ -// 'role' => 'ADMIN', -// 'mixedStatus' => 'ACTIVE' -// ] -``` - -##### 回退枚举 - -```php -use Astral\Serialize\Serialize; - -// BackedEnum -enum UserStatus: string { - case ACTIVE = 'active'; - case INACTIVE = 'inactive'; - case SUSPENDED = 'suspended'; -} - -// 定义带有枚举的用户类 -class User extends Serialize { - public string $name; - - // 支持 UnitEnum 和 BackedEnum - public UserStatus $status; - - // 支持多枚举类型 - public UserStatus|string $alternateStatus; -} - -// 创建用户对象 -$user = User::from([ - 'name' => '张三', - 'status' => 'active', // 自动转换为 UserStatus::ACTIVE - 'alternateStatus' => 'inactive' // 支持字符串或枚举值 -]); - -var_dump($user->status); // 输出: UserStatus::ACTIVE - -// 转换为数组 -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'name' => '张三', -// 'status' => 'ACTIVE', // 输出枚举名称 -// 'alternateStatus' => 'INACTIVE' -// ] -``` #### Null 值转换规则详细示例 diff --git a/docs/zh/SUMMARY.md b/docs/zh/SUMMARY.md index 95a486f..e4c7886 100644 --- a/docs/zh/SUMMARY.md +++ b/docs/zh/SUMMARY.md @@ -1,5 +1,14 @@ # Summary +## Use headings to create page groups like this one + * [介绍](description.md) * [快速开始](getting-started.md) -* [进阶使用](base-mapper.md) \ No newline at end of file + +## 属性转换 + +* [基本类型转换](mapper/base-mapper.md) +* [Null值转换](mapper/null-mapper.md) +* [枚举转换](mapper/enum-mapper.md) +* [数组对象转换](mapper/array-mapper.md) +* [联合类型转换](mapper/union-mapper.md) \ No newline at end of file diff --git a/docs/zh/mapper/array-mapper.md b/docs/zh/mapper/array-mapper.md new file mode 100644 index 0000000..539f2fb --- /dev/null +++ b/docs/zh/mapper/array-mapper.md @@ -0,0 +1,132 @@ +#### 数组对象转换 + +##### phpDoc定义 + +```php +use Astral\Serialize\Serialize; + +// 定义基础数组类型 +class ArrayOne extends Serialize { + public string $type = 'one'; + public string $name; +} + +class ArrayTwo extends Serialize { + public string $type = 'two'; + public string $code; +} + +class MultiArraySerialize extends Serialize { + // 场景1:混合类型数组 + /** @var (ArrayOne|ArrayTwo)[] */ + public array $mixedTypeArray; + + // 场景2:多类型数组 + /** @var ArrayOne[]|ArrayTwo[] */ + public array $multiTypeArray; + + // 场景3:键值对混合类型 + /** @var array(string, ArrayOne|ArrayTwo) */ + public array $keyValueMixedArray; +} + +// 场景1:混合类型数组 +$data1 = MultiArraySerialize::from( + mixedTypeArray : [ + ['name' => '张三'], // 转化 ArrayOne 对象 + ['code' => 'ABC123'], // 转化 ArrayTwo 对象 + ['name' => '李四'], // 转化 ArrayOne 对象 + ['code' => 'DEF456'] // 转化 ArrayTwo 对象 + ] +); + +$data1Array = $data1->toArray(); +// $data1Array 的内容: +// [ +// 'mixedTypeArray' => [ +// [0] => ArrayOne Object +// ( +// ['name' => '张三', 'type' => 'one'], +// ) +// [1] => ArrayTwo Object +// ( +// ['code' => 'ABC123', 'type' => 'two'], +// ) +// [2] => ArrayOne Object +// ( +// ['name' => '李四', 'type' => 'one'], +// ) +// [3] => ArrayTwo Object +// ( +// ['code' => 'DEF456', 'type' => 'two'], +// ) +// ] +// ] + +// 场景2:多类型数组 +$data2 = MultiArraySerialize::from( + multiTypeArray:[ + ['name' => '王五'], // 转化 ArrayOne 对象 + ['name' => '赵六'], // 转化 ArrayOne 对象 + ['code' => 'GHI789'] // 转化 ArrayTwo 对象 + ] +); + +$data2Array = $data2->toArray(); +// $data2Array 的内容: +// [ +// 'multiTypeArray' => [ +// ArrayOne Object ( +// ['name' => '王五', 'type' => 'one'] +// ), +// ArrayOne Object ( +// ['name' => '赵六', 'type' => 'one'] +// ), +// ArrayTwo Object ( +// ['code' => 'GHI789', 'type' => 'two'] +// ) +// ] +// ] + +// 场景3:键值对混合类型 +$data3 = MultiArraySerialize::from( + keyValueMixedArray: [ + 'user1' => ['name' => '张三'], // 转化 ArrayOne 对象 + 'system1' => ['code' => 'ABC123'], // 转化 ArrayTwo 对象 + 'user2' => ['name' => '李四'] // 转化 ArrayOne 对象 + ] +); + +$data3Array = $data3->toArray(); +// $data3Array 的内容: +// [ +// 'keyValueMixedArray' => [ +// 'user1' => ArrayOne Object ( +// ['name' => '张三', 'type' => 'one'] +// ), +// 'system1' => ArrayTwo Object ( +// ['code' => 'ABC123', 'type' => 'two'] +// ), +// 'user2' => ArrayOne Object ( +// ['name' => '李四', 'type' => 'one'] +// ) +// ] +// ] + +// 场景4:无法匹配时的处理 +$data4 = MultiArraySerialize::from( + mixedTypeArray : [ + ['unknown' => 'data1'], + ['another' => 'data2'] + ] +); + +$data4Array = $data4->toArray(); +// $data4Array 的内容: +// [ +// 'mixedTypeArray' => [ +// ['unknown' => 'data1'], +// ['another' => 'data2'] +// ] +// ] +``` \ No newline at end of file diff --git a/docs/zh/base-mapper.md b/docs/zh/mapper/base-mapper.md similarity index 100% rename from docs/zh/base-mapper.md rename to docs/zh/mapper/base-mapper.md diff --git a/docs/zh/mapper/enum-mapper.md b/docs/zh/mapper/enum-mapper.md new file mode 100644 index 0000000..a918fef --- /dev/null +++ b/docs/zh/mapper/enum-mapper.md @@ -0,0 +1,82 @@ +#### 枚举转换 + +枚举转换提供了强大且灵活的枚举处理机制,支持多种枚举类型和转换场景。 + +- 支持 `tryFrom()` 和 `cases()` 方法的枚举类型 +- 输入时自动将字符串转换为枚举实例 +- 输出时自动将枚举转换为字符串(枚举名称) +- 提供灵活且安全的枚举处理机制 + +##### 普通枚举 + +```php +enum UserRole { + case ADMIN; + case EDITOR; + case VIEWER; +} + +class ComplexUser extends Serialize { + + public UserRole $role; + + // 支持多种枚举类型 + public UserStatus|UserRole $mixedStatus; +} + +$complexUser = ComplexUser::from([ + 'role' => 'ADMIN', // 自动转换为 UserRole::ADMIN + 'mixedStatus' => 'ACTIVE' // 可以是 UserStatus 或 UserRole +]); + +echo $complexUser->role; // 返回 UserRole枚举实例 + +$complexUserArray = $complexUser->toArray(); +// $complexUserArray 的内容: +// [ +// 'role' => 'ADMIN', +// 'mixedStatus' => 'ACTIVE' +// ] +``` + +##### 回退枚举 + +```php +use Astral\Serialize\Serialize; + +// BackedEnum +enum UserStatus: string { + case ACTIVE = 'active'; + case INACTIVE = 'inactive'; + case SUSPENDED = 'suspended'; +} + +// 定义带有枚举的用户类 +class User extends Serialize { + public string $name; + + // 支持 UnitEnum 和 BackedEnum + public UserStatus $status; + + // 支持多枚举类型 + public UserStatus|string $alternateStatus; +} + +// 创建用户对象 +$user = User::from([ + 'name' => '张三', + 'status' => 'active', // 自动转换为 UserStatus::ACTIVE + 'alternateStatus' => 'inactive' // 支持字符串或枚举值 +]); + +var_dump($user->status); // 输出: UserStatus::ACTIVE + +// 转换为数组 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'status' => 'ACTIVE', // 输出枚举名称 +// 'alternateStatus' => 'INACTIVE' +// ] +``` \ No newline at end of file diff --git a/docs/zh/mapper/null-mapper.md b/docs/zh/mapper/null-mapper.md new file mode 100644 index 0000000..2f11deb --- /dev/null +++ b/docs/zh/mapper/null-mapper.md @@ -0,0 +1,78 @@ +#### Null值转换规则详细示例 + +当属性不是可空类型(`?type`)时,`null` 值会根据目标类型自动转换: + +```php +use Astral\Serialize\Serialize; + +class NullConversionProfile extends Serialize { + public string $username; + public int $score; + public float $balance; + public array $tags; + public object $metadata; +} + +// Null 值转换示例 +$profile = NullConversionProfile::from([ + 'username' => null, // 转换为空字符串 '' + 'score' => null, // 转换为 0 + 'balance' => null, // 转换为 0.0 + 'tags' => null, // 转换为空数组 [] + 'metadata' => null // 转换为空对象 new stdClass() +]); + +// 验证转换结果 +echo $profile->username; // 输出: ""(空字符串) +echo $profile->score; // 输出: 0 +echo $profile->balance; // 输出: 0.0 +var_dump($profile->tags); // 输出: array(0) {} +var_dump($profile->metadata); // 输出: object(stdClass)#123 (0) {} + +// 布尔值的特殊处理 +try { + NullConversionProfile::from([ + 'isActive' => null // 这将抛出类型错误 + ]); +} catch (\TypeError $e) { + echo "布尔类型不支持 null 值:" . $e->getMessage(); +} +``` + +#### 可空类型的方案 + +对于需要接受 `null` 的场景,使用可空类型: + +```php +use Astral\Serialize\Serialize; + +class FlexibleProfile extends Serialize { + public function __construct( + public ?string $username, + public ?int $score, + public ?object $metadata, + public ?array $tags + ) {} +} + +// 创建包含 null 值的对象 +$profile = FlexibleProfile::from([ + 'username' => null, // 允许 null + 'score' => null, // 允许 null + 'metadata' => null, // 允许 null + 'tags' => null // 允许 null +]); + +// 转换为数组 +$profileArray = $profile->toArray(); +// $profileArray 的内容: +// [ +// 'username' => null, +// 'score' => null, +// 'metadata' => null, +// 'tags' => null +// ] + +// 验证可空类型的行为 +echo $profile->username; // 输出 null +``` \ No newline at end of file diff --git a/docs/zh/mapper/union-mapper.md b/docs/zh/mapper/union-mapper.md new file mode 100644 index 0000000..48ea6ac --- /dev/null +++ b/docs/zh/mapper/union-mapper.md @@ -0,0 +1,108 @@ +#### 联合类型 + +1. 可以混合使用基本类型和对象类型 +2. 对象层级匹配 + 对于多个对象类型,会选择最匹配的类型 + 支持继承层级的智能匹配 +3. 动态类型处理 + 自动处理不同类型的输入 + 提供更加灵活的数据建模方式 + +```php +use Astral\Serialize\Serialize; + +// 定义一个基础用户类 +class User extends Serialize { + public string $name; + public int $age; +} + +// 定义一个管理员用户类 +class AdminUser extends User { + public string $role; +} + +class FlexibleData extends Serialize { + // 支持整数或字符串类型的标识符 + public int|string $flexibleId; + + // 支持用户对象或整数标识符 + public User|int $userIdentifier; + + // 支持多种复杂的联合类型 + public AdminUser|User|int $complexIdentifier; +} + +// 场景1:使用整数作为 flexibleId +$data1 = FlexibleData::from([ + 'flexibleId' => 123, + 'userIdentifier' => 456, + 'complexIdentifier' => 789 +]); + +$data1Array = $data1->toArray(); +// $data1Array 的内容: +// [ +// 'flexibleId' => 123, +// 'userIdentifier' => 456, +// 'complexIdentifier' => 789 +// ] + +// 场景2:使用字符串作为 flexibleId +$data2 = FlexibleData::from([ + 'flexibleId' => 'ABC123', + 'userIdentifier' => [ + 'name' => '张三', + 'age' => 30 + ], + 'complexIdentifier' => [ + 'name' => '李四', + 'age' => 25 + ] +]); + +echo $data2->userIdentifier; // 输出 User 对象 +echo $data2->complexIdentifier; // 输出 User 对象 + +$data2Array = $data2->toArray(); +// $data2Array 的内容: +// [ +// 'flexibleId' => 'ABC123', +// 'userIdentifier' => User Object ( +// ['name' => '张三', 'age' => 30] +// ), +// 'complexIdentifier' => User Object ( +// ['name' => '李四', 'age' => 25] +// ) +// ] + +// 场景3:使用管理员用户 +$data3 = FlexibleData::from([ + 'flexibleId' => 'USER001', + 'userIdentifier' => [ + 'name' => '王五', + 'age' => 35, + 'role' => 'admin' + ], + 'complexIdentifier' => [ + 'name' => '赵六', + 'age' => 40, + 'role' => 'super_admin' + ] +]); + +echo $data2->userIdentifier; // 输出 User 对象 +echo $data2->complexIdentifier; // 输出 AdminUser 对象 + +$data3Array = $data3->toArray(); +// $data3Array 的内容: +// [ +// 'flexibleId' => 'USER001', +// 'userIdentifier' => User Object ( +// ['name' => '王五', 'age' => 35] +// ), +// 'complexIdentifier' => AdminUser Object ( +// ['name' => '赵六', 'age' => 40, 'role' => 'super_admin'] +// ) +// ] +``` \ No newline at end of file From 86de98cc3755e4d448b1b90e85c4742624a80c09 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 16:35:24 +0800 Subject: [PATCH 33/59] gitbook test --- docs/zh/SUMMARY.md | 17 +- docs/zh/annotation/alisa-annotation.md | 141 +++++++++++++++++ docs/zh/annotation/date-annotation.md | 92 +++++++++++ docs/zh/annotation/group-annotation.md | 191 +++++++++++++++++++++++ docs/zh/annotation/ignore-annotation.md | 140 +++++++++++++++++ docs/zh/annotation/mapper-annotation.md | 198 ++++++++++++++++++++++++ docs/zh/faker/collection-faker.md | 49 ++++++ docs/zh/faker/method-faker.md | 109 +++++++++++++ docs/zh/faker/nested-faker.md | 52 +++++++ docs/zh/faker/value-faker.md | 36 +++++ 10 files changed, 1024 insertions(+), 1 deletion(-) create mode 100644 docs/zh/annotation/alisa-annotation.md create mode 100644 docs/zh/annotation/date-annotation.md create mode 100644 docs/zh/annotation/group-annotation.md create mode 100644 docs/zh/annotation/ignore-annotation.md create mode 100644 docs/zh/annotation/mapper-annotation.md create mode 100644 docs/zh/faker/collection-faker.md create mode 100644 docs/zh/faker/method-faker.md create mode 100644 docs/zh/faker/nested-faker.md create mode 100644 docs/zh/faker/value-faker.md diff --git a/docs/zh/SUMMARY.md b/docs/zh/SUMMARY.md index e4c7886..1adba5a 100644 --- a/docs/zh/SUMMARY.md +++ b/docs/zh/SUMMARY.md @@ -11,4 +11,19 @@ * [Null值转换](mapper/null-mapper.md) * [枚举转换](mapper/enum-mapper.md) * [数组对象转换](mapper/array-mapper.md) -* [联合类型转换](mapper/union-mapper.md) \ No newline at end of file +* [联合类型转换](mapper/union-mapper.md) + +## 注解类使用 + +* [属性分组](annotation/group-annotation.md) +* [输入/输入出名称映射](annotation/alisa-annotation.md) +* [Mapper映射](annotation/mapper-annotation.md) +* [输入/输出忽略](annotation/ignore-annotation.md) +* [时间格式映射](annotation/date-annotation.md) + +## 参数快速Faker + +* [简单属性Faker](faker/value-faker.md) +* [集合Faker](faker/collection-faker.md) +* [嵌套Faker](faker/nested-faker.md) +* [方法Faker](faker/method-faker.md) \ No newline at end of file diff --git a/docs/zh/annotation/alisa-annotation.md b/docs/zh/annotation/alisa-annotation.md new file mode 100644 index 0000000..e665b18 --- /dev/null +++ b/docs/zh/annotation/alisa-annotation.md @@ -0,0 +1,141 @@ +#### 名称映射 + +##### 基础使用 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Serialize; + +class User extends Serialize { + // 输入时使用不同的属性名 + #[InputName('user_name')] + public string $name; + + // 输出时使用不同的属性名 + #[OutputName('user_id')] + public int $id; + + // 同时支持输入和输出不同名称 + #[InputName('register_time')] + #[OutputName('registeredAt')] + public DateTime $createdAt; +} + +// 使用不同名称的输入数据 +$user = User::from([ + 'user_name' => '张三', // 映射到 $name + 'id' => 123, // 保持不变 + 'register_time' => '2023-01-01 10:00:00' // 映射到 $createdAt +]); + +// 输出数据 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'user_id' => 123, +// 'registeredAt' => '2023-01-01 10:00:00' +// ] +``` + +##### 多输入/输出名称处理 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Serialize; + +class MultiOutputUser extends Serialize { + // 多个输出名称 + #[OutputName('user_id')] + #[OutputName('id')] + #[OutputName('userId')] + public int $id; + + // 多个输出名称 按照声明顺序取地一个匹配的name + #[InputName('user_name')] + #[InputName('other_name')] + #[InputName('userName')] + public int $name; + +} + +// 场景1:使用第一个匹配的输入名称 +$user1 = MultiInputUser::from([ + 'user_name' => '张三' // 使用 'user_name' +]); +echo $user1->name; // 输出 '张三' + +// 场景2:使用第二个匹配的输入名称 +$user2 = MultiInputUser::from([ + 'other_name' => '李四' // 使用 'other_name' +]); +echo $user2->name; // 输出 '李四' + +// 场景3:使用最后的输入名称 +$user3 = MultiInputUser::from([ + 'userName' => '王五' // 使用 'userName' +]); +echo $user3->name; // 输出 '王五' + +// 场景4:传入多个的时候 按照声明顺序取地一个匹配的name +$user4 = MultiInputUser::from([ + 'userName' => '王五', + 'other_name' => '李四', + 'user_name' => '张三', +]); +echo $user4->name; // 输出 '张三' + +// 创建用户对象 +$user = MultiOutputUser::from([ + 'id' => 123, + 'name' => '张三' +]); + +// 转换为数组 +// tips: 因为id 有多个outputname 所以输出了 ['user_id','id','userId'] +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'user_id' => 123, +// 'id' => 123, +// 'userId' => 123, +// ] +``` + +##### 复杂映射场景 + +```php +use Astral\Serialize\Serialize; + +class ComplexUser extends Serialize { + // 嵌套对象的名称映射 + #[InputName('user_profile')] + public UserProfile $profile; + + // 数组元素的名称映射 + #[InputName('user_tags')] + public array $tags; +} + +// 处理复杂的输入结构 +$complexUser = ComplexUser::from([ + 'user_profile' => [ + 'nickname' => '小明', + 'age' => 25 + ], + 'user_tags' => ['developer', 'programmer'] +]); + +// 转换为标准数组 +$complexUserArray = $complexUser->toArray(); +// $complexUserArray 的内容: +// [ +// 'profile' => UserProfile Object ([ +// 'nickname' => '小明', +// 'age' => 25 +// ]), +// 'tags' => ['developer', 'programmer'] +// ] +``` \ No newline at end of file diff --git a/docs/zh/annotation/date-annotation.md b/docs/zh/annotation/date-annotation.md new file mode 100644 index 0000000..a8de4f1 --- /dev/null +++ b/docs/zh/annotation/date-annotation.md @@ -0,0 +1,92 @@ +#### 时间转换 + +1. 格式灵活性 + 支持多种输入和输出时间格式 + 可以轻松处理不同地区的日期表示 +2. 时区处理 + 支持在不同时区间转换 + 自动处理时间的时区偏移 +3. 类型安全 + 自动将字符串转换为 DateTime 对象 + 保证类型的一致性和正确性 + +##### 基础使用 + +```php +use Astral\Serialize\Attributes\Input\InputDateFormat; +use Astral\Serialize\Attributes\Output\OutputDateFormat; +use Astral\Serialize\Serialize; + +class TimeExample extends Serialize { + + // 输入时间格式转换 + #[InputDateFormat('Y-m-d')] + public DateTime $dateTime; + + // 输入时间格式转换 + #[InputDateFormat('Y-m-d')] + public string $dateDateString; + + // 输出时间格式转换 + #[OutputDateFormat('Y/m/d H:i')] + public DateTime|string $processedAt; + + + // 支持多种时间格式 + #[InputDateFormat('Y-m-d H:i:s')] + #[OutputDateFormat('Y-m-d')] + public string $createdAt; +} + +// 创建订单对象 +$order = TimeExample::from([ + 'dateTime' => new DateTime('2023-08-11'), // 输入格式:Y-m-d + 'dateDateString' => '2023-08-15', // 输入格式:Y-m-d + 'processedAt' => '2023-08-16 14:30', // 输入默认格式 也支持DateTime对象 + 'createdAt' => '2023-08-16 14:45:30' // 输入格式:Y-m-d H:i:s +]); + +// 转换为数组 +$orderArray = $order->toArray(); +// $orderArray 的内容: +// [ +// 'dateTime' => '2023-08-11', +// ’dateDateString' => '2023-08-15', +// 'processedAt' => '2023/08/16 14:30', +// 'createdAt' => '2023-08-16' +// ] +``` + +##### 带时区的时间转换 + +```php + +use Astral\Serialize\Attributes\Input\InputDateFormat; +use Astral\Serialize\Attributes\Output\OutputDateFormat; +use Astral\Serialize\Serialize; + +class AdvancedTimeUser extends Serialize { + // 支持时区转换 + #[InputDateFormat('Y-m-d H:i:s', timezone: 'UTC')] + #[OutputDateFormat('Y-m-d H:i:s', timezone: 'Asia/Shanghai')] + public DateTime $registeredAt; + + // 支持不同地区的时间格式 + #[InputDateFormat('d/m/Y')] // 英国格式 + #[OutputDateFormat('Y-m-d')] // 标准格式 + public DateTime $birthDate; +} + +// 使用高级时间转换 +$advancedUser = AdvancedTimeUser::from([ + 'registeredAt' => '2023-08-16 10:00:00', // UTC 时间 + 'birthDate' => '15/08/1990' // 英国日期格式 +]); + +$advancedUserArray = $advancedUser->toArray(); +// $advancedUserArray 的内容: +// [ +// 'registeredAt' => '2023-08-16 18:00:00', // 转换为上海时区 +// 'birthDate' => '1990-08-15' +// ] +``` \ No newline at end of file diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md new file mode 100644 index 0000000..c6b48ac --- /dev/null +++ b/docs/zh/annotation/group-annotation.md @@ -0,0 +1,191 @@ +#### 属性分组 + +属性分组提供了一种灵活的方式来控制属性的输入和输出行为,允许在不同场景下精细地管理数据转换。 + +##### 基本用法 + +在属性上使用 `#[Groups]` 注解来指定属性所属的分组。 + +```php +use Astral\Serialize\Attributes\Groups; +use Astral\Serialize\Serialize; + +class User extends Serialize { + + #[Groups('update','detail')] + public string $id; + + #[Groups('create', 'update', 'detail')] + public string $name; + + #[Groups('create','detail')] + public string $username; + + #[Groups('other')] + public string $sensitiveData; + + // 没有指定Group 的属性将会被默认分组在default分组中 + public string $noGroupInfo; + + // 构造函数参数也支持分组 + public function __construct( + #[Groups('create','detail')] + public readonly string $email, + + #[Groups('update','detail')] + public readonly int $score + ) {} +} + + + +// 使用 默认分组展示所有信息 +$user1 = User::from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用默认分组 toArray,展示所有信息 +$defaultArray = $user1->toArray(); +// $defaultArray 的内容: +// [ +// 'id' => '1', +// 'name' => '李四', +// 'username' => 'username', +// 'score' => 100, +// 'email' => 'zhangsan@example.com', +// 'sensitiveData' => '机密信息', +// 'noGroupInfo' => '默认分组信息' +// ] + +// 指定分组内容输入 +$defaultArray = $user1->withGroups('create')->toArray(); +// 输出内容 +// [ +// 'name' => '李四', +// 'username' => 'username', +// 'email' => 'zhangsan@example.com', +// ] + +$defaultArray = $user1->withGroups(['detail','other'])->toArray(); +// 输出内容 +// [ +// 'id' => '1', +// 'name' => '李四', +// 'username' => 'username', +// 'score' => 100, +// 'email' => 'zhangsan@example.com', +// 'sensitiveData' => '机密信息', +// ] + + +// 使用 create 分组创建用户 只会接受group为create的数据信息 +$user2 = User::setGroups(['create'])->from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用 create 分组 toArray +$createArray = $user2->toArray(); +// $createArray 的内容: +// [ +// 'name' => '李四', +// 'username' => 'username', +// 'email' => 'zhangsan@example.com', +// ] + +// 使用 update 分组更新用户 只会接受group为update的数据信息 +$user3 = User::setGroups(['update'])->from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用 update 分组 toArray +$updateArray = $user3->toArray(); +// $updateArray 的内容: +// [ +// 'id' => '1', +// 'name' => '李四', +// 'score' => 100, +// ] + +// 使用 detail 和 other 展示用户 会接受group为detail和other的数据信息 +$user4 = User::setGroups(['detail','other'])->from( + id:1, + name: '李四', + score: 100, + username: 'username', + email: 'zhangsan@example.com', + sensitiveData:'机密信息', + noGroupInfo:'默认分组信息' +); + +// 使用多个分组 toArray +$multiGroupArray = $user4->toArray(); +// $multiGroupArray 的内容: +// [ +// 'id' => '1', +// 'name' => '李四', +// 'username' => 'username', +// 'score' => 100, +// 'email' => 'zhangsan@example.com', +// 'sensitiveData' => '机密信息', +// ] +``` + +##### 嵌套类指定Group类展示 + +```php +class ComplexUser extends Serialize { + + public string $name; + + public int $sex; + + public ComplexNestedInfo $info; +} + +class ComplexNestedInfo extends Serialize { + + #[Groups(ComplexAUser::class)] + public float $money; + + public string $currency; +} + +// ComplexNestedInfo 会自动隐藏currency +$adminUser = ComplexUser::from( + name: '张三', + sex: 1, + info: [ + 'money' => 100.00, + 'currency' => 'CNY' + ]; +); + +// 输出数据 +$adminUserArray = $adminUser->toArray(); +// $adminUserArray 的内容: +// [ +// 'name' => '张三', +// 'sex' => 1, +// 'info' => ComplexNestedInfo Object ([ +// 'money' => 100.00 +// ]) +// ] +``` \ No newline at end of file diff --git a/docs/zh/annotation/ignore-annotation.md b/docs/zh/annotation/ignore-annotation.md new file mode 100644 index 0000000..0cd5eb2 --- /dev/null +++ b/docs/zh/annotation/ignore-annotation.md @@ -0,0 +1,140 @@ +#### 字段忽略 + +1. **安全性控制** + - 防止敏感信息的意外泄露 + - 精细控制数据的输入和输出 + +2. **数据过滤** + - 根据不同场景过滤字段 + - 为不同的 API 或用户角色定制数据视图 + +3. **性能优化** + - 减少不必要字段的序列化开销 + - 精简数据传输 + +##### 基础使用 + +```php +use Astral\Serialize\Attributes\InputIgnore; +use Astral\Serialize\Attributes\OutputIgnore; +use Astral\Serialize\Serialize; + + +class User extends Serialize { + + public string $name; + + // 输入时忽略的字段 + #[InputIgnore] + public string $internalId; + + // 输出时忽略的字段 + #[OutputIgnore] + public string $tempData; +} + +// 创建用户对象 +$user = User::from([ + 'name' => '张三', + 'internalId' => 'secret123', // 这个字段会被忽略 + 'tempData' => 'temporary' // 这个字段会被忽略 +]); + +echo $user->internalId; // 这里会输出 '' + +// 转换为数组 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'name' => '张三', +// 'internalId' => '', +// ] +``` + +##### 分组忽略 + +忽略分组需要搭配Groups注解一起使用 + +```php +use Astral\Serialize\Attributes\Input\InputIgnore; +use Astral\Serialize\Attributes\Output\OutputIgnore; +use Astral\Serialize\Serialize; +use Astral\Serialize\Attributes\Groups; + +class ComplexUser extends Serialize { + + #[Groups('admin','public')] + #[InputIgnore('admin')] + public string $name; + + #[Groups('admin','public')] + #[OutputIgnore('public')] + public string $secretKey; + + #[Groups('admin','public')] + #[InputIgnore('admin')] + #[OutputIgnore('public')] + public string $sensitiveInfo; + + #[InputIgnore] + public string $globalInputIgnore; + + #[OutputIgnore] + public string $globalOutputIgnore; +} + +// 默认分组 +$complexUser = ComplexUser::from([ + 'name' => '张三', + 'secretKey' => 'confidential', + 'sensitiveInfo' => '机密信息', + 'globalInputIgnore' => '全局输入忽略', + 'globalOutputIgnore' => '全局输出忽略' +]); + +echo $complexUser->globalInputIgnore; // 输出 ‘’ +echo $complexUser->globalOutputIgnore; // 输出 ‘全局输出忽略’ + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'name' => '张三', +// 'secretKey' => 'confidential', +// 'sensitiveInfo' => '机密信息', +// 'globalInputIgnore' => '', +// ] + + +// 使用admin分组 +$complexUser = ComplexUser::setGroups('admin')->from([ + 'name' => '张三', + 'secretKey' => 'confidential', + 'sensitiveInfo' => '机密信息' + 'globalInputIgnore' => '全局输入忽略', + 'globalOutputIgnore' => '全局输出忽略' +]); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'name' => '', +// 'secretKey' => 'confidential', +// 'globalInputIgnore' => '', +// ] + +// 使用public分组 +$complexUser = ComplexUser::setGroups('public')->from([ + 'name' => '张三', + 'secretKey' => 'confidential', + 'sensitiveInfo' => '机密信息' + 'globalInputIgnore' => '全局输入忽略', + 'globalOutputIgnore' => '全局输出忽略' +]); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'name' => '张三', +/// 'globalInputIgnore' => '', +// ] +``` \ No newline at end of file diff --git a/docs/zh/annotation/mapper-annotation.md b/docs/zh/annotation/mapper-annotation.md new file mode 100644 index 0000000..e2827db --- /dev/null +++ b/docs/zh/annotation/mapper-annotation.md @@ -0,0 +1,198 @@ +##### Mapper映射 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Support\Mappers\{ + CamelCaseMapper, + SnakeCaseMapper, + PascalCaseMapper, + KebabCaseMapper +}; +use Astral\Serialize\Serialize; + +#[Groups('profile','api')] +class User extends Serialize { + // 直接指定映射名称 + #[InputName('user_name', groups: ['profile','api'])] + #[OutputName('userName', groups: ['profile','api'])] + public string $name; + + // 使用映射器进行风格转换 + #[InputName(CamelCaseMapper::class, groups: ['profile','api'])] + #[OutputName(SnakeCaseMapper::class, groups: ['profile','api'])] + public int $userId; + + // 支持多个映射和分组 + #[InputName('profile-email', groups: 'profile')] + #[OutputName('userEmail', groups: 'profile')] + public string $email; +} + +// 使用不同的映射策略 +$user = User::setGroups('profile')::from([ + 'user_name' => '张三', // 映射到 $name + 'userId' => 123, // 使用 CamelCaseMapper 转换 + 'profile-email' => 'user@example.com' // 仅在 'profile' 分组生效 +]); + +// 输出时应用不同的映射 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'userName' => '张三', +// 'user_id' => '三', +// 'userEmail' => user@example.com, +// ] +``` + +##### 全局类映射 + +```php +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Support\Mappers\{ + CamelCaseMapper, + SnakeCaseMapper, + PascalCaseMapper, + KebabCaseMapper +}; +use Astral\Serialize\Serialize; + +#[InputName(SnakeCaseMapper::class)] +#[OutputName(CamelCaseMapper::class)] +class GlobalMappedUser extends Serialize { + // 类级别的映射会自动应用到所有属性 + public string $firstName; + public string $lastName; + public int $userId; + public DateTime $registeredAt; +} + +// 使用全局映射 +$user = GlobalMappedUser::from([ + 'first_name' => '张', // 从蛇形映射到 firstName + 'last_name' => '三', // 从蛇形映射到 lastName + 'user_id' => 123, // 从蛇形映射到 userId + 'registered_at' => '2023-01-01' // 从蛇形映射到 registeredAt +]); + +// 输出时会转换为驼峰命名 +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// 'firstName' => '张', +// 'lastName' => '三', +// 'userId' => 123, +// 'registeredAt' => '2023-01-01' +// ] +``` + +###### 属性映射大于类级映射 + +```php + +#[InputName(SnakeCaseMapper::class)] +class PartialOverrideUser extends Serialize { + #[InputName(PascalCaseMapper::class)] + public string $userName; // 优先使用帕斯卡命名映射 + + public string $userEmail; // 继续使用类级别的全局映射 +} + +$partialUser = PartialOverrideUser::from([ + 'User_name' => '张三', // 使用蛇形映射 + 'UserName' => '李四', // 使用帕斯卡映射 + 'user_email' => 'user@example.com' // 使用蛇形映射 +]); + +$partialUser->toArray(); +// $partialUser 的内容: +// [ +// 'userName' => '李四', +// 'userEmail' => 'user@example.com', +// ] +``` + +###### 全局类映射的分组使用 + +需要搭配`Groups`注解一起使用 + +```php +use Astral\Serialize\Attributes\Groups; +use Astral\Serialize\Attributes\InputName; +use Astral\Serialize\Attributes\OutputName; +use Astral\Serialize\Support\Mappers\{ + CamelCaseMapper, + SnakeCaseMapper, + PascalCaseMapper, + KebabCaseMapper +}; +use Astral\Serialize\Serialize; + + +#[InputName(SnakeCaseMapper::class, groups: 'external')] +#[InputName(CamelCaseMapper::class, groups: 'api')] +#[OutputName(PascalCaseMapper::class, groups: ['external','api'])] +class ComplexMappedUser extends Serialize { + + #[Groups('external', 'api')] + public string $firstName; + + #[Groups('external', 'api')] + public string $lastName; + + + #[InputName('full_name', groups: 'special')] + #[OutputName('userEmail', groups: 'api')] + #[Groups('external', 'api')] + public string $fullName; +} + +// 使用admin分组 +$complexUser = ComplexMappedUser::setGroup('external')->from( + first_name :'张', + last_name :'三' + full_name: '张三' +); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'FirstName' => '张', +// 'LastName' => '三', +// 'FullName' => 张三, +// ] + +// 如果熟悉指定了OutputName/InputName 则属性规则优先 +// 使用public分组 +$complexUser = ComplexMappedUser::setGroup('api')->from( + first_name :'张', + last_name :'三' + full_name: '张三' +); + +$complexUser = $complexUser->toArray(); +// $complexUser 的内容: +// [ +// 'FirstName' => '张', +// 'LastName' => '三', +// 'userEmail' => 张三, +// ] +``` +#### 自定义映射器 + +```php +// 自定义映射器 需要继承NameMapper 并实现 resolve +class CustomMapper implements NameMapper { + public function resolve(string $name): string { + // 实现自定义的命名转换逻辑 + return str_replace('user', 'customer', $name); + } +} + +class AdvancedUser extends Serialize { + #[InputName(CustomMapper::class)] + public string $name; +} +``` \ No newline at end of file diff --git a/docs/zh/faker/collection-faker.md b/docs/zh/faker/collection-faker.md new file mode 100644 index 0000000..ec68dfe --- /dev/null +++ b/docs/zh/faker/collection-faker.md @@ -0,0 +1,49 @@ +### 集合模拟 + +```php + +class UserProfile extends Serialize { + public string $nickname; + public int $age; + public string $email; + public string $avatar; +} + +class UserListFaker extends Serialize { + #[FakerCollection(['name', 'email'], num: 3)] + public array $users; + + #[FakerCollection(UserProfile::class, num: 2)] + public array $profiles; +} + +$userList = UserListFaker::faker(); + +$complexUserListFaker = UserListFaker::faker(); + +$complexUserListFakerArray = $complexUserListFaker->toArray(); +// $complexUserListFakerArray 的内容: +// [ +// 'profile' => [ +// [0] => UserProfile Object ( +// [ +// 'nickname' => 'RandomNickname', +// 'age' => 28, 'email' => 'random.user@example.com', +// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' +// ], +// ), +// [1] => UserProfile Object ( +// [ +// 'nickname' => 'RandomNickname', +// 'age' => 28, 'email' => 'random.user@example.com', +// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' +// ], +// ) +// ], +// 'users' => [ +// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] +// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] +// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] +// ] +// ] +``` diff --git a/docs/zh/faker/method-faker.md b/docs/zh/faker/method-faker.md new file mode 100644 index 0000000..cdd8020 --- /dev/null +++ b/docs/zh/faker/method-faker.md @@ -0,0 +1,109 @@ +### Faker类方法模拟 + +```php +class UserService { + public function generateUserData(): array { + return ['name' => 'Generated User']; + } +} + +class UserFaker extends Serialize { + #[FakerMethod(UserService::class, 'generateUserData')] + public array $userData; +} +``` + +#### 完整的示例 + +```php +use Astral\Serialize\Serialize; +use Astral\Serialize\Attributes\Faker\FakerMethod; +use Astral\Serialize\Attributes\Faker\FakerObject; +use Astral\Serialize\Attributes\Faker\FakerCollection; + +// 用户配置文件类 +class UserProfile extends Serialize { + public string $nickname; + public int $age; + public string $email; + public array $types = ['type1' => 'money', 'type2' => 'score']; +} + +// 用户服务类,提供数据生成方法 +class UserService { + public function generateUserData(): array { + return [ + 'name' => 'Generated User', + 'email' => 'generated.user@example.com', + 'age' => 30 + ]; + } + + public function generateUserProfile(UserProfile $user): UserProfile { + return $user; + } + + public function generateUserList(int $count): array { + $users = []; + for ($i = 0; $i < $count; $i++) { + $users[] = [ + 'name' => "User {$i}", + 'email' => "user{$i}@example.com" + ]; + } + return $users; + } +} + +// Faker 方法模拟示例 +class UserFaker extends Serialize { + // 使用方法生成简单数据 + #[FakerMethod(UserService::class, 'generateUserData')] + public array $userData; + + // 使用方法生成对象 + #[FakerMethod(UserService::class, 'generateUserProfile')] + public UserProfile $userProfile; + + // 获取指定属性 + #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'age')] + public int $age; + + // 获取指定属性 多级可以使用[.]链接 + #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'types.type2')] + public string $type2; + + // 传入参数 + #[FakerMethod(UserService::class, 'generateUserList',params:['count'=> 3])] + public array $userList; +} + +// 生成模拟数据 +$userFaker = UserFaker::faker(); + +// 转换为数组 +$userFakerArray = $userFaker->toArray(); +// $userFakerArray 的内容: +// [ +// 'userData' => [ +// 'name' => 'Generated User', +// 'email' => 'generated.user@example.com', +// 'age' => 30 +// ], +// 'userProfile' => UserProfile Object ( +// [ +// 'nickname' => 'GeneratedNickname', +// 'age' => 25, // 随机生成 +// 'email' => 'profile@example.com' +// 'types' => ['type1' => 'money', 'type2' => 'score'] +// ] +// ), +// 'age' => 99 , // 随机生成 +// 'type2' => 'score', +// 'userList' => [ +// ['name' => 'User 0', 'email' => 'user0@example.com'], +// ['name' => 'User 1', 'email' => 'user1@example.com'], +// ['name' => 'User 2', 'email' => 'user2@example.com'] +// ] +// ] +``` \ No newline at end of file diff --git a/docs/zh/faker/nested-faker.md b/docs/zh/faker/nested-faker.md new file mode 100644 index 0000000..216abd2 --- /dev/null +++ b/docs/zh/faker/nested-faker.md @@ -0,0 +1,52 @@ +### 嵌套对象模拟 + +#### 基本用法 + +```php +class ComplexUserFaker extends Serialize { + #[FakerObject(UserProfile::class)] + public UserProfile $profile; +} +``` + +#### 演示实例 + +```php +use Astral\Serialize\Serialize; +use Astral\Serialize\Attributes\FakerObject; +use Astral\Serialize\Attributes\FakerCollection; + +class UserProfile extends Serialize { + public string $nickname; + public int $age; + public string $email; + public string $avatar; +} + +class UserTag extends Serialize { + public string $name; + public string $color; +} + +class ComplexUserFaker extends Serialize { + #[FakerObject(UserProfile::class)] + public UserProfile $profile; + + #[FakerObject(UserTag::class)] + public UserTag|UserProfile $primaryTag; + +} + +$complexUserFaker = ComplexUserFaker::faker(); + +$complexUserFakerArray = $complexUserFaker->toArray(); +// $complexUserFakerArray 的内容: +// [ +// 'profile' => UserProfile Object ( +// ['nickname' => 'RandomNickname', 'age' => 28, 'email' => 'random.user@example.com', 'avatar' => 'https://example.com/avatars/random-avatar.jpg'] +// ), +// 'primaryTag' => UserTag Object ( +// ['name' => 'Developer', 'color' => '#007bff'] +// ) +// ] +``` diff --git a/docs/zh/faker/value-faker.md b/docs/zh/faker/value-faker.md new file mode 100644 index 0000000..2b79213 --- /dev/null +++ b/docs/zh/faker/value-faker.md @@ -0,0 +1,36 @@ +### 简单属性模拟 + +```php +class UserFaker extends Serialize { + #[FakerValue('name')] + public string $name; + + #[FakerValue('email')] + public string $email; + + #[FakerValue('uuid')] + public string $userId; + + #[FakerValue('phoneNumber')] + public string $phone; + + #[FakerValue('age')] + public int $age; + + #[FakerValue('boolean')] + public bool $isActive; +} + +$user = UserFaker::faker(); + +$userArray = $user->toArray(); +// $userArray 的内容: +// [ +// "name" => "John Doe" +// "email" => "john.doe@example.com" +// "userId" => "550e8400-e29b-41d4-a716-446655440000" +// "phone" => "+1-555-123-4567" +// "age" => 35 +// "isActive" => true +// ] +``` \ No newline at end of file From be44e205e352b2e84998a917f345db20a2bdecf1 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 16:36:56 +0800 Subject: [PATCH 34/59] gitbook test --- docs/zh/README.md | 1574 +------------------------------------------- docs/zh/SUMMARY.md | 4 +- 2 files changed, 13 insertions(+), 1565 deletions(-) diff --git a/docs/zh/README.md b/docs/zh/README.md index 43d49ac..9b10cc7 100644 --- a/docs/zh/README.md +++ b/docs/zh/README.md @@ -1,1566 +1,16 @@ -# Astral Serialize 文档 +# php-serialize -## 快速开始 +**php-serialize** 是一个功能强大的基于属性(attribute)的 PHP 序列化库(需要 **PHP ≥ 8.1**)。 +它允许你将对象映射为数组或 JSON,并且可以基于相同的属性 **自动生成 OpenAPI 文档**。 -### 安装 +> 🚀 统一解决方案,支持 API 数据序列化和文档生成。 -使用 Composer 安装: +## ✨ 功能特色 -```bash -composer require astral/serialize -``` - -### 基本用法 - -```php -use Astral\Serialize\Serialize; - -class User extends Serialize { - public string $name, - public int $age -} - -// 从数组创建对象 -$user = User::from([ - 'name' => '张三', - 'age' => 30 -]); - -// 访问对象属性 -echo $user->name; // 输出: 张三 -echo $user->age; // 输出: 30 - -// 转换为数组 -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'name' => '张三', -// 'age' => 30 -// ] -``` - -#### 其他特性 - -1. **不可变性**:只读属性在构造后无法修改 - -```php -use Astral\Serialize\Serialize; - -class User extends Serialize { - public function __construct( - public readonly string $name, - public readonly int $age - ) {} -} - -$user = User::from([ - 'name' => '张三', - 'age' => 30 -]); - -try { - $user->name = '李四'; // 编译时错误:无法修改只读属性 -} catch (Error $e) { - echo "只读属性不能被重新赋值"; -} -``` - -2. **类型安全的初始化** - -```php -$user = User::from([ - 'name' => 123, // 整数会被转换为字符串 - 'age' => '35' // 字符串会被转换为整数 -]); - -echo $user->name; // 输出: "123" -echo $user->age; // 输出: 35 -``` - -3. **构造函数初始化** - -```php -use Astral\Serialize\Serialize; - -class User extends Serialize { - public function __construct( - public readonly string $name, - public readonly int $age - ) { - // 可以在构造函数中添加额外的验证或处理逻辑 - if (strlen($name) < 2) { - throw new \InvalidArgumentException('名称太短'); - } - } -} -``` - -## 生成openapi文档 - -#### 创建Request文件 -```php -use Astral\Serialize\Serialize; - -class UserAddRequest extends Serialize { - public string $name; - public int $id; -} - -class UserDetailRequest extends Serialize { - public int $id; -} -``` - -#### 创建Repose文件 -```php -use Astral\Serialize\Serialize; - -class UserDto extends Serialize { - public string $name, - public int $id; -} -``` - -#### 创建Controller文件 -```php -use Astral\Serialize\Serialize; -use Astral\Serialize\OpenApi\Enum\MethodEnum; - -#[\Astral\Serialize\OpenApi\Annotations\Tag('用户模块管理')] -class UserController { - - #[\Astral\Serialize\OpenApi\Annotations\Summary('创建用户')] - #[\Astral\Serialize\OpenApi\Annotations\Route('/user/create')] - #[\Astral\Serialize\OpenApi\Annotations\RequestBody(UserAddRequest::class)] - #[\Astral\Serialize\OpenApi\Annotations\Response(UserDto::class)] - public function create() - { - return new UserDto(); - } - - #[\Astral\Serialize\OpenApi\Annotations\Summary('用户详情')] - #[\Astral\Serialize\OpenApi\Annotations\Route(route:'/user/detail', method: MethodEnum::GET)] - public function detail(UserDetailRequest $request): UserDto - { - return new UserDto(); - } -} -``` - -## DTO 转换 - -### 类型转换 - -#### 基本类型转换 - -##### 方式一:构造函数属性提升 - -```php -use Astral\Serialize\Serialize; - -class Profile extends Serialize { - public function __construct( - public string $username, - public int $score, - public float $balance, - public bool $isActive - ) {} -} -``` - -##### 方式二:传统属性定义 - -```php -use Astral\Serialize\Serialize; - -class Profile extends Serialize { - public string $username; - public int $score; - public float $balance; - public bool $isActive; -} - -// 两种方式都支持相同的类型转换 -$profile = Profile::from([ - 'username' => 123, // 整数转换为字符串 - 'score' => '100', // 字符串转换为整数 - 'balance' => '99.99', // 字符串转换为浮点数 - 'isActive' => 1 // 数字转换为布尔值 -]); - -// 转换为数组 -$profileArray = $profile->toArray(); -``` - -##### 方式三:只读属性 - -```php -use Astral\Serialize\Serialize; - -class Profile extends Serialize { - public readonly string $username; - public readonly int $score; - public readonly float $balance; - public readonly bool $isActive; - - // 手动初始化 - public function __construct( - string $username, - int $score, - float $balance, - bool $isActive - ) { - $this->username = $username; - $this->score = $score; - $this->balance = $balance; - $this->isActive = $isActive; - } -} -``` - -无论使用哪种方式,`Serialize` 类都能正常工作,并提供相同的类型转换和序列化功能。 - - - -#### Null 值转换规则详细示例 - -当属性不是可空类型(`?type`)时,`null` 值会根据目标类型自动转换: - -```php -use Astral\Serialize\Serialize; - -class NullConversionProfile extends Serialize { - public string $username; - public int $score; - public float $balance; - public array $tags; - public object $metadata; -} - -// Null 值转换示例 -$profile = NullConversionProfile::from([ - 'username' => null, // 转换为空字符串 '' - 'score' => null, // 转换为 0 - 'balance' => null, // 转换为 0.0 - 'tags' => null, // 转换为空数组 [] - 'metadata' => null // 转换为空对象 new stdClass() -]); - -// 验证转换结果 -echo $profile->username; // 输出: ""(空字符串) -echo $profile->score; // 输出: 0 -echo $profile->balance; // 输出: 0.0 -var_dump($profile->tags); // 输出: array(0) {} -var_dump($profile->metadata); // 输出: object(stdClass)#123 (0) {} - -// 布尔值的特殊处理 -try { - NullConversionProfile::from([ - 'isActive' => null // 这将抛出类型错误 - ]); -} catch (\TypeError $e) { - echo "布尔类型不支持 null 值:" . $e->getMessage(); -} -``` - -#### 可空类型的方案 - -对于需要接受 `null` 的场景,使用可空类型: - -```php -use Astral\Serialize\Serialize; - -class FlexibleProfile extends Serialize { - public function __construct( - public ?string $username, - public ?int $score, - public ?object $metadata, - public ?array $tags - ) {} -} - -// 创建包含 null 值的对象 -$profile = FlexibleProfile::from([ - 'username' => null, // 允许 null - 'score' => null, // 允许 null - 'metadata' => null, // 允许 null - 'tags' => null // 允许 null -]); - -// 转换为数组 -$profileArray = $profile->toArray(); -// $profileArray 的内容: -// [ -// 'username' => null, -// 'score' => null, -// 'metadata' => null, -// 'tags' => null -// ] - -// 验证可空类型的行为 -echo $profile->username; // 输出 null -``` - -#### 联合类型 - -1. 可以混合使用基本类型和对象类型 -2. 对象层级匹配 - 对于多个对象类型,会选择最匹配的类型 - 支持继承层级的智能匹配 -3. 动态类型处理 - 自动处理不同类型的输入 - 提供更加灵活的数据建模方式 - -```php -use Astral\Serialize\Serialize; - -// 定义一个基础用户类 -class User extends Serialize { - public string $name; - public int $age; -} - -// 定义一个管理员用户类 -class AdminUser extends User { - public string $role; -} - -class FlexibleData extends Serialize { - // 支持整数或字符串类型的标识符 - public int|string $flexibleId; - - // 支持用户对象或整数标识符 - public User|int $userIdentifier; - - // 支持多种复杂的联合类型 - public AdminUser|User|int $complexIdentifier; -} - -// 场景1:使用整数作为 flexibleId -$data1 = FlexibleData::from([ - 'flexibleId' => 123, - 'userIdentifier' => 456, - 'complexIdentifier' => 789 -]); - -$data1Array = $data1->toArray(); -// $data1Array 的内容: -// [ -// 'flexibleId' => 123, -// 'userIdentifier' => 456, -// 'complexIdentifier' => 789 -// ] - -// 场景2:使用字符串作为 flexibleId -$data2 = FlexibleData::from([ - 'flexibleId' => 'ABC123', - 'userIdentifier' => [ - 'name' => '张三', - 'age' => 30 - ], - 'complexIdentifier' => [ - 'name' => '李四', - 'age' => 25 - ] -]); - -echo $data2->userIdentifier; // 输出 User 对象 -echo $data2->complexIdentifier; // 输出 User 对象 - -$data2Array = $data2->toArray(); -// $data2Array 的内容: -// [ -// 'flexibleId' => 'ABC123', -// 'userIdentifier' => User Object ( -// ['name' => '张三', 'age' => 30] -// ), -// 'complexIdentifier' => User Object ( -// ['name' => '李四', 'age' => 25] -// ) -// ] - -// 场景3:使用管理员用户 -$data3 = FlexibleData::from([ - 'flexibleId' => 'USER001', - 'userIdentifier' => [ - 'name' => '王五', - 'age' => 35, - 'role' => 'admin' - ], - 'complexIdentifier' => [ - 'name' => '赵六', - 'age' => 40, - 'role' => 'super_admin' - ] -]); - -echo $data2->userIdentifier; // 输出 User 对象 -echo $data2->complexIdentifier; // 输出 AdminUser 对象 - -$data3Array = $data3->toArray(); -// $data3Array 的内容: -// [ -// 'flexibleId' => 'USER001', -// 'userIdentifier' => User Object ( -// ['name' => '王五', 'age' => 35] -// ), -// 'complexIdentifier' => AdminUser Object ( -// ['name' => '赵六', 'age' => 40, 'role' => 'super_admin'] -// ) -// ] -``` - -#### 数组对象转换 - -##### phpDoc定义 - -```php -use Astral\Serialize\Serialize; - -// 定义基础数组类型 -class ArrayOne extends Serialize { - public string $type = 'one'; - public string $name; -} - -class ArrayTwo extends Serialize { - public string $type = 'two'; - public string $code; -} - -class MultiArraySerialize extends Serialize { - // 场景1:混合类型数组 - /** @var (ArrayOne|ArrayTwo)[] */ - public array $mixedTypeArray; - - // 场景2:多类型数组 - /** @var ArrayOne[]|ArrayTwo[] */ - public array $multiTypeArray; - - // 场景3:键值对混合类型 - /** @var array(string, ArrayOne|ArrayTwo) */ - public array $keyValueMixedArray; -} - -// 场景1:混合类型数组 -$data1 = MultiArraySerialize::from( - mixedTypeArray : [ - ['name' => '张三'], // 转化 ArrayOne 对象 - ['code' => 'ABC123'], // 转化 ArrayTwo 对象 - ['name' => '李四'], // 转化 ArrayOne 对象 - ['code' => 'DEF456'] // 转化 ArrayTwo 对象 - ] -); - -$data1Array = $data1->toArray(); -// $data1Array 的内容: -// [ -// 'mixedTypeArray' => [ -// [0] => ArrayOne Object -// ( -// ['name' => '张三', 'type' => 'one'], -// ) -// [1] => ArrayTwo Object -// ( -// ['code' => 'ABC123', 'type' => 'two'], -// ) -// [2] => ArrayOne Object -// ( -// ['name' => '李四', 'type' => 'one'], -// ) -// [3] => ArrayTwo Object -// ( -// ['code' => 'DEF456', 'type' => 'two'], -// ) -// ] -// ] - -// 场景2:多类型数组 -$data2 = MultiArraySerialize::from( - multiTypeArray:[ - ['name' => '王五'], // 转化 ArrayOne 对象 - ['name' => '赵六'], // 转化 ArrayOne 对象 - ['code' => 'GHI789'] // 转化 ArrayTwo 对象 - ] -); - -$data2Array = $data2->toArray(); -// $data2Array 的内容: -// [ -// 'multiTypeArray' => [ -// ArrayOne Object ( -// ['name' => '王五', 'type' => 'one'] -// ), -// ArrayOne Object ( -// ['name' => '赵六', 'type' => 'one'] -// ), -// ArrayTwo Object ( -// ['code' => 'GHI789', 'type' => 'two'] -// ) -// ] -// ] - -// 场景3:键值对混合类型 -$data3 = MultiArraySerialize::from( - keyValueMixedArray: [ - 'user1' => ['name' => '张三'], // 转化 ArrayOne 对象 - 'system1' => ['code' => 'ABC123'], // 转化 ArrayTwo 对象 - 'user2' => ['name' => '李四'] // 转化 ArrayOne 对象 - ] -); - -$data3Array = $data3->toArray(); -// $data3Array 的内容: -// [ -// 'keyValueMixedArray' => [ -// 'user1' => ArrayOne Object ( -// ['name' => '张三', 'type' => 'one'] -// ), -// 'system1' => ArrayTwo Object ( -// ['code' => 'ABC123', 'type' => 'two'] -// ), -// 'user2' => ArrayOne Object ( -// ['name' => '李四', 'type' => 'one'] -// ) -// ] -// ] - -// 场景4:无法匹配时的处理 -$data4 = MultiArraySerialize::from( - mixedTypeArray : [ - ['unknown' => 'data1'], - ['another' => 'data2'] - ] -); - -$data4Array = $data4->toArray(); -// $data4Array 的内容: -// [ -// 'mixedTypeArray' => [ -// ['unknown' => 'data1'], -// ['another' => 'data2'] -// ] -// ] -``` - -### 注解类使用 - -#### 属性分组 - -属性分组提供了一种灵活的方式来控制属性的输入和输出行为,允许在不同场景下精细地管理数据转换。 - -##### 基本用法 - -在属性上使用 `#[Groups]` 注解来指定属性所属的分组。 - -```php -use Astral\Serialize\Attributes\Groups; -use Astral\Serialize\Serialize; - -class User extends Serialize { - - #[Groups('update','detail')] - public string $id; - - #[Groups('create', 'update', 'detail')] - public string $name; - - #[Groups('create','detail')] - public string $username; - - #[Groups('other')] - public string $sensitiveData; - - // 没有指定Group 的属性将会被默认分组在default分组中 - public string $noGroupInfo; - - // 构造函数参数也支持分组 - public function __construct( - #[Groups('create','detail')] - public readonly string $email, - - #[Groups('update','detail')] - public readonly int $score - ) {} -} - - - -// 使用 默认分组展示所有信息 -$user1 = User::from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用默认分组 toArray,展示所有信息 -$defaultArray = $user1->toArray(); -// $defaultArray 的内容: -// [ -// 'id' => '1', -// 'name' => '李四', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// 'noGroupInfo' => '默认分组信息' -// ] - -// 指定分组内容输入 -$defaultArray = $user1->withGroups('create')->toArray(); -// 输出内容 -// [ -// 'name' => '李四', -// 'username' => 'username', -// 'email' => 'zhangsan@example.com', -// ] - -$defaultArray = $user1->withGroups(['detail','other'])->toArray(); -// 输出内容 -// [ -// 'id' => '1', -// 'name' => '李四', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// ] - - -// 使用 create 分组创建用户 只会接受group为create的数据信息 -$user2 = User::setGroups(['create'])->from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用 create 分组 toArray -$createArray = $user2->toArray(); -// $createArray 的内容: -// [ -// 'name' => '李四', -// 'username' => 'username', -// 'email' => 'zhangsan@example.com', -// ] - -// 使用 update 分组更新用户 只会接受group为update的数据信息 -$user3 = User::setGroups(['update'])->from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用 update 分组 toArray -$updateArray = $user3->toArray(); -// $updateArray 的内容: -// [ -// 'id' => '1', -// 'name' => '李四', -// 'score' => 100, -// ] - -// 使用 detail 和 other 展示用户 会接受group为detail和other的数据信息 -$user4 = User::setGroups(['detail','other'])->from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用多个分组 toArray -$multiGroupArray = $user4->toArray(); -// $multiGroupArray 的内容: -// [ -// 'id' => '1', -// 'name' => '李四', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// ] -``` - -##### 嵌套类指定Group类展示 - -```php -class ComplexUser extends Serialize { - - public string $name; - - public int $sex; - - public ComplexNestedInfo $info; -} - -class ComplexNestedInfo extends Serialize { - - #[Groups(ComplexAUser::class)] - public float $money; - - public string $currency; -} - -// ComplexNestedInfo 会自动隐藏currency -$adminUser = ComplexUser::from( - name: '张三', - sex: 1, - info: [ - 'money' => 100.00, - 'currency' => 'CNY' - ]; -); - -// 输出数据 -$adminUserArray = $adminUser->toArray(); -// $adminUserArray 的内容: -// [ -// 'name' => '张三', -// 'sex' => 1, -// 'info' => ComplexNestedInfo Object ([ -// 'money' => 100.00 -// ]) -// ] -``` - -#### 名称映射 - -##### 基础使用 - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Serialize; - -class User extends Serialize { - // 输入时使用不同的属性名 - #[InputName('user_name')] - public string $name; - - // 输出时使用不同的属性名 - #[OutputName('user_id')] - public int $id; - - // 同时支持输入和输出不同名称 - #[InputName('register_time')] - #[OutputName('registeredAt')] - public DateTime $createdAt; -} - -// 使用不同名称的输入数据 -$user = User::from([ - 'user_name' => '张三', // 映射到 $name - 'id' => 123, // 保持不变 - 'register_time' => '2023-01-01 10:00:00' // 映射到 $createdAt -]); - -// 输出数据 -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'name' => '张三', -// 'user_id' => 123, -// 'registeredAt' => '2023-01-01 10:00:00' -// ] -``` - -##### 多输入/输出名称处理 - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Serialize; - -class MultiOutputUser extends Serialize { - // 多个输出名称 - #[OutputName('user_id')] - #[OutputName('id')] - #[OutputName('userId')] - public int $id; - - // 多个输出名称 按照声明顺序取地一个匹配的name - #[InputName('user_name')] - #[InputName('other_name')] - #[InputName('userName')] - public int $name; - -} - -// 场景1:使用第一个匹配的输入名称 -$user1 = MultiInputUser::from([ - 'user_name' => '张三' // 使用 'user_name' -]); -echo $user1->name; // 输出 '张三' - -// 场景2:使用第二个匹配的输入名称 -$user2 = MultiInputUser::from([ - 'other_name' => '李四' // 使用 'other_name' -]); -echo $user2->name; // 输出 '李四' - -// 场景3:使用最后的输入名称 -$user3 = MultiInputUser::from([ - 'userName' => '王五' // 使用 'userName' -]); -echo $user3->name; // 输出 '王五' - -// 场景4:传入多个的时候 按照声明顺序取地一个匹配的name -$user4 = MultiInputUser::from([ - 'userName' => '王五', - 'other_name' => '李四', - 'user_name' => '张三', -]); -echo $user4->name; // 输出 '张三' - -// 创建用户对象 -$user = MultiOutputUser::from([ - 'id' => 123, - 'name' => '张三' -]); - -// 转换为数组 -// tips: 因为id 有多个outputname 所以输出了 ['user_id','id','userId'] -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'user_id' => 123, -// 'id' => 123, -// 'userId' => 123, -// ] -``` - -##### 复杂映射场景 - -```php -use Astral\Serialize\Serialize; - -class ComplexUser extends Serialize { - // 嵌套对象的名称映射 - #[InputName('user_profile')] - public UserProfile $profile; - - // 数组元素的名称映射 - #[InputName('user_tags')] - public array $tags; -} - -// 处理复杂的输入结构 -$complexUser = ComplexUser::from([ - 'user_profile' => [ - 'nickname' => '小明', - 'age' => 25 - ], - 'user_tags' => ['developer', 'programmer'] -]); - -// 转换为标准数组 -$complexUserArray = $complexUser->toArray(); -// $complexUserArray 的内容: -// [ -// 'profile' => UserProfile Object ([ -// 'nickname' => '小明', -// 'age' => 25 -// ]), -// 'tags' => ['developer', 'programmer'] -// ] -``` - -##### Mapper映射 - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Support\Mappers\{ - CamelCaseMapper, - SnakeCaseMapper, - PascalCaseMapper, - KebabCaseMapper -}; -use Astral\Serialize\Serialize; - -#[Groups('profile','api')] -class User extends Serialize { - // 直接指定映射名称 - #[InputName('user_name', groups: ['profile','api'])] - #[OutputName('userName', groups: ['profile','api'])] - public string $name; - - // 使用映射器进行风格转换 - #[InputName(CamelCaseMapper::class, groups: ['profile','api'])] - #[OutputName(SnakeCaseMapper::class, groups: ['profile','api'])] - public int $userId; - - // 支持多个映射和分组 - #[InputName('profile-email', groups: 'profile')] - #[OutputName('userEmail', groups: 'profile')] - public string $email; -} - -// 使用不同的映射策略 -$user = User::setGroups('profile')::from([ - 'user_name' => '张三', // 映射到 $name - 'userId' => 123, // 使用 CamelCaseMapper 转换 - 'profile-email' => 'user@example.com' // 仅在 'profile' 分组生效 -]); - -// 输出时应用不同的映射 -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'userName' => '张三', -// 'user_id' => '三', -// 'userEmail' => user@example.com, -// ] -``` - -##### 全局类映射 - -```php -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Support\Mappers\{ - CamelCaseMapper, - SnakeCaseMapper, - PascalCaseMapper, - KebabCaseMapper -}; -use Astral\Serialize\Serialize; - -#[InputName(SnakeCaseMapper::class)] -#[OutputName(CamelCaseMapper::class)] -class GlobalMappedUser extends Serialize { - // 类级别的映射会自动应用到所有属性 - public string $firstName; - public string $lastName; - public int $userId; - public DateTime $registeredAt; -} - -// 使用全局映射 -$user = GlobalMappedUser::from([ - 'first_name' => '张', // 从蛇形映射到 firstName - 'last_name' => '三', // 从蛇形映射到 lastName - 'user_id' => 123, // 从蛇形映射到 userId - 'registered_at' => '2023-01-01' // 从蛇形映射到 registeredAt -]); - -// 输出时会转换为驼峰命名 -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'firstName' => '张', -// 'lastName' => '三', -// 'userId' => 123, -// 'registeredAt' => '2023-01-01' -// ] -``` - -###### 属性映射大于类级映射 - -```php - -#[InputName(SnakeCaseMapper::class)] -class PartialOverrideUser extends Serialize { - #[InputName(PascalCaseMapper::class)] - public string $userName; // 优先使用帕斯卡命名映射 - - public string $userEmail; // 继续使用类级别的全局映射 -} - -$partialUser = PartialOverrideUser::from([ - 'User_name' => '张三', // 使用蛇形映射 - 'UserName' => '李四', // 使用帕斯卡映射 - 'user_email' => 'user@example.com' // 使用蛇形映射 -]); - -$partialUser->toArray(); -// $partialUser 的内容: -// [ -// 'userName' => '李四', -// 'userEmail' => 'user@example.com', -// ] -``` - -###### 全局类映射的分组使用 - -需要搭配`Groups`注解一起使用 - -```php -use Astral\Serialize\Attributes\Groups; -use Astral\Serialize\Attributes\InputName; -use Astral\Serialize\Attributes\OutputName; -use Astral\Serialize\Support\Mappers\{ - CamelCaseMapper, - SnakeCaseMapper, - PascalCaseMapper, - KebabCaseMapper -}; -use Astral\Serialize\Serialize; - - -#[InputName(SnakeCaseMapper::class, groups: 'external')] -#[InputName(CamelCaseMapper::class, groups: 'api')] -#[OutputName(PascalCaseMapper::class, groups: ['external','api'])] -class ComplexMappedUser extends Serialize { - - #[Groups('external', 'api')] - public string $firstName; - - #[Groups('external', 'api')] - public string $lastName; - - - #[InputName('full_name', groups: 'special')] - #[OutputName('userEmail', groups: 'api')] - #[Groups('external', 'api')] - public string $fullName; -} - -// 使用admin分组 -$complexUser = ComplexMappedUser::setGroup('external')->from( - first_name :'张', - last_name :'三' - full_name: '张三' -); - -$complexUser = $complexUser->toArray(); -// $complexUser 的内容: -// [ -// 'FirstName' => '张', -// 'LastName' => '三', -// 'FullName' => 张三, -// ] - -// 如果熟悉指定了OutputName/InputName 则属性规则优先 -// 使用public分组 -$complexUser = ComplexMappedUser::setGroup('api')->from( - first_name :'张', - last_name :'三' - full_name: '张三' -); - -$complexUser = $complexUser->toArray(); -// $complexUser 的内容: -// [ -// 'FirstName' => '张', -// 'LastName' => '三', -// 'userEmail' => 张三, -// ] -``` - -#### 自定义映射器 - -```php -// 自定义映射器 需要继承NameMapper 并实现 resolve -class CustomMapper implements NameMapper { - public function resolve(string $name): string { - // 实现自定义的命名转换逻辑 - return str_replace('user', 'customer', $name); - } -} - -class AdvancedUser extends Serialize { - #[InputName(CustomMapper::class)] - public string $name; -} -``` - -#### 字段忽略 - -1. **安全性控制** - - 防止敏感信息的意外泄露 - - 精细控制数据的输入和输出 - -2. **数据过滤** - - 根据不同场景过滤字段 - - 为不同的 API 或用户角色定制数据视图 - -3. **性能优化** - - 减少不必要字段的序列化开销 - - 精简数据传输 - -##### 基础使用 - -```php -use Astral\Serialize\Attributes\InputIgnore; -use Astral\Serialize\Attributes\OutputIgnore; -use Astral\Serialize\Serialize; - - -class User extends Serialize { - - public string $name; - - // 输入时忽略的字段 - #[InputIgnore] - public string $internalId; - - // 输出时忽略的字段 - #[OutputIgnore] - public string $tempData; -} - -// 创建用户对象 -$user = User::from([ - 'name' => '张三', - 'internalId' => 'secret123', // 这个字段会被忽略 - 'tempData' => 'temporary' // 这个字段会被忽略 -]); - -echo $user->internalId; // 这里会输出 '' - -// 转换为数组 -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// 'name' => '张三', -// 'internalId' => '', -// ] -``` - -##### 分组忽略 - -忽略分组需要搭配Groups注解一起使用 - -```php -use Astral\Serialize\Attributes\Input\InputIgnore; -use Astral\Serialize\Attributes\Output\OutputIgnore; -use Astral\Serialize\Serialize; -use Astral\Serialize\Attributes\Groups; - -class ComplexUser extends Serialize { - - #[Groups('admin','public')] - #[InputIgnore('admin')] - public string $name; - - #[Groups('admin','public')] - #[OutputIgnore('public')] - public string $secretKey; - - #[Groups('admin','public')] - #[InputIgnore('admin')] - #[OutputIgnore('public')] - public string $sensitiveInfo; - - #[InputIgnore] - public string $globalInputIgnore; - - #[OutputIgnore] - public string $globalOutputIgnore; -} - -// 默认分组 -$complexUser = ComplexUser::from([ - 'name' => '张三', - 'secretKey' => 'confidential', - 'sensitiveInfo' => '机密信息', - 'globalInputIgnore' => '全局输入忽略', - 'globalOutputIgnore' => '全局输出忽略' -]); - -echo $complexUser->globalInputIgnore; // 输出 ‘’ -echo $complexUser->globalOutputIgnore; // 输出 ‘全局输出忽略’ - -$complexUser = $complexUser->toArray(); -// $complexUser 的内容: -// [ -// 'name' => '张三', -// 'secretKey' => 'confidential', -// 'sensitiveInfo' => '机密信息', -// 'globalInputIgnore' => '', -// ] - - -// 使用admin分组 -$complexUser = ComplexUser::setGroups('admin')->from([ - 'name' => '张三', - 'secretKey' => 'confidential', - 'sensitiveInfo' => '机密信息' - 'globalInputIgnore' => '全局输入忽略', - 'globalOutputIgnore' => '全局输出忽略' -]); - -$complexUser = $complexUser->toArray(); -// $complexUser 的内容: -// [ -// 'name' => '', -// 'secretKey' => 'confidential', -// 'globalInputIgnore' => '', -// ] - -// 使用public分组 -$complexUser = ComplexUser::setGroups('public')->from([ - 'name' => '张三', - 'secretKey' => 'confidential', - 'sensitiveInfo' => '机密信息' - 'globalInputIgnore' => '全局输入忽略', - 'globalOutputIgnore' => '全局输出忽略' -]); - -$complexUser = $complexUser->toArray(); -// $complexUser 的内容: -// [ -// 'name' => '张三', -/// 'globalInputIgnore' => '', -// ] -``` - -#### 时间转换 - -1. 格式灵活性 - 支持多种输入和输出时间格式 - 可以轻松处理不同地区的日期表示 -2. 时区处理 - 支持在不同时区间转换 - 自动处理时间的时区偏移 -3. 类型安全 - 自动将字符串转换为 DateTime 对象 - 保证类型的一致性和正确性 - -##### 基础使用 - -```php -use Astral\Serialize\Attributes\Input\InputDateFormat; -use Astral\Serialize\Attributes\Output\OutputDateFormat; -use Astral\Serialize\Serialize; - -class TimeExample extends Serialize { - - // 输入时间格式转换 - #[InputDateFormat('Y-m-d')] - public DateTime $dateTime; - - // 输入时间格式转换 - #[InputDateFormat('Y-m-d')] - public string $dateDateString; - - // 输出时间格式转换 - #[OutputDateFormat('Y/m/d H:i')] - public DateTime|string $processedAt; - - - // 支持多种时间格式 - #[InputDateFormat('Y-m-d H:i:s')] - #[OutputDateFormat('Y-m-d')] - public string $createdAt; -} - -// 创建订单对象 -$order = TimeExample::from([ - 'dateTime' => new DateTime('2023-08-11'), // 输入格式:Y-m-d - 'dateDateString' => '2023-08-15', // 输入格式:Y-m-d - 'processedAt' => '2023-08-16 14:30', // 输入默认格式 也支持DateTime对象 - 'createdAt' => '2023-08-16 14:45:30' // 输入格式:Y-m-d H:i:s -]); - -// 转换为数组 -$orderArray = $order->toArray(); -// $orderArray 的内容: -// [ -// 'dateTime' => '2023-08-11', -// ’dateDateString' => '2023-08-15', -// 'processedAt' => '2023/08/16 14:30', -// 'createdAt' => '2023-08-16' -// ] -``` - -##### 带时区的时间转换 - -```php - -use Astral\Serialize\Attributes\Input\InputDateFormat; -use Astral\Serialize\Attributes\Output\OutputDateFormat; -use Astral\Serialize\Serialize; - -class AdvancedTimeUser extends Serialize { - // 支持时区转换 - #[InputDateFormat('Y-m-d H:i:s', timezone: 'UTC')] - #[OutputDateFormat('Y-m-d H:i:s', timezone: 'Asia/Shanghai')] - public DateTime $registeredAt; - - // 支持不同地区的时间格式 - #[InputDateFormat('d/m/Y')] // 英国格式 - #[OutputDateFormat('Y-m-d')] // 标准格式 - public DateTime $birthDate; -} - -// 使用高级时间转换 -$advancedUser = AdvancedTimeUser::from([ - 'registeredAt' => '2023-08-16 10:00:00', // UTC 时间 - 'birthDate' => '15/08/1990' // 英国日期格式 -]); - -$advancedUserArray = $advancedUser->toArray(); -// $advancedUserArray 的内容: -// [ -// 'registeredAt' => '2023-08-16 18:00:00', // 转换为上海时区 -// 'birthDate' => '1990-08-15' -// ] -``` - -## Faker - -### 简单属性模拟 - -```php -class UserFaker extends Serialize { - #[FakerValue('name')] - public string $name; - - #[FakerValue('email')] - public string $email; - - #[FakerValue('uuid')] - public string $userId; - - #[FakerValue('phoneNumber')] - public string $phone; - - #[FakerValue('age')] - public int $age; - - #[FakerValue('boolean')] - public bool $isActive; -} - -$user = UserFaker::faker(); - -$userArray = $user->toArray(); -// $userArray 的内容: -// [ -// "name" => "John Doe" -// "email" => "john.doe@example.com" -// "userId" => "550e8400-e29b-41d4-a716-446655440000" -// "phone" => "+1-555-123-4567" -// "age" => 35 -// "isActive" => true -// ] -``` - -### 集合模拟 - -```php - -class UserProfile extends Serialize { - public string $nickname; - public int $age; - public string $email; - public string $avatar; -} - -class UserListFaker extends Serialize { - #[FakerCollection(['name', 'email'], num: 3)] - public array $users; - - #[FakerCollection(UserProfile::class, num: 2)] - public array $profiles; -} - -$userList = UserListFaker::faker(); - -$complexUserListFaker = UserListFaker::faker(); - -$complexUserListFakerArray = $complexUserListFaker->toArray(); -// $complexUserListFakerArray 的内容: -// [ -// 'profile' => [ -// [0] => UserProfile Object ( -// [ -// 'nickname' => 'RandomNickname', -// 'age' => 28, 'email' => 'random.user@example.com', -// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' -// ], -// ), -// [1] => UserProfile Object ( -// [ -// 'nickname' => 'RandomNickname', -// 'age' => 28, 'email' => 'random.user@example.com', -// 'avatar' => 'https://example.com/avatars/random-avatar.jpg' -// ], -// ) -// ], -// 'users' => [ -// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] -// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] -// ['name' => 'RandomNickname', 'email' => 'RandomEmail@example.com'] -// ] -// ] -``` - -### 嵌套对象模拟 - -#### 基本用法 - -```php -class ComplexUserFaker extends Serialize { - #[FakerObject(UserProfile::class)] - public UserProfile $profile; -} -``` - -#### 演示实例 - -```php -use Astral\Serialize\Serialize; -use Astral\Serialize\Attributes\FakerObject; -use Astral\Serialize\Attributes\FakerCollection; - -class UserProfile extends Serialize { - public string $nickname; - public int $age; - public string $email; - public string $avatar; -} - -class UserTag extends Serialize { - public string $name; - public string $color; -} - -class ComplexUserFaker extends Serialize { - #[FakerObject(UserProfile::class)] - public UserProfile $profile; - - #[FakerObject(UserTag::class)] - public UserTag|UserProfile $primaryTag; - -} - -$complexUserFaker = ComplexUserFaker::faker(); - -$complexUserFakerArray = $complexUserFaker->toArray(); -// $complexUserFakerArray 的内容: -// [ -// 'profile' => UserProfile Object ( -// ['nickname' => 'RandomNickname', 'age' => 28, 'email' => 'random.user@example.com', 'avatar' => 'https://example.com/avatars/random-avatar.jpg'] -// ), -// 'primaryTag' => UserTag Object ( -// ['name' => 'Developer', 'color' => '#007bff'] -// ) -// ] -``` - -### Faker类方法模拟 - -```php -class UserService { - public function generateUserData(): array { - return ['name' => 'Generated User']; - } -} - -class UserFaker extends Serialize { - #[FakerMethod(UserService::class, 'generateUserData')] - public array $userData; -} -``` - -#### 完整的示例 - -```php -use Astral\Serialize\Serialize; -use Astral\Serialize\Attributes\Faker\FakerMethod; -use Astral\Serialize\Attributes\Faker\FakerObject; -use Astral\Serialize\Attributes\Faker\FakerCollection; - -// 用户配置文件类 -class UserProfile extends Serialize { - public string $nickname; - public int $age; - public string $email; - public array $types = ['type1' => 'money', 'type2' => 'score']; -} - -// 用户服务类,提供数据生成方法 -class UserService { - public function generateUserData(): array { - return [ - 'name' => 'Generated User', - 'email' => 'generated.user@example.com', - 'age' => 30 - ]; - } - - public function generateUserProfile(UserProfile $user): UserProfile { - return $user; - } - - public function generateUserList(int $count): array { - $users = []; - for ($i = 0; $i < $count; $i++) { - $users[] = [ - 'name' => "User {$i}", - 'email' => "user{$i}@example.com" - ]; - } - return $users; - } -} - -// Faker 方法模拟示例 -class UserFaker extends Serialize { - // 使用方法生成简单数据 - #[FakerMethod(UserService::class, 'generateUserData')] - public array $userData; - - // 使用方法生成对象 - #[FakerMethod(UserService::class, 'generateUserProfile')] - public UserProfile $userProfile; - - // 获取指定属性 - #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'age')] - public int $age; - - // 获取指定属性 多级可以使用[.]链接 - #[FakerMethod(UserService::class, 'generateUserProfile',returnType:'types.type2')] - public string $type2; - - // 传入参数 - #[FakerMethod(UserService::class, 'generateUserList',params:['count'=> 3])] - public array $userList; -} - -// 生成模拟数据 -$userFaker = UserFaker::faker(); - -// 转换为数组 -$userFakerArray = $userFaker->toArray(); -// $userFakerArray 的内容: -// [ -// 'userData' => [ -// 'name' => 'Generated User', -// 'email' => 'generated.user@example.com', -// 'age' => 30 -// ], -// 'userProfile' => UserProfile Object ( -// [ -// 'nickname' => 'GeneratedNickname', -// 'age' => 25, // 随机生成 -// 'email' => 'profile@example.com' -// 'types' => ['type1' => 'money', 'type2' => 'score'] -// ] -// ), -// 'age' => 99 , // 随机生成 -// 'type2' => 'score', -// 'userList' => [ -// ['name' => 'User 0', 'email' => 'user0@example.com'], -// ['name' => 'User 1', 'email' => 'user1@example.com'], -// ['name' => 'User 2', 'email' => 'user2@example.com'] -// ] -// ] -``` +- 🏷️ 属性别名映射 +- 🔄 自动类型转换(例如 `DateTime ↔ string`) +- 🔁 支持深度对象嵌套 +- ❌ 支持跳过/排除字段 +- 🧩 递归 DTO(数据传输对象)序列化 +- 🧬 **基于对象定义自动生成 OpenAPI schema** +- ⚙️ 与框架无关 — 兼容 Laravel、Symfony 等框架 \ No newline at end of file diff --git a/docs/zh/SUMMARY.md b/docs/zh/SUMMARY.md index 1adba5a..e7851c7 100644 --- a/docs/zh/SUMMARY.md +++ b/docs/zh/SUMMARY.md @@ -1,8 +1,6 @@ # Summary -## Use headings to create page groups like this one - -* [介绍](description.md) +* [介绍](README.md) * [快速开始](getting-started.md) ## 属性转换 From ca7e79fe1742c0f88f4af45a232db6826be8a914 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:14:31 +0800 Subject: [PATCH 35/59] gitbook zh success --- README.md | 2 +- docs/zh/SUMMARY.md | 9 ++- docs/zh/annotation/alisa-annotation.md | 8 +-- docs/zh/annotation/customer-annotation.md | 73 +++++++++++++++++++++++ docs/zh/annotation/date-annotation.md | 6 +- docs/zh/annotation/group-annotation.md | 6 +- docs/zh/annotation/ignore-annotation.md | 6 +- docs/zh/annotation/mapper-annotation.md | 62 +++++++++---------- docs/zh/faker/collection-faker.md | 2 +- docs/zh/faker/method-faker.md | 6 +- docs/zh/faker/nested-faker.md | 6 +- docs/zh/faker/value-faker.md | 2 +- docs/zh/mapper/array-mapper.md | 4 +- docs/zh/mapper/base-mapper.md | 10 ++-- docs/zh/mapper/enum-mapper.md | 6 +- docs/zh/mapper/null-mapper.md | 4 +- docs/zh/mapper/union-mapper.md | 10 +--- docs/zh/openapi/base-openapi.md | 51 ++++++++++++++++ 18 files changed, 201 insertions(+), 72 deletions(-) create mode 100644 docs/zh/annotation/customer-annotation.md create mode 100644 docs/zh/openapi/base-openapi.md diff --git a/README.md b/README.md index 20a1a89..73c0688 100644 --- a/README.md +++ b/README.md @@ -4,7 +4,7 @@ # Languages -- [Complete documen-English](./docs/en/README.md) +- [Complete documen-English](https://astrals-organization.gitbook.io/php-serialize/php-serialize-en) - [完整文档-中文](https://astrals-organization.gitbook.io/php-serialize) # php-serialize diff --git a/docs/zh/SUMMARY.md b/docs/zh/SUMMARY.md index e7851c7..7a4f9db 100644 --- a/docs/zh/SUMMARY.md +++ b/docs/zh/SUMMARY.md @@ -14,14 +14,19 @@ ## 注解类使用 * [属性分组](annotation/group-annotation.md) -* [输入/输入出名称映射](annotation/alisa-annotation.md) +* [输入/输出映射](annotation/alisa-annotation.md) * [Mapper映射](annotation/mapper-annotation.md) * [输入/输出忽略](annotation/ignore-annotation.md) * [时间格式映射](annotation/date-annotation.md) +* [自定义注解](annotation/customer-annotation.md) ## 参数快速Faker * [简单属性Faker](faker/value-faker.md) * [集合Faker](faker/collection-faker.md) * [嵌套Faker](faker/nested-faker.md) -* [方法Faker](faker/method-faker.md) \ No newline at end of file +* [方法Faker](faker/method-faker.md) + +## 自动创建OpenApi文档 + +* [基础演示](openapi/base-openapi.md) \ No newline at end of file diff --git a/docs/zh/annotation/alisa-annotation.md b/docs/zh/annotation/alisa-annotation.md index e665b18..ff61fb7 100644 --- a/docs/zh/annotation/alisa-annotation.md +++ b/docs/zh/annotation/alisa-annotation.md @@ -1,6 +1,6 @@ -#### 名称映射 +## 名称映射 -##### 基础使用 +### 基础使用 ```php use Astral\Serialize\Attributes\InputName; @@ -39,7 +39,7 @@ $userArray = $user->toArray(); // ] ``` -##### 多输入/输出名称处理 +### 多输入/输出名称处理 ```php use Astral\Serialize\Attributes\InputName; @@ -104,7 +104,7 @@ $userArray = $user->toArray(); // ] ``` -##### 复杂映射场景 +### 复杂映射场景 ```php use Astral\Serialize\Serialize; diff --git a/docs/zh/annotation/customer-annotation.md b/docs/zh/annotation/customer-annotation.md new file mode 100644 index 0000000..330c759 --- /dev/null +++ b/docs/zh/annotation/customer-annotation.md @@ -0,0 +1,73 @@ +### 自定义注解类实现 + +你可以通过自定义注解类,灵活地扩展序列化库的输入输出处理逻辑。 + +--- + +#### 入参处理注解类 + +实现 `InputValueCastInterface` 接口,重写其中的 `match` 和 `resolve` 方法,来自定义输入数据的转换和处理。 + +- **`match`**:用于判断是否对当前值进行处理,返回 `true` 表示进入 `resolve`。 +- **`resolve`**:对匹配的输入值进行转换,并返回转换后的结果。 + +示例:给输入值添加自定义前缀的注解类 + +```php +use Astral\Serialize\Contracts\Attribute\InputValueCastInterface; +use Attribute; + +#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS)] +class CustomerInput implements InputValueCastInterface +{ + public function __construct( + public string $prefix = '', + ) { + } + + public function match(mixed $value, DataCollection $collection, InputValueContext $context): bool + { + // 对所有输入值都生效 + return true; + } + + public function resolve(mixed $value, DataCollection $collection, InputValueContext $context): mixed + { + // 给输入值添加前缀 + return $this->prefix . $value; + } +} +```` + +### 输出处理注解类 + +输出处理注解与输入处理注解类似,只是实现的接口不同——需要实现 `OutputValueCastInterface`,用以对序列化输出的值进行自定义转换。 + +示例:给序列化输出的值添加自定义后缀的注解类 + +```php +use Astral\Serialize\Contracts\Attribute\OutputValueCastInterface; +use Attribute; + +#[Attribute(Attribute::TARGET_PROPERTY | Attribute::TARGET_CLASS)] +class CustomerOutput implements OutputValueCastInterface +{ + public function __construct( + public string $suffix = '', + ) { + } + + public function match(mixed $value, DataCollection $collection, OutputValueContext $context): bool + { + // 对所有输出值都生效 + return true; + } + + public function resolve(mixed $value, DataCollection $collection, OutputValueContext $context): mixed + { + // 给输出值添加后缀 + return $value . $this->suffix; + } +} +``` + diff --git a/docs/zh/annotation/date-annotation.md b/docs/zh/annotation/date-annotation.md index a8de4f1..1d37142 100644 --- a/docs/zh/annotation/date-annotation.md +++ b/docs/zh/annotation/date-annotation.md @@ -1,4 +1,4 @@ -#### 时间转换 +## 时间转换 1. 格式灵活性 支持多种输入和输出时间格式 @@ -10,7 +10,7 @@ 自动将字符串转换为 DateTime 对象 保证类型的一致性和正确性 -##### 基础使用 +### 基础使用 ```php use Astral\Serialize\Attributes\Input\InputDateFormat; @@ -57,7 +57,7 @@ $orderArray = $order->toArray(); // ] ``` -##### 带时区的时间转换 +### 带时区的时间转换 ```php diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md index c6b48ac..3cfd040 100644 --- a/docs/zh/annotation/group-annotation.md +++ b/docs/zh/annotation/group-annotation.md @@ -1,8 +1,8 @@ -#### 属性分组 +## 属性分组 属性分组提供了一种灵活的方式来控制属性的输入和输出行为,允许在不同场景下精细地管理数据转换。 -##### 基本用法 +### 基本用法 在属性上使用 `#[Groups]` 注解来指定属性所属的分组。 @@ -148,7 +148,7 @@ $multiGroupArray = $user4->toArray(); // ] ``` -##### 嵌套类指定Group类展示 +### 嵌套类指定Group类展示 ```php class ComplexUser extends Serialize { diff --git a/docs/zh/annotation/ignore-annotation.md b/docs/zh/annotation/ignore-annotation.md index 0cd5eb2..7bfec48 100644 --- a/docs/zh/annotation/ignore-annotation.md +++ b/docs/zh/annotation/ignore-annotation.md @@ -1,4 +1,4 @@ -#### 字段忽略 +## 字段忽略 1. **安全性控制** - 防止敏感信息的意外泄露 @@ -12,7 +12,7 @@ - 减少不必要字段的序列化开销 - 精简数据传输 -##### 基础使用 +### 基础使用 ```php use Astral\Serialize\Attributes\InputIgnore; @@ -51,7 +51,7 @@ $userArray = $user->toArray(); // ] ``` -##### 分组忽略 +### 分组忽略 忽略分组需要搭配Groups注解一起使用 diff --git a/docs/zh/annotation/mapper-annotation.md b/docs/zh/annotation/mapper-annotation.md index e2827db..0d6448d 100644 --- a/docs/zh/annotation/mapper-annotation.md +++ b/docs/zh/annotation/mapper-annotation.md @@ -1,4 +1,6 @@ -##### Mapper映射 +### Mapper映射 + +### 属性映射 ```php use Astral\Serialize\Attributes\InputName; @@ -46,7 +48,7 @@ $userArray = $user->toArray(); // ] ``` -##### 全局类映射 +### 全局类映射 ```php use Astral\Serialize\Attributes\InputName; @@ -88,33 +90,7 @@ $userArray = $user->toArray(); // ] ``` -###### 属性映射大于类级映射 - -```php - -#[InputName(SnakeCaseMapper::class)] -class PartialOverrideUser extends Serialize { - #[InputName(PascalCaseMapper::class)] - public string $userName; // 优先使用帕斯卡命名映射 - - public string $userEmail; // 继续使用类级别的全局映射 -} - -$partialUser = PartialOverrideUser::from([ - 'User_name' => '张三', // 使用蛇形映射 - 'UserName' => '李四', // 使用帕斯卡映射 - 'user_email' => 'user@example.com' // 使用蛇形映射 -]); - -$partialUser->toArray(); -// $partialUser 的内容: -// [ -// 'userName' => '李四', -// 'userEmail' => 'user@example.com', -// ] -``` - -###### 全局类映射的分组使用 +### 全局类映射的分组使用 需要搭配`Groups`注解一起使用 @@ -180,7 +156,7 @@ $complexUser = $complexUser->toArray(); // 'userEmail' => 张三, // ] ``` -#### 自定义映射器 +### 自定义映射器 ```php // 自定义映射器 需要继承NameMapper 并实现 resolve @@ -195,4 +171,30 @@ class AdvancedUser extends Serialize { #[InputName(CustomMapper::class)] public string $name; } +``` + +### Tips:属性映射优先于类级映射 + +```php + +#[InputName(SnakeCaseMapper::class)] +class PartialOverrideUser extends Serialize { + #[InputName(PascalCaseMapper::class)] + public string $userName; // 优先使用帕斯卡命名映射 + + public string $userEmail; // 继续使用类级别的全局映射 +} + +$partialUser = PartialOverrideUser::from([ + 'User_name' => '张三', // 使用蛇形映射 + 'UserName' => '李四', // 使用帕斯卡映射 + 'user_email' => 'user@example.com' // 使用蛇形映射 +]); + +$partialUser->toArray(); +// $partialUser 的内容: +// [ +// 'userName' => '李四', +// 'userEmail' => 'user@example.com', +// ] ``` \ No newline at end of file diff --git a/docs/zh/faker/collection-faker.md b/docs/zh/faker/collection-faker.md index ec68dfe..a7b2126 100644 --- a/docs/zh/faker/collection-faker.md +++ b/docs/zh/faker/collection-faker.md @@ -1,4 +1,4 @@ -### 集合模拟 +## 集合模拟 ```php diff --git a/docs/zh/faker/method-faker.md b/docs/zh/faker/method-faker.md index cdd8020..5d7de3e 100644 --- a/docs/zh/faker/method-faker.md +++ b/docs/zh/faker/method-faker.md @@ -1,4 +1,6 @@ -### Faker类方法模拟 +## Faker类方法模拟 + +### 基本用法 ```php class UserService { @@ -13,7 +15,7 @@ class UserFaker extends Serialize { } ``` -#### 完整的示例 +### 完整的示例 ```php use Astral\Serialize\Serialize; diff --git a/docs/zh/faker/nested-faker.md b/docs/zh/faker/nested-faker.md index 216abd2..b479a52 100644 --- a/docs/zh/faker/nested-faker.md +++ b/docs/zh/faker/nested-faker.md @@ -1,6 +1,6 @@ -### 嵌套对象模拟 +## 嵌套对象模拟 -#### 基本用法 +### 基本用法 ```php class ComplexUserFaker extends Serialize { @@ -9,7 +9,7 @@ class ComplexUserFaker extends Serialize { } ``` -#### 演示实例 +### 演示实例 ```php use Astral\Serialize\Serialize; diff --git a/docs/zh/faker/value-faker.md b/docs/zh/faker/value-faker.md index 2b79213..8645904 100644 --- a/docs/zh/faker/value-faker.md +++ b/docs/zh/faker/value-faker.md @@ -1,4 +1,4 @@ -### 简单属性模拟 +## 简单属性模拟 ```php class UserFaker extends Serialize { diff --git a/docs/zh/mapper/array-mapper.md b/docs/zh/mapper/array-mapper.md index 539f2fb..17434cc 100644 --- a/docs/zh/mapper/array-mapper.md +++ b/docs/zh/mapper/array-mapper.md @@ -1,6 +1,6 @@ -#### 数组对象转换 +## 数组对象转换 -##### phpDoc定义 +### phpDoc定义 ```php use Astral\Serialize\Serialize; diff --git a/docs/zh/mapper/base-mapper.md b/docs/zh/mapper/base-mapper.md index 9fca977..4dfc598 100644 --- a/docs/zh/mapper/base-mapper.md +++ b/docs/zh/mapper/base-mapper.md @@ -1,8 +1,8 @@ -### 类型转换 +## 类型转换 -#### 基本类型转换 +### 基本类型转换 -##### 方式一:构造函数属性提升 +#### 方式一:构造函数属性提升 ```php use Astral\Serialize\Serialize; @@ -17,7 +17,7 @@ class Profile extends Serialize { } ``` -##### 方式二:传统属性定义 +#### 方式二:传统属性定义 ```php use Astral\Serialize\Serialize; @@ -41,7 +41,7 @@ $profile = Profile::from([ $profileArray = $profile->toArray(); ``` -##### 方式三:只读属性 +#### 方式三:只读属性 ```php use Astral\Serialize\Serialize; diff --git a/docs/zh/mapper/enum-mapper.md b/docs/zh/mapper/enum-mapper.md index a918fef..f2b2b2b 100644 --- a/docs/zh/mapper/enum-mapper.md +++ b/docs/zh/mapper/enum-mapper.md @@ -1,4 +1,4 @@ -#### 枚举转换 +## 枚举转换 枚举转换提供了强大且灵活的枚举处理机制,支持多种枚举类型和转换场景。 @@ -7,7 +7,7 @@ - 输出时自动将枚举转换为字符串(枚举名称) - 提供灵活且安全的枚举处理机制 -##### 普通枚举 +### 普通枚举 ```php enum UserRole { @@ -39,7 +39,7 @@ $complexUserArray = $complexUser->toArray(); // ] ``` -##### 回退枚举 +### 回退枚举 ```php use Astral\Serialize\Serialize; diff --git a/docs/zh/mapper/null-mapper.md b/docs/zh/mapper/null-mapper.md index 2f11deb..728e5c9 100644 --- a/docs/zh/mapper/null-mapper.md +++ b/docs/zh/mapper/null-mapper.md @@ -1,4 +1,4 @@ -#### Null值转换规则详细示例 +## Null值转换规则详细示例 当属性不是可空类型(`?type`)时,`null` 值会根据目标类型自动转换: @@ -39,7 +39,7 @@ try { } ``` -#### 可空类型的方案 +## 可空类型的方案 对于需要接受 `null` 的场景,使用可空类型: diff --git a/docs/zh/mapper/union-mapper.md b/docs/zh/mapper/union-mapper.md index 48ea6ac..08d838e 100644 --- a/docs/zh/mapper/union-mapper.md +++ b/docs/zh/mapper/union-mapper.md @@ -1,12 +1,8 @@ -#### 联合类型 +## 联合类型 1. 可以混合使用基本类型和对象类型 -2. 对象层级匹配 - 对于多个对象类型,会选择最匹配的类型 - 支持继承层级的智能匹配 -3. 动态类型处理 - 自动处理不同类型的输入 - 提供更加灵活的数据建模方式 +2. 对象层级匹配。对于多个对象类型,会选择最匹配的类型。支持继承层级的智能匹配 +3. 动态类型处理,自动处理不同类型的,输入提供更加灵活的数据建模方式 ```php use Astral\Serialize\Serialize; diff --git a/docs/zh/openapi/base-openapi.md b/docs/zh/openapi/base-openapi.md new file mode 100644 index 0000000..d4b4c4e --- /dev/null +++ b/docs/zh/openapi/base-openapi.md @@ -0,0 +1,51 @@ +## 创建Request + +```php +use Astral\Serialize\Serialize; + +class UserAddRequest extends Serialize { + public string $name; + public int $id; +} + +class UserDetailRequest extends Serialize { + public int $id; +} +``` + +## 创建Repose +```php +use Astral\Serialize\Serialize; + +class UserDto extends Serialize { + public string $name, + public int $id; +} +``` + +## 创建Controller +```php +use Astral\Serialize\Serialize; +use Astral\Serialize\OpenApi\Enum\MethodEnum; + +#[\Astral\Serialize\OpenApi\Annotations\Tag('用户模块管理')] +class UserController { + + #[\Astral\Serialize\OpenApi\Annotations\Summary('创建用户')] + #[\Astral\Serialize\OpenApi\Annotations\Route('/user/create')] + #[\Astral\Serialize\OpenApi\Annotations\RequestBody(UserAddRequest::class)] + #[\Astral\Serialize\OpenApi\Annotations\Response(UserDto::class)] + public function create() + { + return new UserDto(); + } + + #[\Astral\Serialize\OpenApi\Annotations\Summary('用户详情')] + #[\Astral\Serialize\OpenApi\Annotations\Route(route:'/user/detail', method: MethodEnum::GET)] + public function detail(UserDetailRequest $request): UserDto + { + return new UserDto(); + } +} +``` +## 启动服务 \ No newline at end of file From cab96856863a4126635aec960800c5d6681e9988 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:15:44 +0800 Subject: [PATCH 36/59] gitbook zh success --- docs/zh/SUMMARY.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/SUMMARY.md b/docs/zh/SUMMARY.md index 7a4f9db..45772e4 100644 --- a/docs/zh/SUMMARY.md +++ b/docs/zh/SUMMARY.md @@ -20,7 +20,7 @@ * [时间格式映射](annotation/date-annotation.md) * [自定义注解](annotation/customer-annotation.md) -## 参数快速Faker +## 参数快速模拟生成 * [简单属性Faker](faker/value-faker.md) * [集合Faker](faker/collection-faker.md) From 3220fe41ac6a5a44343e6b6e04bbee2244e72767 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:27:22 +0800 Subject: [PATCH 37/59] gitbook zh success --- docs/zh/annotation/group-annotation.md | 255 +++++++++++-------------- 1 file changed, 113 insertions(+), 142 deletions(-) diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md index 3cfd040..a4c5543 100644 --- a/docs/zh/annotation/group-annotation.md +++ b/docs/zh/annotation/group-annotation.md @@ -1,10 +1,20 @@ -## 属性分组 +## 属性分组(Groups) 属性分组提供了一种灵活的方式来控制属性的输入和输出行为,允许在不同场景下精细地管理数据转换。 -### 基本用法 +--- -在属性上使用 `#[Groups]` 注解来指定属性所属的分组。 +### 🧠 分组原理说明 + +- 使用 `#[Groups(...)]` 注解可将属性归类到一个或多个分组中。 +- 支持: + - **输入时** 按分组过滤数据字段 + - **输出时** 按分组筛选输出字段 +- 未指定分组的属性将自动归入 `"default"` 分组。 + +--- + +### ✨ 基本示例 ```php use Astral\Serialize\Attributes\Groups; @@ -12,180 +22,141 @@ use Astral\Serialize\Serialize; class User extends Serialize { - #[Groups('update','detail')] + #[Groups('update', 'detail')] public string $id; #[Groups('create', 'update', 'detail')] public string $name; - #[Groups('create','detail')] + #[Groups('create', 'detail')] public string $username; #[Groups('other')] public string $sensitiveData; - // 没有指定Group 的属性将会被默认分组在default分组中 + // 未指定分组,默认为 default 分组 public string $noGroupInfo; - // 构造函数参数也支持分组 public function __construct( - #[Groups('create','detail')] + #[Groups('create', 'detail')] public readonly string $email, - - #[Groups('update','detail')] + + #[Groups('update', 'detail')] public readonly int $score ) {} } +``` +### 按分组接收 +```php +// 使用 create 分组创建用户,只接受 group=create 的字段 +$user = User::setGroups(['create'])->from([ + 'id' => 1, + 'name' => '李四', + 'score' => 100, + 'username' => 'username', + 'email' => 'zhangsan@example.com', + 'sensitiveData' => '机密信息', + 'noGroupInfo' => '默认信息' +]); + +$user->toArray(); +/* +[ + 'name' => '李四', + 'username' => 'username', + 'email' => 'zhangsan@example.com', +] +*/ +``` + +### 按分组输出 -// 使用 默认分组展示所有信息 -$user1 = User::from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用默认分组 toArray,展示所有信息 -$defaultArray = $user1->toArray(); -// $defaultArray 的内容: -// [ -// 'id' => '1', -// 'name' => '李四', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// 'noGroupInfo' => '默认分组信息' -// ] - -// 指定分组内容输入 -$defaultArray = $user1->withGroups('create')->toArray(); -// 输出内容 -// [ -// 'name' => '李四', -// 'username' => 'username', -// 'email' => 'zhangsan@example.com', -// ] - -$defaultArray = $user1->withGroups(['detail','other'])->toArray(); -// 输出内容 -// [ -// 'id' => '1', -// 'name' => '李四', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// ] - - -// 使用 create 分组创建用户 只会接受group为create的数据信息 -$user2 = User::setGroups(['create'])->from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用 create 分组 toArray -$createArray = $user2->toArray(); -// $createArray 的内容: -// [ -// 'name' => '李四', -// 'username' => 'username', -// 'email' => 'zhangsan@example.com', -// ] - -// 使用 update 分组更新用户 只会接受group为update的数据信息 -$user3 = User::setGroups(['update'])->from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用 update 分组 toArray -$updateArray = $user3->toArray(); -// $updateArray 的内容: -// [ -// 'id' => '1', -// 'name' => '李四', -// 'score' => 100, -// ] - -// 使用 detail 和 other 展示用户 会接受group为detail和other的数据信息 -$user4 = User::setGroups(['detail','other'])->from( - id:1, - name: '李四', - score: 100, - username: 'username', - email: 'zhangsan@example.com', - sensitiveData:'机密信息', - noGroupInfo:'默认分组信息' -); - -// 使用多个分组 toArray -$multiGroupArray = $user4->toArray(); -// $multiGroupArray 的内容: -// [ -// 'id' => '1', -// 'name' => '李四', -// 'username' => 'username', -// 'score' => 100, -// 'email' => 'zhangsan@example.com', -// 'sensitiveData' => '机密信息', -// ] +```php +$user = User::from([ + 'id' => 1, + 'name' => '李四', + 'score' => 100, + 'username' => 'username', + 'email' => 'zhangsan@example.com', + 'sensitiveData' => '机密信息', + 'noGroupInfo' => '默认信息' +]); + +// 默认输出所有字段 +$user->toArray(); +/* +[ + 'id' => '1', + 'name' => '李四', + 'username' => 'username', + 'score' => 100, + 'email' => 'zhangsan@example.com', + 'sensitiveData' => '机密信息', + 'noGroupInfo' => '默认信息' +] +*/ + +// 指定输出分组 +$user->withGroups('create')->toArray(); +/* +[ + 'name' => '李四', + 'username' => 'username', + 'email' => 'zhangsan@example.com', +] +*/ + +$user->withGroups(['detail', 'other'])->toArray(); +/* +[ + 'id' => '1', + 'name' => '李四', + 'username' => 'username', + 'score' => 100, + 'email' => 'zhangsan@example.com', + 'sensitiveData' => '机密信息', +] +*/ ``` -### 嵌套类指定Group类展示 +### 嵌套对象的分组 ```php class ComplexUser extends Serialize { - public string $name; - public int $sex; - public ComplexNestedInfo $info; } class ComplexNestedInfo extends Serialize { - - #[Groups(ComplexAUser::class)] + #[Groups(ComplexUser::class)] public float $money; public string $currency; } - -// ComplexNestedInfo 会自动隐藏currency -$adminUser = ComplexUser::from( - name: '张三', - sex: 1, - info: [ +php +复制 +编辑 +$adminUser = ComplexUser::from([ + 'name' => '张三', + 'sex' => 1, + 'info' => [ 'money' => 100.00, 'currency' => 'CNY' - ]; -); - -// 输出数据 -$adminUserArray = $adminUser->toArray(); -// $adminUserArray 的内容: -// [ -// 'name' => '张三', -// 'sex' => 1, -// 'info' => ComplexNestedInfo Object ([ -// 'money' => 100.00 -// ]) -// ] + ], +]); + +// 默认输出包含所有字段 +$adminUser->toArray(); +/* +[ + 'name' => '张三', + 'sex' => 1, + 'info' => [ + 'money' => 100.00 + ] +] +*/ ``` \ No newline at end of file From 171df3cb1d08c15d76efa47f92655221fc55c285 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:28:11 +0800 Subject: [PATCH 38/59] gitbook zh success --- docs/zh/annotation/group-annotation.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md index a4c5543..6e6ddfd 100644 --- a/docs/zh/annotation/group-annotation.md +++ b/docs/zh/annotation/group-annotation.md @@ -47,7 +47,7 @@ class User extends Serialize { } ``` -### 按分组接收 +### ✨按分组接收 ```php // 使用 create 分组创建用户,只接受 group=create 的字段 @@ -71,7 +71,7 @@ $user->toArray(); */ ``` -### 按分组输出 +### ✨按分组输出 ```php $user = User::from([ @@ -121,7 +121,7 @@ $user->withGroups(['detail', 'other'])->toArray(); */ ``` -### 嵌套对象的分组 +### ✨嵌套对象的分组 ```php class ComplexUser extends Serialize { From 4c4d50bf91b66c4a930272053c2fb1dd8f3a7484 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:28:42 +0800 Subject: [PATCH 39/59] gitbook zh success --- docs/zh/annotation/group-annotation.md | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md index 6e6ddfd..99ee1fb 100644 --- a/docs/zh/annotation/group-annotation.md +++ b/docs/zh/annotation/group-annotation.md @@ -136,9 +136,7 @@ class ComplexNestedInfo extends Serialize { public string $currency; } -php -复制 -编辑 + $adminUser = ComplexUser::from([ 'name' => '张三', 'sex' => 1, From 7d74bbcb0186e4d2e20eaae9cd37ff740881cd26 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:31:49 +0800 Subject: [PATCH 40/59] gitbook zh success --- docs/zh/annotation/mapper-annotation.md | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/docs/zh/annotation/mapper-annotation.md b/docs/zh/annotation/mapper-annotation.md index 0d6448d..61257d7 100644 --- a/docs/zh/annotation/mapper-annotation.md +++ b/docs/zh/annotation/mapper-annotation.md @@ -1,4 +1,4 @@ -### Mapper映射 +## Mapper映射 ### 属性映射 @@ -106,7 +106,6 @@ use Astral\Serialize\Support\Mappers\{ }; use Astral\Serialize\Serialize; - #[InputName(SnakeCaseMapper::class, groups: 'external')] #[InputName(CamelCaseMapper::class, groups: 'api')] #[OutputName(PascalCaseMapper::class, groups: ['external','api'])] From a3722bb59d03016d5d1da223dbdaadb373f1c5bf Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:32:18 +0800 Subject: [PATCH 41/59] gitbook zh success --- docs/zh/annotation/group-annotation.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md index 99ee1fb..9be3d23 100644 --- a/docs/zh/annotation/group-annotation.md +++ b/docs/zh/annotation/group-annotation.md @@ -4,7 +4,7 @@ --- -### 🧠 分组原理说明 +### 分组原理说明 - 使用 `#[Groups(...)]` 注解可将属性归类到一个或多个分组中。 - 支持: @@ -14,7 +14,7 @@ --- -### ✨ 基本示例 +### 基本示例 ```php use Astral\Serialize\Attributes\Groups; @@ -47,7 +47,7 @@ class User extends Serialize { } ``` -### ✨按分组接收 +### 按分组接收 ```php // 使用 create 分组创建用户,只接受 group=create 的字段 @@ -71,7 +71,7 @@ $user->toArray(); */ ``` -### ✨按分组输出 +### 按分组输出 ```php $user = User::from([ @@ -121,7 +121,7 @@ $user->withGroups(['detail', 'other'])->toArray(); */ ``` -### ✨嵌套对象的分组 +### 嵌套对象的分组 ```php class ComplexUser extends Serialize { From 8fc847527ceb3ce316920e5221fa75cee8d490b7 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:38:27 +0800 Subject: [PATCH 42/59] gitbook zh success --- docs/zh/annotation/group-annotation.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md index 9be3d23..8ef2be5 100644 --- a/docs/zh/annotation/group-annotation.md +++ b/docs/zh/annotation/group-annotation.md @@ -146,7 +146,8 @@ $adminUser = ComplexUser::from([ ], ]); -// 默认输出包含所有字段 +// info只会输出$money +// 因为ComplexNestedInfo 绑定了 ComplexUser的类Group $adminUser->toArray(); /* [ From 771941534b6212af5a9853ea45e87094bfddd40c Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Fri, 20 Jun 2025 17:38:50 +0800 Subject: [PATCH 43/59] gitbook zh success --- docs/zh/annotation/group-annotation.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/zh/annotation/group-annotation.md b/docs/zh/annotation/group-annotation.md index 8ef2be5..e38b948 100644 --- a/docs/zh/annotation/group-annotation.md +++ b/docs/zh/annotation/group-annotation.md @@ -147,7 +147,7 @@ $adminUser = ComplexUser::from([ ]); // info只会输出$money -// 因为ComplexNestedInfo 绑定了 ComplexUser的类Group +// 因为 ComplexNestedInfo 绑定了 ComplexUser的类Group $adminUser->toArray(); /* [ From 6b2e1654af69eb242a1166a5d53b6604b695962b Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Wed, 25 Jun 2025 09:06:47 +0800 Subject: [PATCH 44/59] change composer.json --- composer.json | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/composer.json b/composer.json index 54983e1..0586fef 100755 --- a/composer.json +++ b/composer.json @@ -21,11 +21,10 @@ "illuminate/support": "^6.0||^7.0||^8.0||^9.0||^10.0||^11.0||^12.0", "phpdocumentor/reflection-docblock": "^5.5", "phpdocumentor/reflection": "^6.0", - "fakerphp/faker": "^1.23", - "psr/simple-cache": "^3.0", - "symfony/console": "^6.4" + "psr/simple-cache": "^3.0" }, "require-dev" : { + "fakerphp/faker": "^1.23", "phpstan/phpstan": "^2.0.2", "friendsofphp/php-cs-fixer": "^3.0", "mockery/mockery": "^1.6", From ae652ba97545d083d310124d4a7765201d0b0c93 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Wed, 25 Jun 2025 09:08:32 +0800 Subject: [PATCH 45/59] change composer.json --- composer.lock | 3832 ++++++++++++++++++++++++------------------------- 1 file changed, 1916 insertions(+), 1916 deletions(-) diff --git a/composer.lock b/composer.lock index 80ba5cc..01ef396 100644 --- a/composer.lock +++ b/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "6a9a46d6b2d0f3759c5c4de4bcc10d39", + "content-hash": "b6a3c6ce8a91e62af249f4e71f17064b", "packages": [ { "name": "carbonphp/carbon-doctrine-types", @@ -214,69 +214,6 @@ ], "time": "2024-02-18T20:23:39+00:00" }, - { - "name": "fakerphp/faker", - "version": "v1.24.1", - "source": { - "type": "git", - "url": "https://github.com/FakerPHP/Faker.git", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", - "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", - "shasum": "" - }, - "require": { - "php": "^7.4 || ^8.0", - "psr/container": "^1.0 || ^2.0", - "symfony/deprecation-contracts": "^2.2 || ^3.0" - }, - "conflict": { - "fzaninotto/faker": "*" - }, - "require-dev": { - "bamarni/composer-bin-plugin": "^1.4.1", - "doctrine/persistence": "^1.3 || ^2.0", - "ext-intl": "*", - "phpunit/phpunit": "^9.5.26", - "symfony/phpunit-bridge": "^5.4.16" - }, - "suggest": { - "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", - "ext-curl": "Required by Faker\\Provider\\Image to download images.", - "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", - "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", - "ext-mbstring": "Required for multibyte Unicode string functionality." - }, - "type": "library", - "autoload": { - "psr-4": { - "Faker\\": "src/Faker/" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "François Zaninotto" - } - ], - "description": "Faker is a PHP library that generates fake data for you.", - "keywords": [ - "data", - "faker", - "fixtures" - ], - "support": { - "issues": "https://github.com/FakerPHP/Faker/issues", - "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" - }, - "time": "2024-11-21T13:46:39+00:00" - }, { "name": "illuminate/collections", "version": "v10.48.28", @@ -1154,100 +1091,6 @@ }, "time": "2021-10-29T13:26:27+00:00" }, - { - "name": "symfony/console", - "version": "v6.4.22", - "source": { - "type": "git", - "url": "https://github.com/symfony/console.git", - "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", - "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", - "shasum": "" - }, - "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/string": "^5.4|^6.0|^7.0" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/dotenv": "<5.4", - "symfony/event-dispatcher": "<5.4", - "symfony/lock": "<5.4", - "symfony/process": "<5.4" - }, - "provide": { - "psr/log-implementation": "1.0|2.0|3.0" - }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/event-dispatcher": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^6.4|^7.0", - "symfony/http-kernel": "^6.4|^7.0", - "symfony/lock": "^5.4|^6.0|^7.0", - "symfony/messenger": "^5.4|^6.0|^7.0", - "symfony/process": "^5.4|^6.0|^7.0", - "symfony/stopwatch": "^5.4|^6.0|^7.0", - "symfony/var-dumper": "^5.4|^6.0|^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/v6.4.22" - }, - "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-05-07T07:05:04+00:00" - }, { "name": "symfony/deprecation-contracts", "version": "v3.6.0", @@ -1316,27 +1159,28 @@ "time": "2024-09-25T14:21:43+00:00" }, { - "name": "symfony/polyfill-ctype", + "name": "symfony/polyfill-mbstring", "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-ctype.git", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" + "url": "https://github.com/symfony/polyfill-mbstring.git", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", - "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", + "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", + "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", "shasum": "" }, "require": { + "ext-iconv": "*", "php": ">=7.2" }, "provide": { - "ext-ctype": "*" + "ext-mbstring": "*" }, "suggest": { - "ext-ctype": "For best performance" + "ext-mbstring": "For best performance" }, "type": "library", "extra": { @@ -1350,7 +1194,7 @@ "bootstrap.php" ], "psr-4": { - "Symfony\\Polyfill\\Ctype\\": "" + "Symfony\\Polyfill\\Mbstring\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -1359,24 +1203,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.32.0" + "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" }, "funding": [ { @@ -1392,28 +1237,25 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2024-12-23T08:48:59+00:00" }, { - "name": "symfony/polyfill-intl-grapheme", + "name": "symfony/polyfill-php80", "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-grapheme.git", - "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" + "url": "https://github.com/symfony/polyfill-php80.git", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" }, "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-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", "shasum": "" }, "require": { "php": ">=7.2" }, - "suggest": { - "ext-intl": "For best performance" - }, "type": "library", "extra": { "thanks": { @@ -1426,14 +1268,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" @@ -1443,18 +1292,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.32.0" + "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" }, "funding": [ { @@ -1470,44 +1317,66 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-01-02T08:10:11+00:00" }, { - "name": "symfony/polyfill-intl-normalizer", - "version": "v1.32.0", + "name": "symfony/translation", + "version": "v6.4.22", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-intl-normalizer.git", - "reference": "3833d7255cc303546435cb650316bff708a1c75c" + "url": "https://github.com/symfony/translation.git", + "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", - "reference": "3833d7255cc303546435cb650316bff708a1c75c", + "url": "https://api.github.com/repos/symfony/translation/zipball/7e3b3b7146c6fab36ddff304a8041174bf6e17ad", + "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad", "shasum": "" }, "require": { - "php": ">=7.2" - }, - "suggest": { - "ext-intl": "For best performance" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/translation-contracts": "^2.5|^3.0" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "conflict": { + "symfony/config": "<5.4", + "symfony/console": "<5.4", + "symfony/dependency-injection": "<5.4", + "symfony/http-client-contracts": "<2.5", + "symfony/http-kernel": "<5.4", + "symfony/service-contracts": "<2.5", + "symfony/twig-bundle": "<5.4", + "symfony/yaml": "<5.4" }, + "provide": { + "symfony/translation-implementation": "2.3|3.0" + }, + "require-dev": { + "nikic/php-parser": "^4.18|^5.0", + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/console": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/finder": "^5.4|^6.0|^7.0", + "symfony/http-client-contracts": "^2.5|^3.0", + "symfony/http-kernel": "^5.4|^6.0|^7.0", + "symfony/intl": "^5.4|^6.0|^7.0", + "symfony/polyfill-intl-icu": "^1.21", + "symfony/routing": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/yaml": "^5.4|^6.0|^7.0" + }, + "type": "library", "autoload": { "files": [ - "bootstrap.php" + "Resources/functions.php" ], "psr-4": { - "Symfony\\Polyfill\\Intl\\Normalizer\\": "" + "Symfony\\Component\\Translation\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Tests/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -1516,26 +1385,18 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" }, { "name": "Symfony Community", "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for intl's Normalizer class and related functions", + "description": "Provides tools to internationalize your application", "homepage": "https://symfony.com", - "keywords": [ - "compatibility", - "intl", - "normalizer", - "polyfill", - "portable", - "shim" - ], "support": { - "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" + "source": "https://github.com/symfony/translation/tree/v6.4.22" }, "funding": [ { @@ -1551,46 +1412,42 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-05-29T07:06:44+00:00" }, { - "name": "symfony/polyfill-mbstring", - "version": "v1.32.0", + "name": "symfony/translation-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-mbstring.git", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493" + "url": "https://github.com/symfony/translation-contracts.git", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-mbstring/zipball/6d857f4d76bd4b343eac26d6b539585d2bc56493", - "reference": "6d857f4d76bd4b343eac26d6b539585d2bc56493", + "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", "shasum": "" }, "require": { - "ext-iconv": "*", - "php": ">=7.2" - }, - "provide": { - "ext-mbstring": "*" - }, - "suggest": { - "ext-mbstring": "For best performance" + "php": ">=8.1" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Mbstring\\": "" - } + "Symfony\\Contracts\\Translation\\": "" + }, + "exclude-from-classmap": [ + "/Test/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1606,17 +1463,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill for the Mbstring extension", + "description": "Generic abstractions related to translation", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "mbstring", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/polyfill-mbstring/tree/v1.32.0" + "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" }, "funding": [ { @@ -1632,42 +1490,36 @@ "type": "tidelift" } ], - "time": "2024-12-23T08:48:59+00:00" + "time": "2024-09-27T08:32:26+00:00" }, { - "name": "symfony/polyfill-php80", - "version": "v1.32.0", + "name": "voku/portable-ascii", + "version": "2.0.3", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php80.git", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608" + "url": "https://github.com/voku/portable-ascii.git", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php80/zipball/0cc9dd0f17f61d8131e7df6b84bd344899fe2608", - "reference": "0cc9dd0f17f61d8131e7df6b84bd344899fe2608", + "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", "shasum": "" }, "require": { - "php": ">=7.2" + "php": ">=7.0.0" }, - "type": "library", - "extra": { - "thanks": { - "url": "https://github.com/symfony/polyfill", - "name": "symfony/polyfill" - } + "require-dev": { + "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" + }, + "suggest": { + "ext-intl": "Use Intl for transliterator_transliterate() support" }, + "type": "library", "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php80\\": "" - }, - "classmap": [ - "Resources/stubs" - ] + "voku\\": "src/voku/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1675,84 +1527,80 @@ ], "authors": [ { - "name": "Ion Bazan", - "email": "ion.bazan@gmail.com" - }, - { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Lars Moelleken", + "homepage": "https://www.moelleken.org/" } ], - "description": "Symfony polyfill backporting some PHP 8.0+ features to lower PHP versions", - "homepage": "https://symfony.com", + "description": "Portable ASCII library - performance optimized (ascii) string functions for php.", + "homepage": "https://github.com/voku/portable-ascii", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "ascii", + "clean", + "php" ], "support": { - "source": "https://github.com/symfony/polyfill-php80/tree/v1.32.0" + "issues": "https://github.com/voku/portable-ascii/issues", + "source": "https://github.com/voku/portable-ascii/tree/2.0.3" }, "funding": [ { - "url": "https://symfony.com/sponsor", + "url": "https://www.paypal.me/moelleken", "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/voku", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://opencollective.com/portable-ascii", + "type": "open_collective" + }, + { + "url": "https://www.patreon.com/voku", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", "type": "tidelift" } ], - "time": "2025-01-02T08:10:11+00:00" + "time": "2024-11-21T01:49:47+00:00" }, { - "name": "symfony/service-contracts", - "version": "v3.6.0", + "name": "webmozart/assert", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/symfony/service-contracts.git", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" + "url": "https://github.com/webmozarts/assert.git", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", - "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", + "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/container": "^1.1|^2.0", - "symfony/deprecation-contracts": "^2.5|^3" + "ext-ctype": "*", + "php": "^7.2 || ^8.0" }, "conflict": { - "ext-psr": "<1.1|>=2" + "phpstan/phpstan": "<0.12.20", + "vimeo/psalm": "<4.6.1 || 4.6.2" + }, + "require-dev": { + "phpunit/phpunit": "^8.5.13" }, "type": "library", "extra": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-master": "1.10-dev" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Service\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + "Webmozart\\Assert\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1760,85 +1608,78 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Generic abstractions related to writing services", - "homepage": "https://symfony.com", + "description": "Assertions to validate method input/output with nice error messages.", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "assert", + "check", + "validate" ], "support": { - "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" + "issues": "https://github.com/webmozarts/assert/issues", + "source": "https://github.com/webmozarts/assert/tree/1.11.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/string", - "version": "v6.4.21", + "time": "2022-06-03T18:03:27+00:00" + } + ], + "packages-dev": [ + { + "name": "brianium/paratest", + "version": "v7.3.1", "source": { "type": "git", - "url": "https://github.com/symfony/string.git", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" + "url": "https://github.com/paratestphp/paratest.git", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", - "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", + "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", + "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-intl-grapheme": "~1.0", - "symfony/polyfill-intl-normalizer": "~1.0", - "symfony/polyfill-mbstring": "~1.0" - }, - "conflict": { - "symfony/translation-contracts": "<2.5" + "ext-dom": "*", + "ext-pcre": "*", + "ext-reflection": "*", + "ext-simplexml": "*", + "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", + "jean85/pretty-package-versions": "^2.0.5", + "php": "~8.1.0 || ~8.2.0 || ~8.3.0", + "phpunit/php-code-coverage": "^10.1.7", + "phpunit/php-file-iterator": "^4.1.0", + "phpunit/php-timer": "^6.0", + "phpunit/phpunit": "^10.4.2", + "sebastian/environment": "^6.0.1", + "symfony/console": "^6.3.4 || ^7.0.0", + "symfony/process": "^6.3.4 || ^7.0.0" }, "require-dev": { - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/http-client": "^5.4|^6.0|^7.0", - "symfony/intl": "^6.2|^7.0", - "symfony/translation-contracts": "^2.5|^3.0", - "symfony/var-exporter": "^5.4|^6.0|^7.0" + "doctrine/coding-standard": "^12.0.0", + "ext-pcov": "*", + "ext-posix": "*", + "infection/infection": "^0.27.6", + "phpstan/phpstan": "^1.10.40", + "phpstan/phpstan-deprecation-rules": "^1.1.4", + "phpstan/phpstan-phpunit": "^1.3.15", + "phpstan/phpstan-strict-rules": "^1.5.2", + "squizlabs/php_codesniffer": "^3.7.2", + "symfony/filesystem": "^6.3.1 || ^7.0.0" }, + "bin": [ + "bin/paratest", + "bin/paratest.bat", + "bin/paratest_for_phpstorm" + ], "type": "library", "autoload": { - "files": [ - "Resources/functions.php" - ], "psr-4": { - "Symfony\\Component\\String\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "ParaTest\\": [ + "src/" + ] + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1846,102 +1687,67 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" + "name": "Brian Scaturro", + "email": "scaturrob@gmail.com", + "role": "Developer" }, { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Filippo Tessarotto", + "email": "zoeslam@gmail.com", + "role": "Developer" } ], - "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", + "description": "Parallel testing for PHP", + "homepage": "https://github.com/paratestphp/paratest", "keywords": [ - "grapheme", - "i18n", - "string", - "unicode", - "utf-8", - "utf8" + "concurrent", + "parallel", + "phpunit", + "testing" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.4.21" + "issues": "https://github.com/paratestphp/paratest/issues", + "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" }, "funding": [ { - "url": "https://symfony.com/sponsor", - "type": "custom" - }, - { - "url": "https://github.com/fabpot", + "url": "https://github.com/sponsors/Slamdunk", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" + "url": "https://paypal.me/filippotessarotto", + "type": "paypal" } ], - "time": "2025-04-18T15:23:29+00:00" + "time": "2023-10-31T09:24:17+00:00" }, { - "name": "symfony/translation", - "version": "v6.4.22", + "name": "clue/ndjson-react", + "version": "v1.3.0", "source": { "type": "git", - "url": "https://github.com/symfony/translation.git", - "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad" + "url": "https://github.com/clue/reactphp-ndjson.git", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation/zipball/7e3b3b7146c6fab36ddff304a8041174bf6e17ad", - "reference": "7e3b3b7146c6fab36ddff304a8041174bf6e17ad", + "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", + "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3", - "symfony/polyfill-mbstring": "~1.0", - "symfony/translation-contracts": "^2.5|^3.0" - }, - "conflict": { - "symfony/config": "<5.4", - "symfony/console": "<5.4", - "symfony/dependency-injection": "<5.4", - "symfony/http-client-contracts": "<2.5", - "symfony/http-kernel": "<5.4", - "symfony/service-contracts": "<2.5", - "symfony/twig-bundle": "<5.4", - "symfony/yaml": "<5.4" - }, - "provide": { - "symfony/translation-implementation": "2.3|3.0" + "php": ">=5.3", + "react/stream": "^1.2" }, "require-dev": { - "nikic/php-parser": "^4.18|^5.0", - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/console": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/finder": "^5.4|^6.0|^7.0", - "symfony/http-client-contracts": "^2.5|^3.0", - "symfony/http-kernel": "^5.4|^6.0|^7.0", - "symfony/intl": "^5.4|^6.0|^7.0", - "symfony/polyfill-intl-icu": "^1.21", - "symfony/routing": "^5.4|^6.0|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/yaml": "^5.4|^6.0|^7.0" + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", + "react/event-loop": "^1.2" }, "type": "library", "autoload": { - "files": [ - "Resources/functions.php" - ], "psr-4": { - "Symfony\\Component\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Clue\\React\\NDJson\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -1949,69 +1755,76 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Christian Lück", + "email": "christian@clue.engineering" } ], - "description": "Provides tools to internationalize your application", - "homepage": "https://symfony.com", + "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": { - "source": "https://github.com/symfony/translation/tree/v6.4.22" + "issues": "https://github.com/clue/reactphp-ndjson/issues", + "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" }, "funding": [ { - "url": "https://symfony.com/sponsor", + "url": "https://clue.engineering/support", "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/clue", "type": "github" - }, - { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", - "type": "tidelift" } ], - "time": "2025-05-29T07:06:44+00:00" + "time": "2022-12-23T10:58:28+00:00" }, { - "name": "symfony/translation-contracts", - "version": "v3.6.0", + "name": "composer/pcre", + "version": "3.3.2", "source": { "type": "git", - "url": "https://github.com/symfony/translation-contracts.git", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d" + "url": "https://github.com/composer/pcre.git", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/translation-contracts/zipball/df210c7a2573f1913b2d17cc95f90f53a73d8f7d", - "reference": "df210c7a2573f1913b2d17cc95f90f53a73d8f7d", + "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", "shasum": "" }, "require": { - "php": ">=8.1" + "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": { - "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" + "phpstan": { + "includes": [ + "extension.neon" + ] }, "branch-alias": { - "dev-main": "3.6-dev" + "dev-main": "3.x-dev" } }, "autoload": { "psr-4": { - "Symfony\\Contracts\\Translation\\": "" - }, - "exclude-from-classmap": [ - "/Test/" - ] + "Composer\\Pcre\\": "src" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -2019,70 +1832,68 @@ ], "authors": [ { - "name": "Nicolas Grekas", - "email": "p@tchwork.com" - }, - { - "name": "Symfony Community", - "homepage": "https://symfony.com/contributors" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "http://seld.be" } ], - "description": "Generic abstractions related to translation", - "homepage": "https://symfony.com", + "description": "PCRE wrapping library that offers type-safe preg_* replacements.", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" - ], - "support": { - "source": "https://github.com/symfony/translation-contracts/tree/v3.6.0" + "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://symfony.com/sponsor", + "url": "https://packagist.com", "type": "custom" }, { - "url": "https://github.com/fabpot", + "url": "https://github.com/composer", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2024-09-27T08:32:26+00:00" + "time": "2024-11-12T16:29:46+00:00" }, { - "name": "voku/portable-ascii", - "version": "2.0.3", + "name": "composer/semver", + "version": "3.4.3", "source": { "type": "git", - "url": "https://github.com/voku/portable-ascii.git", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d" + "url": "https://github.com/composer/semver.git", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/voku/portable-ascii/zipball/b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", - "reference": "b1d923f88091c6bf09699efcd7c8a1b1bfd7351d", + "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", "shasum": "" }, "require": { - "php": ">=7.0.0" + "php": "^5.3.2 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "~6.0 || ~7.0 || ~9.0" - }, - "suggest": { - "ext-intl": "Use Intl for transliterator_transliterate() support" + "phpstan/phpstan": "^1.11", + "symfony/phpunit-bridge": "^3 || ^7" }, "type": "library", + "extra": { + "branch-alias": { + "dev-main": "3.x-dev" + } + }, "autoload": { "psr-4": { - "voku\\": "src/voku/" + "Composer\\Semver\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2091,79 +1902,77 @@ ], "authors": [ { - "name": "Lars Moelleken", - "homepage": "https://www.moelleken.org/" + "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": "Portable ASCII library - performance optimized (ascii) string functions for php.", - "homepage": "https://github.com/voku/portable-ascii", + "description": "Semver library that offers utilities, version constraint parsing and validation.", "keywords": [ - "ascii", - "clean", - "php" + "semantic", + "semver", + "validation", + "versioning" ], "support": { - "issues": "https://github.com/voku/portable-ascii/issues", - "source": "https://github.com/voku/portable-ascii/tree/2.0.3" + "irc": "ircs://irc.libera.chat:6697/composer", + "issues": "https://github.com/composer/semver/issues", + "source": "https://github.com/composer/semver/tree/3.4.3" }, "funding": [ { - "url": "https://www.paypal.me/moelleken", + "url": "https://packagist.com", "type": "custom" }, { - "url": "https://github.com/voku", + "url": "https://github.com/composer", "type": "github" }, { - "url": "https://opencollective.com/portable-ascii", - "type": "open_collective" - }, - { - "url": "https://www.patreon.com/voku", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/voku/portable-ascii", + "url": "https://tidelift.com/funding/github/packagist/composer/composer", "type": "tidelift" } ], - "time": "2024-11-21T01:49:47+00:00" + "time": "2024-09-19T14:15:21+00:00" }, { - "name": "webmozart/assert", - "version": "1.11.0", + "name": "composer/xdebug-handler", + "version": "3.0.5", "source": { "type": "git", - "url": "https://github.com/webmozarts/assert.git", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991" + "url": "https://github.com/composer/xdebug-handler.git", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/webmozarts/assert/zipball/11cb2199493b2f8a3b53e7f19068fc6aac760991", - "reference": "11cb2199493b2f8a3b53e7f19068fc6aac760991", + "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", + "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", "shasum": "" }, "require": { - "ext-ctype": "*", - "php": "^7.2 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<0.12.20", - "vimeo/psalm": "<4.6.1 || 4.6.2" + "composer/pcre": "^1 || ^2 || ^3", + "php": "^7.2.5 || ^8.0", + "psr/log": "^1 || ^2 || ^3" }, "require-dev": { - "phpunit/phpunit": "^8.5.13" + "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.10-dev" - } - }, "autoload": { "psr-4": { - "Webmozart\\Assert\\": "src/" + "Composer\\XdebugHandler\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2172,77 +1981,71 @@ ], "authors": [ { - "name": "Bernhard Schussek", - "email": "bschussek@gmail.com" + "name": "John Stevenson", + "email": "john-stevenson@blueyonder.co.uk" } ], - "description": "Assertions to validate method input/output with nice error messages.", + "description": "Restarts a process without Xdebug.", "keywords": [ - "assert", - "check", - "validate" + "Xdebug", + "performance" ], "support": { - "issues": "https://github.com/webmozarts/assert/issues", - "source": "https://github.com/webmozarts/assert/tree/1.11.0" + "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": "2022-06-03T18:03:27+00:00" - } - ], - "packages-dev": [ + "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": "brianium/paratest", - "version": "v7.3.1", + "name": "doctrine/annotations", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/paratestphp/paratest.git", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30" + "url": "https://github.com/doctrine/annotations.git", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/paratestphp/paratest/zipball/551f46f52a93177d873f3be08a1649ae886b4a30", - "reference": "551f46f52a93177d873f3be08a1649ae886b4a30", + "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", + "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-pcre": "*", - "ext-reflection": "*", - "ext-simplexml": "*", - "fidry/cpu-core-counter": "^0.5.1 || ^1.0.0", - "jean85/pretty-package-versions": "^2.0.5", - "php": "~8.1.0 || ~8.2.0 || ~8.3.0", - "phpunit/php-code-coverage": "^10.1.7", - "phpunit/php-file-iterator": "^4.1.0", - "phpunit/php-timer": "^6.0", - "phpunit/phpunit": "^10.4.2", - "sebastian/environment": "^6.0.1", - "symfony/console": "^6.3.4 || ^7.0.0", - "symfony/process": "^6.3.4 || ^7.0.0" + "doctrine/lexer": "^2 || ^3", + "ext-tokenizer": "*", + "php": "^7.2 || ^8.0", + "psr/cache": "^1 || ^2 || ^3" }, "require-dev": { - "doctrine/coding-standard": "^12.0.0", - "ext-pcov": "*", - "ext-posix": "*", - "infection/infection": "^0.27.6", - "phpstan/phpstan": "^1.10.40", - "phpstan/phpstan-deprecation-rules": "^1.1.4", - "phpstan/phpstan-phpunit": "^1.3.15", - "phpstan/phpstan-strict-rules": "^1.5.2", - "squizlabs/php_codesniffer": "^3.7.2", - "symfony/filesystem": "^6.3.1 || ^7.0.0" + "doctrine/cache": "^2.0", + "doctrine/coding-standard": "^10", + "phpstan/phpstan": "^1.10.28", + "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", + "symfony/cache": "^5.4 || ^6.4 || ^7", + "vimeo/psalm": "^4.30 || ^5.14" + }, + "suggest": { + "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" }, - "bin": [ - "bin/paratest", - "bin/paratest.bat", - "bin/paratest_for_phpstorm" - ], "type": "library", "autoload": { "psr-4": { - "ParaTest\\": [ - "src/" - ] + "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" } }, "notification-url": "https://packagist.org/downloads/", @@ -2251,66 +2054,67 @@ ], "authors": [ { - "name": "Brian Scaturro", - "email": "scaturrob@gmail.com", - "role": "Developer" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" }, { - "name": "Filippo Tessarotto", - "email": "zoeslam@gmail.com", - "role": "Developer" + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Benjamin Eberlei", + "email": "kontakt@beberlei.de" + }, + { + "name": "Jonathan Wage", + "email": "jonwage@gmail.com" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "Parallel testing for PHP", - "homepage": "https://github.com/paratestphp/paratest", + "description": "Docblock Annotations Parser", + "homepage": "https://www.doctrine-project.org/projects/annotations.html", "keywords": [ - "concurrent", - "parallel", - "phpunit", - "testing" + "annotations", + "docblock", + "parser" ], "support": { - "issues": "https://github.com/paratestphp/paratest/issues", - "source": "https://github.com/paratestphp/paratest/tree/v7.3.1" + "issues": "https://github.com/doctrine/annotations/issues", + "source": "https://github.com/doctrine/annotations/tree/2.0.2" }, - "funding": [ - { - "url": "https://github.com/sponsors/Slamdunk", - "type": "github" - }, - { - "url": "https://paypal.me/filippotessarotto", - "type": "paypal" - } - ], - "time": "2023-10-31T09:24:17+00:00" + "time": "2024-09-05T10:17:24+00:00" }, { - "name": "clue/ndjson-react", - "version": "v1.3.0", + "name": "doctrine/lexer", + "version": "3.0.1", "source": { "type": "git", - "url": "https://github.com/clue/reactphp-ndjson.git", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0" + "url": "https://github.com/doctrine/lexer.git", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/clue/reactphp-ndjson/zipball/392dc165fce93b5bb5c637b67e59619223c931b0", - "reference": "392dc165fce93b5bb5c637b67e59619223c931b0", + "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", + "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", "shasum": "" }, "require": { - "php": ">=5.3", - "react/stream": "^1.2" + "php": "^8.1" }, "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35", - "react/event-loop": "^1.2" + "doctrine/coding-standard": "^12", + "phpstan/phpstan": "^1.10", + "phpunit/phpunit": "^10.5", + "psalm/plugin-phpunit": "^0.18.3", + "vimeo/psalm": "^5.21" }, "type": "library", "autoload": { "psr-4": { - "Clue\\React\\NDJson\\": "src/" + "Doctrine\\Common\\Lexer\\": "src" } }, "notification-url": "https://packagist.org/downloads/", @@ -2319,75 +2123,71 @@ ], "authors": [ { - "name": "Christian Lück", - "email": "christian@clue.engineering" + "name": "Guilherme Blanco", + "email": "guilhermeblanco@gmail.com" + }, + { + "name": "Roman Borschel", + "email": "roman@code-factory.org" + }, + { + "name": "Johannes Schmitt", + "email": "schmittjoh@gmail.com" } ], - "description": "Streaming newline-delimited JSON (NDJSON) parser and encoder for ReactPHP.", - "homepage": "https://github.com/clue/reactphp-ndjson", + "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", + "homepage": "https://www.doctrine-project.org/projects/lexer.html", "keywords": [ - "NDJSON", - "json", - "jsonlines", - "newline", - "reactphp", - "streaming" + "annotations", + "docblock", + "lexer", + "parser", + "php" ], "support": { - "issues": "https://github.com/clue/reactphp-ndjson/issues", - "source": "https://github.com/clue/reactphp-ndjson/tree/v1.3.0" + "issues": "https://github.com/doctrine/lexer/issues", + "source": "https://github.com/doctrine/lexer/tree/3.0.1" }, "funding": [ { - "url": "https://clue.engineering/support", + "url": "https://www.doctrine-project.org/sponsorship.html", "type": "custom" }, { - "url": "https://github.com/clue", - "type": "github" + "url": "https://www.patreon.com/phpdoctrine", + "type": "patreon" + }, + { + "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", + "type": "tidelift" } ], - "time": "2022-12-23T10:58:28+00:00" + "time": "2024-02-05T11:56:58+00:00" }, { - "name": "composer/pcre", - "version": "3.3.2", + "name": "evenement/evenement", + "version": "v3.0.2", "source": { "type": "git", - "url": "https://github.com/composer/pcre.git", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e" + "url": "https://github.com/igorw/evenement.git", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/pcre/zipball/b2bed4734f0cc156ee1fe9c0da2550420d99a21e", - "reference": "b2bed4734f0cc156ee1fe9c0da2550420d99a21e", + "url": "https://api.github.com/repos/igorw/evenement/zipball/0a16b0d71ab13284339abb99d9d2bd813640efbc", + "reference": "0a16b0d71ab13284339abb99d9d2bd813640efbc", "shasum": "" }, "require": { - "php": "^7.4 || ^8.0" - }, - "conflict": { - "phpstan/phpstan": "<1.11.10" + "php": ">=7.0" }, "require-dev": { - "phpstan/phpstan": "^1.12 || ^2", - "phpstan/phpstan-strict-rules": "^1 || ^2", - "phpunit/phpunit": "^8 || ^9" + "phpunit/phpunit": "^9 || ^6" }, "type": "library", - "extra": { - "phpstan": { - "includes": [ - "extension.neon" - ] - }, - "branch-alias": { - "dev-main": "3.x-dev" - } - }, "autoload": { "psr-4": { - "Composer\\Pcre\\": "src" + "Evenement\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2396,68 +2196,61 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "http://seld.be" + "name": "Igor Wiedler", + "email": "igor@wiedler.ch" } ], - "description": "PCRE wrapping library that offers type-safe preg_* replacements.", + "description": "Événement is a very simple event dispatching library for PHP", "keywords": [ - "PCRE", - "preg", - "regex", - "regular expression" + "event-dispatcher", + "event-emitter" ], "support": { - "issues": "https://github.com/composer/pcre/issues", - "source": "https://github.com/composer/pcre/tree/3.3.2" + "issues": "https://github.com/igorw/evenement/issues", + "source": "https://github.com/igorw/evenement/tree/v3.0.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" + "time": "2023-08-08T05:53:35+00:00" }, { - "name": "composer/semver", - "version": "3.4.3", + "name": "fakerphp/faker", + "version": "v1.24.1", "source": { "type": "git", - "url": "https://github.com/composer/semver.git", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12" + "url": "https://github.com/FakerPHP/Faker.git", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/semver/zipball/4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", - "reference": "4313d26ada5e0c4edfbd1dc481a92ff7bff91f12", + "url": "https://api.github.com/repos/FakerPHP/Faker/zipball/e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", + "reference": "e0ee18eb1e6dc3cda3ce9fd97e5a0689a88a64b5", "shasum": "" }, "require": { - "php": "^5.3.2 || ^7.0 || ^8.0" + "php": "^7.4 || ^8.0", + "psr/container": "^1.0 || ^2.0", + "symfony/deprecation-contracts": "^2.2 || ^3.0" + }, + "conflict": { + "fzaninotto/faker": "*" }, "require-dev": { - "phpstan/phpstan": "^1.11", - "symfony/phpunit-bridge": "^3 || ^7" + "bamarni/composer-bin-plugin": "^1.4.1", + "doctrine/persistence": "^1.3 || ^2.0", + "ext-intl": "*", + "phpunit/phpunit": "^9.5.26", + "symfony/phpunit-bridge": "^5.4.16" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.x-dev" - } + "suggest": { + "doctrine/orm": "Required to use Faker\\ORM\\Doctrine", + "ext-curl": "Required by Faker\\Provider\\Image to download images.", + "ext-dom": "Required by Faker\\Provider\\HtmlLorem for generating random HTML.", + "ext-iconv": "Required by Faker\\Provider\\ru_RU\\Text::realText() for generating real Russian text.", + "ext-mbstring": "Required for multibyte Unicode string functionality." }, + "type": "library", "autoload": { "psr-4": { - "Composer\\Semver\\": "src" + "Faker\\": "src/Faker/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2466,347 +2259,53 @@ ], "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" + "name": "François Zaninotto" } ], - "description": "Semver library that offers utilities, version constraint parsing and validation.", + "description": "Faker is a PHP library that generates fake data for you.", "keywords": [ - "semantic", - "semver", - "validation", - "versioning" + "data", + "faker", + "fixtures" ], "support": { - "irc": "ircs://irc.libera.chat:6697/composer", - "issues": "https://github.com/composer/semver/issues", - "source": "https://github.com/composer/semver/tree/3.4.3" + "issues": "https://github.com/FakerPHP/Faker/issues", + "source": "https://github.com/FakerPHP/Faker/tree/v1.24.1" }, - "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-09-19T14:15:21+00:00" + "time": "2024-11-21T13:46:39+00:00" }, { - "name": "composer/xdebug-handler", - "version": "3.0.5", + "name": "fidry/cpu-core-counter", + "version": "1.2.0", "source": { "type": "git", - "url": "https://github.com/composer/xdebug-handler.git", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef" + "url": "https://github.com/theofidry/cpu-core-counter.git", + "reference": "8520451a140d3f46ac33042715115e290cf5785f" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/composer/xdebug-handler/zipball/6c1925561632e83d60a44492e0b344cf48ab85ef", - "reference": "6c1925561632e83d60a44492e0b344cf48ab85ef", + "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", + "reference": "8520451a140d3f46ac33042715115e290cf5785f", "shasum": "" }, "require": { - "composer/pcre": "^1 || ^2 || ^3", - "php": "^7.2.5 || ^8.0", - "psr/log": "^1 || ^2 || ^3" + "php": "^7.2 || ^8.0" }, "require-dev": { - "phpstan/phpstan": "^1.0", - "phpstan/phpstan-strict-rules": "^1.1", - "phpunit/phpunit": "^8.5 || ^9.6 || ^10.5" + "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" }, "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/annotations", - "version": "2.0.2", - "source": { - "type": "git", - "url": "https://github.com/doctrine/annotations.git", - "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/annotations/zipball/901c2ee5d26eb64ff43c47976e114bf00843acf7", - "reference": "901c2ee5d26eb64ff43c47976e114bf00843acf7", - "shasum": "" - }, - "require": { - "doctrine/lexer": "^2 || ^3", - "ext-tokenizer": "*", - "php": "^7.2 || ^8.0", - "psr/cache": "^1 || ^2 || ^3" - }, - "require-dev": { - "doctrine/cache": "^2.0", - "doctrine/coding-standard": "^10", - "phpstan/phpstan": "^1.10.28", - "phpunit/phpunit": "^7.5 || ^8.5 || ^9.5", - "symfony/cache": "^5.4 || ^6.4 || ^7", - "vimeo/psalm": "^4.30 || ^5.14" - }, - "suggest": { - "php": "PHP 8.0 or higher comes with attributes, a native replacement for annotations" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Annotations\\": "lib/Doctrine/Common/Annotations" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Benjamin Eberlei", - "email": "kontakt@beberlei.de" - }, - { - "name": "Jonathan Wage", - "email": "jonwage@gmail.com" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "Docblock Annotations Parser", - "homepage": "https://www.doctrine-project.org/projects/annotations.html", - "keywords": [ - "annotations", - "docblock", - "parser" - ], - "support": { - "issues": "https://github.com/doctrine/annotations/issues", - "source": "https://github.com/doctrine/annotations/tree/2.0.2" - }, - "time": "2024-09-05T10:17:24+00:00" - }, - { - "name": "doctrine/lexer", - "version": "3.0.1", - "source": { - "type": "git", - "url": "https://github.com/doctrine/lexer.git", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/doctrine/lexer/zipball/31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "reference": "31ad66abc0fc9e1a1f2d9bc6a42668d2fbbcd6dd", - "shasum": "" - }, - "require": { - "php": "^8.1" - }, - "require-dev": { - "doctrine/coding-standard": "^12", - "phpstan/phpstan": "^1.10", - "phpunit/phpunit": "^10.5", - "psalm/plugin-phpunit": "^0.18.3", - "vimeo/psalm": "^5.21" - }, - "type": "library", - "autoload": { - "psr-4": { - "Doctrine\\Common\\Lexer\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "Guilherme Blanco", - "email": "guilhermeblanco@gmail.com" - }, - { - "name": "Roman Borschel", - "email": "roman@code-factory.org" - }, - { - "name": "Johannes Schmitt", - "email": "schmittjoh@gmail.com" - } - ], - "description": "PHP Doctrine Lexer parser library that can be used in Top-Down, Recursive Descent Parsers.", - "homepage": "https://www.doctrine-project.org/projects/lexer.html", - "keywords": [ - "annotations", - "docblock", - "lexer", - "parser", - "php" - ], - "support": { - "issues": "https://github.com/doctrine/lexer/issues", - "source": "https://github.com/doctrine/lexer/tree/3.0.1" - }, - "funding": [ - { - "url": "https://www.doctrine-project.org/sponsorship.html", - "type": "custom" - }, - { - "url": "https://www.patreon.com/phpdoctrine", - "type": "patreon" - }, - { - "url": "https://tidelift.com/funding/github/packagist/doctrine%2Flexer", - "type": "tidelift" - } - ], - "time": "2024-02-05T11:56:58+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.2.0", - "source": { - "type": "git", - "url": "https://github.com/theofidry/cpu-core-counter.git", - "reference": "8520451a140d3f46ac33042715115e290cf5785f" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/theofidry/cpu-core-counter/zipball/8520451a140d3f46ac33042715115e290cf5785f", - "reference": "8520451a140d3f46ac33042715115e290cf5785f", - "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": "^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" - }, - "type": "library", - "autoload": { - "psr-4": { - "Fidry\\CpuCoreCounter\\": "src/" + "Fidry\\CpuCoreCounter\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -2838,16 +2337,16 @@ }, { "name": "filp/whoops", - "version": "2.18.2", + "version": "2.18.3", "source": { "type": "git", "url": "https://github.com/filp/whoops.git", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3" + "reference": "59a123a3d459c5a23055802237cb317f609867e5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/filp/whoops/zipball/89dabca1490bc77dbcab41c2b20968c7e44bf7c3", - "reference": "89dabca1490bc77dbcab41c2b20968c7e44bf7c3", + "url": "https://api.github.com/repos/filp/whoops/zipball/59a123a3d459c5a23055802237cb317f609867e5", + "reference": "59a123a3d459c5a23055802237cb317f609867e5", "shasum": "" }, "require": { @@ -2897,7 +2396,7 @@ ], "support": { "issues": "https://github.com/filp/whoops/issues", - "source": "https://github.com/filp/whoops/tree/2.18.2" + "source": "https://github.com/filp/whoops/tree/2.18.3" }, "funding": [ { @@ -2905,7 +2404,7 @@ "type": "github" } ], - "time": "2025-06-11T20:42:19+00:00" + "time": "2025-06-16T00:02:10+00:00" }, { "name": "friendsofphp/php-cs-fixer", @@ -4384,91 +3883,315 @@ "sebastian/type": "^4.0.0", "sebastian/version": "^4.0.1" }, - "suggest": { - "ext-soap": "To be able to generate mocks based on WSDL files" + "suggest": { + "ext-soap": "To be able to generate mocks based on WSDL files" + }, + "bin": [ + "phpunit" + ], + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "10.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/10.5.36" + }, + "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", + "type": "tidelift" + } + ], + "time": "2024-10-08T15:36:51+00:00" + }, + { + "name": "psr/cache", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/cache.git", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "1.0.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Cache\\": "src/" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for caching libraries", + "keywords": [ + "cache", + "psr", + "psr-6" + ], + "support": { + "source": "https://github.com/php-fig/cache/tree/3.0.0" + }, + "time": "2021-02-03T23:26:27+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": "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" }, - "bin": [ - "phpunit" - ], - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "10.5-dev" - } + "require-dev": { + "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" }, + "type": "library", "autoload": { - "files": [ - "src/Framework/Assert/Functions.php" - ], - "classmap": [ - "src/" - ] + "psr-4": { + "React\\Cache\\": "src/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "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": "The PHP Unit Testing framework.", - "homepage": "https://phpunit.de/", + "description": "Async, Promise-based cache interface for ReactPHP", "keywords": [ - "phpunit", - "testing", - "xunit" + "cache", + "caching", + "promise", + "reactphp" ], "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.36" + "issues": "https://github.com/reactphp/cache/issues", + "source": "https://github.com/reactphp/cache/tree/v1.2.0" }, "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", - "type": "tidelift" + "url": "https://opencollective.com/reactphp", + "type": "open_collective" } ], - "time": "2024-10-08T15:36:51+00:00" + "time": "2022-11-30T15:59:55+00:00" }, { - "name": "psr/cache", - "version": "3.0.0", + "name": "react/child-process", + "version": "v0.6.6", "source": { "type": "git", - "url": "https://github.com/php-fig/cache.git", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf" + "url": "https://github.com/reactphp/child-process.git", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/cache/zipball/aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", - "reference": "aa5030cfa5405eccfdcb1083ce040c2cb8d253bf", + "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", + "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", "shasum": "" }, "require": { - "php": ">=8.0.0" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.0", + "react/event-loop": "^1.2", + "react/stream": "^1.4" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "1.0.x-dev" - } + "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": { - "Psr\\Cache\\": "src/" + "React\\ChildProcess\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4477,47 +4200,73 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "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": "Common interface for caching libraries", + "description": "Event-driven library for executing child processes with ReactPHP.", "keywords": [ - "cache", - "psr", - "psr-6" + "event-driven", + "process", + "reactphp" ], "support": { - "source": "https://github.com/php-fig/cache/tree/3.0.0" + "issues": "https://github.com/reactphp/child-process/issues", + "source": "https://github.com/reactphp/child-process/tree/v0.6.6" }, - "time": "2021-02-03T23:26:27+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2025-01-01T16:37:48+00:00" }, { - "name": "psr/event-dispatcher", - "version": "1.0.0", + "name": "react/dns", + "version": "v1.13.0", "source": { "type": "git", - "url": "https://github.com/php-fig/event-dispatcher.git", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0" + "url": "https://github.com/reactphp/dns.git", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/event-dispatcher/zipball/dbefd12671e8a14ec7f180cab83036ed26714bb0", - "reference": "dbefd12671e8a14ec7f180cab83036ed26714bb0", + "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", "shasum": "" }, "require": { - "php": ">=7.2.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": "1.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": { "psr-4": { - "Psr\\EventDispatcher\\": "src/" + "React\\Dns\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4526,48 +4275,72 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "http://www.php-fig.org/" + "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": "Standard interfaces for event handling.", + "description": "Async DNS resolver for ReactPHP", "keywords": [ - "events", - "psr", - "psr-14" + "async", + "dns", + "dns-resolver", + "reactphp" ], "support": { - "issues": "https://github.com/php-fig/event-dispatcher/issues", - "source": "https://github.com/php-fig/event-dispatcher/tree/1.0.0" + "issues": "https://github.com/reactphp/dns/issues", + "source": "https://github.com/reactphp/dns/tree/v1.13.0" }, - "time": "2019-01-08T18:20:26+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2024-06-13T14:18:03+00:00" }, { - "name": "psr/log", - "version": "3.0.2", + "name": "react/event-loop", + "version": "v1.5.0", "source": { "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3" + "url": "https://github.com/reactphp/event-loop.git", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", - "reference": "f16e1d5863e37f8d8c2a01719f5b34baa2b714d3", + "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", "shasum": "" }, "require": { - "php": ">=8.0.0" + "php": ">=5.3.0" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } + "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": { - "Psr\\Log\\": "src" + "React\\EventLoop\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4576,47 +4349,71 @@ ], "authors": [ { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" + "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": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", + "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", "keywords": [ - "log", - "psr", - "psr-3" + "asynchronous", + "event-loop" ], "support": { - "source": "https://github.com/php-fig/log/tree/3.0.2" + "issues": "https://github.com/reactphp/event-loop/issues", + "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" }, - "time": "2024-09-11T13:17:53+00:00" + "funding": [ + { + "url": "https://opencollective.com/reactphp", + "type": "open_collective" + } + ], + "time": "2023-11-13T13:48:05+00:00" }, { - "name": "react/cache", - "version": "v1.2.0", + "name": "react/promise", + "version": "v3.2.0", "source": { "type": "git", - "url": "https://github.com/reactphp/cache.git", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b" + "url": "https://github.com/reactphp/promise.git", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/cache/zipball/d47c472b64aa5608225f47965a484b75c7817d5b", - "reference": "d47c472b64aa5608225f47965a484b75c7817d5b", + "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", + "reference": "8a164643313c71354582dc850b42b33fa12a4b63", "shasum": "" }, "require": { - "php": ">=5.3.0", - "react/promise": "^3.0 || ^2.0 || ^1.1" + "php": ">=7.1.0" }, "require-dev": { - "phpunit/phpunit": "^9.5 || ^5.7 || ^4.8.35" + "phpstan/phpstan": "1.10.39 || 1.4.10", + "phpunit/phpunit": "^9.6 || ^7.5" }, "type": "library", "autoload": { + "files": [ + "src/functions_include.php" + ], "psr-4": { - "React\\Cache\\": "src/" + "React\\Promise\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4624,6 +4421,11 @@ "MIT" ], "authors": [ + { + "name": "Jan Sorgalla", + "email": "jsorgalla@gmail.com", + "homepage": "https://sorgalla.com/" + }, { "name": "Christian Lück", "email": "christian@clue.engineering", @@ -4634,27 +4436,20 @@ "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", + "description": "A lightweight implementation of CommonJS Promises/A for PHP", "keywords": [ - "cache", - "caching", "promise", - "reactphp" + "promises" ], "support": { - "issues": "https://github.com/reactphp/cache/issues", - "source": "https://github.com/reactphp/cache/tree/v1.2.0" + "issues": "https://github.com/reactphp/promise/issues", + "source": "https://github.com/reactphp/promise/tree/v3.2.0" }, "funding": [ { @@ -4662,37 +4457,40 @@ "type": "open_collective" } ], - "time": "2022-11-30T15:59:55+00:00" + "time": "2024-05-24T10:39:05+00:00" }, { - "name": "react/child-process", - "version": "v0.6.6", + "name": "react/socket", + "version": "v1.16.0", "source": { "type": "git", - "url": "https://github.com/reactphp/child-process.git", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159" + "url": "https://github.com/reactphp/socket.git", + "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/child-process/zipball/1721e2b93d89b745664353b9cfc8f155ba8a6159", - "reference": "1721e2b93d89b745664353b9cfc8f155ba8a6159", + "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/socket": "^1.16", - "sebastian/environment": "^5.0 || ^3.0 || ^2.0 || ^1.0" + "react/async": "^4.3 || ^3.3 || ^2", + "react/promise-stream": "^1.4", + "react/promise-timer": "^1.11" }, "type": "library", "autoload": { "psr-4": { - "React\\ChildProcess\\": "src/" + "React\\Socket\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4721,15 +4519,17 @@ "homepage": "https://cboden.dev/" } ], - "description": "Event-driven library for executing child processes with ReactPHP.", + "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", "keywords": [ - "event-driven", - "process", - "reactphp" + "Connection", + "Socket", + "async", + "reactphp", + "stream" ], "support": { - "issues": "https://github.com/reactphp/child-process/issues", - "source": "https://github.com/reactphp/child-process/tree/v0.6.6" + "issues": "https://github.com/reactphp/socket/issues", + "source": "https://github.com/reactphp/socket/tree/v1.16.0" }, "funding": [ { @@ -4737,37 +4537,35 @@ "type": "open_collective" } ], - "time": "2025-01-01T16:37:48+00:00" + "time": "2024-07-26T10:38:09+00:00" }, { - "name": "react/dns", - "version": "v1.13.0", + "name": "react/stream", + "version": "v1.4.0", "source": { "type": "git", - "url": "https://github.com/reactphp/dns.git", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5" + "url": "https://github.com/reactphp/stream.git", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/dns/zipball/eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", - "reference": "eb8ae001b5a455665c89c1df97f6fb682f8fb0f5", + "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", "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" + "evenement/evenement": "^3.0 || ^2.0 || ^1.0", + "php": ">=5.3.8", + "react/event-loop": "^1.2" }, "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36", - "react/async": "^4.3 || ^3 || ^2", - "react/promise-timer": "^1.11" + "clue/stream-filter": "~1.2", + "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" }, "type": "library", "autoload": { "psr-4": { - "React\\Dns\\": "src/" + "React\\Stream\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -4796,340 +4594,411 @@ "homepage": "https://cboden.dev/" } ], - "description": "Async DNS resolver for ReactPHP", + "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", "keywords": [ - "async", - "dns", - "dns-resolver", - "reactphp" + "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": "2.0.1", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/cli-parser.git", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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/2.0.1" + }, + "funding": [ + { + "url": "https://github.com/sebastianbergmann", + "type": "github" + } + ], + "time": "2024-03-02T07:12:49+00:00" + }, + { + "name": "sebastian/code-unit", + "version": "2.0.0", + "source": { + "type": "git", + "url": "https://github.com/sebastianbergmann/code-unit.git", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", + "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "shasum": "" + }, + "require": { + "php": ">=8.1" + }, + "require-dev": { + "phpunit/phpunit": "^10.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-main": "2.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/reactphp/dns/issues", - "source": "https://github.com/reactphp/dns/tree/v1.13.0" + "issues": "https://github.com/sebastianbergmann/code-unit/issues", + "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2024-06-13T14:18:03+00:00" + "time": "2023-02-03T06:58:43+00:00" }, { - "name": "react/event-loop", - "version": "v1.5.0", + "name": "sebastian/code-unit-reverse-lookup", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/reactphp/event-loop.git", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354" + "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/event-loop/zipball/bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", - "reference": "bbe0bd8c51ffc05ee43f1729087ed3bdf7d53354", + "url": "https://api.github.com/repos/sebastianbergmann/code-unit-reverse-lookup/zipball/5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", + "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d", "shasum": "" }, "require": { - "php": ">=5.3.0" + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" - }, - "suggest": { - "ext-pcntl": "For signal handling support when using the StreamSelectLoop" + "phpunit/phpunit": "^10.0" }, "type": "library", - "autoload": { - "psr-4": { - "React\\EventLoop\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "3.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "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/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" } ], - "description": "ReactPHP's core reactor event loop that libraries can use for evented I/O.", - "keywords": [ - "asynchronous", - "event-loop" - ], + "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/reactphp/event-loop/issues", - "source": "https://github.com/reactphp/event-loop/tree/v1.5.0" + "issues": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/issues", + "source": "https://github.com/sebastianbergmann/code-unit-reverse-lookup/tree/3.0.0" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2023-11-13T13:48:05+00:00" + "time": "2023-02-03T06:59:15+00:00" }, { - "name": "react/promise", - "version": "v3.2.0", + "name": "sebastian/comparator", + "version": "5.0.3", "source": { "type": "git", - "url": "https://github.com/reactphp/promise.git", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63" + "url": "https://github.com/sebastianbergmann/comparator.git", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/promise/zipball/8a164643313c71354582dc850b42b33fa12a4b63", - "reference": "8a164643313c71354582dc850b42b33fa12a4b63", + "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", "shasum": "" }, "require": { - "php": ">=7.1.0" + "ext-dom": "*", + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/diff": "^5.0", + "sebastian/exporter": "^5.0" }, "require-dev": { - "phpstan/phpstan": "1.10.39 || 1.4.10", - "phpunit/phpunit": "^9.6 || ^7.5" + "phpunit/phpunit": "^10.5" }, "type": "library", - "autoload": { - "files": [ - "src/functions_include.php" - ], - "psr-4": { - "React\\Promise\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "5.0-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "authors": [ { - "name": "Jan Sorgalla", - "email": "jsorgalla@gmail.com", - "homepage": "https://sorgalla.com/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Christian Lück", - "email": "christian@clue.engineering", - "homepage": "https://clue.engineering/" + "name": "Jeff Welch", + "email": "whatthejeff@gmail.com" }, { - "name": "Cees-Jan Kiewiet", - "email": "reactphp@ceesjankiewiet.nl", - "homepage": "https://wyrihaximus.net/" + "name": "Volker Dusch", + "email": "github@wallbash.com" }, { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "Bernhard Schussek", + "email": "bschussek@2bepublished.at" } ], - "description": "A lightweight implementation of CommonJS Promises/A for PHP", + "description": "Provides the functionality to compare PHP values for equality", + "homepage": "https://github.com/sebastianbergmann/comparator", "keywords": [ - "promise", - "promises" + "comparator", + "compare", + "equality" ], "support": { - "issues": "https://github.com/reactphp/promise/issues", - "source": "https://github.com/reactphp/promise/tree/v3.2.0" + "issues": "https://github.com/sebastianbergmann/comparator/issues", + "security": "https://github.com/sebastianbergmann/comparator/security/policy", + "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2024-05-24T10:39:05+00:00" + "time": "2024-10-18T14:56:07+00:00" }, { - "name": "react/socket", - "version": "v1.16.0", + "name": "sebastian/complexity", + "version": "3.2.0", "source": { "type": "git", - "url": "https://github.com/reactphp/socket.git", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1" + "url": "https://github.com/sebastianbergmann/complexity.git", + "reference": "68ff824baeae169ec9f2137158ee529584553799" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/socket/zipball/23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", - "reference": "23e4ff33ea3e160d2d1f59a0e6050e4b0fb0eac1", + "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", + "reference": "68ff824baeae169ec9f2137158ee529584553799", "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" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" }, "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" + "phpunit/phpunit": "^10.0" }, "type": "library", - "autoload": { - "psr-4": { - "React\\Socket\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "3.2-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "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/" + "name": "Sebastian Bergmann", + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Async, streaming plaintext TCP/IP and secure TLS socket server and client connections for ReactPHP", - "keywords": [ - "Connection", - "Socket", - "async", - "reactphp", - "stream" - ], + "description": "Library for calculating the complexity of PHP code units", + "homepage": "https://github.com/sebastianbergmann/complexity", "support": { - "issues": "https://github.com/reactphp/socket/issues", - "source": "https://github.com/reactphp/socket/tree/v1.16.0" + "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" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2024-07-26T10:38:09+00:00" + "time": "2023-12-21T08:37:17+00:00" }, { - "name": "react/stream", - "version": "v1.4.0", + "name": "sebastian/diff", + "version": "5.1.1", "source": { "type": "git", - "url": "https://github.com/reactphp/stream.git", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d" + "url": "https://github.com/sebastianbergmann/diff.git", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/reactphp/stream/zipball/1e5b0acb8fe55143b5b426817155190eb6f5b18d", - "reference": "1e5b0acb8fe55143b5b426817155190eb6f5b18d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", + "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", "shasum": "" }, "require": { - "evenement/evenement": "^3.0 || ^2.0 || ^1.0", - "php": ">=5.3.8", - "react/event-loop": "^1.2" + "php": ">=8.1" }, "require-dev": { - "clue/stream-filter": "~1.2", - "phpunit/phpunit": "^9.6 || ^5.7 || ^4.8.36" + "phpunit/phpunit": "^10.0", + "symfony/process": "^6.4" }, "type": "library", - "autoload": { - "psr-4": { - "React\\Stream\\": "src/" + "extra": { + "branch-alias": { + "dev-main": "5.1-dev" } }, + "autoload": { + "classmap": [ + "src/" + ] + }, "notification-url": "https://packagist.org/downloads/", "license": [ - "MIT" + "BSD-3-Clause" ], "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": "Sebastian Bergmann", + "email": "sebastian@phpunit.de" }, { - "name": "Chris Boden", - "email": "cboden@gmail.com", - "homepage": "https://cboden.dev/" + "name": "Kore Nordmann", + "email": "mail@kore-nordmann.de" } ], - "description": "Event-driven readable and writable streams for non-blocking I/O in ReactPHP", + "description": "Diff implementation", + "homepage": "https://github.com/sebastianbergmann/diff", "keywords": [ - "event-driven", - "io", - "non-blocking", - "pipe", - "reactphp", - "readable", - "stream", - "writable" + "diff", + "udiff", + "unidiff", + "unified diff" ], "support": { - "issues": "https://github.com/reactphp/stream/issues", - "source": "https://github.com/reactphp/stream/tree/v1.4.0" + "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" }, "funding": [ { - "url": "https://opencollective.com/reactphp", - "type": "open_collective" + "url": "https://github.com/sebastianbergmann", + "type": "github" } ], - "time": "2024-06-11T12:45:25+00:00" + "time": "2024-03-02T07:15:17+00:00" }, { - "name": "sebastian/cli-parser", - "version": "2.0.1", + "name": "sebastian/environment", + "version": "6.1.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/cli-parser.git", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084" + "url": "https://github.com/sebastianbergmann/environment.git", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/c34583b87e7b7a8055bf6c450c2c77ce32a24084", - "reference": "c34583b87e7b7a8055bf6c450c2c77ce32a24084", + "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", + "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", "shasum": "" }, "require": { @@ -5138,10 +5007,13 @@ "require-dev": { "phpunit/phpunit": "^10.0" }, + "suggest": { + "ext-posix": "*" + }, "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "6.1-dev" } }, "autoload": { @@ -5156,16 +5028,20 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library for parsing CLI options", - "homepage": "https://github.com/sebastianbergmann/cli-parser", + "description": "Provides functionality to handle HHVM/PHP environments", + "homepage": "https://github.com/sebastianbergmann/environment", + "keywords": [ + "Xdebug", + "environment", + "hhvm" + ], "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/environment/issues", + "security": "https://github.com/sebastianbergmann/environment/security/policy", + "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" }, "funding": [ { @@ -5173,24 +5049,26 @@ "type": "github" } ], - "time": "2024-03-02T07:12:49+00:00" + "time": "2024-03-23T08:47:14+00:00" }, { - "name": "sebastian/code-unit", - "version": "2.0.0", + "name": "sebastian/exporter", + "version": "5.1.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit.git", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503" + "url": "https://github.com/sebastianbergmann/exporter.git", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/code-unit/zipball/a81fee9eef0b7a76af11d121767abc44c104e503", - "reference": "a81fee9eef0b7a76af11d121767abc44c104e503", + "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", + "reference": "955288482d97c19a372d3f31006ab3f37da47adf", "shasum": "" }, "require": { - "php": ">=8.1" + "ext-mbstring": "*", + "php": ">=8.1", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "phpunit/phpunit": "^10.0" @@ -5198,7 +5076,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "2.0-dev" + "dev-main": "5.1-dev" } }, "autoload": { @@ -5213,15 +5091,35 @@ "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": "Adam Harvey", + "email": "aharvey@php.net" + }, + { + "name": "Bernhard Schussek", + "email": "bschussek@gmail.com" } ], - "description": "Collection of value objects that represent the PHP code units", - "homepage": "https://github.com/sebastianbergmann/code-unit", + "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/code-unit/issues", - "source": "https://github.com/sebastianbergmann/code-unit/tree/2.0.0" + "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" }, "funding": [ { @@ -5229,32 +5127,35 @@ "type": "github" } ], - "time": "2023-02-03T06:58:43+00:00" + "time": "2024-03-02T07:17:12+00:00" }, { - "name": "sebastian/code-unit-reverse-lookup", - "version": "3.0.0", + "name": "sebastian/global-state", + "version": "6.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/code-unit-reverse-lookup.git", - "reference": "5e3a687f7d8ae33fb362c5c0743794bbb2420a1d" + "url": "https://github.com/sebastianbergmann/global-state.git", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" }, "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/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { + "ext-dom": "*", "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "3.0-dev" + "dev-main": "6.0-dev" } }, "autoload": { @@ -5272,11 +5173,15 @@ "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": "Snapshotting of global state", + "homepage": "https://www.github.com/sebastianbergmann/global-state", + "keywords": [ + "global state" + ], "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/global-state/issues", + "security": "https://github.com/sebastianbergmann/global-state/security/policy", + "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" }, "funding": [ { @@ -5284,36 +5189,33 @@ "type": "github" } ], - "time": "2023-02-03T06:59:15+00:00" + "time": "2024-03-02T07:19:19+00:00" }, { - "name": "sebastian/comparator", - "version": "5.0.3", + "name": "sebastian/lines-of-code", + "version": "2.0.2", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/comparator.git", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e" + "url": "https://github.com/sebastianbergmann/lines-of-code.git", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", - "reference": "a18251eb0b7a2dcd2f7aa3d6078b18545ef0558e", + "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", + "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", "shasum": "" }, "require": { - "ext-dom": "*", - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/diff": "^5.0", - "sebastian/exporter": "^5.0" + "nikic/php-parser": "^4.18 || ^5.0", + "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.5" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.0-dev" + "dev-main": "2.0-dev" } }, "autoload": { @@ -5328,32 +5230,16 @@ "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" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to compare PHP values for equality", - "homepage": "https://github.com/sebastianbergmann/comparator", - "keywords": [ - "comparator", - "compare", - "equality" - ], + "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/comparator/issues", - "security": "https://github.com/sebastianbergmann/comparator/security/policy", - "source": "https://github.com/sebastianbergmann/comparator/tree/5.0.3" + "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" }, "funding": [ { @@ -5361,25 +5247,26 @@ "type": "github" } ], - "time": "2024-10-18T14:56:07+00:00" + "time": "2023-12-21T08:38:20+00:00" }, { - "name": "sebastian/complexity", - "version": "3.2.0", + "name": "sebastian/object-enumerator", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/complexity.git", - "reference": "68ff824baeae169ec9f2137158ee529584553799" + "url": "https://github.com/sebastianbergmann/object-enumerator.git", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/68ff824baeae169ec9f2137158ee529584553799", - "reference": "68ff824baeae169ec9f2137158ee529584553799", + "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", + "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "php": ">=8.1", + "sebastian/object-reflector": "^3.0", + "sebastian/recursion-context": "^5.0" }, "require-dev": { "phpunit/phpunit": "^10.0" @@ -5387,7 +5274,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.2-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -5402,16 +5289,14 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "email": "sebastian@phpunit.de" } ], - "description": "Library for calculating the complexity of PHP code units", - "homepage": "https://github.com/sebastianbergmann/complexity", + "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/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/object-enumerator/issues", + "source": "https://github.com/sebastianbergmann/object-enumerator/tree/5.0.0" }, "funding": [ { @@ -5419,33 +5304,32 @@ "type": "github" } ], - "time": "2023-12-21T08:37:17+00:00" + "time": "2023-02-03T07:08:32+00:00" }, { - "name": "sebastian/diff", - "version": "5.1.1", + "name": "sebastian/object-reflector", + "version": "3.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e" + "url": "https://github.com/sebastianbergmann/object-reflector.git", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e", - "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e", + "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", + "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", "shasum": "" }, "require": { "php": ">=8.1" }, "require-dev": { - "phpunit/phpunit": "^10.0", - "symfony/process": "^6.4" + "phpunit/phpunit": "^10.0" }, "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "3.0-dev" } }, "autoload": { @@ -5461,24 +5345,13 @@ { "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" - ], + "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/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/object-reflector/issues", + "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" }, "funding": [ { @@ -5486,20 +5359,20 @@ "type": "github" } ], - "time": "2024-03-02T07:15:17+00:00" + "time": "2023-02-03T07:06:18+00:00" }, { - "name": "sebastian/environment", - "version": "6.1.0", + "name": "sebastian/recursion-context", + "version": "5.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/environment.git", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984" + "url": "https://github.com/sebastianbergmann/recursion-context.git", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/8074dbcd93529b357029f5cc5058fd3e43666984", - "reference": "8074dbcd93529b357029f5cc5058fd3e43666984", + "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", + "reference": "05909fb5bc7df4c52992396d0116aed689f93712", "shasum": "" }, "require": { @@ -5508,13 +5381,10 @@ "require-dev": { "phpunit/phpunit": "^10.0" }, - "suggest": { - "ext-posix": "*" - }, "type": "library", "extra": { "branch-alias": { - "dev-main": "6.1-dev" + "dev-main": "5.0-dev" } }, "autoload": { @@ -5530,19 +5400,21 @@ { "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 handle HHVM/PHP environments", - "homepage": "https://github.com/sebastianbergmann/environment", - "keywords": [ - "Xdebug", - "environment", - "hhvm" - ], + "description": "Provides functionality to recursively process PHP variables", + "homepage": "https://github.com/sebastianbergmann/recursion-context", "support": { - "issues": "https://github.com/sebastianbergmann/environment/issues", - "security": "https://github.com/sebastianbergmann/environment/security/policy", - "source": "https://github.com/sebastianbergmann/environment/tree/6.1.0" + "issues": "https://github.com/sebastianbergmann/recursion-context/issues", + "source": "https://github.com/sebastianbergmann/recursion-context/tree/5.0.0" }, "funding": [ { @@ -5550,26 +5422,24 @@ "type": "github" } ], - "time": "2024-03-23T08:47:14+00:00" + "time": "2023-02-03T07:05:40+00:00" }, { - "name": "sebastian/exporter", - "version": "5.1.2", + "name": "sebastian/type", + "version": "4.0.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/exporter.git", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf" + "url": "https://github.com/sebastianbergmann/type.git", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/955288482d97c19a372d3f31006ab3f37da47adf", - "reference": "955288482d97c19a372d3f31006ab3f37da47adf", + "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", + "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", "shasum": "" }, "require": { - "ext-mbstring": "*", - "php": ">=8.1", - "sebastian/recursion-context": "^5.0" + "php": ">=8.1" }, "require-dev": { "phpunit/phpunit": "^10.0" @@ -5577,7 +5447,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "5.1-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -5592,35 +5462,15 @@ "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" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Provides the functionality to export PHP variables for visualization", - "homepage": "https://www.github.com/sebastianbergmann/exporter", - "keywords": [ - "export", - "exporter" - ], + "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/exporter/issues", - "security": "https://github.com/sebastianbergmann/exporter/security/policy", - "source": "https://github.com/sebastianbergmann/exporter/tree/5.1.2" + "issues": "https://github.com/sebastianbergmann/type/issues", + "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" }, "funding": [ { @@ -5628,35 +5478,29 @@ "type": "github" } ], - "time": "2024-03-02T07:17:12+00:00" + "time": "2023-02-03T07:10:45+00:00" }, { - "name": "sebastian/global-state", - "version": "6.0.2", + "name": "sebastian/version", + "version": "4.0.1", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/global-state.git", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9" + "url": "https://github.com/sebastianbergmann/version.git", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", - "reference": "987bafff24ecc4c9ac418cab1145b96dd6e9cbd9", + "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", "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": { "branch-alias": { - "dev-main": "6.0-dev" + "dev-main": "4.0-dev" } }, "autoload": { @@ -5671,18 +5515,15 @@ "authors": [ { "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de" + "email": "sebastian@phpunit.de", + "role": "lead" } ], - "description": "Snapshotting of global state", - "homepage": "https://www.github.com/sebastianbergmann/global-state", - "keywords": [ - "global state" - ], + "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/global-state/issues", - "security": "https://github.com/sebastianbergmann/global-state/security/policy", - "source": "https://github.com/sebastianbergmann/global-state/tree/6.0.2" + "issues": "https://github.com/sebastianbergmann/version/issues", + "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" }, "funding": [ { @@ -5690,379 +5531,478 @@ "type": "github" } ], - "time": "2024-03-02T07:19:19+00:00" + "time": "2023-02-07T11:34:05+00:00" }, { - "name": "sebastian/lines-of-code", - "version": "2.0.2", + "name": "seld/jsonlint", + "version": "1.11.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/lines-of-code.git", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0" + "url": "https://github.com/Seldaek/jsonlint.git", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/856e7f6a75a84e339195d48c556f23be2ebf75d0", - "reference": "856e7f6a75a84e339195d48c556f23be2ebf75d0", + "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", "shasum": "" }, "require": { - "nikic/php-parser": "^4.18 || ^5.0", - "php": ">=8.1" + "php": "^5.3 || ^7.0 || ^8.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "phpstan/phpstan": "^1.11", + "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" }, + "bin": [ + "bin/jsonlint" + ], "type": "library", - "extra": { - "branch-alias": { - "dev-main": "2.0-dev" - } - }, "autoload": { - "classmap": [ - "src/" - ] + "psr-4": { + "Seld\\JsonLint\\": "src/Seld/JsonLint/" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ - "BSD-3-Clause" + "MIT" ], "authors": [ { - "name": "Sebastian Bergmann", - "email": "sebastian@phpunit.de", - "role": "lead" + "name": "Jordi Boggiano", + "email": "j.boggiano@seld.be", + "homepage": "https://seld.be" } ], - "description": "Library for counting the lines of code in PHP source code", - "homepage": "https://github.com/sebastianbergmann/lines-of-code", + "description": "JSON Linter", + "keywords": [ + "json", + "linter", + "parser", + "validator" + ], "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" + "issues": "https://github.com/Seldaek/jsonlint/issues", + "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" }, "funding": [ { - "url": "https://github.com/sebastianbergmann", + "url": "https://github.com/Seldaek", "type": "github" + }, + { + "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "type": "tidelift" } ], - "time": "2023-12-21T08:38:20+00:00" + "time": "2024-07-11T14:55:45+00:00" }, { - "name": "sebastian/object-enumerator", - "version": "5.0.0", + "name": "symfony/console", + "version": "v6.4.22", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-enumerator.git", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906" + "url": "https://github.com/symfony/console.git", + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/202d0e344a580d7f7d04b3fafce6933e59dae906", - "reference": "202d0e344a580d7f7d04b3fafce6933e59dae906", + "url": "https://api.github.com/repos/symfony/console/zipball/7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", + "reference": "7d29659bc3c9d8e9a34e2c3414ef9e9e003e6cf3", "shasum": "" }, "require": { "php": ">=8.1", - "sebastian/object-reflector": "^3.0", - "sebastian/recursion-context": "^5.0" + "symfony/deprecation-contracts": "^2.5|^3", + "symfony/polyfill-mbstring": "~1.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/string": "^5.4|^6.0|^7.0" + }, + "conflict": { + "symfony/dependency-injection": "<5.4", + "symfony/dotenv": "<5.4", + "symfony/event-dispatcher": "<5.4", + "symfony/lock": "<5.4", + "symfony/process": "<5.4" + }, + "provide": { + "psr/log-implementation": "1.0|2.0|3.0" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "psr/log": "^1|^2|^3", + "symfony/config": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/event-dispatcher": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^6.4|^7.0", + "symfony/http-kernel": "^6.4|^7.0", + "symfony/lock": "^5.4|^6.0|^7.0", + "symfony/messenger": "^5.4|^6.0|^7.0", + "symfony/process": "^5.4|^6.0|^7.0", + "symfony/stopwatch": "^5.4|^6.0|^7.0", + "symfony/var-dumper": "^5.4|^6.0|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "5.0-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": "Fabien Potencier", + "email": "fabien@symfony.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": "Eases the creation of beautiful and testable command line interfaces", + "homepage": "https://symfony.com", + "keywords": [ + "cli", + "command-line", + "console", + "terminal" + ], "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/console/tree/v6.4.22" }, "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": "2025-05-07T07:05:04+00:00" }, { - "name": "sebastian/object-reflector", - "version": "3.0.0", + "name": "symfony/event-dispatcher", + "version": "v6.4.13", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/object-reflector.git", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957" + "url": "https://github.com/symfony/event-dispatcher.git", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/24ed13d98130f0e7122df55d06c5c4942a577957", - "reference": "24ed13d98130f0e7122df55d06c5c4942a577957", + "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/event-dispatcher-contracts": "^2.5|^3" + }, + "conflict": { + "symfony/dependency-injection": "<5.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": "^5.4|^6.0|^7.0", + "symfony/dependency-injection": "^5.4|^6.0|^7.0", + "symfony/error-handler": "^5.4|^6.0|^7.0", + "symfony/expression-language": "^5.4|^6.0|^7.0", + "symfony/http-foundation": "^5.4|^6.0|^7.0", + "symfony/service-contracts": "^2.5|^3", + "symfony/stopwatch": "^5.4|^6.0|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "3.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" + "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 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/object-reflector/issues", - "source": "https://github.com/sebastianbergmann/object-reflector/tree/3.0.0" + "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" }, "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:06:18+00:00" + "time": "2024-09-25T14:18:03+00:00" }, { - "name": "sebastian/recursion-context", - "version": "5.0.0", + "name": "symfony/event-dispatcher-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/recursion-context.git", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712" + "url": "https://github.com/symfony/event-dispatcher-contracts.git", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/05909fb5bc7df4c52992396d0116aed689f93712", - "reference": "05909fb5bc7df4c52992396d0116aed689f93712", + "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", + "reference": "59eb412e93815df44f05f342958efa9f46b1e586", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "phpunit/phpunit": "^10.0" + "php": ">=8.1", + "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": "Jeff Welch", - "email": "whatthejeff@gmail.com" + "name": "Nicolas Grekas", + "email": "p@tchwork.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": "Generic abstractions related to dispatching event", + "homepage": "https://symfony.com", + "keywords": [ + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" + ], "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/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:05:40+00:00" + "time": "2024-09-25T14:21:43+00:00" }, { - "name": "sebastian/type", - "version": "4.0.0", + "name": "symfony/filesystem", + "version": "v6.4.13", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/type.git", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf" + "url": "https://github.com/symfony/filesystem.git", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/462699a16464c3944eefc02ebdd77882bd3925bf", - "reference": "462699a16464c3944eefc02ebdd77882bd3925bf", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", + "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/polyfill-ctype": "~1.8", + "symfony/polyfill-mbstring": "~1.8" }, "require-dev": { - "phpunit/phpunit": "^10.0" + "symfony/process": "^5.4|^6.4|^7.0" }, "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.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", - "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 basic utilities for the filesystem", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/type/issues", - "source": "https://github.com/sebastianbergmann/type/tree/4.0.0" + "source": "https://github.com/symfony/filesystem/tree/v6.4.13" }, "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:10:45+00:00" + "time": "2024-10-25T15:07:50+00:00" }, { - "name": "sebastian/version", - "version": "4.0.1", + "name": "symfony/finder", + "version": "v6.4.17", "source": { "type": "git", - "url": "https://github.com/sebastianbergmann/version.git", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17" + "url": "https://github.com/symfony/finder.git", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/c51fa83a5d8f43f1402e3f32a005e6262244ef17", - "reference": "c51fa83a5d8f43f1402e3f32a005e6262244ef17", + "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", "shasum": "" }, "require": { "php": ">=8.1" }, - "type": "library", - "extra": { - "branch-alias": { - "dev-main": "4.0-dev" - } + "require-dev": { + "symfony/filesystem": "^6.0|^7.0" }, + "type": "library", "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", - "role": "lead" + "name": "Fabien Potencier", + "email": "fabien@symfony.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": "Finds files and directories via an intuitive fluent interface", + "homepage": "https://symfony.com", "support": { - "issues": "https://github.com/sebastianbergmann/version/issues", - "source": "https://github.com/sebastianbergmann/version/tree/4.0.1" + "source": "https://github.com/symfony/finder/tree/v6.4.17" }, "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-07T11:34:05+00:00" + "time": "2024-12-29T13:51:37+00:00" }, { - "name": "seld/jsonlint", - "version": "1.11.0", + "name": "symfony/options-resolver", + "version": "v6.4.16", "source": { "type": "git", - "url": "https://github.com/Seldaek/jsonlint.git", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2" + "url": "https://github.com/symfony/options-resolver.git", + "reference": "368128ad168f20e22c32159b9f761e456cec0c78" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Seldaek/jsonlint/zipball/1748aaf847fc731cfad7725aec413ee46f0cc3a2", - "reference": "1748aaf847fc731cfad7725aec413ee46f0cc3a2", + "url": "https://api.github.com/repos/symfony/options-resolver/zipball/368128ad168f20e22c32159b9f761e456cec0c78", + "reference": "368128ad168f20e22c32159b9f761e456cec0c78", "shasum": "" }, "require": { - "php": "^5.3 || ^7.0 || ^8.0" - }, - "require-dev": { - "phpstan/phpstan": "^1.11", - "phpunit/phpunit": "^4.8.35 || ^5.7 || ^6.0 || ^8.5.13" + "php": ">=8.1", + "symfony/deprecation-contracts": "^2.5|^3" }, - "bin": [ - "bin/jsonlint" - ], "type": "library", "autoload": { "psr-4": { - "Seld\\JsonLint\\": "src/Seld/JsonLint/" - } + "Symfony\\Component\\OptionsResolver\\": "" + }, + "exclude-from-classmap": [ + "/Tests/" + ] }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6070,78 +6010,77 @@ ], "authors": [ { - "name": "Jordi Boggiano", - "email": "j.boggiano@seld.be", - "homepage": "https://seld.be" + "name": "Fabien Potencier", + "email": "fabien@symfony.com" + }, + { + "name": "Symfony Community", + "homepage": "https://symfony.com/contributors" } ], - "description": "JSON Linter", + "description": "Provides an improved replacement for the array_replace PHP function", + "homepage": "https://symfony.com", "keywords": [ - "json", - "linter", - "parser", - "validator" + "config", + "configuration", + "options" ], "support": { - "issues": "https://github.com/Seldaek/jsonlint/issues", - "source": "https://github.com/Seldaek/jsonlint/tree/1.11.0" + "source": "https://github.com/symfony/options-resolver/tree/v6.4.16" }, "funding": [ { - "url": "https://github.com/Seldaek", + "url": "https://symfony.com/sponsor", + "type": "custom" + }, + { + "url": "https://github.com/fabpot", "type": "github" }, { - "url": "https://tidelift.com/funding/github/packagist/seld/jsonlint", + "url": "https://tidelift.com/funding/github/packagist/symfony/symfony", "type": "tidelift" } ], - "time": "2024-07-11T14:55:45+00:00" + "time": "2024-11-20T10:57:02+00:00" }, { - "name": "symfony/event-dispatcher", - "version": "v6.4.13", + "name": "symfony/polyfill-ctype", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher.git", - "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e" + "url": "https://github.com/symfony/polyfill-ctype.git", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", - "reference": "0ffc48080ab3e9132ea74ef4e09d8dcf26bf897e", + "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/a3cc8b044a6ea513310cbd48ef7333b384945638", + "reference": "a3cc8b044a6ea513310cbd48ef7333b384945638", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/event-dispatcher-contracts": "^2.5|^3" - }, - "conflict": { - "symfony/dependency-injection": "<5.4", - "symfony/service-contracts": "<2.5" + "php": ">=7.2" }, "provide": { - "psr/event-dispatcher-implementation": "1.0", - "symfony/event-dispatcher-implementation": "2.0|3.0" + "ext-ctype": "*" }, - "require-dev": { - "psr/log": "^1|^2|^3", - "symfony/config": "^5.4|^6.0|^7.0", - "symfony/dependency-injection": "^5.4|^6.0|^7.0", - "symfony/error-handler": "^5.4|^6.0|^7.0", - "symfony/expression-language": "^5.4|^6.0|^7.0", - "symfony/http-foundation": "^5.4|^6.0|^7.0", - "symfony/service-contracts": "^2.5|^3", - "symfony/stopwatch": "^5.4|^6.0|^7.0" + "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\\Component\\EventDispatcher\\": "" - }, - "exclude-from-classmap": [ - "/Tests/" - ] + "Symfony\\Polyfill\\Ctype\\": "" + } }, "notification-url": "https://packagist.org/downloads/", "license": [ @@ -6149,18 +6088,24 @@ ], "authors": [ { - "name": "Fabien Potencier", - "email": "fabien@symfony.com" + "name": "Gert de Pagter", + "email": "BackEndTea@gmail.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", + "description": "Symfony polyfill for ctype functions", "homepage": "https://symfony.com", + "keywords": [ + "compatibility", + "ctype", + "polyfill", + "portable" + ], "support": { - "source": "https://github.com/symfony/event-dispatcher/tree/v6.4.13" + "source": "https://github.com/symfony/polyfill-ctype/tree/v1.32.0" }, "funding": [ { @@ -6176,39 +6121,41 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:18:03+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/event-dispatcher-contracts", - "version": "v3.6.0", + "name": "symfony/polyfill-intl-grapheme", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/event-dispatcher-contracts.git", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586" + "url": "https://github.com/symfony/polyfill-intl-grapheme.git", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/event-dispatcher-contracts/zipball/59eb412e93815df44f05f342958efa9f46b1e586", - "reference": "59eb412e93815df44f05f342958efa9f46b1e586", + "url": "https://api.github.com/repos/symfony/polyfill-intl-grapheme/zipball/b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", + "reference": "b9123926e3b7bc2f98c02ad54f6a4b02b91a8abe", "shasum": "" }, "require": { - "php": ">=8.1", - "psr/event-dispatcher": "^1" + "php": ">=7.2" + }, + "suggest": { + "ext-intl": "For best performance" }, "type": "library", "extra": { "thanks": { - "url": "https://github.com/symfony/contracts", - "name": "symfony/contracts" - }, - "branch-alias": { - "dev-main": "3.6-dev" + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" } }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Contracts\\EventDispatcher\\": "" + "Symfony\\Polyfill\\Intl\\Grapheme\\": "" } }, "notification-url": "https://packagist.org/downloads/", @@ -6225,18 +6172,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Generic abstractions related to dispatching event", + "description": "Symfony polyfill for intl's grapheme_* functions", "homepage": "https://symfony.com", "keywords": [ - "abstractions", - "contracts", - "decoupling", - "interfaces", - "interoperability", - "standards" + "compatibility", + "grapheme", + "intl", + "polyfill", + "portable", + "shim" ], "support": { - "source": "https://github.com/symfony/event-dispatcher-contracts/tree/v3.6.0" + "source": "https://github.com/symfony/polyfill-intl-grapheme/tree/v1.32.0" }, "funding": [ { @@ -6252,37 +6199,44 @@ "type": "tidelift" } ], - "time": "2024-09-25T14:21:43+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/filesystem", - "version": "v6.4.13", + "name": "symfony/polyfill-intl-normalizer", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/filesystem.git", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3" + "url": "https://github.com/symfony/polyfill-intl-normalizer.git", + "reference": "3833d7255cc303546435cb650316bff708a1c75c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/4856c9cf585d5a0313d8d35afd681a526f038dd3", - "reference": "4856c9cf585d5a0313d8d35afd681a526f038dd3", + "url": "https://api.github.com/repos/symfony/polyfill-intl-normalizer/zipball/3833d7255cc303546435cb650316bff708a1c75c", + "reference": "3833d7255cc303546435cb650316bff708a1c75c", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/polyfill-ctype": "~1.8", - "symfony/polyfill-mbstring": "~1.8" + "php": ">=7.2" }, - "require-dev": { - "symfony/process": "^5.4|^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\\Filesystem\\": "" + "Symfony\\Polyfill\\Intl\\Normalizer\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6291,18 +6245,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": "Provides basic utilities for the filesystem", + "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/filesystem/tree/v6.4.13" + "source": "https://github.com/symfony/polyfill-intl-normalizer/tree/v1.32.0" }, "funding": [ { @@ -6318,35 +6280,41 @@ "type": "tidelift" } ], - "time": "2024-10-25T15:07:50+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/finder", - "version": "v6.4.17", + "name": "symfony/polyfill-php81", + "version": "v1.32.0", "source": { "type": "git", - "url": "https://github.com/symfony/finder.git", - "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7" + "url": "https://github.com/symfony/polyfill-php81.git", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/finder/zipball/1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", - "reference": "1d0e8266248c5d9ab6a87e3789e6dc482af3c9c7", + "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", "shasum": "" }, "require": { - "php": ">=8.1" - }, - "require-dev": { - "symfony/filesystem": "^6.0|^7.0" + "php": ">=7.2" }, "type": "library", + "extra": { + "thanks": { + "url": "https://github.com/symfony/polyfill", + "name": "symfony/polyfill" + } + }, "autoload": { + "files": [ + "bootstrap.php" + ], "psr-4": { - "Symfony\\Component\\Finder\\": "" + "Symfony\\Polyfill\\Php81\\": "" }, - "exclude-from-classmap": [ - "/Tests/" + "classmap": [ + "Resources/stubs" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6355,18 +6323,24 @@ ], "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 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/finder/tree/v6.4.17" + "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" }, "funding": [ { @@ -6382,30 +6356,29 @@ "type": "tidelift" } ], - "time": "2024-12-29T13:51:37+00:00" + "time": "2024-09-09T11:45:10+00:00" }, { - "name": "symfony/options-resolver", - "version": "v6.4.16", + "name": "symfony/process", + "version": "v6.4.20", "source": { "type": "git", - "url": "https://github.com/symfony/options-resolver.git", - "reference": "368128ad168f20e22c32159b9f761e456cec0c78" + "url": "https://github.com/symfony/process.git", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/options-resolver/zipball/368128ad168f20e22c32159b9f761e456cec0c78", - "reference": "368128ad168f20e22c32159b9f761e456cec0c78", + "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", + "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", "shasum": "" }, "require": { - "php": ">=8.1", - "symfony/deprecation-contracts": "^2.5|^3" + "php": ">=8.1" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\OptionsResolver\\": "" + "Symfony\\Component\\Process\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6425,15 +6398,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Provides an improved replacement for the array_replace PHP function", + "description": "Executes commands in sub-processes", "homepage": "https://symfony.com", - "keywords": [ - "config", - "configuration", - "options" - ], "support": { - "source": "https://github.com/symfony/options-resolver/tree/v6.4.16" + "source": "https://github.com/symfony/process/tree/v6.4.20" }, "funding": [ { @@ -6449,41 +6417,46 @@ "type": "tidelift" } ], - "time": "2024-11-20T10:57:02+00:00" + "time": "2025-03-10T17:11:00+00:00" }, { - "name": "symfony/polyfill-php81", - "version": "v1.32.0", + "name": "symfony/service-contracts", + "version": "v3.6.0", "source": { "type": "git", - "url": "https://github.com/symfony/polyfill-php81.git", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c" + "url": "https://github.com/symfony/service-contracts.git", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/polyfill-php81/zipball/4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", - "reference": "4a4cfc2d253c21a5ad0e53071df248ed48c6ce5c", + "url": "https://api.github.com/repos/symfony/service-contracts/zipball/f021b05a130d35510bd6b25fe9053c2a8a15d5d4", + "reference": "f021b05a130d35510bd6b25fe9053c2a8a15d5d4", "shasum": "" }, "require": { - "php": ">=7.2" + "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/polyfill", - "name": "symfony/polyfill" + "url": "https://github.com/symfony/contracts", + "name": "symfony/contracts" + }, + "branch-alias": { + "dev-main": "3.6-dev" } }, "autoload": { - "files": [ - "bootstrap.php" - ], "psr-4": { - "Symfony\\Polyfill\\Php81\\": "" + "Symfony\\Contracts\\Service\\": "" }, - "classmap": [ - "Resources/stubs" + "exclude-from-classmap": [ + "/Test/" ] }, "notification-url": "https://packagist.org/downloads/", @@ -6500,16 +6473,18 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Symfony polyfill backporting some PHP 8.1+ features to lower PHP versions", + "description": "Generic abstractions related to writing services", "homepage": "https://symfony.com", "keywords": [ - "compatibility", - "polyfill", - "portable", - "shim" + "abstractions", + "contracts", + "decoupling", + "interfaces", + "interoperability", + "standards" ], "support": { - "source": "https://github.com/symfony/polyfill-php81/tree/v1.32.0" + "source": "https://github.com/symfony/service-contracts/tree/v3.6.0" }, "funding": [ { @@ -6525,29 +6500,30 @@ "type": "tidelift" } ], - "time": "2024-09-09T11:45:10+00:00" + "time": "2025-04-25T09:37:31+00:00" }, { - "name": "symfony/process", - "version": "v6.4.20", + "name": "symfony/stopwatch", + "version": "v6.4.19", "source": { "type": "git", - "url": "https://github.com/symfony/process.git", - "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20" + "url": "https://github.com/symfony/stopwatch.git", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/process/zipball/e2a61c16af36c9a07e5c9906498b73e091949a20", - "reference": "e2a61c16af36c9a07e5c9906498b73e091949a20", + "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", "shasum": "" }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "symfony/service-contracts": "^2.5|^3" }, "type": "library", "autoload": { "psr-4": { - "Symfony\\Component\\Process\\": "" + "Symfony\\Component\\Stopwatch\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6567,10 +6543,10 @@ "homepage": "https://symfony.com/contributors" } ], - "description": "Executes commands in sub-processes", + "description": "Provides a way to profile code", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/process/tree/v6.4.20" + "source": "https://github.com/symfony/stopwatch/tree/v6.4.19" }, "funding": [ { @@ -6586,30 +6562,46 @@ "type": "tidelift" } ], - "time": "2025-03-10T17:11:00+00:00" + "time": "2025-02-21T10:06:30+00:00" }, { - "name": "symfony/stopwatch", - "version": "v6.4.19", + "name": "symfony/string", + "version": "v6.4.21", "source": { "type": "git", - "url": "https://github.com/symfony/stopwatch.git", - "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c" + "url": "https://github.com/symfony/string.git", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/stopwatch/zipball/dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", - "reference": "dfe1481c12c06266d0c3d58c0cb4b09bd497ab9c", + "url": "https://api.github.com/repos/symfony/string/zipball/73e2c6966a5aef1d4892873ed5322245295370c6", + "reference": "73e2c6966a5aef1d4892873ed5322245295370c6", "shasum": "" }, "require": { "php": ">=8.1", - "symfony/service-contracts": "^2.5|^3" + "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/error-handler": "^5.4|^6.0|^7.0", + "symfony/http-client": "^5.4|^6.0|^7.0", + "symfony/intl": "^6.2|^7.0", + "symfony/translation-contracts": "^2.5|^3.0", + "symfony/var-exporter": "^5.4|^6.0|^7.0" }, "type": "library", "autoload": { + "files": [ + "Resources/functions.php" + ], "psr-4": { - "Symfony\\Component\\Stopwatch\\": "" + "Symfony\\Component\\String\\": "" }, "exclude-from-classmap": [ "/Tests/" @@ -6621,18 +6613,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": "Provides a way to profile code", + "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/stopwatch/tree/v6.4.19" + "source": "https://github.com/symfony/string/tree/v6.4.21" }, "funding": [ { @@ -6648,7 +6648,7 @@ "type": "tidelift" } ], - "time": "2025-02-21T10:06:30+00:00" + "time": "2025-04-18T15:23:29+00:00" }, { "name": "ta-tikoma/phpunit-architecture-test", From ed6790537f0977a550f7b2198f6257dac10ee926 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Wed, 25 Jun 2025 11:54:13 +0800 Subject: [PATCH 46/59] fix docs --- .openapi.php | 42 +++++++++-- src/Annotations/Input/InputDateFormat.php | 1 - src/Annotations/Output/OutputDateFormat.php | 1 - .../{ => InputValue}/InputConstructCast.php | 8 +- src/Enums/TypeKindEnum.php | 1 - src/Faker/FakerResolver.php | 2 +- src/OpenApi.php | 4 +- src/OpenApi/Annotations/OpenApi.php | 1 - src/OpenApi/Annotations/RequestBody.php | 2 +- src/OpenApi/Annotations/Response.php | 2 +- src/OpenApi/Annotations/Tag.php | 2 - src/OpenApi/Collections/OpenApiCollection.php | 46 ++++++------ .../Collections/ParameterCollection.php | 3 +- src/OpenApi/Enum/MethodEnum.php | 8 +- src/OpenApi/Enum/ParameterTypeEnum.php | 66 ++++++++--------- src/OpenApi/Handler/Config.php | 31 ++++---- src/OpenApi/Handler/HandleInterface.php | 1 - src/OpenApi/Handler/Handler.php | 73 +++++++++---------- src/OpenApi/Storage/OpenAPI/Method/Method.php | 1 - src/OpenApi/Storage/OpenAPI/OpenAPI.php | 3 - .../Storage/OpenAPI/RequestBodyStorage.php | 2 +- .../Storage/OpenAPI/ResponseStorage.php | 2 +- src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 40 +++++----- .../Storage/OpenAPI/ServersStorage.php | 2 +- src/OpenApi/Web/Index.php | 8 +- src/Resolvers/InputResolver.php | 2 +- src/SerializeContainer.php | 2 +- tests/Openapi/AnnotationOpenApiTest.php | 12 +-- tests/Openapi/EnumOpenApiTest.php | 30 ++++---- tests/Openapi/OpenApiTest.php | 13 ++-- tests/Openapi/RouteOrewriteOpenApiTest.php | 10 +-- .../InputIgnoreGroupFromSerializeTest.php | 18 ++--- .../From/InputNameFromSerializeTest.php | 4 +- 33 files changed, 225 insertions(+), 218 deletions(-) rename src/Casts/{ => InputValue}/InputConstructCast.php (74%) diff --git a/.openapi.php b/.openapi.php index 9b018aa..0fa1c10 100644 --- a/.openapi.php +++ b/.openapi.php @@ -3,21 +3,47 @@ use Astral\Serialize\OpenApi\Storage\OpenAPI\ServersStorage; return [ + // API Document Title 'title' => 'API Docs', + // Description of the API document 'description' => 'API Docs description.', /** - * 向全局头部参数存储中添加一个的头部参数。 - * @param string $name - * @param string $example - * @param string $description + * Global headers to be added to every request. + * Each header should include name, example, and description. + * + * Example: + * [ + * 'name' => 'Authorization', + * 'example' => 'Bearer token', + * 'description' => 'Authentication token' + * ] */ - 'headers' => [ - - ], + 'headers' => [], + /** + * Service base URLs (servers). + * You can define multiple environments like production, staging, etc. + * + * @type ServersStorage[] $service + */ 'service' => [ - new ServersStorage('http://127.0.0.1','默认环境'), + new ServersStorage('http://127.0.0.1', 'Default Environment'), ], + + /** + * Directories to exclude from scanning. + * These paths are relative to the project root directory. + * + * Default excluded directories: + * - /vendor + * - /tests + * - /migrations + * Example: + * ['/sdk', '/app'] + * + * You can override or extend this list in your config file. + */ + 'exclude_dirs' => [], ]; diff --git a/src/Annotations/Input/InputDateFormat.php b/src/Annotations/Input/InputDateFormat.php index 2b7bc53..41e4585 100755 --- a/src/Annotations/Input/InputDateFormat.php +++ b/src/Annotations/Input/InputDateFormat.php @@ -28,7 +28,6 @@ public function match(mixed $value, DataCollection $collection, InputValueContex return is_string($value) || is_numeric($value); } - public function resolve(mixed $value, DataCollection $collection, InputValueContext $context): string|DateTime { diff --git a/src/Annotations/Output/OutputDateFormat.php b/src/Annotations/Output/OutputDateFormat.php index 965bdf9..efd51c2 100755 --- a/src/Annotations/Output/OutputDateFormat.php +++ b/src/Annotations/Output/OutputDateFormat.php @@ -30,7 +30,6 @@ public function match(mixed $value, DataCollection $collection, OutContext $cont return is_string($value) || is_numeric($value) || is_subclass_of($value, DateTimeInterface::class); } - public function resolve(mixed $value, DataCollection $collection, OutContext $context): string|DateTime|null { return $this->formatValue($value); diff --git a/src/Casts/InputConstructCast.php b/src/Casts/InputValue/InputConstructCast.php similarity index 74% rename from src/Casts/InputConstructCast.php rename to src/Casts/InputValue/InputConstructCast.php index 3589c40..9add2ea 100755 --- a/src/Casts/InputConstructCast.php +++ b/src/Casts/InputValue/InputConstructCast.php @@ -2,7 +2,7 @@ declare(strict_types=1); -namespace Astral\Serialize\Casts; +namespace Astral\Serialize\Casts\InputValue; use Astral\Serialize\Support\Collections\ConstructDataCollection; @@ -18,7 +18,11 @@ public function resolve(array $constructorParameters, object $object, ?array $re { $args = []; foreach ($constructorParameters as $param) { - $args[] = array_key_exists($param->name, $readonlyVols) ? $readonlyVols[$param->name] : $object->{$param->name}; + $value = array_key_exists($param->name, $readonlyVols) ? $readonlyVols[$param->name] : $object->{$param->name}; + if ($param->isNull === false && $value === null && $param->defaultValue !== null) { + $value = $param->defaultValue; + } + $args[] = $value; } $object->__construct(...$args); diff --git a/src/Enums/TypeKindEnum.php b/src/Enums/TypeKindEnum.php index 438a013..f1e4175 100644 --- a/src/Enums/TypeKindEnum.php +++ b/src/Enums/TypeKindEnum.php @@ -2,7 +2,6 @@ namespace Astral\Serialize\Enums; - use RuntimeException; enum TypeKindEnum diff --git a/src/Faker/FakerResolver.php b/src/Faker/FakerResolver.php index b0caee3..000a25f 100644 --- a/src/Faker/FakerResolver.php +++ b/src/Faker/FakerResolver.php @@ -2,7 +2,7 @@ namespace Astral\Serialize\Faker; -use Astral\Serialize\Casts\InputConstructCast; +use Astral\Serialize\Casts\InputValue\InputConstructCast; use Astral\Serialize\Faker\Rule\FakerDefaultRules; use Astral\Serialize\Resolvers\GroupResolver; use Astral\Serialize\Support\Collections\DataCollection; diff --git a/src/OpenApi.php b/src/OpenApi.php index 1fa4e92..3734db9 100755 --- a/src/OpenApi.php +++ b/src/OpenApi.php @@ -34,7 +34,7 @@ public function buildByClass(string $className): void $tagDoc = isset($tagDoc[0]) ? $tagDoc[0]->newInstance() : null; if ($tagDoc) { - self::$OpenAPI->addTag(new TagStorage($tagDoc->value, $tagDoc->description)); + self::$openAPI->addTag(new TagStorage($tagDoc->value, $tagDoc->description)); } foreach ($classRefection->getMethods() as $item) { @@ -79,7 +79,7 @@ public function buildByClass(string $className): void response: $instances[Response::class], ); - self::$OpenAPI->addPath($openApiCollection); + self::$openAPI->addPath($openApiCollection); } } } diff --git a/src/OpenApi/Annotations/OpenApi.php b/src/OpenApi/Annotations/OpenApi.php index d10c6a3..61ab6cc 100755 --- a/src/OpenApi/Annotations/OpenApi.php +++ b/src/OpenApi/Annotations/OpenApi.php @@ -9,7 +9,6 @@ #[Attribute(Attribute::TARGET_PROPERTY)] class OpenApi { - public function __construct( public string $description = '', public string $example = '', diff --git a/src/OpenApi/Annotations/RequestBody.php b/src/OpenApi/Annotations/RequestBody.php index 89e7ea4..7b6908b 100755 --- a/src/OpenApi/Annotations/RequestBody.php +++ b/src/OpenApi/Annotations/RequestBody.php @@ -15,6 +15,6 @@ public function __construct( public string $className = '', public ContentTypeEnum $contentType = ContentTypeEnum::JSON, public array|null $groups = null - ){ + ) { } } diff --git a/src/OpenApi/Annotations/Response.php b/src/OpenApi/Annotations/Response.php index 6f9376a..da8a1d7 100644 --- a/src/OpenApi/Annotations/Response.php +++ b/src/OpenApi/Annotations/Response.php @@ -14,6 +14,6 @@ public function __construct( public string $className, public ?int $code = 200, public ?array $groups = null, - ){ + ) { } } diff --git a/src/OpenApi/Annotations/Tag.php b/src/OpenApi/Annotations/Tag.php index 79769b1..3b0b1ba 100755 --- a/src/OpenApi/Annotations/Tag.php +++ b/src/OpenApi/Annotations/Tag.php @@ -4,7 +4,6 @@ namespace Astral\Serialize\OpenApi\Annotations; -use Astral\Serialize\OpenApi\Storage\OpenAPI\TagStorage; use Attribute; /** @@ -18,5 +17,4 @@ public function __construct( public string $description = '' ) { } - } diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index d33e3c9..acbe129 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -34,13 +34,13 @@ public function __construct( public array $attributes, public RequestBody|null $requestBody, public Response|null $response, - ){ + ) { } /** * @throws InvalidArgumentException */ - public function build() : Method + public function build(): Method { $methodClass = $this->route->method->value; /** @var Method $openAPIMethod */ @@ -61,7 +61,7 @@ public function build() : Method public function buildRequestBodyByAttribute(): RequestBodyStorage { $openAPIRequestBody = new RequestBodyStorage($this->requestBody->contentType); - $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($this->requestBody->className,$this->requestBody->groups)); + $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($this->requestBody->className, $this->requestBody->groups)); $openAPIRequestBody->withParameter($schemaStorage); return $openAPIRequestBody; } @@ -72,9 +72,9 @@ public function buildRequestBodyByAttribute(): RequestBodyStorage public function buildRequestBodyByParameters(): RequestBodyStorage { $openAPIRequestBody = new RequestBodyStorage(ContentTypeEnum::JSON); - $methodParam = $this->reflectionMethod->getParameters()[0] ?? null; - $type = $methodParam?->getType(); - $requestBodyClass = $type instanceof ReflectionNamedType ? $type->getName() : ''; + $methodParam = $this->reflectionMethod->getParameters()[0] ?? null; + $type = $methodParam?->getType(); + $requestBodyClass = $type instanceof ReflectionNamedType ? $type->getName() : ''; if (is_subclass_of($requestBodyClass, Serialize::class)) { $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($requestBodyClass)); $openAPIRequestBody->withParameter($schemaStorage); @@ -88,19 +88,19 @@ public function buildRequestBodyByParameters(): RequestBodyStorage */ public function buildResponse(): ResponseStorage { - $returnClass = $this->reflectionMethod->getReturnType(); - $returnClass = $returnClass instanceof ReflectionNamedType ? $returnClass->getName() : null; - $responseClass = match(true){ - $this->response !== null => $this->response->className, - $returnClass && is_subclass_of($returnClass,Serialize::class) => $returnClass, - default => null, + $returnClass = $this->reflectionMethod->getReturnType(); + $returnClass = $returnClass instanceof ReflectionNamedType ? $returnClass->getName() : null; + $responseClass = match(true) { + $this->response !== null => $this->response->className, + $returnClass && is_subclass_of($returnClass, Serialize::class) => $returnClass, + default => null, }; $responseStorage = new ResponseStorage(); - if($responseClass) { - $groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default']; + if ($responseClass) { + $groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default']; $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($responseClass, $groups)); $responseStorage->withParameter($schemaStorage); } @@ -121,10 +121,10 @@ public function buildParameterCollections(string $className, array $groups = ['d $properties = $serializeContext->getGroupCollection()->getProperties(); $vols = []; - foreach ($properties as $property){ + foreach ($properties as $property) { $vol = new ParameterCollection( className: $className, - name: current($property->getInputNamesByGroups($groups,$className)), + name: current($property->getInputNamesByGroups($groups, $className)), types: $property->getTypes(), type: ParameterTypeEnum::getByTypes($property->getTypes()), openApiAnnotation: $this->getOpenApiAnnotation($property->getAttributes()), @@ -132,9 +132,9 @@ className: $className, ignore: false, ); - if($property->getChildren()){ - foreach ($property->getChildren() as $children){ - $className = $children->getClassName(); + if ($property->getChildren()) { + foreach ($property->getChildren() as $children) { + $className = $children->getClassName(); $vol->children[$className] = $this->buildParameterCollections($className); } } @@ -147,12 +147,12 @@ className: $className, public function getOpenApiAnnotation(array $attributes): OpenApi|null { - foreach ($attributes as $attribute){ - if($attribute->getName() === OpenApi::class){ - return $attribute->newInstance(); + foreach ($attributes as $attribute) { + if ($attribute->getName() === OpenApi::class) { + return $attribute->newInstance(); } } return null; } -} \ No newline at end of file +} diff --git a/src/OpenApi/Collections/ParameterCollection.php b/src/OpenApi/Collections/ParameterCollection.php index 2e2b901..cc28975 100755 --- a/src/OpenApi/Collections/ParameterCollection.php +++ b/src/OpenApi/Collections/ParameterCollection.php @@ -21,7 +21,6 @@ public function __construct( public bool $ignore = false, /** @var array $children */ public array $children = [], - ){ + ) { } } - diff --git a/src/OpenApi/Enum/MethodEnum.php b/src/OpenApi/Enum/MethodEnum.php index e5a2156..59e2640 100755 --- a/src/OpenApi/Enum/MethodEnum.php +++ b/src/OpenApi/Enum/MethodEnum.php @@ -7,11 +7,11 @@ use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Post; use Astral\Serialize\OpenApi\Storage\OpenAPI\Method\Put; -enum MethodEnum:string +enum MethodEnum: string { - case POST = Post::class; - case GET = Get::class; - case PUT = Put::class; + case POST = Post::class; + case GET = Get::class; + case PUT = Put::class; case DELETE = Delete::class; } diff --git a/src/OpenApi/Enum/ParameterTypeEnum.php b/src/OpenApi/Enum/ParameterTypeEnum.php index 29bf76a..709d471 100755 --- a/src/OpenApi/Enum/ParameterTypeEnum.php +++ b/src/OpenApi/Enum/ParameterTypeEnum.php @@ -7,16 +7,15 @@ enum ParameterTypeEnum: string { - case ARRAY = 'array'; - case STRING = 'string'; - case OBJECT = 'object'; + case ARRAY = 'array'; + case STRING = 'string'; + case OBJECT = 'object'; case BOOLEAN = 'boolean'; case INTEGER = 'integer'; - case NUMBER = 'number'; - case ONE_OF = 'oneOf'; - case ANY_OF = 'anyOf'; - case ALL_OF = 'allOf'; - + case NUMBER = 'number'; + case ONE_OF = 'oneOf'; + case ANY_OF = 'anyOf'; + case ALL_OF = 'allOf'; public function isObject(): bool { @@ -35,12 +34,12 @@ public function isOf(): bool public static function getBaseEnumByTypeKindEnum(TypeCollection $collection): ?ParameterTypeEnum { - return match (true){ + return match (true) { $collection->kind === TypeKindEnum::STRING, $collection->kind === TypeKindEnum::ENUM => self::STRING, - $collection->kind === TypeKindEnum::INT => self::INTEGER, - $collection->kind === TypeKindEnum::FLOAT => self::NUMBER, + $collection->kind === TypeKindEnum::INT => self::INTEGER, + $collection->kind === TypeKindEnum::FLOAT => self::NUMBER, $collection->kind === TypeKindEnum::BOOLEAN => self::BOOLEAN, - default => null, + default => null, }; } @@ -49,16 +48,15 @@ public static function getBaseEnumByTypeKindEnum(TypeCollection $collection): ?P * @param string $className * @return ParameterTypeEnum|null */ - public static function getArrayAndObjectEnumBy(array $types, string $className): ?ParameterTypeEnum { - foreach ($types as $collection){ - if($className === $collection->className && in_array($collection->kind, [TypeKindEnum::CLASS_OBJECT, TypeKindEnum::OBJECT], true)){ + foreach ($types as $collection) { + if ($className === $collection->className && in_array($collection->kind, [TypeKindEnum::CLASS_OBJECT, TypeKindEnum::OBJECT], true)) { return self::OBJECT; } - if( $className === $collection->className && in_array($collection->kind, [TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true)){ + if ($className === $collection->className && in_array($collection->kind, [TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true)) { return self::ARRAY; } } @@ -68,9 +66,9 @@ public static function getArrayAndObjectEnumBy(array $types, string $className): public static function hasEnum(array $types): bool { - foreach ($types as $type){ - if($type->kind === TypeKindEnum::ENUM){ - return true; + foreach ($types as $type) { + if ($type->kind === TypeKindEnum::ENUM) { + return true; } } @@ -85,11 +83,11 @@ public static function getByTypes(array $types): ParameterTypeEnum $count = count($types); - if($count === 1){ + if ($count === 1) { $type = current($types)->kind; - return match (true){ - $type === TypeKindEnum::INT => self::INTEGER, - $type === TypeKindEnum::FLOAT => self::NUMBER, + return match (true) { + $type === TypeKindEnum::INT => self::INTEGER, + $type === TypeKindEnum::FLOAT => self::NUMBER, $type === TypeKindEnum::BOOLEAN => self::BOOLEAN, $type === TypeKindEnum::OBJECT, $type === TypeKindEnum::CLASS_OBJECT => self::OBJECT, $type === TypeKindEnum::ARRAY, $type === TypeKindEnum::COLLECT_SINGLE_OBJECT , $type === TypeKindEnum::COLLECT_UNION_OBJECT => self::ARRAY, @@ -97,29 +95,29 @@ public static function getByTypes(array $types): ParameterTypeEnum }; } - $hasUnion = false; + $hasUnion = false; $unionTypes = []; - foreach ($types as $type){ + foreach ($types as $type) { - if($type->kind === TypeKindEnum::ENUM){ + if ($type->kind === TypeKindEnum::ENUM) { $unionTypes[TypeKindEnum::STRING->name] = $type; - }else{ + } else { $unionTypes[$type->kind->name] = $type; } - if($type->kind === TypeKindEnum::COLLECT_UNION_OBJECT){ + if ($type->kind === TypeKindEnum::COLLECT_UNION_OBJECT) { $hasUnion = true; } } - if(!$hasUnion && count($unionTypes) === 1){ + if (!$hasUnion && count($unionTypes) === 1) { $type = current($types)->kind; - return match (true){ - $type === TypeKindEnum::INT => self::INTEGER, - $type === TypeKindEnum::FLOAT => self::NUMBER, - $type === TypeKindEnum::BOOLEAN => self::BOOLEAN, + return match (true) { + $type === TypeKindEnum::INT => self::INTEGER, + $type === TypeKindEnum::FLOAT => self::NUMBER, + $type === TypeKindEnum::BOOLEAN => self::BOOLEAN, in_array($type, [TypeKindEnum::OBJECT, TypeKindEnum::CLASS_OBJECT, TypeKindEnum::ARRAY, TypeKindEnum::COLLECT_SINGLE_OBJECT, TypeKindEnum::COLLECT_UNION_OBJECT], true) => self::ONE_OF, - default => self::STRING, + default => self::STRING, }; } diff --git a/src/OpenApi/Handler/Config.php b/src/OpenApi/Handler/Config.php index c4a21e5..f4bac81 100755 --- a/src/OpenApi/Handler/Config.php +++ b/src/OpenApi/Handler/Config.php @@ -4,14 +4,7 @@ namespace Astral\Serialize\OpenApi\Handler; - use Astral\Serialize\OpenApi\Collections\OpenApiCollection; -use Astral\Serialize\OpenApi\Storage\OpenAPI\ApiInfo; -use Astral\Serialize\OpenApi\Storage\OpenAPI\OpenAPI; -use Astral\Serialize\OpenApi\Storage\OpenAPI\ParameterStorage; -use Astral\Serialize\OpenApi\Storage\OpenAPI\SchemaStorage; -use Exception; -use JsonException; -use ReflectionException; +use UnexpectedValueException; class Config { @@ -19,33 +12,35 @@ class Config public static function rootPath(): string { - return dirname(__DIR__, 3); + return dirname(__DIR__, 6); } public static function build() { - if(self::$config){ + if (self::$config) { return self::$config; } - self::$config = include dirname(__DIR__, 3).'/.openapi.php'; - - $path = self::rootPath().'/.openapi.php'; - if(is_file($path)){ - self::$config = array_merge(self::$config,include $path); + self::$config = include dirname(__DIR__, 3) . '/.openapi.php'; + $localConfigPath = self::rootPath() . '/.openapi.php'; + if (is_file($localConfigPath)) { + $localConfig = include $localConfigPath; + if (!is_array($localConfig)) { + throw new UnexpectedValueException('Local config must return an array.'); + } + self::$config = array_merge(self::$config, $localConfig); } return self::$config; } - public static function get($key) + public static function get($key, $default = '') { - return self::build()[$key] ?? ''; + return self::build()[$key] ?? $default; } public static function has($key): bool { return isset(self::build()[$key]); } - } diff --git a/src/OpenApi/Handler/HandleInterface.php b/src/OpenApi/Handler/HandleInterface.php index 302a2a5..ff7b103 100755 --- a/src/OpenApi/Handler/HandleInterface.php +++ b/src/OpenApi/Handler/HandleInterface.php @@ -6,6 +6,5 @@ interface HandleInterface { - public function toString(): string; } diff --git a/src/OpenApi/Handler/Handler.php b/src/OpenApi/Handler/Handler.php index c6f506d..2f76d3c 100755 --- a/src/OpenApi/Handler/Handler.php +++ b/src/OpenApi/Handler/Handler.php @@ -4,11 +4,9 @@ namespace Astral\Serialize\OpenApi\Handler; -use Astral\Serialize\OpenApi\Collections\OpenApiCollection; use Astral\Serialize\OpenApi\Storage\OpenAPI\ApiInfo; use Astral\Serialize\OpenApi\Storage\OpenAPI\OpenAPI; use Astral\Serialize\OpenApi\Storage\OpenAPI\ParameterStorage; -use Astral\Serialize\OpenApi\Storage\OpenAPI\SchemaStorage; use Exception; use JsonException; use ReflectionException; @@ -16,17 +14,17 @@ abstract class Handler implements HandleInterface { /** @var OpenAPI|null */ - public static ?OpenAPI $OpenAPI = null; + public static ?OpenAPI $openAPI = null; public function __construct( protected readonly ParameterStorage $headerParameterStorages = new ParameterStorage() ) { - self::$OpenAPI ??= (new OpenAPI()) + self::$openAPI ??= (new OpenAPI()) ->withApiInfo(new ApiInfo(Config::get('title'), Config::get('description'))) ->withServers(Config::get('service')); - if(Config::has('headers')) { + if (Config::has('headers')) { foreach (Config::get('headers') as $header) { $this->headerParameterStorages->addHeaderProperties($header['name'], $header['description'], $header['example']); } @@ -57,46 +55,33 @@ public function addGlobalHeader(string $name, string $example = '', string $desc } /** - * 遍历整个项目根目录,自动扫描所有 PHP 文件, - * 如果文件内容中包含 "Astral\Serialize\OpenApi\Annotations", - * 则认为它是需要处理的 Controller,进而调用 buildByClass。 - * - * @return $this - * @throws ReflectionException - */ + * 遍历整个项目根目录,自动扫描所有 PHP 文件, + * 如果文件内容中包含 "Astral\Serialize\OpenApi\Annotations", + * 则认为它是需要处理的 Controller,进而调用 buildByClass。 + * + * @return $this + * @throws ReflectionException + */ public function handleByFolders(): self { - // 1. 取项目根目录 - $projectRoot = getcwd(); - - if ($projectRoot === false) { - return $this; - } - - // 2. 默认根命名空间留空(根据项目实际情况可改为 "App"、"App\\Http\\Controllers" 等) - // 如果你希望扫描时拼接命名空间前缀,可以在这里做修改。 - $rootNamespace = ''; - - // 3. 调用内部递归方法开始扫描 - $this->scanFolderRecursively($projectRoot, $rootNamespace); - + $this->scanFolderRecursively(Config::rootPath()); return $this; } /** * 递归扫描指定目录下的所有子目录和文件。 - * @param string $folder 要扫描的文件夹路径 - * @param string $namespace 该文件夹对应的 PHP 命名空间前缀 - * + * @param string $folder 要扫描的文件夹路径 * @throws ReflectionException */ - protected function scanFolderRecursively(string $folder, string $namespace): void + protected function scanFolderRecursively(string $folder): void { // 如果不是目录,跳过 if (! is_dir($folder)) { return; } + $excludeDirs = array_merge(Config::get('exclude_dirs', []), ['/vendor', '/tests']); + // 遍历当前文件夹下的所有内容 foreach (scandir($folder) as $file) { // 跳过 . 和 .. ,以及隐藏文件夹/文件 @@ -108,10 +93,14 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi // 如果是子目录,则递归,并拼接命名空间 if (is_dir($path)) { - // 例如,如果当前命名空间是 "App": - // 子目录 "Http" 则新的命名空间为 "App\Http" - $newNamespace = $namespace !== '' ? ($namespace . '\\' . $file) : $file; - $this->scanFolderRecursively($path, $newNamespace); + + // 检查是否是需要排除的目录 + $relativePath = '/' . ltrim(str_replace(Config::rootPath(), '', $path), '\\/'); + if (in_array($relativePath, $excludeDirs)) { + continue; + } + + $this->scanFolderRecursively($path); continue; } @@ -127,13 +116,14 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi } // 如果文件中没有引入 Astral\Serialize\OpenApi\Annotations,就跳过 - if (!str_contains($fileContent, 'Astral\\Serialize\\OpenApi\\Annotations')) { + if (!str_contains($fileContent, 'OpenApi\Annotations')) { continue; } // 计算类名:去掉 .php 之后,将命名空间前缀 + 文件名 组成完整类名 $baseName = substr($file, 0, -4); // 去掉 ".php" - $className = $namespace !== '' ? ($namespace . '\\' . $baseName) : $baseName; + $className = $this->getNamespaceFromFile($fileContent); + $className = $className ? $className . '\\' . $baseName : $baseName; // 如果类尚未加载,则尝试 include if (! class_exists($className)) { @@ -145,11 +135,20 @@ protected function scanFolderRecursively(string $folder, string $namespace): voi } } + protected function getNamespaceFromFile(string $fileContent): ?string + { + if (preg_match('/namespace\s+([^;]+);/', $fileContent, $matches)) { + return trim($matches[1]); + } + + return null; + } + /** * @throws JsonException */ public function toString(): string { - return json_encode(self::$OpenAPI, JSON_THROW_ON_ERROR); + return json_encode(self::$openAPI, JSON_THROW_ON_ERROR); } } diff --git a/src/OpenApi/Storage/OpenAPI/Method/Method.php b/src/OpenApi/Storage/OpenAPI/Method/Method.php index 1fbe8f9..90ee246 100755 --- a/src/OpenApi/Storage/OpenAPI/Method/Method.php +++ b/src/OpenApi/Storage/OpenAPI/Method/Method.php @@ -10,7 +10,6 @@ class Method { - public function __construct( public array $tags = [], public string $summary = '', diff --git a/src/OpenApi/Storage/OpenAPI/OpenAPI.php b/src/OpenApi/Storage/OpenAPI/OpenAPI.php index 8ad8567..5f3a0ae 100755 --- a/src/OpenApi/Storage/OpenAPI/OpenAPI.php +++ b/src/OpenApi/Storage/OpenAPI/OpenAPI.php @@ -33,7 +33,6 @@ public function withApiInfo(ApiInfo $apiInfo): self return $this; } - public function withServers(array $servers): self { $this->servers = $servers; @@ -62,6 +61,4 @@ public function addPath(OpenApiCollection $openApiCollection): self $this->paths[$openApiCollection->route->route][strtolower($openApiCollection->route->method->name)] = $openApiCollection->build(); return $this; } - - } diff --git a/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php index 1444711..60cc18b 100755 --- a/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php +++ b/src/OpenApi/Storage/OpenAPI/RequestBodyStorage.php @@ -29,7 +29,7 @@ public function getData(): array { return [ 'required' => true, - 'content' => [ + 'content' => [ $this->contentType->value => [ 'schema' => $this->parameters ] diff --git a/src/OpenApi/Storage/OpenAPI/ResponseStorage.php b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php index 578edd4..db870b8 100755 --- a/src/OpenApi/Storage/OpenAPI/ResponseStorage.php +++ b/src/OpenApi/Storage/OpenAPI/ResponseStorage.php @@ -27,7 +27,7 @@ public function getData(): array { return [ 'description' => $this->description, - 'content' => [ + 'content' => [ $this->contentType => [ 'schema' => $this->parameter ] diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index ea2ff5a..25dbf73 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -58,11 +58,11 @@ public function build(array $parameterTree, array &$currentNode = null): static $this->buildBasicPropertySchema($parameter, $currentNode); // oneOf/anyOf/allOf 格式 - if($parameter->type->isOf()){ + if ($parameter->type->isOf()) { $this->buildOfProperties($parameter, $currentNode); } // 处理嵌套子属性 - else if ($parameter->children) { + elseif ($parameter->children) { $this->buildNestedProperties($parameter, $currentNode); } } @@ -98,31 +98,31 @@ public function buildOfProperties(ParameterCollection $topParameter, array &$cur // 重构属性结构为 oneOf/anyOf/allOf 格式 $node = &$currentNode['properties'][$propertyName][$topParameter->type->value]; - $i = 0; + $i = 0; $addedTypes = []; - foreach ($topParameter->types as $kindType){ + foreach ($topParameter->types as $kindType) { $type = ParameterTypeEnum::getBaseEnumByTypeKindEnum($kindType); if ($type && !in_array($type->value, $addedTypes, true)) { - $node[$i] = ['type' => $type->value]; + $node[$i] = ['type' => $type->value]; $addedTypes[] = $type->value; $i++; } } - if($topParameter->children){ - foreach ($topParameter->children as $className => $child){ - $type = ParameterTypeEnum::getArrayAndObjectEnumBy($topParameter->types,$className); - if($type->isObject()){ - $node[$i] = ['type'=>'object','properties' => []]; + if ($topParameter->children) { + foreach ($topParameter->children as $className => $child) { + $type = ParameterTypeEnum::getArrayAndObjectEnumBy($topParameter->types, $className); + if ($type->isObject()) { + $node[$i] = ['type' => 'object','properties' => []]; $childNode = &$node[$i]; $i++; - }else if($type->isArray()){ - $node[$i] = ['type'=>'array','items'=> ['type'=>'object','properties' => []]]; + } elseif ($type->isArray()) { + $node[$i] = ['type' => 'array','items' => ['type' => 'object','properties' => []]]; $childNode = &$node[$i]['items']; $i++; } - $this->build($child,$childNode); + $this->build($child, $childNode); } } @@ -134,7 +134,7 @@ public function buildOfProperties(ParameterCollection $topParameter, array &$cur private function buildNestedProperties(ParameterCollection $topParameter, array &$currentNode): void { $propertyName = $topParameter->name; - $nestedNode = null; + $nestedNode = null; if ($topParameter->type->isArray()) { // 数组类型:创建 items 结构 @@ -146,8 +146,8 @@ private function buildNestedProperties(ParameterCollection $topParameter, array } elseif ($topParameter->type->isObject()) { // 对象类型:重构为嵌套对象结构 $currentNode['properties'][$propertyName] = [ - 'type' => 'object', - 'properties' => [], + 'type' => 'object', + 'properties' => [], 'description' => $this->getDescriptions($topParameter), ]; $nestedNode = &$currentNode['properties'][$propertyName]; @@ -161,10 +161,10 @@ private function buildNestedProperties(ParameterCollection $topParameter, array } } - public function getDescriptions(ParameterCollection $parameter):string + public function getDescriptions(ParameterCollection $parameter): string { $description = $parameter->openApiAnnotation->description ?? ''; - if(!ParameterTypeEnum::hasEnum($parameter->types)){ + if (!ParameterTypeEnum::hasEnum($parameter->types)) { return $description; } @@ -177,8 +177,8 @@ public function getDescriptions(ParameterCollection $parameter):string } } - $description .= ($description ? ' ' : '').'optional values:' . implode('、', $names); + $description .= ($description ? ' ' : '') . 'optional values:' . implode('、', $names); return $description; } -} \ No newline at end of file +} diff --git a/src/OpenApi/Storage/OpenAPI/ServersStorage.php b/src/OpenApi/Storage/OpenAPI/ServersStorage.php index cfab890..33297f5 100755 --- a/src/OpenApi/Storage/OpenAPI/ServersStorage.php +++ b/src/OpenApi/Storage/OpenAPI/ServersStorage.php @@ -21,7 +21,7 @@ public function addVariable(string $name, $description, $default = ''): static { $this->variables = $this->variables instanceof stdClass ? [] : $this->variables; - $this->variables[$name] = ['default' => $default, 'description'=> $description]; + $this->variables[$name] = ['default' => $default, 'description' => $description]; return $this; } diff --git a/src/OpenApi/Web/Index.php b/src/OpenApi/Web/Index.php index eecbb44..3ee77a5 100644 --- a/src/OpenApi/Web/Index.php +++ b/src/OpenApi/Web/Index.php @@ -1,12 +1,12 @@ handleByFolders()->toString(); } catch (JsonException|ReflectionException $e) { - throw new RuntimeException($e->getMessage(),$e->getCode(),$e); -} \ No newline at end of file + throw new RuntimeException($e->getMessage(), $e->getCode(), $e); +} diff --git a/src/Resolvers/InputResolver.php b/src/Resolvers/InputResolver.php index 6a31d15..678ed63 100644 --- a/src/Resolvers/InputResolver.php +++ b/src/Resolvers/InputResolver.php @@ -2,7 +2,7 @@ namespace Astral\Serialize\Resolvers; -use Astral\Serialize\Casts\InputConstructCast; +use Astral\Serialize\Casts\InputValue\InputConstructCast; use Astral\Serialize\Resolvers\Casts\InputValueCastResolver; use Astral\Serialize\Support\Collections\DataCollection; use Astral\Serialize\Support\Collections\GroupDataCollection; diff --git a/src/SerializeContainer.php b/src/SerializeContainer.php index 128e999..a17ddc7 100644 --- a/src/SerializeContainer.php +++ b/src/SerializeContainer.php @@ -2,7 +2,7 @@ namespace Astral\Serialize; -use Astral\Serialize\Casts\InputConstructCast; +use Astral\Serialize\Casts\InputValue\InputConstructCast; use Astral\Serialize\Faker\FakerCastResolver; use Astral\Serialize\Faker\FakerResolver; use Astral\Serialize\Faker\Rule\FakerDefaultRules; diff --git a/tests/Openapi/AnnotationOpenApiTest.php b/tests/Openapi/AnnotationOpenApiTest.php index 13993cd..e8ef93c 100644 --- a/tests/Openapi/AnnotationOpenApiTest.php +++ b/tests/Openapi/AnnotationOpenApiTest.php @@ -26,8 +26,8 @@ class AnnotationOpenApiRequest extends Serialize } #[\Astral\Serialize\OpenApi\Annotations\Tag('接口测试')] - class AnnotationOpenApiController{ - + class AnnotationOpenApiController + { #[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法一')] #[\Astral\Serialize\OpenApi\Annotations\Route('/test/description-action')] public function one(AnnotationOpenApiRequest $request): void @@ -41,11 +41,11 @@ public function one(AnnotationOpenApiRequest $request): void $api = new OpenApi(); $api->buildByClass(AnnotationOpenApiController::class); - $openApi = $api::$OpenAPI; - $paths = $openApi->paths; - $post = $paths['/test/description-action']['post']; + $openApi = $api::$openAPI; + $paths = $openApi->paths; + $post = $paths['/test/description-action']['post']; $requestBody = $post->requestBody; - $schema = $requestBody['content']['application/json']['schema']; + $schema = $requestBody['content']['application/json']['schema']; expect(array_keys($schema['properties']))->toMatchArray([ 'test_enum', diff --git a/tests/Openapi/EnumOpenApiTest.php b/tests/Openapi/EnumOpenApiTest.php index 706986a..f14002e 100644 --- a/tests/Openapi/EnumOpenApiTest.php +++ b/tests/Openapi/EnumOpenApiTest.php @@ -35,8 +35,8 @@ class OpenapiEnumRequest extends Serialize } #[\Astral\Serialize\OpenApi\Annotations\Tag('接口测试')] - class OpenapiEnumController{ - + class OpenapiEnumController + { #[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法一')] #[\Astral\Serialize\OpenApi\Annotations\Route('/test/enum-action')] public function one(OpenapiEnumRequest $request): void @@ -51,13 +51,13 @@ public function one(OpenapiEnumRequest $request): void $api = new OpenApi(); $api->buildByClass(OpenapiEnumController::class); - $openApi = $api::$OpenAPI; + $openApi = $api::$openAPI; - $paths = $openApi->paths; - $post = $paths['/test/enum-action']['post']; + $paths = $openApi->paths; + $post = $paths['/test/enum-action']['post']; $requestBody = $post->requestBody; - $schema = $requestBody['content']['application/json']['schema']; + $schema = $requestBody['content']['application/json']['schema']; expect($schema['properties']) ->toHaveKeys([ @@ -67,24 +67,24 @@ public function one(OpenapiEnumRequest $request): void 'test_one_of_enum', ]) ->and($schema['properties']['test_enum'])->toMatchArray([ - 'type' => 'string', + 'type' => 'string', 'description' => 'optional values:ENUM_1、ENUM_2', - 'example' => '', + 'example' => '', ]) ->and($schema['properties']['test_string_enum'])->toMatchArray([ - 'type' => 'string', + 'type' => 'string', 'description' => 'optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', - 'example' => '', + 'example' => '', ]) ->and($schema['properties']['test_string_2_enum'])->toMatchArray([ - 'type' => 'string', + 'type' => 'string', 'description' => 'optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', - 'example' => '', + 'example' => '', ]) ->and($schema['properties']['test_one_of_enum'])->toMatchArray([ - 'type' => 'oneOf', + 'type' => 'oneOf', 'description' => 'optional values:ENUM_1、ENUM_2、ENUM_3、ENUM_4', - 'example' => '', + 'example' => '', ]) ->and($schema['properties']['test_one_of_enum']['oneOf'])->toBeArray()->toHaveCount(2) ->and($schema['properties']['test_one_of_enum']['oneOf'][0])->toMatchArray(['type' => 'string']) @@ -96,4 +96,4 @@ public function one(OpenapiEnumRequest $request): void 'test_one_of_enum', ]); -}); \ No newline at end of file +}); diff --git a/tests/Openapi/OpenApiTest.php b/tests/Openapi/OpenApiTest.php index 15c6db9..479b20a 100644 --- a/tests/Openapi/OpenApiTest.php +++ b/tests/Openapi/OpenApiTest.php @@ -37,15 +37,14 @@ class TestOpenApiResponse extends Serialize } #[\Astral\Serialize\OpenApi\Annotations\Tag('接口测试')] - class TestOpenApiController{ - + class TestOpenApiController + { #[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法一')] #[\Astral\Serialize\OpenApi\Annotations\Route('/test/one-action')] public function one(TestOpenApiRequest $request): TestOpenApiResponse { - return new TestOpenApiResponse(); + return new TestOpenApiResponse(); } - } }); @@ -55,7 +54,7 @@ public function one(TestOpenApiRequest $request): TestOpenApiResponse $api = new OpenApi(); $api->buildByClass(TestOpenApiController::class); - $openApi = $api::$OpenAPI; + $openApi = $api::$openAPI; // 顶层结构断言 expect($openApi->openapi)->toBe('3.1.1') @@ -81,7 +80,7 @@ public function one(TestOpenApiRequest $request): TestOpenApiResponse // id 字段是 oneOf 并包含 string, integer, number $idOneOf = $schema['properties']['id']['oneOf']; - $types = array_map(static fn($item) => $item['type'], $idOneOf); + $types = array_map(static fn ($item) => $item['type'], $idOneOf); expect($types)->toMatchArray(['string', 'integer', 'number']); // any_array 是 oneOf 并包含至少一个 array 类型 @@ -99,4 +98,4 @@ public function one(TestOpenApiRequest $request): TestOpenApiResponse ->and($schema['required'])->toHaveCount(1) ->and($schema['required'][0])->toBeString('id'); -}); \ No newline at end of file +}); diff --git a/tests/Openapi/RouteOrewriteOpenApiTest.php b/tests/Openapi/RouteOrewriteOpenApiTest.php index cffae5a..2fb26aa 100644 --- a/tests/Openapi/RouteOrewriteOpenApiTest.php +++ b/tests/Openapi/RouteOrewriteOpenApiTest.php @@ -9,7 +9,6 @@ #[Attribute(Attribute::TARGET_METHOD)] class CustomerRoute extends OpenApi\Annotations\Route { - } class TestCustomerRouteRequest extends Serialize @@ -20,14 +19,13 @@ class TestCustomerRouteRequest extends Serialize } #[\Astral\Serialize\OpenApi\Annotations\Tag('接口测试')] - class TestCustomerRouteController{ - + class TestCustomerRouteController + { #[\Astral\Serialize\OpenApi\Annotations\Summary('测试方法一')] #[CustomerRoute('/test/customer-route')] public function one(TestCustomerRouteRequest $request): void { } - } }); @@ -37,10 +35,10 @@ public function one(TestCustomerRouteRequest $request): void $api = new OpenApi(); $api->buildByClass(TestCustomerRouteController::class); - $openApi = $api::$OpenAPI; + $openApi = $api::$openAPI; // 路径是否存在 $paths = $openApi->paths; expect($paths)->toHaveKey('/test/customer-route'); -}); \ No newline at end of file +}); diff --git a/tests/Serialize/From/InputIgnoreGroupFromSerializeTest.php b/tests/Serialize/From/InputIgnoreGroupFromSerializeTest.php index 44e3c48..548841f 100644 --- a/tests/Serialize/From/InputIgnoreGroupFromSerializeTest.php +++ b/tests/Serialize/From/InputIgnoreGroupFromSerializeTest.php @@ -1,7 +1,7 @@ '张三', - 'secretKey' => 'confidential', + 'name' => '张三', + 'secretKey' => 'confidential', 'sensitiveInfo' => '机密信息', ]); @@ -36,14 +36,14 @@ class InputIgnoreSerialize extends Serialize $array = $object->toArray(); expect($array)->toMatchArray([ - 'name' => '张三', - 'secretKey' => 'confidential', + 'name' => '张三', + 'secretKey' => 'confidential', 'sensitiveInfo' => null, ]); $object2 = InputIgnoreSerialize::setGroups('admin')->from([ - 'name' => '张三', - 'secretKey' => 'confidential', + 'name' => '张三', + 'secretKey' => 'confidential', 'sensitiveInfo' => '机密信息', ]); @@ -53,8 +53,8 @@ class InputIgnoreSerialize extends Serialize $array2 = $object2->toArray(); expect($array2)->toMatchArray([ - 'name' => null, - 'secretKey' => 'confidential', + 'name' => null, + 'secretKey' => 'confidential', 'sensitiveInfo' => null, ]); }); diff --git a/tests/Serialize/From/InputNameFromSerializeTest.php b/tests/Serialize/From/InputNameFromSerializeTest.php index 04cdb48..6337417 100644 --- a/tests/Serialize/From/InputNameFromSerializeTest.php +++ b/tests/Serialize/From/InputNameFromSerializeTest.php @@ -1,10 +1,10 @@ Date: Wed, 25 Jun 2025 16:20:12 +0800 Subject: [PATCH 47/59] add group and ignore tests --- src/OpenApi.php | 1 + src/OpenApi/Annotations/Response.php | 4 +- src/OpenApi/Collections/OpenApiCollection.php | 140 +++++++++++++----- src/Support/Collections/DataCollection.php | 3 +- tests/Openapi/BaseGroupOpenApiTest.php | 84 +++++++++++ tests/Openapi/IgnoreOpenApiTest.php | 60 ++++++++ 6 files changed, 255 insertions(+), 37 deletions(-) create mode 100644 tests/Openapi/BaseGroupOpenApiTest.php create mode 100644 tests/Openapi/IgnoreOpenApiTest.php diff --git a/src/OpenApi.php b/src/OpenApi.php index 3734db9..9239ffd 100755 --- a/src/OpenApi.php +++ b/src/OpenApi.php @@ -77,6 +77,7 @@ public function buildByClass(string $className): void attributes: $methodAttributes, requestBody: $instances[RequestBody::class], response: $instances[Response::class], + groupResolver: SerializeContainer::get()->groupResolver(), ); self::$openAPI->addPath($openApiCollection); diff --git a/src/OpenApi/Annotations/Response.php b/src/OpenApi/Annotations/Response.php index da8a1d7..f6ba018 100644 --- a/src/OpenApi/Annotations/Response.php +++ b/src/OpenApi/Annotations/Response.php @@ -11,9 +11,9 @@ class Response { public function __construct( /** @var class-string $className */ - public string $className, - public ?int $code = 200, + public string $className = '', public ?array $groups = null, + public ?int $code = 200, ) { } } diff --git a/src/OpenApi/Collections/OpenApiCollection.php b/src/OpenApi/Collections/OpenApiCollection.php index acbe129..5b71d9a 100644 --- a/src/OpenApi/Collections/OpenApiCollection.php +++ b/src/OpenApi/Collections/OpenApiCollection.php @@ -2,6 +2,7 @@ namespace Astral\Serialize\OpenApi\Collections; +use Astral\Serialize\Exceptions\NotFoundGroupException; use Astral\Serialize\OpenApi\Annotations\Headers; use Astral\Serialize\OpenApi\Annotations\OpenApi; use Astral\Serialize\OpenApi\Annotations\RequestBody; @@ -15,7 +16,9 @@ use Astral\Serialize\OpenApi\Storage\OpenAPI\RequestBodyStorage; use Astral\Serialize\OpenApi\Storage\OpenAPI\ResponseStorage; use Astral\Serialize\OpenApi\Storage\OpenAPI\SchemaStorage; +use Astral\Serialize\Resolvers\GroupResolver; use Astral\Serialize\Serialize; +use Astral\Serialize\SerializeContainer; use Astral\Serialize\Support\Factories\ContextFactory; use Psr\SimpleCache\InvalidArgumentException; use ReflectionMethod; @@ -34,6 +37,7 @@ public function __construct( public array $attributes, public RequestBody|null $requestBody, public Response|null $response, + public GroupResolver $groupResolver, ) { } @@ -50,81 +54,149 @@ public function build(): Method description:$this->summary->description ?: '' ); - $openAPIMethod->withRequestBody($this->requestBody !== null ? $this->buildRequestBodyByAttribute() : $this->buildRequestBodyByParameters()); - $openAPIMethod->addResponse(200, $this->buildResponse()); + $requestBody = $this->buildRequestBody( + className:$this->getRequestBodyClass(), + contentType:$this->requestBody->contentType ?? ContentTypeEnum::JSON, + groups: $this->requestBody->groups ?? [] + ); + + $response = $this->buildResponse( + className:$this->getResponseClass(), + groups:$this->response->groups ?? [] + ); + + $openAPIMethod->withRequestBody($requestBody); + $openAPIMethod->addResponse(200, $response); return $openAPIMethod; } - /** - * @throws InvalidArgumentException - */ - public function buildRequestBodyByAttribute(): RequestBodyStorage - { - $openAPIRequestBody = new RequestBodyStorage($this->requestBody->contentType); - $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($this->requestBody->className, $this->requestBody->groups)); - $openAPIRequestBody->withParameter($schemaStorage); - return $openAPIRequestBody; - } - /** - * @throws InvalidArgumentException - */ - public function buildRequestBodyByParameters(): RequestBodyStorage + public function getRequestBodyClass(): string { - $openAPIRequestBody = new RequestBodyStorage(ContentTypeEnum::JSON); + if($this->requestBody?->className){ + return $this->requestBody->className; + } + $methodParam = $this->reflectionMethod->getParameters()[0] ?? null; $type = $methodParam?->getType(); $requestBodyClass = $type instanceof ReflectionNamedType ? $type->getName() : ''; if (is_subclass_of($requestBodyClass, Serialize::class)) { - $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($requestBodyClass)); + return $requestBodyClass; + } + + return ''; + } + + + public function buildRequestBody(string $className,ContentTypeEnum $contentType,array $groups = []): RequestBodyStorage + { + $openAPIRequestBody = new RequestBodyStorage($contentType); + if (is_subclass_of($className, Serialize::class)) { + $schemaStorage = (new SchemaStorage())->build($this->buildRequestParameterCollections($className,$groups)); $openAPIRequestBody->withParameter($schemaStorage); } return $openAPIRequestBody; } - /** - * @throws InvalidArgumentException - */ - public function buildResponse(): ResponseStorage + public function getResponseClass(): string { + if($this->response?->className){ + return $this->response->className; + } + $returnClass = $this->reflectionMethod->getReturnType(); $returnClass = $returnClass instanceof ReflectionNamedType ? $returnClass->getName() : null; - $responseClass = match(true) { - $this->response !== null => $this->response->className, - $returnClass && is_subclass_of($returnClass, Serialize::class) => $returnClass, - default => null, - }; + if (is_subclass_of($returnClass, Serialize::class)) { + return $returnClass; + } - $responseStorage = new ResponseStorage(); + return ''; + } + /** + * @throws InvalidArgumentException + */ + public function buildResponse(string $className,array $groups = []): ResponseStorage + { + $responseStorage = new ResponseStorage(); - if ($responseClass) { - $groups = $this->response && is_array($this->response->groups) ? $this->response->groups : ['default']; - $schemaStorage = (new SchemaStorage())->build($this->buildParameterCollections($responseClass, $groups)); + if ($className) { + $schemaStorage = (new SchemaStorage())->build($this->buildResponseParameterCollections($className,$groups)); $responseStorage->withParameter($schemaStorage); } return $responseStorage; } + /** + * @param string $className + * @param array $groups + * @return array + * @throws InvalidArgumentException|NotFoundGroupException + */ + public function buildRequestParameterCollections(string $className, array $groups = []): array + { + + $serializeContext = ContextFactory::build($className); + $serializeContext->setGroups($groups)->from(); + $properties = $serializeContext->getGroupCollection()->getProperties(); + $groups = $groups ?: [$className]; + + $vols = []; + foreach ($properties as $property) { + + + if($property->isInputIgnoreByGroups($groups) || !$this->groupResolver->resolveExistsGroupsByDataCollection($property, $groups, $className)){ + continue; + } + + $vol = new ParameterCollection( + className: $className, + name: current($property->getInputNamesByGroups($groups, $className)), + types: $property->getTypes(), + type: ParameterTypeEnum::getByTypes($property->getTypes()), + openApiAnnotation: $this->getOpenApiAnnotation($property->getAttributes()), + required: !$property->isNullable(), + ignore: false, + ); + + if ($property->getChildren()) { + foreach ($property->getChildren() as $children) { + $className = $children->getClassName(); + $vol->children[$className] = $this->buildRequestParameterCollections($className); + } + } + + $vols[] = $vol; + } + + return $vols; + } + /** * @param string $className * @param array $groups * @return array * @throws InvalidArgumentException */ - public function buildParameterCollections(string $className, array $groups = ['default']): array + public function buildResponseParameterCollections(string $className, array $groups = []): array { $serializeContext = ContextFactory::build($className); $serializeContext->from(); $properties = $serializeContext->getGroupCollection()->getProperties(); + $groups = $groups ?: [$className]; $vols = []; foreach ($properties as $property) { + + if($property->isOutIgnoreByGroups($groups) || !$this->groupResolver->resolveExistsGroupsByDataCollection($property, $groups, $className)){ + continue; + } + $vol = new ParameterCollection( className: $className, - name: current($property->getInputNamesByGroups($groups, $className)), + name: current($property->getOutNamesByGroups($groups, $className)), types: $property->getTypes(), type: ParameterTypeEnum::getByTypes($property->getTypes()), openApiAnnotation: $this->getOpenApiAnnotation($property->getAttributes()), @@ -135,7 +207,7 @@ className: $className, if ($property->getChildren()) { foreach ($property->getChildren() as $children) { $className = $children->getClassName(); - $vol->children[$className] = $this->buildParameterCollections($className); + $vol->children[$className] = $this->buildResponseParameterCollections($className); } } diff --git a/src/Support/Collections/DataCollection.php b/src/Support/Collections/DataCollection.php index ff089b2..75fa97f 100644 --- a/src/Support/Collections/DataCollection.php +++ b/src/Support/Collections/DataCollection.php @@ -114,7 +114,7 @@ public function getInputNamesByGroups(array $groups, string $defaultGroup): arra $vols = isset($this->inputNames[$group]) ? array_merge($vols, $this->inputNames[$group]) : $vols; } - return array_unique($vols); + return $vols ? array_unique($vols) : [$this->name]; } public function getInputIgnoreGroups(): array @@ -140,6 +140,7 @@ public function getOutIgnoreGroups(): array public function getOutNamesByGroups(array $groups, string $defaultGroup): array { + if (count($groups) === 1 && current($groups) === $defaultGroup) { return $this->outNames[$defaultGroup] ?? $this->outNames['default'] ?? [$this->name]; } diff --git a/tests/Openapi/BaseGroupOpenApiTest.php b/tests/Openapi/BaseGroupOpenApiTest.php new file mode 100644 index 0000000..071ed01 --- /dev/null +++ b/tests/Openapi/BaseGroupOpenApiTest.php @@ -0,0 +1,84 @@ +buildByClass(TestBaseGroupController::class); + + $openApi = $api::$openAPI; + + $paths = $openApi->paths; + $post = $paths['/test/base-group']['post']; + $requestBody = $post->requestBody; + $schema = $requestBody['content']['application/json']['schema']; + expect($schema['properties'])->toHaveCount(3)->toHaveKeys(['input_change_group_1_33_2_11', 'input_group_2_22', 'input_group_2_33']); + + + $schema = $post->responses[200]['content']['application/json']['schema']; + expect($schema['properties'])->toHaveCount(3)->toHaveKeys(['out_group_1_11', 'out_group_1_22', 'out_group_1_33_2_11']); + +}); diff --git a/tests/Openapi/IgnoreOpenApiTest.php b/tests/Openapi/IgnoreOpenApiTest.php new file mode 100644 index 0000000..0a1ef10 --- /dev/null +++ b/tests/Openapi/IgnoreOpenApiTest.php @@ -0,0 +1,60 @@ +buildByClass(TestIgnoreController::class); + + $openApi = $api::$openAPI; + + $paths = $openApi->paths; + $post = $paths['/test/ignore-route']['post']; + $requestBody = $post->requestBody; + $schema = $requestBody['content']['application/json']['schema']; + + expect($schema['properties']['input_ignore'] ?? null)->toBeNull(); + + $schema = $post->responses[200]['content']['application/json']['schema']; + expect($schema['properties']['out_ignore'] ?? null)->toBeNull(); + +}); From 83ba742f8d2bf4da3f4a549104c1be4634e76d55 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Wed, 25 Jun 2025 17:55:44 +0800 Subject: [PATCH 48/59] add group and ignore tests --- src/OpenApi/Storage/OpenAPI/SchemaStorage.php | 2 +- src/OpenApi/Web/{Index.php => index.php} | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) rename src/OpenApi/Web/{Index.php => index.php} (67%) diff --git a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php index 25dbf73..c7b7966 100755 --- a/src/OpenApi/Storage/OpenAPI/SchemaStorage.php +++ b/src/OpenApi/Storage/OpenAPI/SchemaStorage.php @@ -40,7 +40,7 @@ public function getData(): array * @param-out array $currentNode 当前构建节点的引用 * @return static */ - public function build(array $parameterTree, array &$currentNode = null): static + public function build(array $parameterTree, array|null &$currentNode = null): static { if ($currentNode === null) { $currentNode = &$this->data; diff --git a/src/OpenApi/Web/Index.php b/src/OpenApi/Web/index.php similarity index 67% rename from src/OpenApi/Web/Index.php rename to src/OpenApi/Web/index.php index 3ee77a5..a1466af 100644 --- a/src/OpenApi/Web/Index.php +++ b/src/OpenApi/Web/index.php @@ -1,12 +1,11 @@ handleByFolders()->toString(); } catch (JsonException|ReflectionException $e) { throw new RuntimeException($e->getMessage(), $e->getCode(), $e); -} +} \ No newline at end of file From ec00f5cefc792a2e4c1c7d488bac2ab12a84c12c Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 26 Jun 2025 10:37:09 +0800 Subject: [PATCH 49/59] add group and ignore tests --- src/OpenApi/Annotations/Response.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenApi/Annotations/Response.php b/src/OpenApi/Annotations/Response.php index f6ba018..ebddb86 100644 --- a/src/OpenApi/Annotations/Response.php +++ b/src/OpenApi/Annotations/Response.php @@ -10,7 +10,7 @@ class Response { public function __construct( - /** @var class-string $className */ + /** @var class-string|string $className */ public string $className = '', public ?array $groups = null, public ?int $code = 200, From 2e358609b65395bbc7fa075d90acf0a8ef9df562 Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 26 Jun 2025 17:12:30 +0800 Subject: [PATCH 50/59] add frankenphp --- src/OpenApi/Frankenphp/Caddyfile | 55 ++++++++++++++++++++++++++++++++ src/OpenApi/Frankenphp/index.php | 24 ++++++++++++++ src/OpenApi/Web/index.php | 11 ------- 3 files changed, 79 insertions(+), 11 deletions(-) create mode 100644 src/OpenApi/Frankenphp/Caddyfile create mode 100644 src/OpenApi/Frankenphp/index.php delete mode 100644 src/OpenApi/Web/index.php diff --git a/src/OpenApi/Frankenphp/Caddyfile b/src/OpenApi/Frankenphp/Caddyfile new file mode 100644 index 0000000..67c61f3 --- /dev/null +++ b/src/OpenApi/Frankenphp/Caddyfile @@ -0,0 +1,55 @@ +# The Caddyfile is an easy way to configure FrankenPHP and the Caddy web server. +# +# https://frankenphp.dev/docs/config +# https://caddyserver.com/docs/caddyfile + +{ + skip_install_trust + + auto_https disable_redirects + + {$CADDY_GLOBAL_OPTIONS} + + frankenphp { + worker /app/vendor/astral/php-serialize/src/OpenApi/Web/index.php + } +} + +{$CADDY_EXTRA_CONFIG} + +:80 { + #log { + # # Redact the authorization query parameter that can be set by Mercure + # format filter { + # request>uri query { + # replace authorization REDACTED + # } + # } + #} + + root vendor/astral/php-serialize/src/OpenApi/Web + encode zstd br gzip + + # Uncomment the following lines to enable Mercure and Vulcain modules + #mercure { + # # Transport to use (default to Bolt) + # transport_url {$MERCURE_TRANSPORT_URL:bolt:///data/mercure.db} + # # Publisher JWT key + # publisher_jwt {env.MERCURE_PUBLISHER_JWT_KEY} {env.MERCURE_PUBLISHER_JWT_ALG} + # # Subscriber JWT key + # subscriber_jwt {env.MERCURE_SUBSCRIBER_JWT_KEY} {env.MERCURE_SUBSCRIBER_JWT_ALG} + # # Allow anonymous subscribers (double-check that it's what you want) + # anonymous + # # Enable the subscription API (double-check that it's what you want) + # subscriptions + # # Extra directives + # {$MERCURE_EXTRA_DIRECTIVES} + #} + #vulcain + + {$CADDY_SERVER_EXTRA_DIRECTIVES} + + php_server { + #worker /path/to/your/worker.php + } +} diff --git a/src/OpenApi/Frankenphp/index.php b/src/OpenApi/Frankenphp/index.php new file mode 100644 index 0000000..4eb2893 --- /dev/null +++ b/src/OpenApi/Frankenphp/index.php @@ -0,0 +1,24 @@ +handleByFolders()->toString(); + } catch (JsonException|ReflectionException $e) { + http_response_code(500); + echo json_encode(['error' => $e->getMessage(), 'code' => $e->getCode()]); + } +}; + +$maxRequests = (int)($_SERVER['MAX_REQUESTS'] ?? 0); +for ($nbRequests = 0; !$maxRequests || $nbRequests < $maxRequests; ++$nbRequests) { + $keepRunning = frankenphp_handle_request($handler); + gc_collect_cycles(); + if (!$keepRunning) { + break; + } +} diff --git a/src/OpenApi/Web/index.php b/src/OpenApi/Web/index.php deleted file mode 100644 index a1466af..0000000 --- a/src/OpenApi/Web/index.php +++ /dev/null @@ -1,11 +0,0 @@ -handleByFolders()->toString(); -} catch (JsonException|ReflectionException $e) { - throw new RuntimeException($e->getMessage(), $e->getCode(), $e); -} \ No newline at end of file From 5d57ce9a8909da8766ca3cd4277433c913e6650c Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 26 Jun 2025 17:14:22 +0800 Subject: [PATCH 51/59] add frankenphp --- src/OpenApi/Frankenphp/Caddyfile | 2 +- src/OpenApi/Frankenphp/index.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/OpenApi/Frankenphp/Caddyfile b/src/OpenApi/Frankenphp/Caddyfile index 67c61f3..dd6fc82 100644 --- a/src/OpenApi/Frankenphp/Caddyfile +++ b/src/OpenApi/Frankenphp/Caddyfile @@ -11,7 +11,7 @@ {$CADDY_GLOBAL_OPTIONS} frankenphp { - worker /app/vendor/astral/php-serialize/src/OpenApi/Web/index.php + worker /app/vendor/astral/php-serialize/src/OpenApi/Frankenphp/index.php } } diff --git a/src/OpenApi/Frankenphp/index.php b/src/OpenApi/Frankenphp/index.php index 4eb2893..eeed3d5 100644 --- a/src/OpenApi/Frankenphp/index.php +++ b/src/OpenApi/Frankenphp/index.php @@ -8,7 +8,7 @@ try { header('Content-Type: application/json'); echo (new OpenApi())->handleByFolders()->toString(); - } catch (JsonException|ReflectionException $e) { + } catch (ReflectionException $e) { http_response_code(500); echo json_encode(['error' => $e->getMessage(), 'code' => $e->getCode()]); } From 3ca941c239fcb2c650a2b9388596712a3b8ae94f Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 26 Jun 2025 17:38:31 +0800 Subject: [PATCH 52/59] add frankenphp --- src/OpenApi/Frankenphp/Caddyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenApi/Frankenphp/Caddyfile b/src/OpenApi/Frankenphp/Caddyfile index dd6fc82..766c0b4 100644 --- a/src/OpenApi/Frankenphp/Caddyfile +++ b/src/OpenApi/Frankenphp/Caddyfile @@ -27,7 +27,7 @@ # } #} - root vendor/astral/php-serialize/src/OpenApi/Web + root vendor/astral/php-serialize/src/Frankenphp/Web encode zstd br gzip # Uncomment the following lines to enable Mercure and Vulcain modules From f8323a15b0f4c5d0e910409d7090bd5a782e175e Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 26 Jun 2025 17:41:40 +0800 Subject: [PATCH 53/59] add frankenphp --- src/OpenApi/Frankenphp/Caddyfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/OpenApi/Frankenphp/Caddyfile b/src/OpenApi/Frankenphp/Caddyfile index 766c0b4..9044cf3 100644 --- a/src/OpenApi/Frankenphp/Caddyfile +++ b/src/OpenApi/Frankenphp/Caddyfile @@ -27,7 +27,7 @@ # } #} - root vendor/astral/php-serialize/src/Frankenphp/Web + root vendor/astral/php-serialize/src/OpenApi/Frankenphp encode zstd br gzip # Uncomment the following lines to enable Mercure and Vulcain modules From 2aa6f1e37b308e83d81e7406eef18948fa9fe6bf Mon Sep 17 00:00:00 2001 From: "350375092@qq.com" <350375092@qq.com> Date: Thu, 26 Jun 2025 17:55:43 +0800 Subject: [PATCH 54/59] add Swagger ui --- src/OpenApi/Frankenphp/docs/favicon-16x16.png | Bin 0 -> 665 bytes src/OpenApi/Frankenphp/docs/favicon-32x32.png | Bin 0 -> 628 bytes src/OpenApi/Frankenphp/docs/index.css | 16 ++++ src/OpenApi/Frankenphp/docs/index.php | 39 +++++++++ .../Frankenphp/docs/oauth2-redirect.html | 79 ++++++++++++++++++ .../Frankenphp/docs/swagger-ui-bundle.js | 2 + .../docs/swagger-ui-es-bundle-core.js | 3 + .../Frankenphp/docs/swagger-ui-es-bundle.js | 2 + .../docs/swagger-ui-standalone-preset.js | 2 + src/OpenApi/Frankenphp/docs/swagger-ui.css | 3 + src/OpenApi/Frankenphp/docs/swagger-ui.js | 2 + 11 files changed, 148 insertions(+) create mode 100644 src/OpenApi/Frankenphp/docs/favicon-16x16.png create mode 100644 src/OpenApi/Frankenphp/docs/favicon-32x32.png create mode 100644 src/OpenApi/Frankenphp/docs/index.css create mode 100644 src/OpenApi/Frankenphp/docs/index.php create mode 100644 src/OpenApi/Frankenphp/docs/oauth2-redirect.html create mode 100644 src/OpenApi/Frankenphp/docs/swagger-ui-bundle.js create mode 100644 src/OpenApi/Frankenphp/docs/swagger-ui-es-bundle-core.js create mode 100644 src/OpenApi/Frankenphp/docs/swagger-ui-es-bundle.js create mode 100644 src/OpenApi/Frankenphp/docs/swagger-ui-standalone-preset.js create mode 100644 src/OpenApi/Frankenphp/docs/swagger-ui.css create mode 100644 src/OpenApi/Frankenphp/docs/swagger-ui.js diff --git a/src/OpenApi/Frankenphp/docs/favicon-16x16.png b/src/OpenApi/Frankenphp/docs/favicon-16x16.png new file mode 100644 index 0000000000000000000000000000000000000000..8b194e617af1c135e6b37939591d24ac3a5efa18 GIT binary patch literal 665 zcmV;K0%rY*P)}JKSduyL>)s!A4EhTMMEM%Q;aL6%l#xiZiF>S;#Y{N2Zz%pvTGHJduXuC6Lx-)0EGfRy*N{Tv4i8@4oJ41gw zKzThrcRe|7J~(YYIBq{SYCkn-KQm=N8$CrEK1CcqMI1dv9z#VRL_{D)L|`QmF8}}l zJ9JV`Q}p!p_4f7m_U`WQ@apR4;o;!mnU<7}iG_qr zF(e)x9~BG-3IzcG2M4an0002kNkl41`ZiN1i62V%{PM@Ry|IS_+Yc7{bb`MM~xm(7p4|kMHP&!VGuDW4kFixat zXw43VmgwEvB$hXt_u=vZ>+v4i7E}n~eG6;n4Z=zF1n?T*yg<;W6kOfxpC6nao>VR% z?fpr=asSJ&`L*wu^rLJ5Peq*PB0;alL#XazZCBxJLd&giTfw@!hW167F^`7kobi;( ze<<>qNlP|xy7S1zl@lZNIBR7#o9ybJsptO#%}P0hz~sBp00000NkvXXu0mjfUsDF? literal 0 HcmV?d00001 diff --git a/src/OpenApi/Frankenphp/docs/favicon-32x32.png b/src/OpenApi/Frankenphp/docs/favicon-32x32.png new file mode 100644 index 0000000000000000000000000000000000000000..249737fe44558e679f0b67134e274461d988fa98 GIT binary patch literal 628 zcmV-)0*n2LP)Ma*GM0}OV<074bNCP7P7GVd{iMr*I6y~TMLss@FjvgL~HxU z%Vvj33AwpD(Z4*$Mfx=HaU16axM zt2xG_rloN<$iy9j9I5 + + + + + + + Swagger UI + + + + + + + +
+ + + + + diff --git a/src/OpenApi/Frankenphp/docs/oauth2-redirect.html b/src/OpenApi/Frankenphp/docs/oauth2-redirect.html new file mode 100644 index 0000000..5640917 --- /dev/null +++ b/src/OpenApi/Frankenphp/docs/oauth2-redirect.html @@ -0,0 +1,79 @@ + + + + Swagger UI: OAuth2 Redirect + + + + + diff --git a/src/OpenApi/Frankenphp/docs/swagger-ui-bundle.js b/src/OpenApi/Frankenphp/docs/swagger-ui-bundle.js new file mode 100644 index 0000000..a080132 --- /dev/null +++ b/src/OpenApi/Frankenphp/docs/swagger-ui-bundle.js @@ -0,0 +1,2 @@ +/*! For license information please see swagger-ui-bundle.js.LICENSE.txt */ +!function webpackUniversalModuleDefinition(s,o){"object"==typeof exports&&"object"==typeof module?module.exports=o():"function"==typeof define&&define.amd?define([],o):"object"==typeof exports?exports.SwaggerUIBundle=o():s.SwaggerUIBundle=o()}(this,(()=>(()=>{var s={251:(s,o)=>{o.read=function(s,o,i,a,u){var _,w,x=8*u-a-1,C=(1<>1,L=-7,B=i?u-1:0,$=i?-1:1,V=s[o+B];for(B+=$,_=V&(1<<-L)-1,V>>=-L,L+=x;L>0;_=256*_+s[o+B],B+=$,L-=8);for(w=_&(1<<-L)-1,_>>=-L,L+=a;L>0;w=256*w+s[o+B],B+=$,L-=8);if(0===_)_=1-j;else{if(_===C)return w?NaN:1/0*(V?-1:1);w+=Math.pow(2,a),_-=j}return(V?-1:1)*w*Math.pow(2,_-a)},o.write=function(s,o,i,a,u,_){var w,x,C,j=8*_-u-1,L=(1<>1,$=23===u?Math.pow(2,-24)-Math.pow(2,-77):0,V=a?0:_-1,U=a?1:-1,z=o<0||0===o&&1/o<0?1:0;for(o=Math.abs(o),isNaN(o)||o===1/0?(x=isNaN(o)?1:0,w=L):(w=Math.floor(Math.log(o)/Math.LN2),o*(C=Math.pow(2,-w))<1&&(w--,C*=2),(o+=w+B>=1?$/C:$*Math.pow(2,1-B))*C>=2&&(w++,C/=2),w+B>=L?(x=0,w=L):w+B>=1?(x=(o*C-1)*Math.pow(2,u),w+=B):(x=o*Math.pow(2,B-1)*Math.pow(2,u),w=0));u>=8;s[i+V]=255&x,V+=U,x/=256,u-=8);for(w=w<0;s[i+V]=255&w,V+=U,w/=256,j-=8);s[i+V-U]|=128*z}},462:(s,o,i)=>{"use strict";var a=i(40975);s.exports=a},659:(s,o,i)=>{var a=i(51873),u=Object.prototype,_=u.hasOwnProperty,w=u.toString,x=a?a.toStringTag:void 0;s.exports=function getRawTag(s){var o=_.call(s,x),i=s[x];try{s[x]=void 0;var a=!0}catch(s){}var u=w.call(s);return a&&(o?s[x]=i:delete s[x]),u}},694:(s,o,i)=>{"use strict";i(91599);var a=i(37257);i(12560),s.exports=a},953:(s,o,i)=>{"use strict";s.exports=i(53375)},1733:s=>{var o=/[^\x00-\x2f\x3a-\x40\x5b-\x60\x7b-\x7f]+/g;s.exports=function asciiWords(s){return s.match(o)||[]}},1882:(s,o,i)=>{var a=i(72552),u=i(23805);s.exports=function isFunction(s){if(!u(s))return!1;var o=a(s);return"[object Function]"==o||"[object GeneratorFunction]"==o||"[object AsyncFunction]"==o||"[object Proxy]"==o}},1907:(s,o,i)=>{"use strict";var a=i(41505),u=Function.prototype,_=u.call,w=a&&u.bind.bind(_,_);s.exports=a?w:function(s){return function(){return _.apply(s,arguments)}}},2205:function(s,o,i){var a;a=void 0!==i.g?i.g:this,s.exports=function(s){if(s.CSS&&s.CSS.escape)return s.CSS.escape;var cssEscape=function(s){if(0==arguments.length)throw new TypeError("`CSS.escape` requires an argument.");for(var o,i=String(s),a=i.length,u=-1,_="",w=i.charCodeAt(0);++u=1&&o<=31||127==o||0==u&&o>=48&&o<=57||1==u&&o>=48&&o<=57&&45==w?"\\"+o.toString(16)+" ":0==u&&1==a&&45==o||!(o>=128||45==o||95==o||o>=48&&o<=57||o>=65&&o<=90||o>=97&&o<=122)?"\\"+i.charAt(u):i.charAt(u):_+="�";return _};return s.CSS||(s.CSS={}),s.CSS.escape=cssEscape,cssEscape}(a)},2209:(s,o,i)=>{"use strict";var a,u=i(9404),_=function productionTypeChecker(){invariant(!1,"ImmutablePropTypes type checking code is stripped in production.")};_.isRequired=_;var w=function getProductionTypeChecker(){return _};function getPropType(s){var o=typeof s;return Array.isArray(s)?"array":s instanceof RegExp?"object":s instanceof u.Iterable?"Immutable."+s.toSource().split(" ")[0]:o}function createChainableTypeChecker(s){function checkType(o,i,a,u,_,w){for(var x=arguments.length,C=Array(x>6?x-6:0),j=6;j>",null!=i[a]?s.apply(void 0,[i,a,u,_,w].concat(C)):o?new Error("Required "+_+" `"+w+"` was not specified in `"+u+"`."):void 0}var o=checkType.bind(null,!1);return o.isRequired=checkType.bind(null,!0),o}function createIterableSubclassTypeChecker(s,o){return function createImmutableTypeChecker(s,o){return createChainableTypeChecker((function validate(i,a,u,_,w){var x=i[a];if(!o(x)){var C=getPropType(x);return new Error("Invalid "+_+" `"+w+"` of type `"+C+"` supplied to `"+u+"`, expected `"+s+"`.")}return null}))}("Iterable."+s,(function(s){return u.Iterable.isIterable(s)&&o(s)}))}(a={listOf:w,mapOf:w,orderedMapOf:w,setOf:w,orderedSetOf:w,stackOf:w,iterableOf:w,recordOf:w,shape:w,contains:w,mapContains:w,orderedMapContains:w,list:_,map:_,orderedMap:_,set:_,orderedSet:_,stack:_,seq:_,record:_,iterable:_}).iterable.indexed=createIterableSubclassTypeChecker("Indexed",u.Iterable.isIndexed),a.iterable.keyed=createIterableSubclassTypeChecker("Keyed",u.Iterable.isKeyed),s.exports=a},2404:(s,o,i)=>{var a=i(60270);s.exports=function isEqual(s,o){return a(s,o)}},2523:s=>{s.exports=function baseFindIndex(s,o,i,a){for(var u=s.length,_=i+(a?1:-1);a?_--:++_{"use strict";var a=i(45951),u=Object.defineProperty;s.exports=function(s,o){try{u(a,s,{value:o,configurable:!0,writable:!0})}catch(i){a[s]=o}return o}},2694:(s,o,i)=>{"use strict";var a=i(6925);function emptyFunction(){}function emptyFunctionWithReset(){}emptyFunctionWithReset.resetWarningCache=emptyFunction,s.exports=function(){function shim(s,o,i,u,_,w){if(w!==a){var x=new Error("Calling PropTypes validators directly is not supported by the `prop-types` package. Use PropTypes.checkPropTypes() to call them. Read more at http://fb.me/use-check-prop-types");throw x.name="Invariant Violation",x}}function getShim(){return shim}shim.isRequired=shim;var s={array:shim,bigint:shim,bool:shim,func:shim,number:shim,object:shim,string:shim,symbol:shim,any:shim,arrayOf:getShim,element:shim,elementType:shim,instanceOf:getShim,node:shim,objectOf:getShim,oneOf:getShim,oneOfType:getShim,shape:getShim,exact:getShim,checkPropTypes:emptyFunctionWithReset,resetWarningCache:emptyFunction};return s.PropTypes=s,s}},2874:s=>{s.exports={}},2875:(s,o,i)=>{"use strict";var a=i(23045),u=i(80376);s.exports=Object.keys||function keys(s){return a(s,u)}},2955:(s,o,i)=>{"use strict";var a,u=i(65606);function _defineProperty(s,o,i){return(o=function _toPropertyKey(s){var o=function _toPrimitive(s,o){if("object"!=typeof s||null===s)return s;var i=s[Symbol.toPrimitive];if(void 0!==i){var a=i.call(s,o||"default");if("object"!=typeof a)return a;throw new TypeError("@@toPrimitive must return a primitive value.")}return("string"===o?String:Number)(s)}(s,"string");return"symbol"==typeof o?o:String(o)}(o))in s?Object.defineProperty(s,o,{value:i,enumerable:!0,configurable:!0,writable:!0}):s[o]=i,s}var _=i(86238),w=Symbol("lastResolve"),x=Symbol("lastReject"),C=Symbol("error"),j=Symbol("ended"),L=Symbol("lastPromise"),B=Symbol("handlePromise"),$=Symbol("stream");function createIterResult(s,o){return{value:s,done:o}}function readAndResolve(s){var o=s[w];if(null!==o){var i=s[$].read();null!==i&&(s[L]=null,s[w]=null,s[x]=null,o(createIterResult(i,!1)))}}function onReadable(s){u.nextTick(readAndResolve,s)}var V=Object.getPrototypeOf((function(){})),U=Object.setPrototypeOf((_defineProperty(a={get stream(){return this[$]},next:function next(){var s=this,o=this[C];if(null!==o)return Promise.reject(o);if(this[j])return Promise.resolve(createIterResult(void 0,!0));if(this[$].destroyed)return new Promise((function(o,i){u.nextTick((function(){s[C]?i(s[C]):o(createIterResult(void 0,!0))}))}));var i,a=this[L];if(a)i=new Promise(function wrapForNext(s,o){return function(i,a){s.then((function(){o[j]?i(createIterResult(void 0,!0)):o[B](i,a)}),a)}}(a,this));else{var _=this[$].read();if(null!==_)return Promise.resolve(createIterResult(_,!1));i=new Promise(this[B])}return this[L]=i,i}},Symbol.asyncIterator,(function(){return this})),_defineProperty(a,"return",(function _return(){var s=this;return new Promise((function(o,i){s[$].destroy(null,(function(s){s?i(s):o(createIterResult(void 0,!0))}))}))})),a),V);s.exports=function createReadableStreamAsyncIterator(s){var o,i=Object.create(U,(_defineProperty(o={},$,{value:s,writable:!0}),_defineProperty(o,w,{value:null,writable:!0}),_defineProperty(o,x,{value:null,writable:!0}),_defineProperty(o,C,{value:null,writable:!0}),_defineProperty(o,j,{value:s._readableState.endEmitted,writable:!0}),_defineProperty(o,B,{value:function value(s,o){var a=i[$].read();a?(i[L]=null,i[w]=null,i[x]=null,s(createIterResult(a,!1))):(i[w]=s,i[x]=o)},writable:!0}),o));return i[L]=null,_(s,(function(s){if(s&&"ERR_STREAM_PREMATURE_CLOSE"!==s.code){var o=i[x];return null!==o&&(i[L]=null,i[w]=null,i[x]=null,o(s)),void(i[C]=s)}var a=i[w];null!==a&&(i[L]=null,i[w]=null,i[x]=null,a(createIterResult(void 0,!0))),i[j]=!0})),s.on("readable",onReadable.bind(null,i)),i}},3110:(s,o,i)=>{const a=i(5187),u=i(85015),_=i(98023),w=i(53812),x=i(23805),C=i(85105),j=i(86804);class Namespace{constructor(s){this.elementMap={},this.elementDetection=[],this.Element=j.Element,this.KeyValuePair=j.KeyValuePair,s&&s.noDefault||this.useDefault(),this._attributeElementKeys=[],this._attributeElementArrayKeys=[]}use(s){return s.namespace&&s.namespace({base:this}),s.load&&s.load({base:this}),this}useDefault(){return this.register("null",j.NullElement).register("string",j.StringElement).register("number",j.NumberElement).register("boolean",j.BooleanElement).register("array",j.ArrayElement).register("object",j.ObjectElement).register("member",j.MemberElement).register("ref",j.RefElement).register("link",j.LinkElement),this.detect(a,j.NullElement,!1).detect(u,j.StringElement,!1).detect(_,j.NumberElement,!1).detect(w,j.BooleanElement,!1).detect(Array.isArray,j.ArrayElement,!1).detect(x,j.ObjectElement,!1),this}register(s,o){return this._elements=void 0,this.elementMap[s]=o,this}unregister(s){return this._elements=void 0,delete this.elementMap[s],this}detect(s,o,i){return void 0===i||i?this.elementDetection.unshift([s,o]):this.elementDetection.push([s,o]),this}toElement(s){if(s instanceof this.Element)return s;let o;for(let i=0;i{const o=s[0].toUpperCase()+s.substr(1);this._elements[o]=this.elementMap[s]}))),this._elements}get serialiser(){return new C(this)}}C.prototype.Namespace=Namespace,s.exports=Namespace},3121:(s,o,i)=>{"use strict";var a=i(65482),u=Math.min;s.exports=function(s){var o=a(s);return o>0?u(o,9007199254740991):0}},3209:(s,o,i)=>{var a=i(91596),u=i(53320),_=i(36306),w="__lodash_placeholder__",x=128,C=Math.min;s.exports=function mergeData(s,o){var i=s[1],j=o[1],L=i|j,B=L<131,$=j==x&&8==i||j==x&&256==i&&s[7].length<=o[8]||384==j&&o[7].length<=o[8]&&8==i;if(!B&&!$)return s;1&j&&(s[2]=o[2],L|=1&i?0:4);var V=o[3];if(V){var U=s[3];s[3]=U?a(U,V,o[4]):V,s[4]=U?_(s[3],w):o[4]}return(V=o[5])&&(U=s[5],s[5]=U?u(U,V,o[6]):V,s[6]=U?_(s[5],w):o[6]),(V=o[7])&&(s[7]=V),j&x&&(s[8]=null==s[8]?o[8]:C(s[8],o[8])),null==s[9]&&(s[9]=o[9]),s[0]=o[0],s[1]=L,s}},3650:(s,o,i)=>{var a=i(74335)(Object.keys,Object);s.exports=a},3656:(s,o,i)=>{s=i.nmd(s);var a=i(9325),u=i(89935),_=o&&!o.nodeType&&o,w=_&&s&&!s.nodeType&&s,x=w&&w.exports===_?a.Buffer:void 0,C=(x?x.isBuffer:void 0)||u;s.exports=C},4509:(s,o,i)=>{var a=i(12651);s.exports=function mapCacheHas(s){return a(this,s).has(s)}},4640:s=>{"use strict";var o=String;s.exports=function(s){try{return o(s)}catch(s){return"Object"}}},4664:(s,o,i)=>{var a=i(79770),u=i(63345),_=Object.prototype.propertyIsEnumerable,w=Object.getOwnPropertySymbols,x=w?function(s){return null==s?[]:(s=Object(s),a(w(s),(function(o){return _.call(s,o)})))}:u;s.exports=x},4901:(s,o,i)=>{var a=i(72552),u=i(30294),_=i(40346),w={};w["[object Float32Array]"]=w["[object Float64Array]"]=w["[object Int8Array]"]=w["[object Int16Array]"]=w["[object Int32Array]"]=w["[object Uint8Array]"]=w["[object Uint8ClampedArray]"]=w["[object Uint16Array]"]=w["[object Uint32Array]"]=!0,w["[object Arguments]"]=w["[object Array]"]=w["[object ArrayBuffer]"]=w["[object Boolean]"]=w["[object DataView]"]=w["[object Date]"]=w["[object Error]"]=w["[object Function]"]=w["[object Map]"]=w["[object Number]"]=w["[object Object]"]=w["[object RegExp]"]=w["[object Set]"]=w["[object String]"]=w["[object WeakMap]"]=!1,s.exports=function baseIsTypedArray(s){return _(s)&&u(s.length)&&!!w[a(s)]}},4993:(s,o,i)=>{"use strict";var a=i(16946),u=i(74239);s.exports=function(s){return a(u(s))}},5187:s=>{s.exports=function isNull(s){return null===s}},5419:s=>{s.exports=function(s,o,i,a){var u=new Blob(void 0!==a?[a,s]:[s],{type:i||"application/octet-stream"});if(void 0!==window.navigator.msSaveBlob)window.navigator.msSaveBlob(u,o);else{var _=window.URL&&window.URL.createObjectURL?window.URL.createObjectURL(u):window.webkitURL.createObjectURL(u),w=document.createElement("a");w.style.display="none",w.href=_,w.setAttribute("download",o),void 0===w.download&&w.setAttribute("target","_blank"),document.body.appendChild(w),w.click(),setTimeout((function(){document.body.removeChild(w),window.URL.revokeObjectURL(_)}),200)}}},5556:(s,o,i)=>{s.exports=i(2694)()},5861:(s,o,i)=>{var a=i(55580),u=i(68223),_=i(32804),w=i(76545),x=i(28303),C=i(72552),j=i(47473),L="[object Map]",B="[object Promise]",$="[object Set]",V="[object WeakMap]",U="[object DataView]",z=j(a),Y=j(u),Z=j(_),ee=j(w),ie=j(x),ae=C;(a&&ae(new a(new ArrayBuffer(1)))!=U||u&&ae(new u)!=L||_&&ae(_.resolve())!=B||w&&ae(new w)!=$||x&&ae(new x)!=V)&&(ae=function(s){var o=C(s),i="[object Object]"==o?s.constructor:void 0,a=i?j(i):"";if(a)switch(a){case z:return U;case Y:return L;case Z:return B;case ee:return $;case ie:return V}return o}),s.exports=ae},6048:s=>{s.exports=function negate(s){if("function"!=typeof s)throw new TypeError("Expected a function");return function(){var o=arguments;switch(o.length){case 0:return!s.call(this);case 1:return!s.call(this,o[0]);case 2:return!s.call(this,o[0],o[1]);case 3:return!s.call(this,o[0],o[1],o[2])}return!s.apply(this,o)}}},6205:s=>{s.exports={ROOT:0,GROUP:1,POSITION:2,SET:3,RANGE:4,REPETITION:5,REFERENCE:6,CHAR:7}},6233:(s,o,i)=>{const a=i(6048),u=i(10316),_=i(92340);class ArrayElement extends u{constructor(s,o,i){super(s||[],o,i),this.element="array"}primitive(){return"array"}get(s){return this.content[s]}getValue(s){const o=this.get(s);if(o)return o.toValue()}getIndex(s){return this.content[s]}set(s,o){return this.content[s]=this.refract(o),this}remove(s){const o=this.content.splice(s,1);return o.length?o[0]:null}map(s,o){return this.content.map(s,o)}flatMap(s,o){return this.map(s,o).reduce(((s,o)=>s.concat(o)),[])}compactMap(s,o){const i=[];return this.forEach((a=>{const u=s.bind(o)(a);u&&i.push(u)})),i}filter(s,o){return new _(this.content.filter(s,o))}reject(s,o){return this.filter(a(s),o)}reduce(s,o){let i,a;void 0!==o?(i=0,a=this.refract(o)):(i=1,a="object"===this.primitive()?this.first.value:this.first);for(let o=i;o{s.bind(o)(i,this.refract(a))}))}shift(){return this.content.shift()}unshift(s){this.content.unshift(this.refract(s))}push(s){return this.content.push(this.refract(s)),this}add(s){this.push(s)}findElements(s,o){const i=o||{},a=!!i.recursive,u=void 0===i.results?[]:i.results;return this.forEach(((o,i,_)=>{a&&void 0!==o.findElements&&o.findElements(s,{results:u,recursive:a}),s(o,i,_)&&u.push(o)})),u}find(s){return new _(this.findElements(s,{recursive:!0}))}findByElement(s){return this.find((o=>o.element===s))}findByClass(s){return this.find((o=>o.classes.includes(s)))}getById(s){return this.find((o=>o.id.toValue()===s)).first}includes(s){return this.content.some((o=>o.equals(s)))}contains(s){return this.includes(s)}empty(){return new this.constructor([])}"fantasy-land/empty"(){return this.empty()}concat(s){return new this.constructor(this.content.concat(s.content))}"fantasy-land/concat"(s){return this.concat(s)}"fantasy-land/map"(s){return new this.constructor(this.map(s))}"fantasy-land/chain"(s){return this.map((o=>s(o)),this).reduce(((s,o)=>s.concat(o)),this.empty())}"fantasy-land/filter"(s){return new this.constructor(this.content.filter(s))}"fantasy-land/reduce"(s,o){return this.content.reduce(s,o)}get length(){return this.content.length}get isEmpty(){return 0===this.content.length}get first(){return this.getIndex(0)}get second(){return this.getIndex(1)}get last(){return this.getIndex(this.length-1)}}ArrayElement.empty=function empty(){return new this},ArrayElement["fantasy-land/empty"]=ArrayElement.empty,"undefined"!=typeof Symbol&&(ArrayElement.prototype[Symbol.iterator]=function symbol(){return this.content[Symbol.iterator]()}),s.exports=ArrayElement},6499:(s,o,i)=>{"use strict";var a=i(1907),u=0,_=Math.random(),w=a(1..toString);s.exports=function(s){return"Symbol("+(void 0===s?"":s)+")_"+w(++u+_,36)}},6925:s=>{"use strict";s.exports="SECRET_DO_NOT_PASS_THIS_OR_YOU_WILL_BE_FIRED"},7057:(s,o,i)=>{"use strict";var a=i(11470).charAt,u=i(90160),_=i(64932),w=i(60183),x=i(59550),C="String Iterator",j=_.set,L=_.getterFor(C);w(String,"String",(function(s){j(this,{type:C,string:u(s),index:0})}),(function next(){var s,o=L(this),i=o.string,u=o.index;return u>=i.length?x(void 0,!0):(s=a(i,u),o.index+=s.length,x(s,!1))}))},7309:(s,o,i)=>{var a=i(62006)(i(24713));s.exports=a},7376:s=>{"use strict";s.exports=!0},7463:(s,o,i)=>{"use strict";var a=i(98828),u=i(62250),_=/#|\.prototype\./,isForced=function(s,o){var i=x[w(s)];return i===j||i!==C&&(u(o)?a(o):!!o)},w=isForced.normalize=function(s){return String(s).replace(_,".").toLowerCase()},x=isForced.data={},C=isForced.NATIVE="N",j=isForced.POLYFILL="P";s.exports=isForced},7666:(s,o,i)=>{var a=i(84851),u=i(953);function _extends(){var o;return s.exports=_extends=a?u(o=a).call(o):function(s){for(var o=1;o{const a=i(6205);o.wordBoundary=()=>({type:a.POSITION,value:"b"}),o.nonWordBoundary=()=>({type:a.POSITION,value:"B"}),o.begin=()=>({type:a.POSITION,value:"^"}),o.end=()=>({type:a.POSITION,value:"$"})},8068:s=>{"use strict";var o=(()=>{var s=Object.defineProperty,o=Object.getOwnPropertyDescriptor,i=Object.getOwnPropertyNames,a=Object.getOwnPropertySymbols,u=Object.prototype.hasOwnProperty,_=Object.prototype.propertyIsEnumerable,__defNormalProp=(o,i,a)=>i in o?s(o,i,{enumerable:!0,configurable:!0,writable:!0,value:a}):o[i]=a,__spreadValues=(s,o)=>{for(var i in o||(o={}))u.call(o,i)&&__defNormalProp(s,i,o[i]);if(a)for(var i of a(o))_.call(o,i)&&__defNormalProp(s,i,o[i]);return s},__publicField=(s,o,i)=>__defNormalProp(s,"symbol"!=typeof o?o+"":o,i),w={};((o,i)=>{for(var a in i)s(o,a,{get:i[a],enumerable:!0})})(w,{DEFAULT_OPTIONS:()=>C,DEFAULT_UUID_LENGTH:()=>x,default:()=>B});var x=6,C={dictionary:"alphanum",shuffle:!0,debug:!1,length:x,counter:0},j=class _ShortUniqueId{constructor(s={}){__publicField(this,"counter"),__publicField(this,"debug"),__publicField(this,"dict"),__publicField(this,"version"),__publicField(this,"dictIndex",0),__publicField(this,"dictRange",[]),__publicField(this,"lowerBound",0),__publicField(this,"upperBound",0),__publicField(this,"dictLength",0),__publicField(this,"uuidLength"),__publicField(this,"_digit_first_ascii",48),__publicField(this,"_digit_last_ascii",58),__publicField(this,"_alpha_lower_first_ascii",97),__publicField(this,"_alpha_lower_last_ascii",123),__publicField(this,"_hex_last_ascii",103),__publicField(this,"_alpha_upper_first_ascii",65),__publicField(this,"_alpha_upper_last_ascii",91),__publicField(this,"_number_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii]}),__publicField(this,"_alpha_dict_ranges",{lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii],upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),__publicField(this,"_alpha_lower_dict_ranges",{lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii]}),__publicField(this,"_alpha_upper_dict_ranges",{upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),__publicField(this,"_alphanum_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii],lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii],upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),__publicField(this,"_alphanum_lower_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii],lowerCase:[this._alpha_lower_first_ascii,this._alpha_lower_last_ascii]}),__publicField(this,"_alphanum_upper_dict_ranges",{digits:[this._digit_first_ascii,this._digit_last_ascii],upperCase:[this._alpha_upper_first_ascii,this._alpha_upper_last_ascii]}),__publicField(this,"_hex_dict_ranges",{decDigits:[this._digit_first_ascii,this._digit_last_ascii],alphaDigits:[this._alpha_lower_first_ascii,this._hex_last_ascii]}),__publicField(this,"_dict_ranges",{_number_dict_ranges:this._number_dict_ranges,_alpha_dict_ranges:this._alpha_dict_ranges,_alpha_lower_dict_ranges:this._alpha_lower_dict_ranges,_alpha_upper_dict_ranges:this._alpha_upper_dict_ranges,_alphanum_dict_ranges:this._alphanum_dict_ranges,_alphanum_lower_dict_ranges:this._alphanum_lower_dict_ranges,_alphanum_upper_dict_ranges:this._alphanum_upper_dict_ranges,_hex_dict_ranges:this._hex_dict_ranges}),__publicField(this,"log",((...s)=>{const o=[...s];o[0]="[short-unique-id] ".concat(s[0]),!0!==this.debug||"undefined"==typeof console||null===console||console.log(...o)})),__publicField(this,"_normalizeDictionary",((s,o)=>{let i;if(s&&Array.isArray(s)&&s.length>1)i=s;else{i=[],this.dictIndex=0;const o="_".concat(s,"_dict_ranges"),a=this._dict_ranges[o];let u=0;for(const[,s]of Object.entries(a)){const[o,i]=s;u+=Math.abs(i-o)}i=new Array(u);let _=0;for(const[,s]of Object.entries(a)){this.dictRange=s,this.lowerBound=this.dictRange[0],this.upperBound=this.dictRange[1];const o=this.lowerBound<=this.upperBound,a=this.lowerBound,u=this.upperBound;if(o)for(let s=a;su;s--)i[_++]=String.fromCharCode(s),this.dictIndex=s}i.length=_}if(o){for(let s=i.length-1;s>0;s--){const o=Math.floor(Math.random()*(s+1));[i[s],i[o]]=[i[o],i[s]]}}return i})),__publicField(this,"setDictionary",((s,o)=>{this.dict=this._normalizeDictionary(s,o),this.dictLength=this.dict.length,this.setCounter(0)})),__publicField(this,"seq",(()=>this.sequentialUUID())),__publicField(this,"sequentialUUID",(()=>{const s=this.dictLength,o=this.dict;let i=this.counter;const a=[];do{const u=i%s;i=Math.trunc(i/s),a.push(o[u])}while(0!==i);const u=a.join("");return this.counter+=1,u})),__publicField(this,"rnd",((s=this.uuidLength||x)=>this.randomUUID(s))),__publicField(this,"randomUUID",((s=this.uuidLength||x)=>{if(null==s||s<1)throw new Error("Invalid UUID Length Provided");const o=new Array(s),i=this.dictLength,a=this.dict;for(let u=0;uthis.formattedUUID(s,o))),__publicField(this,"formattedUUID",((s,o)=>{const i={$r:this.randomUUID,$s:this.sequentialUUID,$t:this.stamp};return s.replace(/\$[rs]\d{0,}|\$t0|\$t[1-9]\d{1,}/g,(s=>{const a=s.slice(0,2),u=Number.parseInt(s.slice(2),10);return"$s"===a?i[a]().padStart(u,"0"):"$t"===a&&o?i[a](u,o):i[a](u)}))})),__publicField(this,"availableUUIDs",((s=this.uuidLength)=>Number.parseFloat(([...new Set(this.dict)].length**s).toFixed(0)))),__publicField(this,"_collisionCache",new Map),__publicField(this,"approxMaxBeforeCollision",((s=this.availableUUIDs(this.uuidLength))=>{const o=s,i=this._collisionCache.get(o);if(void 0!==i)return i;const a=Number.parseFloat(Math.sqrt(Math.PI/2*s).toFixed(20));return this._collisionCache.set(o,a),a})),__publicField(this,"collisionProbability",((s=this.availableUUIDs(this.uuidLength),o=this.uuidLength)=>Number.parseFloat((this.approxMaxBeforeCollision(s)/this.availableUUIDs(o)).toFixed(20)))),__publicField(this,"uniqueness",((s=this.availableUUIDs(this.uuidLength))=>{const o=Number.parseFloat((1-this.approxMaxBeforeCollision(s)/s).toFixed(20));return o>1?1:o<0?0:o})),__publicField(this,"getVersion",(()=>this.version)),__publicField(this,"stamp",((s,o)=>{const i=Math.floor(+(o||new Date)/1e3).toString(16);if("number"==typeof s&&0===s)return i;if("number"!=typeof s||s<10)throw new Error(["Param finalLength must be a number greater than or equal to 10,","or 0 if you want the raw hexadecimal timestamp"].join("\n"));const a=s-9,u=Math.round(Math.random()*(a>15?15:a)),_=this.randomUUID(a);return"".concat(_.substring(0,u)).concat(i).concat(_.substring(u)).concat(u.toString(16))})),__publicField(this,"parseStamp",((s,o)=>{if(o&&!/t0|t[1-9]\d{1,}/.test(o))throw new Error("Cannot extract date from a formated UUID with no timestamp in the format");const i=o?o.replace(/\$[rs]\d{0,}|\$t0|\$t[1-9]\d{1,}/g,(s=>{const o={$r:s=>[...Array(s)].map((()=>"r")).join(""),$s:s=>[...Array(s)].map((()=>"s")).join(""),$t:s=>[...Array(s)].map((()=>"t")).join("")},i=s.slice(0,2),a=Number.parseInt(s.slice(2),10);return o[i](a)})).replace(/^(.*?)(t{8,})(.*)$/g,((o,i,a)=>s.substring(i.length,i.length+a.length))):s;if(8===i.length)return new Date(1e3*Number.parseInt(i,16));if(i.length<10)throw new Error("Stamp length invalid");const a=Number.parseInt(i.substring(i.length-1),16);return new Date(1e3*Number.parseInt(i.substring(a,a+8),16))})),__publicField(this,"setCounter",(s=>{this.counter=s})),__publicField(this,"validate",((s,o)=>{const i=o?this._normalizeDictionary(o):this.dict;return s.split("").every((s=>i.includes(s)))}));const o=__spreadValues(__spreadValues({},C),s);this.counter=0,this.debug=!1,this.dict=[],this.version="5.3.2";const{dictionary:i,shuffle:a,length:u,counter:_}=o;this.uuidLength=u,this.setDictionary(i,a),this.setCounter(_),this.debug=o.debug,this.log(this.dict),this.log("Generator instantiated with Dictionary Size ".concat(this.dictLength," and counter set to ").concat(this.counter)),this.log=this.log.bind(this),this.setDictionary=this.setDictionary.bind(this),this.setCounter=this.setCounter.bind(this),this.seq=this.seq.bind(this),this.sequentialUUID=this.sequentialUUID.bind(this),this.rnd=this.rnd.bind(this),this.randomUUID=this.randomUUID.bind(this),this.fmt=this.fmt.bind(this),this.formattedUUID=this.formattedUUID.bind(this),this.availableUUIDs=this.availableUUIDs.bind(this),this.approxMaxBeforeCollision=this.approxMaxBeforeCollision.bind(this),this.collisionProbability=this.collisionProbability.bind(this),this.uniqueness=this.uniqueness.bind(this),this.getVersion=this.getVersion.bind(this),this.stamp=this.stamp.bind(this),this.parseStamp=this.parseStamp.bind(this)}};__publicField(j,"default",j);var L,B=j;return L=w,((a,_,w,x)=>{if(_&&"object"==typeof _||"function"==typeof _)for(let C of i(_))u.call(a,C)||C===w||s(a,C,{get:()=>_[C],enumerable:!(x=o(_,C))||x.enumerable});return a})(s({},"__esModule",{value:!0}),L)})();s.exports=o.default,"undefined"!=typeof window&&(o=o.default)},9325:(s,o,i)=>{var a=i(34840),u="object"==typeof self&&self&&self.Object===Object&&self,_=a||u||Function("return this")();s.exports=_},9404:function(s){s.exports=function(){"use strict";var s=Array.prototype.slice;function createClass(s,o){o&&(s.prototype=Object.create(o.prototype)),s.prototype.constructor=s}function Iterable(s){return isIterable(s)?s:Seq(s)}function KeyedIterable(s){return isKeyed(s)?s:KeyedSeq(s)}function IndexedIterable(s){return isIndexed(s)?s:IndexedSeq(s)}function SetIterable(s){return isIterable(s)&&!isAssociative(s)?s:SetSeq(s)}function isIterable(s){return!(!s||!s[o])}function isKeyed(s){return!(!s||!s[i])}function isIndexed(s){return!(!s||!s[a])}function isAssociative(s){return isKeyed(s)||isIndexed(s)}function isOrdered(s){return!(!s||!s[u])}createClass(KeyedIterable,Iterable),createClass(IndexedIterable,Iterable),createClass(SetIterable,Iterable),Iterable.isIterable=isIterable,Iterable.isKeyed=isKeyed,Iterable.isIndexed=isIndexed,Iterable.isAssociative=isAssociative,Iterable.isOrdered=isOrdered,Iterable.Keyed=KeyedIterable,Iterable.Indexed=IndexedIterable,Iterable.Set=SetIterable;var o="@@__IMMUTABLE_ITERABLE__@@",i="@@__IMMUTABLE_KEYED__@@",a="@@__IMMUTABLE_INDEXED__@@",u="@@__IMMUTABLE_ORDERED__@@",_="delete",w=5,x=1<>>0;if(""+i!==o||4294967295===i)return NaN;o=i}return o<0?ensureSize(s)+o:o}function returnTrue(){return!0}function wholeSlice(s,o,i){return(0===s||void 0!==i&&s<=-i)&&(void 0===o||void 0!==i&&o>=i)}function resolveBegin(s,o){return resolveIndex(s,o,0)}function resolveEnd(s,o){return resolveIndex(s,o,o)}function resolveIndex(s,o,i){return void 0===s?i:s<0?Math.max(0,o+s):void 0===o?s:Math.min(o,s)}var $=0,V=1,U=2,z="function"==typeof Symbol&&Symbol.iterator,Y="@@iterator",Z=z||Y;function Iterator(s){this.next=s}function iteratorValue(s,o,i,a){var u=0===s?o:1===s?i:[o,i];return a?a.value=u:a={value:u,done:!1},a}function iteratorDone(){return{value:void 0,done:!0}}function hasIterator(s){return!!getIteratorFn(s)}function isIterator(s){return s&&"function"==typeof s.next}function getIterator(s){var o=getIteratorFn(s);return o&&o.call(s)}function getIteratorFn(s){var o=s&&(z&&s[z]||s[Y]);if("function"==typeof o)return o}function isArrayLike(s){return s&&"number"==typeof s.length}function Seq(s){return null==s?emptySequence():isIterable(s)?s.toSeq():seqFromValue(s)}function KeyedSeq(s){return null==s?emptySequence().toKeyedSeq():isIterable(s)?isKeyed(s)?s.toSeq():s.fromEntrySeq():keyedSeqFromValue(s)}function IndexedSeq(s){return null==s?emptySequence():isIterable(s)?isKeyed(s)?s.entrySeq():s.toIndexedSeq():indexedSeqFromValue(s)}function SetSeq(s){return(null==s?emptySequence():isIterable(s)?isKeyed(s)?s.entrySeq():s:indexedSeqFromValue(s)).toSetSeq()}Iterator.prototype.toString=function(){return"[Iterator]"},Iterator.KEYS=$,Iterator.VALUES=V,Iterator.ENTRIES=U,Iterator.prototype.inspect=Iterator.prototype.toSource=function(){return this.toString()},Iterator.prototype[Z]=function(){return this},createClass(Seq,Iterable),Seq.of=function(){return Seq(arguments)},Seq.prototype.toSeq=function(){return this},Seq.prototype.toString=function(){return this.__toString("Seq {","}")},Seq.prototype.cacheResult=function(){return!this._cache&&this.__iterateUncached&&(this._cache=this.entrySeq().toArray(),this.size=this._cache.length),this},Seq.prototype.__iterate=function(s,o){return seqIterate(this,s,o,!0)},Seq.prototype.__iterator=function(s,o){return seqIterator(this,s,o,!0)},createClass(KeyedSeq,Seq),KeyedSeq.prototype.toKeyedSeq=function(){return this},createClass(IndexedSeq,Seq),IndexedSeq.of=function(){return IndexedSeq(arguments)},IndexedSeq.prototype.toIndexedSeq=function(){return this},IndexedSeq.prototype.toString=function(){return this.__toString("Seq [","]")},IndexedSeq.prototype.__iterate=function(s,o){return seqIterate(this,s,o,!1)},IndexedSeq.prototype.__iterator=function(s,o){return seqIterator(this,s,o,!1)},createClass(SetSeq,Seq),SetSeq.of=function(){return SetSeq(arguments)},SetSeq.prototype.toSetSeq=function(){return this},Seq.isSeq=isSeq,Seq.Keyed=KeyedSeq,Seq.Set=SetSeq,Seq.Indexed=IndexedSeq;var ee,ie,ae,ce="@@__IMMUTABLE_SEQ__@@";function ArraySeq(s){this._array=s,this.size=s.length}function ObjectSeq(s){var o=Object.keys(s);this._object=s,this._keys=o,this.size=o.length}function IterableSeq(s){this._iterable=s,this.size=s.length||s.size}function IteratorSeq(s){this._iterator=s,this._iteratorCache=[]}function isSeq(s){return!(!s||!s[ce])}function emptySequence(){return ee||(ee=new ArraySeq([]))}function keyedSeqFromValue(s){var o=Array.isArray(s)?new ArraySeq(s).fromEntrySeq():isIterator(s)?new IteratorSeq(s).fromEntrySeq():hasIterator(s)?new IterableSeq(s).fromEntrySeq():"object"==typeof s?new ObjectSeq(s):void 0;if(!o)throw new TypeError("Expected Array or iterable object of [k, v] entries, or keyed object: "+s);return o}function indexedSeqFromValue(s){var o=maybeIndexedSeqFromValue(s);if(!o)throw new TypeError("Expected Array or iterable object of values: "+s);return o}function seqFromValue(s){var o=maybeIndexedSeqFromValue(s)||"object"==typeof s&&new ObjectSeq(s);if(!o)throw new TypeError("Expected Array or iterable object of values, or keyed object: "+s);return o}function maybeIndexedSeqFromValue(s){return isArrayLike(s)?new ArraySeq(s):isIterator(s)?new IteratorSeq(s):hasIterator(s)?new IterableSeq(s):void 0}function seqIterate(s,o,i,a){var u=s._cache;if(u){for(var _=u.length-1,w=0;w<=_;w++){var x=u[i?_-w:w];if(!1===o(x[1],a?x[0]:w,s))return w+1}return w}return s.__iterateUncached(o,i)}function seqIterator(s,o,i,a){var u=s._cache;if(u){var _=u.length-1,w=0;return new Iterator((function(){var s=u[i?_-w:w];return w++>_?iteratorDone():iteratorValue(o,a?s[0]:w-1,s[1])}))}return s.__iteratorUncached(o,i)}function fromJS(s,o){return o?fromJSWith(o,s,"",{"":s}):fromJSDefault(s)}function fromJSWith(s,o,i,a){return Array.isArray(o)?s.call(a,i,IndexedSeq(o).map((function(i,a){return fromJSWith(s,i,a,o)}))):isPlainObj(o)?s.call(a,i,KeyedSeq(o).map((function(i,a){return fromJSWith(s,i,a,o)}))):o}function fromJSDefault(s){return Array.isArray(s)?IndexedSeq(s).map(fromJSDefault).toList():isPlainObj(s)?KeyedSeq(s).map(fromJSDefault).toMap():s}function isPlainObj(s){return s&&(s.constructor===Object||void 0===s.constructor)}function is(s,o){if(s===o||s!=s&&o!=o)return!0;if(!s||!o)return!1;if("function"==typeof s.valueOf&&"function"==typeof o.valueOf){if((s=s.valueOf())===(o=o.valueOf())||s!=s&&o!=o)return!0;if(!s||!o)return!1}return!("function"!=typeof s.equals||"function"!=typeof o.equals||!s.equals(o))}function deepEqual(s,o){if(s===o)return!0;if(!isIterable(o)||void 0!==s.size&&void 0!==o.size&&s.size!==o.size||void 0!==s.__hash&&void 0!==o.__hash&&s.__hash!==o.__hash||isKeyed(s)!==isKeyed(o)||isIndexed(s)!==isIndexed(o)||isOrdered(s)!==isOrdered(o))return!1;if(0===s.size&&0===o.size)return!0;var i=!isAssociative(s);if(isOrdered(s)){var a=s.entries();return o.every((function(s,o){var u=a.next().value;return u&&is(u[1],s)&&(i||is(u[0],o))}))&&a.next().done}var u=!1;if(void 0===s.size)if(void 0===o.size)"function"==typeof s.cacheResult&&s.cacheResult();else{u=!0;var _=s;s=o,o=_}var w=!0,x=o.__iterate((function(o,a){if(i?!s.has(o):u?!is(o,s.get(a,j)):!is(s.get(a,j),o))return w=!1,!1}));return w&&s.size===x}function Repeat(s,o){if(!(this instanceof Repeat))return new Repeat(s,o);if(this._value=s,this.size=void 0===o?1/0:Math.max(0,o),0===this.size){if(ie)return ie;ie=this}}function invariant(s,o){if(!s)throw new Error(o)}function Range(s,o,i){if(!(this instanceof Range))return new Range(s,o,i);if(invariant(0!==i,"Cannot step a Range by 0"),s=s||0,void 0===o&&(o=1/0),i=void 0===i?1:Math.abs(i),oa?iteratorDone():iteratorValue(s,u,i[o?a-u++:u++])}))},createClass(ObjectSeq,KeyedSeq),ObjectSeq.prototype.get=function(s,o){return void 0===o||this.has(s)?this._object[s]:o},ObjectSeq.prototype.has=function(s){return this._object.hasOwnProperty(s)},ObjectSeq.prototype.__iterate=function(s,o){for(var i=this._object,a=this._keys,u=a.length-1,_=0;_<=u;_++){var w=a[o?u-_:_];if(!1===s(i[w],w,this))return _+1}return _},ObjectSeq.prototype.__iterator=function(s,o){var i=this._object,a=this._keys,u=a.length-1,_=0;return new Iterator((function(){var w=a[o?u-_:_];return _++>u?iteratorDone():iteratorValue(s,w,i[w])}))},ObjectSeq.prototype[u]=!0,createClass(IterableSeq,IndexedSeq),IterableSeq.prototype.__iterateUncached=function(s,o){if(o)return this.cacheResult().__iterate(s,o);var i=getIterator(this._iterable),a=0;if(isIterator(i))for(var u;!(u=i.next()).done&&!1!==s(u.value,a++,this););return a},IterableSeq.prototype.__iteratorUncached=function(s,o){if(o)return this.cacheResult().__iterator(s,o);var i=getIterator(this._iterable);if(!isIterator(i))return new Iterator(iteratorDone);var a=0;return new Iterator((function(){var o=i.next();return o.done?o:iteratorValue(s,a++,o.value)}))},createClass(IteratorSeq,IndexedSeq),IteratorSeq.prototype.__iterateUncached=function(s,o){if(o)return this.cacheResult().__iterate(s,o);for(var i,a=this._iterator,u=this._iteratorCache,_=0;_=a.length){var o=i.next();if(o.done)return o;a[u]=o.value}return iteratorValue(s,u,a[u++])}))},createClass(Repeat,IndexedSeq),Repeat.prototype.toString=function(){return 0===this.size?"Repeat []":"Repeat [ "+this._value+" "+this.size+" times ]"},Repeat.prototype.get=function(s,o){return this.has(s)?this._value:o},Repeat.prototype.includes=function(s){return is(this._value,s)},Repeat.prototype.slice=function(s,o){var i=this.size;return wholeSlice(s,o,i)?this:new Repeat(this._value,resolveEnd(o,i)-resolveBegin(s,i))},Repeat.prototype.reverse=function(){return this},Repeat.prototype.indexOf=function(s){return is(this._value,s)?0:-1},Repeat.prototype.lastIndexOf=function(s){return is(this._value,s)?this.size:-1},Repeat.prototype.__iterate=function(s,o){for(var i=0;i=0&&o=0&&ii?iteratorDone():iteratorValue(s,_++,w)}))},Range.prototype.equals=function(s){return s instanceof Range?this._start===s._start&&this._end===s._end&&this._step===s._step:deepEqual(this,s)},createClass(Collection,Iterable),createClass(KeyedCollection,Collection),createClass(IndexedCollection,Collection),createClass(SetCollection,Collection),Collection.Keyed=KeyedCollection,Collection.Indexed=IndexedCollection,Collection.Set=SetCollection;var le="function"==typeof Math.imul&&-2===Math.imul(4294967295,2)?Math.imul:function imul(s,o){var i=65535&(s|=0),a=65535&(o|=0);return i*a+((s>>>16)*a+i*(o>>>16)<<16>>>0)|0};function smi(s){return s>>>1&1073741824|3221225471&s}function hash(s){if(!1===s||null==s)return 0;if("function"==typeof s.valueOf&&(!1===(s=s.valueOf())||null==s))return 0;if(!0===s)return 1;var o=typeof s;if("number"===o){if(s!=s||s===1/0)return 0;var i=0|s;for(i!==s&&(i^=4294967295*s);s>4294967295;)i^=s/=4294967295;return smi(i)}if("string"===o)return s.length>Se?cachedHashString(s):hashString(s);if("function"==typeof s.hashCode)return s.hashCode();if("object"===o)return hashJSObj(s);if("function"==typeof s.toString)return hashString(s.toString());throw new Error("Value type "+o+" cannot be hashed.")}function cachedHashString(s){var o=Pe[s];return void 0===o&&(o=hashString(s),xe===we&&(xe=0,Pe={}),xe++,Pe[s]=o),o}function hashString(s){for(var o=0,i=0;i0)switch(s.nodeType){case 1:return s.uniqueID;case 9:return s.documentElement&&s.documentElement.uniqueID}}var fe,ye="function"==typeof WeakMap;ye&&(fe=new WeakMap);var be=0,_e="__immutablehash__";"function"==typeof Symbol&&(_e=Symbol(_e));var Se=16,we=255,xe=0,Pe={};function assertNotInfinite(s){invariant(s!==1/0,"Cannot perform this action with an infinite size.")}function Map(s){return null==s?emptyMap():isMap(s)&&!isOrdered(s)?s:emptyMap().withMutations((function(o){var i=KeyedIterable(s);assertNotInfinite(i.size),i.forEach((function(s,i){return o.set(i,s)}))}))}function isMap(s){return!(!s||!s[Re])}createClass(Map,KeyedCollection),Map.of=function(){var o=s.call(arguments,0);return emptyMap().withMutations((function(s){for(var i=0;i=o.length)throw new Error("Missing value for key: "+o[i]);s.set(o[i],o[i+1])}}))},Map.prototype.toString=function(){return this.__toString("Map {","}")},Map.prototype.get=function(s,o){return this._root?this._root.get(0,void 0,s,o):o},Map.prototype.set=function(s,o){return updateMap(this,s,o)},Map.prototype.setIn=function(s,o){return this.updateIn(s,j,(function(){return o}))},Map.prototype.remove=function(s){return updateMap(this,s,j)},Map.prototype.deleteIn=function(s){return this.updateIn(s,(function(){return j}))},Map.prototype.update=function(s,o,i){return 1===arguments.length?s(this):this.updateIn([s],o,i)},Map.prototype.updateIn=function(s,o,i){i||(i=o,o=void 0);var a=updateInDeepMap(this,forceIterator(s),o,i);return a===j?void 0:a},Map.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._root=null,this.__hash=void 0,this.__altered=!0,this):emptyMap()},Map.prototype.merge=function(){return mergeIntoMapWith(this,void 0,arguments)},Map.prototype.mergeWith=function(o){return mergeIntoMapWith(this,o,s.call(arguments,1))},Map.prototype.mergeIn=function(o){var i=s.call(arguments,1);return this.updateIn(o,emptyMap(),(function(s){return"function"==typeof s.merge?s.merge.apply(s,i):i[i.length-1]}))},Map.prototype.mergeDeep=function(){return mergeIntoMapWith(this,deepMerger,arguments)},Map.prototype.mergeDeepWith=function(o){var i=s.call(arguments,1);return mergeIntoMapWith(this,deepMergerWith(o),i)},Map.prototype.mergeDeepIn=function(o){var i=s.call(arguments,1);return this.updateIn(o,emptyMap(),(function(s){return"function"==typeof s.mergeDeep?s.mergeDeep.apply(s,i):i[i.length-1]}))},Map.prototype.sort=function(s){return OrderedMap(sortFactory(this,s))},Map.prototype.sortBy=function(s,o){return OrderedMap(sortFactory(this,o,s))},Map.prototype.withMutations=function(s){var o=this.asMutable();return s(o),o.wasAltered()?o.__ensureOwner(this.__ownerID):this},Map.prototype.asMutable=function(){return this.__ownerID?this:this.__ensureOwner(new OwnerID)},Map.prototype.asImmutable=function(){return this.__ensureOwner()},Map.prototype.wasAltered=function(){return this.__altered},Map.prototype.__iterator=function(s,o){return new MapIterator(this,s,o)},Map.prototype.__iterate=function(s,o){var i=this,a=0;return this._root&&this._root.iterate((function(o){return a++,s(o[1],o[0],i)}),o),a},Map.prototype.__ensureOwner=function(s){return s===this.__ownerID?this:s?makeMap(this.size,this._root,s,this.__hash):(this.__ownerID=s,this.__altered=!1,this)},Map.isMap=isMap;var Te,Re="@@__IMMUTABLE_MAP__@@",$e=Map.prototype;function ArrayMapNode(s,o){this.ownerID=s,this.entries=o}function BitmapIndexedNode(s,o,i){this.ownerID=s,this.bitmap=o,this.nodes=i}function HashArrayMapNode(s,o,i){this.ownerID=s,this.count=o,this.nodes=i}function HashCollisionNode(s,o,i){this.ownerID=s,this.keyHash=o,this.entries=i}function ValueNode(s,o,i){this.ownerID=s,this.keyHash=o,this.entry=i}function MapIterator(s,o,i){this._type=o,this._reverse=i,this._stack=s._root&&mapIteratorFrame(s._root)}function mapIteratorValue(s,o){return iteratorValue(s,o[0],o[1])}function mapIteratorFrame(s,o){return{node:s,index:0,__prev:o}}function makeMap(s,o,i,a){var u=Object.create($e);return u.size=s,u._root=o,u.__ownerID=i,u.__hash=a,u.__altered=!1,u}function emptyMap(){return Te||(Te=makeMap(0))}function updateMap(s,o,i){var a,u;if(s._root){var _=MakeRef(L),w=MakeRef(B);if(a=updateNode(s._root,s.__ownerID,0,void 0,o,i,_,w),!w.value)return s;u=s.size+(_.value?i===j?-1:1:0)}else{if(i===j)return s;u=1,a=new ArrayMapNode(s.__ownerID,[[o,i]])}return s.__ownerID?(s.size=u,s._root=a,s.__hash=void 0,s.__altered=!0,s):a?makeMap(u,a):emptyMap()}function updateNode(s,o,i,a,u,_,w,x){return s?s.update(o,i,a,u,_,w,x):_===j?s:(SetRef(x),SetRef(w),new ValueNode(o,a,[u,_]))}function isLeafNode(s){return s.constructor===ValueNode||s.constructor===HashCollisionNode}function mergeIntoNode(s,o,i,a,u){if(s.keyHash===a)return new HashCollisionNode(o,a,[s.entry,u]);var _,x=(0===i?s.keyHash:s.keyHash>>>i)&C,j=(0===i?a:a>>>i)&C;return new BitmapIndexedNode(o,1<>>=1)w[C]=1&i?o[_++]:void 0;return w[a]=u,new HashArrayMapNode(s,_+1,w)}function mergeIntoMapWith(s,o,i){for(var a=[],u=0;u>1&1431655765))+(s>>2&858993459))+(s>>4)&252645135,s+=s>>8,127&(s+=s>>16)}function setIn(s,o,i,a){var u=a?s:arrCopy(s);return u[o]=i,u}function spliceIn(s,o,i,a){var u=s.length+1;if(a&&o+1===u)return s[o]=i,s;for(var _=new Array(u),w=0,x=0;x=qe)return createNodes(s,C,a,u);var V=s&&s===this.ownerID,U=V?C:arrCopy(C);return $?x?L===B-1?U.pop():U[L]=U.pop():U[L]=[a,u]:U.push([a,u]),V?(this.entries=U,this):new ArrayMapNode(s,U)}},BitmapIndexedNode.prototype.get=function(s,o,i,a){void 0===o&&(o=hash(i));var u=1<<((0===s?o:o>>>s)&C),_=this.bitmap;return _&u?this.nodes[popCount(_&u-1)].get(s+w,o,i,a):a},BitmapIndexedNode.prototype.update=function(s,o,i,a,u,_,x){void 0===i&&(i=hash(a));var L=(0===o?i:i>>>o)&C,B=1<=ze)return expandNodes(s,z,$,L,Z);if(V&&!Z&&2===z.length&&isLeafNode(z[1^U]))return z[1^U];if(V&&Z&&1===z.length&&isLeafNode(Z))return Z;var ee=s&&s===this.ownerID,ie=V?Z?$:$^B:$|B,ae=V?Z?setIn(z,U,Z,ee):spliceOut(z,U,ee):spliceIn(z,U,Z,ee);return ee?(this.bitmap=ie,this.nodes=ae,this):new BitmapIndexedNode(s,ie,ae)},HashArrayMapNode.prototype.get=function(s,o,i,a){void 0===o&&(o=hash(i));var u=(0===s?o:o>>>s)&C,_=this.nodes[u];return _?_.get(s+w,o,i,a):a},HashArrayMapNode.prototype.update=function(s,o,i,a,u,_,x){void 0===i&&(i=hash(a));var L=(0===o?i:i>>>o)&C,B=u===j,$=this.nodes,V=$[L];if(B&&!V)return this;var U=updateNode(V,s,o+w,i,a,u,_,x);if(U===V)return this;var z=this.count;if(V){if(!U&&--z0&&a=0&&s>>o&C;if(a>=this.array.length)return new VNode([],s);var u,_=0===a;if(o>0){var x=this.array[a];if((u=x&&x.removeBefore(s,o-w,i))===x&&_)return this}if(_&&!u)return this;var j=editableVNode(this,s);if(!_)for(var L=0;L>>o&C;if(u>=this.array.length)return this;if(o>0){var _=this.array[u];if((a=_&&_.removeAfter(s,o-w,i))===_&&u===this.array.length-1)return this}var x=editableVNode(this,s);return x.array.splice(u+1),a&&(x.array[u]=a),x};var Xe,Qe,et={};function iterateList(s,o){var i=s._origin,a=s._capacity,u=getTailOffset(a),_=s._tail;return iterateNodeOrLeaf(s._root,s._level,0);function iterateNodeOrLeaf(s,o,i){return 0===o?iterateLeaf(s,i):iterateNode(s,o,i)}function iterateLeaf(s,w){var C=w===u?_&&_.array:s&&s.array,j=w>i?0:i-w,L=a-w;return L>x&&(L=x),function(){if(j===L)return et;var s=o?--L:j++;return C&&C[s]}}function iterateNode(s,u,_){var C,j=s&&s.array,L=_>i?0:i-_>>u,B=1+(a-_>>u);return B>x&&(B=x),function(){for(;;){if(C){var s=C();if(s!==et)return s;C=null}if(L===B)return et;var i=o?--B:L++;C=iterateNodeOrLeaf(j&&j[i],u-w,_+(i<=s.size||o<0)return s.withMutations((function(s){o<0?setListBounds(s,o).set(0,i):setListBounds(s,0,o+1).set(o,i)}));o+=s._origin;var a=s._tail,u=s._root,_=MakeRef(B);return o>=getTailOffset(s._capacity)?a=updateVNode(a,s.__ownerID,0,o,i,_):u=updateVNode(u,s.__ownerID,s._level,o,i,_),_.value?s.__ownerID?(s._root=u,s._tail=a,s.__hash=void 0,s.__altered=!0,s):makeList(s._origin,s._capacity,s._level,u,a):s}function updateVNode(s,o,i,a,u,_){var x,j=a>>>i&C,L=s&&j0){var B=s&&s.array[j],$=updateVNode(B,o,i-w,a,u,_);return $===B?s:((x=editableVNode(s,o)).array[j]=$,x)}return L&&s.array[j]===u?s:(SetRef(_),x=editableVNode(s,o),void 0===u&&j===x.array.length-1?x.array.pop():x.array[j]=u,x)}function editableVNode(s,o){return o&&s&&o===s.ownerID?s:new VNode(s?s.array.slice():[],o)}function listNodeFor(s,o){if(o>=getTailOffset(s._capacity))return s._tail;if(o<1<0;)i=i.array[o>>>a&C],a-=w;return i}}function setListBounds(s,o,i){void 0!==o&&(o|=0),void 0!==i&&(i|=0);var a=s.__ownerID||new OwnerID,u=s._origin,_=s._capacity,x=u+o,j=void 0===i?_:i<0?_+i:u+i;if(x===u&&j===_)return s;if(x>=j)return s.clear();for(var L=s._level,B=s._root,$=0;x+$<0;)B=new VNode(B&&B.array.length?[void 0,B]:[],a),$+=1<<(L+=w);$&&(x+=$,u+=$,j+=$,_+=$);for(var V=getTailOffset(_),U=getTailOffset(j);U>=1<V?new VNode([],a):z;if(z&&U>V&&x<_&&z.array.length){for(var Z=B=editableVNode(B,a),ee=L;ee>w;ee-=w){var ie=V>>>ee&C;Z=Z.array[ie]=editableVNode(Z.array[ie],a)}Z.array[V>>>w&C]=z}if(j<_&&(Y=Y&&Y.removeAfter(a,0,j)),x>=U)x-=U,j-=U,L=w,B=null,Y=Y&&Y.removeBefore(a,0,x);else if(x>u||U>>L&C;if(ae!==U>>>L&C)break;ae&&($+=(1<u&&(B=B.removeBefore(a,L,x-$)),B&&Uu&&(u=x.size),isIterable(w)||(x=x.map((function(s){return fromJS(s)}))),a.push(x)}return u>s.size&&(s=s.setSize(u)),mergeIntoCollectionWith(s,o,a)}function getTailOffset(s){return s>>w<=x&&w.size>=2*_.size?(a=(u=w.filter((function(s,o){return void 0!==s&&C!==o}))).toKeyedSeq().map((function(s){return s[0]})).flip().toMap(),s.__ownerID&&(a.__ownerID=u.__ownerID=s.__ownerID)):(a=_.remove(o),u=C===w.size-1?w.pop():w.set(C,void 0))}else if(L){if(i===w.get(C)[1])return s;a=_,u=w.set(C,[o,i])}else a=_.set(o,w.size),u=w.set(w.size,[o,i]);return s.__ownerID?(s.size=a.size,s._map=a,s._list=u,s.__hash=void 0,s):makeOrderedMap(a,u)}function ToKeyedSequence(s,o){this._iter=s,this._useKeys=o,this.size=s.size}function ToIndexedSequence(s){this._iter=s,this.size=s.size}function ToSetSequence(s){this._iter=s,this.size=s.size}function FromEntriesSequence(s){this._iter=s,this.size=s.size}function flipFactory(s){var o=makeSequence(s);return o._iter=s,o.size=s.size,o.flip=function(){return s},o.reverse=function(){var o=s.reverse.apply(this);return o.flip=function(){return s.reverse()},o},o.has=function(o){return s.includes(o)},o.includes=function(o){return s.has(o)},o.cacheResult=cacheResultThrough,o.__iterateUncached=function(o,i){var a=this;return s.__iterate((function(s,i){return!1!==o(i,s,a)}),i)},o.__iteratorUncached=function(o,i){if(o===U){var a=s.__iterator(o,i);return new Iterator((function(){var s=a.next();if(!s.done){var o=s.value[0];s.value[0]=s.value[1],s.value[1]=o}return s}))}return s.__iterator(o===V?$:V,i)},o}function mapFactory(s,o,i){var a=makeSequence(s);return a.size=s.size,a.has=function(o){return s.has(o)},a.get=function(a,u){var _=s.get(a,j);return _===j?u:o.call(i,_,a,s)},a.__iterateUncached=function(a,u){var _=this;return s.__iterate((function(s,u,w){return!1!==a(o.call(i,s,u,w),u,_)}),u)},a.__iteratorUncached=function(a,u){var _=s.__iterator(U,u);return new Iterator((function(){var u=_.next();if(u.done)return u;var w=u.value,x=w[0];return iteratorValue(a,x,o.call(i,w[1],x,s),u)}))},a}function reverseFactory(s,o){var i=makeSequence(s);return i._iter=s,i.size=s.size,i.reverse=function(){return s},s.flip&&(i.flip=function(){var o=flipFactory(s);return o.reverse=function(){return s.flip()},o}),i.get=function(i,a){return s.get(o?i:-1-i,a)},i.has=function(i){return s.has(o?i:-1-i)},i.includes=function(o){return s.includes(o)},i.cacheResult=cacheResultThrough,i.__iterate=function(o,i){var a=this;return s.__iterate((function(s,i){return o(s,i,a)}),!i)},i.__iterator=function(o,i){return s.__iterator(o,!i)},i}function filterFactory(s,o,i,a){var u=makeSequence(s);return a&&(u.has=function(a){var u=s.get(a,j);return u!==j&&!!o.call(i,u,a,s)},u.get=function(a,u){var _=s.get(a,j);return _!==j&&o.call(i,_,a,s)?_:u}),u.__iterateUncached=function(u,_){var w=this,x=0;return s.__iterate((function(s,_,C){if(o.call(i,s,_,C))return x++,u(s,a?_:x-1,w)}),_),x},u.__iteratorUncached=function(u,_){var w=s.__iterator(U,_),x=0;return new Iterator((function(){for(;;){var _=w.next();if(_.done)return _;var C=_.value,j=C[0],L=C[1];if(o.call(i,L,j,s))return iteratorValue(u,a?j:x++,L,_)}}))},u}function countByFactory(s,o,i){var a=Map().asMutable();return s.__iterate((function(u,_){a.update(o.call(i,u,_,s),0,(function(s){return s+1}))})),a.asImmutable()}function groupByFactory(s,o,i){var a=isKeyed(s),u=(isOrdered(s)?OrderedMap():Map()).asMutable();s.__iterate((function(_,w){u.update(o.call(i,_,w,s),(function(s){return(s=s||[]).push(a?[w,_]:_),s}))}));var _=iterableClass(s);return u.map((function(o){return reify(s,_(o))}))}function sliceFactory(s,o,i,a){var u=s.size;if(void 0!==o&&(o|=0),void 0!==i&&(i===1/0?i=u:i|=0),wholeSlice(o,i,u))return s;var _=resolveBegin(o,u),w=resolveEnd(i,u);if(_!=_||w!=w)return sliceFactory(s.toSeq().cacheResult(),o,i,a);var x,C=w-_;C==C&&(x=C<0?0:C);var j=makeSequence(s);return j.size=0===x?x:s.size&&x||void 0,!a&&isSeq(s)&&x>=0&&(j.get=function(o,i){return(o=wrapIndex(this,o))>=0&&ox)return iteratorDone();var s=u.next();return a||o===V?s:iteratorValue(o,C-1,o===$?void 0:s.value[1],s)}))},j}function takeWhileFactory(s,o,i){var a=makeSequence(s);return a.__iterateUncached=function(a,u){var _=this;if(u)return this.cacheResult().__iterate(a,u);var w=0;return s.__iterate((function(s,u,x){return o.call(i,s,u,x)&&++w&&a(s,u,_)})),w},a.__iteratorUncached=function(a,u){var _=this;if(u)return this.cacheResult().__iterator(a,u);var w=s.__iterator(U,u),x=!0;return new Iterator((function(){if(!x)return iteratorDone();var s=w.next();if(s.done)return s;var u=s.value,C=u[0],j=u[1];return o.call(i,j,C,_)?a===U?s:iteratorValue(a,C,j,s):(x=!1,iteratorDone())}))},a}function skipWhileFactory(s,o,i,a){var u=makeSequence(s);return u.__iterateUncached=function(u,_){var w=this;if(_)return this.cacheResult().__iterate(u,_);var x=!0,C=0;return s.__iterate((function(s,_,j){if(!x||!(x=o.call(i,s,_,j)))return C++,u(s,a?_:C-1,w)})),C},u.__iteratorUncached=function(u,_){var w=this;if(_)return this.cacheResult().__iterator(u,_);var x=s.__iterator(U,_),C=!0,j=0;return new Iterator((function(){var s,_,L;do{if((s=x.next()).done)return a||u===V?s:iteratorValue(u,j++,u===$?void 0:s.value[1],s);var B=s.value;_=B[0],L=B[1],C&&(C=o.call(i,L,_,w))}while(C);return u===U?s:iteratorValue(u,_,L,s)}))},u}function concatFactory(s,o){var i=isKeyed(s),a=[s].concat(o).map((function(s){return isIterable(s)?i&&(s=KeyedIterable(s)):s=i?keyedSeqFromValue(s):indexedSeqFromValue(Array.isArray(s)?s:[s]),s})).filter((function(s){return 0!==s.size}));if(0===a.length)return s;if(1===a.length){var u=a[0];if(u===s||i&&isKeyed(u)||isIndexed(s)&&isIndexed(u))return u}var _=new ArraySeq(a);return i?_=_.toKeyedSeq():isIndexed(s)||(_=_.toSetSeq()),(_=_.flatten(!0)).size=a.reduce((function(s,o){if(void 0!==s){var i=o.size;if(void 0!==i)return s+i}}),0),_}function flattenFactory(s,o,i){var a=makeSequence(s);return a.__iterateUncached=function(a,u){var _=0,w=!1;function flatDeep(s,x){var C=this;s.__iterate((function(s,u){return(!o||x0}function zipWithFactory(s,o,i){var a=makeSequence(s);return a.size=new ArraySeq(i).map((function(s){return s.size})).min(),a.__iterate=function(s,o){for(var i,a=this.__iterator(V,o),u=0;!(i=a.next()).done&&!1!==s(i.value,u++,this););return u},a.__iteratorUncached=function(s,a){var u=i.map((function(s){return s=Iterable(s),getIterator(a?s.reverse():s)})),_=0,w=!1;return new Iterator((function(){var i;return w||(i=u.map((function(s){return s.next()})),w=i.some((function(s){return s.done}))),w?iteratorDone():iteratorValue(s,_++,o.apply(null,i.map((function(s){return s.value}))))}))},a}function reify(s,o){return isSeq(s)?o:s.constructor(o)}function validateEntry(s){if(s!==Object(s))throw new TypeError("Expected [K, V] tuple: "+s)}function resolveSize(s){return assertNotInfinite(s.size),ensureSize(s)}function iterableClass(s){return isKeyed(s)?KeyedIterable:isIndexed(s)?IndexedIterable:SetIterable}function makeSequence(s){return Object.create((isKeyed(s)?KeyedSeq:isIndexed(s)?IndexedSeq:SetSeq).prototype)}function cacheResultThrough(){return this._iter.cacheResult?(this._iter.cacheResult(),this.size=this._iter.size,this):Seq.prototype.cacheResult.call(this)}function defaultComparator(s,o){return s>o?1:s=0;i--)o={value:arguments[i],next:o};return this.__ownerID?(this.size=s,this._head=o,this.__hash=void 0,this.__altered=!0,this):makeStack(s,o)},Stack.prototype.pushAll=function(s){if(0===(s=IndexedIterable(s)).size)return this;assertNotInfinite(s.size);var o=this.size,i=this._head;return s.reverse().forEach((function(s){o++,i={value:s,next:i}})),this.__ownerID?(this.size=o,this._head=i,this.__hash=void 0,this.__altered=!0,this):makeStack(o,i)},Stack.prototype.pop=function(){return this.slice(1)},Stack.prototype.unshift=function(){return this.push.apply(this,arguments)},Stack.prototype.unshiftAll=function(s){return this.pushAll(s)},Stack.prototype.shift=function(){return this.pop.apply(this,arguments)},Stack.prototype.clear=function(){return 0===this.size?this:this.__ownerID?(this.size=0,this._head=void 0,this.__hash=void 0,this.__altered=!0,this):emptyStack()},Stack.prototype.slice=function(s,o){if(wholeSlice(s,o,this.size))return this;var i=resolveBegin(s,this.size);if(resolveEnd(o,this.size)!==this.size)return IndexedCollection.prototype.slice.call(this,s,o);for(var a=this.size-i,u=this._head;i--;)u=u.next;return this.__ownerID?(this.size=a,this._head=u,this.__hash=void 0,this.__altered=!0,this):makeStack(a,u)},Stack.prototype.__ensureOwner=function(s){return s===this.__ownerID?this:s?makeStack(this.size,this._head,s,this.__hash):(this.__ownerID=s,this.__altered=!1,this)},Stack.prototype.__iterate=function(s,o){if(o)return this.reverse().__iterate(s);for(var i=0,a=this._head;a&&!1!==s(a.value,i++,this);)a=a.next;return i},Stack.prototype.__iterator=function(s,o){if(o)return this.reverse().__iterator(s);var i=0,a=this._head;return new Iterator((function(){if(a){var o=a.value;return a=a.next,iteratorValue(s,i++,o)}return iteratorDone()}))},Stack.isStack=isStack;var at,ct="@@__IMMUTABLE_STACK__@@",lt=Stack.prototype;function makeStack(s,o,i,a){var u=Object.create(lt);return u.size=s,u._head=o,u.__ownerID=i,u.__hash=a,u.__altered=!1,u}function emptyStack(){return at||(at=makeStack(0))}function mixin(s,o){var keyCopier=function(i){s.prototype[i]=o[i]};return Object.keys(o).forEach(keyCopier),Object.getOwnPropertySymbols&&Object.getOwnPropertySymbols(o).forEach(keyCopier),s}lt[ct]=!0,lt.withMutations=$e.withMutations,lt.asMutable=$e.asMutable,lt.asImmutable=$e.asImmutable,lt.wasAltered=$e.wasAltered,Iterable.Iterator=Iterator,mixin(Iterable,{toArray:function(){assertNotInfinite(this.size);var s=new Array(this.size||0);return this.valueSeq().__iterate((function(o,i){s[i]=o})),s},toIndexedSeq:function(){return new ToIndexedSequence(this)},toJS:function(){return this.toSeq().map((function(s){return s&&"function"==typeof s.toJS?s.toJS():s})).__toJS()},toJSON:function(){return this.toSeq().map((function(s){return s&&"function"==typeof s.toJSON?s.toJSON():s})).__toJS()},toKeyedSeq:function(){return new ToKeyedSequence(this,!0)},toMap:function(){return Map(this.toKeyedSeq())},toObject:function(){assertNotInfinite(this.size);var s={};return this.__iterate((function(o,i){s[i]=o})),s},toOrderedMap:function(){return OrderedMap(this.toKeyedSeq())},toOrderedSet:function(){return OrderedSet(isKeyed(this)?this.valueSeq():this)},toSet:function(){return Set(isKeyed(this)?this.valueSeq():this)},toSetSeq:function(){return new ToSetSequence(this)},toSeq:function(){return isIndexed(this)?this.toIndexedSeq():isKeyed(this)?this.toKeyedSeq():this.toSetSeq()},toStack:function(){return Stack(isKeyed(this)?this.valueSeq():this)},toList:function(){return List(isKeyed(this)?this.valueSeq():this)},toString:function(){return"[Iterable]"},__toString:function(s,o){return 0===this.size?s+o:s+" "+this.toSeq().map(this.__toStringMapper).join(", ")+" "+o},concat:function(){return reify(this,concatFactory(this,s.call(arguments,0)))},includes:function(s){return this.some((function(o){return is(o,s)}))},entries:function(){return this.__iterator(U)},every:function(s,o){assertNotInfinite(this.size);var i=!0;return this.__iterate((function(a,u,_){if(!s.call(o,a,u,_))return i=!1,!1})),i},filter:function(s,o){return reify(this,filterFactory(this,s,o,!0))},find:function(s,o,i){var a=this.findEntry(s,o);return a?a[1]:i},forEach:function(s,o){return assertNotInfinite(this.size),this.__iterate(o?s.bind(o):s)},join:function(s){assertNotInfinite(this.size),s=void 0!==s?""+s:",";var o="",i=!0;return this.__iterate((function(a){i?i=!1:o+=s,o+=null!=a?a.toString():""})),o},keys:function(){return this.__iterator($)},map:function(s,o){return reify(this,mapFactory(this,s,o))},reduce:function(s,o,i){var a,u;return assertNotInfinite(this.size),arguments.length<2?u=!0:a=o,this.__iterate((function(o,_,w){u?(u=!1,a=o):a=s.call(i,a,o,_,w)})),a},reduceRight:function(s,o,i){var a=this.toKeyedSeq().reverse();return a.reduce.apply(a,arguments)},reverse:function(){return reify(this,reverseFactory(this,!0))},slice:function(s,o){return reify(this,sliceFactory(this,s,o,!0))},some:function(s,o){return!this.every(not(s),o)},sort:function(s){return reify(this,sortFactory(this,s))},values:function(){return this.__iterator(V)},butLast:function(){return this.slice(0,-1)},isEmpty:function(){return void 0!==this.size?0===this.size:!this.some((function(){return!0}))},count:function(s,o){return ensureSize(s?this.toSeq().filter(s,o):this)},countBy:function(s,o){return countByFactory(this,s,o)},equals:function(s){return deepEqual(this,s)},entrySeq:function(){var s=this;if(s._cache)return new ArraySeq(s._cache);var o=s.toSeq().map(entryMapper).toIndexedSeq();return o.fromEntrySeq=function(){return s.toSeq()},o},filterNot:function(s,o){return this.filter(not(s),o)},findEntry:function(s,o,i){var a=i;return this.__iterate((function(i,u,_){if(s.call(o,i,u,_))return a=[u,i],!1})),a},findKey:function(s,o){var i=this.findEntry(s,o);return i&&i[0]},findLast:function(s,o,i){return this.toKeyedSeq().reverse().find(s,o,i)},findLastEntry:function(s,o,i){return this.toKeyedSeq().reverse().findEntry(s,o,i)},findLastKey:function(s,o){return this.toKeyedSeq().reverse().findKey(s,o)},first:function(){return this.find(returnTrue)},flatMap:function(s,o){return reify(this,flatMapFactory(this,s,o))},flatten:function(s){return reify(this,flattenFactory(this,s,!0))},fromEntrySeq:function(){return new FromEntriesSequence(this)},get:function(s,o){return this.find((function(o,i){return is(i,s)}),void 0,o)},getIn:function(s,o){for(var i,a=this,u=forceIterator(s);!(i=u.next()).done;){var _=i.value;if((a=a&&a.get?a.get(_,j):j)===j)return o}return a},groupBy:function(s,o){return groupByFactory(this,s,o)},has:function(s){return this.get(s,j)!==j},hasIn:function(s){return this.getIn(s,j)!==j},isSubset:function(s){return s="function"==typeof s.includes?s:Iterable(s),this.every((function(o){return s.includes(o)}))},isSuperset:function(s){return(s="function"==typeof s.isSubset?s:Iterable(s)).isSubset(this)},keyOf:function(s){return this.findKey((function(o){return is(o,s)}))},keySeq:function(){return this.toSeq().map(keyMapper).toIndexedSeq()},last:function(){return this.toSeq().reverse().first()},lastKeyOf:function(s){return this.toKeyedSeq().reverse().keyOf(s)},max:function(s){return maxFactory(this,s)},maxBy:function(s,o){return maxFactory(this,o,s)},min:function(s){return maxFactory(this,s?neg(s):defaultNegComparator)},minBy:function(s,o){return maxFactory(this,o?neg(o):defaultNegComparator,s)},rest:function(){return this.slice(1)},skip:function(s){return this.slice(Math.max(0,s))},skipLast:function(s){return reify(this,this.toSeq().reverse().skip(s).reverse())},skipWhile:function(s,o){return reify(this,skipWhileFactory(this,s,o,!0))},skipUntil:function(s,o){return this.skipWhile(not(s),o)},sortBy:function(s,o){return reify(this,sortFactory(this,o,s))},take:function(s){return this.slice(0,Math.max(0,s))},takeLast:function(s){return reify(this,this.toSeq().reverse().take(s).reverse())},takeWhile:function(s,o){return reify(this,takeWhileFactory(this,s,o))},takeUntil:function(s,o){return this.takeWhile(not(s),o)},valueSeq:function(){return this.toIndexedSeq()},hashCode:function(){return this.__hash||(this.__hash=hashIterable(this))}});var ut=Iterable.prototype;ut[o]=!0,ut[Z]=ut.values,ut.__toJS=ut.toArray,ut.__toStringMapper=quoteString,ut.inspect=ut.toSource=function(){return this.toString()},ut.chain=ut.flatMap,ut.contains=ut.includes,mixin(KeyedIterable,{flip:function(){return reify(this,flipFactory(this))},mapEntries:function(s,o){var i=this,a=0;return reify(this,this.toSeq().map((function(u,_){return s.call(o,[_,u],a++,i)})).fromEntrySeq())},mapKeys:function(s,o){var i=this;return reify(this,this.toSeq().flip().map((function(a,u){return s.call(o,a,u,i)})).flip())}});var pt=KeyedIterable.prototype;function keyMapper(s,o){return o}function entryMapper(s,o){return[o,s]}function not(s){return function(){return!s.apply(this,arguments)}}function neg(s){return function(){return-s.apply(this,arguments)}}function quoteString(s){return"string"==typeof s?JSON.stringify(s):String(s)}function defaultZipper(){return arrCopy(arguments)}function defaultNegComparator(s,o){return so?-1:0}function hashIterable(s){if(s.size===1/0)return 0;var o=isOrdered(s),i=isKeyed(s),a=o?1:0;return murmurHashOfSize(s.__iterate(i?o?function(s,o){a=31*a+hashMerge(hash(s),hash(o))|0}:function(s,o){a=a+hashMerge(hash(s),hash(o))|0}:o?function(s){a=31*a+hash(s)|0}:function(s){a=a+hash(s)|0}),a)}function murmurHashOfSize(s,o){return o=le(o,3432918353),o=le(o<<15|o>>>-15,461845907),o=le(o<<13|o>>>-13,5),o=le((o=o+3864292196^s)^o>>>16,2246822507),o=smi((o=le(o^o>>>13,3266489909))^o>>>16)}function hashMerge(s,o){return s^o+2654435769+(s<<6)+(s>>2)}return pt[i]=!0,pt[Z]=ut.entries,pt.__toJS=ut.toObject,pt.__toStringMapper=function(s,o){return JSON.stringify(o)+": "+quoteString(s)},mixin(IndexedIterable,{toKeyedSeq:function(){return new ToKeyedSequence(this,!1)},filter:function(s,o){return reify(this,filterFactory(this,s,o,!1))},findIndex:function(s,o){var i=this.findEntry(s,o);return i?i[0]:-1},indexOf:function(s){var o=this.keyOf(s);return void 0===o?-1:o},lastIndexOf:function(s){var o=this.lastKeyOf(s);return void 0===o?-1:o},reverse:function(){return reify(this,reverseFactory(this,!1))},slice:function(s,o){return reify(this,sliceFactory(this,s,o,!1))},splice:function(s,o){var i=arguments.length;if(o=Math.max(0|o,0),0===i||2===i&&!o)return this;s=resolveBegin(s,s<0?this.count():this.size);var a=this.slice(0,s);return reify(this,1===i?a:a.concat(arrCopy(arguments,2),this.slice(s+o)))},findLastIndex:function(s,o){var i=this.findLastEntry(s,o);return i?i[0]:-1},first:function(){return this.get(0)},flatten:function(s){return reify(this,flattenFactory(this,s,!1))},get:function(s,o){return(s=wrapIndex(this,s))<0||this.size===1/0||void 0!==this.size&&s>this.size?o:this.find((function(o,i){return i===s}),void 0,o)},has:function(s){return(s=wrapIndex(this,s))>=0&&(void 0!==this.size?this.size===1/0||s{"use strict";i(71340);var a=i(92046);s.exports=a.Object.assign},9999:(s,o,i)=>{var a=i(37217),u=i(83729),_=i(16547),w=i(74733),x=i(43838),C=i(93290),j=i(23007),L=i(92271),B=i(48948),$=i(50002),V=i(83349),U=i(5861),z=i(76189),Y=i(77199),Z=i(35529),ee=i(56449),ie=i(3656),ae=i(87730),ce=i(23805),le=i(38440),pe=i(95950),de=i(37241),fe="[object Arguments]",ye="[object Function]",be="[object Object]",_e={};_e[fe]=_e["[object Array]"]=_e["[object ArrayBuffer]"]=_e["[object DataView]"]=_e["[object Boolean]"]=_e["[object Date]"]=_e["[object Float32Array]"]=_e["[object Float64Array]"]=_e["[object Int8Array]"]=_e["[object Int16Array]"]=_e["[object Int32Array]"]=_e["[object Map]"]=_e["[object Number]"]=_e[be]=_e["[object RegExp]"]=_e["[object Set]"]=_e["[object String]"]=_e["[object Symbol]"]=_e["[object Uint8Array]"]=_e["[object Uint8ClampedArray]"]=_e["[object Uint16Array]"]=_e["[object Uint32Array]"]=!0,_e["[object Error]"]=_e[ye]=_e["[object WeakMap]"]=!1,s.exports=function baseClone(s,o,i,Se,we,xe){var Pe,Te=1&o,Re=2&o,$e=4&o;if(i&&(Pe=we?i(s,Se,we,xe):i(s)),void 0!==Pe)return Pe;if(!ce(s))return s;var qe=ee(s);if(qe){if(Pe=z(s),!Te)return j(s,Pe)}else{var ze=U(s),We=ze==ye||"[object GeneratorFunction]"==ze;if(ie(s))return C(s,Te);if(ze==be||ze==fe||We&&!we){if(Pe=Re||We?{}:Z(s),!Te)return Re?B(s,x(Pe,s)):L(s,w(Pe,s))}else{if(!_e[ze])return we?s:{};Pe=Y(s,ze,Te)}}xe||(xe=new a);var He=xe.get(s);if(He)return He;xe.set(s,Pe),le(s)?s.forEach((function(a){Pe.add(baseClone(a,o,i,a,s,xe))})):ae(s)&&s.forEach((function(a,u){Pe.set(u,baseClone(a,o,i,u,s,xe))}));var Ye=qe?void 0:($e?Re?V:$:Re?de:pe)(s);return u(Ye||s,(function(a,u){Ye&&(a=s[u=a]),_(Pe,u,baseClone(a,o,i,u,s,xe))})),Pe}},10023:(s,o,i)=>{const a=i(6205),INTS=()=>[{type:a.RANGE,from:48,to:57}],WORDS=()=>[{type:a.CHAR,value:95},{type:a.RANGE,from:97,to:122},{type:a.RANGE,from:65,to:90}].concat(INTS()),WHITESPACE=()=>[{type:a.CHAR,value:9},{type:a.CHAR,value:10},{type:a.CHAR,value:11},{type:a.CHAR,value:12},{type:a.CHAR,value:13},{type:a.CHAR,value:32},{type:a.CHAR,value:160},{type:a.CHAR,value:5760},{type:a.RANGE,from:8192,to:8202},{type:a.CHAR,value:8232},{type:a.CHAR,value:8233},{type:a.CHAR,value:8239},{type:a.CHAR,value:8287},{type:a.CHAR,value:12288},{type:a.CHAR,value:65279}];o.words=()=>({type:a.SET,set:WORDS(),not:!1}),o.notWords=()=>({type:a.SET,set:WORDS(),not:!0}),o.ints=()=>({type:a.SET,set:INTS(),not:!1}),o.notInts=()=>({type:a.SET,set:INTS(),not:!0}),o.whitespace=()=>({type:a.SET,set:WHITESPACE(),not:!1}),o.notWhitespace=()=>({type:a.SET,set:WHITESPACE(),not:!0}),o.anyChar=()=>({type:a.SET,set:[{type:a.CHAR,value:10},{type:a.CHAR,value:13},{type:a.CHAR,value:8232},{type:a.CHAR,value:8233}],not:!0})},10043:(s,o,i)=>{"use strict";var a=i(54018),u=String,_=TypeError;s.exports=function(s){if(a(s))return s;throw new _("Can't set "+u(s)+" as a prototype")}},10124:(s,o,i)=>{var a=i(9325);s.exports=function(){return a.Date.now()}},10300:(s,o,i)=>{"use strict";var a=i(13930),u=i(82159),_=i(36624),w=i(4640),x=i(73448),C=TypeError;s.exports=function(s,o){var i=arguments.length<2?x(s):o;if(u(i))return _(a(i,s));throw new C(w(s)+" is not iterable")}},10316:(s,o,i)=>{const a=i(2404),u=i(55973),_=i(92340);class Element{constructor(s,o,i){o&&(this.meta=o),i&&(this.attributes=i),this.content=s}freeze(){Object.isFrozen(this)||(this._meta&&(this.meta.parent=this,this.meta.freeze()),this._attributes&&(this.attributes.parent=this,this.attributes.freeze()),this.children.forEach((s=>{s.parent=this,s.freeze()}),this),this.content&&Array.isArray(this.content)&&Object.freeze(this.content),Object.freeze(this))}primitive(){}clone(){const s=new this.constructor;return s.element=this.element,this.meta.length&&(s._meta=this.meta.clone()),this.attributes.length&&(s._attributes=this.attributes.clone()),this.content?this.content.clone?s.content=this.content.clone():Array.isArray(this.content)?s.content=this.content.map((s=>s.clone())):s.content=this.content:s.content=this.content,s}toValue(){return this.content instanceof Element?this.content.toValue():this.content instanceof u?{key:this.content.key.toValue(),value:this.content.value?this.content.value.toValue():void 0}:this.content&&this.content.map?this.content.map((s=>s.toValue()),this):this.content}toRef(s){if(""===this.id.toValue())throw Error("Cannot create reference to an element that does not contain an ID");const o=new this.RefElement(this.id.toValue());return s&&(o.path=s),o}findRecursive(...s){if(arguments.length>1&&!this.isFrozen)throw new Error("Cannot find recursive with multiple element names without first freezing the element. Call `element.freeze()`");const o=s.pop();let i=new _;const append=(s,o)=>(s.push(o),s),checkElement=(s,i)=>{i.element===o&&s.push(i);const a=i.findRecursive(o);return a&&a.reduce(append,s),i.content instanceof u&&(i.content.key&&checkElement(s,i.content.key),i.content.value&&checkElement(s,i.content.value)),s};return this.content&&(this.content.element&&checkElement(i,this.content),Array.isArray(this.content)&&this.content.reduce(checkElement,i)),s.isEmpty||(i=i.filter((o=>{let i=o.parents.map((s=>s.element));for(const o in s){const a=s[o],u=i.indexOf(a);if(-1===u)return!1;i=i.splice(0,u)}return!0}))),i}set(s){return this.content=s,this}equals(s){return a(this.toValue(),s)}getMetaProperty(s,o){if(!this.meta.hasKey(s)){if(this.isFrozen){const s=this.refract(o);return s.freeze(),s}this.meta.set(s,o)}return this.meta.get(s)}setMetaProperty(s,o){this.meta.set(s,o)}get element(){return this._storedElement||"element"}set element(s){this._storedElement=s}get content(){return this._content}set content(s){if(s instanceof Element)this._content=s;else if(s instanceof _)this.content=s.elements;else if("string"==typeof s||"number"==typeof s||"boolean"==typeof s||"null"===s||null==s)this._content=s;else if(s instanceof u)this._content=s;else if(Array.isArray(s))this._content=s.map(this.refract);else{if("object"!=typeof s)throw new Error("Cannot set content to given value");this._content=Object.keys(s).map((o=>new this.MemberElement(o,s[o])))}}get meta(){if(!this._meta){if(this.isFrozen){const s=new this.ObjectElement;return s.freeze(),s}this._meta=new this.ObjectElement}return this._meta}set meta(s){s instanceof this.ObjectElement?this._meta=s:this.meta.set(s||{})}get attributes(){if(!this._attributes){if(this.isFrozen){const s=new this.ObjectElement;return s.freeze(),s}this._attributes=new this.ObjectElement}return this._attributes}set attributes(s){s instanceof this.ObjectElement?this._attributes=s:this.attributes.set(s||{})}get id(){return this.getMetaProperty("id","")}set id(s){this.setMetaProperty("id",s)}get classes(){return this.getMetaProperty("classes",[])}set classes(s){this.setMetaProperty("classes",s)}get title(){return this.getMetaProperty("title","")}set title(s){this.setMetaProperty("title",s)}get description(){return this.getMetaProperty("description","")}set description(s){this.setMetaProperty("description",s)}get links(){return this.getMetaProperty("links",[])}set links(s){this.setMetaProperty("links",s)}get isFrozen(){return Object.isFrozen(this)}get parents(){let{parent:s}=this;const o=new _;for(;s;)o.push(s),s=s.parent;return o}get children(){if(Array.isArray(this.content))return new _(this.content);if(this.content instanceof u){const s=new _([this.content.key]);return this.content.value&&s.push(this.content.value),s}return this.content instanceof Element?new _([this.content]):new _}get recursiveChildren(){const s=new _;return this.children.forEach((o=>{s.push(o),o.recursiveChildren.forEach((o=>{s.push(o)}))})),s}}s.exports=Element},10392:s=>{s.exports=function getValue(s,o){return null==s?void 0:s[o]}},10776:(s,o,i)=>{var a=i(30756),u=i(95950);s.exports=function getMatchData(s){for(var o=u(s),i=o.length;i--;){var _=o[i],w=s[_];o[i]=[_,w,a(w)]}return o}},10866:(s,o,i)=>{const a=i(6048),u=i(92340);class ObjectSlice extends u{map(s,o){return this.elements.map((i=>s.bind(o)(i.value,i.key,i)))}filter(s,o){return new ObjectSlice(this.elements.filter((i=>s.bind(o)(i.value,i.key,i))))}reject(s,o){return this.filter(a(s.bind(o)))}forEach(s,o){return this.elements.forEach(((i,a)=>{s.bind(o)(i.value,i.key,i,a)}))}keys(){return this.map(((s,o)=>o.toValue()))}values(){return this.map((s=>s.toValue()))}}s.exports=ObjectSlice},11042:(s,o,i)=>{"use strict";var a=i(85582),u=i(1907),_=i(24443),w=i(87170),x=i(36624),C=u([].concat);s.exports=a("Reflect","ownKeys")||function ownKeys(s){var o=_.f(x(s)),i=w.f;return i?C(o,i(s)):o}},11091:(s,o,i)=>{"use strict";var a=i(45951),u=i(76024),_=i(92361),w=i(62250),x=i(13846).f,C=i(7463),j=i(92046),L=i(28311),B=i(61626),$=i(49724);i(36128);var wrapConstructor=function(s){var Wrapper=function(o,i,a){if(this instanceof Wrapper){switch(arguments.length){case 0:return new s;case 1:return new s(o);case 2:return new s(o,i)}return new s(o,i,a)}return u(s,this,arguments)};return Wrapper.prototype=s.prototype,Wrapper};s.exports=function(s,o){var i,u,V,U,z,Y,Z,ee,ie,ae=s.target,ce=s.global,le=s.stat,pe=s.proto,de=ce?a:le?a[ae]:a[ae]&&a[ae].prototype,fe=ce?j:j[ae]||B(j,ae,{})[ae],ye=fe.prototype;for(U in o)u=!(i=C(ce?U:ae+(le?".":"#")+U,s.forced))&&de&&$(de,U),Y=fe[U],u&&(Z=s.dontCallGetSet?(ie=x(de,U))&&ie.value:de[U]),z=u&&Z?Z:o[U],(i||pe||typeof Y!=typeof z)&&(ee=s.bind&&u?L(z,a):s.wrap&&u?wrapConstructor(z):pe&&w(z)?_(z):z,(s.sham||z&&z.sham||Y&&Y.sham)&&B(ee,"sham",!0),B(fe,U,ee),pe&&($(j,V=ae+"Prototype")||B(j,V,{}),B(j[V],U,z),s.real&&ye&&(i||!ye[U])&&B(ye,U,z)))}},11287:s=>{s.exports=function getHolder(s){return s.placeholder}},11331:(s,o,i)=>{var a=i(72552),u=i(28879),_=i(40346),w=Function.prototype,x=Object.prototype,C=w.toString,j=x.hasOwnProperty,L=C.call(Object);s.exports=function isPlainObject(s){if(!_(s)||"[object Object]"!=a(s))return!1;var o=u(s);if(null===o)return!0;var i=j.call(o,"constructor")&&o.constructor;return"function"==typeof i&&i instanceof i&&C.call(i)==L}},11470:(s,o,i)=>{"use strict";var a=i(1907),u=i(65482),_=i(90160),w=i(74239),x=a("".charAt),C=a("".charCodeAt),j=a("".slice),createMethod=function(s){return function(o,i){var a,L,B=_(w(o)),$=u(i),V=B.length;return $<0||$>=V?s?"":void 0:(a=C(B,$))<55296||a>56319||$+1===V||(L=C(B,$+1))<56320||L>57343?s?x(B,$):a:s?j(B,$,$+2):L-56320+(a-55296<<10)+65536}};s.exports={codeAt:createMethod(!1),charAt:createMethod(!0)}},11842:(s,o,i)=>{var a=i(82819),u=i(9325);s.exports=function createBind(s,o,i){var _=1&o,w=a(s);return function wrapper(){return(this&&this!==u&&this instanceof wrapper?w:s).apply(_?i:this,arguments)}}},12242:(s,o,i)=>{const a=i(10316);s.exports=class BooleanElement extends a{constructor(s,o,i){super(s,o,i),this.element="boolean"}primitive(){return"boolean"}}},12507:(s,o,i)=>{var a=i(28754),u=i(49698),_=i(63912),w=i(13222);s.exports=function createCaseFirst(s){return function(o){o=w(o);var i=u(o)?_(o):void 0,x=i?i[0]:o.charAt(0),C=i?a(i,1).join(""):o.slice(1);return x[s]()+C}}},12560:(s,o,i)=>{"use strict";i(99363);var a=i(19287),u=i(45951),_=i(14840),w=i(93742);for(var x in a)_(u[x],x),w[x]=w.Array},12651:(s,o,i)=>{var a=i(74218);s.exports=function getMapData(s,o){var i=s.__data__;return a(o)?i["string"==typeof o?"string":"hash"]:i.map}},12749:(s,o,i)=>{var a=i(81042),u=Object.prototype.hasOwnProperty;s.exports=function hashHas(s){var o=this.__data__;return a?void 0!==o[s]:u.call(o,s)}},13222:(s,o,i)=>{var a=i(77556);s.exports=function toString(s){return null==s?"":a(s)}},13846:(s,o,i)=>{"use strict";var a=i(39447),u=i(13930),_=i(22574),w=i(75817),x=i(4993),C=i(70470),j=i(49724),L=i(73648),B=Object.getOwnPropertyDescriptor;o.f=a?B:function getOwnPropertyDescriptor(s,o){if(s=x(s),o=C(o),L)try{return B(s,o)}catch(s){}if(j(s,o))return w(!u(_.f,s,o),s[o])}},13930:(s,o,i)=>{"use strict";var a=i(41505),u=Function.prototype.call;s.exports=a?u.bind(u):function(){return u.apply(u,arguments)}},14248:s=>{s.exports=function arraySome(s,o){for(var i=-1,a=null==s?0:s.length;++i{s.exports=function arrayPush(s,o){for(var i=-1,a=o.length,u=s.length;++i{const a=i(10316);s.exports=class RefElement extends a{constructor(s,o,i){super(s||[],o,i),this.element="ref",this.path||(this.path="element")}get path(){return this.attributes.get("path")}set path(s){this.attributes.set("path",s)}}},14744:s=>{"use strict";var o=function isMergeableObject(s){return function isNonNullObject(s){return!!s&&"object"==typeof s}(s)&&!function isSpecial(s){var o=Object.prototype.toString.call(s);return"[object RegExp]"===o||"[object Date]"===o||function isReactElement(s){return s.$$typeof===i}(s)}(s)};var i="function"==typeof Symbol&&Symbol.for?Symbol.for("react.element"):60103;function cloneUnlessOtherwiseSpecified(s,o){return!1!==o.clone&&o.isMergeableObject(s)?deepmerge(function emptyTarget(s){return Array.isArray(s)?[]:{}}(s),s,o):s}function defaultArrayMerge(s,o,i){return s.concat(o).map((function(s){return cloneUnlessOtherwiseSpecified(s,i)}))}function getKeys(s){return Object.keys(s).concat(function getEnumerableOwnPropertySymbols(s){return Object.getOwnPropertySymbols?Object.getOwnPropertySymbols(s).filter((function(o){return Object.propertyIsEnumerable.call(s,o)})):[]}(s))}function propertyIsOnObject(s,o){try{return o in s}catch(s){return!1}}function mergeObject(s,o,i){var a={};return i.isMergeableObject(s)&&getKeys(s).forEach((function(o){a[o]=cloneUnlessOtherwiseSpecified(s[o],i)})),getKeys(o).forEach((function(u){(function propertyIsUnsafe(s,o){return propertyIsOnObject(s,o)&&!(Object.hasOwnProperty.call(s,o)&&Object.propertyIsEnumerable.call(s,o))})(s,u)||(propertyIsOnObject(s,u)&&i.isMergeableObject(o[u])?a[u]=function getMergeFunction(s,o){if(!o.customMerge)return deepmerge;var i=o.customMerge(s);return"function"==typeof i?i:deepmerge}(u,i)(s[u],o[u],i):a[u]=cloneUnlessOtherwiseSpecified(o[u],i))})),a}function deepmerge(s,i,a){(a=a||{}).arrayMerge=a.arrayMerge||defaultArrayMerge,a.isMergeableObject=a.isMergeableObject||o,a.cloneUnlessOtherwiseSpecified=cloneUnlessOtherwiseSpecified;var u=Array.isArray(i);return u===Array.isArray(s)?u?a.arrayMerge(s,i,a):mergeObject(s,i,a):cloneUnlessOtherwiseSpecified(i,a)}deepmerge.all=function deepmergeAll(s,o){if(!Array.isArray(s))throw new Error("first argument should be an array");return s.reduce((function(s,i){return deepmerge(s,i,o)}),{})};var a=deepmerge;s.exports=a},14792:(s,o,i)=>{var a=i(13222),u=i(55808);s.exports=function capitalize(s){return u(a(s).toLowerCase())}},14840:(s,o,i)=>{"use strict";var a=i(52623),u=i(74284).f,_=i(61626),w=i(49724),x=i(54878),C=i(76264)("toStringTag");s.exports=function(s,o,i,j){var L=i?s:s&&s.prototype;L&&(w(L,C)||u(L,C,{configurable:!0,value:o}),j&&!a&&_(L,"toString",x))}},14974:s=>{s.exports=function safeGet(s,o){if(("constructor"!==o||"function"!=typeof s[o])&&"__proto__"!=o)return s[o]}},15287:(s,o)=>{"use strict";var i=Symbol.for("react.element"),a=Symbol.for("react.portal"),u=Symbol.for("react.fragment"),_=Symbol.for("react.strict_mode"),w=Symbol.for("react.profiler"),x=Symbol.for("react.provider"),C=Symbol.for("react.context"),j=Symbol.for("react.forward_ref"),L=Symbol.for("react.suspense"),B=Symbol.for("react.memo"),$=Symbol.for("react.lazy"),V=Symbol.iterator;var U={isMounted:function(){return!1},enqueueForceUpdate:function(){},enqueueReplaceState:function(){},enqueueSetState:function(){}},z=Object.assign,Y={};function E(s,o,i){this.props=s,this.context=o,this.refs=Y,this.updater=i||U}function F(){}function G(s,o,i){this.props=s,this.context=o,this.refs=Y,this.updater=i||U}E.prototype.isReactComponent={},E.prototype.setState=function(s,o){if("object"!=typeof s&&"function"!=typeof s&&null!=s)throw Error("setState(...): takes an object of state variables to update or a function which returns an object of state variables.");this.updater.enqueueSetState(this,s,o,"setState")},E.prototype.forceUpdate=function(s){this.updater.enqueueForceUpdate(this,s,"forceUpdate")},F.prototype=E.prototype;var Z=G.prototype=new F;Z.constructor=G,z(Z,E.prototype),Z.isPureReactComponent=!0;var ee=Array.isArray,ie=Object.prototype.hasOwnProperty,ae={current:null},ce={key:!0,ref:!0,__self:!0,__source:!0};function M(s,o,a){var u,_={},w=null,x=null;if(null!=o)for(u in void 0!==o.ref&&(x=o.ref),void 0!==o.key&&(w=""+o.key),o)ie.call(o,u)&&!ce.hasOwnProperty(u)&&(_[u]=o[u]);var C=arguments.length-2;if(1===C)_.children=a;else if(1{var a=i(96131);s.exports=function arrayIncludes(s,o){return!!(null==s?0:s.length)&&a(s,o,0)>-1}},15340:()=>{},15389:(s,o,i)=>{var a=i(93663),u=i(87978),_=i(83488),w=i(56449),x=i(50583);s.exports=function baseIteratee(s){return"function"==typeof s?s:null==s?_:"object"==typeof s?w(s)?u(s[0],s[1]):a(s):x(s)}},15972:(s,o,i)=>{"use strict";var a=i(49724),u=i(62250),_=i(39298),w=i(92522),x=i(57382),C=w("IE_PROTO"),j=Object,L=j.prototype;s.exports=x?j.getPrototypeOf:function(s){var o=_(s);if(a(o,C))return o[C];var i=o.constructor;return u(i)&&o instanceof i?i.prototype:o instanceof j?L:null}},16038:(s,o,i)=>{var a=i(5861),u=i(40346);s.exports=function baseIsSet(s){return u(s)&&"[object Set]"==a(s)}},16426:s=>{s.exports=function(){var s=document.getSelection();if(!s.rangeCount)return function(){};for(var o=document.activeElement,i=[],a=0;a{var a=i(43360),u=i(75288),_=Object.prototype.hasOwnProperty;s.exports=function assignValue(s,o,i){var w=s[o];_.call(s,o)&&u(w,i)&&(void 0!==i||o in s)||a(s,o,i)}},16708:(s,o,i)=>{"use strict";var a,u=i(65606);function CorkedRequest(s){var o=this;this.next=null,this.entry=null,this.finish=function(){!function onCorkedFinish(s,o,i){var a=s.entry;s.entry=null;for(;a;){var u=a.callback;o.pendingcb--,u(i),a=a.next}o.corkedRequestsFree.next=s}(o,s)}}s.exports=Writable,Writable.WritableState=WritableState;var _={deprecate:i(94643)},w=i(40345),x=i(48287).Buffer,C=(void 0!==i.g?i.g:"undefined"!=typeof window?window:"undefined"!=typeof self?self:{}).Uint8Array||function(){};var j,L=i(75896),B=i(65291).getHighWaterMark,$=i(86048).F,V=$.ERR_INVALID_ARG_TYPE,U=$.ERR_METHOD_NOT_IMPLEMENTED,z=$.ERR_MULTIPLE_CALLBACK,Y=$.ERR_STREAM_CANNOT_PIPE,Z=$.ERR_STREAM_DESTROYED,ee=$.ERR_STREAM_NULL_VALUES,ie=$.ERR_STREAM_WRITE_AFTER_END,ae=$.ERR_UNKNOWN_ENCODING,ce=L.errorOrDestroy;function nop(){}function WritableState(s,o,_){a=a||i(25382),s=s||{},"boolean"!=typeof _&&(_=o instanceof a),this.objectMode=!!s.objectMode,_&&(this.objectMode=this.objectMode||!!s.writableObjectMode),this.highWaterMark=B(this,s,"writableHighWaterMark",_),this.finalCalled=!1,this.needDrain=!1,this.ending=!1,this.ended=!1,this.finished=!1,this.destroyed=!1;var w=!1===s.decodeStrings;this.decodeStrings=!w,this.defaultEncoding=s.defaultEncoding||"utf8",this.length=0,this.writing=!1,this.corked=0,this.sync=!0,this.bufferProcessing=!1,this.onwrite=function(s){!function onwrite(s,o){var i=s._writableState,a=i.sync,_=i.writecb;if("function"!=typeof _)throw new z;if(function onwriteStateUpdate(s){s.writing=!1,s.writecb=null,s.length-=s.writelen,s.writelen=0}(i),o)!function onwriteError(s,o,i,a,_){--o.pendingcb,i?(u.nextTick(_,a),u.nextTick(finishMaybe,s,o),s._writableState.errorEmitted=!0,ce(s,a)):(_(a),s._writableState.errorEmitted=!0,ce(s,a),finishMaybe(s,o))}(s,i,a,o,_);else{var w=needFinish(i)||s.destroyed;w||i.corked||i.bufferProcessing||!i.bufferedRequest||clearBuffer(s,i),a?u.nextTick(afterWrite,s,i,w,_):afterWrite(s,i,w,_)}}(o,s)},this.writecb=null,this.writelen=0,this.bufferedRequest=null,this.lastBufferedRequest=null,this.pendingcb=0,this.prefinished=!1,this.errorEmitted=!1,this.emitClose=!1!==s.emitClose,this.autoDestroy=!!s.autoDestroy,this.bufferedRequestCount=0,this.corkedRequestsFree=new CorkedRequest(this)}function Writable(s){var o=this instanceof(a=a||i(25382));if(!o&&!j.call(Writable,this))return new Writable(s);this._writableState=new WritableState(s,this,o),this.writable=!0,s&&("function"==typeof s.write&&(this._write=s.write),"function"==typeof s.writev&&(this._writev=s.writev),"function"==typeof s.destroy&&(this._destroy=s.destroy),"function"==typeof s.final&&(this._final=s.final)),w.call(this)}function doWrite(s,o,i,a,u,_,w){o.writelen=a,o.writecb=w,o.writing=!0,o.sync=!0,o.destroyed?o.onwrite(new Z("write")):i?s._writev(u,o.onwrite):s._write(u,_,o.onwrite),o.sync=!1}function afterWrite(s,o,i,a){i||function onwriteDrain(s,o){0===o.length&&o.needDrain&&(o.needDrain=!1,s.emit("drain"))}(s,o),o.pendingcb--,a(),finishMaybe(s,o)}function clearBuffer(s,o){o.bufferProcessing=!0;var i=o.bufferedRequest;if(s._writev&&i&&i.next){var a=o.bufferedRequestCount,u=new Array(a),_=o.corkedRequestsFree;_.entry=i;for(var w=0,x=!0;i;)u[w]=i,i.isBuf||(x=!1),i=i.next,w+=1;u.allBuffers=x,doWrite(s,o,!0,o.length,u,"",_.finish),o.pendingcb++,o.lastBufferedRequest=null,_.next?(o.corkedRequestsFree=_.next,_.next=null):o.corkedRequestsFree=new CorkedRequest(o),o.bufferedRequestCount=0}else{for(;i;){var C=i.chunk,j=i.encoding,L=i.callback;if(doWrite(s,o,!1,o.objectMode?1:C.length,C,j,L),i=i.next,o.bufferedRequestCount--,o.writing)break}null===i&&(o.lastBufferedRequest=null)}o.bufferedRequest=i,o.bufferProcessing=!1}function needFinish(s){return s.ending&&0===s.length&&null===s.bufferedRequest&&!s.finished&&!s.writing}function callFinal(s,o){s._final((function(i){o.pendingcb--,i&&ce(s,i),o.prefinished=!0,s.emit("prefinish"),finishMaybe(s,o)}))}function finishMaybe(s,o){var i=needFinish(o);if(i&&(function prefinish(s,o){o.prefinished||o.finalCalled||("function"!=typeof s._final||o.destroyed?(o.prefinished=!0,s.emit("prefinish")):(o.pendingcb++,o.finalCalled=!0,u.nextTick(callFinal,s,o)))}(s,o),0===o.pendingcb&&(o.finished=!0,s.emit("finish"),o.autoDestroy))){var a=s._readableState;(!a||a.autoDestroy&&a.endEmitted)&&s.destroy()}return i}i(56698)(Writable,w),WritableState.prototype.getBuffer=function getBuffer(){for(var s=this.bufferedRequest,o=[];s;)o.push(s),s=s.next;return o},function(){try{Object.defineProperty(WritableState.prototype,"buffer",{get:_.deprecate((function writableStateBufferGetter(){return this.getBuffer()}),"_writableState.buffer is deprecated. Use _writableState.getBuffer instead.","DEP0003")})}catch(s){}}(),"function"==typeof Symbol&&Symbol.hasInstance&&"function"==typeof Function.prototype[Symbol.hasInstance]?(j=Function.prototype[Symbol.hasInstance],Object.defineProperty(Writable,Symbol.hasInstance,{value:function value(s){return!!j.call(this,s)||this===Writable&&(s&&s._writableState instanceof WritableState)}})):j=function realHasInstance(s){return s instanceof this},Writable.prototype.pipe=function(){ce(this,new Y)},Writable.prototype.write=function(s,o,i){var a=this._writableState,_=!1,w=!a.objectMode&&function _isUint8Array(s){return x.isBuffer(s)||s instanceof C}(s);return w&&!x.isBuffer(s)&&(s=function _uint8ArrayToBuffer(s){return x.from(s)}(s)),"function"==typeof o&&(i=o,o=null),w?o="buffer":o||(o=a.defaultEncoding),"function"!=typeof i&&(i=nop),a.ending?function writeAfterEnd(s,o){var i=new ie;ce(s,i),u.nextTick(o,i)}(this,i):(w||function validChunk(s,o,i,a){var _;return null===i?_=new ee:"string"==typeof i||o.objectMode||(_=new V("chunk",["string","Buffer"],i)),!_||(ce(s,_),u.nextTick(a,_),!1)}(this,a,s,i))&&(a.pendingcb++,_=function writeOrBuffer(s,o,i,a,u,_){if(!i){var w=function decodeChunk(s,o,i){s.objectMode||!1===s.decodeStrings||"string"!=typeof o||(o=x.from(o,i));return o}(o,a,u);a!==w&&(i=!0,u="buffer",a=w)}var C=o.objectMode?1:a.length;o.length+=C;var j=o.length-1))throw new ae(s);return this._writableState.defaultEncoding=s,this},Object.defineProperty(Writable.prototype,"writableBuffer",{enumerable:!1,get:function get(){return this._writableState&&this._writableState.getBuffer()}}),Object.defineProperty(Writable.prototype,"writableHighWaterMark",{enumerable:!1,get:function get(){return this._writableState.highWaterMark}}),Writable.prototype._write=function(s,o,i){i(new U("_write()"))},Writable.prototype._writev=null,Writable.prototype.end=function(s,o,i){var a=this._writableState;return"function"==typeof s?(i=s,s=null,o=null):"function"==typeof o&&(i=o,o=null),null!=s&&this.write(s,o),a.corked&&(a.corked=1,this.uncork()),a.ending||function endWritable(s,o,i){o.ending=!0,finishMaybe(s,o),i&&(o.finished?u.nextTick(i):s.once("finish",i));o.ended=!0,s.writable=!1}(this,a,i),this},Object.defineProperty(Writable.prototype,"writableLength",{enumerable:!1,get:function get(){return this._writableState.length}}),Object.defineProperty(Writable.prototype,"destroyed",{enumerable:!1,get:function get(){return void 0!==this._writableState&&this._writableState.destroyed},set:function set(s){this._writableState&&(this._writableState.destroyed=s)}}),Writable.prototype.destroy=L.destroy,Writable.prototype._undestroy=L.undestroy,Writable.prototype._destroy=function(s,o){o(s)}},16946:(s,o,i)=>{"use strict";var a=i(1907),u=i(98828),_=i(45807),w=Object,x=a("".split);s.exports=u((function(){return!w("z").propertyIsEnumerable(0)}))?function(s){return"String"===_(s)?x(s,""):w(s)}:w},16962:(s,o)=>{o.aliasToReal={each:"forEach",eachRight:"forEachRight",entries:"toPairs",entriesIn:"toPairsIn",extend:"assignIn",extendAll:"assignInAll",extendAllWith:"assignInAllWith",extendWith:"assignInWith",first:"head",conforms:"conformsTo",matches:"isMatch",property:"get",__:"placeholder",F:"stubFalse",T:"stubTrue",all:"every",allPass:"overEvery",always:"constant",any:"some",anyPass:"overSome",apply:"spread",assoc:"set",assocPath:"set",complement:"negate",compose:"flowRight",contains:"includes",dissoc:"unset",dissocPath:"unset",dropLast:"dropRight",dropLastWhile:"dropRightWhile",equals:"isEqual",identical:"eq",indexBy:"keyBy",init:"initial",invertObj:"invert",juxt:"over",omitAll:"omit",nAry:"ary",path:"get",pathEq:"matchesProperty",pathOr:"getOr",paths:"at",pickAll:"pick",pipe:"flow",pluck:"map",prop:"get",propEq:"matchesProperty",propOr:"getOr",props:"at",symmetricDifference:"xor",symmetricDifferenceBy:"xorBy",symmetricDifferenceWith:"xorWith",takeLast:"takeRight",takeLastWhile:"takeRightWhile",unapply:"rest",unnest:"flatten",useWith:"overArgs",where:"conformsTo",whereEq:"isMatch",zipObj:"zipObject"},o.aryMethod={1:["assignAll","assignInAll","attempt","castArray","ceil","create","curry","curryRight","defaultsAll","defaultsDeepAll","floor","flow","flowRight","fromPairs","invert","iteratee","memoize","method","mergeAll","methodOf","mixin","nthArg","over","overEvery","overSome","rest","reverse","round","runInContext","spread","template","trim","trimEnd","trimStart","uniqueId","words","zipAll"],2:["add","after","ary","assign","assignAllWith","assignIn","assignInAllWith","at","before","bind","bindAll","bindKey","chunk","cloneDeepWith","cloneWith","concat","conformsTo","countBy","curryN","curryRightN","debounce","defaults","defaultsDeep","defaultTo","delay","difference","divide","drop","dropRight","dropRightWhile","dropWhile","endsWith","eq","every","filter","find","findIndex","findKey","findLast","findLastIndex","findLastKey","flatMap","flatMapDeep","flattenDepth","forEach","forEachRight","forIn","forInRight","forOwn","forOwnRight","get","groupBy","gt","gte","has","hasIn","includes","indexOf","intersection","invertBy","invoke","invokeMap","isEqual","isMatch","join","keyBy","lastIndexOf","lt","lte","map","mapKeys","mapValues","matchesProperty","maxBy","meanBy","merge","mergeAllWith","minBy","multiply","nth","omit","omitBy","overArgs","pad","padEnd","padStart","parseInt","partial","partialRight","partition","pick","pickBy","propertyOf","pull","pullAll","pullAt","random","range","rangeRight","rearg","reject","remove","repeat","restFrom","result","sampleSize","some","sortBy","sortedIndex","sortedIndexOf","sortedLastIndex","sortedLastIndexOf","sortedUniqBy","split","spreadFrom","startsWith","subtract","sumBy","take","takeRight","takeRightWhile","takeWhile","tap","throttle","thru","times","trimChars","trimCharsEnd","trimCharsStart","truncate","union","uniqBy","uniqWith","unset","unzipWith","without","wrap","xor","zip","zipObject","zipObjectDeep"],3:["assignInWith","assignWith","clamp","differenceBy","differenceWith","findFrom","findIndexFrom","findLastFrom","findLastIndexFrom","getOr","includesFrom","indexOfFrom","inRange","intersectionBy","intersectionWith","invokeArgs","invokeArgsMap","isEqualWith","isMatchWith","flatMapDepth","lastIndexOfFrom","mergeWith","orderBy","padChars","padCharsEnd","padCharsStart","pullAllBy","pullAllWith","rangeStep","rangeStepRight","reduce","reduceRight","replace","set","slice","sortedIndexBy","sortedLastIndexBy","transform","unionBy","unionWith","update","xorBy","xorWith","zipWith"],4:["fill","setWith","updateWith"]},o.aryRearg={2:[1,0],3:[2,0,1],4:[3,2,0,1]},o.iterateeAry={dropRightWhile:1,dropWhile:1,every:1,filter:1,find:1,findFrom:1,findIndex:1,findIndexFrom:1,findKey:1,findLast:1,findLastFrom:1,findLastIndex:1,findLastIndexFrom:1,findLastKey:1,flatMap:1,flatMapDeep:1,flatMapDepth:1,forEach:1,forEachRight:1,forIn:1,forInRight:1,forOwn:1,forOwnRight:1,map:1,mapKeys:1,mapValues:1,partition:1,reduce:2,reduceRight:2,reject:1,remove:1,some:1,takeRightWhile:1,takeWhile:1,times:1,transform:2},o.iterateeRearg={mapKeys:[1],reduceRight:[1,0]},o.methodRearg={assignInAllWith:[1,0],assignInWith:[1,2,0],assignAllWith:[1,0],assignWith:[1,2,0],differenceBy:[1,2,0],differenceWith:[1,2,0],getOr:[2,1,0],intersectionBy:[1,2,0],intersectionWith:[1,2,0],isEqualWith:[1,2,0],isMatchWith:[2,1,0],mergeAllWith:[1,0],mergeWith:[1,2,0],padChars:[2,1,0],padCharsEnd:[2,1,0],padCharsStart:[2,1,0],pullAllBy:[2,1,0],pullAllWith:[2,1,0],rangeStep:[1,2,0],rangeStepRight:[1,2,0],setWith:[3,1,2,0],sortedIndexBy:[2,1,0],sortedLastIndexBy:[2,1,0],unionBy:[1,2,0],unionWith:[1,2,0],updateWith:[3,1,2,0],xorBy:[1,2,0],xorWith:[1,2,0],zipWith:[1,2,0]},o.methodSpread={assignAll:{start:0},assignAllWith:{start:0},assignInAll:{start:0},assignInAllWith:{start:0},defaultsAll:{start:0},defaultsDeepAll:{start:0},invokeArgs:{start:2},invokeArgsMap:{start:2},mergeAll:{start:0},mergeAllWith:{start:0},partial:{start:1},partialRight:{start:1},without:{start:1},zipAll:{start:0}},o.mutate={array:{fill:!0,pull:!0,pullAll:!0,pullAllBy:!0,pullAllWith:!0,pullAt:!0,remove:!0,reverse:!0},object:{assign:!0,assignAll:!0,assignAllWith:!0,assignIn:!0,assignInAll:!0,assignInAllWith:!0,assignInWith:!0,assignWith:!0,defaults:!0,defaultsAll:!0,defaultsDeep:!0,defaultsDeepAll:!0,merge:!0,mergeAll:!0,mergeAllWith:!0,mergeWith:!0},set:{set:!0,setWith:!0,unset:!0,update:!0,updateWith:!0}},o.realToAlias=function(){var s=Object.prototype.hasOwnProperty,i=o.aliasToReal,a={};for(var u in i){var _=i[u];s.call(a,_)?a[_].push(u):a[_]=[u]}return a}(),o.remap={assignAll:"assign",assignAllWith:"assignWith",assignInAll:"assignIn",assignInAllWith:"assignInWith",curryN:"curry",curryRightN:"curryRight",defaultsAll:"defaults",defaultsDeepAll:"defaultsDeep",findFrom:"find",findIndexFrom:"findIndex",findLastFrom:"findLast",findLastIndexFrom:"findLastIndex",getOr:"get",includesFrom:"includes",indexOfFrom:"indexOf",invokeArgs:"invoke",invokeArgsMap:"invokeMap",lastIndexOfFrom:"lastIndexOf",mergeAll:"merge",mergeAllWith:"mergeWith",padChars:"pad",padCharsEnd:"padEnd",padCharsStart:"padStart",propertyOf:"get",rangeStep:"range",rangeStepRight:"rangeRight",restFrom:"rest",spreadFrom:"spread",trimChars:"trim",trimCharsEnd:"trimEnd",trimCharsStart:"trimStart",zipAll:"zip"},o.skipFixed={castArray:!0,flow:!0,flowRight:!0,iteratee:!0,mixin:!0,rearg:!0,runInContext:!0},o.skipRearg={add:!0,assign:!0,assignIn:!0,bind:!0,bindKey:!0,concat:!0,difference:!0,divide:!0,eq:!0,gt:!0,gte:!0,isEqual:!0,lt:!0,lte:!0,matchesProperty:!0,merge:!0,multiply:!0,overArgs:!0,partial:!0,partialRight:!0,propertyOf:!0,random:!0,range:!0,rangeRight:!0,subtract:!0,zip:!0,zipObject:!0,zipObjectDeep:!0}},17255:(s,o,i)=>{var a=i(47422);s.exports=function basePropertyDeep(s){return function(o){return a(o,s)}}},17285:s=>{function source(s){return s?"string"==typeof s?s:s.source:null}function lookahead(s){return concat("(?=",s,")")}function concat(...s){return s.map((s=>source(s))).join("")}function either(...s){return"("+s.map((s=>source(s))).join("|")+")"}s.exports=function xml(s){const o=concat(/[A-Z_]/,function optional(s){return concat("(",s,")?")}(/[A-Z0-9_.-]*:/),/[A-Z0-9_.-]*/),i={className:"symbol",begin:/&[a-z]+;|&#[0-9]+;|&#x[a-f0-9]+;/},a={begin:/\s/,contains:[{className:"meta-keyword",begin:/#?[a-z_][a-z1-9_-]+/,illegal:/\n/}]},u=s.inherit(a,{begin:/\(/,end:/\)/}),_=s.inherit(s.APOS_STRING_MODE,{className:"meta-string"}),w=s.inherit(s.QUOTE_STRING_MODE,{className:"meta-string"}),x={endsWithParent:!0,illegal:/`]+/}]}]}]};return{name:"HTML, XML",aliases:["html","xhtml","rss","atom","xjb","xsd","xsl","plist","wsf","svg"],case_insensitive:!0,contains:[{className:"meta",begin://,relevance:10,contains:[a,w,_,u,{begin:/\[/,end:/\]/,contains:[{className:"meta",begin://,contains:[a,u,w,_]}]}]},s.COMMENT(//,{relevance:10}),{begin://,relevance:10},i,{className:"meta",begin:/<\?xml/,end:/\?>/,relevance:10},{className:"tag",begin:/)/,end:/>/,keywords:{name:"style"},contains:[x],starts:{end:/<\/style>/,returnEnd:!0,subLanguage:["css","xml"]}},{className:"tag",begin:/)/,end:/>/,keywords:{name:"script"},contains:[x],starts:{end:/<\/script>/,returnEnd:!0,subLanguage:["javascript","handlebars","xml"]}},{className:"tag",begin:/<>|<\/>/},{className:"tag",begin:concat(//,/>/,/\s/)))),end:/\/?>/,contains:[{className:"name",begin:o,relevance:0,starts:x}]},{className:"tag",begin:concat(/<\//,lookahead(concat(o,/>/))),contains:[{className:"name",begin:o,relevance:0},{begin:/>/,relevance:0,endsParent:!0}]}]}}},17400:(s,o,i)=>{var a=i(99374),u=1/0;s.exports=function toFinite(s){return s?(s=a(s))===u||s===-1/0?17976931348623157e292*(s<0?-1:1):s==s?s:0:0===s?s:0}},17533:s=>{s.exports=function yaml(s){var o="true false yes no null",i="[\\w#;/?:@&=+$,.~*'()[\\]]+",a={className:"string",relevance:0,variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/\S+/}],contains:[s.BACKSLASH_ESCAPE,{className:"template-variable",variants:[{begin:/\{\{/,end:/\}\}/},{begin:/%\{/,end:/\}/}]}]},u=s.inherit(a,{variants:[{begin:/'/,end:/'/},{begin:/"/,end:/"/},{begin:/[^\s,{}[\]]+/}]}),_={className:"number",begin:"\\b[0-9]{4}(-[0-9][0-9]){0,2}([Tt \\t][0-9][0-9]?(:[0-9][0-9]){2})?(\\.[0-9]*)?([ \\t])*(Z|[-+][0-9][0-9]?(:[0-9][0-9])?)?\\b"},w={end:",",endsWithParent:!0,excludeEnd:!0,keywords:o,relevance:0},x={begin:/\{/,end:/\}/,contains:[w],illegal:"\\n",relevance:0},C={begin:"\\[",end:"\\]",contains:[w],illegal:"\\n",relevance:0},j=[{className:"attr",variants:[{begin:"\\w[\\w :\\/.-]*:(?=[ \t]|$)"},{begin:'"\\w[\\w :\\/.-]*":(?=[ \t]|$)'},{begin:"'\\w[\\w :\\/.-]*':(?=[ \t]|$)"}]},{className:"meta",begin:"^---\\s*$",relevance:10},{className:"string",begin:"[\\|>]([1-9]?[+-])?[ ]*\\n( +)[^ ][^\\n]*\\n(\\2[^\\n]+\\n?)*"},{begin:"<%[%=-]?",end:"[%-]?%>",subLanguage:"ruby",excludeBegin:!0,excludeEnd:!0,relevance:0},{className:"type",begin:"!\\w+!"+i},{className:"type",begin:"!<"+i+">"},{className:"type",begin:"!"+i},{className:"type",begin:"!!"+i},{className:"meta",begin:"&"+s.UNDERSCORE_IDENT_RE+"$"},{className:"meta",begin:"\\*"+s.UNDERSCORE_IDENT_RE+"$"},{className:"bullet",begin:"-(?=[ ]|$)",relevance:0},s.HASH_COMMENT_MODE,{beginKeywords:o,keywords:{literal:o}},_,{className:"number",begin:s.C_NUMBER_RE+"\\b",relevance:0},x,C,a],L=[...j];return L.pop(),L.push(u),w.contains=L,{name:"YAML",case_insensitive:!0,aliases:["yml"],contains:j}}},17670:(s,o,i)=>{var a=i(12651);s.exports=function mapCacheDelete(s){var o=a(this,s).delete(s);return this.size-=o?1:0,o}},17965:(s,o,i)=>{"use strict";var a=i(16426),u={"text/plain":"Text","text/html":"Url",default:"Text"};s.exports=function copy(s,o){var i,_,w,x,C,j,L=!1;o||(o={}),i=o.debug||!1;try{if(w=a(),x=document.createRange(),C=document.getSelection(),(j=document.createElement("span")).textContent=s,j.ariaHidden="true",j.style.all="unset",j.style.position="fixed",j.style.top=0,j.style.clip="rect(0, 0, 0, 0)",j.style.whiteSpace="pre",j.style.webkitUserSelect="text",j.style.MozUserSelect="text",j.style.msUserSelect="text",j.style.userSelect="text",j.addEventListener("copy",(function(a){if(a.stopPropagation(),o.format)if(a.preventDefault(),void 0===a.clipboardData){i&&console.warn("unable to use e.clipboardData"),i&&console.warn("trying IE specific stuff"),window.clipboardData.clearData();var _=u[o.format]||u.default;window.clipboardData.setData(_,s)}else a.clipboardData.clearData(),a.clipboardData.setData(o.format,s);o.onCopy&&(a.preventDefault(),o.onCopy(a.clipboardData))})),document.body.appendChild(j),x.selectNodeContents(j),C.addRange(x),!document.execCommand("copy"))throw new Error("copy command was unsuccessful");L=!0}catch(a){i&&console.error("unable to copy using execCommand: ",a),i&&console.warn("trying IE specific stuff");try{window.clipboardData.setData(o.format||"text",s),o.onCopy&&o.onCopy(window.clipboardData),L=!0}catch(a){i&&console.error("unable to copy using clipboardData: ",a),i&&console.error("falling back to prompt"),_=function format(s){var o=(/mac os x/i.test(navigator.userAgent)?"⌘":"Ctrl")+"+C";return s.replace(/#{\s*key\s*}/g,o)}("message"in o?o.message:"Copy to clipboard: #{key}, Enter"),window.prompt(_,s)}}finally{C&&("function"==typeof C.removeRange?C.removeRange(x):C.removeAllRanges()),j&&document.body.removeChild(j),w()}return L}},18073:(s,o,i)=>{var a=i(85087),u=i(54641),_=i(70981);s.exports=function createRecurry(s,o,i,w,x,C,j,L,B,$){var V=8&o;o|=V?32:64,4&(o&=~(V?64:32))||(o&=-4);var U=[s,o,x,V?C:void 0,V?j:void 0,V?void 0:C,V?void 0:j,L,B,$],z=i.apply(void 0,U);return a(s)&&u(z,U),z.placeholder=w,_(z,s,o)}},19123:(s,o,i)=>{var a=i(65606),u=i(31499),_=i(88310).Stream;function resolve(s,o,i){var a,_=function create_indent(s,o){return new Array(o||0).join(s||"")}(o,i=i||0),w=s;if("object"==typeof s&&((w=s[a=Object.keys(s)[0]])&&w._elem))return w._elem.name=a,w._elem.icount=i,w._elem.indent=o,w._elem.indents=_,w._elem.interrupt=w,w._elem;var x,C=[],j=[];function get_attributes(s){Object.keys(s).forEach((function(o){C.push(function attribute(s,o){return s+'="'+u(o)+'"'}(o,s[o]))}))}switch(typeof w){case"object":if(null===w)break;w._attr&&get_attributes(w._attr),w._cdata&&j.push(("/g,"]]]]>")+"]]>"),w.forEach&&(x=!1,j.push(""),w.forEach((function(s){"object"==typeof s?"_attr"==Object.keys(s)[0]?get_attributes(s._attr):j.push(resolve(s,o,i+1)):(j.pop(),x=!0,j.push(u(s)))})),x||j.push(""));break;default:j.push(u(w))}return{name:a,interrupt:!1,attributes:C,content:j,icount:i,indents:_,indent:o}}function format(s,o,i){if("object"!=typeof o)return s(!1,o);var a=o.interrupt?1:o.content.length;function proceed(){for(;o.content.length;){var u=o.content.shift();if(void 0!==u){if(interrupt(u))return;format(s,u)}}s(!1,(a>1?o.indents:"")+(o.name?"":"")+(o.indent&&!i?"\n":"")),i&&i()}function interrupt(o){return!!o.interrupt&&(o.interrupt.append=s,o.interrupt.end=proceed,o.interrupt=!1,s(!0),!0)}if(s(!1,o.indents+(o.name?"<"+o.name:"")+(o.attributes.length?" "+o.attributes.join(" "):"")+(a?o.name?">":"":o.name?"/>":"")+(o.indent&&a>1?"\n":"")),!a)return s(!1,o.indent?"\n":"");interrupt(o)||proceed()}s.exports=function xml(s,o){"object"!=typeof o&&(o={indent:o});var i=o.stream?new _:null,u="",w=!1,x=o.indent?!0===o.indent?" ":o.indent:"",C=!0;function delay(s){C?a.nextTick(s):s()}function append(s,o){if(void 0!==o&&(u+=o),s&&!w&&(i=i||new _,w=!0),s&&w){var a=u;delay((function(){i.emit("data",a)})),u=""}}function add(s,o){format(append,resolve(s,x,x?1:0),o)}function end(){if(i){var s=u;delay((function(){i.emit("data",s),i.emit("end"),i.readable=!1,i.emit("close")}))}}return delay((function(){C=!1})),o.declaration&&function addXmlDeclaration(s){var o={version:"1.0",encoding:s.encoding||"UTF-8"};s.standalone&&(o.standalone=s.standalone),add({"?xml":{_attr:o}}),u=u.replace("/>","?>")}(o.declaration),s&&s.forEach?s.forEach((function(o,i){var a;i+1===s.length&&(a=end),add(o,a)})):add(s,end),i?(i.readable=!0,i):u},s.exports.element=s.exports.Element=function element(){var s={_elem:resolve(Array.prototype.slice.call(arguments)),push:function(s){if(!this.append)throw new Error("not assigned to a parent!");var o=this,i=this._elem.indent;format(this.append,resolve(s,i,this._elem.icount+(i?1:0)),(function(){o.append(!0)}))},close:function(s){void 0!==s&&this.push(s),this.end&&this.end()}};return s}},19219:s=>{s.exports=function cacheHas(s,o){return s.has(o)}},19287:s=>{"use strict";s.exports={CSSRuleList:0,CSSStyleDeclaration:0,CSSValueList:0,ClientRectList:0,DOMRectList:0,DOMStringList:0,DOMTokenList:1,DataTransferItemList:0,FileList:0,HTMLAllCollection:0,HTMLCollection:0,HTMLFormElement:0,HTMLSelectElement:0,MediaList:0,MimeTypeArray:0,NamedNodeMap:0,NodeList:1,PaintRequestList:0,Plugin:0,PluginArray:0,SVGLengthList:0,SVGNumberList:0,SVGPathSegList:0,SVGPointList:0,SVGStringList:0,SVGTransformList:0,SourceBufferList:0,StyleSheetList:0,TextTrackCueList:0,TextTrackList:0,TouchList:0}},19358:(s,o,i)=>{"use strict";var a=i(85582),u=i(49724),_=i(61626),w=i(88280),x=i(79192),C=i(19595),j=i(54829),L=i(34084),B=i(32096),$=i(39259),V=i(85884),U=i(39447),z=i(7376);s.exports=function(s,o,i,Y){var Z="stackTraceLimit",ee=Y?2:1,ie=s.split("."),ae=ie[ie.length-1],ce=a.apply(null,ie);if(ce){var le=ce.prototype;if(!z&&u(le,"cause")&&delete le.cause,!i)return ce;var pe=a("Error"),de=o((function(s,o){var i=B(Y?o:s,void 0),a=Y?new ce(s):new ce;return void 0!==i&&_(a,"message",i),V(a,de,a.stack,2),this&&w(le,this)&&L(a,this,de),arguments.length>ee&&$(a,arguments[ee]),a}));if(de.prototype=le,"Error"!==ae?x?x(de,pe):C(de,pe,{name:!0}):U&&Z in ce&&(j(de,ce,Z),j(de,ce,"prepareStackTrace")),C(de,ce),!z)try{le.name!==ae&&_(le,"name",ae),le.constructor=de}catch(s){}return de}}},19570:(s,o,i)=>{var a=i(37334),u=i(93243),_=i(83488),w=u?function(s,o){return u(s,"toString",{configurable:!0,enumerable:!1,value:a(o),writable:!0})}:_;s.exports=w},19595:(s,o,i)=>{"use strict";var a=i(49724),u=i(11042),_=i(13846),w=i(74284);s.exports=function(s,o,i){for(var x=u(o),C=w.f,j=_.f,L=0;L{"use strict";var a=i(23034);s.exports=a},19846:(s,o,i)=>{"use strict";var a=i(20798),u=i(98828),_=i(45951).String;s.exports=!!Object.getOwnPropertySymbols&&!u((function(){var s=Symbol("symbol detection");return!_(s)||!(Object(s)instanceof Symbol)||!Symbol.sham&&a&&a<41}))},19931:(s,o,i)=>{var a=i(31769),u=i(68090),_=i(68969),w=i(77797);s.exports=function baseUnset(s,o){return o=a(o,s),null==(s=_(s,o))||delete s[w(u(o))]}},20181:(s,o,i)=>{var a=/^\s+|\s+$/g,u=/^[-+]0x[0-9a-f]+$/i,_=/^0b[01]+$/i,w=/^0o[0-7]+$/i,x=parseInt,C="object"==typeof i.g&&i.g&&i.g.Object===Object&&i.g,j="object"==typeof self&&self&&self.Object===Object&&self,L=C||j||Function("return this")(),B=Object.prototype.toString,$=Math.max,V=Math.min,now=function(){return L.Date.now()};function isObject(s){var o=typeof s;return!!s&&("object"==o||"function"==o)}function toNumber(s){if("number"==typeof s)return s;if(function isSymbol(s){return"symbol"==typeof s||function isObjectLike(s){return!!s&&"object"==typeof s}(s)&&"[object Symbol]"==B.call(s)}(s))return NaN;if(isObject(s)){var o="function"==typeof s.valueOf?s.valueOf():s;s=isObject(o)?o+"":o}if("string"!=typeof s)return 0===s?s:+s;s=s.replace(a,"");var i=_.test(s);return i||w.test(s)?x(s.slice(2),i?2:8):u.test(s)?NaN:+s}s.exports=function debounce(s,o,i){var a,u,_,w,x,C,j=0,L=!1,B=!1,U=!0;if("function"!=typeof s)throw new TypeError("Expected a function");function invokeFunc(o){var i=a,_=u;return a=u=void 0,j=o,w=s.apply(_,i)}function shouldInvoke(s){var i=s-C;return void 0===C||i>=o||i<0||B&&s-j>=_}function timerExpired(){var s=now();if(shouldInvoke(s))return trailingEdge(s);x=setTimeout(timerExpired,function remainingWait(s){var i=o-(s-C);return B?V(i,_-(s-j)):i}(s))}function trailingEdge(s){return x=void 0,U&&a?invokeFunc(s):(a=u=void 0,w)}function debounced(){var s=now(),i=shouldInvoke(s);if(a=arguments,u=this,C=s,i){if(void 0===x)return function leadingEdge(s){return j=s,x=setTimeout(timerExpired,o),L?invokeFunc(s):w}(C);if(B)return x=setTimeout(timerExpired,o),invokeFunc(C)}return void 0===x&&(x=setTimeout(timerExpired,o)),w}return o=toNumber(o)||0,isObject(i)&&(L=!!i.leading,_=(B="maxWait"in i)?$(toNumber(i.maxWait)||0,o):_,U="trailing"in i?!!i.trailing:U),debounced.cancel=function cancel(){void 0!==x&&clearTimeout(x),j=0,a=C=u=x=void 0},debounced.flush=function flush(){return void 0===x?w:trailingEdge(now())},debounced}},20317:s=>{s.exports=function mapToArray(s){var o=-1,i=Array(s.size);return s.forEach((function(s,a){i[++o]=[a,s]})),i}},20334:(s,o,i)=>{"use strict";var a=i(48287).Buffer;class NonError extends Error{constructor(s){super(NonError._prepareSuperMessage(s)),Object.defineProperty(this,"name",{value:"NonError",configurable:!0,writable:!0}),Error.captureStackTrace&&Error.captureStackTrace(this,NonError)}static _prepareSuperMessage(s){try{return JSON.stringify(s)}catch{return String(s)}}}const u=[{property:"name",enumerable:!1},{property:"message",enumerable:!1},{property:"stack",enumerable:!1},{property:"code",enumerable:!0}],_=Symbol(".toJSON called"),destroyCircular=({from:s,seen:o,to_:i,forceEnumerable:w,maxDepth:x,depth:C})=>{const j=i||(Array.isArray(s)?[]:{});if(o.push(s),C>=x)return j;if("function"==typeof s.toJSON&&!0!==s[_])return(s=>{s[_]=!0;const o=s.toJSON();return delete s[_],o})(s);for(const[i,u]of Object.entries(s))"function"==typeof a&&a.isBuffer(u)?j[i]="[object Buffer]":"function"!=typeof u&&(u&&"object"==typeof u?o.includes(s[i])?j[i]="[Circular]":(C++,j[i]=destroyCircular({from:s[i],seen:o.slice(),forceEnumerable:w,maxDepth:x,depth:C})):j[i]=u);for(const{property:o,enumerable:i}of u)"string"==typeof s[o]&&Object.defineProperty(j,o,{value:s[o],enumerable:!!w||i,configurable:!0,writable:!0});return j};s.exports={serializeError:(s,o={})=>{const{maxDepth:i=Number.POSITIVE_INFINITY}=o;return"object"==typeof s&&null!==s?destroyCircular({from:s,seen:[],forceEnumerable:!0,maxDepth:i,depth:0}):"function"==typeof s?`[Function: ${s.name||"anonymous"}]`:s},deserializeError:(s,o={})=>{const{maxDepth:i=Number.POSITIVE_INFINITY}=o;if(s instanceof Error)return s;if("object"==typeof s&&null!==s&&!Array.isArray(s)){const o=new Error;return destroyCircular({from:s,seen:[],to_:o,maxDepth:i,depth:0}),o}return new NonError(s)}}},20426:s=>{var o=Object.prototype.hasOwnProperty;s.exports=function baseHas(s,i){return null!=s&&o.call(s,i)}},20575:(s,o,i)=>{"use strict";var a=i(3121);s.exports=function(s){return a(s.length)}},20798:(s,o,i)=>{"use strict";var a,u,_=i(45951),w=i(96794),x=_.process,C=_.Deno,j=x&&x.versions||C&&C.version,L=j&&j.v8;L&&(u=(a=L.split("."))[0]>0&&a[0]<4?1:+(a[0]+a[1])),!u&&w&&(!(a=w.match(/Edge\/(\d+)/))||a[1]>=74)&&(a=w.match(/Chrome\/(\d+)/))&&(u=+a[1]),s.exports=u},20850:(s,o,i)=>{"use strict";s.exports=i(46076)},20999:(s,o,i)=>{var a=i(69302),u=i(36800);s.exports=function createAssigner(s){return a((function(o,i){var a=-1,_=i.length,w=_>1?i[_-1]:void 0,x=_>2?i[2]:void 0;for(w=s.length>3&&"function"==typeof w?(_--,w):void 0,x&&u(i[0],i[1],x)&&(w=_<3?void 0:w,_=1),o=Object(o);++a<_;){var C=i[a];C&&s(o,C,a,w)}return o}))}},21549:(s,o,i)=>{var a=i(22032),u=i(63862),_=i(66721),w=i(12749),x=i(35749);function Hash(s){var o=-1,i=null==s?0:s.length;for(this.clear();++o{var a=i(16547),u=i(43360);s.exports=function copyObject(s,o,i,_){var w=!i;i||(i={});for(var x=-1,C=o.length;++x{var a=i(51873),u=i(37828),_=i(75288),w=i(25911),x=i(20317),C=i(84247),j=a?a.prototype:void 0,L=j?j.valueOf:void 0;s.exports=function equalByTag(s,o,i,a,j,B,$){switch(i){case"[object DataView]":if(s.byteLength!=o.byteLength||s.byteOffset!=o.byteOffset)return!1;s=s.buffer,o=o.buffer;case"[object ArrayBuffer]":return!(s.byteLength!=o.byteLength||!B(new u(s),new u(o)));case"[object Boolean]":case"[object Date]":case"[object Number]":return _(+s,+o);case"[object Error]":return s.name==o.name&&s.message==o.message;case"[object RegExp]":case"[object String]":return s==o+"";case"[object Map]":var V=x;case"[object Set]":var U=1&a;if(V||(V=C),s.size!=o.size&&!U)return!1;var z=$.get(s);if(z)return z==o;a|=2,$.set(s,o);var Y=w(V(s),V(o),a,j,B,$);return $.delete(s),Y;case"[object Symbol]":if(L)return L.call(s)==L.call(o)}return!1}},22032:(s,o,i)=>{var a=i(81042);s.exports=function hashClear(){this.__data__=a?a(null):{},this.size=0}},22225:s=>{var o="\\ud800-\\udfff",i="\\u2700-\\u27bf",a="a-z\\xdf-\\xf6\\xf8-\\xff",u="A-Z\\xc0-\\xd6\\xd8-\\xde",_="\\xac\\xb1\\xd7\\xf7\\x00-\\x2f\\x3a-\\x40\\x5b-\\x60\\x7b-\\xbf\\u2000-\\u206f \\t\\x0b\\f\\xa0\\ufeff\\n\\r\\u2028\\u2029\\u1680\\u180e\\u2000\\u2001\\u2002\\u2003\\u2004\\u2005\\u2006\\u2007\\u2008\\u2009\\u200a\\u202f\\u205f\\u3000",w="["+_+"]",x="\\d+",C="["+i+"]",j="["+a+"]",L="[^"+o+_+x+i+a+u+"]",B="(?:\\ud83c[\\udde6-\\uddff]){2}",$="[\\ud800-\\udbff][\\udc00-\\udfff]",V="["+u+"]",U="(?:"+j+"|"+L+")",z="(?:"+V+"|"+L+")",Y="(?:['’](?:d|ll|m|re|s|t|ve))?",Z="(?:['’](?:D|LL|M|RE|S|T|VE))?",ee="(?:[\\u0300-\\u036f\\ufe20-\\ufe2f\\u20d0-\\u20ff]|\\ud83c[\\udffb-\\udfff])?",ie="[\\ufe0e\\ufe0f]?",ae=ie+ee+("(?:\\u200d(?:"+["[^"+o+"]",B,$].join("|")+")"+ie+ee+")*"),ce="(?:"+[C,B,$].join("|")+")"+ae,le=RegExp([V+"?"+j+"+"+Y+"(?="+[w,V,"$"].join("|")+")",z+"+"+Z+"(?="+[w,V+U,"$"].join("|")+")",V+"?"+U+"+"+Y,V+"+"+Z,"\\d*(?:1ST|2ND|3RD|(?![123])\\dTH)(?=\\b|[a-z_])","\\d*(?:1st|2nd|3rd|(?![123])\\dth)(?=\\b|[A-Z_])",x,ce].join("|"),"g");s.exports=function unicodeWords(s){return s.match(le)||[]}},22551:(s,o,i)=>{"use strict";var a=i(96540),u=i(69982);function p(s){for(var o="https://reactjs.org/docs/error-decoder.html?invariant="+s,i=1;i