diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..bc541e8
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,14 @@
+root = true
+
+[*]
+end_of_line = lf
+indent_style = tab
+insert_final_newline = true
+trim_trailing_whitespace = true
+
+[*.{md,neon,yml}]
+indent_size = 2
+indent_style = space
+
+[phpstan-baseline.neon]
+indent_style = tab
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 0000000..acbcff9
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,9 @@
+.editorconfig export-ignore
+.gitattributes export-ignore
+.gitignore export-ignore
+.github export-ignore
+phpstan-baseline.neon export-ignore
+phpstan.neon export-ignore
+tests/ export-ignore
+
+*.php* diff=php linguist-language=PHP
diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml
new file mode 100644
index 0000000..b2ff8fc
--- /dev/null
+++ b/.github/workflows/checks.yml
@@ -0,0 +1,90 @@
+name: checks
+
+on:
+ - push
+
+env:
+ COMPOSER_NO_INTERACTION: "1"
+
+jobs:
+ format:
+ name: Code style
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.4'
+ coverage: none
+
+ - run: composer install --ansi --no-progress --prefer-dist
+
+ - name: Run PHP_Codesniffer
+ run: vendor/bin/phpcs
+
+ static_analysis:
+ name: Static analysis
+
+ runs-on: ubuntu-latest
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: '8.4'
+ coverage: none
+
+ - run: composer install --ansi --no-progress --prefer-dist
+
+ - name: Run parallel-lint
+ run: composer run lint
+
+ - name: Run PHPStan
+ run: composer run phpstan
+
+ tests:
+ name: PHP ${{ matrix.php }} tests on ${{ matrix.os }} with ${{ matrix.deps }} deps
+
+ needs:
+ - static_analysis
+
+ strategy:
+ matrix:
+ deps:
+ - stable
+ - lowest
+ os:
+ - macos-latest
+ - ubuntu-latest
+ - windows-latest
+ php:
+ - '8.4'
+
+ fail-fast: false
+
+ runs-on: ${{ matrix.os }}
+
+ steps:
+ - uses: actions/checkout@v4
+
+ - uses: shivammathur/setup-php@v2
+ with:
+ php-version: ${{ matrix.php }}
+ coverage: none
+
+ - run: composer install --ansi --no-progress --prefer-dist
+
+ - run: composer update --ansi --no-progress --prefer-lowest
+ if: matrix.deps == 'lowest'
+
+ - name: Run tests
+ run: composer run test
+
+ - if: failure()
+ uses: actions/upload-artifact@v4
+ with:
+ path: tests/output
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..13e687e
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+.DS_Store
+.phpunit.cache
+.phpunit.result.cache
+tests-temp
+vendor
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..8c8493d
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,28 @@
+BSD 3-Clause License
+
+Copyright (c) 2025, Vojtěch Dobeš
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are met:
+
+1. Redistributions of source code must retain the above copyright notice, this
+ list of conditions and the following disclaimer.
+
+2. Redistributions in binary form must reproduce the above copyright notice,
+ this list of conditions and the following disclaimer in the documentation
+ and/or other materials provided with the distribution.
+
+3. Neither the name of the copyright holder nor the names of its
+ contributors may be used to endorse or promote products derived from
+ this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/composer.json b/composer.json
new file mode 100644
index 0000000..ed05520
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,65 @@
+{
+ "authors": [
+ {
+ "name": "Vojtěch Dobeš",
+ "homepage": "https://vojtechdobes.com"
+ }
+ ],
+ "autoload": {
+ "psr-4": {
+ "Vojtechdobes\\PHPStan\\": "src/"
+ }
+ },
+ "autoload-dev": {
+ "psr-4": {
+ "Vojtechdobes\\Tests\\": "tests/"
+ }
+ },
+ "config": {
+ "allow-plugins": {
+ "dealerdirect/phpcodesniffer-composer-installer": false
+ },
+ "sort-packages": true
+ },
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "keywords": [
+ "ci",
+ "integration",
+ "normalization",
+ "phpstan",
+ "phpstan-rules",
+ "sanitization",
+ "static-analysis",
+ "static-code-analysis",
+ "validation"
+ ],
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "name": "vojtech-dobes/phpstan-php-conformance",
+ "require": {
+ "php": "~8.4",
+ "vojtech-dobes/php-conformance": "dev-master"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpstan/phpstan": "^2.1.12",
+ "phpstan/phpstan-strict-rules": "^2.0.4",
+ "phpunit/phpunit": "^12.1",
+ "spaze/phpstan-disallowed-calls": "^4.5.0",
+ "tracy/tracy": "^2.10.9",
+ "vojtech-dobes/php-codestyle": "~0.2.0"
+ },
+ "scripts": {
+ "lint": "parallel-lint src tests",
+ "phpstan": "phpstan analyse --memory-limit 256M",
+ "test": "composer dump-autoload && phpunit tests"
+ },
+ "type": "phpstan-extension"
+}
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 0000000..ea56e4d
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,2333 @@
+{
+ "_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": "f86a899042af4479bd90cae246bc1d76",
+ "packages": [
+ {
+ "name": "vojtech-dobes/php-conformance",
+ "version": "dev-master",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vojtech-dobes/php-conformance.git",
+ "reference": "a2a6c28068826a904dc77db9afe21e75fa9be3b7"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vojtech-dobes/php-conformance/zipball/a2a6c28068826a904dc77db9afe21e75fa9be3b7",
+ "reference": "a2a6c28068826a904dc77db9afe21e75fa9be3b7",
+ "shasum": ""
+ },
+ "require": {
+ "php": "~8.4"
+ },
+ "require-dev": {
+ "nette/tester": "^2.5.4",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpstan/phpstan": "^2.1.12",
+ "phpstan/phpstan-strict-rules": "^2.0.4",
+ "spaze/phpstan-disallowed-calls": "^4.5.0",
+ "tracy/tracy": "^2.10.9",
+ "vojtech-dobes/php-codestyle": "~0.2.0"
+ },
+ "default-branch": true,
+ "type": "library",
+ "autoload": {
+ "psr-4": {
+ "Vojtechdobes\\Conformance\\": "src/Conformance"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Vojtěch Dobeš",
+ "homepage": "https://vojtechdobes.com"
+ }
+ ],
+ "keywords": [
+ "constraint",
+ "data",
+ "input",
+ "normalization",
+ "sanitization",
+ "schema",
+ "validation"
+ ],
+ "support": {
+ "issues": "https://github.com/vojtech-dobes/php-conformance/issues",
+ "source": "https://github.com/vojtech-dobes/php-conformance/tree/master"
+ },
+ "time": "2025-07-18T12:27:35+00:00"
+ }
+ ],
+ "packages-dev": [
+ {
+ "name": "dealerdirect/phpcodesniffer-composer-installer",
+ "version": "v1.1.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/composer-installer.git",
+ "reference": "6e0fa428497bf560152ee73ffbb8af5c6a56b0dd"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/6e0fa428497bf560152ee73ffbb8af5c6a56b0dd",
+ "reference": "6e0fa428497bf560152ee73ffbb8af5c6a56b0dd",
+ "shasum": ""
+ },
+ "require": {
+ "composer-plugin-api": "^2.2",
+ "php": ">=5.4",
+ "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0"
+ },
+ "require-dev": {
+ "composer/composer": "^2.2",
+ "ext-json": "*",
+ "ext-zip": "*",
+ "php-parallel-lint/php-parallel-lint": "^1.4.0",
+ "phpcompatibility/php-compatibility": "^9.0",
+ "yoast/phpunit-polyfills": "^1.0"
+ },
+ "type": "composer-plugin",
+ "extra": {
+ "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin"
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Franck Nijhof",
+ "email": "opensource@frenck.dev",
+ "homepage": "https://frenck.dev",
+ "role": "Open source developer"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer Standards Composer Installer Plugin",
+ "keywords": [
+ "PHPCodeSniffer",
+ "PHP_CodeSniffer",
+ "code quality",
+ "codesniffer",
+ "composer",
+ "installer",
+ "phpcbf",
+ "phpcs",
+ "plugin",
+ "qa",
+ "quality",
+ "standard",
+ "standards",
+ "style guide",
+ "stylecheck",
+ "tests"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/composer-installer/issues",
+ "security": "https://github.com/PHPCSStandards/composer-installer/security/policy",
+ "source": "https://github.com/PHPCSStandards/composer-installer"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-06-27T17:24:01+00:00"
+ },
+ {
+ "name": "myclabs/deep-copy",
+ "version": "1.13.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/myclabs/DeepCopy.git",
+ "reference": "faed855a7b5f4d4637717c2b3863e277116beb36"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/faed855a7b5f4d4637717c2b3863e277116beb36",
+ "reference": "faed855a7b5f4d4637717c2b3863e277116beb36",
+ "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.3"
+ },
+ "funding": [
+ {
+ "url": "https://tidelift.com/funding/github/packagist/myclabs/deep-copy",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-05T12:25:42+00:00"
+ },
+ {
+ "name": "nikic/php-parser",
+ "version": "v5.5.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nikic/PHP-Parser.git",
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/ae59794362fe85e051a58ad36b289443f57be7a9",
+ "reference": "ae59794362fe85e051a58ad36b289443f57be7a9",
+ "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.5.0"
+ },
+ "time": "2025-05-31T08:24:38+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": "php-parallel-lint/php-parallel-lint",
+ "version": "v1.4.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/php-parallel-lint/PHP-Parallel-Lint.git",
+ "reference": "6db563514f27e19595a19f45a4bf757b6401194e"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/php-parallel-lint/PHP-Parallel-Lint/zipball/6db563514f27e19595a19f45a4bf757b6401194e",
+ "reference": "6db563514f27e19595a19f45a4bf757b6401194e",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "php": ">=5.3.0"
+ },
+ "replace": {
+ "grogy/php-parallel-lint": "*",
+ "jakub-onderka/php-parallel-lint": "*"
+ },
+ "require-dev": {
+ "nette/tester": "^1.3 || ^2.0",
+ "php-parallel-lint/php-console-highlighter": "0.* || ^1.0",
+ "squizlabs/php_codesniffer": "^3.6"
+ },
+ "suggest": {
+ "php-parallel-lint/php-console-highlighter": "Highlight syntax in code snippet"
+ },
+ "bin": [
+ "parallel-lint"
+ ],
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "./src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-2-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Jakub Onderka",
+ "email": "ahoj@jakubonderka.cz"
+ }
+ ],
+ "description": "This tool checks the syntax of PHP files about 20x faster than serial check.",
+ "homepage": "https://github.com/php-parallel-lint/PHP-Parallel-Lint",
+ "keywords": [
+ "lint",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/issues",
+ "source": "https://github.com/php-parallel-lint/PHP-Parallel-Lint/tree/v1.4.0"
+ },
+ "time": "2024-03-27T12:14:49+00:00"
+ },
+ {
+ "name": "phpstan/phpdoc-parser",
+ "version": "2.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpdoc-parser.git",
+ "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
+ "reference": "b9e61a61e39e02dd90944e9115241c7f7e76bfd8",
+ "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.2.0"
+ },
+ "time": "2025-07-13T07:04:09+00:00"
+ },
+ {
+ "name": "phpstan/phpstan",
+ "version": "2.1.17",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan.git",
+ "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan/zipball/89b5ef665716fa2a52ecd2633f21007a6a349053",
+ "reference": "89b5ef665716fa2a52ecd2633f21007a6a349053",
+ "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-05-21T20:55:28+00:00"
+ },
+ {
+ "name": "phpstan/phpstan-strict-rules",
+ "version": "2.0.4",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/phpstan/phpstan-strict-rules.git",
+ "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/phpstan/phpstan-strict-rules/zipball/3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
+ "reference": "3e139cbe67fafa3588e1dbe27ca50f31fdb6236a",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^2.0.4"
+ },
+ "require-dev": {
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^2.0",
+ "phpstan/phpstan-phpunit": "^2.0",
+ "phpunit/phpunit": "^9.6"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "rules.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "PHPStan\\": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Extra strict and opinionated rules for PHPStan",
+ "support": {
+ "issues": "https://github.com/phpstan/phpstan-strict-rules/issues",
+ "source": "https://github.com/phpstan/phpstan-strict-rules/tree/2.0.4"
+ },
+ "time": "2025-03-18T11:42:40+00:00"
+ },
+ {
+ "name": "phpunit/php-code-coverage",
+ "version": "12.3.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-code-coverage.git",
+ "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/ddec29dfc128eba9c204389960f2063f3b7fa170",
+ "reference": "ddec29dfc128eba9c204389960f2063f3b7fa170",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-libxml": "*",
+ "ext-xmlwriter": "*",
+ "nikic/php-parser": "^5.4.0",
+ "php": ">=8.3",
+ "phpunit/php-file-iterator": "^6.0",
+ "phpunit/php-text-template": "^5.0",
+ "sebastian/complexity": "^5.0",
+ "sebastian/environment": "^8.0",
+ "sebastian/lines-of-code": "^4.0",
+ "sebastian/version": "^6.0",
+ "theseer/tokenizer": "^1.2.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.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": "12.3.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/12.3.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/php-code-coverage",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-18T08:58:13+00:00"
+ },
+ {
+ "name": "phpunit/php-file-iterator",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-file-iterator.git",
+ "reference": "961bc913d42fe24a257bfff826a5068079ac7782"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-file-iterator/zipball/961bc913d42fe24a257bfff826a5068079ac7782",
+ "reference": "961bc913d42fe24a257bfff826a5068079ac7782",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.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": "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/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:58:37+00:00"
+ },
+ {
+ "name": "phpunit/php-invoker",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-invoker.git",
+ "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-invoker/zipball/12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+ "reference": "12b54e689b07a25a9b41e57736dfab6ec9ae5406",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "ext-pcntl": "*",
+ "phpunit/phpunit": "^12.0"
+ },
+ "suggest": {
+ "ext-pcntl": "*"
+ },
+ "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": "Invoke callables with a timeout",
+ "homepage": "https://github.com/sebastianbergmann/php-invoker/",
+ "keywords": [
+ "process"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-invoker/issues",
+ "security": "https://github.com/sebastianbergmann/php-invoker/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-invoker/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:58:58+00:00"
+ },
+ {
+ "name": "phpunit/php-text-template",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-text-template.git",
+ "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-text-template/zipball/e1367a453f0eda562eedb4f659e13aa900d66c53",
+ "reference": "e1367a453f0eda562eedb4f659e13aa900d66c53",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "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/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:59:16+00:00"
+ },
+ {
+ "name": "phpunit/php-timer",
+ "version": "8.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/php-timer.git",
+ "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/php-timer/zipball/f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+ "reference": "f258ce36aa457f3aa3339f9ed4c81fc66dc8c2cc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Utility class for timing",
+ "homepage": "https://github.com/sebastianbergmann/php-timer/",
+ "keywords": [
+ "timer"
+ ],
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/php-timer/issues",
+ "security": "https://github.com/sebastianbergmann/php-timer/security/policy",
+ "source": "https://github.com/sebastianbergmann/php-timer/tree/8.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:59:38+00:00"
+ },
+ {
+ "name": "phpunit/phpunit",
+ "version": "12.2.7",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/phpunit.git",
+ "reference": "8b1348b254e5959acaf1539c6bd790515fb49414"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/8b1348b254e5959acaf1539c6bd790515fb49414",
+ "reference": "8b1348b254e5959acaf1539c6bd790515fb49414",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-json": "*",
+ "ext-libxml": "*",
+ "ext-mbstring": "*",
+ "ext-xml": "*",
+ "ext-xmlwriter": "*",
+ "myclabs/deep-copy": "^1.13.3",
+ "phar-io/manifest": "^2.0.4",
+ "phar-io/version": "^3.2.1",
+ "php": ">=8.3",
+ "phpunit/php-code-coverage": "^12.3.1",
+ "phpunit/php-file-iterator": "^6.0.0",
+ "phpunit/php-invoker": "^6.0.0",
+ "phpunit/php-text-template": "^5.0.0",
+ "phpunit/php-timer": "^8.0.0",
+ "sebastian/cli-parser": "^4.0.0",
+ "sebastian/comparator": "^7.1.0",
+ "sebastian/diff": "^7.0.0",
+ "sebastian/environment": "^8.0.2",
+ "sebastian/exporter": "^7.0.0",
+ "sebastian/global-state": "^8.0.0",
+ "sebastian/object-enumerator": "^7.0.0",
+ "sebastian/type": "^6.0.2",
+ "sebastian/version": "^6.0.0",
+ "staabm/side-effects-detector": "^1.0.5"
+ },
+ "bin": [
+ "phpunit"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "12.2-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/12.2.7"
+ },
+ "funding": [
+ {
+ "url": "https://phpunit.de/sponsors.html",
+ "type": "custom"
+ },
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/phpunit/phpunit",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-07-11T04:11:13+00:00"
+ },
+ {
+ "name": "sebastian/cli-parser",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/cli-parser.git",
+ "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/cli-parser/zipball/6d584c727d9114bcdc14c86711cd1cad51778e7c",
+ "reference": "6d584c727d9114bcdc14c86711cd1cad51778e7c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for 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/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:53:50+00:00"
+ },
+ {
+ "name": "sebastian/comparator",
+ "version": "7.1.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/comparator.git",
+ "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/comparator/zipball/03d905327dccc0851c9a08d6a979dfc683826b6f",
+ "reference": "03d905327dccc0851c9a08d6a979dfc683826b6f",
+ "shasum": ""
+ },
+ "require": {
+ "ext-dom": "*",
+ "ext-mbstring": "*",
+ "php": ">=8.3",
+ "sebastian/diff": "^7.0",
+ "sebastian/exporter": "^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.2"
+ },
+ "suggest": {
+ "ext-bcmath": "For comparing BcMath\\Number objects"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.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": "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/7.1.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/comparator",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-17T07:41:58+00:00"
+ },
+ {
+ "name": "sebastian/complexity",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/complexity.git",
+ "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/complexity/zipball/bad4316aba5303d0221f43f8cee37eb58d384bbb",
+ "reference": "bad4316aba5303d0221f43f8cee37eb58d384bbb",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "5.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "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/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:55:25+00:00"
+ },
+ {
+ "name": "sebastian/diff",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/diff.git",
+ "reference": "7ab1ea946c012266ca32390913653d844ecd085f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/7ab1ea946c012266ca32390913653d844ecd085f",
+ "reference": "7ab1ea946c012266ca32390913653d844ecd085f",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0",
+ "symfony/process": "^7.2"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "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/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:55:46+00:00"
+ },
+ {
+ "name": "sebastian/environment",
+ "version": "8.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/environment.git",
+ "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/environment/zipball/d364b9e5d0d3b18a2573351a1786fbf96b7e0792",
+ "reference": "d364b9e5d0d3b18a2573351a1786fbf96b7e0792",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "suggest": {
+ "ext-posix": "*"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.0-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/8.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ },
+ {
+ "url": "https://liberapay.com/sebastianbergmann",
+ "type": "liberapay"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/sebastianbergmann",
+ "type": "thanks_dev"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/sebastian/environment",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-05-21T15:05:44+00:00"
+ },
+ {
+ "name": "sebastian/exporter",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/exporter.git",
+ "reference": "76432aafc58d50691a00d86d0632f1217a47b688"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/exporter/zipball/76432aafc58d50691a00d86d0632f1217a47b688",
+ "reference": "76432aafc58d50691a00d86d0632f1217a47b688",
+ "shasum": ""
+ },
+ "require": {
+ "ext-mbstring": "*",
+ "php": ">=8.3",
+ "sebastian/recursion-context": "^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "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/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:56:42+00:00"
+ },
+ {
+ "name": "sebastian/global-state",
+ "version": "8.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/global-state.git",
+ "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/global-state/zipball/570a2aeb26d40f057af686d63c4e99b075fb6cbc",
+ "reference": "570a2aeb26d40f057af686d63c4e99b075fb6cbc",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3",
+ "sebastian/object-reflector": "^5.0",
+ "sebastian/recursion-context": "^7.0"
+ },
+ "require-dev": {
+ "ext-dom": "*",
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "8.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/8.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:56:59+00:00"
+ },
+ {
+ "name": "sebastian/lines-of-code",
+ "version": "4.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/lines-of-code.git",
+ "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/lines-of-code/zipball/97ffee3bcfb5805568d6af7f0f893678fc076d2f",
+ "reference": "97ffee3bcfb5805568d6af7f0f893678fc076d2f",
+ "shasum": ""
+ },
+ "require": {
+ "nikic/php-parser": "^5.0",
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "4.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library for 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/4.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:57:28+00:00"
+ },
+ {
+ "name": "sebastian/object-enumerator",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-enumerator.git",
+ "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-enumerator/zipball/1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+ "reference": "1effe8e9b8e068e9ae228e542d5d11b5d16db894",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3",
+ "sebastian/object-reflector": "^5.0",
+ "sebastian/recursion-context": "^7.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ }
+ ],
+ "description": "Traverses array structures and object graphs to enumerate all referenced objects",
+ "homepage": "https://github.com/sebastianbergmann/object-enumerator/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-enumerator/issues",
+ "security": "https://github.com/sebastianbergmann/object-enumerator/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-enumerator/tree/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:57:48+00:00"
+ },
+ {
+ "name": "sebastian/object-reflector",
+ "version": "5.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/object-reflector.git",
+ "reference": "4bfa827c969c98be1e527abd576533293c634f6a"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/object-reflector/zipball/4bfa827c969c98be1e527abd576533293c634f6a",
+ "reference": "4bfa827c969c98be1e527abd576533293c634f6a",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.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": "Allows reflection of object attributes, including inherited and non-public ones",
+ "homepage": "https://github.com/sebastianbergmann/object-reflector/",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/object-reflector/issues",
+ "security": "https://github.com/sebastianbergmann/object-reflector/security/policy",
+ "source": "https://github.com/sebastianbergmann/object-reflector/tree/5.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T04:58:17+00:00"
+ },
+ {
+ "name": "sebastian/recursion-context",
+ "version": "7.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/recursion-context.git",
+ "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/recursion-context/zipball/c405ae3a63e01b32eb71577f8ec1604e39858a7c",
+ "reference": "c405ae3a63e01b32eb71577f8ec1604e39858a7c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "7.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de"
+ },
+ {
+ "name": "Jeff Welch",
+ "email": "whatthejeff@gmail.com"
+ },
+ {
+ "name": "Adam Harvey",
+ "email": "aharvey@php.net"
+ }
+ ],
+ "description": "Provides functionality to recursively process PHP variables",
+ "homepage": "https://github.com/sebastianbergmann/recursion-context",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/recursion-context/issues",
+ "security": "https://github.com/sebastianbergmann/recursion-context/security/policy",
+ "source": "https://github.com/sebastianbergmann/recursion-context/tree/7.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T05:00:01+00:00"
+ },
+ {
+ "name": "sebastian/type",
+ "version": "6.0.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/type.git",
+ "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/type/zipball/1d7cd6e514384c36d7a390347f57c385d4be6069",
+ "reference": "1d7cd6e514384c36d7a390347f57c385d4be6069",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^12.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": "Collection of value objects that represent the types of the PHP type system",
+ "homepage": "https://github.com/sebastianbergmann/type",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/type/issues",
+ "security": "https://github.com/sebastianbergmann/type/security/policy",
+ "source": "https://github.com/sebastianbergmann/type/tree/6.0.2"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-03-18T13:37:31+00:00"
+ },
+ {
+ "name": "sebastian/version",
+ "version": "6.0.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sebastianbergmann/version.git",
+ "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sebastianbergmann/version/zipball/3e6ccf7657d4f0a59200564b08cead899313b53c",
+ "reference": "3e6ccf7657d4f0a59200564b08cead899313b53c",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=8.3"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "6.0-dev"
+ }
+ },
+ "autoload": {
+ "classmap": [
+ "src/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Sebastian Bergmann",
+ "email": "sebastian@phpunit.de",
+ "role": "lead"
+ }
+ ],
+ "description": "Library that helps with managing the version number of Git-hosted PHP projects",
+ "homepage": "https://github.com/sebastianbergmann/version",
+ "support": {
+ "issues": "https://github.com/sebastianbergmann/version/issues",
+ "security": "https://github.com/sebastianbergmann/version/security/policy",
+ "source": "https://github.com/sebastianbergmann/version/tree/6.0.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/sebastianbergmann",
+ "type": "github"
+ }
+ ],
+ "time": "2025-02-07T05:00:38+00:00"
+ },
+ {
+ "name": "slevomat/coding-standard",
+ "version": "8.19.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/slevomat/coding-standard.git",
+ "reference": "458d665acd49009efebd7e0cb385d71ae9ac3220"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/458d665acd49009efebd7e0cb385d71ae9ac3220",
+ "reference": "458d665acd49009efebd7e0cb385d71ae9ac3220",
+ "shasum": ""
+ },
+ "require": {
+ "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0",
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpdoc-parser": "^2.1.0",
+ "squizlabs/php_codesniffer": "^3.13.0"
+ },
+ "require-dev": {
+ "phing/phing": "3.0.1",
+ "php-parallel-lint/php-parallel-lint": "1.4.0",
+ "phpstan/phpstan": "2.1.17",
+ "phpstan/phpstan-deprecation-rules": "2.0.3",
+ "phpstan/phpstan-phpunit": "2.0.6",
+ "phpstan/phpstan-strict-rules": "2.0.4",
+ "phpunit/phpunit": "9.6.8|10.5.45|11.4.4|11.5.21|12.1.3"
+ },
+ "type": "phpcodesniffer-standard",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "SlevomatCodingStandard\\": "SlevomatCodingStandard/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.",
+ "keywords": [
+ "dev",
+ "phpcs"
+ ],
+ "support": {
+ "issues": "https://github.com/slevomat/coding-standard/issues",
+ "source": "https://github.com/slevomat/coding-standard/tree/8.19.1"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/kukulich",
+ "type": "github"
+ },
+ {
+ "url": "https://tidelift.com/funding/github/packagist/slevomat/coding-standard",
+ "type": "tidelift"
+ }
+ ],
+ "time": "2025-06-09T17:53:57+00:00"
+ },
+ {
+ "name": "spaze/phpstan-disallowed-calls",
+ "version": "v4.6.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/spaze/phpstan-disallowed-calls.git",
+ "reference": "d77ea1351ac2cc16c00a389ea0db87fc11072b80"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/spaze/phpstan-disallowed-calls/zipball/d77ea1351ac2cc16c00a389ea0db87fc11072b80",
+ "reference": "d77ea1351ac2cc16c00a389ea0db87fc11072b80",
+ "shasum": ""
+ },
+ "require": {
+ "php": "^7.4 || ^8.0",
+ "phpstan/phpstan": "^1.12.6 || ^2.0"
+ },
+ "require-dev": {
+ "nette/neon": "^3.3.1",
+ "nikic/php-parser": "^4.13.2 || ^5.0",
+ "php-parallel-lint/php-console-highlighter": "^1.0",
+ "php-parallel-lint/php-parallel-lint": "^1.2",
+ "phpstan/phpstan-deprecation-rules": "^1.2 || ^2.0",
+ "phpunit/phpunit": "^8.5.14 || ^10.1 || ^11.0 || ^12.0",
+ "shipmonk/dead-code-detector": "^0.12",
+ "spaze/coding-standard": "^1.8"
+ },
+ "type": "phpstan-extension",
+ "extra": {
+ "phpstan": {
+ "includes": [
+ "extension.neon"
+ ]
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Spaze\\PHPStan\\Rules\\Disallowed\\": "src"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michal Špaček",
+ "email": "mail@michalspacek.cz",
+ "homepage": "https://www.michalspacek.cz"
+ }
+ ],
+ "description": "PHPStan rules to detect disallowed method & function calls, constant, namespace, attribute & superglobal usages, with powerful rules to re-allow a call or a usage in places where it should be allowed.",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/spaze/phpstan-disallowed-calls/issues",
+ "source": "https://github.com/spaze/phpstan-disallowed-calls/tree/v4.6.0"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/spaze",
+ "type": "github"
+ }
+ ],
+ "time": "2025-07-11T18:17:33+00:00"
+ },
+ {
+ "name": "squizlabs/php_codesniffer",
+ "version": "3.13.2",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/PHPCSStandards/PHP_CodeSniffer.git",
+ "reference": "5b5e3821314f947dd040c70f7992a64eac89025c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/PHPCSStandards/PHP_CodeSniffer/zipball/5b5e3821314f947dd040c70f7992a64eac89025c",
+ "reference": "5b5e3821314f947dd040c70f7992a64eac89025c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-simplexml": "*",
+ "ext-tokenizer": "*",
+ "ext-xmlwriter": "*",
+ "php": ">=5.4.0"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "^4.0 || ^5.0 || ^6.0 || ^7.0 || ^8.0 || ^9.3.4"
+ },
+ "bin": [
+ "bin/phpcbf",
+ "bin/phpcs"
+ ],
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.x-dev"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "Greg Sherwood",
+ "role": "Former lead"
+ },
+ {
+ "name": "Juliette Reinders Folmer",
+ "role": "Current lead"
+ },
+ {
+ "name": "Contributors",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer/graphs/contributors"
+ }
+ ],
+ "description": "PHP_CodeSniffer tokenizes PHP, JavaScript and CSS files and detects violations of a defined set of coding standards.",
+ "homepage": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "keywords": [
+ "phpcs",
+ "standards",
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/PHPCSStandards/PHP_CodeSniffer/issues",
+ "security": "https://github.com/PHPCSStandards/PHP_CodeSniffer/security/policy",
+ "source": "https://github.com/PHPCSStandards/PHP_CodeSniffer",
+ "wiki": "https://github.com/PHPCSStandards/PHP_CodeSniffer/wiki"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/PHPCSStandards",
+ "type": "github"
+ },
+ {
+ "url": "https://github.com/jrfnl",
+ "type": "github"
+ },
+ {
+ "url": "https://opencollective.com/php_codesniffer",
+ "type": "open_collective"
+ },
+ {
+ "url": "https://thanks.dev/u/gh/phpcsstandards",
+ "type": "thanks_dev"
+ }
+ ],
+ "time": "2025-06-17T22:17:01+00:00"
+ },
+ {
+ "name": "staabm/side-effects-detector",
+ "version": "1.0.5",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/staabm/side-effects-detector.git",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/staabm/side-effects-detector/zipball/d8334211a140ce329c13726d4a715adbddd0a163",
+ "reference": "d8334211a140ce329c13726d4a715adbddd0a163",
+ "shasum": ""
+ },
+ "require": {
+ "ext-tokenizer": "*",
+ "php": "^7.4 || ^8.0"
+ },
+ "require-dev": {
+ "phpstan/extension-installer": "^1.4.3",
+ "phpstan/phpstan": "^1.12.6",
+ "phpunit/phpunit": "^9.6.21",
+ "symfony/var-dumper": "^5.4.43",
+ "tomasvotruba/type-coverage": "1.0.0",
+ "tomasvotruba/unused-public": "1.0.0"
+ },
+ "type": "library",
+ "autoload": {
+ "classmap": [
+ "lib/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "description": "A static analysis tool to detect side effects in PHP code",
+ "keywords": [
+ "static analysis"
+ ],
+ "support": {
+ "issues": "https://github.com/staabm/side-effects-detector/issues",
+ "source": "https://github.com/staabm/side-effects-detector/tree/1.0.5"
+ },
+ "funding": [
+ {
+ "url": "https://github.com/staabm",
+ "type": "github"
+ }
+ ],
+ "time": "2024-10-20T05:08:20+00:00"
+ },
+ {
+ "name": "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": "tracy/tracy",
+ "version": "v2.10.10",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/nette/tracy.git",
+ "reference": "32303e02c222eea8571402a8310fc3fe70422c37"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/nette/tracy/zipball/32303e02c222eea8571402a8310fc3fe70422c37",
+ "reference": "32303e02c222eea8571402a8310fc3fe70422c37",
+ "shasum": ""
+ },
+ "require": {
+ "ext-json": "*",
+ "ext-session": "*",
+ "php": "8.0 - 8.4"
+ },
+ "conflict": {
+ "nette/di": "<3.0"
+ },
+ "require-dev": {
+ "latte/latte": "^2.5 || ^3.0",
+ "nette/di": "^3.0",
+ "nette/http": "^3.0",
+ "nette/mail": "^3.0 || ^4.0",
+ "nette/tester": "^2.2",
+ "nette/utils": "^3.0 || ^4.0",
+ "phpstan/phpstan": "^1.0",
+ "psr/log": "^1.0 || ^2.0 || ^3.0"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.10-dev"
+ }
+ },
+ "autoload": {
+ "files": [
+ "src/Tracy/functions.php"
+ ],
+ "classmap": [
+ "src"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "authors": [
+ {
+ "name": "David Grudl",
+ "homepage": "https://davidgrudl.com"
+ },
+ {
+ "name": "Nette Community",
+ "homepage": "https://nette.org/contributors"
+ }
+ ],
+ "description": "😎 Tracy: the addictive tool to ease debugging PHP code for cool developers. Friendly design, logging, profiler, advanced features like debugging AJAX calls or CLI support. You will love it.",
+ "homepage": "https://tracy.nette.org",
+ "keywords": [
+ "Xdebug",
+ "debug",
+ "debugger",
+ "nette",
+ "profiler"
+ ],
+ "support": {
+ "issues": "https://github.com/nette/tracy/issues",
+ "source": "https://github.com/nette/tracy/tree/v2.10.10"
+ },
+ "time": "2025-04-28T14:35:15+00:00"
+ },
+ {
+ "name": "vojtech-dobes/php-codestyle",
+ "version": "0.2.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/vojtech-dobes/php-codestyle.git",
+ "reference": "d59431f2d4cc36987b9eefba40b6bd8fddd4a440"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/vojtech-dobes/php-codestyle/zipball/d59431f2d4cc36987b9eefba40b6bd8fddd4a440",
+ "reference": "d59431f2d4cc36987b9eefba40b6bd8fddd4a440",
+ "shasum": ""
+ },
+ "require": {
+ "slevomat/coding-standard": "~8.19.0"
+ },
+ "type": "phpcodesniffer-standard",
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "BSD-3-Clause"
+ ],
+ "keywords": [
+ "code-style",
+ "codesniffer",
+ "coding-standard",
+ "dev",
+ "phpcs"
+ ],
+ "support": {
+ "issues": "https://github.com/vojtech-dobes/php-codestyle/issues",
+ "source": "https://github.com/vojtech-dobes/php-codestyle/tree/0.2.0"
+ },
+ "time": "2025-06-09T07:30:13+00:00"
+ }
+ ],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": {
+ "vojtech-dobes/php-conformance": 20
+ },
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": {
+ "php": "~8.4"
+ },
+ "platform-dev": {},
+ "plugin-api-version": "2.6.0"
+}
diff --git a/extension.neon b/extension.neon
new file mode 100644
index 0000000..aed3105
--- /dev/null
+++ b/extension.neon
@@ -0,0 +1,4 @@
+services:
+ - class: Vojtechdobes\PHPStan\Conformance\ExpressionTypeResolverExtension
+ tags:
+ - phpstan.broker.expressionTypeResolverExtension
diff --git a/phpcs.xml.dist b/phpcs.xml.dist
new file mode 100644
index 0000000..b5863e4
--- /dev/null
+++ b/phpcs.xml.dist
@@ -0,0 +1,20 @@
+
+
+
+
+ src
+ tests
+
+
+
+
+
+
+
+
+
+
+
+ */tests/cases/*
+
+
diff --git a/phpstan-baseline.neon b/phpstan-baseline.neon
new file mode 100644
index 0000000..aab4991
--- /dev/null
+++ b/phpstan-baseline.neon
@@ -0,0 +1,2 @@
+parameters:
+ ignoreErrors: []
diff --git a/phpstan.dist.neon b/phpstan.dist.neon
new file mode 100644
index 0000000..295dfdc
--- /dev/null
+++ b/phpstan.dist.neon
@@ -0,0 +1,34 @@
+includes:
+ - phpstan-baseline.neon
+ - vendor/phpstan/phpstan-strict-rules/rules.neon
+ - vendor/spaze/phpstan-disallowed-calls/extension.neon
+
+parameters:
+ checkMissingCallableSignature: true
+ disallowedFunctionCalls:
+ - function:
+ - 'dump()'
+ - 'var_dump()'
+ message: 'avoid committing debug calls'
+
+ exceptions:
+ check:
+ missingCheckedExceptionInThrows: true
+ tooWideThrowType: true
+
+ uncheckedExceptionClasses:
+ - Error
+ - LogicException
+ - PHPStan\ShouldNotHappenException
+
+ excludePaths:
+ - tests/data/TypeInferenceTest
+
+ level: 8
+
+ paths:
+ - src
+ - tests
+
+ strictRules:
+ disallowedShortTernary: false
diff --git a/phpunit.xml.dist b/phpunit.xml.dist
new file mode 100644
index 0000000..5f2cf32
--- /dev/null
+++ b/phpunit.xml.dist
@@ -0,0 +1,17 @@
+
+
+
+
+
+
diff --git a/readme.md b/readme.md
new file mode 100644
index 0000000..cf25f58
--- /dev/null
+++ b/readme.md
@@ -0,0 +1,24 @@
+# PHPStan extension for [Vojtechdobes\Conformance](https://github.com/vojtech-dobes/php-conformance)
+
+
+
+This PHPStan extension is recommended to use with [`vojtech-dobes/php-conformance`](https://github.com/vojtech-dobes/php-conformance) validation & normalization library. It lets PHPStan understand what exact data structure does given constraint lead to.
+
+
+
+## Installation
+
+To install the latest version, run the following command:
+
+```
+composer require vojtech-dobes/phpstan-php-conformance
+```
+
+If you're not using [`phpstan/extension-installer`](https://github.com/phpstan/extension-installer), then add this to your PHPStan config:
+
+```neon
+includes:
+ - vendor/vojtech-dobes/phpstan-php-conformance/extension.neon
+```
+
+No further configuration needed.
diff --git a/src/Conformance/ExpressionTypeResolverExtension.php b/src/Conformance/ExpressionTypeResolverExtension.php
new file mode 100644
index 0000000..2d47b49
--- /dev/null
+++ b/src/Conformance/ExpressionTypeResolverExtension.php
@@ -0,0 +1,282 @@
+class instanceof PhpParser\Node\Expr) {
+ $constraintClassNames = $scope
+ ->getType($expr->class)
+ ->getClassStringObjectType()
+ ->getObjectClassNames();
+ } elseif ($expr->class instanceof PhpParser\Node\Name) {
+ $constraintClassNames = [
+ $expr->class->name,
+ ];
+ } elseif ($expr->class->name !== null) {
+ $constraintClassNames = [
+ $expr->class->name->toString(),
+ ];
+ } else {
+ $constraintClassNames = [];
+ }
+
+ $result = [];
+
+ foreach ($constraintClassNames as $constraintClassName) {
+ $resolver = match ($constraintClassName) {
+ Vojtechdobes\Conformance\Constraints\BooleanAll::class => $this->resolveBooleanAll(...),
+ Vojtechdobes\Conformance\Constraints\ForEachItem::class => $this->resolveForEachItem(...),
+ Vojtechdobes\Conformance\Constraints\IsRecord::class => $this->resolveIsRecord(...),
+ default => null,
+ };
+
+ if ($resolver === null) {
+ continue;
+ }
+
+ $constraintType = $resolver($constraintClassName, $expr, $scope);
+
+ if ($constraintType === null) {
+ continue;
+ }
+
+ $result[] = $constraintType;
+ }
+
+ if ($result === []) {
+ return null;
+ }
+
+ return PHPStan\Type\TypeCombinator::union(...$result);
+ }
+
+
+
+ private function resolveBooleanAll(
+ string $className,
+ PhpParser\Node\Expr\New_ $expr,
+ PHPStan\Analyser\Scope $scope,
+ ): ?PHPStan\Type\Type
+ {
+ $parameters = $this->getConstraintConstructorParameters(
+ $expr,
+ $scope,
+ $className,
+ [
+ 'constraints',
+ ],
+ );
+
+ if ($parameters === null) {
+ return null;
+ }
+
+ $arrays = $parameters['constraints']->getConstantArrays();
+
+ if ($arrays === []) {
+ return null;
+ }
+
+ return PHPStan\Type\TypeCombinator::union(
+ ...array_map(
+ static fn ($array) => new PHPStan\Type\Generic\GenericObjectType($className, [
+ $array
+ ->getFirstIterableValueType()
+ ->getTemplateType(Vojtechdobes\Conformance\Constraint::class, 'TPreValue'),
+ PHPStan\Type\TypeCombinator::intersect(
+ ...array_map(
+ static fn ($type) => $type->getTemplateType(Vojtechdobes\Conformance\Constraint::class, 'TPostValue'),
+ $array->getValueTypes(),
+ ),
+ ),
+ ]),
+ $arrays,
+ ),
+ );
+ }
+
+
+
+ private function resolveForEachItem(
+ string $className,
+ PhpParser\Node\Expr\New_ $expr,
+ PHPStan\Analyser\Scope $scope,
+ ): ?PHPStan\Type\Type
+ {
+ $parameters = $this->getConstraintConstructorParameters(
+ $expr,
+ $scope,
+ $className,
+ [
+ 'constraint',
+ ],
+ );
+
+ if ($parameters === null) {
+ return null;
+ }
+
+ $constraint = $parameters['constraint'];
+
+ if (new PHPStan\Type\ObjectType(Vojtechdobes\Conformance\Constraint::class)->isSuperTypeOf($constraint)->yes() === false) {
+ return null;
+ }
+
+ return new PHPStan\Type\Generic\GenericObjectType($className, [
+ $constraint->getTemplateType(Vojtechdobes\Conformance\Constraint::class, 'TPreValue'),
+ $constraint->getTemplateType(Vojtechdobes\Conformance\Constraint::class, 'TPostValue'),
+ ]);
+ }
+
+
+
+ private function resolveIsRecord(
+ string $className,
+ PhpParser\Node\Expr\New_ $expr,
+ PHPStan\Analyser\Scope $scope,
+ ): ?PHPStan\Type\Type
+ {
+ $parameters = $this->getConstraintConstructorParameters(
+ $expr,
+ $scope,
+ $className,
+ [
+ 'requiredFields',
+ 'optionalFields',
+ ],
+ );
+
+ if ($parameters === null) {
+ return null;
+ }
+
+ $resultBuilders = [];
+
+ foreach ($parameters['requiredFields']->getConstantArrays() as $array) {
+ $constantArrayBuilder = PHPStan\Type\Constant\ConstantArrayTypeBuilder::createEmpty();
+
+ foreach ($array->getKeyTypes() as $keyType) {
+ $constantArrayBuilder->setOffsetValueType(
+ $keyType,
+ $array->getOffsetValueType($keyType)->getTemplateType(Vojtechdobes\Conformance\Constraint::class, 'TPostValue'),
+ );
+ }
+
+ $resultBuilders[] = $constantArrayBuilder;
+ }
+
+ foreach ($parameters['optionalFields']->getConstantArrays() as $array) {
+ foreach ($array->getKeyTypes() as $keyType) {
+ foreach ($resultBuilders as $resultBuilder) {
+ $resultBuilder->setOffsetValueType(
+ $keyType,
+ $array->getOffsetValueType($keyType)->getTemplateType(Vojtechdobes\Conformance\Constraint::class, 'TPostValue'),
+ optional: true,
+ );
+ }
+ }
+ }
+
+ return new PHPStan\Type\Generic\GenericObjectType($className, [
+ $parameters['requiredFields'],
+ $parameters['optionalFields'],
+ PHPStan\Type\TypeCombinator::union(
+ ...array_map(
+ static fn ($resultBuilder) => $resultBuilder->getArray(),
+ $resultBuilders,
+ ),
+ ),
+ ]);
+ }
+
+
+
+ /**
+ * @template TKey of string
+ * @param non-empty-list $parameters
+ * @return array|null
+ */
+ private function getConstraintConstructorParameters(
+ PhpParser\Node\Expr\New_ $expr,
+ PHPStan\Analyser\Scope $scope,
+ string $className,
+ array $parameters,
+ ): ?array
+ {
+ $args = [];
+
+ $constructorReflection = $scope->getMethodReflection(
+ new PHPStan\Type\ObjectType($className),
+ '__construct',
+ ) ?? throw new PHPStan\ShouldNotHappenException();
+
+ $nativeParameters = PHPStan\Reflection\ParametersAcceptorSelector::selectFromArgs(
+ $scope,
+ array_filter(
+ $expr->args,
+ static fn ($arg) => $arg instanceof PhpParser\Node\Arg,
+ ),
+ $constructorReflection->getVariants(),
+ $constructorReflection->getNamedArgumentsVariants(),
+ )->getParameters();
+
+ foreach ($expr->args as $i => $arg) {
+ if ($arg instanceof PhpParser\Node\VariadicPlaceholder) {
+ return null;
+ }
+
+ if ($arg->name !== null) {
+ $args[$arg->name->toString()] = $scope->getType($arg->value);
+ } elseif ($arg->unpack) {
+ foreach ($scope->getType($arg->value)->getConstantArrays() as $argConstantArray) {
+ foreach ($parameters as $parameter) {
+ if ($argConstantArray->hasOffsetValueType(new PHPStan\Type\Constant\ConstantStringType($parameter))->yes()) {
+ $args[$parameter] = $argConstantArray->getOffsetValueType(new PHPStan\Type\Constant\ConstantStringType($parameter));
+ }
+ }
+ }
+ } elseif (isset($nativeParameters[$i])) {
+ $args[$nativeParameters[$i]->getName()] = $scope->getType($arg->value);
+ }
+ }
+
+ $getNativeParameterType = static fn ($name) => PHPStan\Type\TypeCombinator::union(
+ ...array_map(
+ static function ($variant) use ($name): PHPStan\Type\Type {
+ $parameter = array_find(
+ $variant->getParameters(),
+ static fn ($parameter) => $parameter->getName() === $name,
+ ) ?? throw new PHPStan\ShouldNotHappenException();
+
+ return $parameter->getDefaultValue() ?? $parameter->getType();
+ },
+ $constructorReflection->getVariants(),
+ ),
+ );
+
+ $result = [];
+
+ foreach ($parameters as $parameter) {
+ $result[$parameter] = $args[$parameter] ?? $getNativeParameterType($parameter);
+ }
+
+ return $result;
+ }
+
+}
diff --git a/tests/TypeInferenceTest.php b/tests/TypeInferenceTest.php
new file mode 100644
index 0000000..97cf96b
--- /dev/null
+++ b/tests/TypeInferenceTest.php
@@ -0,0 +1,41 @@
+>
+ */
+ public static function dataFileAsserts(): iterable
+ {
+ foreach (glob(__DIR__ . '/data/TypeInferenceTest/*.php') ?: [] as $file) {
+ yield from self::gatherAssertTypes($file);
+ }
+ }
+
+
+
+ #[PHPUnit\Framework\Attributes\DataProvider('dataFileAsserts')]
+ public function testFileAsserts(
+ string $assertType,
+ string $file,
+ mixed ...$args,
+ ): void
+ {
+ $this->assertFileAsserts($assertType, $file, ...$args);
+ }
+
+
+
+ public static function getAdditionalConfigFiles(): array
+ {
+ return [__DIR__ . '/../extension.neon'];
+ }
+
+}
diff --git a/tests/data/TypeInferenceTest/BooleanAll.php b/tests/data/TypeInferenceTest/BooleanAll.php
new file mode 100644
index 0000000..4bf16f4
--- /dev/null
+++ b/tests/data/TypeInferenceTest/BooleanAll.php
@@ -0,0 +1,22 @@
+",
+ new Vojtechdobes\Conformance\Constraints\BooleanAll([
+ new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ new Vojtechdobes\Conformance\Constraints\Choice(['a']),
+ ]),
+);
+
+assertType(
+ 'Vojtechdobes\Conformance\Constraints\BooleanAll',
+ new Vojtechdobes\Conformance\Constraints\BooleanAll([
+ new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ new Vojtechdobes\Conformance\Constraints\IsNull(),
+ ]),
+);
diff --git a/tests/data/TypeInferenceTest/Choice.php b/tests/data/TypeInferenceTest/Choice.php
new file mode 100644
index 0000000..bc6bf14
--- /dev/null
+++ b/tests/data/TypeInferenceTest/Choice.php
@@ -0,0 +1,15 @@
+",
+ new Vojtechdobes\Conformance\Constraints\Choice([
+ 'a',
+ 'b',
+ 'c',
+ ]),
+);
diff --git a/tests/data/TypeInferenceTest/ForEachItem.php b/tests/data/TypeInferenceTest/ForEachItem.php
new file mode 100644
index 0000000..a422aa0
--- /dev/null
+++ b/tests/data/TypeInferenceTest/ForEachItem.php
@@ -0,0 +1,17 @@
+",
+ new Vojtechdobes\Conformance\Constraints\ForEachItem(
+ new Vojtechdobes\Conformance\Constraints\Choice([
+ 'a',
+ 'b',
+ 'c',
+ ]),
+ ),
+);
diff --git a/tests/data/TypeInferenceTest/IsRecord.php b/tests/data/TypeInferenceTest/IsRecord.php
new file mode 100644
index 0000000..c610325
--- /dev/null
+++ b/tests/data/TypeInferenceTest/IsRecord.php
@@ -0,0 +1,34 @@
+ [
+ 'a' => new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ 'b' => new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ ],
+];
+
+assertType(
+ 'Vojtechdobes\Conformance\Constraints\IsRecord',
+ new $isRecordClass(...$isRecordArgs),
+);
+
+assertType(
+ 'Vojtechdobes\Conformance\Constraints\IsRecord',
+ new Vojtechdobes\Conformance\Constraints\IsRecord(
+ optionalFields: [
+ 'c' => new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ 'd' => new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ ],
+ requiredFields: [
+ 'a' => new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ 'b' => new Vojtechdobes\Conformance\Constraints\IsNonEmptyString(),
+ ],
+ ),
+);