From: Alexander Ebert Date: Thu, 20 Jun 2024 10:48:35 +0000 (+0200) Subject: Update the Composer dependencies X-Git-Tag: 6.1.0_Alpha_1~4^2~11 X-Git-Url: https://git.stricted.de/?a=commitdiff_plain;h=ed0ebd262ea60913f849e82686c46fa7876b5fba;p=GitHub%2FWoltLab%2FWCF.git Update the Composer dependencies --- diff --git a/wcfsetup/install/files/lib/system/api/composer.json b/wcfsetup/install/files/lib/system/api/composer.json index 5866a608ac..0bd94e0b67 100644 --- a/wcfsetup/install/files/lib/system/api/composer.json +++ b/wcfsetup/install/files/lib/system/api/composer.json @@ -10,16 +10,16 @@ } }, "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", @@ -28,9 +28,9 @@ "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": { diff --git a/wcfsetup/install/files/lib/system/api/composer.lock b/wcfsetup/install/files/lib/system/api/composer.lock index 48a1249f18..1de7f76c90 100644 --- a/wcfsetup/install/files/lib/system/api/composer.lock +++ b/wcfsetup/install/files/lib/system/api/composer.lock @@ -4,37 +4,37 @@ "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", @@ -69,7 +69,7 @@ ], "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": [ { @@ -77,7 +77,7 @@ "type": "github" } ], - "time": "2024-01-08T20:31:48+00:00" + "time": "2024-04-04T16:42:55+00:00" }, { "name": "dragonmantank/cron-expression", @@ -578,16 +578,16 @@ }, { "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": { @@ -607,9 +607,9 @@ "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": { @@ -659,7 +659,7 @@ "type": "community_bridge" } ], - "time": "2023-10-26T11:01:07+00:00" + "time": "2024-02-16T16:06:16+00:00" }, { "name": "laminas/laminas-httphandlerrunner", @@ -850,16 +850,16 @@ }, { "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": { @@ -913,7 +913,7 @@ "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", @@ -1145,20 +1145,20 @@ }, { "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", @@ -1182,7 +1182,7 @@ "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", @@ -1194,9 +1194,9 @@ "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", @@ -1511,16 +1511,16 @@ }, { "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": { @@ -1528,13 +1528,17 @@ "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/" @@ -1547,6 +1551,14 @@ "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", @@ -1557,10 +1569,10 @@ "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", @@ -1644,16 +1656,16 @@ }, { "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": { @@ -1661,7 +1673,7 @@ }, "require-dev": { "phpunit/phpunit": "^10.0", - "symfony/process": "^4.2 || ^5" + "symfony/process": "^6.4" }, "type": "library", "extra": { @@ -1699,7 +1711,7 @@ "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": [ { @@ -1707,20 +1719,20 @@ "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": { @@ -1756,7 +1768,7 @@ "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": [ { @@ -1772,20 +1784,20 @@ "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": { @@ -1794,7 +1806,7 @@ "type": "library", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", @@ -1823,7 +1835,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -1839,20 +1851,20 @@ "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": { @@ -1860,9 +1872,6 @@ }, "type": "library", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" @@ -1902,7 +1911,7 @@ "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": [ { @@ -1918,31 +1927,27 @@ "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" @@ -1982,7 +1987,7 @@ "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": [ { @@ -1998,7 +2003,7 @@ "type": "tidelift" } ], - "time": "2023-08-16T06:22:46+00:00" + "time": "2024-06-19T12:35:24+00:00" }, { "name": "webmozart/assert", diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php b/wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php index 6ebd8e1f50..858534267d 100644 --- a/wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php +++ b/wcfsetup/install/files/lib/system/api/composer/autoload_classmap.php @@ -19,11 +19,6 @@ return array( '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', @@ -34,20 +29,22 @@ return array( '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', @@ -65,7 +62,7 @@ return array( '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', @@ -78,16 +75,20 @@ return array( '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', @@ -130,11 +131,12 @@ return array( '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', @@ -147,6 +149,7 @@ return array( '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', @@ -159,10 +162,13 @@ return array( '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', @@ -185,6 +191,7 @@ return array( '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', @@ -193,7 +200,12 @@ return array( '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', @@ -216,14 +228,10 @@ return array( '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', @@ -239,6 +247,9 @@ return array( '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', @@ -259,15 +270,14 @@ return array( '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', @@ -289,19 +299,20 @@ return array( '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', @@ -891,6 +902,7 @@ return array( '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', @@ -901,6 +913,7 @@ return array( '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', diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_psr4.php b/wcfsetup/install/files/lib/system/api/composer/autoload_psr4.php index 78f3e78533..b75b3a32f0 100644 --- a/wcfsetup/install/files/lib/system/api/composer/autoload_psr4.php +++ b/wcfsetup/install/files/lib/system/api/composer/autoload_psr4.php @@ -15,7 +15,7 @@ return array( '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'), diff --git a/wcfsetup/install/files/lib/system/api/composer/autoload_static.php b/wcfsetup/install/files/lib/system/api/composer/autoload_static.php index 88105fab17..db46c9c43c 100644 --- a/wcfsetup/install/files/lib/system/api/composer/autoload_static.php +++ b/wcfsetup/install/files/lib/system/api/composer/autoload_static.php @@ -111,8 +111,8 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d ), '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 ( @@ -207,11 +207,6 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -222,20 +217,22 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -253,7 +250,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -266,16 +263,20 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -318,11 +319,12 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -335,6 +337,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -347,10 +350,13 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -373,6 +379,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -381,7 +388,12 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -404,14 +416,10 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -427,6 +435,9 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -447,15 +458,14 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -477,19 +487,20 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -1079,6 +1090,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', @@ -1089,6 +1101,7 @@ class ComposerStaticInita1f5f7c74275d47a45049a2936db1d0d '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', diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.json b/wcfsetup/install/files/lib/system/api/composer/installed.json index b34bb87bc1..e12181b6a6 100644 --- a/wcfsetup/install/files/lib/system/api/composer/installed.json +++ b/wcfsetup/install/files/lib/system/api/composer/installed.json @@ -2,37 +2,37 @@ "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": { @@ -66,7 +66,7 @@ ], "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": [ { @@ -593,17 +593,17 @@ }, { "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": { @@ -623,11 +623,11 @@ "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": { @@ -877,17 +877,17 @@ }, { "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": { @@ -897,7 +897,7 @@ "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": { @@ -1187,24 +1187,24 @@ }, { "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": { @@ -1227,7 +1227,7 @@ "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", @@ -1239,7 +1239,7 @@ "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" }, @@ -1574,17 +1574,17 @@ }, { "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": { @@ -1592,14 +1592,18 @@ "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": { @@ -1613,6 +1617,14 @@ "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", @@ -1623,8 +1635,8 @@ "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" }, @@ -1713,17 +1725,17 @@ }, { "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": { @@ -1731,9 +1743,9 @@ }, "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": { @@ -1771,7 +1783,7 @@ "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": [ { @@ -1783,23 +1795,23 @@ }, { "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": { @@ -1831,7 +1843,7 @@ "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": [ { @@ -1851,27 +1863,27 @@ }, { "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", @@ -1901,7 +1913,7 @@ "description": "A generic function and convention to trigger deprecation notices", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/deprecation-contracts/tree/v3.4.0" + "source": "https://github.com/symfony/deprecation-contracts/tree/v3.5.0" }, "funding": [ { @@ -1921,28 +1933,25 @@ }, { "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" @@ -1983,7 +1992,7 @@ "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": [ { @@ -2003,29 +2012,25 @@ }, { "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" @@ -2066,7 +2071,7 @@ "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": [ { diff --git a/wcfsetup/install/files/lib/system/api/composer/installed.php b/wcfsetup/install/files/lib/system/api/composer/installed.php index 265ff4b235..dc64a679aa 100644 --- a/wcfsetup/install/files/lib/system/api/composer/installed.php +++ b/wcfsetup/install/files/lib/system/api/composer/installed.php @@ -3,7 +3,7 @@ '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(), @@ -13,16 +13,16 @@ '__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(), @@ -83,9 +83,9 @@ '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(), @@ -125,9 +125,9 @@ ), ), '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(), @@ -176,9 +176,9 @@ ), ), '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(), @@ -253,9 +253,9 @@ '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(), @@ -271,27 +271,27 @@ '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(), @@ -304,18 +304,18 @@ ), ), '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(), diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/composer.json b/wcfsetup/install/files/lib/system/api/cuyz/valinor/composer.json index e1d6b495bc..e9a9226bd0 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/composer.json +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/composer.json @@ -15,13 +15,13 @@ } ], "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", @@ -29,7 +29,7 @@ "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": { @@ -64,7 +64,6 @@ "rector" ], "mutation": [ - "@putenv XDEBUG_MODE=off", "infection --threads=max --git-diff-lines" ], "doc": [ diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php index c05891c3f9..632cb4bc63 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/ApiAndInternalAnnotationCheck.php @@ -10,6 +10,7 @@ use PHPStan\Node\InClassNode; use PHPStan\Rules\Rule; use PHPStan\Rules\RuleErrorBuilder; +use function str_contains; use function str_starts_with; /** @@ -34,9 +35,7 @@ final class ApiAndInternalAnnotationCheck implements Rule return []; } - if (str_starts_with($reflection->getName(), 'CuyZ\Valinor\Tests') - || str_starts_with($reflection->getName(), 'SimpleNamespace') - ) { + if (str_contains($reflection->getFileName() ?? '', '/tests/')) { return []; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php index ac447d9db4..2b2f73431b 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/PHPStan/Extension/TreeMapperPHPStanExtension.php @@ -8,6 +8,7 @@ use CuyZ\Valinor\Mapper\TreeMapper; 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; @@ -46,7 +47,15 @@ final class TreeMapperPHPStanExtension implements DynamicMethodReturnTypeExtensi 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 diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/Psalm/Plugin/ArgumentsMapperPsalmPlugin.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/Psalm/Plugin/ArgumentsMapperPsalmPlugin.php index fc21e816f5..bc97ab7b78 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/Psalm/Plugin/ArgumentsMapperPsalmPlugin.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/qa/Psalm/Plugin/ArgumentsMapperPsalmPlugin.php @@ -58,13 +58,15 @@ final class ArgumentsMapperPsalmPlugin implements MethodReturnTypeProviderInterf 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()]); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/CacheCompiler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/CacheCompiler.php deleted file mode 100644 index b6273b3fc5..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/CacheCompiler.php +++ /dev/null @@ -1,11 +0,0 @@ - - */ -final class CompiledPhpFileCache implements WarmupCache -{ - private const TEMPORARY_DIR_PERMISSION = 510; - - private const GENERATED_MESSAGE = 'Generated by ' . self::class; - - /** @var array> */ - 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 - */ - 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 <<compiler instanceof \CuyZ\Valinor\Cache\Compiled\HasArguments ? \$this->compiler->arguments() : []) implements \CuyZ\Valinor\Cache\Compiled\PhpCacheFile { - /** @var array */ - 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 - */ - 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'; - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/HasArguments.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/HasArguments.php deleted file mode 100644 index eb9bfa1486..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/HasArguments.php +++ /dev/null @@ -1,14 +0,0 @@ - - */ - public function arguments(): array; -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/MixedValueCacheCompiler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/MixedValueCacheCompiler.php deleted file mode 100644 index 08bfffe9f2..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Compiled/MixedValueCacheCompiler.php +++ /dev/null @@ -1,16 +0,0 @@ -> */ - 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; } /** @@ -115,13 +168,11 @@ final class FileSystemCache implements WarmupCache 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 @@ -134,4 +185,37 @@ final class FileSystemCache implements WarmupCache 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 <<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'; + } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/FileWatchingCache.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/FileWatchingCache.php index 417762bb08..2ec5fb52aa 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/FileWatchingCache.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/FileWatchingCache.php @@ -125,7 +125,7 @@ final class FileWatchingCache implements WarmupCache $fileNames = []; if ($value instanceof ClassDefinition) { - $reflection = Reflection::class($value->name()); + $reflection = Reflection::class($value->name); do { $fileNames[] = $reflection->getFileName(); @@ -133,7 +133,7 @@ final class FileWatchingCache implements WarmupCache } if ($value instanceof FunctionDefinition) { - $fileNames[] = $value->fileName(); + $fileNames[] = $value->fileName; } foreach ($fileNames as $fileName) { diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/KeySanitizerCache.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/KeySanitizerCache.php index 6950e44627..d0518e983e 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/KeySanitizerCache.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/KeySanitizerCache.php @@ -35,7 +35,7 @@ final class KeySanitizerCache implements WarmupCache // 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 diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php index 4648fc681d..568ec4ef4f 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Cache/Warmup/RecursiveCacheWarmupService.php @@ -82,10 +82,10 @@ final class RecursiveCacheWarmupService $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); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/AttributeDefinition.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/AttributeDefinition.php new file mode 100644 index 0000000000..1780935b27 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/AttributeDefinition.php @@ -0,0 +1,20 @@ + */ + public readonly array $arguments, + ) {} + + public function instantiate(): object + { + return new ($this->class->type->className())(...$this->arguments); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Attributes.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Attributes.php index 3a08a2d06e..7cda965ff5 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Attributes.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Attributes.php @@ -6,24 +6,76 @@ namespace CuyZ\Valinor\Definition; use Countable; use IteratorAggregate; +use Traversable; + +use function array_filter; +use function count; +use function is_a; /** * @internal * - * @extends IteratorAggregate + * @implements IteratorAggregate */ -interface Attributes extends IteratorAggregate, Countable +final class Attributes implements IteratorAggregate, Countable { + private static self $empty; + + /** @var list */ + 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 */ - public function has(string $className): bool; + public function toArray(): array + { + return $this->attributes; + } /** - * @template T of object - * - * @param class-string $className - * @return list + * @return Traversable */ - public function ofType(string $className): array; + public function getIterator(): Traversable + { + yield from $this->attributes; + } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/AttributesContainer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/AttributesContainer.php deleted file mode 100644 index f60353c854..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/AttributesContainer.php +++ /dev/null @@ -1,80 +0,0 @@ - */ - 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 - */ - public function getIterator(): Traversable - { - foreach ($this->attributes as $attribute) { - yield $attribute['callback'](); - } - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ClassDefinition.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ClassDefinition.php index 74b11d49e7..e3bd41ce8f 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ClassDefinition.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ClassDefinition.php @@ -4,55 +4,19 @@ declare(strict_types=1); 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; - } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/ExtendTagTypeError.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/ExtendTagTypeError.php new file mode 100644 index 0000000000..1ec0424f51 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/ExtendTagTypeError.php @@ -0,0 +1,25 @@ + $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, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidExtendTagClassName.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidExtendTagClassName.php new file mode 100644 index 0000000000..4e7066b9bf --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidExtendTagClassName.php @@ -0,0 +1,27 @@ + $reflection + */ + public function __construct(ReflectionClass $reflection, Type $invalidExtendTag) + { + /** @var ReflectionClass $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, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidExtendTagType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidExtendTagType.php new file mode 100644 index 0000000000..dd7a90fba4 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidExtendTagType.php @@ -0,0 +1,27 @@ + $reflection + */ + public function __construct(ReflectionClass $reflection, Type $invalidExtendTag) + { + /** @var ReflectionClass $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, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClass.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClass.php index 6175086ea6..8831bce24a 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClass.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClass.php @@ -4,13 +4,13 @@ declare(strict_types=1); 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()}`.", diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClassType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClassType.php index e2684323d5..f5f91cca87 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClassType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/InvalidTypeAliasImportClassType.php @@ -4,14 +4,14 @@ declare(strict_types=1); 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()}`.", diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/SeveralExtendTagsFound.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/SeveralExtendTagsFound.php new file mode 100644 index 0000000000..231141fab1 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/SeveralExtendTagsFound.php @@ -0,0 +1,23 @@ + $reflection + */ + public function __construct(ReflectionClass $reflection) + { + parent::__construct( + "Only one `@extends` tag should be set for the class `$reflection->name`.", + 1670195494, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/TypesDoNotMatch.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/TypesDoNotMatch.php deleted file mode 100644 index 603b9497a8..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/TypesDoNotMatch.php +++ /dev/null @@ -1,31 +0,0 @@ -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); - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/UnknownTypeAliasImport.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/UnknownTypeAliasImport.php index 5f304b1d65..def92da0e8 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/UnknownTypeAliasImport.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Exception/UnknownTypeAliasImport.php @@ -4,7 +4,7 @@ declare(strict_types=1); namespace CuyZ\Valinor\Definition\Exception; -use CuyZ\Valinor\Type\ClassType; +use CuyZ\Valinor\Type\ObjectType; use LogicException; /** @internal */ @@ -13,7 +13,7 @@ final class UnknownTypeAliasImport extends LogicException /** * @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`", diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionDefinition.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionDefinition.php index 77b6ddfb4a..6aa45d44fc 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionDefinition.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionDefinition.php @@ -10,63 +10,18 @@ use CuyZ\Valinor\Type\Type; 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; - } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionObject.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionObject.php index 59b811cd4b..e3777d042d 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionObject.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/FunctionObject.php @@ -7,24 +7,14 @@ namespace CuyZ\Valinor\Definition; /** @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; - } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/MethodDefinition.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/MethodDefinition.php index a04e8928d5..ce043a8ed8 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/MethodDefinition.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/MethodDefinition.php @@ -10,41 +10,14 @@ use CuyZ\Valinor\Type\Type; 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; - } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Methods.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Methods.php index 4c348660fb..b572c2e519 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Methods.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Methods.php @@ -21,7 +21,7 @@ final class Methods implements IteratorAggregate, Countable public function __construct(MethodDefinition ...$methods) { foreach ($methods as $method) { - $this->methods[$method->name()] = $method; + $this->methods[$method->name] = $method; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/NativeAttributes.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/NativeAttributes.php deleted file mode 100644 index 064702aeb9..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/NativeAttributes.php +++ /dev/null @@ -1,89 +0,0 @@ -> */ - private array $definition = []; - - /** - * @param ReflectionClass|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> - */ - public function definition(): array - { - return $this->definition; - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ParameterDefinition.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ParameterDefinition.php index 6ea27a6c67..7d4e1629e0 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ParameterDefinition.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/ParameterDefinition.php @@ -10,47 +10,15 @@ use CuyZ\Valinor\Type\Type; 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; - } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Parameters.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Parameters.php index 7177a0b3d7..c659b5be44 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Parameters.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Parameters.php @@ -23,7 +23,7 @@ final class Parameters implements IteratorAggregate, Countable public function __construct(ParameterDefinition ...$parameters) { foreach ($parameters as $parameter) { - $this->parameters[$parameter->name()] = $parameter; + $this->parameters[$parameter->name] = $parameter; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Properties.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Properties.php index f15c6b3b33..5472cfca3c 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Properties.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Properties.php @@ -21,7 +21,7 @@ final class Properties implements IteratorAggregate, Countable public function __construct(PropertyDefinition ...$properties) { foreach ($properties as $property) { - $this->properties[$property->name()] = $property; + $this->properties[$property->name] = $property; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/PropertyDefinition.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/PropertyDefinition.php index ac0aaaee16..0e249319e6 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/PropertyDefinition.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/PropertyDefinition.php @@ -10,47 +10,15 @@ use CuyZ\Valinor\Type\Type; 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; - } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/AttributesRepository.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/AttributesRepository.php index 9eff034d4a..a5291373ed 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/AttributesRepository.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/AttributesRepository.php @@ -4,18 +4,14 @@ declare(strict_types=1); 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|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflector + * @param ReflectionAttribute $reflection */ - public function for(ReflectionClass|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflector): Attributes; + public function for(ReflectionAttribute $reflection): AttributeDefinition; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheClassDefinitionRepository.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheClassDefinitionRepository.php index e1307d330d..c23cdfa8e2 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheClassDefinitionRepository.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheClassDefinitionRepository.php @@ -6,9 +6,11 @@ namespace CuyZ\Valinor\Definition\Repository\Cache; 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 { @@ -18,9 +20,10 @@ 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); diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheFunctionDefinitionRepository.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheFunctionDefinitionRepository.php index 65dec14647..93a24be271 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheFunctionDefinitionRepository.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/CacheFunctionDefinitionRepository.php @@ -9,6 +9,8 @@ use CuyZ\Valinor\Definition\Repository\FunctionDefinitionRepository; use CuyZ\Valinor\Utility\Reflection\Reflection; use Psr\SimpleCache\CacheInterface; +use function sha1; + /** @internal */ final class CacheFunctionDefinitionRepository implements FunctionDefinitionRepository { @@ -21,7 +23,9 @@ final class CacheFunctionDefinitionRepository implements FunctionDefinitionRepos 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); diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/AttributesCompiler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/AttributesCompiler.php index 8870630787..990c262036 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/AttributesCompiler.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/AttributesCompiler.php @@ -5,8 +5,6 @@ declare(strict_types=1); 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; @@ -17,29 +15,35 @@ use function var_export; /** @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 <<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[] = <<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); @@ -38,28 +38,30 @@ final class ClassDefinitionCompiler implements CacheCompiler { 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 <<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 @@ -33,21 +32,21 @@ final class FunctionDefinitionCompiler implements CacheCompiler $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 <<name()}', - '{$value->signature()}', + '{$value->name}', + '{$value->signature}', $attributes, $fileName, $class, diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/MethodDefinitionCompiler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/MethodDefinitionCompiler.php index 282fa1bc50..1176cca01c 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/MethodDefinitionCompiler.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/MethodDefinitionCompiler.php @@ -14,30 +14,36 @@ final class MethodDefinitionCompiler { 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 <<name()}', - '{$method->signature()}', + '{$method->name}', + '{$method->signature}', + $attributes, new \CuyZ\Valinor\Definition\Parameters($parameters), $isStatic, $isPublic, diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php index be0a590fdd..e0b84ab649 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/ParameterDefinitionCompiler.php @@ -16,17 +16,19 @@ final class ParameterDefinitionCompiler 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 <<name()}', - '{$parameter->signature()}', + '{$parameter->name}', + '{$parameter->signature}', $type, + $nativeType, $isOptional, $isVariadic, $defaultValue, @@ -37,7 +39,7 @@ final class ParameterDefinitionCompiler private function defaultValue(ParameterDefinition $parameter): string { - $defaultValue = $parameter->defaultValue(); + $defaultValue = $parameter->defaultValue; return is_object($defaultValue) ? 'unserialize(' . var_export(serialize($defaultValue), true) . ')' diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php index 3a6fb9f087..070c8b2038 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/PropertyDefinitionCompiler.php @@ -16,17 +16,19 @@ final class PropertyDefinitionCompiler 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 <<name()}', - '{$property->signature()}', + '{$property->name}', + '{$property->signature}', $type, + $nativeType, $hasDefaultValue, $defaultValue, $isPublic, diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php index 1b05675e27..adebebd901 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Cache/Compiler/TypeCompiler.php @@ -97,13 +97,20 @@ final class TypeCompiler 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') { @@ -138,15 +145,7 @@ final class TypeCompiler $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()"; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php index 59517cb8af..7d742843f1 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/ClassDefinitionRepository.php @@ -5,10 +5,10 @@ declare(strict_types=1); 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; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/NativeAttributesRepository.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/NativeAttributesRepository.php deleted file mode 100644 index 9da982e222..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/NativeAttributesRepository.php +++ /dev/null @@ -1,18 +0,0 @@ -classDefinitionRepository->for(new NativeClassType($reflection->getName())); + + return new AttributeDefinition( + $class, + $reflection->getArguments(), + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php index 7824749bd1..1d034aba5a 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionClassDefinitionRepository.php @@ -4,10 +4,16 @@ declare(strict_types=1); 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; @@ -15,21 +21,24 @@ use CuyZ\Valinor\Definition\Properties; 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; @@ -40,7 +49,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi { private TypeParserFactory $typeParserFactory; - private AttributesRepository $attributesFactory; + private AttributesRepository $attributesRepository; private ReflectionPropertyDefinitionBuilder $propertyBuilder; @@ -49,21 +58,22 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi /** @var array */ 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(), @@ -71,10 +81,22 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi ); } + /** + * @param ReflectionClass $reflection + * @return list + */ + private function attributes(ReflectionClass $reflection): array + { + return array_map( + fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute), + Reflection::attributes($reflection) + ); + } + /** * @return list */ - private function properties(ClassType $type): array + private function properties(ObjectType $type): array { return array_map( function (ReflectionProperty $property) use ($type) { @@ -82,14 +104,14 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi return $this->propertyBuilder->for($property, $typeResolver); }, - Reflection::class($type->className())->getProperties() + Reflection::class($type->className())->getProperties(), ); } /** * @return list */ - private function methods(ClassType $type): array + private function methods(ObjectType $type): array { $reflection = Reflection::class($type->className()); $methods = $reflection->getMethods(); @@ -111,7 +133,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi /** * @param ReflectionClass $target */ - private function typeResolver(ClassType $type, ReflectionClass $target): ReflectionTypeResolver + private function typeResolver(ObjectType $type, ReflectionClass $target): ReflectionTypeResolver { $typeKey = $target->isInterface() ? "{$type->toString()}/{$type->className()}" @@ -122,7 +144,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi } while ($type->className() !== $target->name) { - $type = $type->parent(); + $type = $this->parentType($type); } $generics = $type instanceof GenericType ? $type->generics() : []; @@ -147,11 +169,12 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi $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); @@ -160,7 +183,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi /** * @return array */ - private function localTypeAliases(ClassType $type): array + private function localTypeAliases(ObjectType $type): array { $reflection = Reflection::class($type->className()); $rawTypes = DocParser::localTypeAliases($reflection); @@ -185,7 +208,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi /** * @return array */ - private function importedTypeAliases(ClassType $type): array + private function importedTypeAliases(ObjectType $type): array { $reflection = Reflection::class($type->className()); $importedTypesRaw = DocParser::importedTypeAliases($reflection); @@ -201,7 +224,7 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi throw new InvalidTypeAliasImportClass($type, $class); } - if (! $classType instanceof ClassType) { + if (! $classType instanceof ObjectType) { throw new InvalidTypeAliasImportClassType($type, $classType); } @@ -219,11 +242,12 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi 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) { @@ -232,4 +256,38 @@ final class ReflectionClassDefinitionRepository implements ClassDefinitionReposi return $this->typeParserFactory->get(...$specs); } + + private function parentType(ObjectType $type): NativeClassType + { + $reflection = Reflection::class($type->className()); + + /** @var ReflectionClass $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; + } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php index 4dc72a31c8..3566476969 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionFunctionDefinitionRepository.php @@ -4,17 +4,22 @@ declare(strict_types=1); 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 */ @@ -52,7 +57,7 @@ final class ReflectionFunctionDefinitionRepository implements FunctionDefinition return new FunctionDefinition( $name, Reflection::signature($reflection), - $this->attributesRepository->for($reflection), + new Attributes(...$this->attributes($reflection)), $reflection->getFileName() ?: null, $class?->name, $reflection->getClosureThis() === null, @@ -67,7 +72,10 @@ final class ReflectionFunctionDefinitionRepository implements FunctionDefinition $class = $reflection->getClosureScopeClass(); $nativeSpecifications = []; - $advancedSpecification = [new AliasSpecification($reflection)]; + $advancedSpecification = [ + new AliasSpecification($reflection), + new GenericCheckerSpecification(), + ]; if ($class !== null) { $nativeSpecifications[] = new ClassContextSpecification($class->name); @@ -79,4 +87,15 @@ final class ReflectionFunctionDefinitionRepository implements FunctionDefinition return new ReflectionTypeResolver($nativeParser, $advancedParser); } + + /** + * @return list + */ + private function attributes(ReflectionFunction $reflection): array + { + return array_map( + fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute), + Reflection::attributes($reflection) + ); + } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php index b0c62e233b..919c9d34be 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionMethodDefinitionBuilder.php @@ -4,10 +4,12 @@ declare(strict_types=1); 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; @@ -16,15 +18,26 @@ use function array_map; /** @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() @@ -33,8 +46,9 @@ final class ReflectionMethodDefinitionBuilder $returnType = $typeResolver->resolveType($reflection); return new MethodDefinition( - $reflection->name, + $name, Reflection::signature($reflection), + new Attributes(...$attributes), new Parameters(...$parameters), $reflection->isStatic(), $reflection->isPublic(), diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php index cc0d212072..37f75282d7 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionParameterDefinitionBuilder.php @@ -4,25 +4,31 @@ declare(strict_types=1); 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(); @@ -39,6 +45,26 @@ final class ReflectionParameterDefinitionBuilder $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 + */ + private function attributes(ReflectionParameter $reflection): array + { + return array_map( + fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute), + Reflection::attributes($reflection) + ); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php index 802f242c9d..64936f33dd 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionPropertyDefinitionBuilder.php @@ -4,14 +4,19 @@ declare(strict_types=1); 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 { @@ -19,13 +24,14 @@ 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 @@ -38,10 +44,11 @@ final class ReflectionPropertyDefinitionBuilder $name, $signature, $type, + $nativeType, $hasDefaultValue, $defaultValue, $isPublic, - $attributes + new Attributes(...$this->attributes($reflection)), ); } @@ -54,4 +61,15 @@ final class ReflectionPropertyDefinitionBuilder return $reflection->getDeclaringClass()->getDefaultProperties()[$reflection->name] !== null || NullType::get()->matches($type); } + + /** + * @return list + */ + private function attributes(ReflectionProperty $reflection): array + { + return array_map( + fn (ReflectionAttribute $attribute) => $this->attributesRepository->for($attribute), + Reflection::attributes($reflection) + ); + } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionTypeResolver.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionTypeResolver.php index ef8f3568ed..4135f0ec82 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionTypeResolver.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Definition/Repository/Reflection/ReflectionTypeResolver.php @@ -4,7 +4,7 @@ declare(strict_types=1); 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; @@ -18,40 +18,58 @@ use ReflectionFunctionAbstract; 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 @@ -82,22 +100,6 @@ final class ReflectionTypeResolver 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 { diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Library/Container.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Library/Container.php index 35fda30213..ea74709887 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Library/Container.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Library/Container.php @@ -9,12 +9,11 @@ use CuyZ\Valinor\Cache\KeySanitizerCache; 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; @@ -34,10 +33,11 @@ use CuyZ\Valinor\Mapper\Tree\Builder\ErrorCatcherNodeBuilder; 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; @@ -49,11 +49,12 @@ use CuyZ\Valinor\Mapper\TypeArgumentsMapper; 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; @@ -63,6 +64,7 @@ use CuyZ\Valinor\Type\Types\IterableType; 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; @@ -108,29 +110,29 @@ final class Container 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); @@ -151,7 +153,7 @@ final class Container 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( @@ -188,9 +190,7 @@ final class Container new ValueTransformersHandler( $this->get(FunctionDefinitionRepository::class), ), - new KeyTransformersHandler( - $this->get(FunctionDefinitionRepository::class), - ), + new KeyTransformersHandler(), $settings->transformersSortedByPriority(), array_keys($settings->transformerAttributes), ), @@ -199,10 +199,13 @@ final class Container $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), ), @@ -210,13 +213,13 @@ final class Container 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(), diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/ArgumentsMapperError.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/ArgumentsMapperError.php index b7bd58b9fc..0cfbf969f8 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/ArgumentsMapperError.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/ArgumentsMapperError.php @@ -25,11 +25,11 @@ final class ArgumentsMapperError extends RuntimeException implements MappingErro 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); diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringArgumentsMapping.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringArgumentsMapping.php new file mode 100644 index 0000000000..3ccbc02fe5 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringArgumentsMapping.php @@ -0,0 +1,22 @@ +signature`: {$exception->getMessage()}", + 1711534351, + $exception, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringMapping.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringMapping.php new file mode 100644 index 0000000000..21b443c5e1 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Exception/TypeErrorDuringMapping.php @@ -0,0 +1,22 @@ +toString()}`: {$exception->getMessage()}", + 1711526329, + $exception, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Argument.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Argument.php index f97470919c..339b5631a9 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Argument.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Argument.php @@ -5,7 +5,6 @@ declare(strict_types=1); 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; @@ -31,11 +30,11 @@ final class Argument 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; } @@ -44,11 +43,11 @@ final class Argument 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; } @@ -77,6 +76,6 @@ final class Argument public function attributes(): Attributes { - return $this->attributes ??= AttributesContainer::empty(); + return $this->attributes ??= Attributes::empty(); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Arguments.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Arguments.php index 3ff47c6a18..023e390d0d 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Arguments.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Arguments.php @@ -14,7 +14,6 @@ use Traversable; use function array_map; use function array_values; -use function iterator_to_array; /** * @internal @@ -35,7 +34,7 @@ final class Arguments implements IteratorAggregate, Countable { return new self(...array_map( fn (ParameterDefinition $parameter) => Argument::fromParameter($parameter), - array_values(iterator_to_array($parameters)) // PHP8.1 array unpacking + array_values([...$parameters]) )); } @@ -43,7 +42,7 @@ final class Arguments implements IteratorAggregate, Countable { return new self(...array_map( fn (PropertyDefinition $property) => Argument::fromProperty($property), - array_values(iterator_to_array($properties)) // PHP8.1 array unpacking + array_values([...$properties]) )); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ArgumentsValues.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ArgumentsValues.php index 4a3129ebb4..f2d7aee4c7 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ArgumentsValues.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ArgumentsValues.php @@ -6,6 +6,7 @@ namespace CuyZ\Valinor\Mapper\Object; use CuyZ\Valinor\Mapper\Object\Exception\InvalidSource; use CuyZ\Valinor\Type\CompositeTraversableType; +use CuyZ\Valinor\Type\Types\ArrayKeyType; use IteratorAggregate; use Traversable; @@ -35,22 +36,22 @@ final class ArgumentsValues implements IteratorAggregate $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; } @@ -81,11 +82,11 @@ final class ArgumentsValues implements IteratorAggregate 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); @@ -108,7 +109,7 @@ final class ArgumentsValues implements IteratorAggregate return $clone; } - private function transformValueForSingleArgument(mixed $value): mixed + private function transformValueForSingleArgument(mixed $value, bool $allowSuperfluousKeys): mixed { if (count($this->arguments) !== 1) { return $value; @@ -116,15 +117,17 @@ final class ArgumentsValues implements IteratorAggregate $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; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Constructor.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Constructor.php new file mode 100644 index 0000000000..c39c5adc3a --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Constructor.php @@ -0,0 +1,45 @@ +mapper() + * ->map(Email::class, [ + * 'userName' => 'john.doe', + * 'domainName' => 'example.com', + * ]); // john.doe@example.com + * ``` + * + * @api + */ +#[Attribute(Attribute::TARGET_METHOD)] +final class Constructor {} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/CannotInstantiateObject.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/CannotInstantiateObject.php index cbecad81dd..b60fae7c08 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/CannotInstantiateObject.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/CannotInstantiateObject.php @@ -13,7 +13,7 @@ final class CannotInstantiateObject extends RuntimeException public function __construct(ClassDefinition $class) { parent::__construct( - "No available constructor found for class `{$class->name()}`.", + "No available constructor found for class `{$class->name}`.", 1646916477 ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorClassTypeParameter.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorClassTypeParameter.php index f47208efb3..5f7fc45b11 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorClassTypeParameter.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorClassTypeParameter.php @@ -14,7 +14,7 @@ final class InvalidConstructorClassTypeParameter extends LogicException 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 ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorMethodWithAttributeReturnType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorMethodWithAttributeReturnType.php new file mode 100644 index 0000000000..083fa2409c --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorMethodWithAttributeReturnType.php @@ -0,0 +1,27 @@ +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); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorReturnType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorReturnType.php index 136b66e29d..5fff4656ed 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorReturnType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/InvalidConstructorReturnType.php @@ -13,12 +13,10 @@ final class InvalidConstructorReturnType extends LogicException { 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); diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/MissingConstructorClassTypeParameter.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/MissingConstructorClassTypeParameter.php index 1c5c8d4e17..57110e9c40 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/MissingConstructorClassTypeParameter.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/MissingConstructorClassTypeParameter.php @@ -13,7 +13,7 @@ final class MissingConstructorClassTypeParameter extends LogicException 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 ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/ObjectBuildersCollision.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/ObjectBuildersCollision.php index dce0aeee48..697fed7cbb 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/ObjectBuildersCollision.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Exception/ObjectBuildersCollision.php @@ -20,7 +20,7 @@ final class ObjectBuildersCollision extends RuntimeException $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 ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/CacheObjectBuilderFactory.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/CacheObjectBuilderFactory.php index 82f140072b..c1e460dfdd 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/CacheObjectBuilderFactory.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/CacheObjectBuilderFactory.php @@ -19,7 +19,7 @@ final class CacheObjectBuilderFactory implements ObjectBuilderFactory public function for(ClassDefinition $class): array { - $signature = $class->type()->toString(); + $signature = $class->type->toString(); $entry = $this->cache->get($signature); diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php index feff612f5c..22346f325a 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ConstructorObjectBuilderFactory.php @@ -7,9 +7,11 @@ namespace CuyZ\Valinor\Mapper\Object\Factory; 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; @@ -22,8 +24,10 @@ use CuyZ\Valinor\Type\ObjectType; 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; @@ -37,7 +41,7 @@ final class ConstructorObjectBuilderFactory implements ObjectBuilderFactory private ObjectBuilderFactory $delegate, /** @var array */ private array $nativeConstructors, - private FunctionsContainer $constructors + private FunctionsContainer $constructors, ) {} public function for(ClassDefinition $class): array @@ -45,7 +49,7 @@ final class ConstructorObjectBuilderFactory implements ObjectBuilderFactory $builders = $this->builders($class); if (count($builders) === 0) { - if ($class->methods()->hasConstructor()) { + if ($class->methods->hasConstructor()) { throw new CannotInstantiateObject($class); } @@ -60,9 +64,9 @@ final class ConstructorObjectBuilderFactory implements ObjectBuilderFactory */ 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 = []; @@ -71,50 +75,79 @@ final class ConstructorObjectBuilderFactory implements ObjectBuilderFactory 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(); @@ -142,13 +175,16 @@ final class ConstructorObjectBuilderFactory implements ObjectBuilderFactory $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); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php index f1c80bc209..fa5613ac4d 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeObjectBuilderFactory.php @@ -12,7 +12,7 @@ use CuyZ\Valinor\Mapper\Object\DateTimeFormatConstructor; 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; @@ -31,7 +31,7 @@ final class DateTimeObjectBuilderFactory implements ObjectBuilderFactory public function for(ClassDefinition $class): array { - $className = $class->name(); + $className = $class->name; $builders = $this->delegate->for($class); @@ -45,13 +45,13 @@ final class DateTimeObjectBuilderFactory implements ObjectBuilderFactory $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); diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeZoneObjectBuilderFactory.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeZoneObjectBuilderFactory.php index 48871ca9c7..cc17713fcb 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeZoneObjectBuilderFactory.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/DateTimeZoneObjectBuilderFactory.php @@ -11,7 +11,7 @@ use CuyZ\Valinor\Mapper\Object\FunctionObjectBuilder; 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; @@ -35,7 +35,7 @@ final class DateTimeZoneObjectBuilderFactory implements ObjectBuilderFactory { $builders = $this->delegate->for($class); - if ($class->name() !== DateTimeZone::class) { + if ($class->name !== DateTimeZone::class) { return $builders; } @@ -54,13 +54,13 @@ final class DateTimeZoneObjectBuilderFactory implements ObjectBuilderFactory 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 { diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php index abdb91dc7e..187f11bcc2 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/Factory/ReflectionObjectBuilderFactory.php @@ -6,15 +6,14 @@ namespace CuyZ\Valinor\Mapper\Object\Factory; 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 []; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FilteredObjectBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FilteredObjectBuilder.php index ed5dd07e77..acabadfe17 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FilteredObjectBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FilteredObjectBuilder.php @@ -9,23 +9,30 @@ use CuyZ\Valinor\Mapper\Object\Exception\SeveralObjectBuildersFound; 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 @@ -41,7 +48,7 @@ final class FilteredObjectBuilder implements ObjectBuilder private function filterBuilder(mixed $source, ObjectBuilder ...$builders): ObjectBuilder { if (count($builders) === 1) { - return $builders[0]; + return reset($builders); } /** @var non-empty-list $builders */ diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FunctionObjectBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FunctionObjectBuilder.php index 57f2e08f92..db1cc19dae 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FunctionObjectBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/FunctionObjectBuilder.php @@ -7,7 +7,7 @@ namespace CuyZ\Valinor\Mapper\Object; 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; @@ -24,16 +24,16 @@ final class FunctionObjectBuilder implements ObjectBuilder 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); @@ -51,16 +51,16 @@ final class FunctionObjectBuilder implements ObjectBuilder 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); } @@ -68,6 +68,6 @@ final class FunctionObjectBuilder implements ObjectBuilder public function signature(): string { - return $this->function->definition()->signature(); + return $this->function->definition->signature; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/MethodArguments.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/MethodArguments.php index 7a10723f42..ded388c63c 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/MethodArguments.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/MethodArguments.php @@ -26,9 +26,9 @@ final class MethodArguments implements IteratorAggregate 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]; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/NativeConstructorObjectBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/NativeConstructorObjectBuilder.php index ec7bedd304..808a16f998 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/NativeConstructorObjectBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/NativeConstructorObjectBuilder.php @@ -17,13 +17,13 @@ final class NativeConstructorObjectBuilder implements ObjectBuilder 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); @@ -34,6 +34,6 @@ final class NativeConstructorObjectBuilder implements ObjectBuilder public function signature(): string { - return $this->class->methods()->constructor()->signature(); + return $this->class->methods->constructor()->signature; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ReflectionObjectBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ReflectionObjectBuilder.php index 54447c757d..54eccbf427 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ReflectionObjectBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Object/ReflectionObjectBuilder.php @@ -17,12 +17,12 @@ final class ReflectionObjectBuilder implements ObjectBuilder 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 { @@ -37,6 +37,6 @@ final class ReflectionObjectBuilder implements ObjectBuilder public function signature(): string { - return $this->class->name() . ' (properties)'; + return $this->class->name . ' (properties)'; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/CasterProxyNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/CasterProxyNodeBuilder.php index db2de428b2..9a50c340fa 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/CasterProxyNodeBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/CasterProxyNodeBuilder.php @@ -5,6 +5,10 @@ declare(strict_types=1); 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 @@ -16,11 +20,28 @@ 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); + } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/FilteredObjectNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/FilteredObjectNodeBuilder.php new file mode 100644 index 0000000000..712de13078 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/FilteredObjectNodeBuilder.php @@ -0,0 +1,79 @@ +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 + */ + 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); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php index eb6a03c44f..b96b663a48 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/InterfaceNodeBuilder.php @@ -4,6 +4,7 @@ declare(strict_types=1); 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; @@ -11,9 +12,11 @@ use CuyZ\Valinor\Mapper\Object\Factory\ObjectBuilderFactory; 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; @@ -25,8 +28,10 @@ final class InterfaceNodeBuilder implements NodeBuilder 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 @@ -37,6 +42,14 @@ final class InterfaceNodeBuilder implements NodeBuilder 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([]); } @@ -44,7 +57,7 @@ final class InterfaceNodeBuilder implements NodeBuilder $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); } @@ -52,9 +65,9 @@ final class InterfaceNodeBuilder implements NodeBuilder } $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); } @@ -73,15 +86,26 @@ final class InterfaceNodeBuilder implements NodeBuilder 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 @@ -116,7 +140,7 @@ final class InterfaceNodeBuilder implements NodeBuilder */ 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 = []; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/NativeClassNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/NativeClassNodeBuilder.php deleted file mode 100644 index f129b3d313..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/NativeClassNodeBuilder.php +++ /dev/null @@ -1,41 +0,0 @@ -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); - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/NullNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/NullNodeBuilder.php new file mode 100644 index 0000000000..d7631bee46 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/NullNodeBuilder.php @@ -0,0 +1,29 @@ +type(); + $value = $shell->value(); + + assert($type instanceof NullType); + + if ($value !== null) { + throw new SourceIsNotNull(); + } + + return TreeNode::leaf($shell, null); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectImplementations.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectImplementations.php index 8b9ba566e3..bf92f65720 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectImplementations.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectImplementations.php @@ -6,14 +6,12 @@ namespace CuyZ\Valinor\Mapper\Tree\Builder; 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; @@ -21,6 +19,8 @@ use CuyZ\Valinor\Type\Types\InterfaceType; use CuyZ\Valinor\Type\Types\UnionType; use Exception; +use function assert; + /** @internal */ final class ObjectImplementations { @@ -30,12 +30,7 @@ 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 { @@ -44,7 +39,7 @@ final class ObjectImplementations public function function(string $name): FunctionDefinition { - return $this->functions->get($name)->definition(); + return $this->functions->get($name)->definition; } /** @@ -52,6 +47,9 @@ final class ObjectImplementations */ 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] @@ -64,7 +62,7 @@ final class ObjectImplementations 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); } @@ -81,20 +79,16 @@ final class ObjectImplementations */ 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); } @@ -113,10 +107,10 @@ final class ObjectImplementations */ 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 []; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectNodeBuilder.php index e0965e5b49..e67b613a76 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectNodeBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ObjectNodeBuilder.php @@ -4,76 +4,39 @@ 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\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 - */ - 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); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ShapedArrayNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ShapedArrayNodeBuilder.php index 492d74a402..6dc633f235 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ShapedArrayNodeBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ShapedArrayNodeBuilder.php @@ -69,6 +69,15 @@ final class ShapedArrayNodeBuilder implements NodeBuilder 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; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/TreeNode.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/TreeNode.php index 8a17f437e7..890c2ec053 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/TreeNode.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/TreeNode.php @@ -9,6 +9,7 @@ use CuyZ\Valinor\Mapper\Tree\Message\Message; 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; @@ -77,11 +78,7 @@ final class TreeNode 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); } @@ -91,6 +88,19 @@ final class TreeNode return $this->shell->name(); } + public function type(): Type + { + return $this->shell->type(); + } + + /** + * @return array + */ + public function children(): array + { + return $this->children; + } + public function isValid(): bool { return $this->valid; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/UnionNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/UnionNodeBuilder.php index eebd747aec..c118c2e5d0 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/UnionNodeBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/UnionNodeBuilder.php @@ -4,30 +4,26 @@ 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\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 { @@ -37,72 +33,78 @@ final class UnionNodeBuilder implements NodeBuilder 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); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ValueAlteringNodeBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ValueAlteringNodeBuilder.php index 66a6630dac..ed518d946e 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ValueAlteringNodeBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Builder/ValueAlteringNodeBuilder.php @@ -26,19 +26,19 @@ final class ValueAlteringNodeBuilder implements NodeBuilder $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); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotInferFinalClass.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotInferFinalClass.php index d2bd244e4a..b8e84f7b72 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotInferFinalClass.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotInferFinalClass.php @@ -14,7 +14,7 @@ final class CannotInferFinalClass extends RuntimeException 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 ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveTypeFromUnion.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveTypeFromUnion.php index ee470cacdb..a8dacb2f6c 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveTypeFromUnion.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/CannotResolveTypeFromUnion.php @@ -27,8 +27,7 @@ final class CannotResolveTypeFromUnion extends RuntimeException implements Error $this->parameters = [ 'allowed_types' => implode( ', ', - // PHP8.1 First-class callable syntax - array_map([TypeHelper::class, 'dump'], $unionType->types()) + array_map(TypeHelper::dump(...), $unionType->types()) ), ]; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/InterfaceHasBothConstructorAndInfer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/InterfaceHasBothConstructorAndInfer.php new file mode 100644 index 0000000000..77cb9851a9 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/InterfaceHasBothConstructorAndInfer.php @@ -0,0 +1,22 @@ +returnType()->toString()}` of `{$functionDefinition->signature()}`.", + "No implementation of `$name` found with return type `{$functionDefinition->returnType->toString()}` of `$functionDefinition->signature`.", 1653990549 ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/SourceIsNotNull.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/SourceIsNotNull.php new file mode 100644 index 0000000000..a07f4368b2 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/SourceIsNotNull.php @@ -0,0 +1,26 @@ +body = 'Value {source_value} is not null.'; + + parent::__construct($this->body, 1710263908); + } + + public function body(): string + { + return $this->body; + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/TooManyResolvedTypesFromUnion.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/TooManyResolvedTypesFromUnion.php new file mode 100644 index 0000000000..857eaae1ac --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/TooManyResolvedTypesFromUnion.php @@ -0,0 +1,50 @@ + */ + 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; + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/UnresolvableShellType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/UnresolvableShellType.php new file mode 100644 index 0000000000..5552973362 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Exception/UnresolvableShellType.php @@ -0,0 +1,17 @@ +message()); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/DefaultMessage.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/DefaultMessage.php index b35d0f5830..3fdb96bdb6 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/DefaultMessage.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/DefaultMessage.php @@ -35,6 +35,9 @@ interface DefaultMessage '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.', ], @@ -74,6 +77,12 @@ interface DefaultMessage '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}.', ], diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/MessageBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/MessageBuilder.php index aa7e55cf21..943e1ed26d 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/MessageBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/MessageBuilder.php @@ -121,10 +121,9 @@ final class MessageBuilder /** * @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 @@ -132,7 +131,7 @@ final class MessageBuilder : $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 { /** @@ -161,7 +160,7 @@ final class MessageBuilder }; } - 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 { /** diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/UserlandError.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/UserlandError.php index 2313239a7d..fd4fc4f41b 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/UserlandError.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Message/UserlandError.php @@ -10,11 +10,7 @@ use Throwable; /** @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 diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Shell.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Shell.php index 8d61989189..ca87bafe78 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Shell.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/Tree/Shell.php @@ -5,7 +5,7 @@ declare(strict_types=1); 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; @@ -28,7 +28,9 @@ final class Shell 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 @@ -95,7 +97,7 @@ final class Shell public function attributes(): Attributes { - return $this->attributes ?? AttributesContainer::empty(); + return $this->attributes ?? Attributes::empty(); } public function path(): string diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeArgumentsMapper.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeArgumentsMapper.php index 5d128cdc23..f0d73f8116 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeArgumentsMapper.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeArgumentsMapper.php @@ -6,7 +6,9 @@ namespace CuyZ\Valinor\Mapper; 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; @@ -32,17 +34,21 @@ final class TypeArgumentsMapper implements ArgumentsMapper $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()); diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeTreeMapper.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeTreeMapper.php index a9b4194c7e..d7691dc185 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeTreeMapper.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Mapper/TypeTreeMapper.php @@ -5,8 +5,10 @@ declare(strict_types=1); 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; @@ -41,6 +43,10 @@ final class TypeTreeMapper implements TreeMapper $shell = Shell::root($type, $source); - return $this->nodeBuilder->build($shell); + try { + return $this->nodeBuilder->build($shell); + } catch (UnresolvableShellType $exception) { + throw new TypeErrorDuringMapping($type, $exception); + } } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/MapperBuilder.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/MapperBuilder.php index 60d4888176..48ffecc21a 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/MapperBuilder.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/MapperBuilder.php @@ -176,8 +176,8 @@ final class MapperBuilder * 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 * { @@ -477,8 +477,9 @@ final class MapperBuilder * 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: * @@ -505,9 +506,9 @@ final class MapperBuilder * 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?! diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/ArrayNormalizer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/ArrayNormalizer.php index c711e5e69f..c76a000f7e 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/ArrayNormalizer.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/ArrayNormalizer.php @@ -26,13 +26,10 @@ final class ArrayNormalizer implements Normalizer { $value = $this->transformer->transform($value); + /** @var array|scalar|null */ return $this->normalizeIterator($value); } - /** - * @param iterable|scalar|null $value - * @return array|scalar|null - */ private function normalizeIterator(mixed $value): mixed { if (is_iterable($value)) { @@ -40,8 +37,7 @@ final class ArrayNormalizer implements Normalizer $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; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/AsTransformer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/AsTransformer.php new file mode 100644 index 0000000000..7263e9a8b3 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/AsTransformer.php @@ -0,0 +1,55 @@ +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 {} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerHasTooManyParameters.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerHasTooManyParameters.php index eec49cd50d..3a751f3dbe 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerHasTooManyParameters.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerHasTooManyParameters.php @@ -4,16 +4,16 @@ declare(strict_types=1); 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, ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerParameterInvalidType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerParameterInvalidType.php index 30c955fc36..a1cef84276 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerParameterInvalidType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/KeyTransformerParameterInvalidType.php @@ -4,16 +4,16 @@ declare(strict_types=1); 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, ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasInvalidCallableParameter.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasInvalidCallableParameter.php index a9794b1209..b0360044bb 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasInvalidCallableParameter.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasInvalidCallableParameter.php @@ -5,16 +5,17 @@ declare(strict_types=1); 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, ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasNoParameter.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasNoParameter.php index 272e441263..d04173db18 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasNoParameter.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasNoParameter.php @@ -5,15 +5,16 @@ declare(strict_types=1); 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, ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasTooManyParameters.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasTooManyParameters.php index f8291094ec..46dc929be2 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasTooManyParameters.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Exception/TransformerHasTooManyParameters.php @@ -5,15 +5,16 @@ declare(strict_types=1); 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, ); } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Format.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Format.php index c8c32b454e..758b6c3c1f 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Format.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Format.php @@ -52,6 +52,37 @@ final class Format 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 + */ + public static function json(): self + { + return new self(JsonNormalizer::class); + } + /** * @param class-string $type */ diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Formatter/Exception/CannotFormatInvalidTypeToJson.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Formatter/Exception/CannotFormatInvalidTypeToJson.php new file mode 100644 index 0000000000..ae0854c1d3 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Formatter/Exception/CannotFormatInvalidTypeToJson.php @@ -0,0 +1,21 @@ +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); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Formatter/StreamFormatter.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Formatter/StreamFormatter.php new file mode 100644 index 0000000000..a1c8e4c61b --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Formatter/StreamFormatter.php @@ -0,0 +1,16 @@ + + */ +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` 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)); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/StreamNormalizer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/StreamNormalizer.php new file mode 100644 index 0000000000..a5a683bec0 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/StreamNormalizer.php @@ -0,0 +1,30 @@ + + */ +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(); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/KeyTransformersHandler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/KeyTransformersHandler.php index 3c6c2d67eb..8f86c89f03 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/KeyTransformersHandler.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/KeyTransformersHandler.php @@ -4,8 +4,8 @@ declare(strict_types=1); 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; @@ -16,53 +16,48 @@ final class KeyTransformersHandler /** @var array */ private array $keyTransformerCheck = []; - public function __construct( - private FunctionDefinitionRepository $functionDefinitionRepository, - ) {} - /** - * @param list $attributes + * @param list $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); } } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/RecursiveTransformer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/RecursiveTransformer.php index 917cb3726b..2b8763df57 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/RecursiveTransformer.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/RecursiveTransformer.php @@ -4,23 +4,26 @@ declare(strict_types=1); 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; @@ -47,7 +50,7 @@ final class RecursiveTransformer /** * @param WeakMap $references - * @param list $attributes + * @param list $attributes * @return iterable|scalar|null */ private function doTransform(mixed $value, WeakMap $references, array $attributes = []): mixed @@ -57,20 +60,23 @@ final class RecursiveTransformer 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, @@ -102,7 +108,11 @@ final class RecursiveTransformer 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 @@ -111,37 +121,12 @@ final class RecursiveTransformer $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); @@ -169,22 +154,20 @@ final class RecursiveTransformer throw new TypeUnhandledByNormalizer($value); } - /** - * @return list - */ - 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; + }); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/ValueTransformersHandler.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/ValueTransformersHandler.php index ad5b629f46..d5cdf90ad3 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/ValueTransformersHandler.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Normalizer/Transformer/ValueTransformersHandler.php @@ -4,7 +4,9 @@ declare(strict_types=1); 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; @@ -13,7 +15,6 @@ use CuyZ\Valinor\Type\Types\CallableType; use function array_shift; use function call_user_func; -use function method_exists; /** @internal */ final class ValueTransformersHandler @@ -26,7 +27,7 @@ final class ValueTransformersHandler ) {} /** - * @param array $attributes + * @param array $attributes * @param list $transformers * @return array|scalar|null */ @@ -39,7 +40,7 @@ final class ValueTransformersHandler /** * @param list $transformers - * @param list $attributes + * @param array $attributes */ private function next(array $transformers, mixed $value, array $attributes, callable $defaultTransformer): callable { @@ -61,7 +62,7 @@ final class ValueTransformersHandler $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); } @@ -69,7 +70,7 @@ final class ValueTransformersHandler } /** - * @param array $attributes + * @param array $attributes */ private function nextAttribute(mixed $value, array $attributes, callable $next): callable { @@ -79,43 +80,46 @@ final class ValueTransformersHandler 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); } } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/CombiningType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/CombiningType.php index a659fde28b..8dbd7f98ad 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/CombiningType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/CombiningType.php @@ -10,7 +10,7 @@ interface CombiningType extends CompositeType public function isMatchedBy(Type $other): bool; /** - * @return Type[] + * @return non-empty-list */ - public function types(): iterable; + public function types(): array; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/GenericType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/GenericType.php index ce8a491960..fb8fde99b5 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/GenericType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/GenericType.php @@ -6,7 +6,12 @@ namespace CuyZ\Valinor\Type; interface GenericType extends CompositeType { /** - * @return array + * @return class-string + */ + public function className(): string; + + /** + * @return array */ public function generics(): array; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/ExtendTagTypeError.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/ExtendTagTypeError.php deleted file mode 100644 index 6db374aefc..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/ExtendTagTypeError.php +++ /dev/null @@ -1,25 +0,0 @@ - $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 - ); - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagClassName.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagClassName.php deleted file mode 100644 index e61268ba1d..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagClassName.php +++ /dev/null @@ -1,28 +0,0 @@ - $reflection - */ - public function __construct(ReflectionClass $reflection, Type $invalidExtendTag) - { - /** @var ReflectionClass $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 - ); - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagType.php deleted file mode 100644 index cb2afe702b..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/InvalidExtendTagType.php +++ /dev/null @@ -1,28 +0,0 @@ - $reflection - */ - public function __construct(ReflectionClass $reflection, Type $invalidExtendTag) - { - /** @var ReflectionClass $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 - ); - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/MissingGenerics.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/MissingGenerics.php index 2f7039c5aa..c775bcadcd 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/MissingGenerics.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/MissingGenerics.php @@ -19,7 +19,7 @@ final class MissingGenerics extends RuntimeException implements InvalidType /** * @param class-string $className * @param Type[] $generics - * @param Type[] $templates + * @param array $templates */ public function __construct(string $className, array $generics, array $templates) { diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/SeveralExtendTagsFound.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/SeveralExtendTagsFound.php deleted file mode 100644 index b6cf57c44a..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Generic/SeveralExtendTagsFound.php +++ /dev/null @@ -1,24 +0,0 @@ - $reflection - */ - public function __construct(ReflectionClass $reflection) - { - parent::__construct( - "Only one `@extends` tag should be set for the class `$reflection->name`.", - 1670195494 - ); - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayClosingBracketMissing.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayClosingBracketMissing.php index b6bf3bc081..9ee03855be 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayClosingBracketMissing.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayClosingBracketMissing.php @@ -5,6 +5,7 @@ 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; @@ -16,10 +17,16 @@ final class ShapedArrayClosingBracketMissing extends RuntimeException implements /** * @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 diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayInvalidUnsealedType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayInvalidUnsealedType.php new file mode 100644 index 0000000000..cef92e20c7 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayInvalidUnsealedType.php @@ -0,0 +1,32 @@ + $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, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayUnexpectedTokenAfterSealedType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayUnexpectedTokenAfterSealedType.php new file mode 100644 index 0000000000..15be0280fd --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayUnexpectedTokenAfterSealedType.php @@ -0,0 +1,36 @@ + $elements + * @param list $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, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayWithoutElementsWithSealedType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayWithoutElementsWithSealedType.php new file mode 100644 index 0000000000..578f00c51a --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Exception/Iterable/ShapedArrayWithoutElementsWithSealedType.php @@ -0,0 +1,23 @@ +toString()}}"; + + parent::__construct( + "Missing elements in shaped array signature `$signature`.", + 1711629845, + ); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php index facde9206e..461f18d087 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/LexingTypeParserFactory.php @@ -6,8 +6,8 @@ namespace CuyZ\Valinor\Type\Parser\Factory; 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; @@ -18,26 +18,24 @@ final class LexingTypeParserFactory implements TypeParserFactory 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; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php index 59e706016a..bf8e4aaa0b 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/AliasSpecification.php @@ -4,8 +4,12 @@ declare(strict_types=1); 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; @@ -15,11 +19,95 @@ final class AliasSpecification implements TypeParserSpecification { public function __construct( /** @var ReflectionClass|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; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/ClassContextSpecification.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/ClassContextSpecification.php index dcb3c628e1..272ea946b2 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/ClassContextSpecification.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/ClassContextSpecification.php @@ -4,19 +4,30 @@ declare(strict_types=1); 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; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/GenericCheckerSpecification.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/GenericCheckerSpecification.php new file mode 100644 index 0000000000..a33eaa5fbb --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/GenericCheckerSpecification.php @@ -0,0 +1,24 @@ + */ - 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; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php index 6c26b887bf..a48aba87b8 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Factory/Specifications/TypeParserSpecification.php @@ -4,10 +4,14 @@ declare(strict_types=1); 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; } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/GenericCheckerParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/GenericCheckerParser.php new file mode 100644 index 0000000000..6e47b9bdba --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/GenericCheckerParser.php @@ -0,0 +1,101 @@ +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()); + } + } + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php deleted file mode 100644 index 6601ef8962..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AdvancedClassLexer.php +++ /dev/null @@ -1,30 +0,0 @@ -delegate->tokenize($symbol); - - if ($token instanceof ClassNameToken) { - return new AdvancedClassNameToken($token, $this->typeParserFactory); - } - - return $token; - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php deleted file mode 100644 index 746d0a7795..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/AliasLexer.php +++ /dev/null @@ -1,107 +0,0 @@ -|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; - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/ClassContextLexer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/ClassContextLexer.php deleted file mode 100644 index b3af6bfdc7..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/ClassContextLexer.php +++ /dev/null @@ -1,43 +0,0 @@ - */ - 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; - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/NativeLexer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/NativeLexer.php index 68b1ad040f..5040df131b 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/NativeLexer.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/NativeLexer.php @@ -6,7 +6,6 @@ namespace CuyZ\Valinor\Type\Parser\Lexer; 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; @@ -14,7 +13,6 @@ use CuyZ\Valinor\Type\Parser\Lexer\Token\ClosingSquareBracketToken; 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; @@ -28,10 +26,8 @@ use CuyZ\Valinor\Type\Parser\Lexer\Token\OpeningCurlyBracketToken; 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; @@ -40,6 +36,8 @@ use function strtolower; /** @internal */ final class NativeLexer implements TypeLexer { + public function __construct(private TypeLexer $delegate) {} + public function tokenize(string $symbol): Token { if (NativeToken::accepts($symbol)) { @@ -59,6 +57,7 @@ final class NativeLexer implements TypeLexer ':' => ColonToken::get(), '?' => NullableToken::get(), ',' => CommaToken::get(), + '...' => TripleDotsToken::get(), '"', "'" => new QuoteToken($symbol), 'int', 'integer' => IntegerToken::get(), 'array' => ArrayToken::array(), @@ -83,17 +82,6 @@ final class NativeLexer implements TypeLexer return new FloatValueToken((float)$symbol); } - /** @infection-ignore-all */ - if (PHP_VERSION_ID >= 8_01_00 && enum_exists($symbol)) { - /** @var class-string $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); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/SpecificationsLexer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/SpecificationsLexer.php new file mode 100644 index 0000000000..521863a0d3 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/SpecificationsLexer.php @@ -0,0 +1,23 @@ + */ + private array $specifications, + ) {} + + public function tokenize(string $symbol): Token + { + return (new VacantToken($symbol, $this->specifications)); + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/AdvancedClassNameToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/AdvancedClassNameToken.php deleted file mode 100644 index adb3dc2e2c..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/AdvancedClassNameToken.php +++ /dev/null @@ -1,231 +0,0 @@ -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 $reflection - * @return array - */ - 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 $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 $templates - * @param Type[] $generics - * @return array - */ - 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 $reflection - * @param ReflectionClass $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; - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ArrayToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ArrayToken.php index 8d1563f069..57bcd85f4f 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ArrayToken.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ArrayToken.php @@ -12,6 +12,9 @@ use CuyZ\Valinor\Type\Parser\Exception\Iterable\ShapedArrayColonTokenMissing; 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; @@ -99,6 +102,8 @@ final class ArrayToken implements TraversingToken $elements = []; $index = 0; + $isUnsealed = false; + $unsealedType = null; while (! $stream->done()) { if ($stream->next() instanceof ClosingCurlyBracketToken) { @@ -121,12 +126,50 @@ final class ArrayToken implements TraversingToken $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); @@ -178,10 +221,16 @@ final class ArrayToken implements TraversingToken } } - 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); } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ClassNameToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ClassNameToken.php index f3be92690a..d1a5e0abf6 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ClassNameToken.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ClassNameToken.php @@ -7,17 +7,25 @@ namespace CuyZ\Valinor\Type\Parser\Lexer\Token; 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; @@ -47,7 +55,14 @@ final class ClassNameToken implements TraversingToken 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 @@ -87,9 +102,74 @@ final class ClassNameToken implements TraversingToken $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 $templates + * @param class-string $className + * @return list + */ + 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 $templates + * @param list $generics + * @return array + */ + 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; + } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/IntegerToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/IntegerToken.php index 2f2e699504..43b05976c3 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/IntegerToken.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/IntegerToken.php @@ -34,7 +34,7 @@ final class IntegerToken implements TraversingToken throw new IntegerRangeMissingMinValue(); } - if ($stream->next() instanceof UnknownSymbolToken) { + if ($stream->next()->symbol() === 'min') { $min = new IntegerValueType(PHP_INT_MIN); $stream->forward(); } else { @@ -53,7 +53,7 @@ final class IntegerToken implements TraversingToken throw new IntegerRangeMissingMaxValue($min); } - if ($stream->next() instanceof UnknownSymbolToken) { + if ($stream->next()->symbol() === 'max') { $max = new IntegerValueType(PHP_INT_MAX); $stream->forward(); } else { diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ObjectToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ObjectToken.php new file mode 100644 index 0000000000..852978f3c5 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/ObjectToken.php @@ -0,0 +1,30 @@ +className) + ? (new EnumNameToken($this->className))->traverse($stream) + : (new ClassNameToken($this->className))->traverse($stream); + } + + public function symbol(): string + { + return $this->className; + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/TripleDotsToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/TripleDotsToken.php new file mode 100644 index 0000000000..a800bbaa58 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/TripleDotsToken.php @@ -0,0 +1,18 @@ +symbol); - } - - public function symbol(): string - { - return $this->symbol; - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/VacantToken.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/VacantToken.php new file mode 100644 index 0000000000..f3fa0c364e --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/Token/VacantToken.php @@ -0,0 +1,49 @@ + */ + 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; + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/TokensExtractor.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/TokensExtractor.php new file mode 100644 index 0000000000..aa2197498a --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/TokensExtractor.php @@ -0,0 +1,82 @@ + '[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 */ + 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 + */ + public function all(): array + { + return $this->symbols; + } +} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/TypeAliasLexer.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/TypeAliasLexer.php deleted file mode 100644 index 698a929299..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/Lexer/TypeAliasLexer.php +++ /dev/null @@ -1,28 +0,0 @@ - */ - 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); - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LexingParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LexingParser.php index 116de4ce0f..10ecb35f03 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LexingParser.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/LexingParser.php @@ -2,6 +2,7 @@ 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; @@ -13,7 +14,7 @@ class LexingParser implements TypeParser public function parse(string $raw): Type { - $symbols = new ParserSymbols($raw); + $symbols = new TokensExtractor($raw); $tokens = array_map( fn (string $symbol) => $this->lexer->tokenize($symbol), diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/ParserSymbols.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/ParserSymbols.php deleted file mode 100644 index 15053aec54..0000000000 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Parser/ParserSymbols.php +++ /dev/null @@ -1,82 +0,0 @@ -', '[', ']', '{', '}', ':', '?', ',', "'", '"']; - - /** @var list */ - 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 - */ - 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); - } - } -} diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ArrayKeyType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ArrayKeyType.php index dd0bd84777..931f062a17 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ArrayKeyType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ArrayKeyType.php @@ -4,16 +4,19 @@ declare(strict_types=1); 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; @@ -21,28 +24,36 @@ final class ArrayKeyType implements Type private static self $string; - /** @var array */ + /** @var non-empty-list */ 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 $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 @@ -55,7 +66,7 @@ final class ArrayKeyType implements Type 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, @@ -86,6 +97,10 @@ final class ArrayKeyType implements Type return true; } + if ($other instanceof UnionType) { + return $this->isMatchedBy($other); + } + if (! $other instanceof self) { return false; } @@ -114,6 +129,33 @@ final class ArrayKeyType implements Type 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; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/InterfaceType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/InterfaceType.php index 4db2359d44..b94cfddca3 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/InterfaceType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/InterfaceType.php @@ -18,7 +18,7 @@ final class InterfaceType implements ObjectType, GenericType public function __construct( /** @var class-string */ private string $interfaceName, - /** @var array */ + /** @var array */ private array $generics = [] ) {} @@ -51,7 +51,7 @@ final class InterfaceType implements ObjectType, GenericType return false; } - return is_a($other->className(), $this->interfaceName, true); + return is_a($this->interfaceName, $other->className(), true); } public function traverse(): array diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/IntersectionType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/IntersectionType.php index 61724eba52..9b220df91f 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/IntersectionType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/IntersectionType.php @@ -9,20 +9,21 @@ use CuyZ\Valinor\Type\ObjectType; 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 */ 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 @@ -82,7 +83,7 @@ final class IntersectionType implements CombiningType } /** - * @return ObjectType[] + * @return non-empty-list */ public function types(): array { diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ListType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ListType.php index f669dd08ea..84d6460e7f 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ListType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ListType.php @@ -42,7 +42,6 @@ final class ListType implements CompositeTraversableType return false; } - // PHP8.1 use `array_is_list` $i = 0; foreach ($value as $key => $item) { diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/NativeClassType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/NativeClassType.php index 6b84346b19..e7ccf38381 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/NativeClassType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/NativeClassType.php @@ -11,8 +11,6 @@ use CuyZ\Valinor\Type\CompositeType; use CuyZ\Valinor\Type\Type; use function array_map; -use function assert; -use function get_parent_class; use function is_a; /** @internal */ @@ -21,24 +19,12 @@ final class NativeClassType implements ClassType, GenericType public function __construct( /** @var class-string */ private string $className, - /** @var array */ + /** @var array */ 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; @@ -49,18 +35,6 @@ final class NativeClassType implements ClassType, GenericType 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; diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ShapedArrayType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ShapedArrayType.php index 4238cdb6ff..e94cd09aa3 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ShapedArrayType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/ShapedArrayType.php @@ -9,10 +9,9 @@ use CuyZ\Valinor\Type\CompositeType; 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; @@ -24,15 +23,13 @@ final class ShapedArrayType implements CompositeType /** @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 = []; @@ -40,24 +37,56 @@ final class ShapedArrayType implements CompositeType $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()) { @@ -67,11 +96,15 @@ final class ShapedArrayType implements CompositeType 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 @@ -98,6 +131,10 @@ final class ShapedArrayType implements CompositeType } } + if ($this->isUnsealed && ! $this->unsealedType()->matches($other)) { + return false; + } + return true; } @@ -107,11 +144,9 @@ final class ShapedArrayType implements CompositeType 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; } } @@ -121,6 +156,11 @@ final class ShapedArrayType implements CompositeType } } + if ($other->isUnsealed) { + return $this->isUnsealed + && $this->unsealedType()->matches($other->unsealedType()); + } + return true; } @@ -136,6 +176,10 @@ final class ShapedArrayType implements CompositeType } } + if ($this->isUnsealed) { + $types = [...$types, $this->unsealedType(), ...$this->unsealedType()->traverse()]; + } + return $types; } @@ -149,6 +193,19 @@ final class ShapedArrayType implements CompositeType 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; } } diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnionType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnionType.php index c66ce2bcc2..5ed460ddd4 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnionType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnionType.php @@ -15,30 +15,34 @@ use function implode; /** @internal */ final class UnionType implements CombiningType { - /** @var Type[] */ - private array $types = []; + /** @var non-empty-list */ + 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 @@ -65,12 +69,12 @@ final class UnionType implements CombiningType } 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 diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnresolvableType.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnresolvableType.php index a212e12478..db4e68114b 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnresolvableType.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Type/Types/UnresolvableType.php @@ -4,11 +4,15 @@ declare(strict_types=1); 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 @@ -62,7 +66,22 @@ 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, diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/DocParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/DocParser.php index bab41b4616..b0cf76f728 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/DocParser.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/DocParser.php @@ -3,14 +3,21 @@ 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; @@ -41,11 +48,25 @@ final class DocParser return null; } - if (! preg_match("/(?.*)\\$$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 @@ -132,7 +153,7 @@ final class DocParser /** * @param ReflectionClass $reflection - * @return array + * @return array */ public static function classTemplates(ReflectionClass $reflection): array { @@ -147,12 +168,18 @@ final class DocParser preg_match_all("/@(phpstan-|psalm-)?template\s+(?\w+)(\s+of\s+(?.+))?/", $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; @@ -171,6 +198,9 @@ final class DocParser return null; } + /** + * @return non-empty-string + */ private static function findType(string $string): string { $operatorsMatrix = [ @@ -207,7 +237,11 @@ final class DocParser $type .= $char; } - return trim($type); + $type = trim($type); + + assert($type !== ''); + + return $type; } private static function sanitizeDocComment(string|false $doc): ?string @@ -219,7 +253,7 @@ final class DocParser $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); } /** diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/Reflection.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/Reflection.php index 307f7f51ed..600cf5ee58 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/Reflection.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/Reflection.php @@ -4,9 +4,13 @@ declare(strict_types=1); 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; @@ -15,11 +19,15 @@ use ReflectionProperty; 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; @@ -32,16 +40,31 @@ final class Reflection /** @var array */ private static array $functionReflection = []; + /** @var array */ + private static array $classOrInterfaceExists = []; + + /** @var array */ + 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 $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); } /** @@ -60,12 +83,45 @@ final class Reflection return self::$functionReflection[spl_object_hash($closure)] ??= new ReflectionFunction($closure); } - public static function signature(Reflector $reflection): string + /** + * @param ReflectionClass|ReflectionProperty|ReflectionMethod|ReflectionFunction|ReflectionParameter $reflection + * @return array> + */ + 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|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"; } @@ -74,7 +130,7 @@ final class Reflection 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(); @@ -100,7 +156,7 @@ final class Reflection return $signature; } - throw new RuntimeException('Invalid reflection type `' . $reflection::class . '`.'); + return $reflection->name; } public static function flattenType(ReflectionType $type): string diff --git a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/TokenParser.php b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/TokenParser.php index 2e58f74085..e97bd34c97 100644 --- a/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/TokenParser.php +++ b/wcfsetup/install/files/lib/system/api/cuyz/valinor/src/Utility/Reflection/TokenParser.php @@ -38,7 +38,7 @@ final class TokenParser while ($token = $this->next()) { if ($currentNamespace === $namespaceName && $token->is(T_USE)) { - $statements = array_merge($statements, $this->parseUseStatement()); + $statements = [...$statements, ...$this->parseUseStatement()]; continue; } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/README.md b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/README.md index 13ece3829c..38a16e46e7 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/README.md +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/README.md @@ -1,6 +1,8 @@ # 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) > ## 🇷🇺 Русским гражданам > diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/composer.json b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/composer.json index d8082a165b..f15cd1ea28 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/composer.json +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/composer.json @@ -46,9 +46,9 @@ "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", diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/AbstractSerializer.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/AbstractSerializer.php index 3b290f0594..5dc49c1c2a 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/AbstractSerializer.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/AbstractSerializer.php @@ -99,7 +99,7 @@ abstract class AbstractSerializer continue; } - if (! $currentHeader) { + if ($currentHeader === false) { throw Exception\DeserializationException::forInvalidHeader(); } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/CallbackStream.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/CallbackStream.php index 11e10df15a..4e32fc0f7b 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/CallbackStream.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/CallbackStream.php @@ -84,7 +84,7 @@ class CallbackStream implements StreamInterface, Stringable */ public function eof(): bool { - return empty($this->callback); + return $this->callback === null; } /** @@ -149,7 +149,7 @@ class CallbackStream implements StreamInterface, Stringable public function getContents(): string { $callback = $this->detach(); - $contents = $callback ? $callback() : ''; + $contents = $callback !== null ? $callback() : ''; return (string) $contents; } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidForwardedHeaderNameException.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidForwardedHeaderNameException.php index 5a3bc3bad5..318ce8e69b 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidForwardedHeaderNameException.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidForwardedHeaderNameException.php @@ -6,8 +6,7 @@ namespace Laminas\Diactoros\Exception; use Laminas\Diactoros\ServerRequestFilter\FilterUsingXForwardedHeaders; -use function gettype; -use function is_object; +use function get_debug_type; use function is_string; use function sprintf; @@ -16,7 +15,7 @@ class InvalidForwardedHeaderNameException extends RuntimeException implements Ex 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( diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidProxyAddressException.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidProxyAddressException.php index 771c7befcc..f9bec776d4 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidProxyAddressException.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Exception/InvalidProxyAddressException.php @@ -4,15 +4,14 @@ declare(strict_types=1); 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' diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/HeaderSecurity.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/HeaderSecurity.php index d12486aebe..0951eb99e9 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/HeaderSecurity.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/HeaderSecurity.php @@ -4,10 +4,9 @@ declare(strict_types=1); 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; @@ -128,7 +127,7 @@ final class HeaderSecurity 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)) { @@ -151,7 +150,7 @@ final class HeaderSecurity 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)) { diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/RequestTrait.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/RequestTrait.php index 4ce4478b0e..e8519a3682 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/RequestTrait.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/RequestTrait.php @@ -252,7 +252,7 @@ trait RequestTrait } $host = $uri->getHost(); - if ($uri->getPort()) { + if ($uri->getPort() !== null) { $host .= ':' . $uri->getPort(); } @@ -294,7 +294,7 @@ trait RequestTrait 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; } } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/HtmlResponse.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/HtmlResponse.php index 746f3df322..0e0aadb715 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/HtmlResponse.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/HtmlResponse.php @@ -9,8 +9,7 @@ use Laminas\Diactoros\Response; 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; @@ -60,7 +59,7 @@ class HtmlResponse extends Response 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 )); } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/RedirectResponse.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/RedirectResponse.php index 0f42ebc92f..a6d3bae373 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/RedirectResponse.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/RedirectResponse.php @@ -8,8 +8,7 @@ use Laminas\Diactoros\Exception; 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; @@ -36,7 +35,7 @@ class RedirectResponse extends Response 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) )); } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/TextResponse.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/TextResponse.php index 602984911c..6e9ff64203 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/TextResponse.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/TextResponse.php @@ -9,8 +9,7 @@ use Laminas\Diactoros\Response; 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; @@ -60,7 +59,7 @@ class TextResponse extends Response 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 )); } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/XmlResponse.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/XmlResponse.php index 8a4d4237e8..a1efe5632f 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/XmlResponse.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Response/XmlResponse.php @@ -9,8 +9,7 @@ use Laminas\Diactoros\Response; 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; @@ -62,7 +61,7 @@ class XmlResponse extends Response 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 )); } diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Stream.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Stream.php index 2af1bb8bce..2c0955de33 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Stream.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/Stream.php @@ -210,11 +210,11 @@ class Stream implements StreamInterface, Stringable $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; } /** @@ -251,7 +251,7 @@ class Stream implements StreamInterface, Stringable $meta = stream_get_meta_data($this->resource); $mode = $meta['mode']; - return strstr($mode, 'r') || strstr($mode, '+'); + return strstr($mode, 'r') !== false || strstr($mode, '+') !== false; } /** diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UploadedFile.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UploadedFile.php index 354b04280d..ba32a1c650 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UploadedFile.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UploadedFile.php @@ -50,8 +50,7 @@ class UploadedFile implements UploadedFileInterface private bool $moved = false; - /** @var null|StreamInterface */ - private $stream; + private ?StreamInterface $stream = null; /** * @param string|resource|StreamInterface $streamOrFile @@ -72,7 +71,7 @@ class UploadedFile implements UploadedFileInterface $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'); } @@ -150,7 +149,10 @@ class UploadedFile implements UploadedFileInterface $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); diff --git a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UriFactory.php b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UriFactory.php index 31f5a1de86..1c8c3a5044 100644 --- a/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UriFactory.php +++ b/wcfsetup/install/files/lib/system/api/laminas/laminas-diactoros/src/UriFactory.php @@ -60,7 +60,7 @@ class UriFactory implements UriFactoryInterface [$host, $port] = self::marshalHostAndPort($server, $headers); if (! empty($host)) { $uri = $uri->withHost($host); - if (! empty($port)) { + if ($port !== null) { $uri = $uri->withPort($port); } } @@ -115,7 +115,7 @@ class UriFactory implements UriFactoryInterface * Marshal the host and port from the PHP environment. * * @param array> $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 @@ -162,7 +162,7 @@ class UriFactory implements UriFactoryInterface 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) { diff --git a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/README.md b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/README.md index cedddd8633..d2572e91ce 100644 --- a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/README.md +++ b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/README.md @@ -11,7 +11,7 @@ this library aims to offer character encoding functions that do not leak 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 diff --git a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base32.php b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base32.php index 7508b3df6f..48d00b9911 100644 --- a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base32.php +++ b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base32.php @@ -44,8 +44,11 @@ abstract class Base32 implements EncoderInterface * @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); } @@ -56,8 +59,11 @@ abstract class Base32 implements EncoderInterface * @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); } @@ -68,10 +74,13 @@ abstract class Base32 implements EncoderInterface * @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) * @@ -79,8 +88,10 @@ abstract class Base32 implements EncoderInterface * @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); } @@ -91,8 +102,10 @@ abstract class Base32 implements EncoderInterface * @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); } @@ -103,8 +116,10 @@ abstract class Base32 implements EncoderInterface * @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); } @@ -191,8 +206,11 @@ abstract class Base32 implements EncoderInterface * @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 ''; @@ -222,9 +240,9 @@ abstract class Base32 implements EncoderInterface * @return string * * @throws TypeError - * @psalm-suppress RedundantCondition */ protected static function doDecode( + #[\SensitiveParameter] string $src, bool $upper = false, bool $strictPadding = false @@ -434,8 +452,12 @@ abstract class Base32 implements EncoderInterface * @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' diff --git a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base64.php b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base64.php index f5716179f1..2e3ecc8595 100644 --- a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base64.php +++ b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Base64.php @@ -47,8 +47,10 @@ abstract class Base64 implements EncoderInterface * * @throws TypeError */ - public static function encode(string $binString): string - { + public static function encode( + #[\SensitiveParameter] + string $binString + ): string { return static::doEncode($binString, true); } @@ -62,8 +64,10 @@ abstract class Base64 implements EncoderInterface * * @throws TypeError */ - public static function encodeUnpadded(string $src): string - { + public static function encodeUnpadded( + #[\SensitiveParameter] + string $src + ): string { return static::doEncode($src, false); } @@ -74,8 +78,11 @@ abstract class Base64 implements EncoderInterface * * @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): @@ -129,10 +136,12 @@ abstract class Base64 implements EncoderInterface * * @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) { @@ -227,25 +236,21 @@ abstract class Base64 implements EncoderInterface * @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, diff --git a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Binary.php b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Binary.php index 828f3e0f64..5368e4bba7 100644 --- a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Binary.php +++ b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Binary.php @@ -45,8 +45,10 @@ abstract class Binary * @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 */ @@ -70,6 +72,7 @@ abstract class Binary * @throws TypeError */ public static function safeSubstr( + #[\SensitiveParameter] string $str, int $start = 0, $length = null diff --git a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Encoding.php b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Encoding.php index 8649f31fc1..8b7e3878e1 100644 --- a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Encoding.php +++ b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Encoding.php @@ -40,8 +40,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32Encode(string $str): string - { + public static function base32Encode( + #[\SensitiveParameter] + string $str + ): string { return Base32::encode($str); } @@ -52,8 +54,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32EncodeUpper(string $str): string - { + public static function base32EncodeUpper( + #[\SensitiveParameter] + string $str + ): string { return Base32::encodeUpper($str); } @@ -64,8 +68,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32Decode(string $str): string - { + public static function base32Decode( + #[\SensitiveParameter] + string $str + ): string { return Base32::decode($str); } @@ -76,8 +82,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32DecodeUpper(string $str): string - { + public static function base32DecodeUpper( + #[\SensitiveParameter] + string $str + ): string { return Base32::decodeUpper($str); } @@ -88,8 +96,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32HexEncode(string $str): string - { + public static function base32HexEncode( + #[\SensitiveParameter] + string $str + ): string { return Base32Hex::encode($str); } @@ -100,8 +110,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32HexEncodeUpper(string $str): string - { + public static function base32HexEncodeUpper( + #[\SensitiveParameter] + string $str + ): string { return Base32Hex::encodeUpper($str); } @@ -112,8 +124,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32HexDecode(string $str): string - { + public static function base32HexDecode( + #[\SensitiveParameter] + string $str + ): string { return Base32Hex::decode($str); } @@ -124,8 +138,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base32HexDecodeUpper(string $str): string - { + public static function base32HexDecodeUpper( + #[\SensitiveParameter] + string $str + ): string { return Base32Hex::decodeUpper($str); } @@ -136,8 +152,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base64Encode(string $str): string - { + public static function base64Encode( + #[\SensitiveParameter] + string $str + ): string { return Base64::encode($str); } @@ -148,8 +166,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base64Decode(string $str): string - { + public static function base64Decode( + #[\SensitiveParameter] + string $str + ): string { return Base64::decode($str); } @@ -161,8 +181,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base64EncodeDotSlash(string $str): string - { + public static function base64EncodeDotSlash( + #[\SensitiveParameter] + string $str + ): string { return Base64DotSlash::encode($str); } @@ -176,8 +198,10 @@ abstract class Encoding * @throws \RangeException * @throws TypeError */ - public static function base64DecodeDotSlash(string $str): string - { + public static function base64DecodeDotSlash( + #[\SensitiveParameter] + string $str + ): string { return Base64DotSlash::decode($str); } @@ -189,8 +213,10 @@ abstract class Encoding * @return string * @throws TypeError */ - public static function base64EncodeDotSlashOrdered(string $str): string - { + public static function base64EncodeDotSlashOrdered( + #[\SensitiveParameter] + string $str + ): string { return Base64DotSlashOrdered::encode($str); } @@ -204,8 +230,10 @@ abstract class Encoding * @throws \RangeException * @throws TypeError */ - public static function base64DecodeDotSlashOrdered(string $str): string - { + public static function base64DecodeDotSlashOrdered( + #[\SensitiveParameter] + string $str + ): string { return Base64DotSlashOrdered::decode($str); } @@ -217,8 +245,10 @@ abstract class Encoding * @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); } @@ -230,8 +260,10 @@ abstract class Encoding * @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); } @@ -243,8 +275,10 @@ abstract class Encoding * @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); } @@ -255,8 +289,10 @@ abstract class Encoding * @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); } } diff --git a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Hex.php b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Hex.php index a9e058cd36..97c2046f09 100644 --- a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Hex.php +++ b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/Hex.php @@ -42,8 +42,10 @@ abstract class Hex implements EncoderInterface * @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) { @@ -69,8 +71,10 @@ abstract class Hex implements EncoderInterface * @return string * @throws TypeError */ - public static function encodeUpper(string $binString): string - { + public static function encodeUpper( + #[\SensitiveParameter] + string $binString + ): string { $hex = ''; $len = Binary::safeStrlen($binString); @@ -99,6 +103,7 @@ abstract class Hex implements EncoderInterface * @throws RangeException */ public static function decode( + #[\SensitiveParameter] string $encodedString, bool $strictPadding = false ): string { diff --git a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/RFC4648.php b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/RFC4648.php index f124d65bfd..7cd2e9909d 100644 --- a/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/RFC4648.php +++ b/wcfsetup/install/files/lib/system/api/paragonie/constant_time_encoding/src/RFC4648.php @@ -46,8 +46,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base64Encode(string $str): string - { + public static function base64Encode( + #[\SensitiveParameter] + string $str + ): string { return Base64::encode($str); } @@ -61,8 +63,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base64Decode(string $str): string - { + public static function base64Decode( + #[\SensitiveParameter] + string $str + ): string { return Base64::decode($str, true); } @@ -76,8 +80,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base64UrlSafeEncode(string $str): string - { + public static function base64UrlSafeEncode( + #[\SensitiveParameter] + string $str + ): string { return Base64UrlSafe::encode($str); } @@ -91,8 +97,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base64UrlSafeDecode(string $str): string - { + public static function base64UrlSafeDecode( + #[\SensitiveParameter] + string $str + ): string { return Base64UrlSafe::decode($str, true); } @@ -106,8 +114,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base32Encode(string $str): string - { + public static function base32Encode( + #[\SensitiveParameter] + string $str + ): string { return Base32::encodeUpper($str); } @@ -121,8 +131,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base32Decode(string $str): string - { + public static function base32Decode( + #[\SensitiveParameter] + string $str + ): string { return Base32::decodeUpper($str, true); } @@ -136,8 +148,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base32HexEncode(string $str): string - { + public static function base32HexEncode( + #[\SensitiveParameter] + string $str + ): string { return Base32::encodeUpper($str); } @@ -151,8 +165,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base32HexDecode(string $str): string - { + public static function base32HexDecode( + #[\SensitiveParameter] + string $str + ): string { return Base32::decodeUpper($str, true); } @@ -166,8 +182,10 @@ abstract class RFC4648 * * @throws TypeError */ - public static function base16Encode(string $str): string - { + public static function base16Encode( + #[\SensitiveParameter] + string $str + ): string { return Hex::encodeUpper($str); } @@ -179,8 +197,10 @@ abstract class RFC4648 * @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 +} diff --git a/wcfsetup/install/files/lib/system/api/psr/http-factory/composer.json b/wcfsetup/install/files/lib/system/api/psr/http-factory/composer.json index d1bbddeea3..82a1d3266b 100644 --- a/wcfsetup/install/files/lib/system/api/psr/http-factory/composer.json +++ b/wcfsetup/install/files/lib/system/api/psr/http-factory/composer.json @@ -1,6 +1,6 @@ { "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", @@ -18,8 +18,11 @@ "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": { diff --git a/wcfsetup/install/files/lib/system/api/psr/http-factory/src/UploadedFileFactoryInterface.php b/wcfsetup/install/files/lib/system/api/psr/http-factory/src/UploadedFileFactoryInterface.php index 7db4e30af7..d7adbf0e26 100644 --- a/wcfsetup/install/files/lib/system/api/psr/http-factory/src/UploadedFileFactoryInterface.php +++ b/wcfsetup/install/files/lib/system/api/psr/http-factory/src/UploadedFileFactoryInterface.php @@ -15,10 +15,10 @@ interface UploadedFileFactoryInterface * * @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 * @@ -26,9 +26,9 @@ interface UploadedFileFactoryInterface */ 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; } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/CHANGELOG.md b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/CHANGELOG.md index a23fd0e683..302cf1dd16 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/CHANGELOG.md +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/CHANGELOG.md @@ -1,4 +1,41 @@ -# 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 diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/README.md b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/README.md index 66fb1c6558..90428cbf8a 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/README.md +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/README.md @@ -33,7 +33,7 @@ The resulting CSS document structure can be manipulated prior to being output. #### 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() @@ -43,7 +43,7 @@ $parser = new \Sabberworm\CSS\Parser($css, $settings); #### 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( @@ -52,6 +52,8 @@ $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. @@ -67,12 +69,9 @@ The resulting data structure consists mainly of five basic types: `CSSList`, `Ru #### 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. @@ -80,16 +79,16 @@ To append a new item (selector, media query, etc.) to an existing `CSSList`, con `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 @@ -98,19 +97,21 @@ If you want to manipulate a `RuleSet`, use the methods `addRule(Rule $rule)`, `g * `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 @@ -156,7 +157,7 @@ $cssDocument = $parser->parse(); 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'); } @@ -214,7 +215,8 @@ html, body { ``` -#### Structure (`var_dump()`) +
+ Structure (var_dump()) ```php class Sabberworm\CSS\CSSList\Document#4 (2) { @@ -435,6 +437,7 @@ class Sabberworm\CSS\CSSList\Document#4 (2) { } ``` +
#### Output (`render()`) @@ -458,7 +461,8 @@ html, body {font-size: 1.6em;} ``` -#### Structure (`var_dump()`) +
+ Structure (var_dump()) ```php class Sabberworm\CSS\CSSList\Document#4 (2) { @@ -603,6 +607,7 @@ class Sabberworm\CSS\CSSList\Document#4 (2) { } ``` +
#### Output (`render()`) diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/composer.json b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/composer.json index e192dd56a1..cf15a9442e 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/composer.json +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/composer.json @@ -12,6 +12,14 @@ "authors": [ { "name": "Raphael Schweikert" + }, + { + "name": "Oliver Klee", + "email": "github@oliverklee.de" + }, + { + "name": "Jake Hotson", + "email": "jake.github@qzdesign.co.uk" } ], "require": { @@ -19,8 +27,7 @@ "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" @@ -35,6 +42,11 @@ "Sabberworm\\CSS\\Tests\\": "tests/" } }, + "extra": { + "branch-alias": { + "dev-main": "9.0.x-dev" + } + }, "scripts": { "ci": [ "@ci:static" diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php index 218adb9a19..598fefc12a 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/AtRuleBlockList.php @@ -61,13 +61,14 @@ class AtRuleBlockList extends CSSBlockList implements AtRule */ 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; diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/CSSList.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/CSSList.php index 946740a499..603f662b17 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/CSSList.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/CSSList.php @@ -24,10 +24,10 @@ use Sabberworm\CSS\Value\URL; 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 { @@ -69,8 +69,9 @@ 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 { @@ -86,11 +87,12 @@ abstract class CSSList implements Renderable, Commentable 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()); } @@ -125,22 +127,19 @@ abstract class CSSList implements Renderable, Commentable $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); @@ -172,10 +171,10 @@ abstract class CSSList implements Renderable, Commentable $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); @@ -272,7 +271,7 @@ abstract class CSSList implements Renderable, Commentable } /** - * Appends an item to tje list of contents. + * Appends an item to the list of contents. * * @param RuleSet|CSSList|Import|Charset $oItem * @@ -402,7 +401,7 @@ abstract class CSSList implements Renderable, Commentable /** * @return string */ - public function render(OutputFormat $oOutputFormat) + protected function renderListContents(OutputFormat $oOutputFormat) { $sResult = ''; $bIsFirst = true; @@ -442,6 +441,8 @@ abstract class CSSList implements Renderable, Commentable abstract public function isRootList(); /** + * Returns the stored items. + * * @return array */ public function getContents() diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/Document.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/Document.php index 91ab2c6b2e..bad9983186 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/Document.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/Document.php @@ -11,8 +11,8 @@ use Sabberworm\CSS\RuleSet\RuleSet; 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 { @@ -37,7 +37,8 @@ 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 */ @@ -62,7 +63,7 @@ class Document extends CSSBlockList } /** - * 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 */ @@ -75,7 +76,7 @@ class Document extends CSSBlockList } /** - * 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). @@ -102,7 +103,7 @@ class Document extends CSSBlockList } /** - * 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). @@ -159,7 +160,7 @@ class Document extends CSSBlockList if ($oOutputFormat === null) { $oOutputFormat = new OutputFormat(); } - return parent::render($oOutputFormat); + return $oOutputFormat->comments($this) . $this->renderListContents($oOutputFormat); } /** diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/KeyFrame.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/KeyFrame.php index d9420e9c0d..caef7b3d11 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/KeyFrame.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/CSSList/KeyFrame.php @@ -72,8 +72,9 @@ class KeyFrame extends CSSList implements AtRule */ 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; } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormat.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormat.php index 595d306431..96f26e147e 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormat.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormat.php @@ -143,6 +143,13 @@ class OutputFormat */ public $bIgnoreExceptions = false; + /** + * Render comments for lists and RuleSets + * + * @var bool + */ + public $bRenderComments = false; + /** * @var OutputFormatter|null */ @@ -314,8 +321,12 @@ class OutputFormat 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; } @@ -327,8 +338,11 @@ class OutputFormat 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; } } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormatter.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormatter.php index 535feca7ff..501d15da30 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormatter.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/OutputFormatter.php @@ -2,6 +2,7 @@ namespace Sabberworm\CSS; +use Sabberworm\CSS\Comment\Commentable; use Sabberworm\CSS\Parsing\OutputException; class OutputFormatter @@ -211,6 +212,29 @@ class OutputFormatter return implode(';', $sString); } + /** + * + * @param array $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 * diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parser.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parser.php index f3b0493a5f..e582cfab3b 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parser.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parser.php @@ -17,7 +17,7 @@ class Parser 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) */ @@ -30,6 +30,8 @@ class Parser } /** + * Sets the charset to be used if the CSS does not contain an `@charset` declaration. + * * @param string $sCharset * * @return void @@ -40,6 +42,8 @@ class Parser } /** + * Returns the charset that is used if the CSS does not contain an `@charset` declaration. + * * @return void */ public function getCharset() @@ -49,6 +53,8 @@ class Parser } /** + * Parses the CSS provided to the constructor and creates a `Document` from it. + * * @return Document * * @throws SourceException diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parsing/Anchor.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parsing/Anchor.php new file mode 100644 index 0000000000..93789e26b0 --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parsing/Anchor.php @@ -0,0 +1,34 @@ +iPosition = $iPosition; + $this->oParserState = $oParserState; + } + + /** + * @return void + */ + public function backtrack() + { + $this->oParserState->setPosition($this->iPosition); + } +} diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parsing/ParserState.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parsing/ParserState.php index e7d85ee0f1..7a99f327be 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parsing/ParserState.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Parsing/ParserState.php @@ -33,6 +33,8 @@ class ParserState private $iCurrentPosition; /** + * will only be used if the CSS does not contain an `@charset` declaration + * * @var string */ private $sCharset; @@ -48,7 +50,7 @@ class ParserState 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) @@ -61,6 +63,8 @@ class ParserState } /** + * Sets the charset to be used if the CSS does not contain an `@charset` declaration. + * * @param string $sCharset * * @return void @@ -75,6 +79,8 @@ class ParserState } /** + * Returns the charset that is used if the CSS does not contain an `@charset` declaration. + * * @return string */ public function getCharset() @@ -106,6 +112,24 @@ class ParserState 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 * @@ -115,12 +139,15 @@ class ParserState */ 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 { @@ -204,7 +231,7 @@ class ParserState */ public function consumeWhiteSpace() { - $comments = []; + $aComments = []; do { while (preg_match('/\\s/isSu', $this->peek()) === 1) { $this->consume(1); @@ -214,16 +241,16 @@ class ParserState $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; } /** diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Charset.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Charset.php index 3ee0c3d042..26e1b250a6 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Charset.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Charset.php @@ -4,6 +4,7 @@ namespace Sabberworm\CSS\Property; use Sabberworm\CSS\Comment\Comment; use Sabberworm\CSS\OutputFormat; +use Sabberworm\CSS\Value\CSSString; /** * Class representing an `@charset` rule. @@ -16,9 +17,9 @@ use Sabberworm\CSS\OutputFormat; class Charset implements AtRule { /** - * @var string + * @var CSSString */ - private $sCharset; + private $oCharset; /** * @var int @@ -31,12 +32,12 @@ class Charset implements AtRule 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 = []; } @@ -50,13 +51,14 @@ class Charset implements AtRule } /** - * @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; } /** @@ -64,7 +66,7 @@ class Charset implements AtRule */ public function getCharset() { - return $this->sCharset; + return $this->oCharset->getString(); } /** @@ -80,7 +82,7 @@ class Charset implements AtRule */ public function render(OutputFormat $oOutputFormat) { - return "@charset {$this->sCharset->render($oOutputFormat)};"; + return "{$oOutputFormat->comments($this)}@charset {$this->oCharset->render($oOutputFormat)};"; } /** @@ -96,7 +98,7 @@ class Charset implements AtRule */ public function atRuleArgs() { - return $this->sCharset; + return $this->oCharset; } /** diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Import.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Import.php index a2253016b0..d715a7a041 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Import.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Property/Import.php @@ -83,7 +83,7 @@ class Import implements AtRule */ 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) . ';'; } @@ -134,4 +134,12 @@ class Import implements AtRule { $this->aComments = $aComments; } + + /** + * @return string + */ + public function getMediaQuery() + { + return $this->sMediaQuery; + } } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Rule/Rule.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Rule/Rule.php index c1ea6df74e..fc00c8808b 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Rule/Rule.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Rule/Rule.php @@ -13,8 +13,9 @@ use Sabberworm\CSS\Value\RuleValueList; 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 { @@ -24,7 +25,7 @@ class Rule implements Renderable, Commentable private $sRule; /** - * @var RuleValueList|null + * @var RuleValueList|string|null */ private $mValue; @@ -171,7 +172,7 @@ class Rule implements Renderable, Commentable } /** - * @return RuleValueList|null + * @return RuleValueList|string|null */ public function getValue() { @@ -179,7 +180,7 @@ class Rule implements Renderable, Commentable } /** - * @param RuleValueList|null $mValue + * @param RuleValueList|string|null $mValue * * @return void */ @@ -346,8 +347,8 @@ class Rule implements Renderable, Commentable */ 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; diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php index 88bc5bd313..aab6d799d3 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/AtRuleSet.php @@ -6,7 +6,10 @@ use Sabberworm\CSS\OutputFormat; 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 { @@ -61,12 +64,13 @@ 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; } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php index c27cdd4c81..b218bd8c41 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/DeclarationBlock.php @@ -19,7 +19,10 @@ use Sabberworm\CSS\Value\URL; 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`). */ @@ -562,6 +565,7 @@ class DeclarationBlock extends RuleSet public function createShorthandProperties(array $aProperties, $sShorthand) { $aRules = $this->getRulesAssoc(); + $oRule = null; $aNewValues = []; foreach ($aProperties as $sProperty) { if (!isset($aRules[$sProperty])) { @@ -582,7 +586,7 @@ class DeclarationBlock extends RuleSet $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); @@ -812,18 +816,19 @@ class DeclarationBlock extends RuleSet */ 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; diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/RuleSet.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/RuleSet.php index 9404bb0bdb..adb9be9225 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/RuleSet.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/RuleSet/RuleSet.php @@ -12,8 +12,13 @@ use Sabberworm\CSS\Renderable; 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 { @@ -266,23 +271,24 @@ 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; } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Settings.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Settings.php index 7b8580962c..79d99803cc 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Settings.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Settings.php @@ -11,7 +11,8 @@ class Settings { /** * 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 @@ -19,15 +20,14 @@ class Settings 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 */ @@ -47,6 +47,11 @@ class Settings } /** + * 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 @@ -58,6 +63,8 @@ class Settings } /** + * Sets the charset to be used if the CSS does not contain an `@charset` declaration. + * * @param string $sDefaultCharset * * @return self fluent interface @@ -69,6 +76,8 @@ class Settings } /** + * Configures whether the parser should silently ignore invalid rules. + * * @param bool $bLenientParsing * * @return self fluent interface @@ -80,6 +89,8 @@ class Settings } /** + * Configures the parser to choke on invalid rules. + * * @return self fluent interface */ public function beStrict() diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSFunction.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSFunction.php index e6b8c1189f..300dc3ec0a 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSFunction.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSFunction.php @@ -3,7 +3,12 @@ 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 { /** @@ -28,6 +33,26 @@ 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 */ diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSString.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSString.php index 9fafedd7a4..da498d4112 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSString.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CSSString.php @@ -8,6 +8,11 @@ use Sabberworm\CSS\Parsing\SourceException; 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 { /** diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CalcFunction.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CalcFunction.php index 5c92e0c089..5ffd071f97 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CalcFunction.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/CalcFunction.php @@ -19,20 +19,35 @@ class CalcFunction extends CSSFunction 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++; @@ -83,7 +98,9 @@ class CalcFunction extends CSSFunction $oParserState->consumeWhiteSpace(); } $oList->addListComponent($oCalcList); - $oParserState->consume(')'); + if (!$oParserState->isEnd()) { + $oParserState->consume(')'); + } return new CalcFunction($sFunction, $oList, ',', $oParserState->currentLine()); } } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Color.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Color.php index 8dc52960cd..1cf00ccec9 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Color.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Color.php @@ -7,6 +7,10 @@ use Sabberworm\CSS\Parsing\ParserState; 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 { /** @@ -19,12 +23,15 @@ 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('#')) { diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/RuleValueList.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/RuleValueList.php index 5d533a7cbe..8fe88b6106 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/RuleValueList.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/RuleValueList.php @@ -2,6 +2,11 @@ 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 { /** diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Size.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Size.php index b3801dc17d..36a3238141 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Size.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Size.php @@ -7,6 +7,9 @@ use Sabberworm\CSS\Parsing\ParserState; 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 { /** @@ -24,7 +27,7 @@ class Size extends PrimitiveValue /** * @var array */ - 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>|null @@ -74,9 +77,16 @@ class Size extends PrimitiveValue 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); } diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/URL.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/URL.php index 1467d505c5..cdb911c38c 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/URL.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/URL.php @@ -8,6 +8,9 @@ use Sabberworm\CSS\Parsing\SourceException; 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 { /** @@ -33,11 +36,21 @@ 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()); diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Value.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Value.php index 66cb9fd47e..ce6d5790a7 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Value.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/Value.php @@ -8,6 +8,10 @@ use Sabberworm\CSS\Parsing\UnexpectedEOFException; 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 { /** @@ -39,8 +43,9 @@ 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; @@ -101,16 +106,25 @@ abstract class Value implements Renderable */ 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; } /** @@ -133,13 +147,6 @@ abstract class Value implements Renderable $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) { diff --git a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/ValueList.php b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/ValueList.php index af5348b967..a93acc7b61 100644 --- a/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/ValueList.php +++ b/wcfsetup/install/files/lib/system/api/sabberworm/php-css-parser/src/Value/ValueList.php @@ -4,6 +4,12 @@ namespace Sabberworm\CSS\Value; 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 { /** diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/ChangeLog.md b/wcfsetup/install/files/lib/system/api/sebastian/diff/ChangeLog.md index 8081b37ffc..10c5452908 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/ChangeLog.md +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/ChangeLog.md @@ -2,6 +2,12 @@ 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 @@ -124,6 +130,7 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt * 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 diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/LICENSE b/wcfsetup/install/files/lib/system/api/sebastian/diff/LICENSE index a453252d52..5b4705a48d 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/LICENSE +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/LICENSE @@ -1,6 +1,6 @@ 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 diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/composer.json b/wcfsetup/install/files/lib/system/api/sebastian/diff/composer.json index bc6c3a435e..c6ebec9c28 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/composer.json +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/composer.json @@ -31,7 +31,7 @@ }, "require-dev": { "phpunit/phpunit": "^10.0", - "symfony/process": "^4.2 || ^5" + "symfony/process": "^6.4" }, "autoload": { "classmap": [ diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Differ.php b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Differ.php index 19ccf97c33..801fe02aaf 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Differ.php +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Differ.php @@ -42,14 +42,14 @@ final class Differ $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); diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Exception/ConfigurationException.php b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Exception/ConfigurationException.php index 85f066c260..b2abf0cbf3 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Exception/ConfigurationException.php +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Exception/ConfigurationException.php @@ -21,17 +21,17 @@ final class ConfigurationException extends InvalidArgumentException 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 ? '' : gettype($value) . '#' . $value) + is_object($value) ? $value::class : (null === $value ? '' : gettype($value) . '#' . $value), ), $code, - $previous + $previous, ); } } diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php index a46de07d6d..b9846c37e7 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php @@ -61,7 +61,7 @@ final class MemoryEfficientLongestCommonSubsequenceCalculator implements Longest return array_merge( $this->calculate($fromStart, $toStart), - $this->calculate($fromEnd, $toEnd) + $this->calculate($fromEnd, $toEnd), ); } @@ -78,7 +78,11 @@ final class MemoryEfficientLongestCommonSubsequenceCalculator implements Longest 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 { diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php index 3eb7428d7d..a2a73b679e 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/StrictUnifiedDiffOutputBuilder.php @@ -82,7 +82,7 @@ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface $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']; @@ -201,11 +201,11 @@ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface $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; @@ -251,7 +251,7 @@ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; - $toRange -= $sameCount; + $toRange -= $sameCount; $this->writeHunk( $diff, @@ -261,7 +261,7 @@ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, - $output + $output, ); } @@ -302,11 +302,11 @@ final class StrictUnifiedDiffOutputBuilder implements DiffOutputBuilderInterface $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 - //} + // } } } diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php index 1483b3aff6..683ab1b6d6 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Output/UnifiedDiffOutputBuilder.php @@ -18,7 +18,6 @@ use function max; use function min; use function str_ends_with; use function stream_get_contents; -use function strlen; use function substr; use SebastianBergmann\Diff\Differ; @@ -67,7 +66,7 @@ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder // 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; } @@ -151,11 +150,11 @@ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder $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; @@ -199,7 +198,7 @@ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder $contextEndOffset = min($sameCount, $this->contextLines); $fromRange -= $sameCount; - $toRange -= $sameCount; + $toRange -= $sameCount; $this->writeHunk( $diff, @@ -209,7 +208,7 @@ final class UnifiedDiffOutputBuilder extends AbstractChunkOutputBuilder $fromRange + $contextStartOffset + $contextEndOffset, $toStart - $contextStartOffset, $toRange + $contextStartOffset + $contextEndOffset, - $output + $output, ); } diff --git a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Parser.php b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Parser.php index 6232c95761..9293fc9194 100644 --- a/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Parser.php +++ b/wcfsetup/install/files/lib/system/api/sebastian/diff/src/Parser.php @@ -83,7 +83,7 @@ final class Parser (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; diff --git a/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/ElementNode.php b/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/ElementNode.php index 39f4d9cc07..76d2078ea3 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/ElementNode.php +++ b/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/ElementNode.php @@ -26,7 +26,7 @@ class ElementNode extends AbstractNode 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; diff --git a/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/SelectorNode.php b/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/SelectorNode.php index 0b09ab5dc7..2318b2bf26 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/SelectorNode.php +++ b/wcfsetup/install/files/lib/system/api/symfony/css-selector/Node/SelectorNode.php @@ -26,7 +26,7 @@ class SelectorNode extends AbstractNode 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; diff --git a/wcfsetup/install/files/lib/system/api/symfony/css-selector/Parser/Parser.php b/wcfsetup/install/files/lib/system/api/symfony/css-selector/Parser/Parser.php index 5313d3435b..309c9b5215 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/css-selector/Parser/Parser.php +++ b/wcfsetup/install/files/lib/system/api/symfony/css-selector/Parser/Parser.php @@ -29,7 +29,7 @@ class Parser implements ParserInterface { private Tokenizer $tokenizer; - public function __construct(Tokenizer $tokenizer = null) + public function __construct(?Tokenizer $tokenizer = null) { $this->tokenizer = $tokenizer ?? new Tokenizer(); } diff --git a/wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Translator.php b/wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Translator.php index 83e855b5c4..9e66ce7ddb 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Translator.php +++ b/wcfsetup/install/files/lib/system/api/symfony/css-selector/XPath/Translator.php @@ -48,7 +48,7 @@ class Translator implements TranslatorInterface private array $pseudoClassTranslators = []; private array $attributeMatchingTranslators = []; - public function __construct(ParserInterface $parser = null) + public function __construct(?ParserInterface $parser = null) { $this->mainParser = $parser ?? new Parser(); diff --git a/wcfsetup/install/files/lib/system/api/symfony/deprecation-contracts/composer.json b/wcfsetup/install/files/lib/system/api/symfony/deprecation-contracts/composer.json index c6d02d8749..ceb6c07961 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/deprecation-contracts/composer.json +++ b/wcfsetup/install/files/lib/system/api/symfony/deprecation-contracts/composer.json @@ -25,7 +25,7 @@ "minimum-stability": "dev", "extra": { "branch-alias": { - "dev-main": "3.4-dev" + "dev-main": "3.5-dev" }, "thanks": { "name": "symfony/contracts", diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/Php82.php b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/Php82.php index fcd1281a97..91da117f95 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/Php82.php +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/Php82.php @@ -174,6 +174,42 @@ class Php82 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: @@ -197,16 +233,6 @@ class Php82 $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; } diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/bootstrap.php b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/bootstrap.php index f875f3947c..399504dcbb 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/bootstrap.php +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/bootstrap.php @@ -15,20 +15,18 @@ if (\PHP_VERSION_ID >= 80200) { 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')) { diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/composer.json b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/composer.json index e0422658ac..e1f8230c8e 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/composer.json +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php82/composer.json @@ -25,9 +25,6 @@ }, "minimum-stability": "dev", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill" diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/Php83.php b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/Php83.php index 23a9cf3890..3d94b6c324 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/Php83.php +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/Php83.php @@ -13,6 +13,7 @@ namespace Symfony\Polyfill\Php83; /** * @author Ion Bazan + * @author Pierre Ambroise * * @internal */ @@ -39,7 +40,7 @@ final class Php83 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'); @@ -82,4 +83,115 @@ final class Php83 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; + } } diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/README.md b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/README.md index 92516e7a45..f298776814 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/README.md +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/README.md @@ -9,7 +9,9 @@ This component provides features added to PHP 8.3 core: - [`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). diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php new file mode 100644 index 0000000000..ecb7c98e0e --- /dev/null +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/Resources/stubs/SQLite3Exception.php @@ -0,0 +1,16 @@ + + * + * 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 + { + } +} diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap.php b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap.php index b5beb71a60..f43af17e0e 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap.php +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap.php @@ -27,12 +27,20 @@ if (!function_exists('stream_context_set_options')) { 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')) { diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap81.php b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap81.php index 63a4f78008..68395b439d 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap81.php +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/bootstrap81.php @@ -14,7 +14,7 @@ if (\PHP_VERSION_ID >= 80300) { } 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')) { diff --git a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/composer.json b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/composer.json index 51c81b16a2..02a0bf8301 100644 --- a/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/composer.json +++ b/wcfsetup/install/files/lib/system/api/symfony/polyfill-php83/composer.json @@ -16,8 +16,7 @@ } ], "require": { - "php": ">=7.1", - "symfony/polyfill-php80": "^1.14" + "php": ">=7.1" }, "autoload": { "psr-4": { "Symfony\\Polyfill\\Php83\\": "" }, @@ -26,9 +25,6 @@ }, "minimum-stability": "dev", "extra": { - "branch-alias": { - "dev-main": "1.28-dev" - }, "thanks": { "name": "symfony/polyfill", "url": "https://github.com/symfony/polyfill"