}
},
"require": {
- "cuyz/valinor": "^1.8.2",
+ "cuyz/valinor": "^1.12.0",
"dragonmantank/cron-expression": "^3.3.3",
"erusev/parsedown": "^1.7.4",
"ezyang/htmlpurifier": "^4.17",
"guzzlehttp/guzzle": "^7.8.1",
"guzzlehttp/psr7": "^2.6.2",
- "laminas/laminas-diactoros": "^3.3.0",
+ "laminas/laminas-diactoros": "^3.3.1",
"laminas/laminas-httphandlerrunner": "^2.10.0",
"laminas/laminas-progressbar": "^2.13",
- "paragonie/constant_time_encoding": "^2.6.3",
+ "paragonie/constant_time_encoding": "^2.7.0",
"pelago/emogrifier": "^7.2.0",
"psr/clock": "^1.0",
"psr/event-dispatcher": "^1.0",
"psr/http-server-middleware": "^1.0.2",
"psr/log": "^3.0",
"scssphp/scssphp": "^1.12.1",
- "sebastian/diff": "^5.1.0",
- "symfony/polyfill-php82": "^1.28.0",
- "symfony/polyfill-php83": "^1.28",
+ "sebastian/diff": "^5.1.1",
+ "symfony/polyfill-php82": "^1.30.0",
+ "symfony/polyfill-php83": "^1.30",
"willdurand/negotiation": "^3.1"
},
"replace": {
"Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
"This file is @generated automatically"
],
- "content-hash": "1a986b53b455f0e5e4a58126dc8364ce",
+ "content-hash": "f252359a254921f59dc57b78f2c04059",
"packages": [
{
"name": "cuyz/valinor",
- "version": "1.8.2",
+ "version": "1.12.0",
"source": {
"type": "git",
"url": "https://github.com/CuyZ/Valinor.git",
- "reference": "daf8206d11b1cb6b308ecd2eb6b65657d2248544"
+ "reference": "3bc40798a5ff64aee8a28509b73f7f84d5c66ac9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/daf8206d11b1cb6b308ecd2eb6b65657d2248544",
- "reference": "daf8206d11b1cb6b308ecd2eb6b65657d2248544",
+ "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/3bc40798a5ff64aee8a28509b73f7f84d5c66ac9",
+ "reference": "3bc40798a5ff64aee8a28509b73f7f84d5c66ac9",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0",
- "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.4",
- "infection/infection": "^0.26",
+ "infection/infection": "^0.27",
"marcocesarato/php-conventional-changelog": "^1.12",
"mikey179/vfsstream": "^1.6.10",
"phpstan/phpstan": "^1.3",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
- "phpunit/phpunit": "^9.5",
- "rector/rector": "~0.17.0",
+ "phpunit/phpunit": "^10.5",
+ "rector/rector": "^1.0",
"vimeo/psalm": "^5.0"
},
"type": "library",
],
"support": {
"issues": "https://github.com/CuyZ/Valinor/issues",
- "source": "https://github.com/CuyZ/Valinor/tree/1.8.2"
+ "source": "https://github.com/CuyZ/Valinor/tree/1.12.0"
},
"funding": [
{
"type": "github"
}
],
- "time": "2024-01-08T20:31:48+00:00"
+ "time": "2024-04-04T16:42:55+00:00"
},
{
"name": "dragonmantank/cron-expression",
},
{
"name": "laminas/laminas-diactoros",
- "version": "3.3.0",
+ "version": "3.3.1",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-diactoros.git",
- "reference": "4db52734837c60259c9b2d7caf08eef8f7f9b9ac"
+ "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/4db52734837c60259c9b2d7caf08eef8f7f9b9ac",
- "reference": "4db52734837c60259c9b2d7caf08eef8f7f9b9ac",
+ "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45",
+ "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45",
"shasum": ""
},
"require": {
"http-interop/http-factory-tests": "^0.9.0",
"laminas/laminas-coding-standard": "~2.5.0",
"php-http/psr7-integration-tests": "^1.3",
- "phpunit/phpunit": "^9.5.28",
+ "phpunit/phpunit": "^9.6.16",
"psalm/plugin-phpunit": "^0.18.4",
- "vimeo/psalm": "^5.15.0"
+ "vimeo/psalm": "^5.22.1"
},
"type": "library",
"extra": {
"type": "community_bridge"
}
],
- "time": "2023-10-26T11:01:07+00:00"
+ "time": "2024-02-16T16:06:16+00:00"
},
{
"name": "laminas/laminas-httphandlerrunner",
},
{
"name": "paragonie/constant_time_encoding",
- "version": "v2.6.3",
+ "version": "v2.7.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938"
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938",
+ "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105",
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105",
"shasum": ""
},
"require": {
"issues": "https://github.com/paragonie/constant_time_encoding/issues",
"source": "https://github.com/paragonie/constant_time_encoding"
},
- "time": "2022-06-14T06:56:20+00:00"
+ "time": "2024-05-08T12:18:48+00:00"
},
{
"name": "pelago/emogrifier",
},
{
"name": "psr/http-factory",
- "version": "1.0.2",
+ "version": "1.1.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
- "reference": "e616d01114759c4c489f93b099585439f795fe35"
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
- "reference": "e616d01114759c4c489f93b099585439f795fe35",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
- "php": ">=7.0.0",
+ "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"type": "library",
"homepage": "https://www.php-fig.org/"
}
],
- "description": "Common interfaces for PSR-7 HTTP message factories",
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
"response"
],
"support": {
- "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+ "source": "https://github.com/php-fig/http-factory"
},
- "time": "2023-04-10T20:10:41+00:00"
+ "time": "2024-04-15T12:06:14+00:00"
},
{
"name": "psr/http-message",
},
{
"name": "sabberworm/php-css-parser",
- "version": "8.4.0",
+ "version": "v8.5.1",
"source": {
"type": "git",
- "url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
- "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30"
+ "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
+ "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30",
- "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30",
+ "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
+ "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
"shasum": ""
},
"require": {
"php": ">=5.6.20"
},
"require-dev": {
- "codacy/coverage": "^1.4",
- "phpunit/phpunit": "^4.8.36"
+ "phpunit/phpunit": "^5.7.27"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "9.0.x-dev"
+ }
+ },
"autoload": {
"psr-4": {
"Sabberworm\\CSS\\": "src/"
"authors": [
{
"name": "Raphael Schweikert"
+ },
+ {
+ "name": "Oliver Klee",
+ "email": "github@oliverklee.de"
+ },
+ {
+ "name": "Jake Hotson",
+ "email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"stylesheet"
],
"support": {
- "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
- "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0"
+ "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
+ "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.5.1"
},
- "time": "2021-12-11T13:40:54+00:00"
+ "time": "2024-02-15T16:41:13+00:00"
},
{
"name": "scssphp/scssphp",
},
{
"name": "sebastian/diff",
- "version": "5.1.0",
+ "version": "5.1.1",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
- "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
"shasum": ""
},
"require": {
},
"require-dev": {
"phpunit/phpunit": "^10.0",
- "symfony/process": "^4.2 || ^5"
+ "symfony/process": "^6.4"
},
"type": "library",
"extra": {
"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.0"
+ "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
},
"funding": [
{
"type": "github"
}
],
- "time": "2023-12-22T10:55:06+00:00"
+ "time": "2024-03-02T07:15:17+00:00"
},
{
"name": "symfony/css-selector",
- "version": "v6.4.0",
+ "version": "v6.4.8",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4"
+ "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/d036c6c0d0b09e24a14a35f8292146a658f986e4",
- "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/4b61b02fe15db48e3687ce1c45ea385d1780fe08",
+ "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08",
"shasum": ""
},
"require": {
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v6.4.0"
+ "source": "https://github.com/symfony/css-selector/tree/v6.4.8"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-10-31T08:40:20+00:00"
+ "time": "2024-05-31T14:49:08+00:00"
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.4.0",
+ "version": "v3.5.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-05-23T14:45:45+00:00"
+ "time": "2024-04-18T09:32:20+00:00"
},
{
"name": "symfony/polyfill-php82",
- "version": "v1.28.0",
+ "version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php82.git",
- "reference": "7716bea9c86776fb3362d6b52fe1fc9471056a49"
+ "reference": "77ff49780f56906788a88974867ed68bc49fae5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/7716bea9c86776fb3362d6b52fe1fc9471056a49",
- "reference": "7716bea9c86776fb3362d6b52fe1fc9471056a49",
+ "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/77ff49780f56906788a88974867ed68bc49fae5b",
+ "reference": "77ff49780f56906788a88974867ed68bc49fae5b",
"shasum": ""
},
"require": {
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php82/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-php82/tree/v1.30.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-08-25T17:27:25+00:00"
+ "time": "2024-06-19T12:30:46+00:00"
},
{
"name": "symfony/polyfill-php83",
- "version": "v1.28.0",
+ "version": "v1.30.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php83.git",
- "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11"
+ "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11",
- "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
+ "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
"shasum": ""
},
"require": {
- "php": ">=7.1",
- "symfony/polyfill-php80": "^1.14"
+ "php": ">=7.1"
},
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0"
},
"funding": [
{
"type": "tidelift"
}
],
- "time": "2023-08-16T06:22:46+00:00"
+ "time": "2024-06-19T12:35:24+00:00"
},
{
"name": "webmozart/assert",
'Cron\\MinutesField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/MinutesField.php',
'Cron\\MonthField' => $vendorDir . '/dragonmantank/cron-expression/src/Cron/MonthField.php',
'CuyZ\\Valinor\\Cache\\ChainCache' => $vendorDir . '/cuyz/valinor/src/Cache/ChainCache.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\CacheCompiler' => $vendorDir . '/cuyz/valinor/src/Cache/Compiled/CacheCompiler.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\CompiledPhpFileCache' => $vendorDir . '/cuyz/valinor/src/Cache/Compiled/CompiledPhpFileCache.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\HasArguments' => $vendorDir . '/cuyz/valinor/src/Cache/Compiled/HasArguments.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\MixedValueCacheCompiler' => $vendorDir . '/cuyz/valinor/src/Cache/Compiled/MixedValueCacheCompiler.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\PhpCacheFile' => $vendorDir . '/cuyz/valinor/src/Cache/Compiled/PhpCacheFile.php',
'CuyZ\\Valinor\\Cache\\Exception\\CacheDirectoryNotWritable' => $vendorDir . '/cuyz/valinor/src/Cache/Exception/CacheDirectoryNotWritable.php',
'CuyZ\\Valinor\\Cache\\Exception\\CompiledPhpCacheFileNotWritten' => $vendorDir . '/cuyz/valinor/src/Cache/Exception/CompiledPhpCacheFileNotWritten.php',
'CuyZ\\Valinor\\Cache\\Exception\\CorruptedCompiledPhpCacheFile' => $vendorDir . '/cuyz/valinor/src/Cache/Exception/CorruptedCompiledPhpCacheFile.php',
'CuyZ\\Valinor\\Cache\\RuntimeCache' => $vendorDir . '/cuyz/valinor/src/Cache/RuntimeCache.php',
'CuyZ\\Valinor\\Cache\\WarmupCache' => $vendorDir . '/cuyz/valinor/src/Cache/WarmupCache.php',
'CuyZ\\Valinor\\Cache\\Warmup\\RecursiveCacheWarmupService' => $vendorDir . '/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php',
+ 'CuyZ\\Valinor\\Definition\\AttributeDefinition' => $vendorDir . '/cuyz/valinor/src/Definition/AttributeDefinition.php',
'CuyZ\\Valinor\\Definition\\Attributes' => $vendorDir . '/cuyz/valinor/src/Definition/Attributes.php',
- 'CuyZ\\Valinor\\Definition\\AttributesContainer' => $vendorDir . '/cuyz/valinor/src/Definition/AttributesContainer.php',
'CuyZ\\Valinor\\Definition\\ClassDefinition' => $vendorDir . '/cuyz/valinor/src/Definition/ClassDefinition.php',
'CuyZ\\Valinor\\Definition\\Exception\\ClassTypeAliasesDuplication' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/ClassTypeAliasesDuplication.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\ExtendTagTypeError' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/ExtendTagTypeError.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\InvalidExtendTagClassName' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/InvalidExtendTagClassName.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\InvalidExtendTagType' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/InvalidExtendTagType.php',
'CuyZ\\Valinor\\Definition\\Exception\\InvalidTypeAliasImportClass' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClass.php',
'CuyZ\\Valinor\\Definition\\Exception\\InvalidTypeAliasImportClassType' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClassType.php',
- 'CuyZ\\Valinor\\Definition\\Exception\\TypesDoNotMatch' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/TypesDoNotMatch.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\SeveralExtendTagsFound' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/SeveralExtendTagsFound.php',
'CuyZ\\Valinor\\Definition\\Exception\\UnknownTypeAliasImport' => $vendorDir . '/cuyz/valinor/src/Definition/Exception/UnknownTypeAliasImport.php',
'CuyZ\\Valinor\\Definition\\FunctionDefinition' => $vendorDir . '/cuyz/valinor/src/Definition/FunctionDefinition.php',
'CuyZ\\Valinor\\Definition\\FunctionObject' => $vendorDir . '/cuyz/valinor/src/Definition/FunctionObject.php',
'CuyZ\\Valinor\\Definition\\FunctionsContainer' => $vendorDir . '/cuyz/valinor/src/Definition/FunctionsContainer.php',
'CuyZ\\Valinor\\Definition\\MethodDefinition' => $vendorDir . '/cuyz/valinor/src/Definition/MethodDefinition.php',
'CuyZ\\Valinor\\Definition\\Methods' => $vendorDir . '/cuyz/valinor/src/Definition/Methods.php',
- 'CuyZ\\Valinor\\Definition\\NativeAttributes' => $vendorDir . '/cuyz/valinor/src/Definition/NativeAttributes.php',
'CuyZ\\Valinor\\Definition\\ParameterDefinition' => $vendorDir . '/cuyz/valinor/src/Definition/ParameterDefinition.php',
'CuyZ\\Valinor\\Definition\\Parameters' => $vendorDir . '/cuyz/valinor/src/Definition/Parameters.php',
'CuyZ\\Valinor\\Definition\\Properties' => $vendorDir . '/cuyz/valinor/src/Definition/Properties.php',
'CuyZ\\Valinor\\Definition\\Repository\\Cache\\Compiler\\TypeCompiler' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php',
'CuyZ\\Valinor\\Definition\\Repository\\ClassDefinitionRepository' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\FunctionDefinitionRepository' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/FunctionDefinitionRepository.php',
- 'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\NativeAttributesRepository' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/Reflection/NativeAttributesRepository.php',
+ 'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionAttributesRepository' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionClassDefinitionRepository' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionFunctionDefinitionRepository' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionMethodDefinitionBuilder' => $vendorDir . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php',
'CuyZ\\Valinor\\Mapper\\ArgumentsMapper' => $vendorDir . '/cuyz/valinor/src/Mapper/ArgumentsMapper.php',
'CuyZ\\Valinor\\Mapper\\ArgumentsMapperError' => $vendorDir . '/cuyz/valinor/src/Mapper/ArgumentsMapperError.php',
'CuyZ\\Valinor\\Mapper\\Exception\\InvalidMappingTypeSignature' => $vendorDir . '/cuyz/valinor/src/Mapper/Exception/InvalidMappingTypeSignature.php',
+ 'CuyZ\\Valinor\\Mapper\\Exception\\TypeErrorDuringArgumentsMapping' => $vendorDir . '/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringArgumentsMapping.php',
+ 'CuyZ\\Valinor\\Mapper\\Exception\\TypeErrorDuringMapping' => $vendorDir . '/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringMapping.php',
'CuyZ\\Valinor\\Mapper\\MappingError' => $vendorDir . '/cuyz/valinor/src/Mapper/MappingError.php',
'CuyZ\\Valinor\\Mapper\\Object\\Argument' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Argument.php',
'CuyZ\\Valinor\\Mapper\\Object\\Arguments' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Arguments.php',
'CuyZ\\Valinor\\Mapper\\Object\\ArgumentsValues' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/ArgumentsValues.php',
+ 'CuyZ\\Valinor\\Mapper\\Object\\Constructor' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Constructor.php',
'CuyZ\\Valinor\\Mapper\\Object\\DateTimeFormatConstructor' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/DateTimeFormatConstructor.php',
'CuyZ\\Valinor\\Mapper\\Object\\DynamicConstructor' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/DynamicConstructor.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\CannotFindObjectBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/CannotFindObjectBuilder.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\CannotInstantiateObject' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/CannotInstantiateObject.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\CannotParseToDateTime' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/CannotParseToDateTime.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidConstructorClassTypeParameter' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorClassTypeParameter.php',
+ 'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidConstructorMethodWithAttributeReturnType' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorMethodWithAttributeReturnType.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidConstructorReturnType' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorReturnType.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidSource' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidSource.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\MissingConstructorClassTypeParameter' => $vendorDir . '/cuyz/valinor/src/Mapper/Object/Exception/MissingConstructorClassTypeParameter.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\CasterNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/CasterNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\CasterProxyNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/CasterProxyNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ErrorCatcherNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/ErrorCatcherNodeBuilder.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\FilteredObjectNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/FilteredObjectNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\InterfaceNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\IterableNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/IterableNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ListNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/ListNodeBuilder.php',
- 'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\NativeClassNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/NativeClassNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\NodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/NodeBuilder.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\NullNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/NullNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ObjectImplementations' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/ObjectImplementations.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ObjectNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/ObjectNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\RootNodeBuilder' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Builder/RootNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\CannotInferFinalClass' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/CannotInferFinalClass.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\CannotResolveObjectType' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveObjectType.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\CannotResolveTypeFromUnion' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveTypeFromUnion.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InterfaceHasBothConstructorAndInfer' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/InterfaceHasBothConstructorAndInfer.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InvalidAbstractObjectName' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/InvalidAbstractObjectName.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InvalidListKey' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/InvalidListKey.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InvalidNodeHasNoMappedValue' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/InvalidNodeHasNoMappedValue.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\ObjectImplementationCallbackError' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/ObjectImplementationCallbackError.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\ObjectImplementationNotRegistered' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/ObjectImplementationNotRegistered.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\ResolvedImplementationIsNotAccepted' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/ResolvedImplementationIsNotAccepted.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\SourceIsNotNull' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/SourceIsNotNull.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\SourceMustBeIterable' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/SourceMustBeIterable.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\SourceValueWasNotFilled' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/SourceValueWasNotFilled.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\TooManyResolvedTypesFromUnion' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/TooManyResolvedTypesFromUnion.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\UnexpectedArrayKeysForClass' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/UnexpectedArrayKeysForClass.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\UnexpectedShapedArrayKeys' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/UnexpectedShapedArrayKeys.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\UnresolvableShellType' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Exception/UnresolvableShellType.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Message\\DefaultMessage' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Message/DefaultMessage.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Message\\ErrorMessage' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Message/ErrorMessage.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Message\\Formatter\\AggregateMessageFormatter' => $vendorDir . '/cuyz/valinor/src/Mapper/Tree/Message/Formatter/AggregateMessageFormatter.php',
'CuyZ\\Valinor\\Mapper\\TypeTreeMapper' => $vendorDir . '/cuyz/valinor/src/Mapper/TypeTreeMapper.php',
'CuyZ\\Valinor\\Mapper\\TypeTreeMapperError' => $vendorDir . '/cuyz/valinor/src/Mapper/TypeTreeMapperError.php',
'CuyZ\\Valinor\\Normalizer\\ArrayNormalizer' => $vendorDir . '/cuyz/valinor/src/Normalizer/ArrayNormalizer.php',
+ 'CuyZ\\Valinor\\Normalizer\\AsTransformer' => $vendorDir . '/cuyz/valinor/src/Normalizer/AsTransformer.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\CircularReferenceFoundDuringNormalization' => $vendorDir . '/cuyz/valinor/src/Normalizer/Exception/CircularReferenceFoundDuringNormalization.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\KeyTransformerHasTooManyParameters' => $vendorDir . '/cuyz/valinor/src/Normalizer/Exception/KeyTransformerHasTooManyParameters.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\KeyTransformerParameterInvalidType' => $vendorDir . '/cuyz/valinor/src/Normalizer/Exception/KeyTransformerParameterInvalidType.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\TransformerHasTooManyParameters' => $vendorDir . '/cuyz/valinor/src/Normalizer/Exception/TransformerHasTooManyParameters.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\TypeUnhandledByNormalizer' => $vendorDir . '/cuyz/valinor/src/Normalizer/Exception/TypeUnhandledByNormalizer.php',
'CuyZ\\Valinor\\Normalizer\\Format' => $vendorDir . '/cuyz/valinor/src/Normalizer/Format.php',
+ 'CuyZ\\Valinor\\Normalizer\\Formatter\\Exception\\CannotFormatInvalidTypeToJson' => $vendorDir . '/cuyz/valinor/src/Normalizer/Formatter/Exception/CannotFormatInvalidTypeToJson.php',
+ 'CuyZ\\Valinor\\Normalizer\\Formatter\\JsonFormatter' => $vendorDir . '/cuyz/valinor/src/Normalizer/Formatter/JsonFormatter.php',
+ 'CuyZ\\Valinor\\Normalizer\\Formatter\\StreamFormatter' => $vendorDir . '/cuyz/valinor/src/Normalizer/Formatter/StreamFormatter.php',
+ 'CuyZ\\Valinor\\Normalizer\\JsonNormalizer' => $vendorDir . '/cuyz/valinor/src/Normalizer/JsonNormalizer.php',
'CuyZ\\Valinor\\Normalizer\\Normalizer' => $vendorDir . '/cuyz/valinor/src/Normalizer/Normalizer.php',
+ 'CuyZ\\Valinor\\Normalizer\\StreamNormalizer' => $vendorDir . '/cuyz/valinor/src/Normalizer/StreamNormalizer.php',
'CuyZ\\Valinor\\Normalizer\\Transformer\\KeyTransformersHandler' => $vendorDir . '/cuyz/valinor/src/Normalizer/Transformer/KeyTransformersHandler.php',
'CuyZ\\Valinor\\Normalizer\\Transformer\\RecursiveTransformer' => $vendorDir . '/cuyz/valinor/src/Normalizer/Transformer/RecursiveTransformer.php',
'CuyZ\\Valinor\\Normalizer\\Transformer\\ValueTransformersHandler' => $vendorDir . '/cuyz/valinor/src/Normalizer/Transformer/ValueTransformersHandler.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingSpecificEnumCase' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingSpecificEnumCase.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\AssignedGenericNotFound' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/AssignedGenericNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\CannotAssignGeneric' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/CannotAssignGeneric.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\ExtendTagTypeError' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/ExtendTagTypeError.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\GenericClosingBracketMissing' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/GenericClosingBracketMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\GenericCommaMissing' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/GenericCommaMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\InvalidAssignedGeneric' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidAssignedGeneric.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\InvalidExtendTagClassName' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagClassName.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\InvalidExtendTagType' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\MissingGenerics' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/MissingGenerics.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\SeveralExtendTagsFound' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Generic/SeveralExtendTagsFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\InvalidIntersectionType' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/InvalidIntersectionType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\InvalidType' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/InvalidType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ArrayClosingBracketMissing' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ArrayClosingBracketMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayElementDuplicatedKey' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayElementDuplicatedKey.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayElementTypeMissing' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayElementTypeMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayEmptyElements' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayEmptyElements.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayInvalidUnsealedType' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayInvalidUnsealedType.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayUnexpectedTokenAfterSealedType' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayUnexpectedTokenAfterSealedType.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayWithoutElementsWithSealedType' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayWithoutElementsWithSealedType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\SimpleArrayClosingBracketMissing' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/SimpleArrayClosingBracketMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\MissingClosingQuoteChar' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/MissingClosingQuoteChar.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\RightIntersectionTypeMissing' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Exception/RightIntersectionTypeMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\LexingTypeParserFactory' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\AliasSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\ClassContextSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/ClassContextSpecification.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\GenericCheckerSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/GenericCheckerSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeAliasAssignerSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeAliasAssignerSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeParserSpecification' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\TypeParserFactory' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Factory/TypeParserFactory.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AdvancedClassLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AliasLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\ClassContextLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/ClassContextLexer.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\GenericCheckerParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/GenericCheckerParser.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\NativeLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/NativeLexer.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\SpecificationsLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/SpecificationsLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TokenStream' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/TokenStream.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\AdvancedClassNameToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/AdvancedClassNameToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ArrayToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ArrayToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\CallableToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/CallableToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\CaseFinder' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/CaseFinder.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ListToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ListToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\NativeToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/NativeToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\NullableToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/NullableToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ObjectToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ObjectToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\OpeningBracketToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/OpeningBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\OpeningCurlyBracketToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/OpeningCurlyBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\OpeningSquareBracketToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/OpeningSquareBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\QuoteToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/QuoteToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\Token' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/Token.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\TraversingToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/TraversingToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\TripleDotsToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/TripleDotsToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\TypeToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/TypeToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\UnionToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/UnionToken.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\UnknownSymbolToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/UnknownSymbolToken.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeAliasLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/TypeAliasLexer.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\VacantToken' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/Token/VacantToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TokensExtractor' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/TokensExtractor.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeLexer' => $vendorDir . '/cuyz/valinor/src/Type/Parser/Lexer/TypeLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\LexingParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/LexingParser.php',
- 'CuyZ\\Valinor\\Type\\Parser\\ParserSymbols' => $vendorDir . '/cuyz/valinor/src/Type/Parser/ParserSymbols.php',
'CuyZ\\Valinor\\Type\\Parser\\TypeParser' => $vendorDir . '/cuyz/valinor/src/Type/Parser/TypeParser.php',
'CuyZ\\Valinor\\Type\\ScalarType' => $vendorDir . '/cuyz/valinor/src/Type/ScalarType.php',
'CuyZ\\Valinor\\Type\\StringType' => $vendorDir . '/cuyz/valinor/src/Type/StringType.php',
'Random\\Engine\\Secure' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/Engine/Secure.php',
'Random\\RandomError' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/RandomError.php',
'Random\\RandomException' => $vendorDir . '/symfony/polyfill-php82/Resources/stubs/Random/RandomException.php',
+ 'SQLite3Exception' => $vendorDir . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
'Sabberworm\\CSS\\CSSList\\AtRuleBlockList' => $vendorDir . '/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php',
'Sabberworm\\CSS\\CSSList\\CSSBlockList' => $vendorDir . '/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php',
'Sabberworm\\CSS\\CSSList\\CSSList' => $vendorDir . '/sabberworm/php-css-parser/src/CSSList/CSSList.php',
'Sabberworm\\CSS\\OutputFormat' => $vendorDir . '/sabberworm/php-css-parser/src/OutputFormat.php',
'Sabberworm\\CSS\\OutputFormatter' => $vendorDir . '/sabberworm/php-css-parser/src/OutputFormatter.php',
'Sabberworm\\CSS\\Parser' => $vendorDir . '/sabberworm/php-css-parser/src/Parser.php',
+ 'Sabberworm\\CSS\\Parsing\\Anchor' => $vendorDir . '/sabberworm/php-css-parser/src/Parsing/Anchor.php',
'Sabberworm\\CSS\\Parsing\\OutputException' => $vendorDir . '/sabberworm/php-css-parser/src/Parsing/OutputException.php',
'Sabberworm\\CSS\\Parsing\\ParserState' => $vendorDir . '/sabberworm/php-css-parser/src/Parsing/ParserState.php',
'Sabberworm\\CSS\\Parsing\\SourceException' => $vendorDir . '/sabberworm/php-css-parser/src/Parsing/SourceException.php',
'Psr\\SimpleCache\\' => array($vendorDir . '/psr/simple-cache/src'),
'Psr\\Log\\' => array($vendorDir . '/psr/log/src'),
'Psr\\Http\\Server\\' => array($vendorDir . '/psr/http-server-handler/src', $vendorDir . '/psr/http-server-middleware/src'),
- 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-factory/src', $vendorDir . '/psr/http-message/src'),
+ 'Psr\\Http\\Message\\' => array($vendorDir . '/psr/http-message/src', $vendorDir . '/psr/http-factory/src'),
'Psr\\Http\\Client\\' => array($vendorDir . '/psr/http-client/src'),
'Psr\\EventDispatcher\\' => array($vendorDir . '/psr/event-dispatcher/src'),
'Psr\\Clock\\' => array($vendorDir . '/psr/clock/src'),
),
'Psr\\Http\\Message\\' =>
array (
- 0 => __DIR__ . '/..' . '/psr/http-factory/src',
- 1 => __DIR__ . '/..' . '/psr/http-message/src',
+ 0 => __DIR__ . '/..' . '/psr/http-message/src',
+ 1 => __DIR__ . '/..' . '/psr/http-factory/src',
),
'Psr\\Http\\Client\\' =>
array (
'Cron\\MinutesField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/MinutesField.php',
'Cron\\MonthField' => __DIR__ . '/..' . '/dragonmantank/cron-expression/src/Cron/MonthField.php',
'CuyZ\\Valinor\\Cache\\ChainCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/ChainCache.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\CacheCompiler' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Compiled/CacheCompiler.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\CompiledPhpFileCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Compiled/CompiledPhpFileCache.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\HasArguments' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Compiled/HasArguments.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\MixedValueCacheCompiler' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Compiled/MixedValueCacheCompiler.php',
- 'CuyZ\\Valinor\\Cache\\Compiled\\PhpCacheFile' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Compiled/PhpCacheFile.php',
'CuyZ\\Valinor\\Cache\\Exception\\CacheDirectoryNotWritable' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Exception/CacheDirectoryNotWritable.php',
'CuyZ\\Valinor\\Cache\\Exception\\CompiledPhpCacheFileNotWritten' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Exception/CompiledPhpCacheFileNotWritten.php',
'CuyZ\\Valinor\\Cache\\Exception\\CorruptedCompiledPhpCacheFile' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Exception/CorruptedCompiledPhpCacheFile.php',
'CuyZ\\Valinor\\Cache\\RuntimeCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/RuntimeCache.php',
'CuyZ\\Valinor\\Cache\\WarmupCache' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/WarmupCache.php',
'CuyZ\\Valinor\\Cache\\Warmup\\RecursiveCacheWarmupService' => __DIR__ . '/..' . '/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php',
+ 'CuyZ\\Valinor\\Definition\\AttributeDefinition' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/AttributeDefinition.php',
'CuyZ\\Valinor\\Definition\\Attributes' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Attributes.php',
- 'CuyZ\\Valinor\\Definition\\AttributesContainer' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/AttributesContainer.php',
'CuyZ\\Valinor\\Definition\\ClassDefinition' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/ClassDefinition.php',
'CuyZ\\Valinor\\Definition\\Exception\\ClassTypeAliasesDuplication' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/ClassTypeAliasesDuplication.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\ExtendTagTypeError' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/ExtendTagTypeError.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\InvalidExtendTagClassName' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/InvalidExtendTagClassName.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\InvalidExtendTagType' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/InvalidExtendTagType.php',
'CuyZ\\Valinor\\Definition\\Exception\\InvalidTypeAliasImportClass' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClass.php',
'CuyZ\\Valinor\\Definition\\Exception\\InvalidTypeAliasImportClassType' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClassType.php',
- 'CuyZ\\Valinor\\Definition\\Exception\\TypesDoNotMatch' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/TypesDoNotMatch.php',
+ 'CuyZ\\Valinor\\Definition\\Exception\\SeveralExtendTagsFound' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/SeveralExtendTagsFound.php',
'CuyZ\\Valinor\\Definition\\Exception\\UnknownTypeAliasImport' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Exception/UnknownTypeAliasImport.php',
'CuyZ\\Valinor\\Definition\\FunctionDefinition' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/FunctionDefinition.php',
'CuyZ\\Valinor\\Definition\\FunctionObject' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/FunctionObject.php',
'CuyZ\\Valinor\\Definition\\FunctionsContainer' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/FunctionsContainer.php',
'CuyZ\\Valinor\\Definition\\MethodDefinition' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/MethodDefinition.php',
'CuyZ\\Valinor\\Definition\\Methods' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Methods.php',
- 'CuyZ\\Valinor\\Definition\\NativeAttributes' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/NativeAttributes.php',
'CuyZ\\Valinor\\Definition\\ParameterDefinition' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/ParameterDefinition.php',
'CuyZ\\Valinor\\Definition\\Parameters' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Parameters.php',
'CuyZ\\Valinor\\Definition\\Properties' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Properties.php',
'CuyZ\\Valinor\\Definition\\Repository\\Cache\\Compiler\\TypeCompiler' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php',
'CuyZ\\Valinor\\Definition\\Repository\\ClassDefinitionRepository' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\FunctionDefinitionRepository' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/FunctionDefinitionRepository.php',
- 'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\NativeAttributesRepository' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/Reflection/NativeAttributesRepository.php',
+ 'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionAttributesRepository' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionAttributesRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionClassDefinitionRepository' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionFunctionDefinitionRepository' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php',
'CuyZ\\Valinor\\Definition\\Repository\\Reflection\\ReflectionMethodDefinitionBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php',
'CuyZ\\Valinor\\Mapper\\ArgumentsMapper' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/ArgumentsMapper.php',
'CuyZ\\Valinor\\Mapper\\ArgumentsMapperError' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/ArgumentsMapperError.php',
'CuyZ\\Valinor\\Mapper\\Exception\\InvalidMappingTypeSignature' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Exception/InvalidMappingTypeSignature.php',
+ 'CuyZ\\Valinor\\Mapper\\Exception\\TypeErrorDuringArgumentsMapping' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringArgumentsMapping.php',
+ 'CuyZ\\Valinor\\Mapper\\Exception\\TypeErrorDuringMapping' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringMapping.php',
'CuyZ\\Valinor\\Mapper\\MappingError' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/MappingError.php',
'CuyZ\\Valinor\\Mapper\\Object\\Argument' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Argument.php',
'CuyZ\\Valinor\\Mapper\\Object\\Arguments' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Arguments.php',
'CuyZ\\Valinor\\Mapper\\Object\\ArgumentsValues' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/ArgumentsValues.php',
+ 'CuyZ\\Valinor\\Mapper\\Object\\Constructor' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Constructor.php',
'CuyZ\\Valinor\\Mapper\\Object\\DateTimeFormatConstructor' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/DateTimeFormatConstructor.php',
'CuyZ\\Valinor\\Mapper\\Object\\DynamicConstructor' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/DynamicConstructor.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\CannotFindObjectBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/CannotFindObjectBuilder.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\CannotInstantiateObject' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/CannotInstantiateObject.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\CannotParseToDateTime' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/CannotParseToDateTime.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidConstructorClassTypeParameter' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorClassTypeParameter.php',
+ 'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidConstructorMethodWithAttributeReturnType' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorMethodWithAttributeReturnType.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidConstructorReturnType' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorReturnType.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\InvalidSource' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/InvalidSource.php',
'CuyZ\\Valinor\\Mapper\\Object\\Exception\\MissingConstructorClassTypeParameter' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Object/Exception/MissingConstructorClassTypeParameter.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\CasterNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/CasterNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\CasterProxyNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/CasterProxyNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ErrorCatcherNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/ErrorCatcherNodeBuilder.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\FilteredObjectNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/FilteredObjectNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\InterfaceNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\IterableNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/IterableNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ListNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/ListNodeBuilder.php',
- 'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\NativeClassNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/NativeClassNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\NodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/NodeBuilder.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\NullNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/NullNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ObjectImplementations' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/ObjectImplementations.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\ObjectNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/ObjectNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Builder\\RootNodeBuilder' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Builder/RootNodeBuilder.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\CannotInferFinalClass' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/CannotInferFinalClass.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\CannotResolveObjectType' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveObjectType.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\CannotResolveTypeFromUnion' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveTypeFromUnion.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InterfaceHasBothConstructorAndInfer' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/InterfaceHasBothConstructorAndInfer.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InvalidAbstractObjectName' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/InvalidAbstractObjectName.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InvalidListKey' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/InvalidListKey.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\InvalidNodeHasNoMappedValue' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/InvalidNodeHasNoMappedValue.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\ObjectImplementationCallbackError' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/ObjectImplementationCallbackError.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\ObjectImplementationNotRegistered' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/ObjectImplementationNotRegistered.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\ResolvedImplementationIsNotAccepted' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/ResolvedImplementationIsNotAccepted.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\SourceIsNotNull' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/SourceIsNotNull.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\SourceMustBeIterable' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/SourceMustBeIterable.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\SourceValueWasNotFilled' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/SourceValueWasNotFilled.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\TooManyResolvedTypesFromUnion' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/TooManyResolvedTypesFromUnion.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\UnexpectedArrayKeysForClass' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/UnexpectedArrayKeysForClass.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\UnexpectedShapedArrayKeys' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/UnexpectedShapedArrayKeys.php',
+ 'CuyZ\\Valinor\\Mapper\\Tree\\Exception\\UnresolvableShellType' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Exception/UnresolvableShellType.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Message\\DefaultMessage' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Message/DefaultMessage.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Message\\ErrorMessage' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Message/ErrorMessage.php',
'CuyZ\\Valinor\\Mapper\\Tree\\Message\\Formatter\\AggregateMessageFormatter' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/Tree/Message/Formatter/AggregateMessageFormatter.php',
'CuyZ\\Valinor\\Mapper\\TypeTreeMapper' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/TypeTreeMapper.php',
'CuyZ\\Valinor\\Mapper\\TypeTreeMapperError' => __DIR__ . '/..' . '/cuyz/valinor/src/Mapper/TypeTreeMapperError.php',
'CuyZ\\Valinor\\Normalizer\\ArrayNormalizer' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/ArrayNormalizer.php',
+ 'CuyZ\\Valinor\\Normalizer\\AsTransformer' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/AsTransformer.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\CircularReferenceFoundDuringNormalization' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Exception/CircularReferenceFoundDuringNormalization.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\KeyTransformerHasTooManyParameters' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Exception/KeyTransformerHasTooManyParameters.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\KeyTransformerParameterInvalidType' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Exception/KeyTransformerParameterInvalidType.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\TransformerHasTooManyParameters' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Exception/TransformerHasTooManyParameters.php',
'CuyZ\\Valinor\\Normalizer\\Exception\\TypeUnhandledByNormalizer' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Exception/TypeUnhandledByNormalizer.php',
'CuyZ\\Valinor\\Normalizer\\Format' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Format.php',
+ 'CuyZ\\Valinor\\Normalizer\\Formatter\\Exception\\CannotFormatInvalidTypeToJson' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Formatter/Exception/CannotFormatInvalidTypeToJson.php',
+ 'CuyZ\\Valinor\\Normalizer\\Formatter\\JsonFormatter' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Formatter/JsonFormatter.php',
+ 'CuyZ\\Valinor\\Normalizer\\Formatter\\StreamFormatter' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Formatter/StreamFormatter.php',
+ 'CuyZ\\Valinor\\Normalizer\\JsonNormalizer' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/JsonNormalizer.php',
'CuyZ\\Valinor\\Normalizer\\Normalizer' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Normalizer.php',
+ 'CuyZ\\Valinor\\Normalizer\\StreamNormalizer' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/StreamNormalizer.php',
'CuyZ\\Valinor\\Normalizer\\Transformer\\KeyTransformersHandler' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Transformer/KeyTransformersHandler.php',
'CuyZ\\Valinor\\Normalizer\\Transformer\\RecursiveTransformer' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Transformer/RecursiveTransformer.php',
'CuyZ\\Valinor\\Normalizer\\Transformer\\ValueTransformersHandler' => __DIR__ . '/..' . '/cuyz/valinor/src/Normalizer/Transformer/ValueTransformersHandler.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Enum\\MissingSpecificEnumCase' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Enum/MissingSpecificEnumCase.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\AssignedGenericNotFound' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/AssignedGenericNotFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\CannotAssignGeneric' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/CannotAssignGeneric.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\ExtendTagTypeError' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/ExtendTagTypeError.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\GenericClosingBracketMissing' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/GenericClosingBracketMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\GenericCommaMissing' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/GenericCommaMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\InvalidAssignedGeneric' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidAssignedGeneric.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\InvalidExtendTagClassName' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagClassName.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\InvalidExtendTagType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\MissingGenerics' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/MissingGenerics.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Generic\\SeveralExtendTagsFound' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Generic/SeveralExtendTagsFound.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\InvalidIntersectionType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/InvalidIntersectionType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\InvalidType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/InvalidType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ArrayClosingBracketMissing' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ArrayClosingBracketMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayElementDuplicatedKey' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayElementDuplicatedKey.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayElementTypeMissing' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayElementTypeMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayEmptyElements' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayEmptyElements.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayInvalidUnsealedType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayInvalidUnsealedType.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayUnexpectedTokenAfterSealedType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayUnexpectedTokenAfterSealedType.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\ShapedArrayWithoutElementsWithSealedType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayWithoutElementsWithSealedType.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\Iterable\\SimpleArrayClosingBracketMissing' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/Iterable/SimpleArrayClosingBracketMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\MissingClosingQuoteChar' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/MissingClosingQuoteChar.php',
'CuyZ\\Valinor\\Type\\Parser\\Exception\\RightIntersectionTypeMissing' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Exception/RightIntersectionTypeMissing.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\LexingTypeParserFactory' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\AliasSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\ClassContextSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/ClassContextSpecification.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\GenericCheckerSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/GenericCheckerSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeAliasAssignerSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeAliasAssignerSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\Specifications\\TypeParserSpecification' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php',
'CuyZ\\Valinor\\Type\\Parser\\Factory\\TypeParserFactory' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Factory/TypeParserFactory.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AdvancedClassLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\AliasLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\ClassContextLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/ClassContextLexer.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\GenericCheckerParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/GenericCheckerParser.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\NativeLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/NativeLexer.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\SpecificationsLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/SpecificationsLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TokenStream' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/TokenStream.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\AdvancedClassNameToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/AdvancedClassNameToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ArrayToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ArrayToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\CallableToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/CallableToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\CaseFinder' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/CaseFinder.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ListToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ListToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\NativeToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/NativeToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\NullableToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/NullableToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\ObjectToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/ObjectToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\OpeningBracketToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/OpeningBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\OpeningCurlyBracketToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/OpeningCurlyBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\OpeningSquareBracketToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/OpeningSquareBracketToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\QuoteToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/QuoteToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\Token' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/Token.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\TraversingToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/TraversingToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\TripleDotsToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/TripleDotsToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\TypeToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/TypeToken.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\UnionToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/UnionToken.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\UnknownSymbolToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/UnknownSymbolToken.php',
- 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeAliasLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/TypeAliasLexer.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\Token\\VacantToken' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/Token/VacantToken.php',
+ 'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TokensExtractor' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/TokensExtractor.php',
'CuyZ\\Valinor\\Type\\Parser\\Lexer\\TypeLexer' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/Lexer/TypeLexer.php',
'CuyZ\\Valinor\\Type\\Parser\\LexingParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/LexingParser.php',
- 'CuyZ\\Valinor\\Type\\Parser\\ParserSymbols' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/ParserSymbols.php',
'CuyZ\\Valinor\\Type\\Parser\\TypeParser' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/Parser/TypeParser.php',
'CuyZ\\Valinor\\Type\\ScalarType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/ScalarType.php',
'CuyZ\\Valinor\\Type\\StringType' => __DIR__ . '/..' . '/cuyz/valinor/src/Type/StringType.php',
'Random\\Engine\\Secure' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/Engine/Secure.php',
'Random\\RandomError' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/RandomError.php',
'Random\\RandomException' => __DIR__ . '/..' . '/symfony/polyfill-php82/Resources/stubs/Random/RandomException.php',
+ 'SQLite3Exception' => __DIR__ . '/..' . '/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php',
'Sabberworm\\CSS\\CSSList\\AtRuleBlockList' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php',
'Sabberworm\\CSS\\CSSList\\CSSBlockList' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/CSSList/CSSBlockList.php',
'Sabberworm\\CSS\\CSSList\\CSSList' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/CSSList/CSSList.php',
'Sabberworm\\CSS\\OutputFormat' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/OutputFormat.php',
'Sabberworm\\CSS\\OutputFormatter' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/OutputFormatter.php',
'Sabberworm\\CSS\\Parser' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Parser.php',
+ 'Sabberworm\\CSS\\Parsing\\Anchor' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Parsing/Anchor.php',
'Sabberworm\\CSS\\Parsing\\OutputException' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Parsing/OutputException.php',
'Sabberworm\\CSS\\Parsing\\ParserState' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Parsing/ParserState.php',
'Sabberworm\\CSS\\Parsing\\SourceException' => __DIR__ . '/..' . '/sabberworm/php-css-parser/src/Parsing/SourceException.php',
"packages": [
{
"name": "cuyz/valinor",
- "version": "1.8.2",
- "version_normalized": "1.8.2.0",
+ "version": "1.12.0",
+ "version_normalized": "1.12.0.0",
"source": {
"type": "git",
"url": "https://github.com/CuyZ/Valinor.git",
- "reference": "daf8206d11b1cb6b308ecd2eb6b65657d2248544"
+ "reference": "3bc40798a5ff64aee8a28509b73f7f84d5c66ac9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/daf8206d11b1cb6b308ecd2eb6b65657d2248544",
- "reference": "daf8206d11b1cb6b308ecd2eb6b65657d2248544",
+ "url": "https://api.github.com/repos/CuyZ/Valinor/zipball/3bc40798a5ff64aee8a28509b73f7f84d5c66ac9",
+ "reference": "3bc40798a5ff64aee8a28509b73f7f84d5c66ac9",
"shasum": ""
},
"require": {
"composer-runtime-api": "^2.0",
- "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
"friendsofphp/php-cs-fixer": "^3.4",
- "infection/infection": "^0.26",
+ "infection/infection": "^0.27",
"marcocesarato/php-conventional-changelog": "^1.12",
"mikey179/vfsstream": "^1.6.10",
"phpstan/phpstan": "^1.3",
"phpstan/phpstan-phpunit": "^1.0",
"phpstan/phpstan-strict-rules": "^1.0",
- "phpunit/phpunit": "^9.5",
- "rector/rector": "~0.17.0",
+ "phpunit/phpunit": "^10.5",
+ "rector/rector": "^1.0",
"vimeo/psalm": "^5.0"
},
- "time": "2024-01-08T20:31:48+00:00",
+ "time": "2024-04-04T16:42:55+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
],
"support": {
"issues": "https://github.com/CuyZ/Valinor/issues",
- "source": "https://github.com/CuyZ/Valinor/tree/1.8.2"
+ "source": "https://github.com/CuyZ/Valinor/tree/1.12.0"
},
"funding": [
{
},
{
"name": "laminas/laminas-diactoros",
- "version": "3.3.0",
- "version_normalized": "3.3.0.0",
+ "version": "3.3.1",
+ "version_normalized": "3.3.1.0",
"source": {
"type": "git",
"url": "https://github.com/laminas/laminas-diactoros.git",
- "reference": "4db52734837c60259c9b2d7caf08eef8f7f9b9ac"
+ "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/4db52734837c60259c9b2d7caf08eef8f7f9b9ac",
- "reference": "4db52734837c60259c9b2d7caf08eef8f7f9b9ac",
+ "url": "https://api.github.com/repos/laminas/laminas-diactoros/zipball/74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45",
+ "reference": "74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45",
"shasum": ""
},
"require": {
"http-interop/http-factory-tests": "^0.9.0",
"laminas/laminas-coding-standard": "~2.5.0",
"php-http/psr7-integration-tests": "^1.3",
- "phpunit/phpunit": "^9.5.28",
+ "phpunit/phpunit": "^9.6.16",
"psalm/plugin-phpunit": "^0.18.4",
- "vimeo/psalm": "^5.15.0"
+ "vimeo/psalm": "^5.22.1"
},
- "time": "2023-10-26T11:01:07+00:00",
+ "time": "2024-02-16T16:06:16+00:00",
"type": "library",
"extra": {
"laminas": {
},
{
"name": "paragonie/constant_time_encoding",
- "version": "v2.6.3",
- "version_normalized": "2.6.3.0",
+ "version": "v2.7.0",
+ "version_normalized": "2.7.0.0",
"source": {
"type": "git",
"url": "https://github.com/paragonie/constant_time_encoding.git",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938"
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/58c3f47f650c94ec05a151692652a868995d2938",
- "reference": "58c3f47f650c94ec05a151692652a868995d2938",
+ "url": "https://api.github.com/repos/paragonie/constant_time_encoding/zipball/52a0d99e69f56b9ec27ace92ba56897fe6993105",
+ "reference": "52a0d99e69f56b9ec27ace92ba56897fe6993105",
"shasum": ""
},
"require": {
"phpunit/phpunit": "^6|^7|^8|^9",
"vimeo/psalm": "^1|^2|^3|^4"
},
- "time": "2022-06-14T06:56:20+00:00",
+ "time": "2024-05-08T12:18:48+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
},
{
"name": "psr/http-factory",
- "version": "1.0.2",
- "version_normalized": "1.0.2.0",
+ "version": "1.1.0",
+ "version_normalized": "1.1.0.0",
"source": {
"type": "git",
"url": "https://github.com/php-fig/http-factory.git",
- "reference": "e616d01114759c4c489f93b099585439f795fe35"
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/php-fig/http-factory/zipball/e616d01114759c4c489f93b099585439f795fe35",
- "reference": "e616d01114759c4c489f93b099585439f795fe35",
+ "url": "https://api.github.com/repos/php-fig/http-factory/zipball/2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
+ "reference": "2b4765fddfe3b508ac62f829e852b1501d3f6e8a",
"shasum": ""
},
"require": {
- "php": ">=7.0.0",
+ "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
- "time": "2023-04-10T20:10:41+00:00",
+ "time": "2024-04-15T12:06:14+00:00",
"type": "library",
"extra": {
"branch-alias": {
"homepage": "https://www.php-fig.org/"
}
],
- "description": "Common interfaces for PSR-7 HTTP message factories",
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"factory",
"http",
"response"
],
"support": {
- "source": "https://github.com/php-fig/http-factory/tree/1.0.2"
+ "source": "https://github.com/php-fig/http-factory"
},
"install-path": "../psr/http-factory"
},
},
{
"name": "sabberworm/php-css-parser",
- "version": "8.4.0",
- "version_normalized": "8.4.0.0",
+ "version": "v8.5.1",
+ "version_normalized": "8.5.1.0",
"source": {
"type": "git",
- "url": "https://github.com/sabberworm/PHP-CSS-Parser.git",
- "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30"
+ "url": "https://github.com/MyIntervals/PHP-CSS-Parser.git",
+ "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sabberworm/PHP-CSS-Parser/zipball/e41d2140031d533348b2192a83f02d8dd8a71d30",
- "reference": "e41d2140031d533348b2192a83f02d8dd8a71d30",
+ "url": "https://api.github.com/repos/MyIntervals/PHP-CSS-Parser/zipball/4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
+ "reference": "4a3d572b0f8b28bb6fd016ae8bbfc445facef152",
"shasum": ""
},
"require": {
"php": ">=5.6.20"
},
"require-dev": {
- "codacy/coverage": "^1.4",
- "phpunit/phpunit": "^4.8.36"
+ "phpunit/phpunit": "^5.7.27"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
},
- "time": "2021-12-11T13:40:54+00:00",
+ "time": "2024-02-15T16:41:13+00:00",
"type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-main": "9.0.x-dev"
+ }
+ },
"installation-source": "dist",
"autoload": {
"psr-4": {
"authors": [
{
"name": "Raphael Schweikert"
+ },
+ {
+ "name": "Oliver Klee",
+ "email": "github@oliverklee.de"
+ },
+ {
+ "name": "Jake Hotson",
+ "email": "jake.github@qzdesign.co.uk"
}
],
"description": "Parser for CSS Files written in PHP",
"stylesheet"
],
"support": {
- "issues": "https://github.com/sabberworm/PHP-CSS-Parser/issues",
- "source": "https://github.com/sabberworm/PHP-CSS-Parser/tree/8.4.0"
+ "issues": "https://github.com/MyIntervals/PHP-CSS-Parser/issues",
+ "source": "https://github.com/MyIntervals/PHP-CSS-Parser/tree/v8.5.1"
},
"install-path": "../sabberworm/php-css-parser"
},
},
{
"name": "sebastian/diff",
- "version": "5.1.0",
- "version_normalized": "5.1.0.0",
+ "version": "5.1.1",
+ "version_normalized": "5.1.1.0",
"source": {
"type": "git",
"url": "https://github.com/sebastianbergmann/diff.git",
- "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f"
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
- "reference": "fbf413a49e54f6b9b17e12d900ac7f6101591b7f",
+ "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/c41e007b4b62af48218231d6c2275e4c9b975b2e",
+ "reference": "c41e007b4b62af48218231d6c2275e4c9b975b2e",
"shasum": ""
},
"require": {
},
"require-dev": {
"phpunit/phpunit": "^10.0",
- "symfony/process": "^4.2 || ^5"
+ "symfony/process": "^6.4"
},
- "time": "2023-12-22T10:55:06+00:00",
+ "time": "2024-03-02T07:15:17+00:00",
"type": "library",
"extra": {
"branch-alias": {
"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.0"
+ "source": "https://github.com/sebastianbergmann/diff/tree/5.1.1"
},
"funding": [
{
},
{
"name": "symfony/css-selector",
- "version": "v6.4.0",
- "version_normalized": "6.4.0.0",
+ "version": "v6.4.8",
+ "version_normalized": "6.4.8.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/css-selector.git",
- "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4"
+ "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/css-selector/zipball/d036c6c0d0b09e24a14a35f8292146a658f986e4",
- "reference": "d036c6c0d0b09e24a14a35f8292146a658f986e4",
+ "url": "https://api.github.com/repos/symfony/css-selector/zipball/4b61b02fe15db48e3687ce1c45ea385d1780fe08",
+ "reference": "4b61b02fe15db48e3687ce1c45ea385d1780fe08",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
- "time": "2023-10-31T08:40:20+00:00",
+ "time": "2024-05-31T14:49:08+00:00",
"type": "library",
"installation-source": "dist",
"autoload": {
"description": "Converts CSS selectors to XPath expressions",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/css-selector/tree/v6.4.0"
+ "source": "https://github.com/symfony/css-selector/tree/v6.4.8"
},
"funding": [
{
},
{
"name": "symfony/deprecation-contracts",
- "version": "v3.4.0",
- "version_normalized": "3.4.0.0",
+ "version": "v3.5.0",
+ "version_normalized": "3.5.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/deprecation-contracts.git",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf"
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/7c3aff79d10325257a001fcf92d991f24fc967cf",
- "reference": "7c3aff79d10325257a001fcf92d991f24fc967cf",
+ "url": "https://api.github.com/repos/symfony/deprecation-contracts/zipball/0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
+ "reference": "0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1",
"shasum": ""
},
"require": {
"php": ">=8.1"
},
- "time": "2023-05-23T14:45:45+00:00",
+ "time": "2024-04-18T09:32:20+00:00",
"type": "library",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
"description": "A generic function and convention to trigger deprecation notices",
"homepage": "https://symfony.com",
"support": {
- "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0"
+ "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0"
},
"funding": [
{
},
{
"name": "symfony/polyfill-php82",
- "version": "v1.28.0",
- "version_normalized": "1.28.0.0",
+ "version": "v1.30.0",
+ "version_normalized": "1.30.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php82.git",
- "reference": "7716bea9c86776fb3362d6b52fe1fc9471056a49"
+ "reference": "77ff49780f56906788a88974867ed68bc49fae5b"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/7716bea9c86776fb3362d6b52fe1fc9471056a49",
- "reference": "7716bea9c86776fb3362d6b52fe1fc9471056a49",
+ "url": "https://api.github.com/repos/symfony/polyfill-php82/zipball/77ff49780f56906788a88974867ed68bc49fae5b",
+ "reference": "77ff49780f56906788a88974867ed68bc49fae5b",
"shasum": ""
},
"require": {
"php": ">=7.1"
},
- "time": "2023-08-25T17:27:25+00:00",
+ "time": "2024-06-19T12:30:46+00:00",
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php82/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-php82/tree/v1.30.0"
},
"funding": [
{
},
{
"name": "symfony/polyfill-php83",
- "version": "v1.28.0",
- "version_normalized": "1.28.0.0",
+ "version": "v1.30.0",
+ "version_normalized": "1.30.0.0",
"source": {
"type": "git",
"url": "https://github.com/symfony/polyfill-php83.git",
- "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11"
+ "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9"
},
"dist": {
"type": "zip",
- "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11",
- "reference": "b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11",
+ "url": "https://api.github.com/repos/symfony/polyfill-php83/zipball/dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
+ "reference": "dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9",
"shasum": ""
},
"require": {
- "php": ">=7.1",
- "symfony/polyfill-php80": "^1.14"
+ "php": ">=7.1"
},
- "time": "2023-08-16T06:22:46+00:00",
+ "time": "2024-06-19T12:35:24+00:00",
"type": "library",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
"shim"
],
"support": {
- "source": "https://github.com/symfony/polyfill-php83/tree/v1.28.0"
+ "source": "https://github.com/symfony/polyfill-php83/tree/v1.30.0"
},
"funding": [
{
'name' => '__root__',
'pretty_version' => '6.0.x-dev',
'version' => '6.0.9999999.9999999-dev',
- 'reference' => '06074fe9cfc6d00fc36fd1b72cafa582b73ac5d8',
+ 'reference' => '284adc7603f0e4cc0733ad4011c123c12ffa6e3a',
'type' => 'project',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
'__root__' => array(
'pretty_version' => '6.0.x-dev',
'version' => '6.0.9999999.9999999-dev',
- 'reference' => '06074fe9cfc6d00fc36fd1b72cafa582b73ac5d8',
+ 'reference' => '284adc7603f0e4cc0733ad4011c123c12ffa6e3a',
'type' => 'project',
'install_path' => __DIR__ . '/../',
'aliases' => array(),
'dev_requirement' => false,
),
'cuyz/valinor' => array(
- 'pretty_version' => '1.8.2',
- 'version' => '1.8.2.0',
- 'reference' => 'daf8206d11b1cb6b308ecd2eb6b65657d2248544',
+ 'pretty_version' => '1.12.0',
+ 'version' => '1.12.0.0',
+ 'reference' => '3bc40798a5ff64aee8a28509b73f7f84d5c66ac9',
'type' => 'library',
'install_path' => __DIR__ . '/../cuyz/valinor',
'aliases' => array(),
'dev_requirement' => false,
),
'laminas/laminas-diactoros' => array(
- 'pretty_version' => '3.3.0',
- 'version' => '3.3.0.0',
- 'reference' => '4db52734837c60259c9b2d7caf08eef8f7f9b9ac',
+ 'pretty_version' => '3.3.1',
+ 'version' => '3.3.1.0',
+ 'reference' => '74cfb9a7522ffd2a161d1ebe10db2fc2abb9df45',
'type' => 'library',
'install_path' => __DIR__ . '/../laminas/laminas-diactoros',
'aliases' => array(),
),
),
'paragonie/constant_time_encoding' => array(
- 'pretty_version' => 'v2.6.3',
- 'version' => '2.6.3.0',
- 'reference' => '58c3f47f650c94ec05a151692652a868995d2938',
+ 'pretty_version' => 'v2.7.0',
+ 'version' => '2.7.0.0',
+ 'reference' => '52a0d99e69f56b9ec27ace92ba56897fe6993105',
'type' => 'library',
'install_path' => __DIR__ . '/../paragonie/constant_time_encoding',
'aliases' => array(),
),
),
'psr/http-factory' => array(
- 'pretty_version' => '1.0.2',
- 'version' => '1.0.2.0',
- 'reference' => 'e616d01114759c4c489f93b099585439f795fe35',
+ 'pretty_version' => '1.1.0',
+ 'version' => '1.1.0.0',
+ 'reference' => '2b4765fddfe3b508ac62f829e852b1501d3f6e8a',
'type' => 'library',
'install_path' => __DIR__ . '/../psr/http-factory',
'aliases' => array(),
'dev_requirement' => false,
),
'sabberworm/php-css-parser' => array(
- 'pretty_version' => '8.4.0',
- 'version' => '8.4.0.0',
- 'reference' => 'e41d2140031d533348b2192a83f02d8dd8a71d30',
+ 'pretty_version' => 'v8.5.1',
+ 'version' => '8.5.1.0',
+ 'reference' => '4a3d572b0f8b28bb6fd016ae8bbfc445facef152',
'type' => 'library',
'install_path' => __DIR__ . '/../sabberworm/php-css-parser',
'aliases' => array(),
'dev_requirement' => false,
),
'sebastian/diff' => array(
- 'pretty_version' => '5.1.0',
- 'version' => '5.1.0.0',
- 'reference' => 'fbf413a49e54f6b9b17e12d900ac7f6101591b7f',
+ 'pretty_version' => '5.1.1',
+ 'version' => '5.1.1.0',
+ 'reference' => 'c41e007b4b62af48218231d6c2275e4c9b975b2e',
'type' => 'library',
'install_path' => __DIR__ . '/../sebastian/diff',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/css-selector' => array(
- 'pretty_version' => 'v6.4.0',
- 'version' => '6.4.0.0',
- 'reference' => 'd036c6c0d0b09e24a14a35f8292146a658f986e4',
+ 'pretty_version' => 'v6.4.8',
+ 'version' => '6.4.8.0',
+ 'reference' => '4b61b02fe15db48e3687ce1c45ea385d1780fe08',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/css-selector',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/deprecation-contracts' => array(
- 'pretty_version' => 'v3.4.0',
- 'version' => '3.4.0.0',
- 'reference' => '7c3aff79d10325257a001fcf92d991f24fc967cf',
+ 'pretty_version' => 'v3.5.0',
+ 'version' => '3.5.0.0',
+ 'reference' => '0e0d29ce1f20deffb4ab1b016a7257c4f1e789a1',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/deprecation-contracts',
'aliases' => array(),
),
),
'symfony/polyfill-php82' => array(
- 'pretty_version' => 'v1.28.0',
- 'version' => '1.28.0.0',
- 'reference' => '7716bea9c86776fb3362d6b52fe1fc9471056a49',
+ 'pretty_version' => 'v1.30.0',
+ 'version' => '1.30.0.0',
+ 'reference' => '77ff49780f56906788a88974867ed68bc49fae5b',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php82',
'aliases' => array(),
'dev_requirement' => false,
),
'symfony/polyfill-php83' => array(
- 'pretty_version' => 'v1.28.0',
- 'version' => '1.28.0.0',
- 'reference' => 'b0f46ebbeeeda3e9d2faebdfbf4b4eae9b59fa11',
+ 'pretty_version' => 'v1.30.0',
+ 'version' => '1.30.0.0',
+ 'reference' => 'dbdcdf1a4dcc2743591f1079d0c35ab1e2dcbbc9',
'type' => 'library',
'install_path' => __DIR__ . '/../symfony/polyfill-php83',
'aliases' => array(),
}
],
"require": {
- "php": "~8.0.0 || ~8.1.0 || ~8.2.0 || ~8.3.0",
+ "php": "~8.1.0 || ~8.2.0 || ~8.3.0",
"composer-runtime-api": "^2.0",
"psr/simple-cache": "^1.0 || ^2.0 || ^3.0"
},
"require-dev": {
- "phpunit/phpunit": "^9.5",
- "infection/infection": "^0.26",
+ "phpunit/phpunit": "^10.5",
+ "infection/infection": "^0.27",
"phpstan/phpstan": "^1.3",
"phpstan/phpstan-strict-rules": "^1.0",
"phpstan/phpstan-phpunit": "^1.0",
"marcocesarato/php-conventional-changelog": "^1.12",
"vimeo/psalm": "^5.0",
"mikey179/vfsstream": "^1.6.10",
- "rector/rector": "~0.17.0"
+ "rector/rector": "^1.0"
},
"autoload": {
"psr-4": {
"rector"
],
"mutation": [
- "@putenv XDEBUG_MODE=off",
"infection --threads=max --git-diff-lines"
],
"doc": [
use PHPStan\Rules\Rule;
use PHPStan\Rules\RuleErrorBuilder;
+use function str_contains;
use function str_starts_with;
/**
return [];
}
- if (str_starts_with($reflection->getName(), 'CuyZ\Valinor\Tests')
- || str_starts_with($reflection->getName(), 'SimpleNamespace')
- ) {
+ if (str_contains($reflection->getFileName() ?? '', '/tests/')) {
return [];
}
use PhpParser\Node\Expr\MethodCall;
use PHPStan\Analyser\Scope;
use PHPStan\PhpDoc\TypeStringResolver;
+use PHPStan\PhpDocParser\Parser\ParserException;
use PHPStan\Reflection\MethodReflection;
use PHPStan\Type\ClassStringType;
use PHPStan\Type\Constant\ConstantStringType;
return $type->traverse(fn (Type $type) => $this->type($type));
}
- return $this->type($type);
+ try {
+ return $this->type($type);
+ } catch (ParserException) {
+ // Fallback to `mixed` type if the type cannot be resolved. This can
+ // occur with a type that is not understood/supported by PHPStan. If
+ // that happens, returning a mixed type is the safest option, as it
+ // will not make the analysis fail.
+ return new MixedType();
+ }
}
private function type(Type $type): Type
return null;
}
- if (empty($type->params ?? [])) {
+ $typeParams = $type->params ?? [];
+
+ if ($typeParams === []) {
return null;
}
$params = [];
- foreach ($type->params as $param) {
+ foreach ($typeParams as $param) {
$params[$param->name] = $param->type ?? new Union([new TMixed()]);
}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Cache\Compiled;
-
-/** @internal */
-interface CacheCompiler
-{
- public function compile(mixed $value): string;
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Cache\Compiled;
-
-use CuyZ\Valinor\Cache\Exception\CacheDirectoryNotWritable;
-use CuyZ\Valinor\Cache\Exception\CompiledPhpCacheFileNotWritten;
-use CuyZ\Valinor\Cache\Exception\CorruptedCompiledPhpCacheFile;
-use CuyZ\Valinor\Cache\WarmupCache;
-use DateInterval;
-use DateTime;
-use Error;
-use FilesystemIterator;
-use Traversable;
-
-use function bin2hex;
-use function file_exists;
-use function file_put_contents;
-use function is_dir;
-use function mkdir;
-use function random_bytes;
-use function rename;
-use function sha1;
-use function str_contains;
-use function time;
-use function unlink;
-
-/**
- * @internal
- *
- * @template EntryType
- * @implements WarmupCache<EntryType>
- */
-final class CompiledPhpFileCache implements WarmupCache
-{
- private const TEMPORARY_DIR_PERMISSION = 510;
-
- private const GENERATED_MESSAGE = 'Generated by ' . self::class;
-
- /** @var array<PhpCacheFile<EntryType>> */
- private array $files = [];
-
- public function __construct(
- private string $cacheDir,
- private CacheCompiler $compiler
- ) {}
-
- public function warmup(): void
- {
- $this->createTemporaryDir();
- }
-
- public function has($key): bool
- {
- $filename = $this->path($key);
-
- if (! file_exists($filename)) {
- return false;
- }
-
- return $this->getFile($filename)->isValid();
- }
-
- public function get($key, $default = null): mixed
- {
- if (! $this->has($key)) {
- return $default;
- }
-
- $filename = $this->path($key);
-
- return $this->getFile($filename)->value();
- }
-
- public function set($key, $value, $ttl = null): bool
- {
- $filename = $this->path($key);
-
- $code = $this->compile($value, $ttl);
-
- $tmpDir = $this->createTemporaryDir();
-
- /** @infection-ignore-all */
- $tmpFilename = $tmpDir . DIRECTORY_SEPARATOR . bin2hex(random_bytes(16));
-
- try {
- if (! @file_put_contents($tmpFilename, $code)) {
- throw new CompiledPhpCacheFileNotWritten($tmpFilename);
- }
-
- if (! file_exists($filename) && ! @rename($tmpFilename, $filename)) {
- throw new CompiledPhpCacheFileNotWritten($filename);
- }
- } finally {
- if (file_exists($tmpFilename)) {
- unlink($tmpFilename);
- }
- }
-
- return true;
- }
-
- public function delete($key): bool
- {
- $filename = $this->path($key);
-
- if (file_exists($filename)) {
- return @unlink($filename);
- }
-
- return true;
- }
-
- public function clear(): bool
- {
- if (! is_dir($this->cacheDir)) {
- return true;
- }
-
- $success = true;
-
- /** @var FilesystemIterator $file */
- foreach (new FilesystemIterator($this->cacheDir) as $file) {
- if (! $file->isFile()) {
- continue;
- }
-
- $line = $file->openFile()->getCurrentLine();
-
- if (! $line || ! str_contains($line, self::GENERATED_MESSAGE)) {
- continue;
- }
-
- $success = @unlink($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
- }
-
- return $success;
- }
-
- /**
- * @return Traversable<string, EntryType|null>
- */
- public function getMultiple($keys, $default = null): Traversable
- {
- foreach ($keys as $key) {
- yield $key => $this->get($key, $default);
- }
- }
-
- public function setMultiple($values, $ttl = null): bool
- {
- foreach ($values as $key => $value) {
- $this->set($key, $value, $ttl);
- }
-
- return true;
- }
-
- public function deleteMultiple($keys): bool
- {
- $deleted = true;
-
- foreach ($keys as $key) {
- $deleted = $this->delete($key) && $deleted;
- }
-
- return $deleted;
- }
-
- private function compile(mixed $value, int|DateInterval|null $ttl = null): string
- {
- $validationCode = 'true';
-
- if ($ttl) {
- $time = $ttl instanceof DateInterval
- ? (new DateTime())->add($ttl)->getTimestamp()
- : time() + $ttl;
-
- $validationCode = "time() < $time";
- }
-
- $generatedMessage = self::GENERATED_MESSAGE;
-
- $code = $this->compiler->compile($value);
-
- return <<<PHP
- <?php // $generatedMessage
- return new class(\$this->compiler instanceof \CuyZ\Valinor\Cache\Compiled\HasArguments ? \$this->compiler->arguments() : []) implements \CuyZ\Valinor\Cache\Compiled\PhpCacheFile {
- /** @var array<string, mixed> */
- private array \$arguments;
-
- public function __construct(array \$arguments)
- {
- \$this->arguments = \$arguments;
- }
-
- public function value()
- {
- return $code;
- }
-
- public function isValid(): bool
- {
- return $validationCode;
- }
- };
- PHP;
- }
-
- /**
- * @return PhpCacheFile<EntryType>
- */
- private function getFile(string $filename): PhpCacheFile
- {
- if (! isset($this->files[$filename])) {
- try {
- $object = include $filename;
- } catch (Error) {
- }
-
- if (! isset($object) || ! $object instanceof PhpCacheFile) {
- throw new CorruptedCompiledPhpCacheFile($filename);
- }
-
- $this->files[$filename] = $object;
- }
-
- return $this->files[$filename];
- }
-
- private function createTemporaryDir(): string
- {
- $tmpDir = $this->cacheDir . DIRECTORY_SEPARATOR . '.valinor.tmp';
-
- if (! is_dir($tmpDir) && ! @mkdir($tmpDir, self::TEMPORARY_DIR_PERMISSION, true)) {
- throw new CacheDirectoryNotWritable($this->cacheDir);
- }
-
- return $tmpDir;
- }
-
- private function path(string $key): string
- {
- /** @infection-ignore-all */
- return $this->cacheDir . DIRECTORY_SEPARATOR . sha1($key) . '.php';
- }
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Cache\Compiled;
-
-/** @internal */
-interface HasArguments extends CacheCompiler
-{
- /**
- * @return array<string, mixed>
- */
- public function arguments(): array;
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Cache\Compiled;
-
-use function var_export;
-
-/** @internal */
-final class MixedValueCacheCompiler implements CacheCompiler
-{
- public function compile(mixed $value): string
- {
- return var_export($value, true);
- }
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Cache\Compiled;
-
-/**
- * @internal
- *
- * @template ValueType
- */
-interface PhpCacheFile
-{
- /**
- * @return ValueType
- */
- public function value();
-
- public function isValid(): bool;
-}
namespace CuyZ\Valinor\Cache;
-use CuyZ\Valinor\Cache\Compiled\CompiledPhpFileCache;
-use CuyZ\Valinor\Cache\Compiled\MixedValueCacheCompiler;
+use CuyZ\Valinor\Cache\Exception\CacheDirectoryNotWritable;
+use CuyZ\Valinor\Cache\Exception\CompiledPhpCacheFileNotWritten;
+use CuyZ\Valinor\Cache\Exception\CorruptedCompiledPhpCacheFile;
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\ClassDefinitionCompiler;
use CuyZ\Valinor\Definition\Repository\Cache\Compiler\FunctionDefinitionCompiler;
-use Psr\SimpleCache\CacheInterface;
+use Error;
+use FilesystemIterator;
use Traversable;
-use function is_object;
-use function sys_get_temp_dir;
+use function bin2hex;
+use function file_exists;
+use function file_put_contents;
+use function is_dir;
+use function mkdir;
+use function random_bytes;
+use function rename;
+use function rmdir;
+use function str_contains;
+use function unlink;
+use function var_export;
/**
* @api
*/
final class FileSystemCache implements WarmupCache
{
- /** @var array<string, CacheInterface<EntryType>> */
- private array $delegates;
+ private const TEMPORARY_DIR_PERMISSION = 510;
- public function __construct(string $cacheDir = null)
+ private const GENERATED_MESSAGE = 'Generated by ' . self::class;
+
+ private string $cacheDir;
+
+ private ClassDefinitionCompiler $classDefinitionCompiler;
+
+ private FunctionDefinitionCompiler $functionDefinitionCompiler;
+
+ public function __construct(string $cacheDir)
{
- $cacheDir ??= sys_get_temp_dir();
-
- // @infection-ignore-all
- $this->delegates = [
- '*' => new CompiledPhpFileCache($cacheDir . DIRECTORY_SEPARATOR . 'mixed', new MixedValueCacheCompiler()),
- ClassDefinition::class => new CompiledPhpFileCache($cacheDir . DIRECTORY_SEPARATOR . 'classes', new ClassDefinitionCompiler()),
- FunctionDefinition::class => new CompiledPhpFileCache($cacheDir . DIRECTORY_SEPARATOR . 'functions', new FunctionDefinitionCompiler()),
- ];
+ $this->cacheDir = $cacheDir;
+ $this->classDefinitionCompiler = new ClassDefinitionCompiler();
+ $this->functionDefinitionCompiler = new FunctionDefinitionCompiler();
}
public function warmup(): void
{
- foreach ($this->delegates as $delegate) {
- if ($delegate instanceof WarmupCache) {
- $delegate->warmup();
- }
- }
+ $this->createTemporaryDir();
}
public function has($key): bool
{
- foreach ($this->delegates as $delegate) {
- if ($delegate->has($key)) {
- return true;
- }
- }
+ $filename = $this->path($key);
- return false;
+ return file_exists($filename);
}
public function get($key, $default = null): mixed
{
- foreach ($this->delegates as $delegate) {
- if ($delegate->has($key)) {
- return $delegate->get($key, $default);
- }
+ $filename = $this->path($key);
+
+ if (! file_exists($filename)) {
+ return $default;
}
- return $default;
+ try {
+ return include $filename;
+ } catch (Error) {
+ throw new CorruptedCompiledPhpCacheFile($filename);
+ }
}
public function set($key, $value, $ttl = null): bool
{
- $delegate = $this->delegates['*'];
+ $filename = $this->path($key);
- if (is_object($value) && isset($this->delegates[$value::class])) {
- $delegate = $this->delegates[$value::class];
+ $code = $this->compile($value);
+
+ $tmpDir = $this->createTemporaryDir();
+
+ /** @infection-ignore-all */
+ $tmpFilename = $tmpDir . DIRECTORY_SEPARATOR . bin2hex(random_bytes(16));
+
+ try {
+ if (! @file_put_contents($tmpFilename, $code)) {
+ throw new CompiledPhpCacheFileNotWritten($tmpFilename);
+ }
+
+ if (! file_exists($filename) && ! @rename($tmpFilename, $filename)) {
+ throw new CompiledPhpCacheFileNotWritten($filename);
+ }
+ } finally {
+ if (file_exists($tmpFilename)) {
+ unlink($tmpFilename);
+ }
}
- return $delegate->set($key, $value, $ttl);
+ return true;
}
public function delete($key): bool
{
- $deleted = true;
+ $filename = $this->path($key);
- foreach ($this->delegates as $delegate) {
- $deleted = $delegate->delete($key) && $deleted;
+ if (file_exists($filename)) {
+ return @unlink($filename);
}
- return $deleted;
+ return true;
}
public function clear(): bool
{
- $cleared = true;
+ if (! is_dir($this->cacheDir)) {
+ return true;
+ }
+
+ $success = true;
+ $shouldDeleteRootDir = true;
- foreach ($this->delegates as $delegate) {
- $cleared = $delegate->clear() && $cleared;
+ /** @var FilesystemIterator $file */
+ foreach (new FilesystemIterator($this->cacheDir) as $file) {
+ if ($file->getFilename() === '.valinor.tmp') {
+ $success = @rmdir($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
+ continue;
+ }
+
+ if (! $file->isFile()) {
+ $shouldDeleteRootDir = false;
+ continue;
+ }
+
+ $line = $file->openFile()->getCurrentLine();
+
+ if (! $line || ! str_contains($line, self::GENERATED_MESSAGE)) {
+ $shouldDeleteRootDir = false;
+ continue;
+ }
+
+ $success = @unlink($this->cacheDir . DIRECTORY_SEPARATOR . $file->getFilename()) && $success;
+ }
+
+ if ($shouldDeleteRootDir) {
+ $success = @rmdir($this->cacheDir) && $success;
}
- return $cleared;
+ return $success;
}
/**
public function setMultiple($values, $ttl = null): bool
{
- $set = true;
-
foreach ($values as $key => $value) {
- $set = $this->set($key, $value, $ttl) && $set;
+ $this->set($key, $value, $ttl);
}
- return $set;
+ return true;
}
public function deleteMultiple($keys): bool
return $deleted;
}
+
+ private function compile(mixed $value): string
+ {
+ $generatedMessage = self::GENERATED_MESSAGE;
+
+ $code = match (true) {
+ $value instanceof ClassDefinition => $this->classDefinitionCompiler->compile($value),
+ $value instanceof FunctionDefinition => $this->functionDefinitionCompiler->compile($value),
+ default => var_export($value, true),
+ };
+
+ return <<<PHP
+ <?php // $generatedMessage
+ return $code;
+ PHP;
+ }
+
+ private function createTemporaryDir(): string
+ {
+ $tmpDir = $this->cacheDir . DIRECTORY_SEPARATOR . '.valinor.tmp';
+
+ if (! is_dir($tmpDir) && ! @mkdir($tmpDir, self::TEMPORARY_DIR_PERMISSION, true)) {
+ throw new CacheDirectoryNotWritable($this->cacheDir);
+ }
+
+ return $tmpDir;
+ }
+
+ private function path(string $key): string
+ {
+ /** @infection-ignore-all */
+ return $this->cacheDir . DIRECTORY_SEPARATOR . $key . '.php';
+ }
}
$fileNames = [];
if ($value instanceof ClassDefinition) {
- $reflection = Reflection::class($value->name());
+ $reflection = Reflection::class($value->name);
do {
$fileNames[] = $reflection->getFileName();
}
if ($value instanceof FunctionDefinition) {
- $fileNames[] = $value->fileName();
+ $fileNames[] = $value->fileName;
}
foreach ($fileNames as $fileName) {
// 2. The key is sha1'd so that it does not contain illegal characters.
// @see https://www.php-fig.org/psr/psr-16/#12-definitions
// @infection-ignore-all
- $this->sanitize = static fn (string $key) => sha1("$key." . self::$version ??= PHP_VERSION . '/' . Package::version());
+ $this->sanitize = static fn (string $key) => $key . sha1(self::$version ??= PHP_VERSION . '/' . Package::version());
}
public function warmup(): void
$function = $this->implementations->function($interfaceName);
- $this->warmupType($function->returnType());
+ $this->warmupType($function->returnType);
- foreach ($function->parameters() as $parameter) {
- $this->warmupType($parameter->type());
+ foreach ($function->parameters as $parameter) {
+ $this->warmupType($parameter->type);
}
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Definition;
+
+/** @internal */
+final class AttributeDefinition
+{
+ public function __construct(
+ public readonly ClassDefinition $class,
+ /** @var list<mixed> */
+ public readonly array $arguments,
+ ) {}
+
+ public function instantiate(): object
+ {
+ return new ($this->class->type->className())(...$this->arguments);
+ }
+}
use Countable;
use IteratorAggregate;
+use Traversable;
+
+use function array_filter;
+use function count;
+use function is_a;
/**
* @internal
*
- * @extends IteratorAggregate<object>
+ * @implements IteratorAggregate<AttributeDefinition>
*/
-interface Attributes extends IteratorAggregate, Countable
+final class Attributes implements IteratorAggregate, Countable
{
+ private static self $empty;
+
+ /** @var list<AttributeDefinition> */
+ private array $attributes;
+
+ /**
+ * @no-named-arguments
+ */
+ public function __construct(AttributeDefinition ...$attributes)
+ {
+ $this->attributes = $attributes;
+ }
+
+ public static function empty(): self
+ {
+ return self::$empty ??= new self();
+ }
+
+ public function has(string $className): bool
+ {
+ foreach ($this->attributes as $attribute) {
+ if (is_a($attribute->class->type->className(), $className, true)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ /**
+ * @param callable(AttributeDefinition): bool $callback
+ */
+ public function filter(callable $callback): self
+ {
+ return new self(
+ ...array_filter($this->attributes, $callback)
+ );
+ }
+
+ public function count(): int
+ {
+ return count($this->attributes);
+ }
+
/**
- * @param class-string $className
+ * @return list<AttributeDefinition>
*/
- public function has(string $className): bool;
+ public function toArray(): array
+ {
+ return $this->attributes;
+ }
/**
- * @template T of object
- *
- * @param class-string<T> $className
- * @return list<T>
+ * @return Traversable<AttributeDefinition>
*/
- public function ofType(string $className): array;
+ public function getIterator(): Traversable
+ {
+ yield from $this->attributes;
+ }
}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Definition;
-
-use Traversable;
-
-use function array_filter;
-use function array_map;
-use function array_values;
-use function count;
-use function is_a;
-
-/**
- * @phpstan-type AttributeParam = array{class: class-string, callback: callable(): object}
- *
- * @internal
- */
-final class AttributesContainer implements Attributes
-{
- private static self $empty;
-
- /** @var list<AttributeParam> */
- private array $attributes;
-
- /**
- * @no-named-arguments
- * @param AttributeParam ...$attributes
- */
- public function __construct(array ...$attributes)
- {
- $this->attributes = $attributes;
- }
-
- public static function empty(): self
- {
- return self::$empty ??= new self();
- }
-
- public function has(string $className): bool
- {
- foreach ($this->attributes as $attribute) {
- if (is_a($attribute['class'], $className, true)) {
- return true;
- }
- }
-
- return false;
- }
-
- public function ofType(string $className): array
- {
- $attributes = array_filter(
- $this->attributes,
- static fn (array $attribute): bool => is_a($attribute['class'], $className, true)
- );
-
- /** @phpstan-ignore-next-line */
- return array_values(array_map(
- fn (array $attribute) => $attribute['callback'](),
- $attributes
- ));
- }
-
- public function count(): int
- {
- return count($this->attributes);
- }
-
- /**
- * @return Traversable<object>
- */
- public function getIterator(): Traversable
- {
- foreach ($this->attributes as $attribute) {
- yield $attribute['callback']();
- }
- }
-}
namespace CuyZ\Valinor\Definition;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
/** @internal */
final class ClassDefinition
{
public function __construct(
- private ClassType $type,
- private Attributes $attributes,
- private Properties $properties,
- private Methods $methods,
- private bool $isFinal,
- private bool $isAbstract,
+ /** @var class-string */
+ public readonly string $name,
+ public readonly ObjectType $type,
+ public readonly Attributes $attributes,
+ public readonly Properties $properties,
+ public readonly Methods $methods,
+ public readonly bool $isFinal,
+ public readonly bool $isAbstract,
) {}
-
- /**
- * @return class-string
- */
- public function name(): string
- {
- return $this->type->className();
- }
-
- public function type(): ClassType
- {
- return $this->type;
- }
-
- public function attributes(): Attributes
- {
- return $this->attributes;
- }
-
- public function properties(): Properties
- {
- return $this->properties;
- }
-
- public function methods(): Methods
- {
- return $this->methods;
- }
-
- public function isFinal(): bool
- {
- return $this->isFinal;
- }
-
- public function isAbstract(): bool
- {
- return $this->isAbstract;
- }
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Definition\Exception;
+
+use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
+use ReflectionClass;
+use RuntimeException;
+
+/** @internal */
+final class ExtendTagTypeError extends RuntimeException
+{
+ /**
+ * @param ReflectionClass<object> $reflection
+ */
+ public function __construct(ReflectionClass $reflection, InvalidType $previous)
+ {
+ parent::__construct(
+ "The `@extends` tag of the class `$reflection->name` is not valid: {$previous->getMessage()}",
+ 1670193574,
+ $previous,
+ );
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Definition\Exception;
+
+use CuyZ\Valinor\Type\Type;
+use ReflectionClass;
+use RuntimeException;
+
+/** @internal */
+final class InvalidExtendTagClassName extends RuntimeException
+{
+ /**
+ * @param ReflectionClass<object> $reflection
+ */
+ public function __construct(ReflectionClass $reflection, Type $invalidExtendTag)
+ {
+ /** @var ReflectionClass<object> $parentClass */
+ $parentClass = $reflection->getParentClass();
+
+ parent::__construct(
+ "The `@extends` tag of the class `$reflection->name` has invalid class `{$invalidExtendTag->toString()}`, it should be `$parentClass->name`.",
+ 1670183564,
+ );
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Definition\Exception;
+
+use CuyZ\Valinor\Type\Type;
+use ReflectionClass;
+use RuntimeException;
+
+/** @internal */
+final class InvalidExtendTagType extends RuntimeException
+{
+ /**
+ * @param ReflectionClass<object> $reflection
+ */
+ public function __construct(ReflectionClass $reflection, Type $invalidExtendTag)
+ {
+ /** @var ReflectionClass<object> $parentClass */
+ $parentClass = $reflection->getParentClass();
+
+ parent::__construct(
+ "The `@extends` tag of the class `$reflection->name` has invalid type `{$invalidExtendTag->toString()}`, it should be `{$parentClass->name}`.",
+ 1670181134,
+ );
+ }
+}
namespace CuyZ\Valinor\Definition\Exception;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use LogicException;
/** @internal */
final class InvalidTypeAliasImportClass extends LogicException
{
- public function __construct(ClassType $type, string $className)
+ public function __construct(ObjectType $type, string $className)
{
parent::__construct(
"Cannot import a type alias from unknown class `$className` in class `{$type->className()}`.",
namespace CuyZ\Valinor\Definition\Exception;
+use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\Type;
-use CuyZ\Valinor\Type\ClassType;
use LogicException;
/** @internal */
final class InvalidTypeAliasImportClassType extends LogicException
{
- public function __construct(ClassType $classType, Type $type)
+ public function __construct(ObjectType $classType, Type $type)
{
parent::__construct(
"Importing a type alias can only be done with classes, `{$type->toString()}` was given in class `{$classType->className()}`.",
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Definition\Exception;
+
+use ReflectionClass;
+use RuntimeException;
+
+/** @internal */
+final class SeveralExtendTagsFound extends RuntimeException
+{
+ /**
+ * @param ReflectionClass<object> $reflection
+ */
+ public function __construct(ReflectionClass $reflection)
+ {
+ parent::__construct(
+ "Only one `@extends` tag should be set for the class `$reflection->name`.",
+ 1670195494,
+ );
+ }
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Definition\Exception;
-
-use CuyZ\Valinor\Type\Type;
-use CuyZ\Valinor\Utility\Reflection\Reflection;
-use LogicException;
-use ReflectionFunctionAbstract;
-use ReflectionParameter;
-use ReflectionProperty;
-
-/** @internal */
-final class TypesDoNotMatch extends LogicException
-{
- public function __construct(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection, Type $typeFromDocBlock, Type $typeFromReflection)
- {
- $signature = Reflection::signature($reflection);
-
- if ($reflection instanceof ReflectionProperty) {
- $message = "Types for property `$signature` do not match: `{$typeFromDocBlock->toString()}` (docblock) does not accept `{$typeFromReflection->toString()}` (native).";
- } elseif ($reflection instanceof ReflectionParameter) {
- $message = "Types for parameter `$signature` do not match: `{$typeFromDocBlock->toString()}` (docblock) does not accept `{$typeFromReflection->toString()}` (native).";
- } else {
- $message = "Return types for method `$signature` do not match: `{$typeFromDocBlock->toString()}` (docblock) does not accept `{$typeFromReflection->toString()}` (native).";
- }
-
- parent::__construct($message, 1638471381);
- }
-}
namespace CuyZ\Valinor\Definition\Exception;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use LogicException;
/** @internal */
/**
* @param class-string $importClassName
*/
- public function __construct(ClassType $type, string $importClassName, string $alias)
+ public function __construct(ObjectType $type, string $importClassName, string $alias)
{
parent::__construct(
"Type alias `$alias` imported in `{$type->className()}` could not be found in `$importClassName`",
final class FunctionDefinition
{
public function __construct(
- private string $name,
- private string $signature,
- private Attributes $attributes,
- private ?string $fileName,
+ /** @var non-empty-string */
+ public readonly string $name,
+ /** @var non-empty-string */
+ public readonly string $signature,
+ public readonly Attributes $attributes,
+ /** @var non-empty-string|null */
+ public readonly ?string $fileName,
/** @var class-string|null */
- private ?string $class,
- private bool $isStatic,
- private bool $isClosure,
- private Parameters $parameters,
- private Type $returnType
+ public readonly ?string $class,
+ public readonly bool $isStatic,
+ public readonly bool $isClosure,
+ public readonly Parameters $parameters,
+ public readonly Type $returnType
) {}
-
- public function name(): string
- {
- return $this->name;
- }
-
- public function signature(): string
- {
- return $this->signature;
- }
-
- public function attributes(): Attributes
- {
- return $this->attributes;
- }
-
- public function fileName(): ?string
- {
- return $this->fileName;
- }
-
- /**
- * @return class-string|null
- */
- public function class(): ?string
- {
- return $this->class;
- }
-
- public function isStatic(): bool
- {
- return $this->isStatic;
- }
-
- public function isClosure(): bool
- {
- return $this->isClosure;
- }
-
- public function parameters(): Parameters
- {
- return $this->parameters;
- }
-
- public function returnType(): Type
- {
- return $this->returnType;
- }
}
/** @internal */
final class FunctionObject
{
- private FunctionDefinition $definition;
+ public readonly FunctionDefinition $definition;
/** @var callable */
- private $callback;
+ public readonly mixed $callback;
public function __construct(FunctionDefinition $definition, callable $callback)
{
$this->definition = $definition;
$this->callback = $callback;
}
-
- public function definition(): FunctionDefinition
- {
- return $this->definition;
- }
-
- public function callback(): callable
- {
- return $this->callback;
- }
}
final class MethodDefinition
{
public function __construct(
- private string $name,
- private string $signature,
- private Parameters $parameters,
- private bool $isStatic,
- private bool $isPublic,
- private Type $returnType
+ /** @var non-empty-string */
+ public readonly string $name,
+ /** @var non-empty-string */
+ public readonly string $signature,
+ public readonly Attributes $attributes,
+ public readonly Parameters $parameters,
+ public readonly bool $isStatic,
+ public readonly bool $isPublic,
+ public readonly Type $returnType
) {}
-
- public function name(): string
- {
- return $this->name;
- }
-
- public function signature(): string
- {
- return $this->signature;
- }
-
- public function parameters(): Parameters
- {
- return $this->parameters;
- }
-
- public function isStatic(): bool
- {
- return $this->isStatic;
- }
-
- public function isPublic(): bool
- {
- return $this->isPublic;
- }
-
- public function returnType(): Type
- {
- return $this->returnType;
- }
}
public function __construct(MethodDefinition ...$methods)
{
foreach ($methods as $method) {
- $this->methods[$method->name()] = $method;
+ $this->methods[$method->name] = $method;
}
}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Definition;
-
-use Error;
-use ReflectionAttribute;
-use ReflectionClass;
-use ReflectionFunction;
-use ReflectionMethod;
-use ReflectionParameter;
-use ReflectionProperty;
-use Traversable;
-
-use function array_map;
-
-/** @internal */
-final class NativeAttributes implements Attributes
-{
- private AttributesContainer $delegate;
-
- /** @var array<class-string, array<mixed>> */
- private array $definition = [];
-
- /**
- * @param ReflectionClass<object>|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflection
- */
- public function __construct(ReflectionClass|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflection)
- {
- $attributes = array_filter(
- array_map(
- static function (ReflectionAttribute $attribute) {
- try {
- $instance = $attribute->newInstance();
-
- return [
- 'class' => $attribute->getName(),
- 'callback' => fn () => $instance
- ];
- } catch (Error) {
- // Race condition when the attribute is affected to a property/parameter
- // that was PROMOTED, in this case the attribute will be applied to both
- // ParameterReflection AND PropertyReflection, BUT the target arg inside the attribute
- // class is configured to support only ONE of them (parameter OR property)
- // https://wiki.php.net/rfc/constructor_promotion#attributes for more details.
- // Ignore attribute if the instantiation failed.
- return null;
- }
- },
- $reflection->getAttributes(),
- ),
- );
-
- foreach ($reflection->getAttributes() as $attribute) {
- $this->definition[$attribute->getName()] = $attribute->getArguments();
- }
-
- $this->delegate = new AttributesContainer(...$attributes);
- }
-
- public function has(string $className): bool
- {
- return $this->delegate->has($className);
- }
-
- public function ofType(string $className): array
- {
- return $this->delegate->ofType($className);
- }
-
- public function getIterator(): Traversable
- {
- yield from $this->delegate;
- }
-
- public function count(): int
- {
- return count($this->delegate);
- }
-
- /**
- * @return array<class-string, array<mixed>>
- */
- public function definition(): array
- {
- return $this->definition;
- }
-}
final class ParameterDefinition
{
public function __construct(
- private string $name,
- private string $signature,
- private Type $type,
- private bool $isOptional,
- private bool $isVariadic,
- private mixed $defaultValue,
- private Attributes $attributes
+ /** @var non-empty-string */
+ public readonly string $name,
+ /** @var non-empty-string */
+ public readonly string $signature,
+ public readonly Type $type,
+ public readonly Type $nativeType,
+ public readonly bool $isOptional,
+ public readonly bool $isVariadic,
+ public readonly mixed $defaultValue,
+ public readonly Attributes $attributes
) {}
-
- public function name(): string
- {
- return $this->name;
- }
-
- public function signature(): string
- {
- return $this->signature;
- }
-
- public function type(): Type
- {
- return $this->type;
- }
-
- public function isOptional(): bool
- {
- return $this->isOptional;
- }
-
- public function isVariadic(): bool
- {
- return $this->isVariadic;
- }
-
- public function defaultValue(): mixed
- {
- return $this->defaultValue;
- }
-
- public function attributes(): Attributes
- {
- return $this->attributes;
- }
}
public function __construct(ParameterDefinition ...$parameters)
{
foreach ($parameters as $parameter) {
- $this->parameters[$parameter->name()] = $parameter;
+ $this->parameters[$parameter->name] = $parameter;
}
}
public function __construct(PropertyDefinition ...$properties)
{
foreach ($properties as $property) {
- $this->properties[$property->name()] = $property;
+ $this->properties[$property->name] = $property;
}
}
final class PropertyDefinition
{
public function __construct(
- private string $name,
- private string $signature,
- private Type $type,
- private bool $hasDefaultValue,
- private mixed $defaultValue,
- private bool $isPublic,
- private Attributes $attributes
+ /** @var non-empty-string */
+ public readonly string $name,
+ /** @var non-empty-string */
+ public readonly string $signature,
+ public readonly Type $type,
+ public readonly Type $nativeType,
+ public readonly bool $hasDefaultValue,
+ public readonly mixed $defaultValue,
+ public readonly bool $isPublic,
+ public readonly Attributes $attributes
) {}
-
- public function name(): string
- {
- return $this->name;
- }
-
- public function signature(): string
- {
- return $this->signature;
- }
-
- public function type(): Type
- {
- return $this->type;
- }
-
- public function hasDefaultValue(): bool
- {
- return $this->hasDefaultValue;
- }
-
- public function defaultValue(): mixed
- {
- return $this->defaultValue;
- }
-
- public function isPublic(): bool
- {
- return $this->isPublic;
- }
-
- public function attributes(): Attributes
- {
- return $this->attributes;
- }
}
namespace CuyZ\Valinor\Definition\Repository;
-use CuyZ\Valinor\Definition\Attributes;
-use ReflectionClass;
-use ReflectionFunction;
-use ReflectionMethod;
-use ReflectionParameter;
-use ReflectionProperty;
+use CuyZ\Valinor\Definition\AttributeDefinition;
+use ReflectionAttribute;
/** @internal */
interface AttributesRepository
{
/**
- * @param ReflectionClass<object>|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflector
+ * @param ReflectionAttribute<object> $reflection
*/
- public function for(ReflectionClass|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflector): Attributes;
+ public function for(ReflectionAttribute $reflection): AttributeDefinition;
}
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use Psr\SimpleCache\CacheInterface;
+use function sha1;
+
/** @internal */
final class CacheClassDefinitionRepository implements ClassDefinitionRepository
{
private CacheInterface $cache
) {}
- public function for(ClassType $type): ClassDefinition
+ public function for(ObjectType $type): ClassDefinition
{
- $key = "class-definition-{$type->toString()}";
+ // @infection-ignore-all
+ $key = 'class-definition' . sha1($type->toString());
$entry = $this->cache->get($key);
use CuyZ\Valinor\Utility\Reflection\Reflection;
use Psr\SimpleCache\CacheInterface;
+use function sha1;
+
/** @internal */
final class CacheFunctionDefinitionRepository implements FunctionDefinitionRepository
{
public function for(callable $function): FunctionDefinition
{
$reflection = Reflection::function($function);
- $key = "function-definition-{$reflection->getFileName()}-{$reflection->getStartLine()}-{$reflection->getEndLine()}";
+
+ // @infection-ignore-all
+ $key = 'function-definition-' . sha1($reflection->getFileName() . $reflection->getStartLine() . $reflection->getEndLine());
$entry = $this->cache->get($key);
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
use CuyZ\Valinor\Definition\Attributes;
-use CuyZ\Valinor\Definition\AttributesContainer;
-use CuyZ\Valinor\Definition\NativeAttributes;
use function count;
use function implode;
/** @internal */
final class AttributesCompiler
{
+ public function __construct(private ClassDefinitionCompiler $classDefinitionCompiler) {}
+
public function compile(Attributes $attributes): string
{
if (count($attributes) === 0) {
- return AttributesContainer::class . '::empty()';
+ return Attributes::class . '::empty()';
}
- assert($attributes instanceof NativeAttributes);
-
- $attributesListCode = $this->compileNativeAttributes($attributes);
+ $attributesListCode = $this->compileAttributes($attributes);
return <<<PHP
- new \CuyZ\Valinor\Definition\AttributesContainer($attributesListCode)
+ new \CuyZ\Valinor\Definition\Attributes($attributesListCode)
PHP;
}
- private function compileNativeAttributes(NativeAttributes $attributes): string
+ private function compileAttributes(Attributes $attributes): string
{
$attributesListCode = [];
- foreach ($attributes->definition() as $className => $arguments) {
- $argumentsCode = $this->compileAttributeArguments($arguments);
+ foreach ($attributes as $attribute) {
+ $class = $this->classDefinitionCompiler->compile($attribute->class);
+ $arguments = $this->compileAttributeArguments($attribute->arguments);
- $attributesListCode[] = "['class' => '$className', 'callback' => fn () => new $className($argumentsCode)]";
+ $attributesListCode[] = <<<PHP
+ new \CuyZ\Valinor\Definition\AttributeDefinition(
+ $class,
+ [$arguments],
+ )
+ PHP;
}
return implode(', ', $attributesListCode);
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
-use CuyZ\Valinor\Cache\Compiled\CacheCompiler;
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Definition\PropertyDefinition;
use function assert;
use function implode;
use function iterator_to_array;
+use function var_export;
/** @internal */
-final class ClassDefinitionCompiler implements CacheCompiler
+final class ClassDefinitionCompiler
{
private TypeCompiler $typeCompiler;
public function __construct()
{
$this->typeCompiler = new TypeCompiler();
- $this->attributesCompiler = new AttributesCompiler();
+ $this->attributesCompiler = new AttributesCompiler($this);
$this->methodCompiler = new MethodDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
$this->propertyCompiler = new PropertyDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
{
assert($value instanceof ClassDefinition);
- $type = $this->typeCompiler->compile($value->type());
+ $name = var_export($value->name, true);
+ $type = $this->typeCompiler->compile($value->type);
$properties = array_map(
fn (PropertyDefinition $property) => $this->propertyCompiler->compile($property),
- iterator_to_array($value->properties())
+ iterator_to_array($value->properties)
);
$properties = implode(', ', $properties);
$methods = array_map(
fn (MethodDefinition $method) => $this->methodCompiler->compile($method),
- iterator_to_array($value->methods())
+ iterator_to_array($value->methods)
);
$methods = implode(', ', $methods);
- $attributes = $this->attributesCompiler->compile($value->attributes());
+ $attributes = $this->attributesCompiler->compile($value->attributes);
- $isFinal = var_export($value->isFinal(), true);
- $isAbstract = var_export($value->isAbstract(), true);
+ $isFinal = var_export($value->isFinal, true);
+ $isAbstract = var_export($value->isAbstract, true);
return <<<PHP
new \CuyZ\Valinor\Definition\ClassDefinition(
+ $name,
$type,
$attributes,
new \CuyZ\Valinor\Definition\Properties($properties),
namespace CuyZ\Valinor\Definition\Repository\Cache\Compiler;
-use CuyZ\Valinor\Cache\Compiled\CacheCompiler;
use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\ParameterDefinition;
use function var_export;
/** @internal */
-final class FunctionDefinitionCompiler implements CacheCompiler
+final class FunctionDefinitionCompiler
{
private TypeCompiler $typeCompiler;
public function __construct()
{
$this->typeCompiler = new TypeCompiler();
- $this->attributesCompiler = new AttributesCompiler();
+ $this->attributesCompiler = new AttributesCompiler(new ClassDefinitionCompiler());
- $this->parameterCompiler = new ParameterDefinitionCompiler($this->typeCompiler, new AttributesCompiler());
+ $this->parameterCompiler = new ParameterDefinitionCompiler($this->typeCompiler, $this->attributesCompiler);
}
public function compile(mixed $value): string
$parameters = array_map(
fn (ParameterDefinition $parameter) => $this->parameterCompiler->compile($parameter),
- iterator_to_array($value->parameters())
+ iterator_to_array($value->parameters)
);
- $attributes = $this->attributesCompiler->compile($value->attributes());
- $fileName = var_export($value->fileName(), true);
- $class = var_export($value->class(), true);
- $isStatic = var_export($value->isStatic(), true);
- $isClosure = var_export($value->isClosure(), true);
+ $attributes = $this->attributesCompiler->compile($value->attributes);
+ $fileName = var_export($value->fileName, true);
+ $class = var_export($value->class, true);
+ $isStatic = var_export($value->isStatic, true);
+ $isClosure = var_export($value->isClosure, true);
$parameters = implode(', ', $parameters);
- $returnType = $this->typeCompiler->compile($value->returnType());
+ $returnType = $this->typeCompiler->compile($value->returnType);
return <<<PHP
new \CuyZ\Valinor\Definition\FunctionDefinition(
- '{$value->name()}',
- '{$value->signature()}',
+ '{$value->name}',
+ '{$value->signature}',
$attributes,
$fileName,
$class,
{
private TypeCompiler $typeCompiler;
+ private AttributesCompiler $attributesCompiler;
+
private ParameterDefinitionCompiler $parameterCompiler;
public function __construct(TypeCompiler $typeCompiler, AttributesCompiler $attributesCompiler)
{
$this->typeCompiler = $typeCompiler;
+ $this->attributesCompiler = $attributesCompiler;
$this->parameterCompiler = new ParameterDefinitionCompiler($typeCompiler, $attributesCompiler);
}
public function compile(MethodDefinition $method): string
{
+ $attributes = $this->attributesCompiler->compile($method->attributes);
+
$parameters = array_map(
fn (ParameterDefinition $parameter) => $this->parameterCompiler->compile($parameter),
- iterator_to_array($method->parameters())
+ iterator_to_array($method->parameters)
);
$parameters = implode(', ', $parameters);
- $isStatic = var_export($method->isStatic(), true);
- $isPublic = var_export($method->isPublic(), true);
- $returnType = $this->typeCompiler->compile($method->returnType());
+ $isStatic = var_export($method->isStatic, true);
+ $isPublic = var_export($method->isPublic, true);
+ $returnType = $this->typeCompiler->compile($method->returnType);
return <<<PHP
new \CuyZ\Valinor\Definition\MethodDefinition(
- '{$method->name()}',
- '{$method->signature()}',
+ '{$method->name}',
+ '{$method->signature}',
+ $attributes,
new \CuyZ\Valinor\Definition\Parameters($parameters),
$isStatic,
$isPublic,
public function compile(ParameterDefinition $parameter): string
{
- $isOptional = var_export($parameter->isOptional(), true);
- $isVariadic = var_export($parameter->isVariadic(), true);
+ $isOptional = var_export($parameter->isOptional, true);
+ $isVariadic = var_export($parameter->isVariadic, true);
$defaultValue = $this->defaultValue($parameter);
- $type = $this->typeCompiler->compile($parameter->type());
- $attributes = $this->attributesCompiler->compile($parameter->attributes());
+ $type = $this->typeCompiler->compile($parameter->type);
+ $nativeType = $this->typeCompiler->compile($parameter->nativeType);
+ $attributes = $this->attributesCompiler->compile($parameter->attributes);
return <<<PHP
new \CuyZ\Valinor\Definition\ParameterDefinition(
- '{$parameter->name()}',
- '{$parameter->signature()}',
+ '{$parameter->name}',
+ '{$parameter->signature}',
$type,
+ $nativeType,
$isOptional,
$isVariadic,
$defaultValue,
private function defaultValue(ParameterDefinition $parameter): string
{
- $defaultValue = $parameter->defaultValue();
+ $defaultValue = $parameter->defaultValue;
return is_object($defaultValue)
? 'unserialize(' . var_export(serialize($defaultValue), true) . ')'
public function compile(PropertyDefinition $property): string
{
- $type = $this->typeCompiler->compile($property->type());
- $hasDefaultValue = var_export($property->hasDefaultValue(), true);
- $defaultValue = var_export($property->defaultValue(), true);
- $isPublic = var_export($property->isPublic(), true);
- $attributes = $this->attributesCompiler->compile($property->attributes());
+ $type = $this->typeCompiler->compile($property->type);
+ $nativeType = $this->typeCompiler->compile($property->nativeType);
+ $hasDefaultValue = var_export($property->hasDefaultValue, true);
+ $defaultValue = var_export($property->defaultValue, true);
+ $isPublic = var_export($property->isPublic, true);
+ $attributes = $this->attributesCompiler->compile($property->attributes);
return <<<PHP
new \CuyZ\Valinor\Definition\PropertyDefinition(
- '{$property->name()}',
- '{$property->signature()}',
+ '{$property->name}',
+ '{$property->signature}',
$type,
+ $nativeType,
$hasDefaultValue,
$defaultValue,
$isPublic,
default => "$class::default()",
};
case $type instanceof ShapedArrayType:
- $shapes = array_map(
+ $elements = implode(', ', array_map(
fn (ShapedArrayElement $element) => $this->compileArrayShapeElement($element),
$type->elements()
- );
- $shapes = implode(', ', $shapes);
+ ));
+
+ if ($type->hasUnsealedType()) {
+ $unsealedType = $this->compile($type->unsealedType());
+
+ return "$class::unsealed($unsealedType, $elements)";
+ } elseif ($type->isUnsealed()) {
+ return "$class::unsealedWithoutType($elements)";
+ }
- return "new $class(...[$shapes])";
+ return "new $class($elements)";
case $type instanceof ArrayType:
case $type instanceof NonEmptyArrayType:
if ($type->toString() === 'array' || $type->toString() === 'non-empty-array') {
$generics = implode(', ', $generics);
- if ($type instanceof InterfaceType) {
- return "new $class('{$type->className()}', [$generics])";
- }
-
- $parent = $type->hasParent()
- ? $this->compile($type->parent())
- : 'null';
-
- return "new $class('{$type->className()}', [$generics], $parent)";
+ return "new $class('{$type->className()}', [$generics])";
case $type instanceof ClassStringType:
if (null === $type->subType()) {
return "new $class()";
namespace CuyZ\Valinor\Definition\Repository;
use CuyZ\Valinor\Definition\ClassDefinition;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
/** @internal */
interface ClassDefinitionRepository
{
- public function for(ClassType $type): ClassDefinition;
+ public function for(ObjectType $type): ClassDefinition;
}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Definition\Repository\Reflection;
-
-use CuyZ\Valinor\Definition\NativeAttributes;
-use CuyZ\Valinor\Definition\Repository\AttributesRepository;
-use Reflector;
-
-/** @internal */
-final class NativeAttributesRepository implements AttributesRepository
-{
- public function for(Reflector $reflector): NativeAttributes
- {
- return new NativeAttributes($reflector);
- }
-}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Definition\Repository\Reflection;
+
+use CuyZ\Valinor\Definition\AttributeDefinition;
+use CuyZ\Valinor\Definition\Repository\AttributesRepository;
+use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
+use CuyZ\Valinor\Type\Types\NativeClassType;
+use ReflectionAttribute;
+
+/** @internal */
+final class ReflectionAttributesRepository implements AttributesRepository
+{
+ public function __construct(private ClassDefinitionRepository $classDefinitionRepository) {}
+
+ public function for(ReflectionAttribute $reflection): AttributeDefinition
+ {
+ $class = $this->classDefinitionRepository->for(new NativeClassType($reflection->getName()));
+
+ return new AttributeDefinition(
+ $class,
+ $reflection->getArguments(),
+ );
+ }
+}
namespace CuyZ\Valinor\Definition\Repository\Reflection;
+use CuyZ\Valinor\Definition\AttributeDefinition;
+use CuyZ\Valinor\Definition\Attributes;
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\Exception\ClassTypeAliasesDuplication;
+use CuyZ\Valinor\Definition\Exception\ExtendTagTypeError;
+use CuyZ\Valinor\Definition\Exception\InvalidExtendTagClassName;
+use CuyZ\Valinor\Definition\Exception\InvalidExtendTagType;
use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClass;
use CuyZ\Valinor\Definition\Exception\InvalidTypeAliasImportClassType;
+use CuyZ\Valinor\Definition\Exception\SeveralExtendTagsFound;
use CuyZ\Valinor\Definition\Exception\UnknownTypeAliasImport;
use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Definition\Methods;
use CuyZ\Valinor\Definition\PropertyDefinition;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
-use CuyZ\Valinor\Type\ClassType;
use CuyZ\Valinor\Type\GenericType;
+use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\AliasSpecification;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\ClassContextSpecification;
+use CuyZ\Valinor\Type\Parser\Factory\Specifications\GenericCheckerSpecification;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeAliasAssignerSpecification;
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Type\Types\NativeClassType;
use CuyZ\Valinor\Type\Types\UnresolvableType;
+use CuyZ\Valinor\Utility\Reflection\DocParser;
use CuyZ\Valinor\Utility\Reflection\Reflection;
+use ReflectionAttribute;
use ReflectionClass;
use ReflectionMethod;
use ReflectionProperty;
-use CuyZ\Valinor\Utility\Reflection\DocParser;
use function array_filter;
use function array_keys;
{
private TypeParserFactory $typeParserFactory;
- private AttributesRepository $attributesFactory;
+ private AttributesRepository $attributesRepository;
private ReflectionPropertyDefinitionBuilder $propertyBuilder;
/** @var array<string, ReflectionTypeResolver> */
private array $typeResolver = [];
- public function __construct(TypeParserFactory $typeParserFactory, AttributesRepository $attributesFactory)
+ public function __construct(TypeParserFactory $typeParserFactory)
{
$this->typeParserFactory = $typeParserFactory;
- $this->attributesFactory = $attributesFactory;
- $this->propertyBuilder = new ReflectionPropertyDefinitionBuilder($attributesFactory);
- $this->methodBuilder = new ReflectionMethodDefinitionBuilder($attributesFactory);
+ $this->attributesRepository = new ReflectionAttributesRepository($this);
+ $this->propertyBuilder = new ReflectionPropertyDefinitionBuilder($this->attributesRepository);
+ $this->methodBuilder = new ReflectionMethodDefinitionBuilder($this->attributesRepository);
}
- public function for(ClassType $type): ClassDefinition
+ public function for(ObjectType $type): ClassDefinition
{
$reflection = Reflection::class($type->className());
return new ClassDefinition(
+ $reflection->name,
$type,
- $this->attributesFactory->for($reflection),
+ new Attributes(...$this->attributes($reflection)),
new Properties(...$this->properties($type)),
new Methods(...$this->methods($type)),
$reflection->isFinal(),
);
}
+ /**
+ * @param ReflectionClass<object> $reflection
+ * @return list<AttributeDefinition>
+ */
+ private function attributes(ReflectionClass $reflection): array
+ {
+ return array_map(
+ fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute),
+ Reflection::attributes($reflection)
+ );
+ }
+
/**
* @return list<PropertyDefinition>
*/
- private function properties(ClassType $type): array
+ private function properties(ObjectType $type): array
{
return array_map(
function (ReflectionProperty $property) use ($type) {
return $this->propertyBuilder->for($property, $typeResolver);
},
- Reflection::class($type->className())->getProperties()
+ Reflection::class($type->className())->getProperties(),
);
}
/**
* @return list<MethodDefinition>
*/
- private function methods(ClassType $type): array
+ private function methods(ObjectType $type): array
{
$reflection = Reflection::class($type->className());
$methods = $reflection->getMethods();
/**
* @param ReflectionClass<object> $target
*/
- private function typeResolver(ClassType $type, ReflectionClass $target): ReflectionTypeResolver
+ private function typeResolver(ObjectType $type, ReflectionClass $target): ReflectionTypeResolver
{
$typeKey = $target->isInterface()
? "{$type->toString()}/{$type->className()}"
}
while ($type->className() !== $target->name) {
- $type = $type->parent();
+ $type = $this->parentType($type);
}
$generics = $type instanceof GenericType ? $type->generics() : [];
$advancedParser = $this->typeParserFactory->get(
new ClassContextSpecification($type->className()),
new AliasSpecification(Reflection::class($type->className())),
- new TypeAliasAssignerSpecification($generics + $localAliases + $importedAliases)
+ new TypeAliasAssignerSpecification($generics + $localAliases + $importedAliases),
+ new GenericCheckerSpecification(),
);
$nativeParser = $this->typeParserFactory->get(
- new ClassContextSpecification($type->className())
+ new ClassContextSpecification($type->className()),
);
return $this->typeResolver[$typeKey] = new ReflectionTypeResolver($nativeParser, $advancedParser);
/**
* @return array<string, Type>
*/
- private function localTypeAliases(ClassType $type): array
+ private function localTypeAliases(ObjectType $type): array
{
$reflection = Reflection::class($type->className());
$rawTypes = DocParser::localTypeAliases($reflection);
/**
* @return array<string, Type>
*/
- private function importedTypeAliases(ClassType $type): array
+ private function importedTypeAliases(ObjectType $type): array
{
$reflection = Reflection::class($type->className());
$importedTypesRaw = DocParser::importedTypeAliases($reflection);
throw new InvalidTypeAliasImportClass($type, $class);
}
- if (! $classType instanceof ClassType) {
+ if (! $classType instanceof ObjectType) {
throw new InvalidTypeAliasImportClassType($type, $classType);
}
return $importedTypes;
}
- private function typeParser(ClassType $type): TypeParser
+ private function typeParser(ObjectType $type): TypeParser
{
$specs = [
new ClassContextSpecification($type->className()),
new AliasSpecification(Reflection::class($type->className())),
+ new GenericCheckerSpecification(),
];
if ($type instanceof GenericType) {
return $this->typeParserFactory->get(...$specs);
}
+
+ private function parentType(ObjectType $type): NativeClassType
+ {
+ $reflection = Reflection::class($type->className());
+
+ /** @var ReflectionClass<object> $parentReflection */
+ $parentReflection = $reflection->getParentClass();
+
+ $extendedClass = DocParser::classExtendsTypes($reflection);
+
+ if (count($extendedClass) > 1) {
+ throw new SeveralExtendTagsFound($reflection);
+ } elseif (count($extendedClass) === 0) {
+ $extendedClass = $parentReflection->name;
+ } else {
+ $extendedClass = $extendedClass[0];
+ }
+
+ try {
+ $parentType = $this->typeParser($type)->parse($extendedClass);
+ } catch (InvalidType $exception) {
+ throw new ExtendTagTypeError($reflection, $exception);
+ }
+
+ if (! $parentType instanceof NativeClassType) {
+ throw new InvalidExtendTagType($reflection, $parentType);
+ }
+
+ if ($parentType->className() !== $parentReflection->name) {
+ throw new InvalidExtendTagClassName($reflection, $parentType);
+ }
+
+ return $parentType;
+ }
}
namespace CuyZ\Valinor\Definition\Repository\Reflection;
+use CuyZ\Valinor\Definition\AttributeDefinition;
+use CuyZ\Valinor\Definition\Attributes;
use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\Parameters;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\AliasSpecification;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\ClassContextSpecification;
+use CuyZ\Valinor\Type\Parser\Factory\Specifications\GenericCheckerSpecification;
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use CuyZ\Valinor\Utility\Reflection\Reflection;
+use ReflectionAttribute;
use ReflectionFunction;
use ReflectionParameter;
+use function array_map;
use function str_ends_with;
/** @internal */
return new FunctionDefinition(
$name,
Reflection::signature($reflection),
- $this->attributesRepository->for($reflection),
+ new Attributes(...$this->attributes($reflection)),
$reflection->getFileName() ?: null,
$class?->name,
$reflection->getClosureThis() === null,
$class = $reflection->getClosureScopeClass();
$nativeSpecifications = [];
- $advancedSpecification = [new AliasSpecification($reflection)];
+ $advancedSpecification = [
+ new AliasSpecification($reflection),
+ new GenericCheckerSpecification(),
+ ];
if ($class !== null) {
$nativeSpecifications[] = new ClassContextSpecification($class->name);
return new ReflectionTypeResolver($nativeParser, $advancedParser);
}
+
+ /**
+ * @return list<AttributeDefinition>
+ */
+ private function attributes(ReflectionFunction $reflection): array
+ {
+ return array_map(
+ fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute),
+ Reflection::attributes($reflection)
+ );
+ }
}
namespace CuyZ\Valinor\Definition\Repository\Reflection;
+use CuyZ\Valinor\Definition\Attributes;
use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Definition\Parameters;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Utility\Reflection\Reflection;
+use ReflectionAttribute;
use ReflectionMethod;
use ReflectionParameter;
/** @internal */
final class ReflectionMethodDefinitionBuilder
{
+ private AttributesRepository $attributesRepository;
+
private ReflectionParameterDefinitionBuilder $parameterBuilder;
public function __construct(AttributesRepository $attributesRepository)
{
+ $this->attributesRepository = $attributesRepository;
$this->parameterBuilder = new ReflectionParameterDefinitionBuilder($attributesRepository);
}
public function for(ReflectionMethod $reflection, ReflectionTypeResolver $typeResolver): MethodDefinition
{
+ /** @var non-empty-string $name */
+ $name = $reflection->name;
+
+ $attributes = array_map(
+ fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute),
+ Reflection::attributes($reflection)
+ );
+
$parameters = array_map(
fn (ReflectionParameter $parameter) => $this->parameterBuilder->for($parameter, $typeResolver),
$reflection->getParameters()
$returnType = $typeResolver->resolveType($reflection);
return new MethodDefinition(
- $reflection->name,
+ $name,
Reflection::signature($reflection),
+ new Attributes(...$attributes),
new Parameters(...$parameters),
$reflection->isStatic(),
$reflection->isPublic(),
namespace CuyZ\Valinor\Definition\Repository\Reflection;
+use CuyZ\Valinor\Definition\AttributeDefinition;
+use CuyZ\Valinor\Definition\Attributes;
use CuyZ\Valinor\Definition\ParameterDefinition;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Type\Types\UnresolvableType;
use CuyZ\Valinor\Utility\Reflection\Reflection;
+use ReflectionAttribute;
use ReflectionParameter;
+use function array_map;
+
/** @internal */
final class ReflectionParameterDefinitionBuilder
{
- public function __construct(private AttributesRepository $attributesFactory) {}
+ public function __construct(private AttributesRepository $attributesRepository) {}
public function for(ReflectionParameter $reflection, ReflectionTypeResolver $typeResolver): ParameterDefinition
{
+ /** @var non-empty-string $name */
$name = $reflection->name;
$signature = Reflection::signature($reflection);
$type = $typeResolver->resolveType($reflection);
+ $nativeType = $typeResolver->resolveNativeType($reflection);
$isOptional = $reflection->isOptional();
$isVariadic = $reflection->isVariadic();
- $attributes = $this->attributesFactory->for($reflection);
if ($reflection->isDefaultValueAvailable()) {
$defaultValue = $reflection->getDefaultValue();
$type = UnresolvableType::forInvalidParameterDefaultValue($signature, $type, $defaultValue);
}
- return new ParameterDefinition($name, $signature, $type, $isOptional, $isVariadic, $defaultValue, $attributes);
+ return new ParameterDefinition(
+ $name,
+ $signature,
+ $type,
+ $nativeType,
+ $isOptional,
+ $isVariadic,
+ $defaultValue,
+ new Attributes(...$this->attributes($reflection)),
+ );
+ }
+
+ /**
+ * @return list<AttributeDefinition>
+ */
+ private function attributes(ReflectionParameter $reflection): array
+ {
+ return array_map(
+ fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute),
+ Reflection::attributes($reflection)
+ );
}
}
namespace CuyZ\Valinor\Definition\Repository\Reflection;
+use CuyZ\Valinor\Definition\AttributeDefinition;
+use CuyZ\Valinor\Definition\Attributes;
use CuyZ\Valinor\Definition\PropertyDefinition;
use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\NullType;
use CuyZ\Valinor\Type\Types\UnresolvableType;
use CuyZ\Valinor\Utility\Reflection\Reflection;
+use ReflectionAttribute;
use ReflectionProperty;
+use function array_map;
+
/** @internal */
final class ReflectionPropertyDefinitionBuilder
{
public function for(ReflectionProperty $reflection, ReflectionTypeResolver $typeResolver): PropertyDefinition
{
+ /** @var non-empty-string $name */
$name = $reflection->name;
$signature = Reflection::signature($reflection);
$type = $typeResolver->resolveType($reflection);
+ $nativeType = $typeResolver->resolveNativeType($reflection);
$hasDefaultValue = $this->hasDefaultValue($reflection, $type);
$defaultValue = $reflection->getDefaultValue();
$isPublic = $reflection->isPublic();
- $attributes = $this->attributesRepository->for($reflection);
if ($hasDefaultValue
&& ! $type instanceof UnresolvableType
$name,
$signature,
$type,
+ $nativeType,
$hasDefaultValue,
$defaultValue,
$isPublic,
- $attributes
+ new Attributes(...$this->attributes($reflection)),
);
}
return $reflection->getDeclaringClass()->getDefaultProperties()[$reflection->name] !== null
|| NullType::get()->matches($type);
}
+
+ /**
+ * @return list<AttributeDefinition>
+ */
+ private function attributes(ReflectionProperty $reflection): array
+ {
+ return array_map(
+ fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute),
+ Reflection::attributes($reflection)
+ );
+ }
}
namespace CuyZ\Valinor\Definition\Repository\Reflection;
-use CuyZ\Valinor\Definition\Exception\TypesDoNotMatch;
+use CuyZ\Valinor\Type\GenericType;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Type;
use ReflectionParameter;
use ReflectionProperty;
+use function trim;
+
/** @internal */
final class ReflectionTypeResolver
{
public function __construct(
private TypeParser $nativeParser,
- private TypeParser $advancedParser
+ private TypeParser $advancedParser,
) {}
public function resolveType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): Type
{
- $nativeType = $this->nativeType($reflection);
+ $nativeType = $this->resolveNativeType($reflection);
$typeFromDocBlock = $this->typeFromDocBlock($reflection);
- if (! $nativeType && ! $typeFromDocBlock) {
- return MixedType::get();
+ if (! $typeFromDocBlock) {
+ // When the type is a class, it may declare templates that must be
+ // filled with generics. PHP does not handle generics natively, so
+ // we need to make sure that no generics are left unassigned by
+ // parsing the type again using the advanced parser.
+ if ($nativeType instanceof GenericType) {
+ $nativeType = $this->parseType($nativeType->toString(), $reflection, $this->advancedParser);
+ }
+
+ return $nativeType;
}
- if (! $nativeType) {
- /** @var Type $typeFromDocBlock */
+ if ($typeFromDocBlock instanceof UnresolvableType) {
return $typeFromDocBlock;
}
- if (! $typeFromDocBlock) {
- return $nativeType;
+ if (! $typeFromDocBlock->matches($nativeType)) {
+ return UnresolvableType::forDocBlockTypeNotMatchingNative($reflection, $typeFromDocBlock, $nativeType);
}
- if (! $typeFromDocBlock instanceof UnresolvableType
- && ! $nativeType instanceof UnresolvableType
- && ! $typeFromDocBlock->matches($nativeType)
- ) {
- throw new TypesDoNotMatch($reflection, $typeFromDocBlock, $nativeType);
+ return $typeFromDocBlock;
+ }
+
+ public function resolveNativeType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): Type
+ {
+ $reflectionType = $reflection instanceof ReflectionFunctionAbstract
+ ? $reflection->getReturnType()
+ : $reflection->getType();
+
+ if (! $reflectionType) {
+ return MixedType::get();
}
- return $typeFromDocBlock;
+ $type = Reflection::flattenType($reflectionType);
+ $type = $this->parseType($type, $reflection, $this->nativeParser);
+
+ return $this->handleVariadicType($reflection, $type);
}
private function typeFromDocBlock(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type
return $this->handleVariadicType($reflection, $type);
}
- private function nativeType(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection): ?Type
- {
- $reflectionType = $reflection instanceof ReflectionFunctionAbstract
- ? $reflection->getReturnType()
- : $reflection->getType();
-
- if (! $reflectionType) {
- return null;
- }
-
- $type = Reflection::flattenType($reflectionType);
- $type = $this->parseType($type, $reflection, $this->nativeParser);
-
- return $this->handleVariadicType($reflection, $type);
- }
-
private function parseType(string $raw, ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection, TypeParser $parser): Type
{
try {
use CuyZ\Valinor\Cache\RuntimeCache;
use CuyZ\Valinor\Cache\Warmup\RecursiveCacheWarmupService;
use CuyZ\Valinor\Definition\FunctionsContainer;
-use CuyZ\Valinor\Definition\Repository\AttributesRepository;
use CuyZ\Valinor\Definition\Repository\Cache\CacheClassDefinitionRepository;
use CuyZ\Valinor\Definition\Repository\Cache\CacheFunctionDefinitionRepository;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
-use CuyZ\Valinor\Definition\Repository\Reflection\NativeAttributesRepository;
+use CuyZ\Valinor\Definition\Repository\Reflection\ReflectionAttributesRepository;
use CuyZ\Valinor\Definition\Repository\Reflection\ReflectionClassDefinitionRepository;
use CuyZ\Valinor\Definition\Repository\Reflection\ReflectionFunctionDefinitionRepository;
use CuyZ\Valinor\Mapper\ArgumentsMapper;
use CuyZ\Valinor\Mapper\Tree\Builder\InterfaceNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\IterableNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\ListNodeBuilder;
-use CuyZ\Valinor\Mapper\Tree\Builder\NativeClassNodeBuilder;
+use CuyZ\Valinor\Mapper\Tree\Builder\ObjectNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\NodeBuilder;
+use CuyZ\Valinor\Mapper\Tree\Builder\NullNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\ObjectImplementations;
-use CuyZ\Valinor\Mapper\Tree\Builder\ObjectNodeBuilder;
+use CuyZ\Valinor\Mapper\Tree\Builder\FilteredObjectNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\RootNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\ScalarNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\ShapedArrayNodeBuilder;
use CuyZ\Valinor\Mapper\TypeTreeMapper;
use CuyZ\Valinor\Normalizer\ArrayNormalizer;
use CuyZ\Valinor\Normalizer\Format;
+use CuyZ\Valinor\Normalizer\JsonNormalizer;
use CuyZ\Valinor\Normalizer\Normalizer;
use CuyZ\Valinor\Normalizer\Transformer\KeyTransformersHandler;
use CuyZ\Valinor\Normalizer\Transformer\RecursiveTransformer;
use CuyZ\Valinor\Normalizer\Transformer\ValueTransformersHandler;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\Parser\Factory\LexingTypeParserFactory;
use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Types\ListType;
use CuyZ\Valinor\Type\Types\NonEmptyArrayType;
use CuyZ\Valinor\Type\Types\NonEmptyListType;
+use CuyZ\Valinor\Type\Types\NullType;
use CuyZ\Valinor\Type\Types\ShapedArrayType;
use Psr\SimpleCache\CacheInterface;
IterableType::class => $arrayNodeBuilder,
ShapedArrayType::class => new ShapedArrayNodeBuilder($settings->allowSuperfluousKeys),
ScalarType::class => new ScalarNodeBuilder($settings->enableFlexibleCasting),
- ClassType::class => new NativeClassNodeBuilder(
+ NullType::class => new NullNodeBuilder(),
+ ObjectType::class => new ObjectNodeBuilder(
$this->get(ClassDefinitionRepository::class),
$this->get(ObjectBuilderFactory::class),
- $this->get(ObjectNodeBuilder::class),
+ $this->get(FilteredObjectNodeBuilder::class),
$settings->enableFlexibleCasting,
),
]);
- $builder = new UnionNodeBuilder(
- $builder,
- $this->get(ClassDefinitionRepository::class),
- $this->get(ObjectBuilderFactory::class),
- $this->get(ObjectNodeBuilder::class),
- $settings->enableFlexibleCasting
- );
+ $builder = new UnionNodeBuilder($builder);
$builder = new InterfaceNodeBuilder(
$builder,
$this->get(ObjectImplementations::class),
$this->get(ClassDefinitionRepository::class),
$this->get(ObjectBuilderFactory::class),
- $this->get(ObjectNodeBuilder::class),
- $settings->enableFlexibleCasting
+ $this->get(FilteredObjectNodeBuilder::class),
+ new FunctionsContainer(
+ $this->get(FunctionDefinitionRepository::class),
+ $settings->customConstructors
+ ),
+ $settings->enableFlexibleCasting,
+ $settings->allowSuperfluousKeys,
);
$builder = new CasterProxyNodeBuilder($builder);
return new ErrorCatcherNodeBuilder($builder, $settings->exceptionFilter);
},
- ObjectNodeBuilder::class => fn () => new ObjectNodeBuilder($settings->allowSuperfluousKeys),
+ FilteredObjectNodeBuilder::class => fn () => new FilteredObjectNodeBuilder($settings->allowSuperfluousKeys),
ObjectImplementations::class => fn () => new ObjectImplementations(
new FunctionsContainer(
new ValueTransformersHandler(
$this->get(FunctionDefinitionRepository::class),
),
- new KeyTransformersHandler(
- $this->get(FunctionDefinitionRepository::class),
- ),
+ new KeyTransformersHandler(),
$settings->transformersSortedByPriority(),
array_keys($settings->transformerAttributes),
),
$this->get(RecursiveTransformer::class),
),
+ JsonNormalizer::class => fn () => new JsonNormalizer(
+ $this->get(RecursiveTransformer::class),
+ ),
+
ClassDefinitionRepository::class => fn () => new CacheClassDefinitionRepository(
new ReflectionClassDefinitionRepository(
$this->get(TypeParserFactory::class),
- $this->get(AttributesRepository::class),
),
$this->get(CacheInterface::class),
),
FunctionDefinitionRepository::class => fn () => new CacheFunctionDefinitionRepository(
new ReflectionFunctionDefinitionRepository(
$this->get(TypeParserFactory::class),
- $this->get(AttributesRepository::class),
+ new ReflectionAttributesRepository(
+ $this->get(ClassDefinitionRepository::class),
+ ),
),
$this->get(CacheInterface::class)
),
- AttributesRepository::class => fn () => new NativeAttributesRepository(),
-
TypeParserFactory::class => fn () => new LexingTypeParserFactory(),
TypeParser::class => fn () => $this->get(TypeParserFactory::class)->get(),
if ($errorsCount === 1) {
$body = $errors
->toArray()[0]
- ->withBody("Could not map arguments of `{$function->signature()}`. An error occurred at path {node_path}: {original_message}")
+ ->withBody("Could not map arguments of `$function->signature`. An error occurred at path {node_path}: {original_message}")
->toString();
} else {
$source = ValueDumper::dump($node->sourceValue());
- $body = "Could not map arguments of `{$function->signature()}` with value $source. A total of $errorsCount errors were encountered.";
+ $body = "Could not map arguments of `$function->signature` with value $source. A total of $errorsCount errors were encountered.";
}
parent::__construct($body, 1671115362);
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Exception;
+
+use CuyZ\Valinor\Definition\FunctionDefinition;
+use CuyZ\Valinor\Mapper\Tree\Exception\UnresolvableShellType;
+use LogicException;
+
+/** @internal */
+final class TypeErrorDuringArgumentsMapping extends LogicException
+{
+ public function __construct(FunctionDefinition $function, UnresolvableShellType $exception)
+ {
+ parent::__construct(
+ "Could not map arguments of `$function->signature`: {$exception->getMessage()}",
+ 1711534351,
+ $exception,
+ );
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Exception;
+
+use CuyZ\Valinor\Mapper\Tree\Exception\UnresolvableShellType;
+use CuyZ\Valinor\Type\Type;
+use LogicException;
+
+/** @internal */
+final class TypeErrorDuringMapping extends LogicException
+{
+ public function __construct(Type $type, UnresolvableShellType $exception)
+ {
+ parent::__construct(
+ "Error while trying to map to `{$type->toString()}`: {$exception->getMessage()}",
+ 1711526329,
+ $exception,
+ );
+ }
+}
namespace CuyZ\Valinor\Mapper\Object;
use CuyZ\Valinor\Definition\Attributes;
-use CuyZ\Valinor\Definition\AttributesContainer;
use CuyZ\Valinor\Definition\ParameterDefinition;
use CuyZ\Valinor\Definition\PropertyDefinition;
use CuyZ\Valinor\Type\Type;
public static function fromParameter(ParameterDefinition $parameter): self
{
- $instance = new self($parameter->name(), $parameter->type());
- $instance->attributes = $parameter->attributes();
+ $instance = new self($parameter->name, $parameter->type);
+ $instance->attributes = $parameter->attributes;
- if ($parameter->isOptional()) {
- $instance->defaultValue = $parameter->defaultValue();
+ if ($parameter->isOptional) {
+ $instance->defaultValue = $parameter->defaultValue;
$instance->isRequired = false;
}
public static function fromProperty(PropertyDefinition $property): self
{
- $instance = new self($property->name(), $property->type());
- $instance->attributes = $property->attributes();
+ $instance = new self($property->name, $property->type);
+ $instance->attributes = $property->attributes;
- if ($property->hasDefaultValue()) {
- $instance->defaultValue = $property->defaultValue();
+ if ($property->hasDefaultValue) {
+ $instance->defaultValue = $property->defaultValue;
$instance->isRequired = false;
}
public function attributes(): Attributes
{
- return $this->attributes ??= AttributesContainer::empty();
+ return $this->attributes ??= Attributes::empty();
}
}
use function array_map;
use function array_values;
-use function iterator_to_array;
/**
* @internal
{
return new self(...array_map(
fn (ParameterDefinition $parameter) => Argument::fromParameter($parameter),
- array_values(iterator_to_array($parameters)) // PHP8.1 array unpacking
+ array_values([...$parameters])
));
}
{
return new self(...array_map(
fn (PropertyDefinition $property) => Argument::fromProperty($property),
- array_values(iterator_to_array($properties)) // PHP8.1 array unpacking
+ array_values([...$properties])
));
}
use CuyZ\Valinor\Mapper\Object\Exception\InvalidSource;
use CuyZ\Valinor\Type\CompositeTraversableType;
+use CuyZ\Valinor\Type\Types\ArrayKeyType;
use IteratorAggregate;
use Traversable;
$this->arguments = $arguments;
}
- public static function forInterface(Arguments $arguments, mixed $value): self
+ public static function forInterface(Arguments $arguments, mixed $value, bool $allowSuperfluousKeys): self
{
$self = new self($arguments);
$self->forInterface = true;
if (count($arguments) > 0) {
- $self = $self->transform($value);
+ $self = $self->transform($value, $allowSuperfluousKeys);
}
return $self;
}
- public static function forClass(Arguments $arguments, mixed $value): self
+ public static function forClass(Arguments $arguments, mixed $value, bool $allowSuperfluousKeys): self
{
$self = new self($arguments);
- $self = $self->transform($value);
+ $self = $self->transform($value, $allowSuperfluousKeys);
return $self;
}
return $this->hadSingleArgument;
}
- private function transform(mixed $value): self
+ private function transform(mixed $value, bool $allowSuperfluousKeys): self
{
$clone = clone $this;
- $transformedValue = $this->transformValueForSingleArgument($value);
+ $transformedValue = $this->transformValueForSingleArgument($value, $allowSuperfluousKeys);
if (! is_array($transformedValue)) {
throw new InvalidSource($transformedValue, $this->arguments);
return $clone;
}
- private function transformValueForSingleArgument(mixed $value): mixed
+ private function transformValueForSingleArgument(mixed $value, bool $allowSuperfluousKeys): mixed
{
if (count($this->arguments) !== 1) {
return $value;
$argument = $this->arguments->at(0);
$name = $argument->name();
- $isTraversable = $argument-> type() instanceof CompositeTraversableType;
+ $type = $argument->type();
+ $isTraversableAndAllowsStringKeys = $type instanceof CompositeTraversableType
+ && $type->keyType() !== ArrayKeyType::integer();
if (is_array($value) && array_key_exists($name, $value)) {
- if ($this->forInterface || ! $isTraversable || count($value) === 1) {
+ if ($this->forInterface || ! $isTraversableAndAllowsStringKeys || $allowSuperfluousKeys || count($value) === 1) {
return $value;
}
}
- if ($value === [] && ! $isTraversable) {
+ if ($value === [] && ! $isTraversableAndAllowsStringKeys) {
return $value;
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Object;
+
+use Attribute;
+
+/**
+ * This attribute allows a static method inside a class to be marked as a
+ * constructor, that can be used by the mapper to instantiate the object. The
+ * method must be public, static and return an instance of the class it is part
+ * of.
+ *
+ * This attribute is a convenient replacement to the usage of the constructor
+ * registration method: @see \CuyZ\Valinor\MapperBuilder::registerConstructor()
+ *
+ * ```php
+ * final readonly class Email
+ * {
+ * // When another constructor is registered for the class, the native
+ * // constructor is disabled. To enable it again, it is mandatory to
+ * // explicitly register it again.
+ * #[\CuyZ\Valinor\Mapper\Object\Constructor]
+ * public function __construct(public string $value) {}
+ *
+ * #[\CuyZ\Valinor\Mapper\Object\Constructor]
+ * public static function createFrom(string $user, string $domainName): self
+ * {
+ * return new self($user . '@' . $domainName);
+ * }
+ * }
+ *
+ * (new \CuyZ\Valinor\MapperBuilder())
+ * ->mapper()
+ * ->map(Email::class, [
+ * 'userName' => 'john.doe',
+ * 'domainName' => 'example.com',
+ * ]); // john.doe@example.com
+ * ```
+ *
+ * @api
+ */
+#[Attribute(Attribute::TARGET_METHOD)]
+final class Constructor {}
public function __construct(ClassDefinition $class)
{
parent::__construct(
- "No available constructor found for class `{$class->name()}`.",
+ "No available constructor found for class `{$class->name}`.",
1646916477
);
}
public function __construct(FunctionDefinition $function, Type $type)
{
parent::__construct(
- "Invalid type `{$type->toString()}` for the first parameter of the constructor `{$function->signature()}`, it should be of type `class-string`.",
+ "Invalid type `{$type->toString()}` for the first parameter of the constructor `{$function->signature}`, it should be of type `class-string`.",
1661517000
);
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Object\Exception;
+
+use CuyZ\Valinor\Definition\MethodDefinition;
+use CuyZ\Valinor\Type\Types\UnresolvableType;
+use LogicException;
+
+/** @internal */
+final class InvalidConstructorMethodWithAttributeReturnType extends LogicException
+{
+ /**
+ * @param class-string $expectedClassName
+ */
+ public function __construct(string $expectedClassName, MethodDefinition $method)
+ {
+ if ($method->returnType instanceof UnresolvableType) {
+ $message = $method->returnType->message();
+ } else {
+ $message = "Invalid return type `{$method->returnType->toString()}` for constructor `{$method->signature}`, it must be `$expectedClassName`.";
+ }
+
+ parent::__construct($message, 1708104783);
+ }
+}
{
public function __construct(FunctionDefinition $function)
{
- $returnType = $function->returnType();
-
- if ($returnType instanceof UnresolvableType) {
- $message = $returnType->message();
+ if ($function->returnType instanceof UnresolvableType) {
+ $message = $function->returnType->message();
} else {
- $message = "Invalid return type `{$returnType->toString()}` for constructor `{$function->signature()}`, it must be a valid class name.";
+ $message = "Invalid return type `{$function->returnType->toString()}` for constructor `{$function->signature}`, it must be a valid class name.";
}
parent::__construct($message, 1659446121);
public function __construct(FunctionDefinition $function)
{
parent::__construct(
- "Missing first parameter of type `class-string` for the constructor `{$function->signature()}`.",
+ "Missing first parameter of type `class-string` for the constructor `{$function->signature}`.",
1661516853
);
}
$constructors = implode('`, `', $constructors);
parent::__construct(
- "A collision was detected between the following constructors of the class `{$class->type()->toString()}`: `$constructors`.",
+ "A collision was detected between the following constructors of the class `{$class->type->toString()}`: `$constructors`.",
1654955787
);
}
public function for(ClassDefinition $class): array
{
- $signature = $class->type()->toString();
+ $signature = $class->type->toString();
$entry = $this->cache->get($signature);
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Definition\FunctionObject;
use CuyZ\Valinor\Definition\FunctionsContainer;
+use CuyZ\Valinor\Mapper\Object\Constructor;
use CuyZ\Valinor\Mapper\Object\DynamicConstructor;
use CuyZ\Valinor\Mapper\Object\Exception\CannotInstantiateObject;
use CuyZ\Valinor\Mapper\Object\Exception\InvalidConstructorClassTypeParameter;
+use CuyZ\Valinor\Mapper\Object\Exception\InvalidConstructorMethodWithAttributeReturnType;
use CuyZ\Valinor\Mapper\Object\Exception\InvalidConstructorReturnType;
use CuyZ\Valinor\Mapper\Object\Exception\MissingConstructorClassTypeParameter;
use CuyZ\Valinor\Mapper\Object\FunctionObjectBuilder;
use CuyZ\Valinor\Type\Types\ClassStringType;
use CuyZ\Valinor\Type\Types\EnumType;
use CuyZ\Valinor\Type\Types\NativeStringType;
+use CuyZ\Valinor\Utility\Reflection\Reflection;
use function array_key_exists;
+use function array_values;
use function count;
use function is_a;
private ObjectBuilderFactory $delegate,
/** @var array<class-string, null> */
private array $nativeConstructors,
- private FunctionsContainer $constructors
+ private FunctionsContainer $constructors,
) {}
public function for(ClassDefinition $class): array
$builders = $this->builders($class);
if (count($builders) === 0) {
- if ($class->methods()->hasConstructor()) {
+ if ($class->methods->hasConstructor()) {
throw new CannotInstantiateObject($class);
}
*/
private function builders(ClassDefinition $class): array
{
- $className = $class->name();
- $classType = $class->type();
- $methods = $class->methods();
+ $className = $class->name;
+ $classType = $class->type;
+ $methods = $class->methods;
$builders = [];
continue;
}
- $definition = $constructor->definition();
- $functionClass = $definition->class();
+ $definition = $constructor->definition;
+ $functionClass = $definition->class;
- if ($functionClass && $definition->isStatic() && ! $definition->isClosure()) {
+ if ($functionClass && $definition->isStatic && ! $definition->isClosure) {
$scopedClass = is_a($className, $functionClass, true) ? $className : $functionClass;
- $builders[] = new MethodObjectBuilder($scopedClass, $definition->name(), $definition->parameters());
+ $builders[$definition->signature] = new MethodObjectBuilder($scopedClass, $definition->name, $definition->parameters);
} else {
- $builders[] = new FunctionObjectBuilder($constructor, $classType);
+ $builders[$definition->signature] = new FunctionObjectBuilder($constructor, $classType);
}
}
- if (! array_key_exists($className, $this->nativeConstructors) && count($builders) > 0) {
- return $builders;
+ foreach ($methods as $method) {
+ if (! $method->isStatic) {
+ continue;
+ }
+
+ if (! $method->attributes->has(Constructor::class)) {
+ continue;
+ }
+
+ if (! $method->returnType instanceof ClassType) {
+ throw new InvalidConstructorMethodWithAttributeReturnType($className, $method);
+ }
+
+ if (! is_a($className, $method->returnType->className(), true)) {
+ throw new InvalidConstructorMethodWithAttributeReturnType($className, $method);
+ }
+
+ if (! $class->type->matches($method->returnType)) {
+ continue;
+ }
+
+ $builders[$method->signature] = new MethodObjectBuilder($className, $method->name, $method->parameters);
}
if ($classType instanceof EnumType) {
- $builders[] = new NativeEnumObjectBuilder($classType);
- } elseif ($methods->hasConstructor() && $methods->constructor()->isPublic()) {
+ $buildersWithOneArguments = array_filter($builders, fn (ObjectBuilder $builder) => $builder->describeArguments()->count() === 1);
+
+ if (count($buildersWithOneArguments) === 0) {
+ $builders[] = new NativeEnumObjectBuilder($classType);
+ }
+ } elseif ($methods->hasConstructor()
+ && $methods->constructor()->isPublic
+ && (
+ count($builders) === 0
+ || $methods->constructor()->attributes->has(Constructor::class)
+ || array_key_exists($className, $this->nativeConstructors)
+ )
+ ) {
$builders[] = new NativeConstructorObjectBuilder($class);
}
- return $builders;
+ return array_values($builders);
}
- private function constructorMatches(FunctionObject $function, ClassType $classType): bool
+ private function constructorMatches(FunctionObject $function, ObjectType $classType): bool
{
- $definition = $function->definition();
- $parameters = $definition->parameters();
- $returnType = $definition->returnType();
+ $definition = $function->definition;
- if (! $classType->matches($returnType)) {
+ if (! $classType->matches($definition->returnType)) {
return false;
}
- if (! $definition->attributes()->has(DynamicConstructor::class)) {
+ if (! $definition->attributes->has(DynamicConstructor::class)) {
return true;
}
- if (count($parameters) === 0) {
+ if (count($definition->parameters) === 0) {
throw new MissingConstructorClassTypeParameter($definition);
}
- $parameterType = $parameters->at(0)->type();
+ $parameterType = $definition->parameters->at(0)->type;
if ($parameterType instanceof NativeStringType) {
$parameterType = ClassStringType::get();
$this->filteredConstructors = [];
foreach ($this->constructors as $constructor) {
- $function = $constructor->definition();
+ $function = $constructor->definition;
- if (enum_exists($function->class() ?? '') && in_array($function->name(), ['from', 'tryFrom'], true)) {
+ if ($function->class
+ && Reflection::enumExists($function->class)
+ && in_array($function->name, ['from', 'tryFrom'], true)
+ ) {
continue;
}
- if (! $function->returnType() instanceof ObjectType) {
+ if (! $function->returnType instanceof ObjectType) {
throw new InvalidConstructorReturnType($function);
}
use CuyZ\Valinor\Mapper\Object\FunctionObjectBuilder;
use CuyZ\Valinor\Mapper\Object\NativeConstructorObjectBuilder;
use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use DateTime;
use DateTimeImmutable;
public function for(ClassDefinition $class): array
{
- $className = $class->name();
+ $className = $class->name;
$builders = $this->delegate->for($class);
$buildersWithOneArgument = array_filter($builders, fn (ObjectBuilder $builder) => count($builder->describeArguments()) === 1);
if (count($buildersWithOneArgument) === 0 || $this->supportedDateFormats !== Settings::DEFAULT_SUPPORTED_DATETIME_FORMATS) {
- $builders[] = $this->internalDateTimeBuilder($class->type());
+ $builders[] = $this->internalDateTimeBuilder($class->type);
}
return $builders;
}
- private function internalDateTimeBuilder(ClassType $type): FunctionObjectBuilder
+ private function internalDateTimeBuilder(ObjectType $type): FunctionObjectBuilder
{
$constructor = new DateTimeFormatConstructor(...$this->supportedDateFormats);
$function = new FunctionObject($this->functionDefinitionRepository->for($constructor), $constructor);
use CuyZ\Valinor\Mapper\Object\NativeConstructorObjectBuilder;
use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
use CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use DateTimeZone;
use Exception;
{
$builders = $this->delegate->for($class);
- if ($class->name() !== DateTimeZone::class) {
+ if ($class->name !== DateTimeZone::class) {
return $builders;
}
if ($useDefaultBuilder) {
// @infection-ignore-all / Ignore memoization
- $builders[] = $this->defaultBuilder($class->type());
+ $builders[] = $this->defaultBuilder($class->type);
}
return $builders;
}
- private function defaultBuilder(ClassType $type): FunctionObjectBuilder
+ private function defaultBuilder(ObjectType $type): FunctionObjectBuilder
{
$constructor = function (string $timezone) {
try {
use CuyZ\Valinor\Definition\ClassDefinition;
use CuyZ\Valinor\Mapper\Object\ReflectionObjectBuilder;
-
-use function enum_exists;
+use CuyZ\Valinor\Utility\Reflection\Reflection;
/** @internal */
final class ReflectionObjectBuilderFactory implements ObjectBuilderFactory
{
public function for(ClassDefinition $class): array
{
- if (enum_exists($class->name())) {
+ if (Reflection::enumExists($class->name)) {
return [];
}
use function count;
use function is_array;
+use function reset;
/** @internal */
final class FilteredObjectBuilder implements ObjectBuilder
{
private ObjectBuilder $delegate;
- private Arguments $arguments;
-
- public function __construct(mixed $source, ObjectBuilder ...$builders)
+ private function __construct(mixed $source, ObjectBuilder ...$builders)
{
$this->delegate = $this->filterBuilder($source, ...$builders);
- $this->arguments = $this->delegate->describeArguments();
+ }
+
+ public static function from(mixed $source, ObjectBuilder ...$builders): ObjectBuilder
+ {
+ if (count($builders) === 1) {
+ return $builders[0];
+ }
+
+ return new self($source, ...$builders);
}
public function describeArguments(): Arguments
{
- return $this->arguments;
+ return $this->delegate->describeArguments();
}
public function build(array $arguments): object
private function filterBuilder(mixed $source, ObjectBuilder ...$builders): ObjectBuilder
{
if (count($builders) === 1) {
- return $builders[0];
+ return reset($builders);
}
/** @var non-empty-list<ObjectBuilder> $builders */
use CuyZ\Valinor\Definition\FunctionObject;
use CuyZ\Valinor\Definition\ParameterDefinition;
use CuyZ\Valinor\Mapper\Tree\Message\UserlandError;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use Exception;
use function array_map;
private bool $isDynamicConstructor;
- public function __construct(FunctionObject $function, ClassType $type)
+ public function __construct(FunctionObject $function, ObjectType $type)
{
- $definition = $function->definition();
+ $definition = $function->definition;
$arguments = array_map(
fn (ParameterDefinition $parameter) => Argument::fromParameter($parameter),
- array_values(iterator_to_array($definition->parameters())) // PHP8.1 array unpacking
+ array_values([...$definition->parameters])
);
- $this->isDynamicConstructor = $definition->attributes()->has(DynamicConstructor::class);
+ $this->isDynamicConstructor = $definition->attributes->has(DynamicConstructor::class);
if ($this->isDynamicConstructor) {
array_shift($arguments);
public function build(array $arguments): object
{
- $parameters = $this->function->definition()->parameters();
+ $parameters = $this->function->definition->parameters;
if ($this->isDynamicConstructor) {
- $arguments[$parameters->at(0)->name()] = $this->className;
+ $arguments[$parameters->at(0)->name] = $this->className;
}
$arguments = new MethodArguments($parameters, $arguments);
try {
- return ($this->function->callback())(...$arguments);
+ return ($this->function->callback)(...$arguments);
} catch (Exception $exception) {
throw UserlandError::from($exception);
}
public function signature(): string
{
- return $this->function->definition()->signature();
+ return $this->function->definition->signature;
}
}
public function __construct(Parameters $parameters, array $arguments)
{
foreach ($parameters as $parameter) {
- $name = $parameter->name();
+ $name = $parameter->name;
- if ($parameter->isVariadic()) {
+ if ($parameter->isVariadic) {
$this->arguments = [...$this->arguments, ...array_values($arguments[$name])]; // @phpstan-ignore-line we know that the argument is iterable
} else {
$this->arguments[] = $arguments[$name];
public function describeArguments(): Arguments
{
- return $this->arguments ??= Arguments::fromParameters($this->class->methods()->constructor()->parameters());
+ return $this->arguments ??= Arguments::fromParameters($this->class->methods->constructor()->parameters);
}
public function build(array $arguments): object
{
- $className = $this->class->name();
- $arguments = new MethodArguments($this->class->methods()->constructor()->parameters(), $arguments);
+ $className = $this->class->name;
+ $arguments = new MethodArguments($this->class->methods->constructor()->parameters, $arguments);
try {
return new $className(...$arguments);
public function signature(): string
{
- return $this->class->methods()->constructor()->signature();
+ return $this->class->methods->constructor()->signature;
}
}
public function describeArguments(): Arguments
{
- return $this->arguments ??= Arguments::fromProperties($this->class->properties());
+ return $this->arguments ??= Arguments::fromProperties($this->class->properties);
}
public function build(array $arguments): object
{
- $object = new ($this->class->name())();
+ $object = new ($this->class->name)();
if (count($arguments) > 0) {
(function () use ($arguments): void {
public function signature(): string
{
- return $this->class->name() . ' (properties)';
+ return $this->class->name . ' (properties)';
}
}
namespace CuyZ\Valinor\Mapper\Tree\Builder;
use CuyZ\Valinor\Mapper\Tree\Shell;
+use CuyZ\Valinor\Type\CompositeTraversableType;
+use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Type\Types\ShapedArrayType;
+use CuyZ\Valinor\Type\Types\UnionType;
/** @internal */
final class CasterProxyNodeBuilder implements NodeBuilder
if ($shell->hasValue()) {
$value = $shell->value();
- if ($shell->type()->accepts($value)) {
+ if ($this->typeAcceptsValue($shell->type(), $value)) {
return TreeNode::leaf($shell, $value);
}
}
return $this->delegate->build($shell, $rootBuilder);
}
+
+ private function typeAcceptsValue(Type $type, mixed $value): bool
+ {
+ if ($type instanceof UnionType) {
+ foreach ($type->types() as $subType) {
+ if ($this->typeAcceptsValue($subType, $value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ return ! $type instanceof CompositeTraversableType
+ && ! $type instanceof ShapedArrayType
+ && $type->accepts($value);
+ }
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Tree\Builder;
+
+use CuyZ\Valinor\Mapper\Object\ArgumentsValues;
+use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
+use CuyZ\Valinor\Mapper\Tree\Exception\UnexpectedArrayKeysForClass;
+use CuyZ\Valinor\Mapper\Tree\Shell;
+
+use function count;
+
+/** @internal */
+final class FilteredObjectNodeBuilder
+{
+ public function __construct(private bool $allowSuperfluousKeys) {}
+
+ public function build(ObjectBuilder $builder, Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
+ {
+ $arguments = ArgumentsValues::forClass($builder->describeArguments(), $shell->value(), $this->allowSuperfluousKeys);
+
+ $children = $this->children($shell, $arguments, $rootBuilder);
+
+ $object = $this->buildObject($builder, $children);
+
+ $node = $arguments->hadSingleArgument()
+ ? TreeNode::flattenedBranch($shell, $object, $children[0])
+ : TreeNode::branch($shell, $object, $children);
+
+ if (! $this->allowSuperfluousKeys && count($arguments->superfluousKeys()) > 0) {
+ $node = $node->withMessage(new UnexpectedArrayKeysForClass($arguments));
+ }
+
+ return $node;
+ }
+
+ /**
+ * @return array<TreeNode>
+ */
+ private function children(Shell $shell, ArgumentsValues $arguments, RootNodeBuilder $rootBuilder): array
+ {
+ $children = [];
+
+ foreach ($arguments as $argument) {
+ $name = $argument->name();
+ $type = $argument->type();
+ $attributes = $argument->attributes();
+
+ $child = $shell->child($name, $type, $attributes);
+
+ if ($arguments->hasValue($name)) {
+ $child = $child->withValue($arguments->getValue($name));
+ }
+
+ $children[] = $rootBuilder->build($child);
+ }
+
+ return $children;
+ }
+
+ /**
+ * @param TreeNode[] $children
+ */
+ private function buildObject(ObjectBuilder $builder, array $children): ?object
+ {
+ $arguments = [];
+
+ foreach ($children as $child) {
+ if (! $child->isValid()) {
+ return null;
+ }
+
+ $arguments[$child->name()] = $child->value();
+ }
+
+ return $builder->build($arguments);
+ }
+}
namespace CuyZ\Valinor\Mapper\Tree\Builder;
+use CuyZ\Valinor\Definition\FunctionsContainer;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
use CuyZ\Valinor\Mapper\Object\Arguments;
use CuyZ\Valinor\Mapper\Object\ArgumentsValues;
use CuyZ\Valinor\Mapper\Object\FilteredObjectBuilder;
use CuyZ\Valinor\Mapper\Tree\Exception\CannotInferFinalClass;
use CuyZ\Valinor\Mapper\Tree\Exception\CannotResolveObjectType;
+use CuyZ\Valinor\Mapper\Tree\Exception\InterfaceHasBothConstructorAndInfer;
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationCallbackError;
use CuyZ\Valinor\Mapper\Tree\Message\UserlandError;
use CuyZ\Valinor\Mapper\Tree\Shell;
+use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\NativeClassType;
use CuyZ\Valinor\Type\Types\InterfaceType;
private ObjectImplementations $implementations,
private ClassDefinitionRepository $classDefinitionRepository,
private ObjectBuilderFactory $objectBuilderFactory,
- private ObjectNodeBuilder $objectNodeBuilder,
- private bool $enableFlexibleCasting
+ private FilteredObjectNodeBuilder $filteredObjectNodeBuilder,
+ private FunctionsContainer $constructors,
+ private bool $enableFlexibleCasting,
+ private bool $allowSuperfluousKeys,
) {}
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
return $this->delegate->build($shell, $rootBuilder);
}
+ if ($this->constructorRegisteredFor($type)) {
+ if ($this->implementations->has($type->className())) {
+ throw new InterfaceHasBothConstructorAndInfer($type->className());
+ }
+
+ return $this->delegate->build($shell, $rootBuilder);
+ }
+
if ($this->enableFlexibleCasting && $shell->value() === null) {
$shell = $shell->withValue([]);
}
$className = $type->className();
if (! $this->implementations->has($className)) {
- if ($type instanceof InterfaceType || $this->classDefinitionRepository->for($type)->isAbstract()) {
+ if ($type instanceof InterfaceType || $this->classDefinitionRepository->for($type)->isAbstract) {
throw new CannotResolveObjectType($className);
}
}
$function = $this->implementations->function($className);
- $arguments = Arguments::fromParameters($function->parameters());
+ $arguments = Arguments::fromParameters($function->parameters);
- if ($type instanceof NativeClassType && $this->classDefinitionRepository->for($type)->isFinal()) {
+ if ($type instanceof NativeClassType && $this->classDefinitionRepository->for($type)->isFinal) {
throw new CannotInferFinalClass($type, $function);
}
try {
$classType = $this->implementations->implementation($className, $values);
} catch (ObjectImplementationCallbackError $exception) {
- throw UserlandError::from($exception->original());
+ throw UserlandError::from($exception);
}
$class = $this->classDefinitionRepository->for($classType);
- $objectBuilder = new FilteredObjectBuilder($shell->value(), ...$this->objectBuilderFactory->for($class));
+ $objectBuilder = FilteredObjectBuilder::from($shell->value(), ...$this->objectBuilderFactory->for($class));
$shell = $this->transformSourceForClass($shell, $arguments, $objectBuilder->describeArguments());
- return $this->objectNodeBuilder->build($objectBuilder, $shell, $rootBuilder);
+ return $this->filteredObjectNodeBuilder->build($objectBuilder, $shell, $rootBuilder);
+ }
+
+ private function constructorRegisteredFor(Type $type): bool
+ {
+ foreach ($this->constructors as $constructor) {
+ if ($type->matches($constructor->definition->returnType)) {
+ return true;
+ }
+ }
+
+ return false;
}
private function transformSourceForClass(Shell $shell, Arguments $interfaceArguments, Arguments $classArguments): Shell
*/
private function children(Shell $shell, Arguments $arguments, RootNodeBuilder $rootBuilder): array
{
- $arguments = ArgumentsValues::forInterface($arguments, $shell->value());
+ $arguments = ArgumentsValues::forInterface($arguments, $shell->value(), $this->allowSuperfluousKeys);
$children = [];
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Mapper\Tree\Builder;
-
-use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
-use CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory;
-use CuyZ\Valinor\Mapper\Object\FilteredObjectBuilder;
-use CuyZ\Valinor\Mapper\Tree\Shell;
-use CuyZ\Valinor\Type\ClassType;
-
-use function assert;
-
-/** @internal */
-final class NativeClassNodeBuilder implements NodeBuilder
-{
- public function __construct(
- private ClassDefinitionRepository $classDefinitionRepository,
- private ObjectBuilderFactory $objectBuilderFactory,
- private ObjectNodeBuilder $objectNodeBuilder,
- private bool $enableFlexibleCasting,
- ) {}
-
- public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
- {
- $type = $shell->type();
-
- // @infection-ignore-all
- assert($type instanceof ClassType);
-
- if ($this->enableFlexibleCasting && $shell->value() === null) {
- $shell = $shell->withValue([]);
- }
-
- $class = $this->classDefinitionRepository->for($type);
- $objectBuilder = new FilteredObjectBuilder($shell->value(), ...$this->objectBuilderFactory->for($class));
-
- return $this->objectNodeBuilder->build($objectBuilder, $shell, $rootBuilder);
- }
-}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Tree\Builder;
+
+use CuyZ\Valinor\Mapper\Tree\Exception\SourceIsNotNull;
+use CuyZ\Valinor\Mapper\Tree\Shell;
+use CuyZ\Valinor\Type\Types\NullType;
+
+use function assert;
+
+/** @internal */
+final class NullNodeBuilder implements NodeBuilder
+{
+ public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
+ {
+ $type = $shell->type();
+ $value = $shell->value();
+
+ assert($type instanceof NullType);
+
+ if ($value !== null) {
+ throw new SourceIsNotNull();
+ }
+
+ return TreeNode::leaf($shell, null);
+ }
+}
use CuyZ\Valinor\Definition\FunctionDefinition;
use CuyZ\Valinor\Definition\FunctionsContainer;
-use CuyZ\Valinor\Mapper\Tree\Exception\InvalidAbstractObjectName;
use CuyZ\Valinor\Mapper\Tree\Exception\InvalidResolvedImplementationValue;
use CuyZ\Valinor\Mapper\Tree\Exception\MissingObjectImplementationRegistration;
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationCallbackError;
use CuyZ\Valinor\Mapper\Tree\Exception\ObjectImplementationNotRegistered;
use CuyZ\Valinor\Mapper\Tree\Exception\ResolvedImplementationIsNotAccepted;
use CuyZ\Valinor\Type\ClassType;
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ClassStringType;
use CuyZ\Valinor\Type\Types\UnionType;
use Exception;
+use function assert;
+
/** @internal */
final class ObjectImplementations
{
public function __construct(
private FunctionsContainer $functions,
private TypeParser $typeParser
- ) {
- foreach ($functions as $name => $function) {
- /** @var string $name */
- $this->implementations[$name] = $this->implementations($name);
- }
- }
+ ) {}
public function has(string $name): bool
{
public function function(string $name): FunctionDefinition
{
- return $this->functions->get($name)->definition();
+ return $this->functions->get($name)->definition;
}
/**
*/
public function implementation(string $name, array $arguments): ClassType
{
+ /** @infection-ignore-all / We cannot test the assignment */
+ $this->implementations[$name] ??= $this->implementations($name);
+
$class = $this->call($name, $arguments);
return $this->implementations[$name][$class]
private function call(string $name, array $arguments): string
{
try {
- $signature = ($this->functions->get($name)->callback())(...$arguments);
+ $signature = ($this->functions->get($name)->callback)(...$arguments);
} catch (Exception $exception) {
throw new ObjectImplementationCallbackError($name, $exception);
}
*/
private function implementations(string $name): array
{
- $function = $this->functions->get($name)->definition();
+ $function = $this->functions->get($name)->definition;
- try {
- $type = $this->typeParser->parse($name);
- } catch (InvalidType) {
- }
+ $type = $this->typeParser->parse($name);
- if (! isset($type) || (! $type instanceof InterfaceType && ! $type instanceof ClassType)) {
- throw new InvalidAbstractObjectName($name);
- }
+ /** @infection-ignore-all */
+ assert($type instanceof InterfaceType || $type instanceof ClassType);
$classes = $this->implementationsByReturnSignature($name, $function);
- if (empty($classes)) {
+ if ($classes === []) {
throw new MissingObjectImplementationRegistration($name, $function);
}
*/
private function implementationsByReturnSignature(string $name, FunctionDefinition $function): array
{
- $returnType = $function->returnType();
+ $returnType = $function->returnType;
if (! $returnType instanceof ClassStringType && ! $returnType instanceof UnionType) {
- if (count($function->parameters()) > 0) {
+ if (count($function->parameters) > 0) {
return [];
}
namespace CuyZ\Valinor\Mapper\Tree\Builder;
-use CuyZ\Valinor\Mapper\Object\ArgumentsValues;
-use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
-use CuyZ\Valinor\Mapper\Tree\Exception\UnexpectedArrayKeysForClass;
+use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
+use CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory;
+use CuyZ\Valinor\Mapper\Object\FilteredObjectBuilder;
use CuyZ\Valinor\Mapper\Tree\Shell;
-use function count;
+use CuyZ\Valinor\Type\ObjectType;
+
+use function assert;
/** @internal */
-final class ObjectNodeBuilder
+final class ObjectNodeBuilder implements NodeBuilder
{
- public function __construct(private bool $allowSuperfluousKeys) {}
-
- public function build(ObjectBuilder $builder, Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
+ public function __construct(
+ private ClassDefinitionRepository $classDefinitionRepository,
+ private ObjectBuilderFactory $objectBuilderFactory,
+ private FilteredObjectNodeBuilder $filteredObjectNodeBuilder,
+ private bool $enableFlexibleCasting,
+ ) {}
+
+ public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
- $arguments = ArgumentsValues::forClass($builder->describeArguments(), $shell->value());
-
- $children = $this->children($shell, $arguments, $rootBuilder);
+ $type = $shell->type();
- $object = $this->buildObject($builder, $children);
+ // @infection-ignore-all
+ assert($type instanceof ObjectType);
- $node = $arguments->hadSingleArgument()
- ? TreeNode::flattenedBranch($shell, $object, $children[0])
- : TreeNode::branch($shell, $object, $children);
-
- if (! $this->allowSuperfluousKeys && count($arguments->superfluousKeys()) > 0) {
- $node = $node->withMessage(new UnexpectedArrayKeysForClass($arguments));
+ if ($this->enableFlexibleCasting && $shell->value() === null) {
+ $shell = $shell->withValue([]);
}
- return $node;
- }
-
- /**
- * @return array<TreeNode>
- */
- private function children(Shell $shell, ArgumentsValues $arguments, RootNodeBuilder $rootBuilder): array
- {
- $children = [];
-
- foreach ($arguments as $argument) {
- $name = $argument->name();
- $type = $argument->type();
- $attributes = $argument->attributes();
-
- $child = $shell->child($name, $type, $attributes);
-
- if ($arguments->hasValue($name)) {
- $child = $child->withValue($arguments->getValue($name));
- }
-
- $children[] = $rootBuilder->build($child);
- }
-
- return $children;
- }
-
- /**
- * @param TreeNode[] $children
- */
- private function buildObject(ObjectBuilder $builder, array $children): ?object
- {
- $arguments = [];
-
- foreach ($children as $child) {
- if (! $child->isValid()) {
- return null;
- }
-
- $arguments[$child->name()] = $child->value();
- }
+ $class = $this->classDefinitionRepository->for($type);
+ $objectBuilder = FilteredObjectBuilder::from($shell->value(), ...$this->objectBuilderFactory->for($class));
- return $builder->build($arguments);
+ return $this->filteredObjectNodeBuilder->build($objectBuilder, $shell, $rootBuilder);
}
}
unset($value[$key]);
}
+ if ($type->isUnsealed()) {
+ $unsealedShell = $shell->withType($type->unsealedType())->withValue($value);
+ $unsealedChildren = $rootBuilder->build($unsealedShell)->children();
+
+ foreach ($unsealedChildren as $unsealedChild) {
+ $children[$unsealedChild->name()] = $unsealedChild;
+ }
+ }
+
return $children;
}
use CuyZ\Valinor\Mapper\Tree\Node;
use CuyZ\Valinor\Mapper\Tree\Shell;
use CuyZ\Valinor\Type\FloatType;
+use CuyZ\Valinor\Type\Type;
use Throwable;
use function array_map;
return $instance;
}
- /**
- * PHP8.1 intersection
- * @param Throwable&Message $message
- */
- public static function error(Shell $shell, Throwable $message): self
+ public static function error(Shell $shell, Throwable&Message $message): self
{
return (new self($shell, null))->withMessage($message);
}
return $this->shell->name();
}
+ public function type(): Type
+ {
+ return $this->shell->type();
+ }
+
+ /**
+ * @return array<self>
+ */
+ public function children(): array
+ {
+ return $this->children;
+ }
+
public function isValid(): bool
{
return $this->valid;
namespace CuyZ\Valinor\Mapper\Tree\Builder;
-use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
-use CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory;
-use CuyZ\Valinor\Mapper\Object\FilteredObjectBuilder;
-use CuyZ\Valinor\Mapper\Object\ObjectBuilder;
use CuyZ\Valinor\Mapper\Tree\Exception\CannotResolveTypeFromUnion;
+use CuyZ\Valinor\Mapper\Tree\Exception\TooManyResolvedTypesFromUnion;
use CuyZ\Valinor\Mapper\Tree\Shell;
-use CuyZ\Valinor\Type\ScalarType;
-use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\ClassType;
-use CuyZ\Valinor\Type\Types\NullType;
+use CuyZ\Valinor\Type\FloatType;
+use CuyZ\Valinor\Type\IntegerType;
+use CuyZ\Valinor\Type\ScalarType;
+use CuyZ\Valinor\Type\StringType;
+use CuyZ\Valinor\Type\Types\InterfaceType;
+use CuyZ\Valinor\Type\Types\ShapedArrayType;
use CuyZ\Valinor\Type\Types\UnionType;
use function count;
+use function krsort;
+use function reset;
/** @internal */
final class UnionNodeBuilder implements NodeBuilder
{
- public function __construct(
- private NodeBuilder $delegate,
- private ClassDefinitionRepository $classDefinitionRepository,
- private ObjectBuilderFactory $objectBuilderFactory,
- private ObjectNodeBuilder $objectNodeBuilder,
- private bool $enableFlexibleCasting
- ) {}
+ public function __construct(private NodeBuilder $delegate) {}
public function build(Shell $shell, RootNodeBuilder $rootBuilder): TreeNode
{
return $this->delegate->build($shell, $rootBuilder);
}
- $classNode = $this->tryToBuildClassNode($type, $shell, $rootBuilder);
-
- if ($classNode instanceof TreeNode) {
- return $classNode;
- }
+ $structs = [];
+ $scalars = [];
+ $all = [];
- $narrowedType = $this->narrow($type, $shell->value());
+ foreach ($type->types() as $subType) {
+ $node = $rootBuilder->build($shell->withType($subType));
- return $rootBuilder->build($shell->withType($narrowedType));
- }
-
- private function narrow(UnionType $type, mixed $source): Type
- {
- $subTypes = $type->types();
-
- if ($source !== null && count($subTypes) === 2) {
- if ($subTypes[0] instanceof NullType) {
- return $subTypes[1];
- } elseif ($subTypes[1] instanceof NullType) {
- return $subTypes[0];
- }
- }
-
- foreach ($subTypes as $subType) {
- if (! $subType instanceof ScalarType) {
+ if (! $node->isValid()) {
continue;
}
- if (! $this->enableFlexibleCasting) {
- continue;
- }
+ $all[] = $node;
- if ($subType->canCast($source)) {
- return $subType;
+ if ($subType instanceof InterfaceType || $subType instanceof ClassType || $subType instanceof ShapedArrayType) {
+ $structs[] = $node;
+ } elseif ($subType instanceof ScalarType) {
+ $scalars[] = $node;
}
}
- throw new CannotResolveTypeFromUnion($source, $type);
- }
-
- private function tryToBuildClassNode(UnionType $type, Shell $shell, RootNodeBuilder $rootBuilder): ?TreeNode
- {
- $classTypes = array_filter(
- $type->types(),
- fn (Type $type) => $type instanceof ClassType,
- );
+ if ($all === []) {
+ throw new CannotResolveTypeFromUnion($shell->value(), $type);
+ }
- if (count($classTypes) === 0) {
- return null;
+ if (count($all) === 1) {
+ return $all[0];
}
- $objectBuilder = $this->objectBuilder($shell->value(), ...$classTypes);
+ if ($structs !== []) {
+ // Structs can be either an interface, a class or a shaped array.
+ // We prioritize the one with the most children, as it's the most
+ // specific type. If there are multiple types with the same number
+ // of children, we consider it as a collision.
+ $childrenCount = [];
- return $this->objectNodeBuilder->build($objectBuilder, $shell, $rootBuilder);
- }
+ foreach ($structs as $node) {
+ $childrenCount[count($node->children())][] = $node;
+ }
- private function objectBuilder(mixed $value, ClassType ...$types): ObjectBuilder
- {
- $builders = [];
+ krsort($childrenCount);
+
+ $first = reset($childrenCount);
+
+ if (count($first) === 1) {
+ return $first[0];
+ }
+ } elseif ($scalars !== []) {
+ // Sorting the scalar types by priority: int, float, string, bool.
+ $sorted = [];
+
+ foreach ($scalars as $node) {
+ if ($node->type() instanceof IntegerType) {
+ $sorted[IntegerType::class] = $node;
+ } elseif ($node->type() instanceof FloatType) {
+ $sorted[FloatType::class] = $node;
+ } elseif ($node->type() instanceof StringType) {
+ $sorted[StringType::class] = $node;
+ }
+ }
- foreach ($types as $type) {
- $class = $this->classDefinitionRepository->for($type);
+ if (isset($sorted[IntegerType::class])) {
+ return $sorted[IntegerType::class];
+ } elseif (isset($sorted[FloatType::class])) {
+ return $sorted[FloatType::class];
+ } elseif (isset($sorted[StringType::class])) {
+ return $sorted[StringType::class];
+ }
- $builders = [...$builders, ...$this->objectBuilderFactory->for($class)];
+ // @infection-ignore-all / We know this is a boolean, so we don't need to mutate the index
+ return $scalars[0];
}
- return new FilteredObjectBuilder($value, ...$builders);
+ throw new TooManyResolvedTypesFromUnion($type);
}
}
$value = $node->value();
foreach ($this->functions as $function) {
- $parameters = $function->definition()->parameters();
+ $parameters = $function->definition->parameters;
if (count($parameters) === 0) {
continue;
}
- $firstParameterType = $parameters->at(0)->type();
+ $firstParameterType = $parameters->at(0)->type;
if (! $firstParameterType->accepts($value)) {
continue;
}
- $value = ($function->callback())($value);
+ $value = ($function->callback)($value);
$node = $node->withValue($value);
}
public function __construct(ClassType $class, FunctionDefinition $function)
{
parent::__construct(
- "Cannot infer final class `{$class->className()}` with function `{$function->signature()}`.",
+ "Cannot infer final class `{$class->className()}` with function `$function->signature`.",
1671468163
);
}
$this->parameters = [
'allowed_types' => implode(
', ',
- // PHP8.1 First-class callable syntax
- array_map([TypeHelper::class, 'dump'], $unionType->types())
+ array_map(TypeHelper::dump(...), $unionType->types())
),
];
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Tree\Exception;
+
+use LogicException;
+
+/** @internal */
+final class InterfaceHasBothConstructorAndInfer extends LogicException
+{
+ /**
+ * @param interface-string $name
+ */
+ public function __construct(string $name)
+ {
+ parent::__construct(
+ "Interface `$name` is configured with at least one constructor but also has an infer configuration. Only one method can be used.",
+ 1711915749,
+ );
+ }
+}
public function __construct(string $name, FunctionDefinition $functionDefinition)
{
parent::__construct(
- "No implementation of `$name` found with return type `{$functionDefinition->returnType()->toString()}` of `{$functionDefinition->signature()}`.",
+ "No implementation of `$name` found with return type `{$functionDefinition->returnType->toString()}` of `$functionDefinition->signature`.",
1653990549
);
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Tree\Exception;
+
+use CuyZ\Valinor\Mapper\Tree\Message\ErrorMessage;
+use RuntimeException;
+
+/** @internal */
+final class SourceIsNotNull extends RuntimeException implements ErrorMessage
+{
+ private string $body;
+
+ public function __construct()
+ {
+ $this->body = 'Value {source_value} is not null.';
+
+ parent::__construct($this->body, 1710263908);
+ }
+
+ public function body(): string
+ {
+ return $this->body;
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Tree\Exception;
+
+use CuyZ\Valinor\Mapper\Tree\Message\ErrorMessage;
+use CuyZ\Valinor\Mapper\Tree\Message\HasParameters;
+use CuyZ\Valinor\Type\Types\UnionType;
+use CuyZ\Valinor\Utility\String\StringFormatter;
+use CuyZ\Valinor\Utility\TypeHelper;
+use RuntimeException;
+
+use function array_map;
+use function implode;
+
+/** @internal */
+final class TooManyResolvedTypesFromUnion extends RuntimeException implements ErrorMessage, HasParameters
+{
+ private string $body;
+
+ /** @var array<string, string> */
+ private array $parameters;
+
+ public function __construct(UnionType $unionType)
+ {
+ $this->parameters = [
+ 'allowed_types' => implode(
+ ', ',
+ array_map(TypeHelper::dump(...), $unionType->types())
+ ),
+ ];
+
+ $this->body = TypeHelper::containsObject($unionType)
+ ? 'Invalid value {source_value}, it matches two or more types from union: cannot take a decision.'
+ : 'Invalid value {source_value}, it matches two or more types from {allowed_types}: cannot take a decision.';
+
+ parent::__construct(StringFormatter::for($this), 1710262975);
+ }
+
+ public function body(): string
+ {
+ return $this->body;
+ }
+
+ public function parameters(): array
+ {
+ return $this->parameters;
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Mapper\Tree\Exception;
+
+use CuyZ\Valinor\Type\Types\UnresolvableType;
+use LogicException;
+
+/** @internal */
+final class UnresolvableShellType extends LogicException
+{
+ public function __construct(UnresolvableType $type)
+ {
+ parent::__construct($type->message());
+ }
+}
'Value {source_value} does not match string value {expected_value}.' => [
'en' => 'Value {source_value} does not match string value {expected_value}.',
],
+ 'Value {source_value} is not null.' => [
+ 'en' => 'Value {source_value} is not null.',
+ ],
'Value {source_value} is not a valid boolean.' => [
'en' => 'Value {source_value} is not a valid boolean.',
],
'Invalid value {source_value}.' => [
'en' => 'Invalid value {source_value}.',
],
+ 'Invalid value {source_value}, it matches at least two types from union.' => [
+ 'en' => 'Invalid value {source_value}, it matches at least two types from union.',
+ ],
+ 'Invalid value {source_value}, it matches at least two types from {allowed_types}.' => [
+ 'en' => 'Invalid value {source_value}, it matches at least two types from {allowed_types}.',
+ ],
'Invalid sequential key {key}, expected {expected}.' => [
'en' => 'Invalid sequential key {key}, expected {expected}.',
],
/**
* @psalm-pure
*
- * PHP8.1 intersection
* @return MessageType&HasCode&HasParameters
*/
- public function build(): Message
+ public function build(): Message&HasCode&HasParameters
{
/** @var MessageType&HasCode&HasParameters */
return $this->isError
: $this->buildMessage();
}
- private function buildMessage(): Message
+ private function buildMessage(): Message&HasCode&HasParameters
{
return new class ($this->body, $this->code, $this->parameters) implements Message, HasCode, HasParameters {
/**
};
}
- private function buildErrorMessage(): ErrorMessage
+ private function buildErrorMessage(): ErrorMessage&HasCode&HasParameters
{
return new class ($this->body, $this->code, $this->parameters) extends RuntimeException implements ErrorMessage, HasCode, HasParameters {
/**
/** @internal */
final class UserlandError extends RuntimeException implements ErrorMessage
{
- /**
- * PHP8.1 intersection
- * @return Message&Throwable
- */
- public static function from(Throwable $message): Message
+ public static function from(Throwable $message): Message&Throwable
{
// @infection-ignore-all
return $message instanceof Message
namespace CuyZ\Valinor\Mapper\Tree;
use CuyZ\Valinor\Definition\Attributes;
-use CuyZ\Valinor\Definition\AttributesContainer;
+use CuyZ\Valinor\Mapper\Tree\Exception\UnresolvableShellType;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\UnresolvableType;
private function __construct(private Type $type)
{
- assert(! $type instanceof UnresolvableType);
+ if ($type instanceof UnresolvableType) {
+ throw new UnresolvableShellType($type);
+ }
}
public static function root(Type $type, mixed $value): self
public function attributes(): Attributes
{
- return $this->attributes ?? AttributesContainer::empty();
+ return $this->attributes ?? Attributes::empty();
}
public function path(): string
use CuyZ\Valinor\Definition\ParameterDefinition;
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
+use CuyZ\Valinor\Mapper\Exception\TypeErrorDuringArgumentsMapping;
use CuyZ\Valinor\Mapper\Tree\Builder\RootNodeBuilder;
+use CuyZ\Valinor\Mapper\Tree\Exception\UnresolvableShellType;
use CuyZ\Valinor\Mapper\Tree\Shell;
use CuyZ\Valinor\Type\Types\ShapedArrayElement;
use CuyZ\Valinor\Type\Types\ShapedArrayType;
$elements = array_map(
fn (ParameterDefinition $parameter) => new ShapedArrayElement(
- new StringValueType($parameter->name()),
- $parameter->type(),
- $parameter->isOptional()
+ new StringValueType($parameter->name),
+ $parameter->type,
+ $parameter->isOptional
),
- iterator_to_array($function->parameters())
+ iterator_to_array($function->parameters)
);
$type = new ShapedArrayType(...$elements);
$shell = Shell::root($type, $source);
- $node = $this->nodeBuilder->build($shell);
+ try {
+ $node = $this->nodeBuilder->build($shell);
+ } catch (UnresolvableShellType $exception) {
+ throw new TypeErrorDuringArgumentsMapping($function, $exception);
+ }
if (! $node->isValid()) {
throw new ArgumentsMapperError($function, $node->node());
namespace CuyZ\Valinor\Mapper;
use CuyZ\Valinor\Mapper\Exception\InvalidMappingTypeSignature;
+use CuyZ\Valinor\Mapper\Exception\TypeErrorDuringMapping;
use CuyZ\Valinor\Mapper\Tree\Builder\RootNodeBuilder;
use CuyZ\Valinor\Mapper\Tree\Builder\TreeNode;
+use CuyZ\Valinor\Mapper\Tree\Exception\UnresolvableShellType;
use CuyZ\Valinor\Mapper\Tree\Shell;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Parser\TypeParser;
$shell = Shell::root($type, $source);
- return $this->nodeBuilder->build($shell);
+ try {
+ return $this->nodeBuilder->build($shell);
+ } catch (UnresolvableShellType $exception) {
+ throw new TypeErrorDuringMapping($type, $exception);
+ }
}
}
* case CASE_D = 'BAR_VALUE_2';
*
* /**
- * * @param 'FOO'|'BAR' $type
- * * @param int<1, 2> $number
+ * * \@param 'FOO'|'BAR' $type
+ * * \@param int<1, 2> $number
* * /
* public static function fromMatrix(string $type, int $number): self
* {
* transformer will be called. Default priority is 0.
*
* An attribute on a property or a class can act as a transformer if:
- * 1. It is callable (they define an `__invoke` method)
- * 2. It is registered using `registerTransformer()`
+ * 1. It defines a `normalize` or `normalizeKey` method.
+ * 2. It is registered using either the `registerTransformer()` method or
+ * the following attribute: @see \CuyZ\Valinor\Normalizer\AsTransformer
*
* Example:
*
* priority: -100 // Negative priority: transformer is called early
* )
*
- * // Transformer attributes must be registered before they are used by
- * // the normalizer.
- * ->registerTransformer(SomeAttribute::class)
+ * // External transformer attributes must be registered before they are
+ * // used by the normalizer.
+ * ->registerTransformer(\Some\External\TransformerAttribute::class)
*
* ->normalizer()
* ->normalize('Hello world'); // HELLO WORLD?!
{
$value = $this->transformer->transform($value);
+ /** @var array<mixed>|scalar|null */
return $this->normalizeIterator($value);
}
- /**
- * @param iterable<mixed>|scalar|null $value
- * @return array<mixed>|scalar|null
- */
private function normalizeIterator(mixed $value): mixed
{
if (is_iterable($value)) {
$value = iterator_to_array($value);
}
- // PHP8.1 First-class callable syntax
- $value = array_map([$this, 'normalizeIterator'], $value);
+ $value = array_map($this->normalizeIterator(...), $value);
}
return $value;
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Normalizer;
+
+use Attribute;
+
+/**
+ * This attribute can be used to automatically register a transformer attribute.
+ *
+ * When there is no control over the transformer attribute class, the following
+ * method can be used: @see \CuyZ\Valinor\MapperBuilder::registerTransformer
+ *
+ * ```php
+ * namespace My\App;
+ *
+ * #[\CuyZ\Valinor\Normalizer\AsTransformer]
+ * #[\Attribute(\Attribute::TARGET_PROPERTY)]
+ * final class DateTimeFormat
+ * {
+ * public function __construct(private string $format) {}
+ *
+ * public function normalize(\DateTimeInterface $date): string
+ * {
+ * return $date->format($this->format);
+ * }
+ * }
+ *
+ * final readonly class Event
+ * {
+ * public function __construct(
+ * public string $eventName,
+ * #[\My\App\DateTimeFormat('Y/m/d')]
+ * public \DateTimeInterface $date,
+ * ) {}
+ * }
+ *
+ * (new \CuyZ\Valinor\MapperBuilder())
+ * ->normalizer(\CuyZ\Valinor\Normalizer\Format::array())
+ * ->normalize(new \My\App\Event(
+ * eventName: 'Release of legendary album',
+ * date: new \DateTimeImmutable('1971-11-08'),
+ * ));
+ *
+ * // [
+ * // 'eventName' => 'Release of legendary album',
+ * // 'date' => '1971/11/08',
+ * // ]
+ * ```
+ *
+ * @api
+ */
+#[Attribute(Attribute::TARGET_CLASS)]
+final class AsTransformer {}
namespace CuyZ\Valinor\Normalizer\Exception;
-use CuyZ\Valinor\Definition\FunctionDefinition;
+use CuyZ\Valinor\Definition\MethodDefinition;
use LogicException;
/** @internal */
final class KeyTransformerHasTooManyParameters extends LogicException
{
- public function __construct(FunctionDefinition $function)
+ public function __construct(MethodDefinition $method)
{
parent::__construct(
- "Key transformer must have at most 1 parameter, {$function->parameters()->count()} given for `{$function->signature()}`.",
+ "Key transformer must have at most 1 parameter, {$method->parameters->count()} given for `$method->signature`.",
1701701102,
);
}
namespace CuyZ\Valinor\Normalizer\Exception;
-use CuyZ\Valinor\Definition\FunctionDefinition;
+use CuyZ\Valinor\Definition\MethodDefinition;
use LogicException;
/** @internal */
final class KeyTransformerParameterInvalidType extends LogicException
{
- public function __construct(FunctionDefinition $function)
+ public function __construct(MethodDefinition $method)
{
parent::__construct(
- "Key transformer parameter must be a string, {$function->parameters()->at(0)->type()->toString()} given for `{$function->signature()}`.",
+ "Key transformer parameter must be a string, {$method->parameters->at(0)->type->toString()} given for `$method->signature`.",
1701706316,
);
}
namespace CuyZ\Valinor\Normalizer\Exception;
use CuyZ\Valinor\Definition\FunctionDefinition;
+use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Type\Type;
use LogicException;
/** @internal */
final class TransformerHasInvalidCallableParameter extends LogicException
{
- public function __construct(FunctionDefinition $function, Type $parameterType)
+ public function __construct(MethodDefinition|FunctionDefinition $method, Type $parameterType)
{
parent::__construct(
- "Transformer's second parameter must be a callable, `{$parameterType->toString()}` given for `{$function->signature()}`.",
+ "Transformer's second parameter must be a callable, `{$parameterType->toString()}` given for `$method->signature`.",
1695065710,
);
}
namespace CuyZ\Valinor\Normalizer\Exception;
use CuyZ\Valinor\Definition\FunctionDefinition;
+use CuyZ\Valinor\Definition\MethodDefinition;
use LogicException;
/** @internal */
final class TransformerHasNoParameter extends LogicException
{
- public function __construct(FunctionDefinition $function)
+ public function __construct(MethodDefinition|FunctionDefinition $method)
{
parent::__construct(
- "Transformer must have at least one parameter, none given for `{$function->signature()}`.",
+ "Transformer must have at least one parameter, none given for `$method->signature`.",
1695064946,
);
}
namespace CuyZ\Valinor\Normalizer\Exception;
use CuyZ\Valinor\Definition\FunctionDefinition;
+use CuyZ\Valinor\Definition\MethodDefinition;
use LogicException;
/** @internal */
final class TransformerHasTooManyParameters extends LogicException
{
- public function __construct(FunctionDefinition $function)
+ public function __construct(MethodDefinition|FunctionDefinition $method)
{
parent::__construct(
- "Transformer must have at most 2 parameters, {$function->parameters()->count()} given for `{$function->signature()}`.",
+ "Transformer must have at most 2 parameters, {$method->parameters->count()} given for `$method->signature`.",
1695065433,
);
}
return new self(ArrayNormalizer::class);
}
+ /**
+ * Allows a normalizer to format an input to JSON syntax.
+ *
+ * ```php
+ * namespace My\App;
+ *
+ * $normalizer = (new \CuyZ\Valinor\MapperBuilder())
+ * ->normalizer(\CuyZ\Valinor\Normalizer\Format::json());
+ *
+ * $userAsJson = $normalizer->normalize(
+ * new \My\App\User(
+ * name: 'John Doe',
+ * age: 42,
+ * country: new \My\App\Country(
+ * name: 'France',
+ * code: 'FR',
+ * ),
+ * )
+ * );
+ *
+ * // `$userAsJson` is a valid JSON string representing the data:
+ * // {"name":"John Doe","age":42,"country":{"name":"France","code":"FR"}}
+ * ```
+ *
+ * @return self<JsonNormalizer>
+ */
+ public static function json(): self
+ {
+ return new self(JsonNormalizer::class);
+ }
+
/**
* @param class-string<T> $type
*/
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Normalizer\Formatter\Exception;
+
+use RuntimeException;
+
+/** @internal */
+final class CannotFormatInvalidTypeToJson extends RuntimeException
+{
+ public function __construct(mixed $value)
+ {
+ $type = get_debug_type($value);
+
+ parent::__construct(
+ "Value of type `$type` cannot be normalized to JSON.",
+ 1704749897,
+ );
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Normalizer\Formatter;
+
+use CuyZ\Valinor\Normalizer\Formatter\Exception\CannotFormatInvalidTypeToJson;
+use Generator;
+
+use function array_is_list;
+use function fwrite;
+use function is_array;
+use function is_bool;
+use function is_iterable;
+use function is_null;
+use function is_scalar;
+use function json_encode;
+
+use const JSON_THROW_ON_ERROR;
+
+/** @internal */
+final class JsonFormatter implements StreamFormatter
+{
+ /**
+ * @param resource $resource
+ */
+ public function __construct(
+ private mixed $resource,
+ private int $jsonEncodingOptions,
+ ) {}
+
+ public function format(mixed $value): void
+ {
+ if (is_null($value)) {
+ $this->write('null');
+ } elseif (is_bool($value)) {
+ $this->write($value ? 'true' : 'false');
+ } elseif (is_scalar($value)) {
+ /**
+ * @phpstan-ignore-next-line / Due to the new json encoding options feature, it is not possible to let SA
+ * tools understand that JSON_THROW_ON_ERROR is always set.
+ */
+ $this->write(json_encode($value, $this->jsonEncodingOptions));
+ } elseif (is_iterable($value)) {
+ // Note: when a generator is formatted, it is considered as a list
+ // if its first key is 0. This is done early because the first JSON
+ // character for an array differs from the one for an object, and we
+ // need to know that before actually looping on the generator.
+ //
+ // For generators having a first key of 0 and inconsistent keys
+ // afterward, this leads to a JSON array being written, while it
+ // should have been an object. This is a trade-off we accept,
+ // considering most generators starting at 0 are actually lists.
+ $isList = ($value instanceof Generator && $value->key() === 0)
+ || (is_array($value) && array_is_list($value));
+
+ $isFirst = true;
+
+ $this->write($isList ? '[' : '{');
+
+ foreach ($value as $key => $val) {
+ if (! $isFirst) {
+ $this->write(',');
+ }
+
+ $isFirst = false;
+
+ if (! $isList) {
+ $key = json_encode((string)$key, $this->jsonEncodingOptions);
+
+ $this->write($key . ':');
+ }
+
+ $this->format($val);
+ }
+
+ $this->write($isList ? ']' : '}');
+ } else {
+ throw new CannotFormatInvalidTypeToJson($value);
+ }
+ }
+
+ public function resource(): mixed
+ {
+ return $this->resource;
+ }
+
+ private function write(string $content): void
+ {
+ fwrite($this->resource, $content);
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Normalizer\Formatter;
+
+/** @internal */
+interface StreamFormatter
+{
+ public function format(mixed $value): void;
+
+ /**
+ * @return resource
+ */
+ public function resource(): mixed;
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Normalizer;
+
+use CuyZ\Valinor\Normalizer\Formatter\JsonFormatter;
+use CuyZ\Valinor\Normalizer\Transformer\RecursiveTransformer;
+use RuntimeException;
+
+use function fclose;
+use function fopen;
+use function get_debug_type;
+use function is_resource;
+use function stream_get_contents;
+
+use const JSON_HEX_AMP;
+use const JSON_HEX_APOS;
+use const JSON_HEX_QUOT;
+use const JSON_HEX_TAG;
+use const JSON_INVALID_UTF8_IGNORE;
+use const JSON_INVALID_UTF8_SUBSTITUTE;
+use const JSON_NUMERIC_CHECK;
+use const JSON_PRESERVE_ZERO_FRACTION;
+use const JSON_THROW_ON_ERROR;
+use const JSON_UNESCAPED_LINE_TERMINATORS;
+use const JSON_UNESCAPED_SLASHES;
+use const JSON_UNESCAPED_UNICODE;
+
+/**
+ * @api
+ *
+ * @implements Normalizer<string>
+ */
+final class JsonNormalizer implements Normalizer
+{
+ private const ACCEPTABLE_JSON_OPTIONS = JSON_HEX_QUOT
+ | JSON_HEX_TAG
+ | JSON_HEX_AMP
+ | JSON_HEX_APOS
+ | JSON_INVALID_UTF8_IGNORE
+ | JSON_INVALID_UTF8_SUBSTITUTE
+ | JSON_NUMERIC_CHECK
+ | JSON_PRESERVE_ZERO_FRACTION
+ | JSON_UNESCAPED_LINE_TERMINATORS
+ | JSON_UNESCAPED_SLASHES
+ | JSON_UNESCAPED_UNICODE
+ | JSON_THROW_ON_ERROR;
+
+ private RecursiveTransformer $transformer;
+
+ private int $jsonEncodingOptions;
+
+ /**
+ * Internal note
+ * -------------
+ *
+ * We could use the `int-mask-of<JsonNormalizer::JSON_*>` annotation
+ * to let PHPStan infer the type of the accepted options, but some caveats
+ * were found:
+ * - SA tools are not able to infer that we end up having only accepted
+ * options. Might be fixed with https://github.com/phpstan/phpstan/issues/9384
+ * for PHPStan but Psalm does have some (not all) issues as well.
+ * - Using this annotation provokes *severe* performance issues when
+ * running PHPStan analysis, therefore it is preferable to avoid it.
+ */
+ public function __construct(
+ RecursiveTransformer $transformer,
+ int $jsonEncodingOptions = JSON_THROW_ON_ERROR,
+ ) {
+ $this->transformer = $transformer;
+ $this->jsonEncodingOptions = (self::ACCEPTABLE_JSON_OPTIONS & $jsonEncodingOptions) | JSON_THROW_ON_ERROR;
+ }
+
+ /**
+ * By default, the JSON normalizer will only use `JSON_THROW_ON_ERROR` to
+ * encode non-boolean scalar values. There might be use-cases where projects
+ * will need flags like `JSON_JSON_PRESERVE_ZERO_FRACTION`.
+ *
+ * This can be achieved by passing these flags to this method:
+ *
+ * ```php
+ * $normalizer = (new \CuyZ\Valinor\MapperBuilder())
+ * ->normalizer(\CuyZ\Valinor\Normalizer\Format::json())
+ * ->withOptions(\JSON_PRESERVE_ZERO_FRACTION);
+ *
+ * $lowerManhattanAsJson = $normalizer->normalize(
+ * new \My\App\Coordinates(
+ * longitude: 40.7128,
+ * latitude: -74.0000
+ * )
+ * );
+ *
+ * // `$lowerManhattanAsJson` is a valid JSON string representing the data:
+ * // {"longitude":40.7128,"latitude":-74.0000}
+ * ```
+ */
+ public function withOptions(int $options): self
+ {
+ return new self($this->transformer, $options);
+ }
+
+ public function normalize(mixed $value): string
+ {
+ $value = $this->transformer->transform($value);
+
+ /** @var resource $resource */
+ $resource = fopen('php://memory', 'w');
+
+ (new JsonFormatter($resource, $this->jsonEncodingOptions))->format($value);
+
+ rewind($resource);
+
+ /** @var string */
+ $json = stream_get_contents($resource);
+
+ fclose($resource);
+
+ return $json;
+ }
+
+ /**
+ * Returns a new normalizer that will write the JSON to the given resource
+ * instead of returning a string.
+ *
+ * A benefit of streaming the data to a PHP resource is that it may be more
+ * memory-efficient when using generators — for instance when querying a
+ * database:
+ *
+ * ```php
+ * // In this example, we assume that the result of the query below is a
+ * // generator, every entry will be yielded one by one, instead of
+ * // everything being loaded in memory at once.
+ * $users = $database->execute('SELECT * FROM users');
+ *
+ * $file = fopen('path/to/some_file.json', 'w');
+ *
+ * $normalizer = (new \CuyZ\Valinor\MapperBuilder())
+ * ->normalizer(\CuyZ\Valinor\Normalizer\Format::json())
+ * ->streamTo($file);
+ *
+ * // Even if there are thousands of users, memory usage will be kept low
+ * // when writing JSON into the file.
+ * $normalizer->normalize($users);
+ * ```
+ *
+ * @param resource $resource
+ */
+ public function streamTo(mixed $resource): StreamNormalizer
+ {
+ // This check is there to help people that do not use static analyzers.
+ // @phpstan-ignore-next-line
+ if (! is_resource($resource)) {
+ throw new RuntimeException('Expected a valid resource, got ' . get_debug_type($resource));
+ }
+
+ return new StreamNormalizer($this->transformer, new JsonFormatter($resource, $this->jsonEncodingOptions));
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Normalizer;
+
+use CuyZ\Valinor\Normalizer\Formatter\StreamFormatter;
+use CuyZ\Valinor\Normalizer\Transformer\RecursiveTransformer;
+
+/**
+ * @api
+ *
+ * @implements Normalizer<resource>
+ */
+final class StreamNormalizer implements Normalizer
+{
+ public function __construct(
+ private RecursiveTransformer $transformer,
+ private StreamFormatter $formatter,
+ ) {}
+
+ public function normalize(mixed $value): mixed
+ {
+ $value = $this->transformer->transform($value);
+
+ $this->formatter->format($value);
+
+ return $this->formatter->resource();
+ }
+}
namespace CuyZ\Valinor\Normalizer\Transformer;
-use CuyZ\Valinor\Definition\FunctionDefinition;
-use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
+use CuyZ\Valinor\Definition\AttributeDefinition;
+use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Normalizer\Exception\KeyTransformerHasTooManyParameters;
use CuyZ\Valinor\Normalizer\Exception\KeyTransformerParameterInvalidType;
use CuyZ\Valinor\Type\StringType;
/** @var array<string, true> */
private array $keyTransformerCheck = [];
- public function __construct(
- private FunctionDefinitionRepository $functionDefinitionRepository,
- ) {}
-
/**
- * @param list<object> $attributes
+ * @param list<AttributeDefinition> $attributes
*/
public function transformKey(string|int $key, array $attributes): string|int
{
foreach ($attributes as $attribute) {
- if (! method_exists($attribute, 'normalizeKey')) {
+ if (! $attribute->class->methods->has('normalizeKey')) {
continue;
}
- // PHP8.1 First-class callable syntax
- $function = $this->functionDefinitionRepository->for([$attribute, 'normalizeKey']);
+ $method = $attribute->class->methods->get('normalizeKey');
- $this->checkKeyTransformer($function);
+ $this->checkKeyTransformer($method);
- if ($function->parameters()->count() === 0 || $function->parameters()->at(0)->type()->accepts($key)) {
- $key = $attribute->normalizeKey($key);
+ if ($method->parameters->count() === 0 || $method->parameters->at(0)->type->accepts($key)) {
+ $key = $attribute->instantiate()->normalizeKey($key); // @phpstan-ignore-line / We know the method exists
}
}
return $key;
}
- private function checkKeyTransformer(FunctionDefinition $function): void
+ private function checkKeyTransformer(MethodDefinition $method): void
{
- if (isset($this->keyTransformerCheck[$function->signature()])) {
+ if (isset($this->keyTransformerCheck[$method->signature])) {
return;
}
// @infection-ignore-all
- $this->keyTransformerCheck[$function->signature()] = true;
+ $this->keyTransformerCheck[$method->signature] = true;
- $parameters = $function->parameters();
+ $parameters = $method->parameters;
if ($parameters->count() > 1) {
- throw new KeyTransformerHasTooManyParameters($function);
+ throw new KeyTransformerHasTooManyParameters($method);
}
if ($parameters->count() > 0) {
- $type = $parameters->at(0)->type();
+ $type = $parameters->at(0)->type;
if (! $type instanceof StringType) {
- throw new KeyTransformerParameterInvalidType($function);
+ throw new KeyTransformerParameterInvalidType($method);
}
}
}
namespace CuyZ\Valinor\Normalizer\Transformer;
+use ArrayObject;
use BackedEnum;
use Closure;
+use CuyZ\Valinor\Definition\AttributeDefinition;
use CuyZ\Valinor\Definition\Attributes;
use CuyZ\Valinor\Definition\Repository\ClassDefinitionRepository;
+use CuyZ\Valinor\Normalizer\AsTransformer;
use CuyZ\Valinor\Normalizer\Exception\CircularReferenceFoundDuringNormalization;
use CuyZ\Valinor\Normalizer\Exception\TypeUnhandledByNormalizer;
use CuyZ\Valinor\Type\Types\NativeClassType;
use DateTimeInterface;
+use DateTimeZone;
use Generator;
-use ReflectionClass;
use stdClass;
use UnitEnum;
use WeakMap;
use function array_map;
-use function array_reverse;
use function get_object_vars;
+use function is_a;
use function is_array;
use function is_iterable;
/**
* @param WeakMap<object, true> $references
- * @param list<object> $attributes
+ * @param list<AttributeDefinition> $attributes
* @return iterable<mixed>|scalar|null
*/
private function doTransform(mixed $value, WeakMap $references, array $attributes = []): mixed
throw new CircularReferenceFoundDuringNormalization($value);
}
+ $references = clone $references;
+
// @infection-ignore-all
$references[$value] = true;
}
- if ($this->transformers === [] && $this->transformerAttributes === []) {
- return $this->defaultTransformer($value, $references);
- }
-
- if ($this->transformerAttributes !== [] && is_object($value)) {
- $classAttributes = $this->classDefinitionRepository->for(NativeClassType::for($value::class))->attributes();
+ if (is_object($value)) {
+ $classAttributes = $this->classDefinitionRepository->for(new NativeClassType($value::class))->attributes;
+ $classAttributes = $this->filterAttributes($classAttributes);
$attributes = [...$attributes, ...$classAttributes];
}
+ if ($this->transformers === [] && $attributes === []) {
+ return $this->defaultTransformer($value, $references);
+ }
+
return $this->valueTransformers->transform(
$value,
$attributes,
return $value->format('Y-m-d\\TH:i:s.uP'); // RFC 3339
}
- if ($value::class === stdClass::class) {
+ if ($value instanceof DateTimeZone) {
+ return $value->getName();
+ }
+
+ if ($value::class === stdClass::class || $value instanceof ArrayObject) {
return array_map(
fn (mixed $value) => $this->doTransform($value, $references),
(array)$value
$values = (fn () => get_object_vars($this))->call($value);
- // @infection-ignore-all
- if (PHP_VERSION_ID < 8_01_00) {
- // In PHP 8.1, behavior changed for `get_object_vars` function:
- // the sorting order was taking children properties first, now
- // it takes parents properties first. This code is a temporary
- // workaround to keep the same behavior in PHP 8.0 and later
- // versions.
- $sorted = [];
-
- $parents = array_reverse(class_parents($value));
- $parents[] = $value::class;
-
- foreach ($parents as $parent) {
- foreach ((new ReflectionClass($parent))->getProperties() as $property) {
- if (! isset($values[$property->name])) {
- continue;
- }
-
- $sorted[$property->name] = $values[$property->name];
- }
- }
-
- $values = $sorted;
- }
-
$transformed = [];
- $class = $this->classDefinitionRepository->for(NativeClassType::for($value::class));
+ $class = $this->classDefinitionRepository->for(new NativeClassType($value::class));
foreach ($values as $key => $subValue) {
- $attributes = $this->filterAttributes($class->properties()->get($key)->attributes());
+ $attributes = $this->filterAttributes($class->properties->get($key)->attributes)->toArray();
$key = $this->keyTransformers->transformKey($key, $attributes);
throw new TypeUnhandledByNormalizer($value);
}
- /**
- * @return list<object>
- */
- private function filterAttributes(Attributes $attributes): array
+ private function filterAttributes(Attributes $attributes): Attributes
{
- $filteredAttributes = [];
+ return $attributes->filter(function (AttributeDefinition $attribute) {
+ if ($attribute->class->attributes->has(AsTransformer::class)) {
+ return true;
+ }
- foreach ($attributes as $attribute) {
foreach ($this->transformerAttributes as $transformerAttribute) {
- if ($attribute instanceof $transformerAttribute) {
- $filteredAttributes[] = $attribute;
- break;
+ if (is_a($attribute->class->type->className(), $transformerAttribute, true)) {
+ return true;
}
}
- }
- return $filteredAttributes;
+ return false;
+ });
}
}
namespace CuyZ\Valinor\Normalizer\Transformer;
+use CuyZ\Valinor\Definition\AttributeDefinition;
use CuyZ\Valinor\Definition\FunctionDefinition;
+use CuyZ\Valinor\Definition\MethodDefinition;
use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository;
use CuyZ\Valinor\Normalizer\Exception\TransformerHasInvalidCallableParameter;
use CuyZ\Valinor\Normalizer\Exception\TransformerHasNoParameter;
use function array_shift;
use function call_user_func;
-use function method_exists;
/** @internal */
final class ValueTransformersHandler
) {}
/**
- * @param array<object> $attributes
+ * @param array<AttributeDefinition> $attributes
* @param list<callable> $transformers
* @return array<mixed>|scalar|null
*/
/**
* @param list<callable> $transformers
- * @param list<object> $attributes
+ * @param array<AttributeDefinition> $attributes
*/
private function next(array $transformers, mixed $value, array $attributes, callable $defaultTransformer): callable
{
$this->checkTransformer($function);
- if (! $function->parameters()->at(0)->type()->accepts($value)) {
+ if (! $function->parameters->at(0)->type->accepts($value)) {
return $this->next($transformers, $value, [], $defaultTransformer);
}
}
/**
- * @param array<object> $attributes
+ * @param array<AttributeDefinition> $attributes
*/
private function nextAttribute(mixed $value, array $attributes, callable $next): callable
{
return $next;
}
- if (! method_exists($attribute, 'normalize')) {
+ if (! $attribute->class->methods->has('normalize')) {
return $this->nextAttribute($value, $attributes, $next);
}
- // PHP8.1 First-class callable syntax
- $function = $this->functionDefinitionRepository->for([$attribute, 'normalize']);
+ $method = $attribute->class->methods->get('normalize');
- $this->checkTransformer($function);
+ $this->checkTransformer($method);
- if (! $function->parameters()->at(0)->type()->accepts($value)) {
+ if (! $method->parameters->at(0)->type->accepts($value)) {
return $this->nextAttribute($value, $attributes, $next);
}
- return fn () => $attribute->normalize($value, fn () => call_user_func($this->nextAttribute($value, $attributes, $next)));
+ // @phpstan-ignore-next-line / We know the method exists
+ return fn () => $attribute->instantiate()->normalize(
+ $value,
+ fn () => call_user_func($this->nextAttribute($value, $attributes, $next))
+ );
}
- private function checkTransformer(FunctionDefinition $function): void
+ private function checkTransformer(MethodDefinition|FunctionDefinition $method): void
{
- if (isset($this->transformerCheck[$function->signature()])) {
+ if (isset($this->transformerCheck[$method->signature])) {
return;
}
// @infection-ignore-all
- $this->transformerCheck[$function->signature()] = true;
+ $this->transformerCheck[$method->signature] = true;
- $parameters = $function->parameters();
+ $parameters = $method->parameters;
if ($parameters->count() === 0) {
- throw new TransformerHasNoParameter($function);
+ throw new TransformerHasNoParameter($method);
}
if ($parameters->count() > 2) {
- throw new TransformerHasTooManyParameters($function);
+ throw new TransformerHasTooManyParameters($method);
}
- if ($parameters->count() > 1 && ! $parameters->at(1)->type() instanceof CallableType) {
- throw new TransformerHasInvalidCallableParameter($function, $parameters->at(1)->type());
+ if ($parameters->count() > 1 && ! $parameters->at(1)->type instanceof CallableType) {
+ throw new TransformerHasInvalidCallableParameter($method, $parameters->at(1)->type);
}
}
}
public function isMatchedBy(Type $other): bool;
/**
- * @return Type[]
+ * @return non-empty-list<Type>
*/
- public function types(): iterable;
+ public function types(): array;
}
interface GenericType extends CompositeType
{
/**
- * @return array<string, Type>
+ * @return class-string
+ */
+ public function className(): string;
+
+ /**
+ * @return array<non-empty-string, Type>
*/
public function generics(): array;
}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Generic;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use ReflectionClass;
-use RuntimeException;
-
-/** @internal */
-final class ExtendTagTypeError extends RuntimeException implements InvalidType
-{
- /**
- * @param ReflectionClass<object> $reflection
- */
- public function __construct(ReflectionClass $reflection, InvalidType $previous)
- {
- parent::__construct(
- "The `@extends` tag of the class `$reflection->name` is not valid: {$previous->getMessage()}",
- 1670193574,
- $previous
- );
- }
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Generic;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use CuyZ\Valinor\Type\Type;
-use ReflectionClass;
-use RuntimeException;
-
-/** @internal */
-final class InvalidExtendTagClassName extends RuntimeException implements InvalidType
-{
- /**
- * @param ReflectionClass<object> $reflection
- */
- public function __construct(ReflectionClass $reflection, Type $invalidExtendTag)
- {
- /** @var ReflectionClass<object> $parentClass */
- $parentClass = $reflection->getParentClass();
-
- parent::__construct(
- "The `@extends` tag of the class `$reflection->name` has invalid class `{$invalidExtendTag->toString()}`, it should be `$parentClass->name`.",
- 1670183564
- );
- }
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Generic;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use CuyZ\Valinor\Type\Type;
-use ReflectionClass;
-use RuntimeException;
-
-/** @internal */
-final class InvalidExtendTagType extends RuntimeException implements InvalidType
-{
- /**
- * @param ReflectionClass<object> $reflection
- */
- public function __construct(ReflectionClass $reflection, Type $invalidExtendTag)
- {
- /** @var ReflectionClass<object> $parentClass */
- $parentClass = $reflection->getParentClass();
-
- parent::__construct(
- "The `@extends` tag of the class `$reflection->name` has invalid type `{$invalidExtendTag->toString()}`, it should be `{$parentClass->name}`.",
- 1670181134
- );
- }
-}
/**
* @param class-string $className
* @param Type[] $generics
- * @param Type[] $templates
+ * @param array<non-empty-string> $templates
*/
public function __construct(string $className, array $generics, array $templates)
{
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Exception\Generic;
-
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use ReflectionClass;
-use RuntimeException;
-
-/** @internal */
-final class SeveralExtendTagsFound extends RuntimeException implements InvalidType
-{
- /**
- * @param ReflectionClass<object> $reflection
- */
- public function __construct(ReflectionClass $reflection)
- {
- parent::__construct(
- "Only one `@extends` tag should be set for the class `$reflection->name`.",
- 1670195494
- );
- }
-}
namespace CuyZ\Valinor\Type\Parser\Exception\Iterable;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
+use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ShapedArrayElement;
use RuntimeException;
/**
* @param ShapedArrayElement[] $elements
*/
- public function __construct(array $elements)
+ public function __construct(array $elements, Type|null|false $unsealedType = null)
{
$signature = 'array{' . implode(', ', array_map(fn (ShapedArrayElement $element) => $element->toString(), $elements));
+ if ($unsealedType === false) {
+ $signature .= ', ...';
+ } elseif ($unsealedType instanceof Type) {
+ $signature .= ', ...' . $unsealedType->toString();
+ }
+
parent::__construct(
"Missing closing curly bracket in shaped array signature `$signature`.",
1631283658
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Exception\Iterable;
+
+use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
+use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Type\Types\ShapedArrayElement;
+use RuntimeException;
+
+use function implode;
+
+/** @internal */
+final class ShapedArrayInvalidUnsealedType extends RuntimeException implements InvalidType
+{
+ /**
+ * @param ShapedArrayElement[] $elements
+ */
+ public function __construct(array $elements, Type $unsealedType)
+ {
+ $signature = 'array{';
+ $signature .= implode(', ', array_map(fn (ShapedArrayElement $element) => $element->toString(), $elements));
+ $signature .= ', ...' . $unsealedType->toString();
+ $signature .= '}';
+
+ parent::__construct(
+ "Invalid unsealed type `{$unsealedType->toString()}` in shaped array signature `$signature`, it should be a valid array.",
+ 1711618899,
+ );
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Exception\Iterable;
+
+use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
+use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Type\Types\ShapedArrayElement;
+use RuntimeException;
+
+use function implode;
+
+/** @internal */
+final class ShapedArrayUnexpectedTokenAfterSealedType extends RuntimeException implements InvalidType
+{
+ /**
+ * @param array<ShapedArrayElement> $elements
+ * @param list<Token> $unexpectedTokens
+ */
+ public function __construct(array $elements, Type $unsealedType, array $unexpectedTokens)
+ {
+ $unexpected = implode('', array_map(fn (Token $token) => $token->symbol(), $unexpectedTokens));
+
+ $signature = 'array{';
+ $signature .= implode(', ', array_map(fn (ShapedArrayElement $element) => $element->toString(), $elements));
+ $signature .= ', ...' . $unsealedType->toString();
+ $signature .= $unexpected;
+
+ parent::__construct(
+ "Unexpected `$unexpected` after sealed type in shaped array signature `$signature`, expected a `}`.",
+ 1711618958,
+ );
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Exception\Iterable;
+
+use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
+use CuyZ\Valinor\Type\Type;
+use RuntimeException;
+
+/** @internal */
+final class ShapedArrayWithoutElementsWithSealedType extends RuntimeException implements InvalidType
+{
+ public function __construct(Type $unsealedType)
+ {
+ $signature = "array{...{$unsealedType->toString()}}";
+
+ parent::__construct(
+ "Missing elements in shaped array signature `$signature`.",
+ 1711629845,
+ );
+ }
+}
use CuyZ\Valinor\Type\Parser\CachedParser;
use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeParserSpecification;
-use CuyZ\Valinor\Type\Parser\Lexer\AdvancedClassLexer;
use CuyZ\Valinor\Type\Parser\Lexer\NativeLexer;
+use CuyZ\Valinor\Type\Parser\Lexer\SpecificationsLexer;
use CuyZ\Valinor\Type\Parser\LexingParser;
use CuyZ\Valinor\Type\Parser\TypeParser;
public function get(TypeParserSpecification ...$specifications): TypeParser
{
- if (empty($specifications)) {
- return $this->nativeParser ??= $this->nativeParser();
+ if ($specifications === []) {
+ return $this->nativeParser ??= new CachedParser($this->buildTypeParser());
}
- $lexer = new NativeLexer();
- $lexer = new AdvancedClassLexer($lexer, $this);
-
- foreach ($specifications as $specification) {
- $lexer = $specification->transform($lexer);
- }
-
- return new LexingParser($lexer);
+ return $this->buildTypeParser(...$specifications);
}
- private function nativeParser(): TypeParser
+ private function buildTypeParser(TypeParserSpecification ...$specifications): TypeParser
{
- $lexer = new NativeLexer();
- $lexer = new AdvancedClassLexer($lexer, $this);
+ $lexer = new SpecificationsLexer($specifications);
+ $lexer = new NativeLexer($lexer);
+
$parser = new LexingParser($lexer);
- return new CachedParser($parser);
+ foreach ($specifications as $specification) {
+ $parser = $specification->manipulateParser($parser, $this);
+ }
+
+ return $parser;
}
}
namespace CuyZ\Valinor\Type\Parser\Factory\Specifications;
-use CuyZ\Valinor\Type\Parser\Lexer\AliasLexer;
-use CuyZ\Valinor\Type\Parser\Lexer\TypeLexer;
+use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\ObjectToken;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\TraversingToken;
+use CuyZ\Valinor\Type\Parser\TypeParser;
+use CuyZ\Valinor\Utility\Reflection\PhpParser;
+use CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionClass;
use ReflectionFunction;
use Reflector;
{
public function __construct(
/** @var ReflectionClass<object>|ReflectionFunction */
- private Reflector $reflection
+ private Reflector $reflection,
) {}
- public function transform(TypeLexer $lexer): TypeLexer
+ public function manipulateToken(TraversingToken $token): TraversingToken
{
- return new AliasLexer($lexer, $this->reflection);
+ $symbol = $token->symbol();
+
+ // Matches the case where a class extends a class with the same name but
+ // in a different namespace.
+ if ($symbol === $this->reflection->getShortName() && Reflection::classOrInterfaceExists($symbol)) {
+ return $token;
+ }
+
+ $alias = $this->resolveAlias($symbol);
+
+ if (strtolower($alias) !== strtolower($symbol)) {
+ /** @var class-string $alias */
+ return new ObjectToken($alias);
+ }
+
+ $namespaced = $this->resolveNamespaced($symbol);
+
+ if ($namespaced !== $symbol) {
+ /** @var class-string $namespaced */
+ return new ObjectToken($namespaced);
+ }
+
+ return $token;
+ }
+
+ public function manipulateParser(TypeParser $parser, TypeParserFactory $typeParserFactory): TypeParser
+ {
+ return $parser;
+ }
+
+ private function resolveAlias(string $symbol): string
+ {
+ $alias = $symbol;
+
+ $namespaceParts = explode('\\', $symbol);
+ $lastPart = array_shift($namespaceParts);
+
+ if ($lastPart) {
+ $alias = strtolower($lastPart);
+ }
+
+ $aliases = PhpParser::parseUseStatements($this->reflection);
+
+ if (! isset($aliases[$alias])) {
+ return $symbol;
+ }
+
+ if ($aliases[$alias] === $symbol) {
+ return $symbol;
+ }
+
+ $full = $aliases[$alias];
+
+ if (! empty($namespaceParts)) {
+ $full .= '\\' . implode('\\', $namespaceParts);
+ }
+
+ return $full;
+ }
+
+ private function resolveNamespaced(string $symbol): string
+ {
+ $reflection = $this->reflection;
+
+ if ($reflection instanceof ReflectionFunction) {
+ $classReflection = $reflection->getClosureScopeClass();
+
+ if ($classReflection && $classReflection->getFileName() === $reflection->getFileName()) {
+ $reflection = $classReflection;
+ }
+ }
+
+ $namespace = $reflection->getNamespaceName();
+
+ if (! $namespace) {
+ return $symbol;
+ }
+
+ $full = $namespace . '\\' . $symbol;
+
+ if (Reflection::classOrInterfaceExists($full)) {
+ return $full;
+ }
+
+ return $symbol;
}
}
namespace CuyZ\Valinor\Type\Parser\Factory\Specifications;
-use CuyZ\Valinor\Type\Parser\Lexer\ClassContextLexer;
-use CuyZ\Valinor\Type\Parser\Lexer\TypeLexer;
+use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\ObjectToken;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\TraversingToken;
+use CuyZ\Valinor\Type\Parser\TypeParser;
/** @internal */
final class ClassContextSpecification implements TypeParserSpecification
{
public function __construct(
/** @var class-string */
- private string $className
+ private string $className,
) {}
- public function transform(TypeLexer $lexer): TypeLexer
+ public function manipulateToken(TraversingToken $token): TraversingToken
{
- return new ClassContextLexer($lexer, $this->className);
+ if ($token->symbol() === 'self' || $token->symbol() === 'static') {
+ return new ObjectToken($this->className);
+ }
+
+ return $token;
+ }
+
+ public function manipulateParser(TypeParser $parser, TypeParserFactory $typeParserFactory): TypeParser
+ {
+ return $parser;
}
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Factory\Specifications;
+
+use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
+use CuyZ\Valinor\Type\Parser\GenericCheckerParser;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\TraversingToken;
+use CuyZ\Valinor\Type\Parser\TypeParser;
+
+/** @internal */
+final class GenericCheckerSpecification implements TypeParserSpecification
+{
+ public function manipulateToken(TraversingToken $token): TraversingToken
+ {
+ return $token;
+ }
+
+ public function manipulateParser(TypeParser $parser, TypeParserFactory $typeParserFactory): TypeParser
+ {
+ return new GenericCheckerParser($parser, $typeParserFactory);
+ }
+}
namespace CuyZ\Valinor\Type\Parser\Factory\Specifications;
-use CuyZ\Valinor\Type\Parser\Lexer\TypeAliasLexer;
-use CuyZ\Valinor\Type\Parser\Lexer\TypeLexer;
+use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\TraversingToken;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\TypeToken;
+use CuyZ\Valinor\Type\Parser\TypeParser;
use CuyZ\Valinor\Type\Type;
/** @internal */
{
public function __construct(
/** @var array<string, Type> */
- private array $aliases
+ private array $aliases,
) {}
- public function transform(TypeLexer $lexer): TypeLexer
+ public function manipulateToken(TraversingToken $token): TraversingToken
{
- return new TypeAliasLexer($lexer, $this->aliases);
+ $symbol = $token->symbol();
+
+ if (isset($this->aliases[$symbol])) {
+ return new TypeToken($this->aliases[$symbol], $symbol);
+ }
+
+ return $token;
+ }
+
+ public function manipulateParser(TypeParser $parser, TypeParserFactory $typeParserFactory): TypeParser
+ {
+ return $parser;
}
}
namespace CuyZ\Valinor\Type\Parser\Factory\Specifications;
-use CuyZ\Valinor\Type\Parser\Lexer\TypeLexer;
+use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\TraversingToken;
+use CuyZ\Valinor\Type\Parser\TypeParser;
/** @internal */
interface TypeParserSpecification
{
- public function transform(TypeLexer $lexer): TypeLexer;
+ public function manipulateToken(TraversingToken $token): TraversingToken;
+
+ public function manipulateParser(TypeParser $parser, TypeParserFactory $typeParserFactory): TypeParser;
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser;
+
+use CuyZ\Valinor\Type\CompositeTraversableType;
+use CuyZ\Valinor\Type\GenericType;
+use CuyZ\Valinor\Type\IntegerType;
+use CuyZ\Valinor\Type\Parser\Exception\Generic\AssignedGenericNotFound;
+use CuyZ\Valinor\Type\Parser\Exception\Generic\InvalidAssignedGeneric;
+use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
+use CuyZ\Valinor\Type\Parser\Exception\Template\InvalidClassTemplate;
+use CuyZ\Valinor\Type\Parser\Factory\Specifications\AliasSpecification;
+use CuyZ\Valinor\Type\Parser\Factory\Specifications\ClassContextSpecification;
+use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
+use CuyZ\Valinor\Type\StringType;
+use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Type\Types\ArrayKeyType;
+use CuyZ\Valinor\Utility\Reflection\DocParser;
+use CuyZ\Valinor\Utility\Reflection\Reflection;
+
+use function array_keys;
+
+/** @internal */
+final class GenericCheckerParser implements TypeParser
+{
+ public function __construct(
+ private TypeParser $delegate,
+ private TypeParserFactory $typeParserFactory,
+ ) {}
+
+ public function parse(string $raw): Type
+ {
+ $type = $this->delegate->parse($raw);
+
+ $this->checkGenerics($type);
+
+ return $type;
+ }
+
+ private function checkGenerics(Type $type): void
+ {
+ if ($type instanceof CompositeTraversableType) {
+ foreach ($type->traverse() as $subType) {
+ $this->checkGenerics($subType);
+ }
+ }
+
+ if (! $type instanceof GenericType) {
+ return;
+ }
+
+ $reflection = Reflection::class($type->className());
+ $templates = DocParser::classTemplates($reflection);
+
+ if ($templates === []) {
+ return;
+ }
+
+ $generics = $type->generics();
+
+ $parser = $this->typeParserFactory->get(
+ new ClassContextSpecification($reflection->name),
+ new AliasSpecification($reflection),
+ );
+
+ foreach ($templates as $templateName => $template) {
+ if (! isset($generics[$templateName])) {
+ throw new AssignedGenericNotFound($reflection->name, ...array_keys($templates));
+ }
+
+ array_shift($templates);
+
+ if ($template === null) {
+ // If no template is provided, it defaults to mixed type.
+ continue;
+ }
+
+ $genericType = $generics[$templateName];
+
+ try {
+ $templateType = $parser->parse($template);
+ } catch (InvalidType $invalidType) {
+ throw new InvalidClassTemplate($reflection->name, $templateName, $invalidType);
+ }
+
+ if ($templateType instanceof ArrayKeyType && $genericType instanceof StringType) {
+ $genericType = ArrayKeyType::string();
+ }
+
+ if ($templateType instanceof ArrayKeyType && $genericType instanceof IntegerType) {
+ $genericType = ArrayKeyType::integer();
+ }
+
+ if (! $genericType->matches($templateType)) {
+ throw new InvalidAssignedGeneric($genericType, $templateType, $templateName, $type->className());
+ }
+ }
+ }
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Lexer;
-
-use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
-use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassNameToken;
-use CuyZ\Valinor\Type\Parser\Lexer\Token\AdvancedClassNameToken;
-use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
-
-/** @internal */
-final class AdvancedClassLexer implements TypeLexer
-{
- public function __construct(
- private TypeLexer $delegate,
- private TypeParserFactory $typeParserFactory,
- ) {}
-
- public function tokenize(string $symbol): Token
- {
- $token = $this->delegate->tokenize($symbol);
-
- if ($token instanceof ClassNameToken) {
- return new AdvancedClassNameToken($token, $this->typeParserFactory);
- }
-
- return $token;
- }
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Lexer;
-
-use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
-use CuyZ\Valinor\Utility\Reflection\PhpParser;
-use CuyZ\Valinor\Utility\Reflection\Reflection;
-use ReflectionClass;
-use ReflectionFunction;
-use Reflector;
-
-use function strtolower;
-
-/** @internal */
-final class AliasLexer implements TypeLexer
-{
- public function __construct(
- private TypeLexer $delegate,
- /** @var ReflectionClass<object>|ReflectionFunction */
- private Reflector $reflection
- ) {}
-
- public function tokenize(string $symbol): Token
- {
- $symbol = $this->resolve($symbol);
-
- return $this->delegate->tokenize($symbol);
- }
-
- private function resolve(string $symbol): string
- {
- // Matches the case where a class extends a class with the same name but
- // in a different namespace.
- if ($symbol === $this->reflection->getShortName() && Reflection::classOrInterfaceExists($symbol)) {
- return $symbol;
- }
-
- $alias = $this->resolveAlias($symbol);
-
- if (strtolower($alias) !== strtolower($symbol)) {
- return $alias;
- }
-
- $namespaced = $this->resolveNamespaced($symbol);
-
- if ($namespaced !== $symbol) {
- return $namespaced;
- }
-
- return $symbol;
- }
-
- private function resolveAlias(string $symbol): string
- {
- $alias = $symbol;
-
- $namespaceParts = explode('\\', $symbol);
- $lastPart = array_shift($namespaceParts);
-
- if ($lastPart) {
- $alias = strtolower($lastPart);
- }
-
- $aliases = PhpParser::parseUseStatements($this->reflection);
-
- if (! isset($aliases[$alias])) {
- return $symbol;
- }
-
- $full = $aliases[$alias];
-
- if (! empty($namespaceParts)) {
- $full .= '\\' . implode('\\', $namespaceParts);
- }
-
- return $full;
- }
-
- private function resolveNamespaced(string $symbol): string
- {
- $reflection = $this->reflection;
-
- if ($reflection instanceof ReflectionFunction) {
- $classReflection = $reflection->getClosureScopeClass();
-
- if ($classReflection && $classReflection->getFileName() === $reflection->getFileName()) {
- $reflection = $classReflection;
- }
- }
-
- $namespace = $reflection->getNamespaceName();
-
- if (! $namespace) {
- return $symbol;
- }
-
- $full = $namespace . '\\' . $symbol;
-
- if (Reflection::classOrInterfaceExists($full)) {
- return $full;
- }
-
- return $symbol;
- }
-}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Lexer;
-
-use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
-use CuyZ\Valinor\Utility\Reflection\Reflection;
-use ReflectionClass;
-
-/** @internal */
-final class ClassContextLexer implements TypeLexer
-{
- private TypeLexer $delegate;
-
- /** @var ReflectionClass<object> */
- private ReflectionClass $reflection;
-
- /**
- * @param class-string $className
- */
- public function __construct(TypeLexer $delegate, string $className)
- {
- $this->delegate = $delegate;
- $this->reflection = Reflection::class($className);
- }
-
- public function tokenize(string $symbol): Token
- {
- $symbol = $this->resolve($symbol);
-
- return $this->delegate->tokenize($symbol);
- }
-
- private function resolve(string $symbol): string
- {
- if ($symbol === 'self' || $symbol === 'static') {
- return $this->reflection->name;
- }
-
- return $symbol;
- }
-}
use CuyZ\Valinor\Type\Parser\Lexer\Token\ArrayToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\CallableToken;
-use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassNameToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\ClassStringToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\ClosingBracketToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\ClosingCurlyBracketToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\ColonToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\CommaToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\DoubleColonToken;
-use CuyZ\Valinor\Type\Parser\Lexer\Token\EnumNameToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\FloatValueToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\IntegerToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\IntegerValueToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\OpeningSquareBracketToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\QuoteToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\TripleDotsToken;
use CuyZ\Valinor\Type\Parser\Lexer\Token\UnionToken;
-use CuyZ\Valinor\Type\Parser\Lexer\Token\UnknownSymbolToken;
-use CuyZ\Valinor\Utility\Reflection\Reflection;
-use UnitEnum;
use function filter_var;
use function is_numeric;
/** @internal */
final class NativeLexer implements TypeLexer
{
+ public function __construct(private TypeLexer $delegate) {}
+
public function tokenize(string $symbol): Token
{
if (NativeToken::accepts($symbol)) {
':' => ColonToken::get(),
'?' => NullableToken::get(),
',' => CommaToken::get(),
+ '...' => TripleDotsToken::get(),
'"', "'" => new QuoteToken($symbol),
'int', 'integer' => IntegerToken::get(),
'array' => ArrayToken::array(),
return new FloatValueToken((float)$symbol);
}
- /** @infection-ignore-all */
- if (PHP_VERSION_ID >= 8_01_00 && enum_exists($symbol)) {
- /** @var class-string<UnitEnum> $symbol */
- return new EnumNameToken($symbol);
- }
-
- if (Reflection::classOrInterfaceExists($symbol)) {
- /** @var class-string $symbol */
- return new ClassNameToken($symbol);
- }
-
- return new UnknownSymbolToken($symbol);
+ return $this->delegate->tokenize($symbol);
}
}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Lexer;
+
+use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeParserSpecification;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
+use CuyZ\Valinor\Type\Parser\Lexer\Token\VacantToken;
+
+/** @internal */
+final class SpecificationsLexer implements TypeLexer
+{
+ public function __construct(
+ /** @var array<TypeParserSpecification> */
+ private array $specifications,
+ ) {}
+
+ public function tokenize(string $symbol): Token
+ {
+ return (new VacantToken($symbol, $this->specifications));
+ }
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
-
-use CuyZ\Valinor\Type\IntegerType;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\AssignedGenericNotFound;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\CannotAssignGeneric;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\GenericClosingBracketMissing;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\GenericCommaMissing;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\InvalidAssignedGeneric;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\InvalidExtendTagClassName;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\InvalidExtendTagType;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\MissingGenerics;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\ExtendTagTypeError;
-use CuyZ\Valinor\Type\Parser\Exception\Generic\SeveralExtendTagsFound;
-use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
-use CuyZ\Valinor\Type\Parser\Exception\Template\InvalidClassTemplate;
-use CuyZ\Valinor\Type\Parser\Factory\Specifications\AliasSpecification;
-use CuyZ\Valinor\Type\Parser\Factory\Specifications\ClassContextSpecification;
-use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeAliasAssignerSpecification;
-use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeParserSpecification;
-use CuyZ\Valinor\Type\Parser\Factory\TypeParserFactory;
-use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
-use CuyZ\Valinor\Type\Parser\TypeParser;
-use CuyZ\Valinor\Type\StringType;
-use CuyZ\Valinor\Type\Type;
-use CuyZ\Valinor\Type\Types\ArrayKeyType;
-use CuyZ\Valinor\Type\ClassType;
-use CuyZ\Valinor\Type\Types\MixedType;
-use CuyZ\Valinor\Type\Types\NativeClassType;
-use CuyZ\Valinor\Utility\Reflection\DocParser;
-use CuyZ\Valinor\Utility\Reflection\Reflection;
-use ReflectionClass;
-
-use function array_keys;
-use function array_shift;
-use function array_slice;
-use function count;
-
-/** @internal */
-final class AdvancedClassNameToken implements TraversingToken
-{
- public function __construct(
- private ClassNameToken $delegate,
- private TypeParserFactory $typeParserFactory,
- ) {}
-
- public function traverse(TokenStream $stream): Type
- {
- $type = $this->delegate->traverse($stream);
-
- if (! $type instanceof ClassType) {
- return $type;
- }
-
- $className = $type->className();
- $reflection = Reflection::class($className);
- $parentReflection = $reflection->getParentClass();
-
- $specifications = [
- new ClassContextSpecification($className),
- new AliasSpecification($reflection),
- ];
-
- $templates = $this->templatesTypes($reflection, ...$specifications);
-
- $generics = $this->generics($stream, $className, $templates);
- $generics = $this->assignGenerics($className, $templates, $generics);
-
- if ($parentReflection) {
- $parserWithGenerics = $this->typeParserFactory->get(new TypeAliasAssignerSpecification($generics), ...$specifications);
-
- $parentType = $this->parentType($reflection, $parentReflection, $parserWithGenerics);
- }
-
- return new NativeClassType($className, $generics, $parentType ?? null);
- }
-
- public function symbol(): string
- {
- return $this->delegate->symbol();
- }
-
- /**
- * @param ReflectionClass<object> $reflection
- * @return array<string, Type>
- */
- private function templatesTypes(ReflectionClass $reflection, TypeParserSpecification ...$specifications): array
- {
- $templates = DocParser::classTemplates($reflection);
-
- if ($templates === []) {
- return [];
- }
-
- $types = [];
-
- foreach ($templates as $templateName => $type) {
- try {
- if ($type === '') {
- $types[$templateName] = MixedType::get();
- } else {
- /** @infection-ignore-all */
- $parser ??= $this->typeParserFactory->get(...$specifications);
-
- $types[$templateName] = $parser->parse($type);
- }
- } catch (InvalidType $invalidType) {
- throw new InvalidClassTemplate($reflection->name, $templateName, $invalidType);
- }
- }
-
- return $types;
- }
-
- /**
- * @param array<string, Type> $templates
- * @param class-string $className
- * @return Type[]
- */
- private function generics(TokenStream $stream, string $className, array $templates): array
- {
- if ($stream->done() || ! $stream->next() instanceof OpeningBracketToken) {
- return [];
- }
-
- $generics = [];
-
- $stream->forward();
-
- while (true) {
- if ($stream->done()) {
- throw new MissingGenerics($className, $generics, $templates);
- }
-
- $generics[] = $stream->read();
-
- if ($stream->done()) {
- throw new GenericClosingBracketMissing($className, $generics);
- }
-
- $next = $stream->forward();
-
- if ($next instanceof ClosingBracketToken) {
- break;
- }
-
- if (! $next instanceof CommaToken) {
- throw new GenericCommaMissing($className, $generics);
- }
- }
-
- return $generics;
- }
-
- /**
- * @param class-string $className
- * @param array<string, Type> $templates
- * @param Type[] $generics
- * @return array<string, Type>
- */
- private function assignGenerics(string $className, array $templates, array $generics): array
- {
- $assignedGenerics = [];
-
- foreach ($templates as $name => $template) {
- $generic = array_shift($generics);
-
- if ($generic === null) {
- $remainingTemplates = array_keys(array_slice($templates, count($assignedGenerics)));
-
- throw new AssignedGenericNotFound($className, ...$remainingTemplates);
- }
-
- if ($template instanceof ArrayKeyType && $generic instanceof StringType) {
- $generic = ArrayKeyType::string();
- }
-
- if ($template instanceof ArrayKeyType && $generic instanceof IntegerType) {
- $generic = ArrayKeyType::integer();
- }
-
- if (! $generic->matches($template)) {
- throw new InvalidAssignedGeneric($generic, $template, $name, $className);
- }
-
- $assignedGenerics[$name] = $generic;
- }
-
- if (! empty($generics)) {
- throw new CannotAssignGeneric($className, ...$generics);
- }
-
- return $assignedGenerics;
- }
-
- /**
- * @param ReflectionClass<object> $reflection
- * @param ReflectionClass<object> $parentReflection
- */
- private function parentType(ReflectionClass $reflection, ReflectionClass $parentReflection, TypeParser $typeParser): NativeClassType
- {
- $extendedClass = DocParser::classExtendsTypes($reflection);
-
- if (count($extendedClass) > 1) {
- throw new SeveralExtendTagsFound($reflection);
- } elseif (count($extendedClass) === 0) {
- $extendedClass = $parentReflection->name;
- } else {
- $extendedClass = $extendedClass[0];
- }
-
- try {
- $parentType = $typeParser->parse($extendedClass);
- } catch (InvalidType $exception) {
- throw new ExtendTagTypeError($reflection, $exception);
- }
-
- if (! $parentType instanceof NativeClassType) {
- throw new InvalidExtendTagType($reflection, $parentType);
- }
-
- if ($parentType->className() !== $parentReflection->name) {
- throw new InvalidExtendTagClassName($reflection, $parentType);
- }
-
- return $parentType;
- }
-}
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayCommaMissing;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayElementTypeMissing;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayEmptyElements;
+use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayInvalidUnsealedType;
+use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayUnexpectedTokenAfterSealedType;
+use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayWithoutElementsWithSealedType;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
use CuyZ\Valinor\Type\Type;
use CuyZ\Valinor\Type\Types\ArrayKeyType;
$elements = [];
$index = 0;
+ $isUnsealed = false;
+ $unsealedType = null;
while (! $stream->done()) {
if ($stream->next() instanceof ClosingCurlyBracketToken) {
$optional = false;
- if ($stream->next() instanceof UnknownSymbolToken) {
+ if ($stream->next() instanceof TripleDotsToken) {
+ $isUnsealed = true;
+ $stream->forward();
+ }
+
+ if ($stream->done()) {
+ throw new ShapedArrayClosingBracketMissing($elements, unsealedType: false);
+ }
+
+ if ($stream->next() instanceof VacantToken) {
$type = new StringValueType($stream->forward()->symbol());
+ } elseif ($isUnsealed && $stream->next() instanceof ClosingCurlyBracketToken) {
+ $stream->forward();
+ break;
} else {
$type = $stream->read();
}
+ if ($isUnsealed) {
+ $unsealedType = $type;
+
+ if ($elements === []) {
+ throw new ShapedArrayWithoutElementsWithSealedType($unsealedType);
+ }
+
+ if (! $unsealedType instanceof ArrayType) {
+ throw new ShapedArrayInvalidUnsealedType($elements, $unsealedType);
+ }
+
+ if ($stream->done()) {
+ throw new ShapedArrayClosingBracketMissing($elements, $unsealedType);
+ } elseif (! $stream->next() instanceof ClosingCurlyBracketToken) {
+ $unexpected = [];
+
+ while (! $stream->done() && ! $stream->next() instanceof ClosingCurlyBracketToken) {
+ $unexpected[] = $stream->forward();
+ }
+
+ throw new ShapedArrayUnexpectedTokenAfterSealedType($elements, $unsealedType, $unexpected);
+ }
+
+ continue;
+ }
+
if ($stream->done()) {
$elements[] = new ShapedArrayElement(new IntegerValueType($index), $type);
}
}
- if (empty($elements)) {
+ if ($elements === []) {
throw new ShapedArrayEmptyElements();
}
+ if ($unsealedType) {
+ return ShapedArrayType::unsealed($unsealedType, ...$elements);
+ } elseif ($isUnsealed) {
+ return ShapedArrayType::unsealedWithoutType(...$elements);
+ }
+
return new ShapedArrayType(...$elements);
}
}
use CuyZ\Valinor\Type\Parser\Exception\Constant\ClassConstantCaseNotFound;
use CuyZ\Valinor\Type\Parser\Exception\Constant\MissingClassConstantCase;
use CuyZ\Valinor\Type\Parser\Exception\Constant\MissingSpecificClassConstantCase;
+use CuyZ\Valinor\Type\Parser\Exception\Generic\CannotAssignGeneric;
+use CuyZ\Valinor\Type\Parser\Exception\Generic\GenericClosingBracketMissing;
+use CuyZ\Valinor\Type\Parser\Exception\Generic\GenericCommaMissing;
+use CuyZ\Valinor\Type\Parser\Exception\Generic\MissingGenerics;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
use CuyZ\Valinor\Type\Type;
-use CuyZ\Valinor\Type\Types\NativeClassType;
use CuyZ\Valinor\Type\Types\Factory\ValueTypeFactory;
use CuyZ\Valinor\Type\Types\InterfaceType;
+use CuyZ\Valinor\Type\Types\NativeClassType;
use CuyZ\Valinor\Type\Types\UnionType;
+use CuyZ\Valinor\Utility\Reflection\DocParser;
use CuyZ\Valinor\Utility\Reflection\Reflection;
use ReflectionClass;
use ReflectionClassConstant;
+use function array_keys;
use function array_map;
+use function array_shift;
+use function array_values;
use function count;
use function explode;
return new InterfaceType($this->reflection->name);
}
- return new NativeClassType($this->reflection->name);
+ $reflection = Reflection::class($this->reflection->name);
+
+ $templates = array_keys(DocParser::classTemplates($reflection));
+
+ $generics = $this->generics($stream, $this->reflection->name, $templates);
+ $generics = $this->assignGenerics($this->reflection->name, $templates, $generics);
+
+ return new NativeClassType($this->reflection->name, $generics);
}
public function symbol(): string
$cases = array_map(static fn ($value) => ValueTypeFactory::from($value), $cases);
if (count($cases) > 1) {
- return new UnionType(...$cases);
+ return new UnionType(...array_values($cases));
}
return reset($cases);
}
+
+ /**
+ * @param array<non-empty-string> $templates
+ * @param class-string $className
+ * @return list<Type>
+ */
+ private function generics(TokenStream $stream, string $className, array $templates): array
+ {
+ if ($stream->done() || ! $stream->next() instanceof OpeningBracketToken) {
+ return [];
+ }
+
+ $generics = [];
+
+ $stream->forward();
+
+ while (true) {
+ if ($stream->done()) {
+ throw new MissingGenerics($className, $generics, $templates);
+ }
+
+ $generics[] = $stream->read();
+
+ if ($stream->done()) {
+ throw new GenericClosingBracketMissing($className, $generics);
+ }
+
+ $next = $stream->forward();
+
+ if ($next instanceof ClosingBracketToken) {
+ break;
+ }
+
+ if (! $next instanceof CommaToken) {
+ throw new GenericCommaMissing($className, $generics);
+ }
+ }
+
+ return $generics;
+ }
+
+ /**
+ * @param class-string $className
+ * @param array<non-empty-string> $templates
+ * @param list<Type> $generics
+ * @return array<non-empty-string, Type>
+ */
+ private function assignGenerics(string $className, array $templates, array $generics): array
+ {
+ $assignedGenerics = [];
+
+ foreach ($templates as $template) {
+ $generic = array_shift($generics);
+
+ if ($generic) {
+ $assignedGenerics[$template] = $generic;
+ }
+ }
+
+ if (! empty($generics)) {
+ throw new CannotAssignGeneric($className, ...$generics);
+ }
+
+ return $assignedGenerics;
+ }
}
throw new IntegerRangeMissingMinValue();
}
- if ($stream->next() instanceof UnknownSymbolToken) {
+ if ($stream->next()->symbol() === 'min') {
$min = new IntegerValueType(PHP_INT_MIN);
$stream->forward();
} else {
throw new IntegerRangeMissingMaxValue($min);
}
- if ($stream->next() instanceof UnknownSymbolToken) {
+ if ($stream->next()->symbol() === 'max') {
$max = new IntegerValueType(PHP_INT_MAX);
$stream->forward();
} else {
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
+
+use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
+use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Utility\Reflection\Reflection;
+
+/** @internal */
+final class ObjectToken implements TraversingToken
+{
+ public function __construct(
+ /** @var class-string */
+ private string $className,
+ ) {}
+
+ public function traverse(TokenStream $stream): Type
+ {
+ return Reflection::enumExists($this->className)
+ ? (new EnumNameToken($this->className))->traverse($stream)
+ : (new ClassNameToken($this->className))->traverse($stream);
+ }
+
+ public function symbol(): string
+ {
+ return $this->className;
+ }
+}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
+
+use CuyZ\Valinor\Utility\IsSingleton;
+
+/** @internal */
+final class TripleDotsToken implements Token
+{
+ use IsSingleton;
+
+ public function symbol(): string
+ {
+ return '...';
+ }
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
-
-use CuyZ\Valinor\Type\Parser\Exception\UnknownSymbol;
-use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
-use CuyZ\Valinor\Type\Type;
-
-/** @internal */
-final class UnknownSymbolToken implements TraversingToken
-{
- public function __construct(private string $symbol) {}
-
- public function traverse(TokenStream $stream): Type
- {
- throw new UnknownSymbol($this->symbol);
- }
-
- public function symbol(): string
- {
- return $this->symbol;
- }
-}
--- /dev/null
+<?php
+
+declare(strict_types=1);
+
+namespace CuyZ\Valinor\Type\Parser\Lexer\Token;
+
+use CuyZ\Valinor\Type\Parser\Exception\UnknownSymbol;
+use CuyZ\Valinor\Type\Parser\Factory\Specifications\TypeParserSpecification;
+use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
+use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Utility\Reflection\Reflection;
+
+/** @internal */
+final class VacantToken implements TraversingToken
+{
+ public function __construct(
+ private string $symbol,
+ /** @var array<TypeParserSpecification> */
+ private array $specifications,
+ ) {}
+
+ public function traverse(TokenStream $stream): Type
+ {
+ $token = $this;
+
+ foreach ($this->specifications as $specification) {
+ $new = $specification->manipulateToken($token);
+
+ if ($new !== $token) {
+ return $new->traverse($stream);
+ }
+ }
+
+ if (Reflection::enumExists($this->symbol)) {
+ return (new EnumNameToken($this->symbol))->traverse($stream);
+ }
+
+ if (Reflection::classOrInterfaceExists($this->symbol)) {
+ return (new ClassNameToken($this->symbol))->traverse($stream);
+ }
+
+ throw new UnknownSymbol($this->symbol);
+ }
+
+ public function symbol(): string
+ {
+ return $this->symbol;
+ }
+}
--- /dev/null
+<?php
+
+namespace CuyZ\Valinor\Type\Parser\Lexer;
+
+use function array_map;
+use function array_shift;
+use function implode;
+use function preg_split;
+
+/** @internal */
+final class TokensExtractor
+{
+ private const TOKEN_PATTERNS = [
+ 'Anonymous class' => '[a-zA-Z_\x7f-\xff][\\\\a-zA-Z0-9_\x7f-\xff]*+@anonymous\x00.*?\.php(?:0x?|:[0-9]++\$)[0-9a-fA-F]++',
+ 'Double colons' => '\:\:',
+ 'Triple dots' => '\.\.\.',
+ 'Dollar sign' => '\$',
+ 'Whitespace' => '\s',
+ 'Union' => '\|',
+ 'Intersection' => '&',
+ 'Opening bracket' => '\<',
+ 'Closing bracket' => '\>',
+ 'Opening square bracket' => '\[',
+ 'Closing square bracket' => '\]',
+ 'Opening curly bracket' => '\{',
+ 'Closing curly bracket' => '\}',
+ 'Colon' => '\:',
+ 'Question mark' => '\?',
+ 'Comma' => ',',
+ 'Single quote' => "'",
+ 'Double quote' => '"',
+ ];
+
+ /** @var list<string> */
+ private array $symbols = [];
+
+ public function __construct(string $string)
+ {
+ $pattern = '/(' . implode('|', self::TOKEN_PATTERNS) . ')' . '/';
+ $tokens = preg_split($pattern, $string, flags: PREG_SPLIT_DELIM_CAPTURE | PREG_SPLIT_NO_EMPTY);
+
+ $quote = null;
+ $text = null;
+
+ while (($token = array_shift($tokens)) !== null) {
+ if ($token === $quote) {
+ if ($text !== null) {
+ $this->symbols[] = $text;
+ }
+
+ $this->symbols[] = $token;
+
+ $text = null;
+ $quote = null;
+ } elseif ($quote !== null) {
+ $text .= $token;
+ } elseif ($token === '"' || $token === "'") {
+ $quote = $token;
+
+ $this->symbols[] = $token;
+ } else {
+ $this->symbols[] = $token;
+ }
+ }
+
+ if ($text !== null) {
+ $this->symbols[] = $text;
+ }
+
+ $this->symbols = array_map('trim', $this->symbols);
+ $this->symbols = array_filter($this->symbols, static fn ($value) => $value !== '');
+ $this->symbols = array_values($this->symbols);
+ }
+
+ /**
+ * @return list<string>
+ */
+ public function all(): array
+ {
+ return $this->symbols;
+ }
+}
+++ /dev/null
-<?php
-
-declare(strict_types=1);
-
-namespace CuyZ\Valinor\Type\Parser\Lexer;
-
-use CuyZ\Valinor\Type\Parser\Lexer\Token\Token;
-use CuyZ\Valinor\Type\Parser\Lexer\Token\TypeToken;
-use CuyZ\Valinor\Type\Type;
-
-/** @internal */
-final class TypeAliasLexer implements TypeLexer
-{
- public function __construct(
- private TypeLexer $delegate,
- /** @var array<string, Type> */
- private array $aliases
- ) {}
-
- public function tokenize(string $symbol): Token
- {
- if (isset($this->aliases[$symbol])) {
- return new TypeToken($this->aliases[$symbol], $symbol);
- }
-
- return $this->delegate->tokenize($symbol);
- }
-}
namespace CuyZ\Valinor\Type\Parser;
+use CuyZ\Valinor\Type\Parser\Lexer\TokensExtractor;
use CuyZ\Valinor\Type\Parser\Lexer\TokenStream;
use CuyZ\Valinor\Type\Parser\Lexer\TypeLexer;
use CuyZ\Valinor\Type\Type;
public function parse(string $raw): Type
{
- $symbols = new ParserSymbols($raw);
+ $symbols = new TokensExtractor($raw);
$tokens = array_map(
fn (string $symbol) => $this->lexer->tokenize($symbol),
+++ /dev/null
-<?php
-
-namespace CuyZ\Valinor\Type\Parser;
-
-/** @internal */
-final class ParserSymbols
-{
- private const OPERATORS = [' ', '|', '&', '<', '>', '[', ']', '{', '}', ':', '?', ',', "'", '"'];
-
- /** @var list<string> */
- private array $symbols = [];
-
- public function __construct(string $string)
- {
- $current = null;
- $quote = null;
-
- foreach (str_split($string) as $char) {
- if ($char === $quote) {
- $quote = null;
- } elseif ($char === '"' || $char === "'") {
- $quote = $char;
- } elseif ($quote !== null || ! in_array($char, self::OPERATORS, true)) {
- $current .= $char;
- continue;
- }
-
- if ($current !== null) {
- $this->symbols[] = $current;
- $current = null;
- }
-
- $this->symbols[] = $char;
- }
-
- if ($current !== null) {
- $this->symbols[] = $current;
- }
-
- $this->symbols = array_map('trim', $this->symbols);
- $this->symbols = array_filter($this->symbols, static fn ($value) => $value !== '');
-
- $this->mergeDoubleColons();
- $this->detectAnonymousClass();
- }
-
- /**
- * @return list<string>
- */
- public function all(): array
- {
- return $this->symbols;
- }
-
- private function mergeDoubleColons(): void
- {
- foreach ($this->symbols as $key => $symbol) {
- /** @infection-ignore-all should not happen so it is not tested */
- if ($key === 0) {
- continue;
- }
-
- if ($symbol === ':' && $this->symbols[$key - 1] === ':') {
- $this->symbols[$key - 1] = '::';
- unset($this->symbols[$key]);
- }
- }
- }
-
- private function detectAnonymousClass(): void
- {
- foreach ($this->symbols as $key => $symbol) {
- if (! str_starts_with($symbol, "class@anonymous\0")) {
- continue;
- }
-
- $this->symbols[$key] = $symbol . $this->symbols[$key + 1] . $this->symbols[$key + 2];
-
- array_splice($this->symbols, $key + 1, 2);
- }
- }
-}
namespace CuyZ\Valinor\Type\Types;
-use CuyZ\Valinor\Type\CombiningType;
+use CuyZ\Valinor\Mapper\Tree\Message\ErrorMessage;
+use CuyZ\Valinor\Mapper\Tree\Message\MessageBuilder;
use CuyZ\Valinor\Type\IntegerType;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\InvalidArrayKey;
+use CuyZ\Valinor\Type\ScalarType;
use CuyZ\Valinor\Type\StringType;
use CuyZ\Valinor\Type\Type;
+use LogicException;
use function is_int;
/** @internal */
-final class ArrayKeyType implements Type
+final class ArrayKeyType implements ScalarType
{
private static self $default;
private static self $string;
- /** @var array<Type> */
+ /** @var non-empty-list<IntegerType|StringType> */
private array $types;
private string $signature;
private function __construct(Type $type)
{
- $this->signature = $type->toString();
- $this->types = $type instanceof CombiningType
+ $types = $type instanceof UnionType
? [...$type->types()]
: [$type];
- foreach ($this->types as $subType) {
+ foreach ($types as $subType) {
if (! $subType instanceof IntegerType && ! $subType instanceof StringType) {
throw new InvalidArrayKey($subType);
}
}
+
+ /** @var non-empty-list<IntegerType|StringType> $types */
+ $this->types = $types;
+ $this->signature = $type->toString();
}
public static function default(): self
{
- return self::$default ??= new self(new UnionType(NativeIntegerType::get(), NativeStringType::get()));
+ if (!isset(self::$default)) {
+ self::$default = new self(new UnionType(NativeIntegerType::get(), NativeStringType::get()));
+ self::$default->signature = 'array-key';
+ }
+
+ return self::$default;
}
public static function integer(): self
return self::$string ??= new self(NativeStringType::get());
}
- public static function from(Type $type): ?self
+ public static function from(Type $type): self
{
return match (true) {
$type instanceof self => $type,
return true;
}
+ if ($other instanceof UnionType) {
+ return $this->isMatchedBy($other);
+ }
+
if (! $other instanceof self) {
return false;
}
return false;
}
+ public function canCast(mixed $value): bool
+ {
+ foreach ($this->types as $type) {
+ if ($type->canCast($value)) {
+ return true;
+ }
+ }
+
+ return false;
+ }
+
+ public function cast(mixed $value): string|int
+ {
+ foreach ($this->types as $type) {
+ if ($type->canCast($value)) {
+ return $type->cast($value);
+ }
+ }
+
+ throw new LogicException();
+ }
+
+ public function errorMessage(): ErrorMessage
+ {
+ return MessageBuilder::newError('Value {source_value} is not a valid array key.')->build();
+ }
+
public function toString(): string
{
return $this->signature;
public function __construct(
/** @var class-string */
private string $interfaceName,
- /** @var array<string, Type> */
+ /** @var array<non-empty-string, Type> */
private array $generics = []
) {}
return false;
}
- return is_a($other->className(), $this->interfaceName, true);
+ return is_a($this->interfaceName, $other->className(), true);
}
public function traverse(): array
use CuyZ\Valinor\Type\CompositeType;
use CuyZ\Valinor\Type\Type;
+use function array_values;
use function implode;
/** @internal */
final class IntersectionType implements CombiningType
{
- /** @var ObjectType[] */
+ /** @var non-empty-list<ObjectType> */
private array $types;
private string $signature;
- public function __construct(ObjectType ...$types)
+ public function __construct(ObjectType $type, ObjectType $otherType, ObjectType ...$otherTypes)
{
- $this->types = $types;
- $this->signature = implode('&', array_map(fn (Type $type) => $type->toString(), $types));
+ $this->types = [$type, $otherType, ...array_values($otherTypes)];
+ $this->signature = implode('&', array_map(fn (Type $type) => $type->toString(), $this->types));
}
public function accepts(mixed $value): bool
}
/**
- * @return ObjectType[]
+ * @return non-empty-list<ObjectType>
*/
public function types(): array
{
return false;
}
- // PHP8.1 use `array_is_list`
$i = 0;
foreach ($value as $key => $item) {
use CuyZ\Valinor\Type\Type;
use function array_map;
-use function assert;
-use function get_parent_class;
use function is_a;
/** @internal */
public function __construct(
/** @var class-string */
private string $className,
- /** @var array<string, Type> */
+ /** @var array<non-empty-string, Type> */
private array $generics = [],
- private ?self $parent = null,
) {
$this->className = ltrim($this->className, '\\');
}
- /**
- * @param class-string $className
- */
- public static function for(string $className): self
- {
- $parentClass = get_parent_class($className);
- $parent = $parentClass ? self::for($parentClass) : null;
-
- return new self($className, [], $parent);
- }
-
public function className(): string
{
return $this->className;
return $this->generics;
}
- public function hasParent(): bool
- {
- return $this->parent instanceof self;
- }
-
- public function parent(): self
- {
- assert($this->parent instanceof self);
-
- return $this->parent;
- }
-
public function accepts(mixed $value): bool
{
return $value instanceof $this->className;
use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayElementDuplicatedKey;
use CuyZ\Valinor\Type\Type;
-use function array_diff;
use function array_key_exists;
-use function array_keys;
use function array_map;
+use function assert;
use function count;
use function implode;
use function in_array;
/** @var ShapedArrayElement[] */
private array $elements;
- private string $signature;
+ private bool $isUnsealed = false;
+
+ private ?ArrayType $unsealedType = null;
public function __construct(ShapedArrayElement ...$elements)
{
$this->elements = $elements;
- $this->signature =
- 'array{' .
- implode(', ', array_map(fn (ShapedArrayElement $element) => $element->toString(), $elements))
- . '}';
$keys = [];
$key = $element->key()->value();
if (in_array($key, $keys, true)) {
- throw new ShapedArrayElementDuplicatedKey((string)$key, $this->signature);
+ throw new ShapedArrayElementDuplicatedKey((string)$key, $this->toString());
}
$keys[] = $key;
}
}
+ public static function unsealed(ArrayType $unsealedType, ShapedArrayElement ...$elements): self
+ {
+ $self = new self(...$elements);
+ $self->isUnsealed = true;
+ $self->unsealedType = $unsealedType;
+
+ return $self;
+ }
+
+ public static function unsealedWithoutType(ShapedArrayElement ...$elements): self
+ {
+ $self = new self(...$elements);
+ $self->isUnsealed = true;
+
+ return $self;
+ }
+
+ public function isUnsealed(): bool
+ {
+ return $this->isUnsealed;
+ }
+
+ public function hasUnsealedType(): bool
+ {
+ return $this->unsealedType !== null;
+ }
+
+ public function unsealedType(): ArrayType
+ {
+ assert($this->isUnsealed);
+
+ return $this->unsealedType ?? ArrayType::native();
+ }
+
public function accepts(mixed $value): bool
{
if (! is_array($value)) {
return false;
}
- $keys = [];
-
foreach ($this->elements as $shape) {
$type = $shape->type();
- $keys[] = $key = $shape->key()->value();
+ $key = $shape->key()->value();
$valueExists = array_key_exists($key, $value);
if (! $valueExists && ! $shape->isOptional()) {
if ($valueExists && ! $type->accepts($value[$key])) {
return false;
}
+
+ unset($value[$key]);
}
- $excess = array_diff(array_keys($value), $keys);
+ if ($this->isUnsealed) {
+ return $this->unsealedType()->accepts($value);
+ }
- return count($excess) === 0;
+ return count($value) === 0;
}
public function matches(Type $other): bool
}
}
+ if ($this->isUnsealed && ! $this->unsealedType()->matches($other)) {
+ return false;
+ }
+
return true;
}
foreach ($this->elements as $element) {
foreach ($other->elements as $otherElement) {
- if ($element->key()->matches($otherElement->key())) {
- if (! $element->type()->matches($otherElement->type())) {
- return false;
- }
-
+ if ($element->key()->matches($otherElement->key())
+ && $element->type()->matches($otherElement->type())
+ ) {
continue 2;
}
}
}
}
+ if ($other->isUnsealed) {
+ return $this->isUnsealed
+ && $this->unsealedType()->matches($other->unsealedType());
+ }
+
return true;
}
}
}
+ if ($this->isUnsealed) {
+ $types = [...$types, $this->unsealedType(), ...$this->unsealedType()->traverse()];
+ }
+
return $types;
}
public function toString(): string
{
- return $this->signature;
+ $signature = 'array{';
+ $signature .= implode(', ', array_map(fn (ShapedArrayElement $element) => $element->toString(), $this->elements));
+
+ if ($this->isUnsealed) {
+ $signature .= ', ...';
+
+ if ($this->unsealedType) {
+ $signature .= $this->unsealedType->toString();
+ }
+ }
+
+ $signature .= '}';
+
+ return $signature;
}
}
/** @internal */
final class UnionType implements CombiningType
{
- /** @var Type[] */
- private array $types = [];
+ /** @var non-empty-list<Type> */
+ private array $types;
private string $signature;
- public function __construct(Type ...$types)
+ public function __construct(Type $type, Type $otherType, Type ...$otherTypes)
{
- $this->signature = implode('|', array_map(fn (Type $type) => $type->toString(), $types));
+ $types = [$type, $otherType, ...$otherTypes];
+ $filteredTypes = [];
- foreach ($types as $type) {
- if ($type instanceof self) {
- foreach ($type->types as $subType) {
- $this->types[] = $subType;
+ foreach ($types as $subType) {
+ if ($subType instanceof self) {
+ foreach ($subType->types as $anotherSubType) {
+ $filteredTypes[] = $anotherSubType;
}
continue;
}
- if ($type instanceof MixedType) {
+ if ($subType instanceof MixedType) {
throw new ForbiddenMixedType();
}
- $this->types[] = $type;
+ $filteredTypes[] = $subType;
}
+
+ $this->types = $filteredTypes;
+ $this->signature = implode('|', array_map(fn (Type $type) => $type->toString(), $this->types));
}
public function accepts(mixed $value): bool
}
foreach ($this->types as $type) {
- if (! $type->matches($other)) {
- return false;
+ if ($type->matches($other)) {
+ return true;
}
}
- return true;
+ return false;
}
public function isMatchedBy(Type $other): bool
namespace CuyZ\Valinor\Type\Types;
-use CuyZ\Valinor\Type\ClassType;
+use CuyZ\Valinor\Type\ObjectType;
use CuyZ\Valinor\Type\Parser\Exception\InvalidType;
use CuyZ\Valinor\Type\Type;
+use CuyZ\Valinor\Utility\Reflection\Reflection;
use CuyZ\Valinor\Utility\ValueDumper;
use LogicException;
+use ReflectionFunctionAbstract;
+use ReflectionParameter;
+use ReflectionProperty;
/** @internal */
final class UnresolvableType implements Type
);
}
- public static function forLocalAlias(string $raw, string $name, ClassType $type, InvalidType $exception): self
+ public static function forDocBlockTypeNotMatchingNative(ReflectionProperty|ReflectionParameter|ReflectionFunctionAbstract $reflection, Type $typeFromDocBlock, Type $typeFromReflection): self
+ {
+ $signature = Reflection::signature($reflection);
+
+ if ($reflection instanceof ReflectionProperty) {
+ $message = "Types for property `$signature` do not match: `{$typeFromDocBlock->toString()}` (docblock) does not accept `{$typeFromReflection->toString()}` (native).";
+ } elseif ($reflection instanceof ReflectionParameter) {
+ $message = "Types for parameter `$signature` do not match: `{$typeFromDocBlock->toString()}` (docblock) does not accept `{$typeFromReflection->toString()}` (native).";
+ } else {
+ $message = "Return types for method `$signature` do not match: `{$typeFromDocBlock->toString()}` (docblock) does not accept `{$typeFromReflection->toString()}` (native).";
+ }
+
+ return new self($typeFromDocBlock->toString(), $message);
+ }
+
+ public static function forLocalAlias(string $raw, string $name, ObjectType $type, InvalidType $exception): self
{
return new self(
$raw,
namespace CuyZ\Valinor\Utility\Reflection;
use CuyZ\Valinor\Type\Parser\Exception\Template\DuplicatedTemplateName;
+use CuyZ\Valinor\Type\Parser\Lexer\TokensExtractor;
use ReflectionClass;
use ReflectionFunctionAbstract;
use ReflectionParameter;
use ReflectionProperty;
+use function array_key_exists;
use function array_merge;
+use function array_search;
+use function array_shift;
+use function array_splice;
+use function assert;
use function end;
use function explode;
+use function in_array;
use function preg_match;
use function preg_match_all;
use function str_replace;
return null;
}
- if (! preg_match("/(?<type>.*)\\$$reflection->name(\s|\z)/s", $doc, $matches)) {
- return null;
+ $parameters = [];
+
+ $tokens = (new TokensExtractor($doc))->all();
+
+ while (($token = array_shift($tokens)) !== null) {
+ if (! in_array($token, ['@param', '@phpstan-param', '@psalm-param'], true)) {
+ continue;
+ }
+
+ $dollarSignKey = (int)array_search('$', $tokens, true);
+ $name = $tokens[$dollarSignKey + 1] ?? null;
+
+ $parameters[$name][$token] = implode('', array_splice($tokens, 0, $dollarSignKey));
}
- return self::annotationType($matches['type'], 'param');
+ return $parameters[$reflection->name]['@phpstan-param']
+ ?? $parameters[$reflection->name]['@psalm-param']
+ ?? $parameters[$reflection->name]['@param']
+ ?? null;
}
public static function functionReturnType(ReflectionFunctionAbstract $reflection): ?string
/**
* @param ReflectionClass<object> $reflection
- * @return array<string, string>
+ * @return array<non-empty-string, non-empty-string|null>
*/
public static function classTemplates(ReflectionClass $reflection): array
{
preg_match_all("/@(phpstan-|psalm-)?template\s+(?<name>\w+)(\s+of\s+(?<type>.+))?/", $doc, $matches);
foreach ($matches['name'] as $key => $name) {
- /** @var string $name */
- if (isset($templates[$name])) {
+ /** @var non-empty-string $name */
+ if (array_key_exists($name, $templates)) {
throw new DuplicatedTemplateName($reflection->name, $name);
}
- $templates[$name] = self::findType($matches['type'][$key]);
+ $template = $matches['type'][$key];
+
+ if ($template === '') {
+ $templates[$name] = null;
+ } else {
+ $templates[$name] = self::findType($template);
+ }
}
return $templates;
return null;
}
+ /**
+ * @return non-empty-string
+ */
private static function findType(string $string): string
{
$operatorsMatrix = [
$type .= $char;
}
- return trim($type);
+ $type = trim($type);
+
+ assert($type !== '');
+
+ return $type;
}
private static function sanitizeDocComment(string|false $doc): ?string
$doc = preg_replace('#^\s*/\*\*([^/]+)\*/\s*$#', '$1', $doc);
- return preg_replace('/^\s*\*\s*(\S*)/m', '$1', $doc); // @phpstan-ignore-line
+ return preg_replace('/^\s*\*\s*(\S*)/m', '$1', (string)$doc);
}
/**
namespace CuyZ\Valinor\Utility\Reflection;
+use Attribute;
use Closure;
+use Error;
+use ReflectionAttribute;
use ReflectionClass;
use ReflectionFunction;
+use ReflectionFunctionAbstract;
use ReflectionIntersectionType;
use ReflectionMethod;
use ReflectionNamedType;
use ReflectionType;
use ReflectionUnionType;
use Reflector;
-use RuntimeException;
+use UnitEnum;
+use function array_filter;
+use function array_map;
use function class_exists;
+use function enum_exists;
use function implode;
use function interface_exists;
+use function ltrim;
use function spl_object_hash;
use function str_contains;
/** @var array<string, ReflectionFunction> */
private static array $functionReflection = [];
+ /** @var array<string, bool> */
+ private static array $classOrInterfaceExists = [];
+
+ /** @var array<string, bool> */
+ private static array $enumExists = [];
+
/**
* Case-sensitive implementation of `class_exists` and `interface_exists`.
+ *
+ * @phpstan-assert-if-true class-string $name
*/
public static function classOrInterfaceExists(string $name): bool
{
- if (! class_exists($name) && ! interface_exists($name)) {
- return false;
- }
+ // @infection-ignore-all / We don't need to test the cache
+ return self::$classOrInterfaceExists[$name] ??= (class_exists($name) || interface_exists($name))
+ && self::class($name)->name === ltrim($name, '\\');
+ }
- return self::class($name)->name === ltrim($name, '\\');
+ /**
+ * @phpstan-assert-if-true class-string<UnitEnum> $name
+ */
+ public static function enumExists(string $name): bool
+ {
+ // @infection-ignore-all / We don't need to test the cache
+ return self::$enumExists[$name] ??= enum_exists($name);
}
/**
return self::$functionReflection[spl_object_hash($closure)] ??= new ReflectionFunction($closure);
}
- public static function signature(Reflector $reflection): string
+ /**
+ * @param ReflectionClass<object>|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflection
+ * @return array<ReflectionAttribute<object>>
+ */
+ public static function attributes(Reflector $reflection): array
{
- if ($reflection instanceof ReflectionClass) {
- return $reflection->name;
- }
+ $attributes = array_filter(
+ $reflection->getAttributes(),
+ static fn (ReflectionAttribute $attribute) => $attribute->getName() !== Attribute::class,
+ );
+
+ return array_filter(
+ array_map(
+ static function (ReflectionAttribute $attribute) {
+ try {
+ $attribute->newInstance();
+
+ return $attribute;
+ } catch (Error) {
+ // Race condition when the attribute is affected to a property/parameter
+ // that was PROMOTED, in this case the attribute will be applied to both
+ // ParameterReflection AND PropertyReflection, BUT the target arg inside the attribute
+ // class is configured to support only ONE of them (parameter OR property)
+ // https://wiki.php.net/rfc/constructor_promotion#attributes for more details.
+ // Ignore attribute if the instantiation failed.
+ return null;
+ }
+ },
+ $attributes,
+ ),
+ );
+ }
+ /**
+ * @param ReflectionClass<object>|ReflectionProperty|ReflectionMethod|ReflectionFunctionAbstract|ReflectionParameter $reflection
+ * @return non-empty-string
+ */
+ public static function signature(ReflectionClass|ReflectionProperty|ReflectionMethod|ReflectionFunctionAbstract|ReflectionParameter $reflection): string
+ {
if ($reflection instanceof ReflectionProperty) {
return "{$reflection->getDeclaringClass()->name}::\$$reflection->name";
}
return "{$reflection->getDeclaringClass()->name}::$reflection->name()";
}
- if ($reflection instanceof ReflectionFunction) {
+ if ($reflection instanceof ReflectionFunctionAbstract) {
if (str_contains($reflection->name, '{closure}')) {
$startLine = $reflection->getStartLine();
$endLine = $reflection->getEndLine();
return $signature;
}
- throw new RuntimeException('Invalid reflection type `' . $reflection::class . '`.');
+ return $reflection->name;
}
public static function flattenType(ReflectionType $type): string
while ($token = $this->next()) {
if ($currentNamespace === $namespaceName && $token->is(T_USE)) {
- $statements = array_merge($statements, $this->parseUseStatement());
+ $statements = [...$statements, ...$this->parseUseStatement()];
continue;
}
# laminas-diactoros
-[![Build Status](https://github.com/laminas/laminas-diactoros/workflows/Continuous%20Integration/badge.svg)](https://github.com/laminas/laminas-diactoros/actions/workflows/continuous-integration.yml)
+[![Build Status](https://github.com/laminas/laminas-diactoros/actions/workflows/continuous-integration.yml/badge.svg)](https://github.com/laminas/laminas-diactoros/actions/workflows/continuous-integration.yml)
+[![type-coverage](https://shepherd.dev/github/laminas/laminas-diactoros/coverage.svg)](https://shepherd.dev/github/laminas/laminas-diactoros)
+[![Psalm level](https://shepherd.dev/github/laminas/laminas-diactoros/level.svg)](https://shepherd.dev/github/laminas/laminas-diactoros)
> ## 🇷🇺 Русским гражданам
>
"http-interop/http-factory-tests": "^0.9.0",
"laminas/laminas-coding-standard": "~2.5.0",
"php-http/psr7-integration-tests": "^1.3",
- "phpunit/phpunit": "^9.5.28",
+ "phpunit/phpunit": "^9.6.16",
"psalm/plugin-phpunit": "^0.18.4",
- "vimeo/psalm": "^5.15.0"
+ "vimeo/psalm": "^5.22.1"
},
"provide": {
"psr/http-factory-implementation": "^1.1 || ^2.0",
continue;
}
- if (! $currentHeader) {
+ if ($currentHeader === false) {
throw Exception\DeserializationException::forInvalidHeader();
}
*/
public function eof(): bool
{
- return empty($this->callback);
+ return $this->callback === null;
}
/**
public function getContents(): string
{
$callback = $this->detach();
- $contents = $callback ? $callback() : '';
+ $contents = $callback !== null ? $callback() : '';
return (string) $contents;
}
use Laminas\Diactoros\ServerRequestFilter\FilterUsingXForwardedHeaders;
-use function gettype;
-use function is_object;
+use function get_debug_type;
use function is_string;
use function sprintf;
public static function forHeader(mixed $name): self
{
if (! is_string($name)) {
- $name = sprintf('(value of type %s)', is_object($name) ? $name::class : gettype($name));
+ $name = sprintf('(value of type %s)', get_debug_type($name));
}
return new self(sprintf(
namespace Laminas\Diactoros\Exception;
-use function gettype;
-use function is_object;
+use function get_debug_type;
use function sprintf;
class InvalidProxyAddressException extends RuntimeException implements ExceptionInterface
{
public static function forInvalidProxyArgument(mixed $proxy): self
{
- $type = is_object($proxy) ? $proxy::class : gettype($proxy);
+ $type = get_debug_type($proxy);
return new self(sprintf(
'Invalid proxy of type "%s" provided;'
. ' must be a valid IPv4 or IPv6 address, optionally with a subnet mask provided'
namespace Laminas\Diactoros;
-use function gettype;
+use function get_debug_type;
use function in_array;
use function is_numeric;
-use function is_object;
use function is_string;
use function ord;
use function preg_match;
if (! is_string($value) && ! is_numeric($value)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header value type; must be a string or numeric; received %s',
- is_object($value) ? $value::class : gettype($value)
+ get_debug_type($value)
));
}
if (! self::isValid($value)) {
if (! is_string($name)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid header name type; expected string; received %s',
- is_object($name) ? $name::class : gettype($name)
+ get_debug_type($name)
));
}
if (! preg_match('/^[a-zA-Z0-9\'`#$%&*+.^_|~!-]+$/D', $name)) {
}
$host = $uri->getHost();
- if ($uri->getPort()) {
+ if ($uri->getPort() !== null) {
$host .= ':' . $uri->getPort();
}
private function getHostFromUri(): string
{
$host = $this->uri->getHost();
- $host .= $this->uri->getPort() ? ':' . $this->uri->getPort() : '';
+ $host .= $this->uri->getPort() !== null ? ':' . $this->uri->getPort() : '';
return $host;
}
}
use Laminas\Diactoros\Stream;
use Psr\Http\Message\StreamInterface;
-use function gettype;
-use function is_object;
+use function get_debug_type;
use function is_string;
use function sprintf;
if (! is_string($html)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid content (%s) provided to %s',
- is_object($html) ? $html::class : gettype($html),
+ get_debug_type($html),
self::class
));
}
use Laminas\Diactoros\Response;
use Psr\Http\Message\UriInterface;
-use function gettype;
-use function is_object;
+use function get_debug_type;
use function is_string;
use function sprintf;
throw new Exception\InvalidArgumentException(sprintf(
'Uri provided to %s MUST be a string or Psr\Http\Message\UriInterface instance; received "%s"',
self::class,
- is_object($uri) ? $uri::class : gettype($uri)
+ get_debug_type($uri)
));
}
use Laminas\Diactoros\Stream;
use Psr\Http\Message\StreamInterface;
-use function gettype;
-use function is_object;
+use function get_debug_type;
use function is_string;
use function sprintf;
if (! is_string($text)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid content (%s) provided to %s',
- is_object($text) ? $text::class : gettype($text),
+ get_debug_type($text),
self::class
));
}
use Laminas\Diactoros\Stream;
use Psr\Http\Message\StreamInterface;
-use function gettype;
-use function is_object;
+use function get_debug_type;
use function is_string;
use function sprintf;
if (! is_string($xml)) {
throw new Exception\InvalidArgumentException(sprintf(
'Invalid content (%s) provided to %s',
- is_object($xml) ? $xml::class : gettype($xml),
+ get_debug_type($xml),
self::class
));
}
$meta = stream_get_meta_data($this->resource);
$mode = $meta['mode'];
- return strstr($mode, 'x')
- || strstr($mode, 'w')
- || strstr($mode, 'c')
- || strstr($mode, 'a')
- || strstr($mode, '+');
+ return strstr($mode, 'x') !== false
+ || strstr($mode, 'w') !== false
+ || strstr($mode, 'c') !== false
+ || strstr($mode, 'a') !== false
+ || strstr($mode, '+') !== false;
}
/**
$meta = stream_get_meta_data($this->resource);
$mode = $meta['mode'];
- return strstr($mode, 'r') || strstr($mode, '+');
+ return strstr($mode, 'r') !== false || strstr($mode, '+') !== false;
}
/**
private bool $moved = false;
- /** @var null|StreamInterface */
- private $stream;
+ private ?StreamInterface $stream = null;
/**
* @param string|resource|StreamInterface $streamOrFile
$this->stream = new Stream($streamOrFile);
}
- if (! $this->file && ! $this->stream) {
+ if ($this->file === null && $this->stream === null) {
if (! $streamOrFile instanceof StreamInterface) {
throw new Exception\InvalidArgumentException('Invalid stream or file provided for UploadedFile');
}
$sapi = PHP_SAPI;
switch (true) {
- case empty($sapi) || str_starts_with($sapi, 'cli') || str_starts_with($sapi, 'phpdbg') || ! $this->file:
+ case empty($sapi)
+ || str_starts_with($sapi, 'cli')
+ || str_starts_with($sapi, 'phpdbg')
+ || $this->file === null:
// Non-SAPI environment, or no filename present
$this->writeFile($targetPath);
[$host, $port] = self::marshalHostAndPort($server, $headers);
if (! empty($host)) {
$uri = $uri->withHost($host);
- if (! empty($port)) {
+ if ($port !== null) {
$uri = $uri->withPort($port);
}
}
* Marshal the host and port from the PHP environment.
*
* @param array<string, string|list<string>> $headers
- * @return array{string, int|null} Array of two items, host and port,
+ * @return array{0:string, 1:int|null} Array of two items, host and port,
* in that order (can be passed to a list() operation).
*/
private static function marshalHostAndPort(array $server, array $headers): array
private static function marshalIpv6HostAndPort(array $server, ?int $port): array
{
$host = '[' . (string) $server['SERVER_ADDR'] . ']';
- $port = $port ?: 80;
+ $port = $port ?? 80;
$portSeparatorPos = strrpos($host, ':');
if (false === $portSeparatorPos) {
information about what you are encoding/decoding via processor cache
misses. Further reading on [cache-timing attacks](http://blog.ircmaxell.com/2014/11/its-all-about-time.html).
-Our fork offers the following enchancements:
+Our fork offers the following enhancements:
* `mbstring.func_overload` resistance
* Unit tests
* @param bool $strictPadding
* @return string
*/
- public static function decode(string $encodedString, bool $strictPadding = false): string
- {
+ public static function decode(
+ #[\SensitiveParameter]
+ string $encodedString,
+ bool $strictPadding = false
+ ): string {
return static::doDecode($encodedString, false, $strictPadding);
}
* @param bool $strictPadding
* @return string
*/
- public static function decodeUpper(string $src, bool $strictPadding = false): string
- {
+ public static function decodeUpper(
+ #[\SensitiveParameter]
+ string $src,
+ bool $strictPadding = false
+ ): string {
return static::doDecode($src, true, $strictPadding);
}
* @return string
* @throws TypeError
*/
- public static function encode(string $binString): string
- {
+ public static function encode(
+ #[\SensitiveParameter]
+ string $binString
+ ): string {
return static::doEncode($binString, false, true);
}
+
/**
* Encode into Base32 (RFC 4648)
*
* @return string
* @throws TypeError
*/
- public static function encodeUnpadded(string $src): string
- {
+ public static function encodeUnpadded(
+ #[\SensitiveParameter]
+ string $src
+ ): string {
return static::doEncode($src, false, false);
}
* @return string
* @throws TypeError
*/
- public static function encodeUpper(string $src): string
- {
+ public static function encodeUpper(
+ #[\SensitiveParameter]
+ string $src
+ ): string {
return static::doEncode($src, true, true);
}
* @return string
* @throws TypeError
*/
- public static function encodeUpperUnpadded(string $src): string
- {
+ public static function encodeUpperUnpadded(
+ #[\SensitiveParameter]
+ string $src
+ ): string {
return static::doEncode($src, true, false);
}
* @param bool $upper
* @return string
*/
- public static function decodeNoPadding(string $encodedString, bool $upper = false): string
- {
+ public static function decodeNoPadding(
+ #[\SensitiveParameter]
+ string $encodedString,
+ bool $upper = false
+ ): string {
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
* @return string
*
* @throws TypeError
- * @psalm-suppress RedundantCondition
*/
protected static function doDecode(
+ #[\SensitiveParameter]
string $src,
bool $upper = false,
bool $strictPadding = false
* @return string
* @throws TypeError
*/
- protected static function doEncode(string $src, bool $upper = false, $pad = true): string
- {
+ protected static function doEncode(
+ #[\SensitiveParameter]
+ string $src,
+ bool $upper = false,
+ $pad = true
+ ): string {
// We do this to reduce code duplication:
$method = $upper
? 'encode5BitsUpper'
*
* @throws TypeError
*/
- public static function encode(string $binString): string
- {
+ public static function encode(
+ #[\SensitiveParameter]
+ string $binString
+ ): string {
return static::doEncode($binString, true);
}
*
* @throws TypeError
*/
- public static function encodeUnpadded(string $src): string
- {
+ public static function encodeUnpadded(
+ #[\SensitiveParameter]
+ string $src
+ ): string {
return static::doEncode($src, false);
}
*
* @throws TypeError
*/
- protected static function doEncode(string $src, bool $pad = true): string
- {
+ protected static function doEncode(
+ #[\SensitiveParameter]
+ string $src,
+ bool $pad = true
+ ): string {
$dest = '';
$srcLen = Binary::safeStrlen($src);
// Main loop (no padding):
*
* @throws RangeException
* @throws TypeError
- * @psalm-suppress RedundantCondition
*/
- public static function decode(string $encodedString, bool $strictPadding = false): string
- {
+ public static function decode(
+ #[\SensitiveParameter]
+ string $encodedString,
+ bool $strictPadding = false
+ ): string {
// Remove padding
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
* @param string $encodedString
* @return string
*/
- public static function decodeNoPadding(string $encodedString): string
- {
+ public static function decodeNoPadding(
+ #[\SensitiveParameter]
+ string $encodedString
+ ): string {
$srcLen = Binary::safeStrlen($encodedString);
if ($srcLen === 0) {
return '';
}
if (($srcLen & 3) === 0) {
- if ($encodedString[$srcLen - 1] === '=') {
+ // If $strLen is not zero, and it is divisible by 4, then it's at least 4.
+ if ($encodedString[$srcLen - 1] === '=' || $encodedString[$srcLen - 2] === '=') {
throw new InvalidArgumentException(
"decodeNoPadding() doesn't tolerate padding"
);
}
- if (($srcLen & 3) > 1) {
- if ($encodedString[$srcLen - 2] === '=') {
- throw new InvalidArgumentException(
- "decodeNoPadding() doesn't tolerate padding"
- );
- }
- }
}
return static::decode(
$encodedString,
* @param string $str
* @return int
*/
- public static function safeStrlen(string $str): int
- {
+ public static function safeStrlen(
+ #[\SensitiveParameter]
+ string $str
+ ): int {
if (\function_exists('mb_strlen')) {
// mb_strlen in PHP 7.x can return false.
/** @psalm-suppress RedundantCast */
* @throws TypeError
*/
public static function safeSubstr(
+ #[\SensitiveParameter]
string $str,
int $start = 0,
$length = null
* @return string
* @throws TypeError
*/
- public static function base32Encode(string $str): string
- {
+ public static function base32Encode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::encode($str);
}
* @return string
* @throws TypeError
*/
- public static function base32EncodeUpper(string $str): string
- {
+ public static function base32EncodeUpper(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::encodeUpper($str);
}
* @return string
* @throws TypeError
*/
- public static function base32Decode(string $str): string
- {
+ public static function base32Decode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::decode($str);
}
* @return string
* @throws TypeError
*/
- public static function base32DecodeUpper(string $str): string
- {
+ public static function base32DecodeUpper(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::decodeUpper($str);
}
* @return string
* @throws TypeError
*/
- public static function base32HexEncode(string $str): string
- {
+ public static function base32HexEncode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32Hex::encode($str);
}
* @return string
* @throws TypeError
*/
- public static function base32HexEncodeUpper(string $str): string
- {
+ public static function base32HexEncodeUpper(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32Hex::encodeUpper($str);
}
* @return string
* @throws TypeError
*/
- public static function base32HexDecode(string $str): string
- {
+ public static function base32HexDecode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32Hex::decode($str);
}
* @return string
* @throws TypeError
*/
- public static function base32HexDecodeUpper(string $str): string
- {
+ public static function base32HexDecodeUpper(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32Hex::decodeUpper($str);
}
* @return string
* @throws TypeError
*/
- public static function base64Encode(string $str): string
- {
+ public static function base64Encode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64::encode($str);
}
* @return string
* @throws TypeError
*/
- public static function base64Decode(string $str): string
- {
+ public static function base64Decode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64::decode($str);
}
* @return string
* @throws TypeError
*/
- public static function base64EncodeDotSlash(string $str): string
- {
+ public static function base64EncodeDotSlash(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64DotSlash::encode($str);
}
* @throws \RangeException
* @throws TypeError
*/
- public static function base64DecodeDotSlash(string $str): string
- {
+ public static function base64DecodeDotSlash(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64DotSlash::decode($str);
}
* @return string
* @throws TypeError
*/
- public static function base64EncodeDotSlashOrdered(string $str): string
- {
+ public static function base64EncodeDotSlashOrdered(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64DotSlashOrdered::encode($str);
}
* @throws \RangeException
* @throws TypeError
*/
- public static function base64DecodeDotSlashOrdered(string $str): string
- {
+ public static function base64DecodeDotSlashOrdered(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64DotSlashOrdered::decode($str);
}
* @return string
* @throws TypeError
*/
- public static function hexEncode(string $bin_string): string
- {
+ public static function hexEncode(
+ #[\SensitiveParameter]
+ string $bin_string
+ ): string {
return Hex::encode($bin_string);
}
* @return string (raw binary)
* @throws \RangeException
*/
- public static function hexDecode(string $hex_string): string
- {
+ public static function hexDecode(
+ #[\SensitiveParameter]
+ string $hex_string
+ ): string {
return Hex::decode($hex_string);
}
* @return string
* @throws TypeError
*/
- public static function hexEncodeUpper(string $bin_string): string
- {
+ public static function hexEncodeUpper(
+ #[\SensitiveParameter]
+ string $bin_string
+ ): string {
return Hex::encodeUpper($bin_string);
}
* @param string $bin_string (raw binary)
* @return string
*/
- public static function hexDecodeUpper(string $bin_string): string
- {
+ public static function hexDecodeUpper(
+ #[\SensitiveParameter]
+ string $bin_string
+ ): string {
return Hex::decode($bin_string);
}
}
* @return string
* @throws TypeError
*/
- public static function encode(string $binString): string
- {
+ public static function encode(
+ #[\SensitiveParameter]
+ string $binString
+ ): string {
$hex = '';
$len = Binary::safeStrlen($binString);
for ($i = 0; $i < $len; ++$i) {
* @return string
* @throws TypeError
*/
- public static function encodeUpper(string $binString): string
- {
+ public static function encodeUpper(
+ #[\SensitiveParameter]
+ string $binString
+ ): string {
$hex = '';
$len = Binary::safeStrlen($binString);
* @throws RangeException
*/
public static function decode(
+ #[\SensitiveParameter]
string $encodedString,
bool $strictPadding = false
): string {
*
* @throws TypeError
*/
- public static function base64Encode(string $str): string
- {
+ public static function base64Encode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64::encode($str);
}
*
* @throws TypeError
*/
- public static function base64Decode(string $str): string
- {
+ public static function base64Decode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64::decode($str, true);
}
*
* @throws TypeError
*/
- public static function base64UrlSafeEncode(string $str): string
- {
+ public static function base64UrlSafeEncode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64UrlSafe::encode($str);
}
*
* @throws TypeError
*/
- public static function base64UrlSafeDecode(string $str): string
- {
+ public static function base64UrlSafeDecode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base64UrlSafe::decode($str, true);
}
*
* @throws TypeError
*/
- public static function base32Encode(string $str): string
- {
+ public static function base32Encode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::encodeUpper($str);
}
*
* @throws TypeError
*/
- public static function base32Decode(string $str): string
- {
+ public static function base32Decode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::decodeUpper($str, true);
}
*
* @throws TypeError
*/
- public static function base32HexEncode(string $str): string
- {
+ public static function base32HexEncode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::encodeUpper($str);
}
*
* @throws TypeError
*/
- public static function base32HexDecode(string $str): string
- {
+ public static function base32HexDecode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Base32::decodeUpper($str, true);
}
*
* @throws TypeError
*/
- public static function base16Encode(string $str): string
- {
+ public static function base16Encode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Hex::encodeUpper($str);
}
* @param string $str
* @return string
*/
- public static function base16Decode(string $str): string
- {
+ public static function base16Decode(
+ #[\SensitiveParameter]
+ string $str
+ ): string {
return Hex::decode($str, true);
}
-}
\ No newline at end of file
+}
{
"name": "psr/http-factory",
- "description": "Common interfaces for PSR-7 HTTP message factories",
+ "description": "PSR-17: Common interfaces for PSR-7 HTTP message factories",
"keywords": [
"psr",
"psr-7",
"homepage": "https://www.php-fig.org/"
}
],
+ "support": {
+ "source": "https://github.com/php-fig/http-factory"
+ },
"require": {
- "php": ">=7.0.0",
+ "php": ">=7.1",
"psr/http-message": "^1.0 || ^2.0"
},
"autoload": {
*
* @param StreamInterface $stream Underlying stream representing the
* uploaded file content.
- * @param int $size in bytes
+ * @param int|null $size in bytes
* @param int $error PHP file upload error
- * @param string $clientFilename Filename as provided by the client, if any.
- * @param string $clientMediaType Media type as provided by the client, if any.
+ * @param string|null $clientFilename Filename as provided by the client, if any.
+ * @param string|null $clientMediaType Media type as provided by the client, if any.
*
* @return UploadedFileInterface
*
*/
public function createUploadedFile(
StreamInterface $stream,
- int $size = null,
+ ?int $size = null,
int $error = \UPLOAD_ERR_OK,
- string $clientFilename = null,
- string $clientMediaType = null
+ ?string $clientFilename = null,
+ ?string $clientMediaType = null
): UploadedFileInterface;
}
-# Revision History
+# Changelog
+
+All notable changes to this project will be documented in this file.
+This project adheres to [Semantic Versioning](https://semver.org/).
+
+## x.y.z
+
+### Added
+
+### Changed
+
+### Deprecated
+
+### Removed
+
+### Fixed
+
+## 8.5.1
+
+### Fixed
+
+- Fix (regression) failure to parse at-rules with strict parsing (#456)
+
+## 8.5.0
+
+### Added
+
+- Add a method to get an import's media queries (#384)
+- Add more unit tests (#381, #382)
+
+### Fixed
+
+- Retain CSSList and Rule comments when rendering CSS (#351)
+- Replace invalid `turns` unit with `turn` (#350)
+- Also allow string values for rules (#348)
+- Fix invalid calc parsing (#169)
+- Handle scientific notation when parsing sizes (#179)
+- Fix PHP 8.1 compatibility in `ParserState::strsplit()` (#344)
## 8.4.0
#### Charset
-The charset option is used only if no `@charset` declaration is found in the CSS file. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that.
+The charset option will only be used if the CSS file does not contain an `@charset` declaration. UTF-8 is the default, so you won’t have to create a settings object at all if you don’t intend to change that.
```php
$settings = \Sabberworm\CSS\Settings::create()
#### Strict parsing
-To have the parser choke on invalid rules, supply a thusly configured `\Sabberworm\CSS\Settings` object:
+To have the parser throw an exception when encountering invalid/unknown constructs (as opposed to trying to ignore them and carry on parsing), supply a thusly configured `\Sabberworm\CSS\Settings` object:
```php
$parser = new \Sabberworm\CSS\Parser(
);
```
+Note that this will also disable a workaround for parsing the unquoted variant of the legacy IE-specific `filter` rule.
+
#### Disable multibyte functions
To achieve faster parsing, you can choose to have PHP-CSS-Parser use regular string functions instead of `mb_*` functions. This should work fine in most cases, even for UTF-8 files, as all the multibyte characters are in string literals. Still it’s not recommended using this with input you have no control over as it’s not thoroughly covered by test cases.
#### CSSList
-`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc. `CSSList` has the following concrete subtypes:
-
-* `Document` – representing the root of a CSS file.
-* `MediaQuery` – represents a subsection of a `CSSList` that only applies to an output device matching the contained media query.
+`CSSList` represents a generic CSS container, most likely containing declaration blocks (rule sets with a selector), but it may also contain at-rules, charset declarations, etc.
-To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use instanceof to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`.
+To access the items stored in a `CSSList` – like the document you got back when calling `$parser->parse()` –, use `getContents()`, then iterate over that collection and use `instanceof` to check whether you’re dealing with another `CSSList`, a `RuleSet`, a `Import` or a `Charset`.
To append a new item (selector, media query, etc.) to an existing `CSSList`, construct it using the constructor for this class and use the `append($oItem)` method.
`RuleSet` is a container for individual rules. The most common form of a rule set is one constrained by a selector. The following concrete subtypes exist:
-* `AtRuleSet` – for generic at-rules which do not match the ones specifically mentioned like `@import`, `@charset` or `@media`. A common example for this is `@font-face`.
+* `AtRuleSet` – for generic at-rules for generic at-rules which are not covered by specific classes, i.e., not `@import`, `@charset` or `@media`. A common example for this is `@font-face`.
* `DeclarationBlock` – a `RuleSet` constrained by a `Selector`; contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the matching elements.
Note: A `CSSList` can contain other `CSSList`s (and `Import`s as well as a `Charset`), while a `RuleSet` can only contain `Rule`s.
-If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` instance or a rule name; optionally suffixed by a dash to remove all related rules).
+If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)` (which accepts either a `Rule` or a rule name; optionally suffixed by a dash to remove all related rules).
#### Rule
-`Rule`s just have a key (the rule) and a value. These values are all instances of a `Value`.
+`Rule`s just have a string key (the rule) and a `Value`.
#### Value
* `Size` – consists of a numeric `size` value and a unit.
* `Color` – colors can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form.
* `CSSString` – this is just a wrapper for quoted strings to distinguish them from keywords; always output with double quotes.
-* `URL` – URLs in CSS; always output in URL("") notation.
+* `URL` – URLs in CSS; always output in `URL("")` notation.
+
+There is another abstract subclass of `Value`, `ValueList`: A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`).
-There is another abstract subclass of `Value`, `ValueList`. A `ValueList` represents a lists of `Value`s, separated by some separation character (mostly `,`, whitespace, or `/`). There are two types of `ValueList`s:
+There are two types of `ValueList`s:
-* `RuleValueList` – The default type, used to represent all multi-valued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list).
+* `RuleValueList` – The default type, used to represent all multivalued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;` (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list and a comma-separated list).
* `CSSFunction` – A special kind of value that also contains a function name and where the values are the function’s arguments. Also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`.
#### Convenience methods
-There are a few convenience methods on Document to ease finding, manipulating and deleting rules:
+There are a few convenience methods on `Document` to ease finding, manipulating and deleting rules:
-* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested your selectors are. Aliased as `getAllSelectors()`.
-* `getAllRuleSets()` – does what it says; no matter how deeply nested your rule sets are.
+* `getAllDeclarationBlocks()` – does what it says; no matter how deeply nested the selectors are. Aliased as `getAllSelectors()`.
+* `getAllRuleSets()` – does what it says; no matter how deeply nested the rule sets are.
* `getAllValues()` – finds all `Value` objects inside `Rule`s.
## To-Do
foreach($cssDocument->getAllRuleSets() as $oRuleSet) {
// Note that the added dash will make this remove all rules starting with
// `font-` (like `font-size`, `font-weight`, etc.) as well as a potential
- // `font-rule`.
+ // `font` rule.
$oRuleSet->removeRule('font-');
$oRuleSet->removeRule('cursor');
}
```
-#### Structure (`var_dump()`)
+<details>
+ <summary><b>Structure (<code>var_dump()</code>)</b></summary>
```php
class Sabberworm\CSS\CSSList\Document#4 (2) {
}
```
+</details>
#### Output (`render()`)
```
-#### Structure (`var_dump()`)
+<details>
+ <summary><b>Structure (<code>var_dump()</code>)</b></summary>
```php
class Sabberworm\CSS\CSSList\Document#4 (2) {
}
```
+</details>
#### Output (`render()`)
"authors": [
{
"name": "Raphael Schweikert"
+ },
+ {
+ "name": "Oliver Klee",
+ "email": "github@oliverklee.de"
+ },
+ {
+ "name": "Jake Hotson",
+ "email": "jake.github@qzdesign.co.uk"
}
],
"require": {
"ext-iconv": "*"
},
"require-dev": {
- "phpunit/phpunit": "^4.8.36",
- "codacy/coverage": "^1.4"
+ "phpunit/phpunit": "^5.7.27"
},
"suggest": {
"ext-mbstring": "for parsing UTF-8 CSS"
"Sabberworm\\CSS\\Tests\\": "tests/"
}
},
+ "extra": {
+ "branch-alias": {
+ "dev-main": "9.0.x-dev"
+ }
+ },
"scripts": {
"ci": [
"@ci:static"
*/
public function render(OutputFormat $oOutputFormat)
{
+ $sResult = $oOutputFormat->comments($this);
+ $sResult .= $oOutputFormat->sBeforeAtRuleBlock;
$sArgs = $this->sArgs;
if ($sArgs) {
$sArgs = ' ' . $sArgs;
}
- $sResult = $oOutputFormat->sBeforeAtRuleBlock;
$sResult .= "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
- $sResult .= parent::render($oOutputFormat);
+ $sResult .= $this->renderListContents($oOutputFormat);
$sResult .= '}';
$sResult .= $oOutputFormat->sAfterAtRuleBlock;
return $sResult;
use Sabberworm\CSS\Value\Value;
/**
- * A `CSSList` is the most generic container available. Its contents include `RuleSet` as well as other `CSSList`
- * objects.
+ * This is the most generic container available. It can contain `DeclarationBlock`s (rule sets with a selector),
+ * `RuleSet`s as well as other `CSSList` objects.
*
- * Also, it may contain `Import` and `Charset` objects stemming from at-rules.
+ * It can also contain `Import` and `Charset` objects stemming from at-rules.
*/
abstract class CSSList implements Renderable, Commentable
{
$oParserState = new ParserState($oParserState, Settings::create());
}
$bLenientParsing = $oParserState->getSettings()->bLenientParsing;
+ $aComments = [];
while (!$oParserState->isEnd()) {
- $comments = $oParserState->consumeWhiteSpace();
+ $aComments = array_merge($aComments, $oParserState->consumeWhiteSpace());
$oListItem = null;
if ($bLenientParsing) {
try {
return;
}
if ($oListItem) {
- $oListItem->setComments($comments);
+ $oListItem->addComments($aComments);
$oList->append($oListItem);
}
- $oParserState->consumeWhiteSpace();
+ $aComments = $oParserState->consumeWhiteSpace();
}
+ $oList->addComments($aComments);
if (!$bIsRoot && !$bLenientParsing) {
throw new SourceException("Unexpected end of document", $oParserState->currentLine());
}
$oParserState->currentLine()
);
}
- $oParserState->setCharset($oAtRule->getCharset()->getString());
+ $oParserState->setCharset($oAtRule->getCharset());
}
return $oAtRule;
} elseif ($oParserState->comes('}')) {
- if (!$oParserState->getSettings()->bLenientParsing) {
- throw new UnexpectedTokenException('CSS selector', '}', 'identifier', $oParserState->currentLine());
- } else {
- if ($bIsRoot) {
- if ($oParserState->getSettings()->bLenientParsing) {
- return DeclarationBlock::parse($oParserState);
- } else {
- throw new SourceException("Unopened {", $oParserState->currentLine());
- }
+ if ($bIsRoot) {
+ if ($oParserState->getSettings()->bLenientParsing) {
+ return DeclarationBlock::parse($oParserState);
} else {
- return null;
+ throw new SourceException("Unopened {", $oParserState->currentLine());
}
+ } else {
+ // End of list
+ return null;
}
} else {
return DeclarationBlock::parse($oParserState, $oList);
$oParserState->consumeUntil([';', ParserState::EOF], true, true);
return new Import($oLocation, $sMediaQuery ?: null, $iIdentifierLineNum);
} elseif ($sIdentifier === 'charset') {
- $sCharset = CSSString::parse($oParserState);
+ $oCharsetString = CSSString::parse($oParserState);
$oParserState->consumeWhiteSpace();
$oParserState->consumeUntil([';', ParserState::EOF], true, true);
- return new Charset($sCharset, $iIdentifierLineNum);
+ return new Charset($oCharsetString, $iIdentifierLineNum);
} elseif (self::identifierIs($sIdentifier, 'keyframes')) {
$oResult = new KeyFrame($iIdentifierLineNum);
$oResult->setVendorKeyFrame($sIdentifier);
}
/**
- * Appends an item to tje list of contents.
+ * Appends an item to the list of contents.
*
* @param RuleSet|CSSList|Import|Charset $oItem
*
/**
* @return string
*/
- public function render(OutputFormat $oOutputFormat)
+ protected function renderListContents(OutputFormat $oOutputFormat)
{
$sResult = '';
$bIsFirst = true;
abstract public function isRootList();
/**
+ * Returns the stored items.
+ *
* @return array<int, RuleSet|Import|Charset|CSSList>
*/
public function getContents()
use Sabberworm\CSS\Value\Value;
/**
- * The root `CSSList` of a parsed file. Contains all top-level CSS contents, mostly declaration blocks,
- * but also any at-rules encountered.
+ * This class represents the root of a parsed CSS file. It contains all top-level CSS contents: mostly declaration
+ * blocks, but also any at-rules encountered (`Import` and `Charset`).
*/
class Document extends CSSBlockList
{
}
/**
- * Gets all `DeclarationBlock` objects recursively.
+ * Gets all `DeclarationBlock` objects recursively, no matter how deeply nested the selectors are.
+ * Aliased as `getAllSelectors()`.
*
* @return array<int, DeclarationBlock>
*/
}
/**
- * Returns all `RuleSet` objects found recursively in the tree.
+ * Returns all `RuleSet` objects recursively found in the tree, no matter how deeply nested the rule sets are.
*
* @return array<int, RuleSet>
*/
}
/**
- * Returns all `Value` objects found recursively in the tree.
+ * Returns all `Value` objects found recursively in `Rule`s in the tree.
*
* @param CSSList|RuleSet|string $mElement
* the `CSSList` or `RuleSet` to start the search from (defaults to the whole document).
}
/**
- * Returns all `Selector` objects found recursively in the tree.
+ * Returns all `Selector` objects with the requested specificity found recursively in the tree.
*
* Note that this does not yield the full `DeclarationBlock` that the selector belongs to
* (and, currently, there is no way to get to that).
if ($oOutputFormat === null) {
$oOutputFormat = new OutputFormat();
}
- return parent::render($oOutputFormat);
+ return $oOutputFormat->comments($this) . $this->renderListContents($oOutputFormat);
}
/**
*/
public function render(OutputFormat $oOutputFormat)
{
- $sResult = "@{$this->vendorKeyFrame} {$this->animationName}{$oOutputFormat->spaceBeforeOpeningBrace()}{";
- $sResult .= parent::render($oOutputFormat);
+ $sResult = $oOutputFormat->comments($this);
+ $sResult .= "@{$this->vendorKeyFrame} {$this->animationName}{$oOutputFormat->spaceBeforeOpeningBrace()}{";
+ $sResult .= $this->renderListContents($oOutputFormat);
$sResult .= '}';
return $sResult;
}
*/
public $bIgnoreExceptions = false;
+ /**
+ * Render comments for lists and RuleSets
+ *
+ * @var bool
+ */
+ public $bRenderComments = false;
+
/**
* @var OutputFormatter|null
*/
public static function createCompact()
{
$format = self::create();
- $format->set('Space*Rules', "")->set('Space*Blocks', "")->setSpaceAfterRuleName('')
- ->setSpaceBeforeOpeningBrace('')->setSpaceAfterSelectorSeparator('');
+ $format->set('Space*Rules', "")
+ ->set('Space*Blocks', "")
+ ->setSpaceAfterRuleName('')
+ ->setSpaceBeforeOpeningBrace('')
+ ->setSpaceAfterSelectorSeparator('')
+ ->setRenderComments(false);
return $format;
}
public static function createPretty()
{
$format = self::create();
- $format->set('Space*Rules', "\n")->set('Space*Blocks', "\n")
- ->setSpaceBetweenBlocks("\n\n")->set('SpaceAfterListArgumentSeparator', ['default' => '', ',' => ' ']);
+ $format->set('Space*Rules', "\n")
+ ->set('Space*Blocks', "\n")
+ ->setSpaceBetweenBlocks("\n\n")
+ ->set('SpaceAfterListArgumentSeparator', ['default' => '', ',' => ' '])
+ ->setRenderComments(true);
return $format;
}
}
namespace Sabberworm\CSS;
+use Sabberworm\CSS\Comment\Commentable;
use Sabberworm\CSS\Parsing\OutputException;
class OutputFormatter
return implode(';', $sString);
}
+ /**
+ *
+ * @param array<Commentable> $aComments
+ *
+ * @return string
+ */
+ public function comments(Commentable $oCommentable)
+ {
+ if (!$this->oFormat->bRenderComments) {
+ return '';
+ }
+
+ $sResult = '';
+ $aComments = $oCommentable->getComments();
+ $iLastCommentIndex = count($aComments) - 1;
+
+ foreach ($aComments as $i => $oComment) {
+ $sResult .= $oComment->render($this->oFormat);
+ $sResult .= $i === $iLastCommentIndex ? $this->spaceAfterBlocks() : $this->spaceBetweenBlocks();
+ }
+ return $sResult;
+ }
+
/**
* @param string $sSpaceString
*
private $oParserState;
/**
- * @param string $sText
+ * @param string $sText the complete CSS as text (i.e., usually the contents of a CSS file)
* @param Settings|null $oParserSettings
* @param int $iLineNo the line number (starting from 1, not from 0)
*/
}
/**
+ * Sets the charset to be used if the CSS does not contain an `@charset` declaration.
+ *
* @param string $sCharset
*
* @return void
}
/**
+ * Returns the charset that is used if the CSS does not contain an `@charset` declaration.
+ *
* @return void
*/
public function getCharset()
}
/**
+ * Parses the CSS provided to the constructor and creates a `Document` from it.
+ *
* @return Document
*
* @throws SourceException
--- /dev/null
+<?php
+
+namespace Sabberworm\CSS\Parsing;
+
+class Anchor
+{
+ /**
+ * @var int
+ */
+ private $iPosition;
+
+ /**
+ * @var \Sabberworm\CSS\Parsing\ParserState
+ */
+ private $oParserState;
+
+ /**
+ * @param int $iPosition
+ * @param \Sabberworm\CSS\Parsing\ParserState $oParserState
+ */
+ public function __construct($iPosition, ParserState $oParserState)
+ {
+ $this->iPosition = $iPosition;
+ $this->oParserState = $oParserState;
+ }
+
+ /**
+ * @return void
+ */
+ public function backtrack()
+ {
+ $this->oParserState->setPosition($this->iPosition);
+ }
+}
private $iCurrentPosition;
/**
+ * will only be used if the CSS does not contain an `@charset` declaration
+ *
* @var string
*/
private $sCharset;
private $iLineNo;
/**
- * @param string $sText
+ * @param string $sText the complete CSS as text (i.e., usually the contents of a CSS file)
* @param int $iLineNo
*/
public function __construct($sText, Settings $oParserSettings, $iLineNo = 1)
}
/**
+ * Sets the charset to be used if the CSS does not contain an `@charset` declaration.
+ *
* @param string $sCharset
*
* @return void
}
/**
+ * Returns the charset that is used if the CSS does not contain an `@charset` declaration.
+ *
* @return string
*/
public function getCharset()
return $this->oParserSettings;
}
+ /**
+ * @return \Sabberworm\CSS\Parsing\Anchor
+ */
+ public function anchor()
+ {
+ return new Anchor($this->iCurrentPosition, $this);
+ }
+
+ /**
+ * @param int $iPosition
+ *
+ * @return void
+ */
+ public function setPosition($iPosition)
+ {
+ $this->iCurrentPosition = $iPosition;
+ }
+
/**
* @param bool $bIgnoreCase
*
*/
public function parseIdentifier($bIgnoreCase = true)
{
+ if ($this->isEnd()) {
+ throw new UnexpectedEOFException('', '', 'identifier', $this->iLineNo);
+ }
$sResult = $this->parseCharacter(true);
if ($sResult === null) {
throw new UnexpectedTokenException($sResult, $this->peek(5), 'identifier', $this->iLineNo);
}
$sCharacter = null;
- while (($sCharacter = $this->parseCharacter(true)) !== null) {
+ while (!$this->isEnd() && ($sCharacter = $this->parseCharacter(true)) !== null) {
if (preg_match('/[a-zA-Z0-9\x{00A0}-\x{FFFF}_-]/Sux', $sCharacter)) {
$sResult .= $sCharacter;
} else {
*/
public function consumeWhiteSpace()
{
- $comments = [];
+ $aComments = [];
do {
while (preg_match('/\\s/isSu', $this->peek()) === 1) {
$this->consume(1);
$oComment = $this->consumeComment();
} catch (UnexpectedEOFException $e) {
$this->iCurrentPosition = $this->iLength;
- return;
+ return $aComments;
}
} else {
$oComment = $this->consumeComment();
}
if ($oComment !== false) {
- $comments[] = $oComment;
+ $aComments[] = $oComment;
}
} while ($oComment !== false);
- return $comments;
+ return $aComments;
}
/**
use Sabberworm\CSS\Comment\Comment;
use Sabberworm\CSS\OutputFormat;
+use Sabberworm\CSS\Value\CSSString;
/**
* Class representing an `@charset` rule.
class Charset implements AtRule
{
/**
- * @var string
+ * @var CSSString
*/
- private $sCharset;
+ private $oCharset;
/**
* @var int
protected $aComments;
/**
- * @param string $sCharset
+ * @param CSSString $oCharset
* @param int $iLineNo
*/
- public function __construct($sCharset, $iLineNo = 0)
+ public function __construct(CSSString $oCharset, $iLineNo = 0)
{
- $this->sCharset = $sCharset;
+ $this->oCharset = $oCharset;
$this->iLineNo = $iLineNo;
$this->aComments = [];
}
}
/**
- * @param string $sCharset
+ * @param string|CSSString $oCharset
*
* @return void
*/
public function setCharset($sCharset)
{
- $this->sCharset = $sCharset;
+ $sCharset = $sCharset instanceof CSSString ? $sCharset : new CSSString($sCharset);
+ $this->oCharset = $sCharset;
}
/**
*/
public function getCharset()
{
- return $this->sCharset;
+ return $this->oCharset->getString();
}
/**
*/
public function render(OutputFormat $oOutputFormat)
{
- return "@charset {$this->sCharset->render($oOutputFormat)};";
+ return "{$oOutputFormat->comments($this)}@charset {$this->oCharset->render($oOutputFormat)};";
}
/**
*/
public function atRuleArgs()
{
- return $this->sCharset;
+ return $this->oCharset;
}
/**
*/
public function render(OutputFormat $oOutputFormat)
{
- return "@import " . $this->oLocation->render($oOutputFormat)
+ return $oOutputFormat->comments($this) . "@import " . $this->oLocation->render($oOutputFormat)
. ($this->sMediaQuery === null ? '' : ' ' . $this->sMediaQuery) . ';';
}
{
$this->aComments = $aComments;
}
+
+ /**
+ * @return string
+ */
+ public function getMediaQuery()
+ {
+ return $this->sMediaQuery;
+ }
}
use Sabberworm\CSS\Value\Value;
/**
- * RuleSets contains Rule objects which always have a key and a value.
- * In CSS, Rules are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];”
+ * `Rule`s just have a string key (the rule) and a 'Value'.
+ *
+ * In CSS, `Rule`s are expressed as follows: “key: value[0][0] value[0][1], value[1][0] value[1][1];”
*/
class Rule implements Renderable, Commentable
{
private $sRule;
/**
- * @var RuleValueList|null
+ * @var RuleValueList|string|null
*/
private $mValue;
}
/**
- * @return RuleValueList|null
+ * @return RuleValueList|string|null
*/
public function getValue()
{
}
/**
- * @param RuleValueList|null $mValue
+ * @param RuleValueList|string|null $mValue
*
* @return void
*/
*/
public function render(OutputFormat $oOutputFormat)
{
- $sResult = "{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}";
- if ($this->mValue instanceof Value) { //Can also be a ValueList
+ $sResult = "{$oOutputFormat->comments($this)}{$this->sRule}:{$oOutputFormat->spaceAfterRuleName()}";
+ if ($this->mValue instanceof Value) { // Can also be a ValueList
$sResult .= $this->mValue->render($oOutputFormat);
} else {
$sResult .= $this->mValue;
use Sabberworm\CSS\Property\AtRule;
/**
- * A RuleSet constructed by an unknown at-rule. `@font-face` rules are rendered into AtRuleSet objects.
+ * This class represents rule sets for generic at-rules which are not covered by specific classes, i.e., not
+ * `@import`, `@charset` or `@media`.
+ *
+ * A common example for this is `@font-face`.
*/
class AtRuleSet extends RuleSet implements AtRule
{
*/
public function render(OutputFormat $oOutputFormat)
{
+ $sResult = $oOutputFormat->comments($this);
$sArgs = $this->sArgs;
if ($sArgs) {
$sArgs = ' ' . $sArgs;
}
- $sResult = "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
- $sResult .= parent::render($oOutputFormat);
+ $sResult .= "@{$this->sType}$sArgs{$oOutputFormat->spaceBeforeOpeningBrace()}{";
+ $sResult .= $this->renderRules($oOutputFormat);
$sResult .= '}';
return $sResult;
}
use Sabberworm\CSS\Value\Value;
/**
- * Declaration blocks are the parts of a CSS file which denote the rules belonging to a selector.
+ * This class represents a `RuleSet` constrained by a `Selector`.
+ *
+ * It contains an array of selector objects (comma-separated in the CSS) as well as the rules to be applied to the
+ * matching elements.
*
* Declaration blocks usually appear directly inside a `Document` or another `CSSList` (mostly a `MediaQuery`).
*/
public function createShorthandProperties(array $aProperties, $sShorthand)
{
$aRules = $this->getRulesAssoc();
+ $oRule = null;
$aNewValues = [];
foreach ($aProperties as $sProperty) {
if (!isset($aRules[$sProperty])) {
$this->removeRule($sProperty);
}
}
- if (count($aNewValues)) {
+ if ($aNewValues !== [] && $oRule instanceof Rule) {
$oNewRule = new Rule($sShorthand, $oRule->getLineNo(), $oRule->getColNo());
foreach ($aNewValues as $mValue) {
$oNewRule->addValue($mValue);
*/
public function render(OutputFormat $oOutputFormat)
{
+ $sResult = $oOutputFormat->comments($this);
if (count($this->aSelectors) === 0) {
// If all the selectors have been removed, this declaration block becomes invalid
throw new OutputException("Attempt to print declaration block with missing selector", $this->iLineNo);
}
- $sResult = $oOutputFormat->sBeforeDeclarationBlock;
+ $sResult .= $oOutputFormat->sBeforeDeclarationBlock;
$sResult .= $oOutputFormat->implode(
$oOutputFormat->spaceBeforeSelectorSeparator() . ',' . $oOutputFormat->spaceAfterSelectorSeparator(),
$this->aSelectors
);
$sResult .= $oOutputFormat->sAfterDeclarationBlockSelectors;
$sResult .= $oOutputFormat->spaceBeforeOpeningBrace() . '{';
- $sResult .= parent::render($oOutputFormat);
+ $sResult .= $this->renderRules($oOutputFormat);
$sResult .= '}';
$sResult .= $oOutputFormat->sAfterDeclarationBlock;
return $sResult;
use Sabberworm\CSS\Rule\Rule;
/**
- * RuleSet is a generic superclass denoting rules. The typical example for rule sets are declaration block.
- * However, unknown At-Rules (like `@font-face`) are also rule sets.
+ * This class is a container for individual 'Rule's.
+ *
+ * The most common form of a rule set is one constrained by a selector, i.e., a `DeclarationBlock`.
+ * However, unknown `AtRule`s (like `@font-face`) are rule sets as well.
+ *
+ * If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `getRules()` and `removeRule($rule)`
+ * (which accepts either a `Rule` or a rule name; optionally suffixed by a dash to remove all related rules).
*/
abstract class RuleSet implements Renderable, Commentable
{
/**
* @return string
*/
- public function render(OutputFormat $oOutputFormat)
+ protected function renderRules(OutputFormat $oOutputFormat)
{
$sResult = '';
$bIsFirst = true;
+ $oNextLevel = $oOutputFormat->nextLevel();
foreach ($this->aRules as $aRules) {
foreach ($aRules as $oRule) {
- $sRendered = $oOutputFormat->safely(function () use ($oRule, $oOutputFormat) {
- return $oRule->render($oOutputFormat->nextLevel());
+ $sRendered = $oNextLevel->safely(function () use ($oRule, $oNextLevel) {
+ return $oRule->render($oNextLevel);
});
if ($sRendered === null) {
continue;
}
if ($bIsFirst) {
$bIsFirst = false;
- $sResult .= $oOutputFormat->nextLevel()->spaceBeforeRules();
+ $sResult .= $oNextLevel->spaceBeforeRules();
} else {
- $sResult .= $oOutputFormat->nextLevel()->spaceBetweenRules();
+ $sResult .= $oNextLevel->spaceBetweenRules();
}
$sResult .= $sRendered;
}
{
/**
* Multi-byte string support.
- * If true (mbstring extension must be enabled), will use (slower) `mb_strlen`, `mb_convert_case`, `mb_substr`
+ *
+ * If `true` (`mbstring` extension must be enabled), will use (slower) `mb_strlen`, `mb_convert_case`, `mb_substr`
* and `mb_strpos` functions. Otherwise, the normal (ASCII-Only) functions will be used.
*
* @var bool
public $bMultibyteSupport;
/**
- * The default charset for the CSS if no `@charset` rule is found. Defaults to utf-8.
+ * The default charset for the CSS if no `@charset` declaration is found. Defaults to utf-8.
*
* @var string
*/
public $sDefaultCharset = 'utf-8';
/**
- * Lenient parsing. When used (which is true by default), the parser will not choke
- * on unexpected tokens but simply ignore them.
+ * Whether the parser silently ignore invalid rules instead of choking on them.
*
* @var bool
*/
}
/**
+ * Enables/disables multi-byte string support.
+ *
+ * If `true` (`mbstring` extension must be enabled), will use (slower) `mb_strlen`, `mb_convert_case`, `mb_substr`
+ * and `mb_strpos` functions. Otherwise, the normal (ASCII-Only) functions will be used.
+ *
* @param bool $bMultibyteSupport
*
* @return self fluent interface
}
/**
+ * Sets the charset to be used if the CSS does not contain an `@charset` declaration.
+ *
* @param string $sDefaultCharset
*
* @return self fluent interface
}
/**
+ * Configures whether the parser should silently ignore invalid rules.
+ *
* @param bool $bLenientParsing
*
* @return self fluent interface
}
/**
+ * Configures the parser to choke on invalid rules.
+ *
* @return self fluent interface
*/
public function beStrict()
namespace Sabberworm\CSS\Value;
use Sabberworm\CSS\OutputFormat;
+use Sabberworm\CSS\Parsing\ParserState;
+/**
+ * A `CSSFunction` represents a special kind of value that also contains a function name and where the values are the
+ * function’s arguments. It also handles equals-sign-separated argument lists like `filter: alpha(opacity=90);`.
+ */
class CSSFunction extends ValueList
{
/**
parent::__construct($aArguments, $sSeparator, $iLineNo);
}
+ /**
+ * @param ParserState $oParserState
+ * @param bool $bIgnoreCase
+ *
+ * @return CSSFunction
+ *
+ * @throws SourceException
+ * @throws UnexpectedEOFException
+ * @throws UnexpectedTokenException
+ */
+ public static function parse(ParserState $oParserState, $bIgnoreCase = false)
+ {
+ $mResult = $oParserState->parseIdentifier($bIgnoreCase);
+ $oParserState->consume('(');
+ $aArguments = Value::parseValue($oParserState, ['=', ' ', ',']);
+ $mResult = new CSSFunction($mResult, $aArguments, ',', $oParserState->currentLine());
+ $oParserState->consume(')');
+ return $mResult;
+ }
+
/**
* @return string
*/
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
+/**
+ * This class is a wrapper for quoted strings to distinguish them from keywords.
+ *
+ * `CSSString`s always output with double quotes.
+ */
class CSSString extends PrimitiveValue
{
/**
const T_OPERATOR = 2;
/**
+ * @param ParserState $oParserState
+ * @param bool $bIgnoreCase
+ *
* @return CalcFunction
*
* @throws UnexpectedTokenException
* @throws UnexpectedEOFException
*/
- public static function parse(ParserState $oParserState)
+ public static function parse(ParserState $oParserState, $bIgnoreCase = false)
{
$aOperators = ['+', '-', '*', '/'];
- $sFunction = trim($oParserState->consumeUntil('(', false, true));
+ $sFunction = $oParserState->parseIdentifier();
+ if ($oParserState->peek() != '(') {
+ // Found ; or end of line before an opening bracket
+ throw new UnexpectedTokenException('(', $oParserState->peek(), 'literal', $oParserState->currentLine());
+ } elseif (!in_array($sFunction, ['calc', '-moz-calc', '-webkit-calc'])) {
+ // Found invalid calc definition. Example calc (...
+ throw new UnexpectedTokenException('calc', $sFunction, 'literal', $oParserState->currentLine());
+ }
+ $oParserState->consume('(');
$oCalcList = new CalcRuleValueList($oParserState->currentLine());
$oList = new RuleValueList(',', $oParserState->currentLine());
$iNestingLevel = 0;
$iLastComponentType = null;
while (!$oParserState->comes(')') || $iNestingLevel > 0) {
+ if ($oParserState->isEnd() && $iNestingLevel === 0) {
+ break;
+ }
+
$oParserState->consumeWhiteSpace();
if ($oParserState->comes('(')) {
$iNestingLevel++;
$oParserState->consumeWhiteSpace();
}
$oList->addListComponent($oCalcList);
- $oParserState->consume(')');
+ if (!$oParserState->isEnd()) {
+ $oParserState->consume(')');
+ }
return new CalcFunction($sFunction, $oList, ',', $oParserState->currentLine());
}
}
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
+/**
+ * `Color's can be input in the form #rrggbb, #rgb or schema(val1, val2, …) but are always stored as an array of
+ * ('s' => val1, 'c' => val2, 'h' => val3, …) and output in the second form.
+ */
class Color extends CSSFunction
{
/**
}
/**
+ * @param ParserState $oParserState
+ * @param bool $bIgnoreCase
+ *
* @return Color|CSSFunction
*
* @throws UnexpectedEOFException
* @throws UnexpectedTokenException
*/
- public static function parse(ParserState $oParserState)
+ public static function parse(ParserState $oParserState, $bIgnoreCase = false)
{
$aColor = [];
if ($oParserState->comes('#')) {
namespace Sabberworm\CSS\Value;
+/**
+ * This class is used to represent all multivalued rules like `font: bold 12px/3 Helvetica, Verdana, sans-serif;`
+ * (where the value would be a whitespace-separated list of the primitive value `bold`, a slash-separated list
+ * and a comma-separated list).
+ */
class RuleValueList extends ValueList
{
/**
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
+/**
+ * A `Size` consists of a numeric `size` value and a unit.
+ */
class Size extends PrimitiveValue
{
/**
/**
* @var array<int, string>
*/
- const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turns', 'Hz', 'kHz'];
+ const NON_SIZE_UNITS = ['deg', 'grad', 'rad', 's', 'ms', 'turn', 'Hz', 'kHz'];
/**
* @var array<int, array<string, string>>|null
if ($oParserState->comes('-')) {
$sSize .= $oParserState->consume('-');
}
- while (is_numeric($oParserState->peek()) || $oParserState->comes('.')) {
+ while (is_numeric($oParserState->peek()) || $oParserState->comes('.') || $oParserState->comes('e', true)) {
if ($oParserState->comes('.')) {
$sSize .= $oParserState->consume('.');
+ } elseif ($oParserState->comes('e', true)) {
+ $sLookahead = $oParserState->peek(1, 1);
+ if (is_numeric($sLookahead) || $sLookahead === '+' || $sLookahead === '-') {
+ $sSize .= $oParserState->consume(2);
+ } else {
+ break; // Reached the unit part of the number like "em" or "ex"
+ }
} else {
$sSize .= $oParserState->consume(1);
}
use Sabberworm\CSS\Parsing\UnexpectedEOFException;
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
+/**
+ * This class represents URLs in CSS. `URL`s always output in `URL("")` notation.
+ */
class URL extends PrimitiveValue
{
/**
*/
public static function parse(ParserState $oParserState)
{
- $bUseUrl = $oParserState->comes('url', true);
+ $oAnchor = $oParserState->anchor();
+ $sIdentifier = '';
+ for ($i = 0; $i < 3; $i++) {
+ $sChar = $oParserState->parseCharacter(true);
+ if ($sChar === null) {
+ break;
+ }
+ $sIdentifier .= $sChar;
+ }
+ $bUseUrl = $oParserState->streql($sIdentifier, 'url');
if ($bUseUrl) {
- $oParserState->consume('url');
$oParserState->consumeWhiteSpace();
$oParserState->consume('(');
+ } else {
+ $oAnchor->backtrack();
}
$oParserState->consumeWhiteSpace();
$oResult = new URL(CSSString::parse($oParserState), $oParserState->currentLine());
use Sabberworm\CSS\Parsing\UnexpectedTokenException;
use Sabberworm\CSS\Renderable;
+/**
+ * Abstract base class for specific classes of CSS values: `Size`, `Color`, `CSSString` and `URL`, and another
+ * abstract subclass `ValueList`.
+ */
abstract class Value implements Renderable
{
/**
//Build a list of delimiters and parsed values
while (
!($oParserState->comes('}') || $oParserState->comes(';') || $oParserState->comes('!')
- || $oParserState->comes(')')
- || $oParserState->comes('\\'))
+ || $oParserState->comes(')')
+ || $oParserState->comes('\\')
+ || $oParserState->isEnd())
) {
if (count($aStack) > 0) {
$bFoundDelimiter = false;
*/
public static function parseIdentifierOrFunction(ParserState $oParserState, $bIgnoreCase = false)
{
- $sResult = $oParserState->parseIdentifier($bIgnoreCase);
+ $oAnchor = $oParserState->anchor();
+ $mResult = $oParserState->parseIdentifier($bIgnoreCase);
if ($oParserState->comes('(')) {
- $oParserState->consume('(');
- $aArguments = Value::parseValue($oParserState, ['=', ' ', ',']);
- $sResult = new CSSFunction($sResult, $aArguments, ',', $oParserState->currentLine());
- $oParserState->consume(')');
+ $oAnchor->backtrack();
+ if ($oParserState->streql('url', $mResult)) {
+ $mResult = URL::parse($oParserState);
+ } elseif (
+ $oParserState->streql('calc', $mResult)
+ || $oParserState->streql('-webkit-calc', $mResult)
+ || $oParserState->streql('-moz-calc', $mResult)
+ ) {
+ $mResult = CalcFunction::parse($oParserState);
+ } else {
+ $mResult = CSSFunction::parse($oParserState, $bIgnoreCase);
+ }
}
- return $sResult;
+ return $mResult;
}
/**
$oValue = Size::parse($oParserState);
} elseif ($oParserState->comes('#') || $oParserState->comes('rgb', true) || $oParserState->comes('hsl', true)) {
$oValue = Color::parse($oParserState);
- } elseif ($oParserState->comes('url', true)) {
- $oValue = URL::parse($oParserState);
- } elseif (
- $oParserState->comes('calc', true) || $oParserState->comes('-webkit-calc', true)
- || $oParserState->comes('-moz-calc', true)
- ) {
- $oValue = CalcFunction::parse($oParserState);
} elseif ($oParserState->comes("'") || $oParserState->comes('"')) {
$oValue = CSSString::parse($oParserState);
} elseif ($oParserState->comes("progid:") && $oParserState->getSettings()->bLenientParsing) {
use Sabberworm\CSS\OutputFormat;
+/**
+ * A `ValueList` represents a lists of `Value`s, separated by some separation character
+ * (mostly `,`, whitespace, or `/`).
+ *
+ * There are two types of `ValueList`s: `RuleValueList` and `CSSFunction`
+ */
abstract class ValueList extends Value
{
/**
All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles.
+## [5.1.1] - 2024-03-02
+
+### Changed
+
+* Do not use implicitly nullable parameters
+
## [5.1.0] - 2023-12-22
### Added
* This component is no longer supported on PHP 5.6
+[5.1.1]: https://github.com/sebastianbergmann/diff/compare/5.1.0...5.1.1
[5.1.0]: https://github.com/sebastianbergmann/diff/compare/5.0.3...5.1.0
[5.0.3]: https://github.com/sebastianbergmann/diff/compare/5.0.2...5.0.3
[5.0.2]: https://github.com/sebastianbergmann/diff/compare/5.0.1...5.0.2
BSD 3-Clause License
-Copyright (c) 2002-2023, Sebastian Bergmann
+Copyright (c) 2002-2024, Sebastian Bergmann
All rights reserved.
Redistribution and use in source and binary forms, with or without
},
"require-dev": {
"phpunit/phpunit": "^10.0",
- "symfony/process": "^4.2 || ^5"
+ "symfony/process": "^6.4"
},
"autoload": {
"classmap": [
$this->outputBuilder = $outputBuilder;
}
- public function diff(array|string $from, array|string $to, LongestCommonSubsequenceCalculator $lcs = null): string
+ public function diff(array|string $from, array|string $to, ?LongestCommonSubsequenceCalculator $lcs = null): string
{
$diff = $this->diffToArray($from, $to, $lcs);
return $this->outputBuilder->getDiff($diff);
}
- public function diffToArray(array|string $from, array|string $to, LongestCommonSubsequenceCalculator $lcs = null): array
+ public function diffToArray(array|string $from, array|string $to, ?LongestCommonSubsequenceCalculator $lcs = null): array
{
if (is_string($from)) {
$from = $this->splitStringByLines($from);
string $expected,
$value,
int $code = 0,
- Exception $previous = null
+ ?Exception $previous = null
) {
parent::__construct(
sprintf(
'Option "%s" must be %s, got "%s".',
$option,
$expected,
- is_object($value) ? $value::class : (null === $value ? '<null>' : gettype($value) . '#' . $value)
+ is_object($value) ? $value::class : (null === $value ? '<null>' : gettype($value) . '#' . $value),
),
$code,
- $previous
+ $previous,
);
}
}
return array_merge(
$this->calculate($fromStart, $toStart),
- $this->calculate($fromEnd, $toEnd)
+ $this->calculate($fromEnd, $toEnd),
);
}
if ($from[$i] === $to[$j]) {
$current[$j + 1] = $prev[$j] + 1;
} else {
- // don't use max() to avoid function call overhead
+ /**
+ * @noinspection PhpConditionCanBeReplacedWithMinMaxCallInspection
+ *
+ * We do not use max() here to avoid the function call overhead
+ */
if ($current[$j] > $prev[$j + 1]) {
$current[$j + 1] = $current[$j];
} else {
$options['fromFile'],
null === $options['fromFileDate'] ? '' : "\t" . $options['fromFileDate'],
$options['toFile'],
- null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate']
+ null === $options['toFileDate'] ? '' : "\t" . $options['toFileDate'],
);
$this->collapseRanges = $options['collapseRanges'];
$fromRange - $cutOff + $contextStartOffset + $this->contextLines,
$toStart - $contextStartOffset,
$toRange - $cutOff + $contextStartOffset + $this->contextLines,
- $output
+ $output,
);
$fromStart += $fromRange;
- $toStart += $toRange;
+ $toStart += $toRange;
$hunkCapture = false;
$sameCount = $toRange = $fromRange = 0;
$contextEndOffset = min($sameCount, $this->contextLines);
$fromRange -= $sameCount;
- $toRange -= $sameCount;
+ $toRange -= $sameCount;
$this->writeHunk(
$diff,
$fromRange + $contextStartOffset + $contextEndOffset,
$toStart - $contextStartOffset,
$toRange + $contextStartOffset + $contextEndOffset,
- $output
+ $output,
);
}
$this->changed = true;
fwrite($output, $diff[$i][0]);
}
- //} elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package
+ // } elseif ($diff[$i][1] === Differ::DIFF_LINE_END_WARNING) { // custom comment inserted by PHPUnit/diff package
// skip
- //} else {
+ // } else {
// unknown/invalid
- //}
+ // }
}
}
use function min;
use function str_ends_with;
use function stream_get_contents;
-use function strlen;
use function substr;
use SebastianBergmann\Diff\Differ;
// This might happen when both the `from` and `to` do not have a trailing linebreak
$last = substr($diff, -1);
- return 0 !== strlen($diff) && "\n" !== $last && "\r" !== $last
+ return '' !== $diff && "\n" !== $last && "\r" !== $last
? $diff . "\n"
: $diff;
}
$fromRange - $cutOff + $contextStartOffset + $this->contextLines,
$toStart - $contextStartOffset,
$toRange - $cutOff + $contextStartOffset + $this->contextLines,
- $output
+ $output,
);
$fromStart += $fromRange;
- $toStart += $toRange;
+ $toStart += $toRange;
$hunkCapture = false;
$sameCount = $toRange = $fromRange = 0;
$contextEndOffset = min($sameCount, $this->contextLines);
$fromRange -= $sameCount;
- $toRange -= $sameCount;
+ $toRange -= $sameCount;
$this->writeHunk(
$diff,
$fromRange + $contextStartOffset + $contextEndOffset,
$toStart - $contextStartOffset,
$toRange + $contextStartOffset + $contextEndOffset,
- $output
+ $output,
);
}
(int) $match['start'],
isset($match['startrange']) ? max(0, (int) $match['startrange']) : 1,
(int) $match['end'],
- isset($match['endrange']) ? max(0, (int) $match['endrange']) : 1
+ isset($match['endrange']) ? max(0, (int) $match['endrange']) : 1,
);
$chunks[] = $chunk;
private ?string $namespace;
private ?string $element;
- public function __construct(string $namespace = null, string $element = null)
+ public function __construct(?string $namespace = null, ?string $element = null)
{
$this->namespace = $namespace;
$this->element = $element;
private NodeInterface $tree;
private ?string $pseudoElement;
- public function __construct(NodeInterface $tree, string $pseudoElement = null)
+ public function __construct(NodeInterface $tree, ?string $pseudoElement = null)
{
$this->tree = $tree;
$this->pseudoElement = $pseudoElement ? strtolower($pseudoElement) : null;
{
private Tokenizer $tokenizer;
- public function __construct(Tokenizer $tokenizer = null)
+ public function __construct(?Tokenizer $tokenizer = null)
{
$this->tokenizer = $tokenizer ?? new Tokenizer();
}
private array $pseudoClassTranslators = [];
private array $attributeMatchingTranslators = [];
- public function __construct(ParserInterface $parser = null)
+ public function __construct(?ParserInterface $parser = null)
{
$this->mainParser = $parser ?? new Parser();
"minimum-stability": "dev",
"extra": {
"branch-alias": {
- "dev-main": "3.4-dev"
+ "dev-main": "3.5-dev"
},
"thanks": {
"name": "symfony/contracts",
return 0;
}
+
+ $digits_consumed = $digits;
+ /* Ignore leading whitespace. */
+ while ($digits_consumed < $str_end && false !== strpos($ctype_space, $value[$digits_consumed])) {
+ ++$digits_consumed;
+ }
+ if ($digits_consumed !== $str_end && ('+' === $value[$digits_consumed] || '-' === $value[$digits_consumed])) {
+ ++$digits_consumed;
+ }
+
+ if ('0' === $value[$digits_consumed]) {
+ /* Value is just 0 */
+ if ($digits_consumed + 1 === $str_end) {
+ goto evaluation;
+ }
+ switch ($value[$digits_consumed + 1]) {
+ case 'x':
+ case 'X':
+ case 'o':
+ case 'O':
+ case 'b':
+ case 'B':
+ $digits_consumed += 2;
+ break;
+ }
+ }
+
+ if ($digits !== $digits_consumed) {
+ $message = sprintf(
+ 'Invalid quantity "%s": no digits after base prefix, interpreting as "0" for backwards compatibility',
+ self::escapeString($value)
+ );
+ trigger_error($message, \E_USER_WARNING);
+
+ return 0;
+ }
}
evaluation:
$digits_end = $digits;
- // The native function treats 0x0x123 the same as 0x123. This is a bug which we must replicate.
- if (
- 16 === $base
- && $digits_end + 2 < $str_end
- && '0x' === substr($value, $digits_end, 2)
- && false !== strpos($allowed_digits, $value[$digits_end + 2])
- ) {
- $digits_end += 2;
- }
-
while ($digits_end < $str_end && false !== strpos($allowed_digits, $value[$digits_end])) {
++$digits_end;
}
return;
}
-if (!extension_loaded('odbc')) {
- return;
-}
-
-if (!function_exists('odbc_connection_string_is_quoted')) {
- function odbc_connection_string_is_quoted(string $str): bool { return p\Php82::odbc_connection_string_is_quoted($str); }
-}
-
-if (!function_exists('odbc_connection_string_should_quote')) {
- function odbc_connection_string_should_quote(string $str): bool { return p\Php82::odbc_connection_string_should_quote($str); }
-}
-
-if (!function_exists('odbc_connection_string_quote')) {
- function odbc_connection_string_quote(string $str): string { return p\Php82::odbc_connection_string_quote($str); }
+if (extension_loaded('odbc')) {
+ if (!function_exists('odbc_connection_string_is_quoted')) {
+ function odbc_connection_string_is_quoted(string $str): bool { return p\Php82::odbc_connection_string_is_quoted($str); }
+ }
+
+ if (!function_exists('odbc_connection_string_should_quote')) {
+ function odbc_connection_string_should_quote(string $str): bool { return p\Php82::odbc_connection_string_should_quote($str); }
+ }
+
+ if (!function_exists('odbc_connection_string_quote')) {
+ function odbc_connection_string_quote(string $str): string { return p\Php82::odbc_connection_string_quote($str); }
+ }
}
if (!function_exists('ini_parse_quantity')) {
},
"minimum-stability": "dev",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"
/**
* @author Ion Bazan <ion.bazan@gmail.com>
+ * @author Pierre Ambroise <pierre27.ambroise@gmail.com>
*
* @internal
*/
return \JSON_ERROR_NONE === json_last_error();
}
- public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, string $encoding = null): string
+ public static function mb_str_pad(string $string, int $length, string $pad_string = ' ', int $pad_type = \STR_PAD_RIGHT, ?string $encoding = null): string
{
if (!\in_array($pad_type, [\STR_PAD_RIGHT, \STR_PAD_LEFT, \STR_PAD_BOTH], true)) {
throw new \ValueError('mb_str_pad(): Argument #4 ($pad_type) must be STR_PAD_LEFT, STR_PAD_RIGHT, or STR_PAD_BOTH');
return mb_substr(str_repeat($pad_string, $leftPaddingLength), 0, $leftPaddingLength, $encoding).$string.mb_substr(str_repeat($pad_string, $rightPaddingLength), 0, $rightPaddingLength, $encoding);
}
}
+
+ public static function str_increment(string $string): string
+ {
+ if ('' === $string) {
+ throw new \ValueError('str_increment(): Argument #1 ($string) cannot be empty');
+ }
+
+ if (!preg_match('/^[a-zA-Z0-9]+$/', $string)) {
+ throw new \ValueError('str_increment(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
+ }
+
+ if (is_numeric($string)) {
+ $offset = stripos($string, 'e');
+ if (false !== $offset) {
+ $char = $string[$offset];
+ ++$char;
+ $string[$offset] = $char;
+ ++$string;
+
+ switch ($string[$offset]) {
+ case 'f':
+ $string[$offset] = 'e';
+ break;
+ case 'F':
+ $string[$offset] = 'E';
+ break;
+ case 'g':
+ $string[$offset] = 'f';
+ break;
+ case 'G':
+ $string[$offset] = 'F';
+ break;
+ }
+
+ return $string;
+ }
+ }
+
+ return ++$string;
+ }
+
+ public static function str_decrement(string $string): string
+ {
+ if ('' === $string) {
+ throw new \ValueError('str_decrement(): Argument #1 ($string) cannot be empty');
+ }
+
+ if (!preg_match('/^[a-zA-Z0-9]+$/', $string)) {
+ throw new \ValueError('str_decrement(): Argument #1 ($string) must be composed only of alphanumeric ASCII characters');
+ }
+
+ if (preg_match('/\A(?:0[aA0]?|[aA])\z/', $string)) {
+ throw new \ValueError(sprintf('str_decrement(): Argument #1 ($string) "%s" is out of decrement range', $string));
+ }
+
+ if (!\in_array(substr($string, -1), ['A', 'a', '0'], true)) {
+ return implode('', \array_slice(str_split($string), 0, -1)).\chr(\ord(substr($string, -1)) - 1);
+ }
+
+ $carry = '';
+ $decremented = '';
+
+ for ($i = \strlen($string) - 1; $i >= 0; --$i) {
+ $char = $string[$i];
+
+ switch ($char) {
+ case 'A':
+ if ('' !== $carry) {
+ $decremented = $carry.$decremented;
+ $carry = '';
+ }
+ $carry = 'Z';
+
+ break;
+ case 'a':
+ if ('' !== $carry) {
+ $decremented = $carry.$decremented;
+ $carry = '';
+ }
+ $carry = 'z';
+
+ break;
+ case '0':
+ if ('' !== $carry) {
+ $decremented = $carry.$decremented;
+ $carry = '';
+ }
+ $carry = '9';
+
+ break;
+ case '1':
+ if ('' !== $carry) {
+ $decremented = $carry.$decremented;
+ $carry = '';
+ }
+
+ break;
+ default:
+ if ('' !== $carry) {
+ $decremented = $carry.$decremented;
+ $carry = '';
+ }
+
+ if (!\in_array($char, ['A', 'a', '0'], true)) {
+ $decremented = \chr(\ord($char) - 1).$decremented;
+ }
+ }
+ }
+
+ return $decremented;
+ }
}
- [`ldap_exop_sync`](https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures)
- [`ldap_connect_wallet`](https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures)
- [`stream_context_set_options`](https://wiki.php.net/rfc/deprecate_functions_with_overloaded_signatures)
+- [`str_increment` and `str_decrement`](https://wiki.php.net/rfc/saner-inc-dec-operators)
- [`Date*Exception/Error classes`](https://wiki.php.net/rfc/datetime-exceptions)
+- [`SQLite3Exception`](https://wiki.php.net/rfc/sqlite3_exceptions)
More information can be found in the
[main Polyfill README](https://github.com/symfony/polyfill/blob/main/README.md).
--- /dev/null
+<?php
+
+/*
+ * This file is part of the Symfony package.
+ *
+ * (c) Fabien Potencier <fabien@symfony.com>
+ *
+ * For the full copyright and license information, please view the LICENSE
+ * file that was distributed with this source code.
+ */
+
+if (\PHP_VERSION_ID < 80300) {
+ class SQLite3Exception extends Exception
+ {
+ }
+}
function stream_context_set_options($context, array $options): bool { return stream_context_set_option($context, $options); }
}
+if (!function_exists('str_increment')) {
+ function str_increment(string $string): string { return p\Php83::str_increment($string); }
+}
+
+if (!function_exists('str_decrement')) {
+ function str_decrement(string $string): string { return p\Php83::str_decrement($string); }
+}
+
if (\PHP_VERSION_ID >= 80100) {
return require __DIR__.'/bootstrap81.php';
}
if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
- function ldap_exop_sync($ldap, string $request_oid, string $request_data = null, array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $controls, $response_data, $response_oid); }
+ function ldap_exop_sync($ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $controls, $response_data, $response_oid); }
}
if (!function_exists('ldap_connect_wallet') && function_exists('ldap_connect')) {
}
if (!function_exists('ldap_exop_sync') && function_exists('ldap_exop')) {
- function ldap_exop_sync(\LDAP\Connection $ldap, string $request_oid, string $request_data = null, array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $controls, $response_data, $response_oid); }
+ function ldap_exop_sync(\LDAP\Connection $ldap, string $request_oid, ?string $request_data = null, ?array $controls = null, &$response_data = null, &$response_oid = null): bool { return ldap_exop($ldap, $request_oid, $request_data, $controls, $response_data, $response_oid); }
}
if (!function_exists('ldap_connect_wallet') && function_exists('ldap_connect')) {
}
],
"require": {
- "php": ">=7.1",
- "symfony/polyfill-php80": "^1.14"
+ "php": ">=7.1"
},
"autoload": {
"psr-4": { "Symfony\\Polyfill\\Php83\\": "" },
},
"minimum-stability": "dev",
"extra": {
- "branch-alias": {
- "dev-main": "1.28-dev"
- },
"thanks": {
"name": "symfony/polyfill",
"url": "https://github.com/symfony/polyfill"