diff --git a/composer.json b/composer.json index d34abf3b..b18cd23f 100644 --- a/composer.json +++ b/composer.json @@ -16,6 +16,7 @@ } }, "require": { - "php": ">=8.1" + "php": ">=8.1", + "psr/log": "^2.0 || ^3.0" } } diff --git a/composer.lock b/composer.lock index 7c17f09f..5d70e659 100644 --- a/composer.lock +++ b/composer.lock @@ -4,8 +4,59 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "c633a27ea30371ec870c8065ca4ae4cd", - "packages": [], + "content-hash": "9a62d2bdd387b7a6f599b27964325845", + "packages": [ + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" + } + ], "packages-dev": [ { "name": "amphp/amp", @@ -680,16 +731,16 @@ }, { "name": "netresearch/jsonmapper", - "version": "v4.1.0", + "version": "v4.2.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956", + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956", "shasum": "" }, "require": { @@ -725,22 +776,22 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0" }, - "time": "2022-12-08T20:46:14+00:00" + "time": "2023-04-09T17:37:40+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.4", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -781,9 +832,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-03-05T19:49:14+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "phan/phan", @@ -1034,22 +1085,22 @@ }, { "name": "phpstan/extension-installer", - "version": "1.2.0", + "version": "1.3.1", "source": { "type": "git", "url": "https://github.com/phpstan/extension-installer.git", - "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40" + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", - "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a", "shasum": "" }, "require": { "composer-plugin-api": "^2.0", "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.8.0" + "phpstan/phpstan": "^1.9.0" }, "require-dev": { "composer/composer": "^2.0", @@ -1072,28 +1123,29 @@ "description": "Composer plugin for automatic installation of PHPStan extensions", "support": { "issues": "https://github.com/phpstan/extension-installer/issues", - "source": "https://github.com/phpstan/extension-installer/tree/1.2.0" + "source": "https://github.com/phpstan/extension-installer/tree/1.3.1" }, - "time": "2022-10-17T12:59:16+00:00" + "time": "2023-05-24T08:59:17+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.16.1", + "version": "1.21.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" + "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c", + "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", @@ -1117,22 +1169,22 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.0" }, - "time": "2023-02-07T18:11:17+00:00" + "time": "2023-05-17T13:13:44+00:00" }, { "name": "phpstan/phpstan", - "version": "1.10.8", + "version": "1.10.15", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9" + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0166aef76e066f0dd2adc2799bdadfa1635711e9", - "reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd", + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd", "shasum": "" }, "require": { @@ -1181,7 +1233,7 @@ "type": "tidelift" } ], - "time": "2023-03-24T10:28:16+00:00" + "time": "2023-05-09T15:28:01+00:00" }, { "name": "phpstan/phpstan-deprecation-rules", @@ -1284,56 +1336,6 @@ }, "time": "2021-11-05T16:47:00+00:00" }, - { - "name": "psr/log", - "version": "3.0.0", - "source": { - "type": "git", - "url": "https://github.com/php-fig/log.git", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" - }, - "dist": { - "type": "zip", - "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", - "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", - "shasum": "" - }, - "require": { - "php": ">=8.0.0" - }, - "type": "library", - "extra": { - "branch-alias": { - "dev-master": "3.x-dev" - } - }, - "autoload": { - "psr-4": { - "Psr\\Log\\": "src" - } - }, - "notification-url": "https://packagist.org/downloads/", - "license": [ - "MIT" - ], - "authors": [ - { - "name": "PHP-FIG", - "homepage": "https://www.php-fig.org/" - } - ], - "description": "Common interface for logging libraries", - "homepage": "https://github.com/php-fig/log", - "keywords": [ - "log", - "psr", - "psr-3" - ], - "support": { - "source": "https://github.com/php-fig/log/tree/3.0.0" - }, - "time": "2021-07-14T16:46:02+00:00" - }, { "name": "sabre/event", "version": "5.1.4", @@ -1402,16 +1404,16 @@ }, { "name": "sebastian/diff", - "version": "5.0.1", + "version": "5.0.3", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02" + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/aae9a0a43bff37bd5d8d0311426c87bf36153f02", - "reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", "shasum": "" }, "require": { @@ -1457,7 +1459,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.0.1" + "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" }, "funding": [ { @@ -1465,20 +1467,20 @@ "type": "github" } ], - "time": "2023-03-23T05:12:41+00:00" + "time": "2023-05-01T07:48:21+00:00" }, { "name": "spatie/array-to-xml", - "version": "3.1.5", + "version": "3.1.6", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43" + "reference": "e210b98957987c755372465be105d32113f339a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/13f76acef5362d15c71ae1ac6350cc3df5e25e43", - "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4", + "reference": "e210b98957987c755372465be105d32113f339a4", "shasum": "" }, "require": { @@ -1516,7 +1518,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.1.5" + "source": "https://github.com/spatie/array-to-xml/tree/3.1.6" }, "funding": [ { @@ -1528,20 +1530,20 @@ "type": "github" } ], - "time": "2022-12-24T13:43:51+00:00" + "time": "2023-05-11T14:04:07+00:00" }, { "name": "symfony/console", - "version": "v6.2.7", + "version": "v6.2.11", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45" + "reference": "5aa03db8ef0a5457c316ec580e69562d97734c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cbad09eb8925b6ad4fb721c7a179344dc4a19d45", - "reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45", + "url": "https://api.github.com/repos/symfony/console/zipball/5aa03db8ef0a5457c316ec580e69562d97734c77", + "reference": "5aa03db8ef0a5457c316ec580e69562d97734c77", "shasum": "" }, "require": { @@ -1603,12 +1605,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.7" + "source": "https://github.com/symfony/console/tree/v6.2.11" }, "funding": [ { @@ -1624,7 +1626,7 @@ "type": "tidelift" } ], - "time": "2023-02-25T17:00:03+00:00" + "time": "2023-05-26T08:16:21+00:00" }, { "name": "symfony/deprecation-contracts", @@ -1695,16 +1697,16 @@ }, { "name": "symfony/filesystem", - "version": "v6.2.7", + "version": "v6.2.10", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894", + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894", "shasum": "" }, "require": { @@ -1738,7 +1740,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.7" + "source": "https://github.com/symfony/filesystem/tree/v6.2.10" }, "funding": [ { @@ -1754,7 +1756,7 @@ "type": "tidelift" } ], - "time": "2023-02-14T08:44:56+00:00" + "time": "2023-04-18T13:46:08+00:00" }, { "name": "symfony/polyfill-ctype", @@ -2256,16 +2258,16 @@ }, { "name": "symfony/string", - "version": "v6.2.7", + "version": "v6.2.8", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "67b8c1eec78296b85dc1c7d9743830160218993d" + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d", - "reference": "67b8c1eec78296b85dc1c7d9743830160218993d", + "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", "shasum": "" }, "require": { @@ -2322,7 +2324,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.7" + "source": "https://github.com/symfony/string/tree/v6.2.8" }, "funding": [ { @@ -2338,7 +2340,7 @@ "type": "tidelift" } ], - "time": "2023-02-24T10:42:00+00:00" + "time": "2023-03-20T16:06:02+00:00" }, { "name": "tysonandre/var_representation_polyfill", @@ -2404,16 +2406,16 @@ }, { "name": "vimeo/psalm", - "version": "5.8.0", + "version": "5.12.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda" + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/9cf4f60a333f779ad3bc704a555920e81d4fdcda", - "reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176", + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176", "shasum": "" }, "require": { @@ -2445,6 +2447,7 @@ "psalm/psalm": "self.version" }, "require-dev": { + "amphp/phpunit-util": "^2.0", "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^6.9", "ext-curl": "*", @@ -2503,9 +2506,9 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.8.0" + "source": "https://github.com/vimeo/psalm/tree/5.12.0" }, - "time": "2023-03-09T04:14:35+00:00" + "time": "2023-05-22T21:19:03+00:00" }, { "name": "webmozart/assert", diff --git a/vendor/composer/installed.json b/vendor/composer/installed.json index 2fe33c4b..b453d2e1 100644 --- a/vendor/composer/installed.json +++ b/vendor/composer/installed.json @@ -706,17 +706,17 @@ }, { "name": "netresearch/jsonmapper", - "version": "v4.1.0", - "version_normalized": "4.1.0.0", + "version": "v4.2.0", + "version_normalized": "4.2.0.0", "source": { "type": "git", "url": "https://github.com/cweiske/jsonmapper.git", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f" + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", - "reference": "cfa81ea1d35294d64adb9c68aa4cb9e92400e53f", + "url": "https://api.github.com/repos/cweiske/jsonmapper/zipball/f60565f8c0566a31acf06884cdaa591867ecc956", + "reference": "f60565f8c0566a31acf06884cdaa591867ecc956", "shasum": "" }, "require": { @@ -730,7 +730,7 @@ "phpunit/phpunit": "~7.5 || ~8.0 || ~9.0", "squizlabs/php_codesniffer": "~3.5" }, - "time": "2022-12-08T20:46:14+00:00", + "time": "2023-04-09T17:37:40+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -754,23 +754,23 @@ "support": { "email": "cweiske@cweiske.de", "issues": "https://github.com/cweiske/jsonmapper/issues", - "source": "https://github.com/cweiske/jsonmapper/tree/v4.1.0" + "source": "https://github.com/cweiske/jsonmapper/tree/v4.2.0" }, "install-path": "../netresearch/jsonmapper" }, { "name": "nikic/php-parser", - "version": "v4.15.4", - "version_normalized": "4.15.4.0", + "version": "v4.15.5", + "version_normalized": "4.15.5.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/6bb5176bc4af8bcb7d926f88718db9b96a2d4290", - "reference": "6bb5176bc4af8bcb7d926f88718db9b96a2d4290", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -781,7 +781,7 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, - "time": "2023-03-05T19:49:14+00:00", + "time": "2023-05-19T20:20:00+00:00", "bin": [ "bin/php-parse" ], @@ -813,7 +813,7 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.4" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, "install-path": "../nikic/php-parser" }, @@ -1078,30 +1078,30 @@ }, { "name": "phpstan/extension-installer", - "version": "1.2.0", - "version_normalized": "1.2.0.0", + "version": "1.3.1", + "version_normalized": "1.3.1.0", "source": { "type": "git", "url": "https://github.com/phpstan/extension-installer.git", - "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40" + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", - "reference": "f06dbb052ddc394e7896fcd1cfcd533f9f6ace40", + "url": "https://api.github.com/repos/phpstan/extension-installer/zipball/f45734bfb9984c6c56c4486b71230355f066a58a", + "reference": "f45734bfb9984c6c56c4486b71230355f066a58a", "shasum": "" }, "require": { "composer-plugin-api": "^2.0", "php": "^7.2 || ^8.0", - "phpstan/phpstan": "^1.8.0" + "phpstan/phpstan": "^1.9.0" }, "require-dev": { "composer/composer": "^2.0", "php-parallel-lint/php-parallel-lint": "^1.2.0", "phpstan/phpstan-strict-rules": "^0.11 || ^0.12 || ^1.0" }, - "time": "2022-10-17T12:59:16+00:00", + "time": "2023-05-24T08:59:17+00:00", "type": "composer-plugin", "extra": { "class": "PHPStan\\ExtensionInstaller\\Plugin" @@ -1119,29 +1119,30 @@ "description": "Composer plugin for automatic installation of PHPStan extensions", "support": { "issues": "https://github.com/phpstan/extension-installer/issues", - "source": "https://github.com/phpstan/extension-installer/tree/1.2.0" + "source": "https://github.com/phpstan/extension-installer/tree/1.3.1" }, "install-path": "../phpstan/extension-installer" }, { "name": "phpstan/phpdoc-parser", - "version": "1.16.1", - "version_normalized": "1.16.1.0", + "version": "1.21.0", + "version_normalized": "1.21.0.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571" + "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/e27e92d939e2e3636f0a1f0afaba59692c0bf571", - "reference": "e27e92d939e2e3636f0a1f0afaba59692c0bf571", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/6df62b08faef4f899772bc7c3bbabb93d2b7a21c", + "reference": "6df62b08faef4f899772bc7c3bbabb93d2b7a21c", "shasum": "" }, "require": { "php": "^7.2 || ^8.0" }, "require-dev": { + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", @@ -1150,7 +1151,7 @@ "phpunit/phpunit": "^9.5", "symfony/process": "^5.2" }, - "time": "2023-02-07T18:11:17+00:00", + "time": "2023-05-17T13:13:44+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1167,23 +1168,23 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.16.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.21.0" }, "install-path": "../phpstan/phpdoc-parser" }, { "name": "phpstan/phpstan", - "version": "1.10.8", - "version_normalized": "1.10.8.0", + "version": "1.10.15", + "version_normalized": "1.10.15.0", "source": { "type": "git", "url": "https://github.com/phpstan/phpstan.git", - "reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9" + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpstan/zipball/0166aef76e066f0dd2adc2799bdadfa1635711e9", - "reference": "0166aef76e066f0dd2adc2799bdadfa1635711e9", + "url": "https://api.github.com/repos/phpstan/phpstan/zipball/762c4dac4da6f8756eebb80e528c3a47855da9bd", + "reference": "762c4dac4da6f8756eebb80e528c3a47855da9bd", "shasum": "" }, "require": { @@ -1192,7 +1193,7 @@ "conflict": { "phpstan/phpstan-shim": "*" }, - "time": "2023-03-24T10:28:16+00:00", + "time": "2023-05-09T15:28:01+00:00", "bin": [ "phpstan", "phpstan.phar" @@ -1467,17 +1468,17 @@ }, { "name": "sebastian/diff", - "version": "5.0.1", - "version_normalized": "5.0.1.0", + "version": "5.0.3", + "version_normalized": "5.0.3.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02" + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/aae9a0a43bff37bd5d8d0311426c87bf36153f02", - "reference": "aae9a0a43bff37bd5d8d0311426c87bf36153f02", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/912dc2fbe3e3c1e7873313cc801b100b6c68c87b", + "reference": "912dc2fbe3e3c1e7873313cc801b100b6c68c87b", "shasum": "" }, "require": { @@ -1487,7 +1488,7 @@ "phpunit/phpunit": "^10.0", "symfony/process": "^4.2 || ^5" }, - "time": "2023-03-23T05:12:41+00:00", + "time": "2023-05-01T07:48:21+00:00", "type": "library", "extra": { "branch-alias": { @@ -1525,7 +1526,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.0.1" + "source": "https://github.com/sebastianbergmann/diff/tree/5.0.3" }, "funding": [ { @@ -1537,17 +1538,17 @@ }, { "name": "spatie/array-to-xml", - "version": "3.1.5", - "version_normalized": "3.1.5.0", + "version": "3.1.6", + "version_normalized": "3.1.6.0", "source": { "type": "git", "url": "https://github.com/spatie/array-to-xml.git", - "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43" + "reference": "e210b98957987c755372465be105d32113f339a4" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/13f76acef5362d15c71ae1ac6350cc3df5e25e43", - "reference": "13f76acef5362d15c71ae1ac6350cc3df5e25e43", + "url": "https://api.github.com/repos/spatie/array-to-xml/zipball/e210b98957987c755372465be105d32113f339a4", + "reference": "e210b98957987c755372465be105d32113f339a4", "shasum": "" }, "require": { @@ -1559,7 +1560,7 @@ "pestphp/pest": "^1.21", "spatie/pest-plugin-snapshots": "^1.1" }, - "time": "2022-12-24T13:43:51+00:00", + "time": "2023-05-11T14:04:07+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1587,7 +1588,7 @@ "xml" ], "support": { - "source": "https://github.com/spatie/array-to-xml/tree/3.1.5" + "source": "https://github.com/spatie/array-to-xml/tree/3.1.6" }, "funding": [ { @@ -1603,17 +1604,17 @@ }, { "name": "symfony/console", - "version": "v6.2.7", - "version_normalized": "6.2.7.0", + "version": "v6.2.11", + "version_normalized": "6.2.11.0", "source": { "type": "git", "url": "https://github.com/symfony/console.git", - "reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45" + "reference": "5aa03db8ef0a5457c316ec580e69562d97734c77" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/console/zipball/cbad09eb8925b6ad4fb721c7a179344dc4a19d45", - "reference": "cbad09eb8925b6ad4fb721c7a179344dc4a19d45", + "url": "https://api.github.com/repos/symfony/console/zipball/5aa03db8ef0a5457c316ec580e69562d97734c77", + "reference": "5aa03db8ef0a5457c316ec580e69562d97734c77", "shasum": "" }, "require": { @@ -1648,7 +1649,7 @@ "symfony/lock": "", "symfony/process": "" }, - "time": "2023-02-25T17:00:03+00:00", + "time": "2023-05-26T08:16:21+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1677,12 +1678,12 @@ "homepage": "https://symfony.com", "keywords": [ "cli", - "command line", + "command-line", "console", "terminal" ], "support": { - "source": "https://github.com/symfony/console/tree/v6.2.7" + "source": "https://github.com/symfony/console/tree/v6.2.11" }, "funding": [ { @@ -1772,17 +1773,17 @@ }, { "name": "symfony/filesystem", - "version": "v6.2.7", - "version_normalized": "6.2.7.0", + "version": "v6.2.10", + "version_normalized": "6.2.10.0", "source": { "type": "git", "url": "https://github.com/symfony/filesystem.git", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3" + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/filesystem/zipball/82b6c62b959f642d000456f08c6d219d749215b3", - "reference": "82b6c62b959f642d000456f08c6d219d749215b3", + "url": "https://api.github.com/repos/symfony/filesystem/zipball/fd588debf7d1bc16a2c84b4b3b71145d9946b894", + "reference": "fd588debf7d1bc16a2c84b4b3b71145d9946b894", "shasum": "" }, "require": { @@ -1790,7 +1791,7 @@ "symfony/polyfill-ctype": "~1.8", "symfony/polyfill-mbstring": "~1.8" }, - "time": "2023-02-14T08:44:56+00:00", + "time": "2023-04-18T13:46:08+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -1818,7 +1819,7 @@ "description": "Provides basic utilities for the filesystem", "homepage": "https://symfony.com", "support": { - "source": "https://github.com/symfony/filesystem/tree/v6.2.7" + "source": "https://github.com/symfony/filesystem/tree/v6.2.10" }, "funding": [ { @@ -2354,17 +2355,17 @@ }, { "name": "symfony/string", - "version": "v6.2.7", - "version_normalized": "6.2.7.0", + "version": "v6.2.8", + "version_normalized": "6.2.8.0", "source": { "type": "git", "url": "https://github.com/symfony/string.git", - "reference": "67b8c1eec78296b85dc1c7d9743830160218993d" + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/symfony/string/zipball/67b8c1eec78296b85dc1c7d9743830160218993d", - "reference": "67b8c1eec78296b85dc1c7d9743830160218993d", + "url": "https://api.github.com/repos/symfony/string/zipball/193e83bbd6617d6b2151c37fff10fa7168ebddef", + "reference": "193e83bbd6617d6b2151c37fff10fa7168ebddef", "shasum": "" }, "require": { @@ -2384,7 +2385,7 @@ "symfony/translation-contracts": "^2.0|^3.0", "symfony/var-exporter": "^5.4|^6.0" }, - "time": "2023-02-24T10:42:00+00:00", + "time": "2023-03-20T16:06:02+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -2423,7 +2424,7 @@ "utf8" ], "support": { - "source": "https://github.com/symfony/string/tree/v6.2.7" + "source": "https://github.com/symfony/string/tree/v6.2.8" }, "funding": [ { @@ -2508,17 +2509,17 @@ }, { "name": "vimeo/psalm", - "version": "5.8.0", - "version_normalized": "5.8.0.0", + "version": "5.12.0", + "version_normalized": "5.12.0.0", "source": { "type": "git", "url": "https://github.com/vimeo/psalm.git", - "reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda" + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/vimeo/psalm/zipball/9cf4f60a333f779ad3bc704a555920e81d4fdcda", - "reference": "9cf4f60a333f779ad3bc704a555920e81d4fdcda", + "url": "https://api.github.com/repos/vimeo/psalm/zipball/f90118cdeacd0088e7215e64c0c99ceca819e176", + "reference": "f90118cdeacd0088e7215e64c0c99ceca819e176", "shasum": "" }, "require": { @@ -2550,6 +2551,7 @@ "psalm/psalm": "self.version" }, "require-dev": { + "amphp/phpunit-util": "^2.0", "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^6.9", "ext-curl": "*", @@ -2568,7 +2570,7 @@ "ext-curl": "In order to send data to shepherd", "ext-igbinary": "^2.0.5 is required, used to serialize caching data" }, - "time": "2023-03-09T04:14:35+00:00", + "time": "2023-05-22T21:19:03+00:00", "bin": [ "psalm", "psalm-language-server", @@ -2610,7 +2612,7 @@ ], "support": { "issues": "https://github.com/vimeo/psalm/issues", - "source": "https://github.com/vimeo/psalm/tree/5.8.0" + "source": "https://github.com/vimeo/psalm/tree/5.12.0" }, "install-path": "../vimeo/psalm" }, @@ -2700,7 +2702,6 @@ "phpstan/phpstan", "phpstan/phpstan-deprecation-rules", "psr/container", - "psr/log", "sabre/event", "sebastian/diff", "spatie/array-to-xml", diff --git a/vendor/composer/installed.php b/vendor/composer/installed.php index 9f147870..2059fa41 100644 --- a/vendor/composer/installed.php +++ b/vendor/composer/installed.php @@ -119,18 +119,18 @@ 'dev_requirement' => true, ), 'netresearch/jsonmapper' => array( - 'pretty_version' => 'v4.1.0', - 'version' => '4.1.0.0', - 'reference' => 'cfa81ea1d35294d64adb9c68aa4cb9e92400e53f', + 'pretty_version' => 'v4.2.0', + 'version' => '4.2.0.0', + 'reference' => 'f60565f8c0566a31acf06884cdaa591867ecc956', 'type' => 'library', 'install_path' => __DIR__ . '/../netresearch/jsonmapper', 'aliases' => array(), 'dev_requirement' => true, ), 'nikic/php-parser' => array( - 'pretty_version' => 'v4.15.4', - 'version' => '4.15.4.0', - 'reference' => '6bb5176bc4af8bcb7d926f88718db9b96a2d4290', + 'pretty_version' => 'v4.15.5', + 'version' => '4.15.5.0', + 'reference' => '11e2663a5bc9db5d714eedb4277ee300403b4a9e', 'type' => 'library', 'install_path' => __DIR__ . '/../nikic/php-parser', 'aliases' => array(), @@ -173,27 +173,27 @@ 'dev_requirement' => true, ), 'phpstan/extension-installer' => array( - 'pretty_version' => '1.2.0', - 'version' => '1.2.0.0', - 'reference' => 'f06dbb052ddc394e7896fcd1cfcd533f9f6ace40', + 'pretty_version' => '1.3.1', + 'version' => '1.3.1.0', + 'reference' => 'f45734bfb9984c6c56c4486b71230355f066a58a', 'type' => 'composer-plugin', 'install_path' => __DIR__ . '/../phpstan/extension-installer', 'aliases' => array(), 'dev_requirement' => true, ), 'phpstan/phpdoc-parser' => array( - 'pretty_version' => '1.16.1', - 'version' => '1.16.1.0', - 'reference' => 'e27e92d939e2e3636f0a1f0afaba59692c0bf571', + 'pretty_version' => '1.21.0', + 'version' => '1.21.0.0', + 'reference' => '6df62b08faef4f899772bc7c3bbabb93d2b7a21c', 'type' => 'library', 'install_path' => __DIR__ . '/../phpstan/phpdoc-parser', 'aliases' => array(), 'dev_requirement' => true, ), 'phpstan/phpstan' => array( - 'pretty_version' => '1.10.8', - 'version' => '1.10.8.0', - 'reference' => '0166aef76e066f0dd2adc2799bdadfa1635711e9', + 'pretty_version' => '1.10.15', + 'version' => '1.10.15.0', + 'reference' => '762c4dac4da6f8756eebb80e528c3a47855da9bd', 'type' => 'library', 'install_path' => __DIR__ . '/../phpstan/phpstan', 'aliases' => array(), @@ -211,7 +211,7 @@ 'psalm/psalm' => array( 'dev_requirement' => true, 'provided' => array( - 0 => '5.8.0', + 0 => '5.12.0', ), ), 'psr/container' => array( @@ -230,7 +230,7 @@ 'type' => 'library', 'install_path' => __DIR__ . '/../psr/log', 'aliases' => array(), - 'dev_requirement' => true, + 'dev_requirement' => false, ), 'psr/log-implementation' => array( 'dev_requirement' => true, @@ -248,27 +248,27 @@ 'dev_requirement' => true, ), 'sebastian/diff' => array( - 'pretty_version' => '5.0.1', - 'version' => '5.0.1.0', - 'reference' => 'aae9a0a43bff37bd5d8d0311426c87bf36153f02', + 'pretty_version' => '5.0.3', + 'version' => '5.0.3.0', + 'reference' => '912dc2fbe3e3c1e7873313cc801b100b6c68c87b', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/diff', 'aliases' => array(), 'dev_requirement' => true, ), 'spatie/array-to-xml' => array( - 'pretty_version' => '3.1.5', - 'version' => '3.1.5.0', - 'reference' => '13f76acef5362d15c71ae1ac6350cc3df5e25e43', + 'pretty_version' => '3.1.6', + 'version' => '3.1.6.0', + 'reference' => 'e210b98957987c755372465be105d32113f339a4', 'type' => 'library', 'install_path' => __DIR__ . '/../spatie/array-to-xml', 'aliases' => array(), 'dev_requirement' => true, ), 'symfony/console' => array( - 'pretty_version' => 'v6.2.7', - 'version' => '6.2.7.0', - 'reference' => 'cbad09eb8925b6ad4fb721c7a179344dc4a19d45', + 'pretty_version' => 'v6.2.11', + 'version' => '6.2.11.0', + 'reference' => '5aa03db8ef0a5457c316ec580e69562d97734c77', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/console', 'aliases' => array(), @@ -284,9 +284,9 @@ 'dev_requirement' => true, ), 'symfony/filesystem' => array( - 'pretty_version' => 'v6.2.7', - 'version' => '6.2.7.0', - 'reference' => '82b6c62b959f642d000456f08c6d219d749215b3', + 'pretty_version' => 'v6.2.10', + 'version' => '6.2.10.0', + 'reference' => 'fd588debf7d1bc16a2c84b4b3b71145d9946b894', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/filesystem', 'aliases' => array(), @@ -347,9 +347,9 @@ 'dev_requirement' => true, ), 'symfony/string' => array( - 'pretty_version' => 'v6.2.7', - 'version' => '6.2.7.0', - 'reference' => '67b8c1eec78296b85dc1c7d9743830160218993d', + 'pretty_version' => 'v6.2.8', + 'version' => '6.2.8.0', + 'reference' => '193e83bbd6617d6b2151c37fff10fa7168ebddef', 'type' => 'library', 'install_path' => __DIR__ . '/../symfony/string', 'aliases' => array(), @@ -365,9 +365,9 @@ 'dev_requirement' => true, ), 'vimeo/psalm' => array( - 'pretty_version' => '5.8.0', - 'version' => '5.8.0.0', - 'reference' => '9cf4f60a333f779ad3bc704a555920e81d4fdcda', + 'pretty_version' => '5.12.0', + 'version' => '5.12.0.0', + 'reference' => 'f90118cdeacd0088e7215e64c0c99ceca819e176', 'type' => 'library', 'install_path' => __DIR__ . '/../vimeo/psalm', 'aliases' => array(), diff --git a/vendor/netresearch/jsonmapper/.github/workflows/test.yml b/vendor/netresearch/jsonmapper/.github/workflows/test.yml new file mode 100644 index 00000000..76a8d654 --- /dev/null +++ b/vendor/netresearch/jsonmapper/.github/workflows/test.yml @@ -0,0 +1,46 @@ +name: JsonMapper tests +on: [push, workflow_dispatch] +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + php-versions: + - '7.1' + - '7.2' + - '7.3' + - '7.4' + - '8.0' + - '8.1' + - '8.2' + name: PHP ${{ matrix.php-versions }} + steps: + - name: Checkout + uses: actions/checkout@v3 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + tools: composer + coverage: xdebug + + - name: Get composer cache directory + id: composer-cache + run: echo "dir=$(composer config cache-files-dir)" >> $GITHUB_OUTPUT + + - name: Cache dependencies + uses: actions/cache@v3 + with: + path: ${{ steps.composer-cache.outputs.dir }} + key: ${{ runner.os }}-composer-${{ matrix.php-versions }}-${{ hashFiles('**/composer.lock') }} + restore-keys: ${{ runner.os }}-composer-${{ matrix.php-versions }}- + + - name: Install dependencies + run: composer install --no-interaction --prefer-dist + + - name: Run unit tests + run: XDEBUG_MODE=coverage ./vendor/bin/phpunit --coverage-text + + - name: Check codestyle + run: ./vendor/bin/phpcs --standard=PEAR src/ diff --git a/vendor/netresearch/jsonmapper/src/JsonMapper.php b/vendor/netresearch/jsonmapper/src/JsonMapper.php index fdb984f0..5ae3895b 100644 --- a/vendor/netresearch/jsonmapper/src/JsonMapper.php +++ b/vendor/netresearch/jsonmapper/src/JsonMapper.php @@ -75,7 +75,7 @@ class JsonMapper public $bStrictNullTypes = true; /** - * Allow mapping of private and proteted properties. + * Allow mapping of private and protected properties. * * @var boolean */ @@ -131,8 +131,8 @@ class JsonMapper /** * Map data all data in $json into the given $object instance. * - * @param object|array $json JSON object structure from json_decode() - * @param object $object Object to map $json data into + * @param object|array $json JSON object structure from json_decode() + * @param object|class-string $object Object to map $json data into * * @return mixed Mapped object is returned. * @see mapArray() @@ -145,13 +145,18 @@ class JsonMapper . ', ' . gettype($json) . ' given.' ); } - if (!is_object($object)) { + if (!is_object($object) && (!is_string($object) || !class_exists($object))) { throw new InvalidArgumentException( - 'JsonMapper::map() requires second argument to be an object' + 'JsonMapper::map() requires second argument to ' + . 'be an object or existing class name' . ', ' . gettype($object) . ' given.' ); } + if (is_string($object)) { + $object = $this->createInstance($object); + } + $strClassName = get_class($object); $rc = new ReflectionClass($object); $strNs = $rc->getNamespaceName(); @@ -177,10 +182,15 @@ class JsonMapper . ' in object of type ' . $strClassName ); } else if ($this->undefinedPropertyHandler !== null) { - call_user_func( + $undefinedPropertyKey = call_user_func( $this->undefinedPropertyHandler, $object, $key, $jvalue ); + + if (is_string($undefinedPropertyKey)) { + list($hasProperty, $accessor, $type, $isNullable) + = $this->inspectProperty($rc, $undefinedPropertyKey); + } } else { $this->log( 'info', @@ -188,7 +198,10 @@ class JsonMapper array('property' => $key, 'class' => $strClassName) ); } - continue; + + if (!$hasProperty) { + continue; + } } if ($accessor === null) { @@ -229,7 +242,9 @@ class JsonMapper } else if ($this->isObjectOfSameType($type, $jvalue)) { $this->setProperty($object, $accessor, $jvalue); continue; - } else if ($this->isSimpleType($type)) { + } else if ($this->isSimpleType($type) + && !(is_array($jvalue) && $this->hasVariadicArrayType($accessor)) + ) { if ($type === 'string' && is_object($jvalue)) { throw new JsonMapper_Exception( 'JSON property "' . $key . '" in class "' @@ -268,8 +283,11 @@ class JsonMapper } else { $array = $this->createInstance($proptype, false, $jvalue); } + } else if (is_array($jvalue) && $this->hasVariadicArrayType($accessor)) { + $array = array(); + $subtype = $type; } else { - if (is_a($type, 'ArrayObject', true)) { + if (is_a($type, 'ArrayAccess', true)) { $array = $this->createInstance($type, false, $jvalue); } } @@ -625,6 +643,8 @@ class JsonMapper } if ($accessor instanceof ReflectionProperty) { $accessor->setValue($object, $value); + } else if (is_array($value) && $this->hasVariadicArrayType($accessor)) { + $accessor->invoke($object, ...$value); } else { //setter method $accessor->invoke($object, $value); @@ -647,6 +667,12 @@ class JsonMapper $class, $useParameter = false, $jvalue = null ) { if ($useParameter) { + if (PHP_VERSION_ID >= 80100 + && is_subclass_of($class, \BackedEnum::class) + ) { + return $class::from($jvalue); + } + return new $class($jvalue); } else { $reflectClass = new ReflectionClass($class); @@ -758,6 +784,32 @@ class JsonMapper return substr($strType, -2) === '[]'; } + /** + * Returns true if accessor is a method and has only one parameter + * which is variadic. + * + * @param ReflectionMethod|ReflectionProperty|null $accessor accessor + * to set value + * + * @return bool + */ + protected function hasVariadicArrayType($accessor) + { + if (!$accessor instanceof ReflectionMethod) { + return false; + } + + $parameters = $accessor->getParameters(); + + if (count($parameters) !== 1) { + return false; + } + + $parameter = $parameters[0]; + + return $parameter->isVariadic(); + } + /** * Checks if the given type is nullable * diff --git a/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php b/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php index de9aae7e..69f35332 100644 --- a/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php +++ b/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php @@ -19,6 +19,8 @@ class Param implements PhpParser\Builder protected $variadic = false; + protected $flags = 0; + /** @var Node\AttributeGroup[] */ protected $attributeGroups = []; @@ -95,6 +97,50 @@ class Param implements PhpParser\Builder return $this; } + /** + * Makes the (promoted) parameter public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC); + + return $this; + } + + /** + * Makes the (promoted) parameter protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED); + + return $this; + } + + /** + * Makes the (promoted) parameter private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE); + + return $this; + } + + /** + * Makes the (promoted) parameter readonly. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeReadonly() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY); + + return $this; + } + /** * Adds an attribute group. * @@ -116,7 +162,7 @@ class Param implements PhpParser\Builder public function getNode() : Node { return new Node\Param( new Node\Expr\Variable($this->name), - $this->default, $this->type, $this->byRef, $this->variadic, [], 0, $this->attributeGroups + $this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups ); } } diff --git a/vendor/phpstan/extension-installer/README.md b/vendor/phpstan/extension-installer/README.md index 70afa415..0616f999 100644 --- a/vendor/phpstan/extension-installer/README.md +++ b/vendor/phpstan/extension-installer/README.md @@ -72,6 +72,22 @@ Add `phpstan` key in the extension `composer.json`'s `extra` section: } ``` +## Ignoring a particular extension + +You may want to disable auto-installation of a particular extension to handle installation manually. Ignore an extension by adding an `extra.phpstan/extension-installer.ignore` array in `composer.json` that specifies a list of packages to ignore: + +```json +{ + "extra": { + "phpstan/extension-installer": { + "ignore": [ + "phpstan/phpstan-phpunit" + ] + } + } +} +``` + ## Limitations The extension installer depends on Composer script events, therefore you cannot use `--no-scripts` flag. diff --git a/vendor/phpstan/extension-installer/build-cs/composer.json b/vendor/phpstan/extension-installer/build-cs/composer.json index e3079710..16a240bc 100644 --- a/vendor/phpstan/extension-installer/build-cs/composer.json +++ b/vendor/phpstan/extension-installer/build-cs/composer.json @@ -1,8 +1,9 @@ { "require-dev": { - "consistence-community/coding-standard": "^3.10", - "dealerdirect/phpcodesniffer-composer-installer": "^0.7.0", - "slevomat/coding-standard": "^7.0" + "consistence-community/coding-standard": "^3.11.0", + "dealerdirect/phpcodesniffer-composer-installer": "^1.0.0", + "slevomat/coding-standard": "^8.8.0", + "squizlabs/php_codesniffer": "^3.5.3" }, "config": { "allow-plugins": { diff --git a/vendor/phpstan/extension-installer/build-cs/composer.lock b/vendor/phpstan/extension-installer/build-cs/composer.lock index 4bcc8de4..db122416 100644 --- a/vendor/phpstan/extension-installer/build-cs/composer.lock +++ b/vendor/phpstan/extension-installer/build-cs/composer.lock @@ -4,35 +4,35 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "4485bbedba7bcc71ace5f69dbb9b6c47", + "content-hash": "e69c1916405a7e3c8001c1b609a0ee61", "packages": [], "packages-dev": [ { "name": "consistence-community/coding-standard", - "version": "3.11.1", + "version": "3.11.3", "source": { "type": "git", "url": "https://github.com/consistence-community/coding-standard.git", - "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df" + "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/4632fead8c9ee8f50044fcbce9f66c797b34c0df", - "reference": "4632fead8c9ee8f50044fcbce9f66c797b34c0df", + "url": "https://api.github.com/repos/consistence-community/coding-standard/zipball/f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1", + "reference": "f38e06327d5bf80ff5ff523a2c05e623b5e8d8b1", "shasum": "" }, "require": { - "php": ">=7.4", - "slevomat/coding-standard": "~7.0", - "squizlabs/php_codesniffer": "~3.6.0" + "php": "~8.0", + "slevomat/coding-standard": "~8.0", + "squizlabs/php_codesniffer": "~3.7.0" }, "replace": { "consistence/coding-standard": "3.10.*" }, "require-dev": { - "phing/phing": "2.16.4", - "php-parallel-lint/php-parallel-lint": "1.3.0", - "phpunit/phpunit": "9.5.4" + "phing/phing": "2.17.0", + "php-parallel-lint/php-parallel-lint": "1.3.1", + "phpunit/phpunit": "9.5.10" }, "type": "library", "autoload": { @@ -70,41 +70,44 @@ ], "support": { "issues": "https://github.com/consistence-community/coding-standard/issues", - "source": "https://github.com/consistence-community/coding-standard/tree/3.11.1" + "source": "https://github.com/consistence-community/coding-standard/tree/3.11.3" }, - "time": "2021-05-03T18:13:22+00:00" + "time": "2023-03-27T14:55:41+00:00" }, { "name": "dealerdirect/phpcodesniffer-composer-installer", - "version": "v0.7.2", + "version": "v1.0.0", "source": { "type": "git", - "url": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer.git", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db" + "url": "https://github.com/PHPCSStandards/composer-installer.git", + "reference": "4be43904336affa5c2f70744a348312336afd0da" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/Dealerdirect/phpcodesniffer-composer-installer/zipball/1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", - "reference": "1c968e542d8843d7cd71de3c5c9c3ff3ad71a1db", + "url": "https://api.github.com/repos/PHPCSStandards/composer-installer/zipball/4be43904336affa5c2f70744a348312336afd0da", + "reference": "4be43904336affa5c2f70744a348312336afd0da", "shasum": "" }, "require": { "composer-plugin-api": "^1.0 || ^2.0", - "php": ">=5.3", + "php": ">=5.4", "squizlabs/php_codesniffer": "^2.0 || ^3.1.0 || ^4.0" }, "require-dev": { "composer/composer": "*", + "ext-json": "*", + "ext-zip": "*", "php-parallel-lint/php-parallel-lint": "^1.3.1", - "phpcompatibility/php-compatibility": "^9.0" + "phpcompatibility/php-compatibility": "^9.0", + "yoast/phpunit-polyfills": "^1.0" }, "type": "composer-plugin", "extra": { - "class": "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" + "class": "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\Plugin" }, "autoload": { "psr-4": { - "Dealerdirect\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" + "PHPCSStandards\\Composer\\Plugin\\Installers\\PHPCodeSniffer\\": "src/" } }, "notification-url": "https://packagist.org/downloads/", @@ -120,7 +123,7 @@ }, { "name": "Contributors", - "homepage": "https://github.com/Dealerdirect/phpcodesniffer-composer-installer/graphs/contributors" + "homepage": "https://github.com/PHPCSStandards/composer-installer/graphs/contributors" } ], "description": "PHP_CodeSniffer Standards Composer Installer Plugin", @@ -144,23 +147,23 @@ "tests" ], "support": { - "issues": "https://github.com/dealerdirect/phpcodesniffer-composer-installer/issues", - "source": "https://github.com/dealerdirect/phpcodesniffer-composer-installer" + "issues": "https://github.com/PHPCSStandards/composer-installer/issues", + "source": "https://github.com/PHPCSStandards/composer-installer" }, - "time": "2022-02-04T12:51:07+00:00" + "time": "2023-01-05T11:28:13+00:00" }, { "name": "phpstan/phpdoc-parser", - "version": "1.5.1", + "version": "1.20.4", "source": { "type": "git", "url": "https://github.com/phpstan/phpdoc-parser.git", - "reference": "981cc368a216c988e862a75e526b6076987d1b50" + "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/981cc368a216c988e862a75e526b6076987d1b50", - "reference": "981cc368a216c988e862a75e526b6076987d1b50", + "url": "https://api.github.com/repos/phpstan/phpdoc-parser/zipball/7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", + "reference": "7d568c87a9df9c5f7e8b5f075fc469aa8cb0a4cd", "shasum": "" }, "require": { @@ -170,6 +173,7 @@ "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", + "phpstan/phpstan-phpunit": "^1.1", "phpstan/phpstan-strict-rules": "^1.0", "phpunit/phpunit": "^9.5", "symfony/process": "^5.2" @@ -189,48 +193,48 @@ "description": "PHPDoc parser with support for nullable, intersection and generic types", "support": { "issues": "https://github.com/phpstan/phpdoc-parser/issues", - "source": "https://github.com/phpstan/phpdoc-parser/tree/1.5.1" + "source": "https://github.com/phpstan/phpdoc-parser/tree/1.20.4" }, - "time": "2022-05-05T11:32:40+00:00" + "time": "2023-05-02T09:19:37+00:00" }, { "name": "slevomat/coding-standard", - "version": "7.2.1", + "version": "8.12.0", "source": { "type": "git", "url": "https://github.com/slevomat/coding-standard.git", - "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90" + "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/aff06ae7a84e4534bf6f821dc982a93a5d477c90", - "reference": "aff06ae7a84e4534bf6f821dc982a93a5d477c90", + "url": "https://api.github.com/repos/slevomat/coding-standard/zipball/cc04334ed0ce5a251389112fbd2dbe1dbc931ae8", + "reference": "cc04334ed0ce5a251389112fbd2dbe1dbc931ae8", "shasum": "" }, "require": { - "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7", + "dealerdirect/phpcodesniffer-composer-installer": "^0.6.2 || ^0.7 || ^1.0", "php": "^7.2 || ^8.0", - "phpstan/phpdoc-parser": "^1.5.1", - "squizlabs/php_codesniffer": "^3.6.2" + "phpstan/phpdoc-parser": ">=1.20.0 <1.21.0", + "squizlabs/php_codesniffer": "^3.7.1" }, "require-dev": { - "phing/phing": "2.17.3", + "phing/phing": "2.17.4", "php-parallel-lint/php-parallel-lint": "1.3.2", - "phpstan/phpstan": "1.4.10|1.7.1", - "phpstan/phpstan-deprecation-rules": "1.0.0", - "phpstan/phpstan-phpunit": "1.0.0|1.1.1", - "phpstan/phpstan-strict-rules": "1.2.3", - "phpunit/phpunit": "7.5.20|8.5.21|9.5.20" + "phpstan/phpstan": "1.10.15", + "phpstan/phpstan-deprecation-rules": "1.1.3", + "phpstan/phpstan-phpunit": "1.3.11", + "phpstan/phpstan-strict-rules": "1.5.1", + "phpunit/phpunit": "7.5.20|8.5.21|9.6.8|10.1.3" }, "type": "phpcodesniffer-standard", "extra": { "branch-alias": { - "dev-master": "7.x-dev" + "dev-master": "8.x-dev" } }, "autoload": { "psr-4": { - "SlevomatCodingStandard\\": "SlevomatCodingStandard" + "SlevomatCodingStandard\\": "SlevomatCodingStandard/" } }, "notification-url": "https://packagist.org/downloads/", @@ -238,9 +242,13 @@ "MIT" ], "description": "Slevomat Coding Standard for PHP_CodeSniffer complements Consistence Coding Standard by providing sniffs with additional checks.", + "keywords": [ + "dev", + "phpcs" + ], "support": { "issues": "https://github.com/slevomat/coding-standard/issues", - "source": "https://github.com/slevomat/coding-standard/tree/7.2.1" + "source": "https://github.com/slevomat/coding-standard/tree/8.12.0" }, "funding": [ { @@ -252,20 +260,20 @@ "type": "tidelift" } ], - "time": "2022-05-25T10:58:12+00:00" + "time": "2023-05-14T20:06:01+00:00" }, { "name": "squizlabs/php_codesniffer", - "version": "3.6.2", + "version": "3.7.2", "source": { "type": "git", "url": "https://github.com/squizlabs/PHP_CodeSniffer.git", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a" + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/5e4e71592f69da17871dba6e80dd51bce74a351a", - "reference": "5e4e71592f69da17871dba6e80dd51bce74a351a", + "url": "https://api.github.com/repos/squizlabs/PHP_CodeSniffer/zipball/ed8e00df0a83aa96acf703f8c2979ff33341f879", + "reference": "ed8e00df0a83aa96acf703f8c2979ff33341f879", "shasum": "" }, "require": { @@ -301,14 +309,15 @@ "homepage": "https://github.com/squizlabs/PHP_CodeSniffer", "keywords": [ "phpcs", - "standards" + "standards", + "static analysis" ], "support": { "issues": "https://github.com/squizlabs/PHP_CodeSniffer/issues", "source": "https://github.com/squizlabs/PHP_CodeSniffer", "wiki": "https://github.com/squizlabs/PHP_CodeSniffer/wiki" }, - "time": "2021-12-12T21:44:58+00:00" + "time": "2023-02-22T23:07:41+00:00" } ], "aliases": [], diff --git a/vendor/phpstan/extension-installer/composer.json b/vendor/phpstan/extension-installer/composer.json index c57fcfe5..f2865a39 100644 --- a/vendor/phpstan/extension-installer/composer.json +++ b/vendor/phpstan/extension-installer/composer.json @@ -8,7 +8,7 @@ "require": { "php": "^7.2 || ^8.0", "composer-plugin-api": "^2.0", - "phpstan/phpstan": "^1.8.0" + "phpstan/phpstan": "^1.9.0" }, "require-dev": { "composer/composer": "^2.0", diff --git a/vendor/phpstan/extension-installer/src/GeneratedConfig.php b/vendor/phpstan/extension-installer/src/GeneratedConfig.php index 3c9004ac..a5a02fad 100644 --- a/vendor/phpstan/extension-installer/src/GeneratedConfig.php +++ b/vendor/phpstan/extension-installer/src/GeneratedConfig.php @@ -3,21 +3,30 @@ namespace PHPStan\ExtensionInstaller; /** - * This is a stub class: it is in place only for scenarios where Composer - * is run with a `--no-scripts` flag, in which scenarios this stub class - * is not being replaced. - * - * If you are reading this docBlock inside your `vendor/` dir, then this means - * that phpstan/extension-installer didn't correctly install. - * + * This class is generated by phpstan/extension-installer. * @internal */ final class GeneratedConfig { - public const EXTENSIONS = []; + public const EXTENSIONS = array ( + 'phpstan/phpstan-deprecation-rules' => + array ( + 'install_path' => '/storage/var/www/html/developers/clemens/core_data/php_libraries/trunk/vendor/phpstan/phpstan-deprecation-rules', + 'relative_install_path' => '../../phpstan-deprecation-rules', + 'extra' => + array ( + 'includes' => + array ( + 0 => 'rules.neon', + ), + ), + 'version' => '1.1.3', + ), +); - public const NOT_INSTALLED = []; + public const NOT_INSTALLED = array ( +); private function __construct() { diff --git a/vendor/phpstan/extension-installer/src/Plugin.php b/vendor/phpstan/extension-installer/src/Plugin.php index 4ff82e4b..b8e605bd 100644 --- a/vendor/phpstan/extension-installer/src/Plugin.php +++ b/vendor/phpstan/extension-installer/src/Plugin.php @@ -19,6 +19,7 @@ use function is_file; use function ksort; use function md5; use function md5_file; +use function sort; use function sprintf; use function strpos; use function var_export; @@ -97,9 +98,18 @@ PHP; } $notInstalledPackages = []; $installedPackages = []; + $ignoredPackages = []; $data = []; $fs = new Filesystem(); + $ignore = []; + + $packageExtra = $composer->getPackage()->getExtra(); + + if (isset($packageExtra['phpstan/extension-installer']['ignore'])) { + $ignore = $packageExtra['phpstan/extension-installer']['ignore']; + } + foreach ($composer->getRepositoryManager()->getLocalRepository()->getPackages() as $package) { if ( $package->getType() !== 'phpstan-extension' @@ -119,7 +129,15 @@ PHP; continue; } + if (in_array($package->getName(), $ignore, true)) { + $ignoredPackages[] = $package->getName(); + continue; + } + $installPath = $installationManager->getInstallPath($package); + if ($installPath === null) { + continue; + } $absoluteInstallPath = $fs->isAbsolutePath($installPath) ? $installPath @@ -138,6 +156,7 @@ PHP; ksort($data); ksort($installedPackages); ksort($notInstalledPackages); + sort($ignoredPackages); $generatedConfigFileContents = sprintf(self::$generatedFileTemplate, var_export($data, true), var_export($notInstalledPackages, true)); file_put_contents($generatedConfigFilePath, $generatedConfigFileContents); @@ -154,6 +173,10 @@ PHP; foreach (array_keys($notInstalledPackages) as $name) { $io->write(sprintf('> %s: not supported', $name)); } + + foreach ($ignoredPackages as $name) { + $io->write(sprintf('> %s: ignored', $name)); + } } } diff --git a/vendor/phpstan/phpdoc-parser/README.md b/vendor/phpstan/phpdoc-parser/README.md index 080e25ee..2d2f6c82 100644 --- a/vendor/phpstan/phpdoc-parser/README.md +++ b/vendor/phpstan/phpdoc-parser/README.md @@ -1,4 +1,4 @@ -

PHPDoc-Parser for PHPStan

+

PHPDoc Parser for PHPStan

Build Status @@ -7,11 +7,99 @@ PHPStan Enabled

-* [PHPStan](https://phpstan.org) +This library `phpstan/phpdoc-parser` represents PHPDocs with an AST (Abstract Syntax Tree). It supports parsing and modifying PHPDocs. ------- +For the complete list of supported PHPDoc features check out PHPStan documentation. PHPStan is the main (but not the only) user of this library. -Next generation phpDoc parser with support for intersection types and generics. +* [PHPDoc Basics](https://phpstan.org/writing-php-code/phpdocs-basics) (list of PHPDoc tags) +* [PHPDoc Types](https://phpstan.org/writing-php-code/phpdoc-types) (list of PHPDoc types) +* [phpdoc-parser API Reference](https://phpstan.github.io/phpdoc-parser/namespace-PHPStan.PhpDocParser.html) with all the AST node types etc. + +## Installation + +``` +composer require phpstan/phpdoc-parser +``` + +## Basic usage + +```php +tokenize('/** @param Lorem $a */')); +$phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode +$paramTags = $phpDocNode->getParamTagValues(); // ParamTagValueNode[] +echo $paramTags[0]->parameterName; // '$a' +echo $paramTags[0]->type; // IdentifierTypeNode - 'Lorem' +``` + +### Format-preserving printer + +This component can be used to modify the AST +and print it again as close as possible to the original. + +It's heavily inspired by format-preserving printer component in [nikic/PHP-Parser](https://github.com/nikic/PHP-Parser). + +```php + true, 'indexes' => true]; + +$lexer = new Lexer(); +$constExprParser = new ConstExprParser(true, true, $usedAttributes); +$typeParser = new TypeParser($constExprParser, true, $usedAttributes); +$phpDocParser = new PhpDocParser($typeParser, $constExprParser, true, true, $usedAttributes); + +$tokens = new TokenIterator($lexer->tokenize('/** @param Lorem $a */')); +$phpDocNode = $phpDocParser->parse($tokens); // PhpDocNode + +$cloningTraverser = new NodeTraverser([new CloningVisitor()]); + +/** @var PhpDocNode $newPhpDocNode */ +$printer = new Printer(); +[$newPhpDocNode] = $cloningTraverser->traverse([$phpDocNode]); + +// change something in $newPhpDocNode +$newPhpDocNode->getParamTagValues()[0]->type = new IdentifierTypeNode('Ipsum'); + +$newPhpDoc = $printer->printFormatPreserving($newPhpDocNode, $phpDocNode, $tokens); +echo $newPhpDoc; // '/** @param Ipsum $a */' +``` ## Code of Conduct diff --git a/vendor/phpstan/phpdoc-parser/composer.json b/vendor/phpstan/phpdoc-parser/composer.json index 3b902ae2..30b879b7 100644 --- a/vendor/phpstan/phpdoc-parser/composer.json +++ b/vendor/phpstan/phpdoc-parser/composer.json @@ -6,6 +6,7 @@ "php": "^7.2 || ^8.0" }, "require-dev": { + "nikic/php-parser": "^4.15", "php-parallel-lint/php-parallel-lint": "^1.2", "phpstan/extension-installer": "^1.0", "phpstan/phpstan": "^1.5", diff --git a/vendor/phpstan/phpdoc-parser/phpstan-baseline.neon b/vendor/phpstan/phpdoc-parser/phpstan-baseline.neon new file mode 100644 index 00000000..04100fcd --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/phpstan-baseline.neon @@ -0,0 +1,31 @@ +parameters: + ignoreErrors: + - + message: "#^Method PHPStan\\\\PhpDocParser\\\\Ast\\\\ConstExpr\\\\QuoteAwareConstExprStringNode\\:\\:escapeDoubleQuotedString\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/Ast/ConstExpr/QuoteAwareConstExprStringNode.php + + - + message: "#^Cannot use array destructuring on array\\\\|int\\|string\\>\\|null\\.$#" + count: 1 + path: src/Ast/NodeTraverser.php + + - + message: "#^Strict comparison using \\=\\=\\= between 2 and 2 will always evaluate to true\\.$#" + count: 2 + path: src/Ast/NodeTraverser.php + + - + message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#" + count: 1 + path: src/Ast/NodeTraverser.php + + - + message: "#^Method PHPStan\\\\PhpDocParser\\\\Parser\\\\StringUnescaper\\:\\:parseEscapeSequences\\(\\) should return string but returns string\\|null\\.$#" + count: 1 + path: src/Parser/StringUnescaper.php + + - + message: "#^Variable property access on PHPStan\\\\PhpDocParser\\\\Ast\\\\Node\\.$#" + count: 2 + path: src/Printer/Printer.php diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php b/vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php new file mode 100644 index 00000000..32d1a04a --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Ast/AbstractNodeVisitor.php @@ -0,0 +1,34 @@ +quoteType = $quoteType; + } + + + public function __toString(): string + { + if ($this->quoteType === self::SINGLE_QUOTED) { + // from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1007 + return sprintf("'%s'", addcslashes($this->value, '\'\\')); + } + + // from https://github.com/nikic/PHP-Parser/blob/0ffddce52d816f72d0efc4d9b02e276d3309ef01/lib/PhpParser/PrettyPrinter/Standard.php#L1010-L1040 + return sprintf('"%s"', $this->escapeDoubleQuotedString()); + } + + private function escapeDoubleQuotedString(): string + { + $quote = '"'; + $escaped = addcslashes($this->value, "\n\r\t\f\v$" . $quote . '\\'); + + // Escape control characters and non-UTF-8 characters. + // Regex based on https://stackoverflow.com/a/11709412/385378. + $regex = '/( + [\x00-\x08\x0E-\x1F] # Control characters + | [\xC0-\xC1] # Invalid UTF-8 Bytes + | [\xF5-\xFF] # Invalid UTF-8 Bytes + | \xE0(?=[\x80-\x9F]) # Overlong encoding of prior code point + | \xF0(?=[\x80-\x8F]) # Overlong encoding of prior code point + | [\xC2-\xDF](?![\x80-\xBF]) # Invalid UTF-8 Sequence Start + | [\xE0-\xEF](?![\x80-\xBF]{2}) # Invalid UTF-8 Sequence Start + | [\xF0-\xF4](?![\x80-\xBF]{3}) # Invalid UTF-8 Sequence Start + | (?<=[\x00-\x7F\xF5-\xFF])[\x80-\xBF] # Invalid UTF-8 Sequence Middle + | (? Visitors */ + private $visitors = []; + + /** @var bool Whether traversal should be stopped */ + private $stopTraversal; + + /** + * @param list $visitors + */ + public function __construct(array $visitors) + { + $this->visitors = $visitors; + } + + /** + * Traverses an array of nodes using the registered visitors. + * + * @param Node[] $nodes Array of nodes + * + * @return Node[] Traversed array of nodes + */ + public function traverse(array $nodes): array + { + $this->stopTraversal = false; + + foreach ($this->visitors as $visitor) { + $return = $visitor->beforeTraverse($nodes); + if ($return === null) { + continue; + } + + $nodes = $return; + } + + $nodes = $this->traverseArray($nodes); + + foreach ($this->visitors as $visitor) { + $return = $visitor->afterTraverse($nodes); + if ($return === null) { + continue; + } + + $nodes = $return; + } + + return $nodes; + } + + /** + * Recursively traverse a node. + * + * @param Node $node Node to traverse. + * + * @return Node Result of traversal (may be original node or new one) + */ + private function traverseNode(Node $node): Node + { + $subNodeNames = array_keys(get_object_vars($node)); + foreach ($subNodeNames as $name) { + $subNode =& $node->$name; + + if (is_array($subNode)) { + $subNode = $this->traverseArray($subNode); + if ($this->stopTraversal) { + break; + } + } elseif ($subNode instanceof Node) { + $traverseChildren = true; + $breakVisitorIndex = null; + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($subNode); + if ($return === null) { + continue; + } + + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = false; + } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = true; + break 2; + } else { + throw new LogicException( + 'enterNode() returned invalid value of type ' . gettype($return) + ); + } + } + + if ($traverseChildren) { + $subNode = $this->traverseNode($subNode); + if ($this->stopTraversal) { + break; + } + } + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($subNode); + + if ($return !== null) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($subNode, $return); + $subNode = $return; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = true; + break 2; + } elseif (is_array($return)) { + throw new LogicException( + 'leaveNode() may only return an array ' . + 'if the parent structure is an array' + ); + } else { + throw new LogicException( + 'leaveNode() returned invalid value of type ' . gettype($return) + ); + } + } + + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } + } + + return $node; + } + + /** + * Recursively traverse array (usually of nodes). + * + * @param mixed[] $nodes Array to traverse + * + * @return mixed[] Result of traversal (may be original array or changed one) + */ + private function traverseArray(array $nodes): array + { + $doNodes = []; + + foreach ($nodes as $i => &$node) { + if ($node instanceof Node) { + $traverseChildren = true; + $breakVisitorIndex = null; + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->enterNode($node); + if ($return === null) { + continue; + } + + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (is_array($return)) { + $doNodes[] = [$i, $return]; + continue 2; + } elseif ($return === self::REMOVE_NODE) { + $doNodes[] = [$i, []]; + continue 2; + } elseif ($return === self::DONT_TRAVERSE_CHILDREN) { + $traverseChildren = false; + } elseif ($return === self::DONT_TRAVERSE_CURRENT_AND_CHILDREN) { + $traverseChildren = false; + $breakVisitorIndex = $visitorIndex; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = true; + break 2; + } else { + throw new LogicException( + 'enterNode() returned invalid value of type ' . gettype($return) + ); + } + } + + if ($traverseChildren) { + $node = $this->traverseNode($node); + if ($this->stopTraversal) { + break; + } + } + + foreach ($this->visitors as $visitorIndex => $visitor) { + $return = $visitor->leaveNode($node); + + if ($return !== null) { + if ($return instanceof Node) { + $this->ensureReplacementReasonable($node, $return); + $node = $return; + } elseif (is_array($return)) { + $doNodes[] = [$i, $return]; + break; + } elseif ($return === self::REMOVE_NODE) { + $doNodes[] = [$i, []]; + break; + } elseif ($return === self::STOP_TRAVERSAL) { + $this->stopTraversal = true; + break 2; + } else { + throw new LogicException( + 'leaveNode() returned invalid value of type ' . gettype($return) + ); + } + } + + if ($breakVisitorIndex === $visitorIndex) { + break; + } + } + } elseif (is_array($node)) { + throw new LogicException('Invalid node structure: Contains nested arrays'); + } + } + + if (count($doNodes) > 0) { + while ([$i, $replace] = array_pop($doNodes)) { + array_splice($nodes, $i, 1, $replace); + } + } + + return $nodes; + } + + private function ensureReplacementReasonable(Node $old, Node $new): void + { + if ($old instanceof TypeNode && !$new instanceof TypeNode) { + throw new LogicException(sprintf('Trying to replace TypeNode with %s', get_class($new))); + } + + if ($old instanceof ConstExprNode && !$new instanceof ConstExprNode) { + throw new LogicException(sprintf('Trying to replace ConstExprNode with %s', get_class($new))); + } + + if ($old instanceof PhpDocChildNode && !$new instanceof PhpDocChildNode) { + throw new LogicException(sprintf('Trying to replace PhpDocChildNode with %s', get_class($new))); + } + + if ($old instanceof PhpDocTagValueNode && !$new instanceof PhpDocTagValueNode) { + throw new LogicException(sprintf('Trying to replace PhpDocTagValueNode with %s', get_class($new))); + } + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php new file mode 100644 index 00000000..bf7d784e --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor.php @@ -0,0 +1,87 @@ + $node stays as-is + * * array (of Nodes) + * => The return value is merged into the parent array (at the position of the $node) + * * NodeTraverser::REMOVE_NODE + * => $node is removed from the parent array + * * NodeTraverser::DONT_TRAVERSE_CHILDREN + * => Children of $node are not traversed. $node stays as-is + * * NodeTraverser::DONT_TRAVERSE_CURRENT_AND_CHILDREN + * => Further visitors for the current node are skipped, and its children are not + * traversed. $node stays as-is. + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * otherwise + * => $node is set to the return value + * + * @param Node $node Node + * + * @return Node|Node[]|NodeTraverser::*|null Replacement node (or special return value) + */ + public function enterNode(Node $node); + + /** + * Called when leaving a node. + * + * Return value semantics: + * * null + * => $node stays as-is + * * NodeTraverser::REMOVE_NODE + * => $node is removed from the parent array + * * NodeTraverser::STOP_TRAVERSAL + * => Traversal is aborted. $node stays as-is + * * array (of Nodes) + * => The return value is merged into the parent array (at the position of the $node) + * * otherwise + * => $node is set to the return value + * + * @param Node $node Node + * + * @return Node|Node[]|NodeTraverser::REMOVE_NODE|NodeTraverser::STOP_TRAVERSAL|null Replacement node (or special return value) + */ + public function leaveNode(Node $node); + + /** + * Called once after traversal. + * + * Return value semantics: + * * null: $nodes stays as-is + * * otherwise: $nodes is set to the return value + * + * @param Node[] $nodes Array of nodes + * + * @return Node[]|null Array of nodes + */ + public function afterTraverse(array $nodes): ?array; + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php new file mode 100644 index 00000000..7200f3af --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Ast/NodeVisitor/CloningVisitor.php @@ -0,0 +1,20 @@ +setAttribute(Attribute::ORIGINAL_NODE, $originalNode); + + return $node; + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php index 17bf04f2..ca7b4f20 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/InvalidTagValueNode.php @@ -31,10 +31,11 @@ class InvalidTagValueNode implements PhpDocTagValueNode $exception->getCurrentOffset(), $exception->getExpectedTokenType(), $exception->getExpectedTokenValue(), + $exception->getCurrentTokenLine(), ]; } - public function __get(string $name) + public function __get(string $name): ?ParserException { if ($name !== 'exception') { trigger_error(sprintf('Undefined property: %s::$%s', self::class, $name), E_USER_WARNING); diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php index 075cec04..211510be 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/PhpDoc/MethodTagValueNode.php @@ -30,6 +30,10 @@ class MethodTagValueNode implements PhpDocTagValueNode /** @var string (may be empty) */ public $description; + /** + * @param MethodTagValueParameterNode[] $parameters + * @param TemplateTagValueNode[] $templateTypes + */ public function __construct(bool $isStatic, ?TypeNode $returnType, string $methodName, array $parameters, string $description, array $templateTypes = []) { $this->isStatic = $isStatic; diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php index 41941f80..806783f9 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayShapeNode.php @@ -23,6 +23,7 @@ class ArrayShapeNode implements TypeNode public $kind; /** + * @param ArrayShapeItemNode[] $items * @param self::KIND_* $kind */ public function __construct(array $items, bool $sealed = true, string $kind = self::KIND_ARRAY) diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php index 90cb9f08..d2031032 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ArrayTypeNode.php @@ -20,6 +20,14 @@ class ArrayTypeNode implements TypeNode public function __toString(): string { + if ( + $this->type instanceof CallableTypeNode + || $this->type instanceof ConstTypeNode + || $this->type instanceof NullableTypeNode + ) { + return '(' . $this->type . ')[]'; + } + return $this->type . '[]'; } diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php index 83ade94c..e57e5f82 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeNode.php @@ -19,6 +19,9 @@ class CallableTypeNode implements TypeNode /** @var TypeNode */ public $returnType; + /** + * @param CallableTypeParameterNode[] $parameters + */ public function __construct(IdentifierTypeNode $identifier, array $parameters, TypeNode $returnType) { $this->identifier = $identifier; @@ -29,8 +32,12 @@ class CallableTypeNode implements TypeNode public function __toString(): string { + $returnType = $this->returnType; + if ($returnType instanceof self) { + $returnType = "({$returnType})"; + } $parameters = implode(', ', $this->parameters); - return "{$this->identifier}({$parameters}): {$this->returnType}"; + return "{$this->identifier}({$parameters}): {$returnType}"; } } diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php index 7ab2d7e3..c78d4c7b 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/CallableTypeParameterNode.php @@ -41,8 +41,8 @@ class CallableTypeParameterNode implements Node $type = "{$this->type} "; $isReference = $this->isReference ? '&' : ''; $isVariadic = $this->isVariadic ? '...' : ''; - $default = $this->isOptional ? ' = default' : ''; - return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $default; + $isOptional = $this->isOptional ? '=' : ''; + return trim("{$type}{$isReference}{$isVariadic}{$this->parameterName}") . $isOptional; } } diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php index 179de55a..44e1d16d 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/GenericTypeNode.php @@ -25,6 +25,10 @@ class GenericTypeNode implements TypeNode /** @var (self::VARIANCE_*)[] */ public $variances; + /** + * @param TypeNode[] $genericTypes + * @param (self::VARIANCE_*)[] $variances + */ public function __construct(IdentifierTypeNode $type, array $genericTypes, array $variances = []) { $this->type = $type; diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php index 7f9aff33..fd761cf7 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/IntersectionTypeNode.php @@ -3,6 +3,7 @@ namespace PHPStan\PhpDocParser\Ast\Type; use PHPStan\PhpDocParser\Ast\NodeAttributes; +use function array_map; use function implode; class IntersectionTypeNode implements TypeNode @@ -13,6 +14,9 @@ class IntersectionTypeNode implements TypeNode /** @var TypeNode[] */ public $types; + /** + * @param TypeNode[] $types + */ public function __construct(array $types) { $this->types = $types; @@ -21,7 +25,13 @@ class IntersectionTypeNode implements TypeNode public function __toString(): string { - return '(' . implode(' & ', $this->types) . ')'; + return '(' . implode(' & ', array_map(static function (TypeNode $type): string { + if ($type instanceof NullableTypeNode) { + return '(' . $type . ')'; + } + + return (string) $type; + }, $this->types)) . ')'; } } diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.php new file mode 100644 index 00000000..1ec47cf6 --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/InvalidTypeNode.php @@ -0,0 +1,38 @@ +exceptionArgs = [ + $exception->getCurrentTokenValue(), + $exception->getCurrentTokenType(), + $exception->getCurrentOffset(), + $exception->getExpectedTokenType(), + $exception->getExpectedTokenValue(), + $exception->getCurrentTokenLine(), + ]; + } + + public function getException(): ParserException + { + return new ParserException(...$this->exceptionArgs); + } + + public function __toString(): string + { + return '*Invalid type*'; + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php new file mode 100644 index 00000000..2f012406 --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeItemNode.php @@ -0,0 +1,48 @@ +keyName = $keyName; + $this->optional = $optional; + $this->valueType = $valueType; + } + + + public function __toString(): string + { + if ($this->keyName !== null) { + return sprintf( + '%s%s: %s', + (string) $this->keyName, + $this->optional ? '?' : '', + (string) $this->valueType + ); + } + + return (string) $this->valueType; + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php new file mode 100644 index 00000000..f418bc30 --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/ObjectShapeNode.php @@ -0,0 +1,31 @@ +items = $items; + } + + public function __toString(): string + { + $items = $this->items; + + return 'object{' . implode(', ', $items) . '}'; + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php index 1d5b04ef..39e83dfe 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/OffsetAccessTypeNode.php @@ -23,6 +23,14 @@ class OffsetAccessTypeNode implements TypeNode public function __toString(): string { + if ( + $this->type instanceof CallableTypeNode + || $this->type instanceof ConstTypeNode + || $this->type instanceof NullableTypeNode + ) { + return '(' . $this->type . ')[' . $this->offset . ']'; + } + return $this->type . '[' . $this->offset . ']'; } diff --git a/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php b/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php index 08acf56c..c552dab5 100644 --- a/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php +++ b/vendor/phpstan/phpdoc-parser/src/Ast/Type/UnionTypeNode.php @@ -3,6 +3,7 @@ namespace PHPStan\PhpDocParser\Ast\Type; use PHPStan\PhpDocParser\Ast\NodeAttributes; +use function array_map; use function implode; class UnionTypeNode implements TypeNode @@ -13,6 +14,9 @@ class UnionTypeNode implements TypeNode /** @var TypeNode[] */ public $types; + /** + * @param TypeNode[] $types + */ public function __construct(array $types) { $this->types = $types; @@ -21,7 +25,13 @@ class UnionTypeNode implements TypeNode public function __toString(): string { - return '(' . implode(' | ', $this->types) . ')'; + return '(' . implode(' | ', array_map(static function (TypeNode $type): string { + if ($type instanceof NullableTypeNode) { + return '(' . $type . ')'; + } + + return (string) $type; + }, $this->types)) . ')'; } } diff --git a/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php b/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php index 1b98839d..ccae6bef 100644 --- a/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php +++ b/vendor/phpstan/phpdoc-parser/src/Lexer/Lexer.php @@ -88,10 +88,14 @@ class Lexer public const VALUE_OFFSET = 0; public const TYPE_OFFSET = 1; + public const LINE_OFFSET = 2; /** @var string|null */ private $regexp; + /** + * @return list + */ public function tokenize(string $s): array { if ($this->regexp === null) { @@ -101,11 +105,18 @@ class Lexer preg_match_all($this->regexp, $s, $matches, PREG_SET_ORDER); $tokens = []; + $line = 1; foreach ($matches as $match) { - $tokens[] = [$match[0], (int) $match['MARK']]; + $type = (int) $match['MARK']; + $tokens[] = [$match[0], $type, $line]; + if ($type !== self::TOKEN_PHPDOC_EOL) { + continue; + } + + $line++; } - $tokens[] = ['', self::TOKEN_END]; + $tokens[] = ['', self::TOKEN_END, $line]; return $tokens; } @@ -149,8 +160,8 @@ class Lexer self::TOKEN_PHPDOC_TAG => '@(?:[a-z][a-z0-9-\\\\]+:)?[a-z][a-z0-9-\\\\]*+', self::TOKEN_PHPDOC_EOL => '\\r?+\\n[\\x09\\x20]*+(?:\\*(?!/)\\x20?+)?', - self::TOKEN_FLOAT => '(?:-?[0-9]++\\.[0-9]*+(?:e-?[0-9]++)?)|(?:-?[0-9]*+\\.[0-9]++(?:e-?[0-9]++)?)|(?:-?[0-9]++e-?[0-9]++)', - self::TOKEN_INTEGER => '-?(?:(?:0b[0-1]++)|(?:0o[0-7]++)|(?:0x[0-9a-f]++)|(?:[0-9]++))', + self::TOKEN_FLOAT => '(?:-?[0-9]++(_[0-9]++)*\\.[0-9]*(_[0-9]++)*+(?:e-?[0-9]++(_[0-9]++)*)?)|(?:-?[0-9]*+(_[0-9]++)*\\.[0-9]++(_[0-9]++)*(?:e-?[0-9]++(_[0-9]++)*)?)|(?:-?[0-9]++(_[0-9]++)*e-?[0-9]++(_[0-9]++)*)', + self::TOKEN_INTEGER => '-?(?:(?:0b[0-1]++(_[0-1]++)*)|(?:0o[0-7]++(_[0-7]++)*)|(?:0x[0-9a-f]++(_[0-9a-f]++)*)|(?:[0-9]++(_[0-9]++)*))', self::TOKEN_SINGLE_QUOTED_STRING => '\'(?:\\\\[^\\r\\n]|[^\'\\r\\n\\\\])*+\'', self::TOKEN_DOUBLE_QUOTED_STRING => '"(?:\\\\[^\\r\\n]|[^"\\r\\n\\\\])*+"', diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php b/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php index d7876303..b6db8a2c 100644 --- a/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php +++ b/vendor/phpstan/phpdoc-parser/src/Parser/ConstExprParser.php @@ -4,10 +4,6 @@ namespace PHPStan\PhpDocParser\Parser; use PHPStan\PhpDocParser\Ast; use PHPStan\PhpDocParser\Lexer\Lexer; -use function chr; -use function hexdec; -use function octdec; -use function preg_replace_callback; use function str_replace; use function strtolower; use function substr; @@ -15,49 +11,93 @@ use function substr; class ConstExprParser { - private const REPLACEMENTS = [ - '\\' => '\\', - 'n' => "\n", - 'r' => "\r", - 't' => "\t", - 'f' => "\f", - 'v' => "\v", - 'e' => "\x1B", - ]; - /** @var bool */ private $unescapeStrings; - public function __construct(bool $unescapeStrings = false) + /** @var bool */ + private $quoteAwareConstExprString; + + /** @var bool */ + private $useLinesAttributes; + + /** @var bool */ + private $useIndexAttributes; + + /** + * @param array{lines?: bool, indexes?: bool} $usedAttributes + */ + public function __construct( + bool $unescapeStrings = false, + bool $quoteAwareConstExprString = false, + array $usedAttributes = [] + ) { $this->unescapeStrings = $unescapeStrings; + $this->quoteAwareConstExprString = $quoteAwareConstExprString; + $this->useLinesAttributes = $usedAttributes['lines'] ?? false; + $this->useIndexAttributes = $usedAttributes['indexes'] ?? false; } public function parse(TokenIterator $tokens, bool $trimStrings = false): Ast\ConstExpr\ConstExprNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); if ($tokens->isCurrentTokenType(Lexer::TOKEN_FLOAT)) { $value = $tokens->currentTokenValue(); $tokens->next(); - return new Ast\ConstExpr\ConstExprFloatNode($value); + + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprFloatNode(str_replace('_', '', $value)), + $startLine, + $startIndex + ); } if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { $value = $tokens->currentTokenValue(); $tokens->next(); - return new Ast\ConstExpr\ConstExprIntegerNode($value); + + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $value)), + $startLine, + $startIndex + ); } if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING, Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { $value = $tokens->currentTokenValue(); + $type = $tokens->currentTokenType(); if ($trimStrings) { if ($this->unescapeStrings) { - $value = self::unescapeString($value); + $value = StringUnescaper::unescapeString($value); } else { $value = substr($value, 1, -1); } } $tokens->next(); - return new Ast\ConstExpr\ConstExprStringNode($value); + + if ($this->quoteAwareConstExprString) { + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\QuoteAwareConstExprStringNode( + $value, + $type === Lexer::TOKEN_SINGLE_QUOTED_STRING + ? Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED + : Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED + ), + $startLine, + $startIndex + ); + } + + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprStringNode($value), + $startLine, + $startIndex + ); } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { $identifier = $tokens->currentTokenValue(); @@ -65,14 +105,29 @@ class ConstExprParser switch (strtolower($identifier)) { case 'true': - return new Ast\ConstExpr\ConstExprTrueNode(); + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprTrueNode(), + $startLine, + $startIndex + ); case 'false': - return new Ast\ConstExpr\ConstExprFalseNode(); + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprFalseNode(), + $startLine, + $startIndex + ); case 'null': - return new Ast\ConstExpr\ConstExprNullNode(); + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprNullNode(), + $startLine, + $startIndex + ); case 'array': $tokens->consumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES); - return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES); + return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_PARENTHESES, $startIndex); } if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_COLON)) { @@ -106,29 +161,43 @@ class ConstExprParser break; } - return new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName); + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstFetchNode($identifier, $classConstantName), + $startLine, + $startIndex + ); } - return new Ast\ConstExpr\ConstFetchNode('', $identifier); + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstFetchNode('', $identifier), + $startLine, + $startIndex + ); } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET); + return $this->parseArray($tokens, Lexer::TOKEN_CLOSE_SQUARE_BRACKET, $startIndex); } throw new ParserException( $tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), - Lexer::TOKEN_IDENTIFIER + Lexer::TOKEN_IDENTIFIER, + null, + $tokens->currentTokenLine() ); } - private function parseArray(TokenIterator $tokens, int $endToken): Ast\ConstExpr\ConstExprArrayNode + private function parseArray(TokenIterator $tokens, int $endToken, int $startIndex): Ast\ConstExpr\ConstExprArrayNode { $items = []; + $startLine = $tokens->currentTokenLine(); + if (!$tokens->tryConsumeTokenType($endToken)) { do { $items[] = $this->parseArrayItem($tokens); @@ -136,12 +205,20 @@ class ConstExprParser $tokens->consumeTokenType($endToken); } - return new Ast\ConstExpr\ConstExprArrayNode($items); + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprArrayNode($items), + $startLine, + $startIndex + ); } private function parseArrayItem(TokenIterator $tokens): Ast\ConstExpr\ConstExprArrayItemNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $expr = $this->parse($tokens); if ($tokens->tryConsumeTokenType(Lexer::TOKEN_DOUBLE_ARROW)) { @@ -153,78 +230,40 @@ class ConstExprParser $value = $expr; } - return new Ast\ConstExpr\ConstExprArrayItemNode($key, $value); - } - - private static function unescapeString(string $string): string - { - $quote = $string[0]; - - if ($quote === '\'') { - return str_replace( - ['\\\\', '\\\''], - ['\\', '\''], - substr($string, 1, -1) - ); - } - - return self::parseEscapeSequences(substr($string, 1, -1), '"'); - } - - /** - * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130 - */ - private static function parseEscapeSequences(string $str, string $quote): string - { - $str = str_replace('\\' . $quote, $quote, $str); - - return preg_replace_callback( - '~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\{([0-9a-fA-F]+)\})~', - static function ($matches) { - $str = $matches[1]; - - if (isset(self::REPLACEMENTS[$str])) { - return self::REPLACEMENTS[$str]; - } - if ($str[0] === 'x' || $str[0] === 'X') { - return chr(hexdec(substr($str, 1))); - } - if ($str[0] === 'u') { - return self::codePointToUtf8(hexdec($matches[2])); - } - - return chr(octdec($str)); - }, - $str + return $this->enrichWithAttributes( + $tokens, + new Ast\ConstExpr\ConstExprArrayItemNode($key, $value), + $startLine, + $startIndex ); } /** - * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154 + * @template T of Ast\ConstExpr\ConstExprNode + * @param T $node + * @return T */ - private static function codePointToUtf8(int $num): string + private function enrichWithAttributes(TokenIterator $tokens, Ast\ConstExpr\ConstExprNode $node, int $startLine, int $startIndex): Ast\ConstExpr\ConstExprNode { - if ($num <= 0x7F) { - return chr($num); - } - if ($num <= 0x7FF) { - return chr(($num >> 6) + 0xC0) - . chr(($num & 0x3F) + 0x80); - } - if ($num <= 0xFFFF) { - return chr(($num >> 12) + 0xE0) - . chr((($num >> 6) & 0x3F) + 0x80) - . chr(($num & 0x3F) + 0x80); - } - if ($num <= 0x1FFFFF) { - return chr(($num >> 18) + 0xF0) - . chr((($num >> 12) & 0x3F) + 0x80) - . chr((($num >> 6) & 0x3F) + 0x80) - . chr(($num & 0x3F) + 0x80); + $endLine = $tokens->currentTokenLine(); + $endIndex = $tokens->currentTokenIndex(); + if ($this->useLinesAttributes) { + $node->setAttribute(Ast\Attribute::START_LINE, $startLine); + $node->setAttribute(Ast\Attribute::END_LINE, $endLine); } - // Invalid UTF-8 codepoint escape sequence: Codepoint too large - return "\xef\xbf\xbd"; + if ($this->useIndexAttributes) { + $tokensArray = $tokens->getTokens(); + $endIndex--; + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + $endIndex--; + } + + $node->setAttribute(Ast\Attribute::START_INDEX, $startIndex); + $node->setAttribute(Ast\Attribute::END_INDEX, $endIndex); + } + + return $node; } } diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php b/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php index badcdcbb..6ab5cc07 100644 --- a/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php +++ b/vendor/phpstan/phpdoc-parser/src/Parser/ParserException.php @@ -7,6 +7,7 @@ use PHPStan\PhpDocParser\Lexer\Lexer; use function assert; use function json_encode; use function sprintf; +use const JSON_INVALID_UTF8_SUBSTITUTE; use const JSON_UNESCAPED_SLASHES; use const JSON_UNESCAPED_UNICODE; @@ -28,12 +29,16 @@ class ParserException extends Exception /** @var string|null */ private $expectedTokenValue; + /** @var int|null */ + private $currentTokenLine; + public function __construct( string $currentTokenValue, int $currentTokenType, int $currentOffset, int $expectedTokenType, - ?string $expectedTokenValue = null + ?string $expectedTokenValue = null, + ?int $currentTokenLine = null ) { $this->currentTokenValue = $currentTokenValue; @@ -41,13 +46,15 @@ class ParserException extends Exception $this->currentOffset = $currentOffset; $this->expectedTokenType = $expectedTokenType; $this->expectedTokenValue = $expectedTokenValue; + $this->currentTokenLine = $currentTokenLine; parent::__construct(sprintf( - 'Unexpected token %s, expected %s%s at offset %d', + 'Unexpected token %s, expected %s%s at offset %d%s', $this->formatValue($currentTokenValue), Lexer::TOKEN_LABELS[$expectedTokenType], $expectedTokenValue !== null ? sprintf(' (%s)', $this->formatValue($expectedTokenValue)) : '', - $currentOffset + $currentOffset, + $currentTokenLine === null ? '' : sprintf(' on line %d', $currentTokenLine) )); } @@ -82,9 +89,15 @@ class ParserException extends Exception } + public function getCurrentTokenLine(): ?int + { + return $this->currentTokenLine; + } + + private function formatValue(string $value): string { - $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES); + $json = json_encode($value, JSON_UNESCAPED_UNICODE | JSON_UNESCAPED_SLASHES | JSON_INVALID_UTF8_SUBSTITUTE); assert($json !== false); return $json; diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php b/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php index d9942b3d..d3eed465 100644 --- a/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php +++ b/vendor/phpstan/phpdoc-parser/src/Parser/PhpDocParser.php @@ -28,11 +28,32 @@ class PhpDocParser /** @var bool */ private $requireWhitespaceBeforeDescription; - public function __construct(TypeParser $typeParser, ConstExprParser $constantExprParser, bool $requireWhitespaceBeforeDescription = false) + /** @var bool */ + private $preserveTypeAliasesWithInvalidTypes; + + /** @var bool */ + private $useLinesAttributes; + + /** @var bool */ + private $useIndexAttributes; + + /** + * @param array{lines?: bool, indexes?: bool} $usedAttributes + */ + public function __construct( + TypeParser $typeParser, + ConstExprParser $constantExprParser, + bool $requireWhitespaceBeforeDescription = false, + bool $preserveTypeAliasesWithInvalidTypes = false, + array $usedAttributes = [] + ) { $this->typeParser = $typeParser; $this->constantExprParser = $constantExprParser; $this->requireWhitespaceBeforeDescription = $requireWhitespaceBeforeDescription; + $this->preserveTypeAliasesWithInvalidTypes = $preserveTypeAliasesWithInvalidTypes; + $this->useLinesAttributes = $usedAttributes['lines'] ?? false; + $this->useIndexAttributes = $usedAttributes['indexes'] ?? false; } @@ -54,30 +75,82 @@ class PhpDocParser $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PHPDOC); } catch (ParserException $e) { $name = ''; + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); if (count($children) > 0) { $lastChild = $children[count($children) - 1]; if ($lastChild instanceof Ast\PhpDoc\PhpDocTagNode) { $name = $lastChild->name; + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); } } + + $tag = new Ast\PhpDoc\PhpDocTagNode( + $name, + $this->enrichWithAttributes( + $tokens, + new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e), + $startLine, + $startIndex + ) + ); + $tokens->forwardToTheEnd(); - return new Ast\PhpDoc\PhpDocNode([ - new Ast\PhpDoc\PhpDocTagNode($name, new Ast\PhpDoc\InvalidTagValueNode($e->getMessage(), $e)), - ]); + + return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode([$this->enrichWithAttributes($tokens, $tag, $startLine, $startIndex)]), 1, 0); } - return new Ast\PhpDoc\PhpDocNode(array_values($children)); + return $this->enrichWithAttributes($tokens, new Ast\PhpDoc\PhpDocNode(array_values($children)), 1, 0); } private function parseChild(TokenIterator $tokens): Ast\PhpDoc\PhpDocChildNode { if ($tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_TAG)) { - return $this->parseTag($tokens); - + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + return $this->enrichWithAttributes($tokens, $this->parseTag($tokens), $startLine, $startIndex); } - return $this->parseText($tokens); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $text = $this->parseText($tokens); + + return $this->enrichWithAttributes($tokens, $text, $startLine, $startIndex); + } + + /** + * @template T of Ast\Node + * @param T $tag + * @return T + */ + private function enrichWithAttributes(TokenIterator $tokens, Ast\Node $tag, int $startLine, int $startIndex): Ast\Node + { + $endLine = $tokens->currentTokenLine(); + $endIndex = $tokens->currentTokenIndex(); + + if ($this->useLinesAttributes) { + $tag->setAttribute(Ast\Attribute::START_LINE, $startLine); + $tag->setAttribute(Ast\Attribute::END_LINE, $endLine); + } + + if ($this->useIndexAttributes) { + $tokensArray = $tokens->getTokens(); + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_CLOSE_PHPDOC) { + $endIndex--; + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + $endIndex--; + } + } elseif ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_PHPDOC_EOL) { + $endIndex--; + } + + $tag->setAttribute(Ast\Attribute::START_INDEX, $startIndex); + $tag->setAttribute(Ast\Attribute::END_INDEX, $endIndex); + } + + return $tag; } @@ -120,6 +193,9 @@ class PhpDocParser public function parseTagValue(TokenIterator $tokens, string $tag): Ast\PhpDoc\PhpDocTagValueNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + try { $tokens->pushSavePoint(); @@ -247,7 +323,7 @@ class PhpDocParser $tagValue = new Ast\PhpDoc\InvalidTagValueNode($this->parseOptionalDescription($tokens), $e); } - return $tagValue; + return $this->enrichWithAttributes($tokens, $tagValue, $startLine, $startIndex); } @@ -257,9 +333,7 @@ class PhpDocParser private function parseParamTagValue(TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode { if ( - $tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE) - || $tokens->isCurrentTokenType(Lexer::TOKEN_VARIADIC) - || $tokens->isCurrentTokenType(Lexer::TOKEN_VARIABLE) + $tokens->isCurrentTokenType(Lexer::TOKEN_REFERENCE, Lexer::TOKEN_VARIADIC, Lexer::TOKEN_VARIABLE) ) { $type = null; } else { @@ -329,6 +403,8 @@ class PhpDocParser private function parseMethodTagValue(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueNode { $isStatic = $tokens->tryConsumeTokenValue('static'); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); $returnTypeOrMethodName = $this->typeParser->parse($tokens); if ($tokens->isCurrentTokenType(Lexer::TOKEN_IDENTIFIER)) { @@ -337,7 +413,9 @@ class PhpDocParser $tokens->next(); } elseif ($returnTypeOrMethodName instanceof Ast\Type\IdentifierTypeNode) { - $returnType = $isStatic ? new Ast\Type\IdentifierTypeNode('static') : null; + $returnType = $isStatic + ? $this->typeParser->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode('static'), $startLine, $startIndex) + : null; $methodName = $returnTypeOrMethodName->name; $isStatic = false; @@ -347,9 +425,12 @@ class PhpDocParser } $templateTypes = []; + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { do { - $templateTypes[] = $this->parseTemplateTagValue($tokens, false); + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $templateTypes[] = $this->enrichWithAttributes($tokens, $this->parseTemplateTagValue($tokens, false), $startLine, $startIndex); } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); } @@ -370,6 +451,9 @@ class PhpDocParser private function parseMethodTagValueParameter(TokenIterator $tokens): Ast\PhpDoc\MethodTagValueParameterNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + switch ($tokens->currentTokenType()) { case Lexer::TOKEN_IDENTIFIER: case Lexer::TOKEN_OPEN_PARENTHESES: @@ -394,7 +478,12 @@ class PhpDocParser $defaultValue = null; } - return new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue); + return $this->enrichWithAttributes( + $tokens, + new Ast\PhpDoc\MethodTagValueParameterNode($parameterType, $isReference, $isVariadic, $parameterName, $defaultValue), + $startLine, + $startIndex + ); } private function parseTemplateTagValue(TokenIterator $tokens, bool $parseDescription): Ast\PhpDoc\TemplateTagValueNode @@ -426,10 +515,15 @@ class PhpDocParser private function parseExtendsTagValue(string $tagName, TokenIterator $tokens): Ast\PhpDoc\PhpDocTagValueNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); $baseType = new IdentifierTypeNode($tokens->currentTokenValue()); $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); - $type = $this->typeParser->parseGeneric($tokens, $baseType); + $type = $this->typeParser->parseGeneric( + $tokens, + $this->typeParser->enrichWithAttributes($tokens, $baseType, $startLine, $startIndex) + ); $description = $this->parseOptionalDescription($tokens); @@ -453,6 +547,34 @@ class PhpDocParser // support psalm-type syntax $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); + if ($this->preserveTypeAliasesWithInvalidTypes) { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + try { + $type = $this->typeParser->parse($tokens); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_CLOSE_PHPDOC)) { + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_PHPDOC_EOL)) { + throw new ParserException( + $tokens->currentTokenValue(), + $tokens->currentTokenType(), + $tokens->currentTokenOffset(), + Lexer::TOKEN_PHPDOC_EOL, + null, + $tokens->currentTokenLine() + ); + } + } + + return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type); + } catch (ParserException $e) { + $this->parseOptionalDescription($tokens); + return new Ast\PhpDoc\TypeAliasTagValueNode( + $alias, + $this->enrichWithAttributes($tokens, new Ast\Type\InvalidTypeNode($e), $startLine, $startIndex) + ); + } + } + $type = $this->typeParser->parse($tokens); return new Ast\PhpDoc\TypeAliasTagValueNode($alias, $type); @@ -465,8 +587,16 @@ class PhpDocParser $tokens->consumeTokenValue(Lexer::TOKEN_IDENTIFIER, 'from'); + $identifierStartLine = $tokens->currentTokenLine(); + $identifierStartIndex = $tokens->currentTokenIndex(); $importedFrom = $tokens->currentTokenValue(); $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $importedFromType = $this->enrichWithAttributes( + $tokens, + new IdentifierTypeNode($importedFrom), + $identifierStartLine, + $identifierStartIndex + ); $importedAs = null; if ($tokens->tryConsumeTokenValue('as')) { @@ -474,7 +604,7 @@ class PhpDocParser $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); } - return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, new IdentifierTypeNode($importedFrom), $importedAs); + return new Ast\PhpDoc\TypeAliasImportTagValueNode($importedAlias, $importedFromType, $importedAs); } /** diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php b/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php new file mode 100644 index 00000000..70524055 --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Parser/StringUnescaper.php @@ -0,0 +1,96 @@ + '\\', + 'n' => "\n", + 'r' => "\r", + 't' => "\t", + 'f' => "\f", + 'v' => "\v", + 'e' => "\x1B", + ]; + + public static function unescapeString(string $string): string + { + $quote = $string[0]; + + if ($quote === '\'') { + return str_replace( + ['\\\\', '\\\''], + ['\\', '\''], + substr($string, 1, -1) + ); + } + + return self::parseEscapeSequences(substr($string, 1, -1), '"'); + } + + /** + * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L90-L130 + */ + private static function parseEscapeSequences(string $str, string $quote): string + { + $str = str_replace('\\' . $quote, $quote, $str); + + return preg_replace_callback( + '~\\\\([\\\\nrtfve]|[xX][0-9a-fA-F]{1,2}|[0-7]{1,3}|u\{([0-9a-fA-F]+)\})~', + static function ($matches) { + $str = $matches[1]; + + if (isset(self::REPLACEMENTS[$str])) { + return self::REPLACEMENTS[$str]; + } + if ($str[0] === 'x' || $str[0] === 'X') { + return chr((int) hexdec(substr($str, 1))); + } + if ($str[0] === 'u') { + return self::codePointToUtf8((int) hexdec($matches[2])); + } + + return chr((int) octdec($str)); + }, + $str + ); + } + + /** + * Implementation based on https://github.com/nikic/PHP-Parser/blob/b0edd4c41111042d43bb45c6c657b2e0db367d9e/lib/PhpParser/Node/Scalar/String_.php#L132-L154 + */ + private static function codePointToUtf8(int $num): string + { + if ($num <= 0x7F) { + return chr($num); + } + if ($num <= 0x7FF) { + return chr(($num >> 6) + 0xC0) + . chr(($num & 0x3F) + 0x80); + } + if ($num <= 0xFFFF) { + return chr(($num >> 12) + 0xE0) + . chr((($num >> 6) & 0x3F) + 0x80) + . chr(($num & 0x3F) + 0x80); + } + if ($num <= 0x1FFFFF) { + return chr(($num >> 18) + 0xF0) + . chr((($num >> 12) & 0x3F) + 0x80) + . chr((($num >> 6) & 0x3F) + 0x80) + . chr(($num & 0x3F) + 0x80); + } + + // Invalid UTF-8 codepoint escape sequence: Codepoint too large + return "\xef\xbf\xbd"; + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php b/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php index 569a9321..4348ab79 100644 --- a/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php +++ b/vendor/phpstan/phpdoc-parser/src/Parser/TokenIterator.php @@ -2,6 +2,7 @@ namespace PHPStan\PhpDocParser\Parser; +use LogicException; use PHPStan\PhpDocParser\Lexer\Lexer; use function array_pop; use function assert; @@ -12,7 +13,7 @@ use function strlen; class TokenIterator { - /** @var mixed[][] */ + /** @var list */ private $tokens; /** @var int */ @@ -21,6 +22,9 @@ class TokenIterator /** @var int[] */ private $savePoints = []; + /** + * @param list $tokens + */ public function __construct(array $tokens, int $index = 0) { $this->tokens = $tokens; @@ -34,6 +38,36 @@ class TokenIterator } + /** + * @return list + */ + public function getTokens(): array + { + return $this->tokens; + } + + + public function getContentBetween(int $startPos, int $endPos): string + { + if ($startPos < 0 || $endPos > count($this->tokens)) { + throw new LogicException(); + } + + $content = ''; + for ($i = $startPos; $i < $endPos; $i++) { + $content .= $this->tokens[$i][Lexer::VALUE_OFFSET]; + } + + return $content; + } + + + public function getTokenCount(): int + { + return count($this->tokens); + } + + public function currentTokenValue(): string { return $this->tokens[$this->index][Lexer::VALUE_OFFSET]; @@ -57,6 +91,18 @@ class TokenIterator } + public function currentTokenLine(): int + { + return $this->tokens[$this->index][Lexer::LINE_OFFSET]; + } + + + public function currentTokenIndex(): int + { + return $this->index; + } + + public function isCurrentTokenValue(string $tokenValue): bool { return $this->tokens[$this->index][Lexer::VALUE_OFFSET] === $tokenValue; @@ -217,8 +263,69 @@ class TokenIterator $this->currentTokenType(), $this->currentTokenOffset(), $expectedTokenType, - $expectedTokenValue + $expectedTokenValue, + $this->currentTokenLine() ); } + /** + * Check whether the position is directly preceded by a certain token type. + * + * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped + */ + public function hasTokenImmediatelyBefore(int $pos, int $expectedTokenType): bool + { + $tokens = $this->tokens; + $pos--; + for (; $pos >= 0; $pos--) { + $token = $tokens[$pos]; + $type = $token[Lexer::TYPE_OFFSET]; + if ($type === $expectedTokenType) { + return true; + } + if (!in_array($type, [ + Lexer::TOKEN_HORIZONTAL_WS, + Lexer::TOKEN_PHPDOC_EOL, + ], true)) { + break; + } + } + return false; + } + + /** + * Check whether the position is directly followed by a certain token type. + * + * During this check TOKEN_HORIZONTAL_WS and TOKEN_PHPDOC_EOL are skipped + */ + public function hasTokenImmediatelyAfter(int $pos, int $expectedTokenType): bool + { + $tokens = $this->tokens; + $pos++; + for ($c = count($tokens); $pos < $c; $pos++) { + $token = $tokens[$pos]; + $type = $token[Lexer::TYPE_OFFSET]; + if ($type === $expectedTokenType) { + return true; + } + if (!in_array($type, [ + Lexer::TOKEN_HORIZONTAL_WS, + Lexer::TOKEN_PHPDOC_EOL, + ], true)) { + break; + } + } + + return false; + } + + /** + * Whether the given position is immediately surrounded by parenthesis. + */ + public function hasParentheses(int $startPos, int $endPos): bool + { + return $this->hasTokenImmediatelyBefore($startPos, Lexer::TOKEN_OPEN_PARENTHESES) + && $this->hasTokenImmediatelyAfter($endPos, Lexer::TOKEN_CLOSE_PARENTHESES); + } + } diff --git a/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php b/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php index d196eb94..4b429809 100644 --- a/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php +++ b/vendor/phpstan/phpdoc-parser/src/Parser/TypeParser.php @@ -6,6 +6,7 @@ use LogicException; use PHPStan\PhpDocParser\Ast; use PHPStan\PhpDocParser\Lexer\Lexer; use function in_array; +use function str_replace; use function strpos; use function trim; @@ -15,14 +16,35 @@ class TypeParser /** @var ConstExprParser|null */ private $constExprParser; - public function __construct(?ConstExprParser $constExprParser = null) + /** @var bool */ + private $quoteAwareConstExprString; + + /** @var bool */ + private $useLinesAttributes; + + /** @var bool */ + private $useIndexAttributes; + + /** + * @param array{lines?: bool, indexes?: bool} $usedAttributes + */ + public function __construct( + ?ConstExprParser $constExprParser = null, + bool $quoteAwareConstExprString = false, + array $usedAttributes = [] + ) { $this->constExprParser = $constExprParser; + $this->quoteAwareConstExprString = $quoteAwareConstExprString; + $this->useLinesAttributes = $usedAttributes['lines'] ?? false; + $this->useIndexAttributes = $usedAttributes['indexes'] ?? false; } /** @phpstan-impure */ public function parse(TokenIterator $tokens): Ast\Type\TypeNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { $type = $this->parseNullable($tokens); @@ -37,12 +59,45 @@ class TypeParser } } + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + + /** + * @internal + * @template T of Ast\Node + * @param T $type + * @return T + */ + public function enrichWithAttributes(TokenIterator $tokens, Ast\Node $type, int $startLine, int $startIndex): Ast\Node + { + $endLine = $tokens->currentTokenLine(); + $endIndex = $tokens->currentTokenIndex(); + + if ($this->useLinesAttributes) { + $type->setAttribute(Ast\Attribute::START_LINE, $startLine); + $type->setAttribute(Ast\Attribute::END_LINE, $endLine); + } + + if ($this->useIndexAttributes) { + $tokensArray = $tokens->getTokens(); + $endIndex--; + if ($tokensArray[$endIndex][Lexer::TYPE_OFFSET] === Lexer::TOKEN_HORIZONTAL_WS) { + $endIndex--; + } + + $type->setAttribute(Ast\Attribute::START_INDEX, $startIndex); + $type->setAttribute(Ast\Attribute::END_INDEX, $endIndex); + } + return $type; } /** @phpstan-impure */ private function subParse(TokenIterator $tokens): Ast\Type\TypeNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { $type = $this->parseNullable($tokens); @@ -66,13 +121,16 @@ class TypeParser } } - return $type; + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); } /** @phpstan-impure */ private function parseAtomic(TokenIterator $tokens): Ast\Type\TypeNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); $type = $this->subParse($tokens); @@ -81,26 +139,26 @@ class TypeParser $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - return $this->tryParseArrayOrOffsetAccess($tokens, $type); + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); } - return $type; + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); } if ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { - $type = new Ast\Type\ThisTypeNode(); + $type = $this->enrichWithAttributes($tokens, new Ast\Type\ThisTypeNode(), $startLine, $startIndex); if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - return $this->tryParseArrayOrOffsetAccess($tokens, $type); + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); } - return $type; + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); } $currentTokenValue = $tokens->currentTokenValue(); $tokens->pushSavePoint(); // because of ConstFetchNode if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { - $type = new Ast\Type\IdentifierTypeNode($currentTokenValue); + $type = $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode($currentTokenValue), $startLine, $startIndex); if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { $tokens->dropSavePoint(); // because of ConstFetchNode @@ -124,15 +182,22 @@ class TypeParser } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); - } elseif (in_array($type->name, ['array', 'list'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { - $type = $this->parseArrayShape($tokens, $type, $type->name); + } elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { + if ($type->name === 'object') { + $type = $this->parseObjectShape($tokens); + } else { + $type = $this->parseArrayShape($tokens, $type, $type->name); + } if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + $type = $this->tryParseArrayOrOffsetAccess( + $tokens, + $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex) + ); } } - return $type; + return $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); } else { $tokens->rollback(); // because of ConstFetchNode } @@ -144,7 +209,9 @@ class TypeParser $tokens->currentTokenValue(), $tokens->currentTokenType(), $tokens->currentTokenOffset(), - Lexer::TOKEN_IDENTIFIER + Lexer::TOKEN_IDENTIFIER, + null, + $tokens->currentTokenLine() ); if ($this->constExprParser === null) { @@ -157,7 +224,7 @@ class TypeParser throw $exception; } - return new Ast\Type\ConstTypeNode($constExpr); + return $this->enrichWithAttributes($tokens, new Ast\Type\ConstTypeNode($constExpr), $startLine, $startIndex); } catch (LogicException $e) { throw $exception; } @@ -336,7 +403,14 @@ class TypeParser $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET)) { // trailing comma case - return new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); + $type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); + $startLine = $baseType->getAttribute(Ast\Attribute::START_LINE); + $startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX); + if ($startLine !== null && $startIndex !== null) { + $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + + return $type; } [$genericTypes[], $variances[]] = $this->parseGenericTypeArgument($tokens); $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); @@ -345,7 +419,14 @@ class TypeParser $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_ANGLE_BRACKET); - return new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); + $type = new Ast\Type\GenericTypeNode($baseType, $genericTypes, $variances); + $startLine = $baseType->getAttribute(Ast\Attribute::START_LINE); + $startIndex = $baseType->getAttribute(Ast\Attribute::START_INDEX); + if ($startLine !== null && $startIndex !== null) { + $type = $this->enrichWithAttributes($tokens, $type, $startLine, $startIndex); + } + + return $type; } @@ -355,9 +436,11 @@ class TypeParser */ public function parseGenericTypeArgument(TokenIterator $tokens): array { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); if ($tokens->tryConsumeTokenType(Lexer::TOKEN_WILDCARD)) { return [ - new Ast\Type\IdentifierTypeNode('mixed'), + $this->enrichWithAttributes($tokens, new Ast\Type\IdentifierTypeNode('mixed'), $startLine, $startIndex), Ast\Type\GenericTypeNode::VARIANCE_BIVARIANT, ]; } @@ -397,7 +480,10 @@ class TypeParser $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); $tokens->consumeTokenType(Lexer::TOKEN_COLON); - $returnType = $this->parseCallableReturnType($tokens); + + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + $returnType = $this->enrichWithAttributes($tokens, $this->parseCallableReturnType($tokens), $startLine, $startIndex); return new Ast\Type\CallableTypeNode($identifier, $parameters, $returnType); } @@ -406,6 +492,8 @@ class TypeParser /** @phpstan-impure */ private function parseCallableParameter(TokenIterator $tokens): Ast\Type\CallableTypeParameterNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); $type = $this->parse($tokens); $isReference = $tokens->tryConsumeTokenType(Lexer::TOKEN_REFERENCE); $isVariadic = $tokens->tryConsumeTokenType(Lexer::TOKEN_VARIADIC); @@ -419,37 +507,141 @@ class TypeParser } $isOptional = $tokens->tryConsumeTokenType(Lexer::TOKEN_EQUAL); - return new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional); + return $this->enrichWithAttributes( + $tokens, + new Ast\Type\CallableTypeParameterNode($type, $isReference, $isVariadic, $parameterName, $isOptional), + $startLine, + $startIndex + ); } /** @phpstan-impure */ private function parseCallableReturnType(TokenIterator $tokens): Ast\Type\TypeNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); if ($tokens->isCurrentTokenType(Lexer::TOKEN_NULLABLE)) { - $type = $this->parseNullable($tokens); + return $this->parseNullable($tokens); } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_OPEN_PARENTHESES)) { $type = $this->parse($tokens); $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_PARENTHESES); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + } + return $type; + } elseif ($tokens->tryConsumeTokenType(Lexer::TOKEN_THIS_VARIABLE)) { + $type = new Ast\Type\ThisTypeNode(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + )); + } + + return $type; } else { - $type = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); - $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + $currentTokenValue = $tokens->currentTokenValue(); + $tokens->pushSavePoint(); // because of ConstFetchNode + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_IDENTIFIER)) { + $type = new Ast\Type\IdentifierTypeNode($currentTokenValue); - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { - $type = $this->parseGeneric($tokens, $type); + if (!$tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_COLON)) { + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_ANGLE_BRACKET)) { + $type = $this->parseGeneric( + $tokens, + $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + ) + ); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + )); + } - } elseif (in_array($type->name, ['array', 'list'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { - $type = $this->parseArrayShape($tokens, $type, $type->name); + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + )); + + } elseif (in_array($type->name, ['array', 'list', 'object'], true) && $tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET) && !$tokens->isPrecededByHorizontalWhitespace()) { + if ($type->name === 'object') { + $type = $this->parseObjectShape($tokens); + } else { + $type = $this->parseArrayShape($tokens, $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + ), $type->name); + } + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + )); + } + } + + return $type; + } else { + $tokens->rollback(); // because of ConstFetchNode + } + } else { + $tokens->dropSavePoint(); // because of ConstFetchNode } } - if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { - $type = $this->tryParseArrayOrOffsetAccess($tokens, $type); + $exception = new ParserException( + $tokens->currentTokenValue(), + $tokens->currentTokenType(), + $tokens->currentTokenOffset(), + Lexer::TOKEN_IDENTIFIER, + null, + $tokens->currentTokenLine() + ); + + if ($this->constExprParser === null) { + throw $exception; } - return $type; + try { + $constExpr = $this->constExprParser->parse($tokens, true); + if ($constExpr instanceof Ast\ConstExpr\ConstExprArrayNode) { + throw $exception; + } + + $type = new Ast\Type\ConstTypeNode($constExpr); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { + $type = $this->tryParseArrayOrOffsetAccess($tokens, $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + )); + } + + return $type; + } catch (LogicException $e) { + throw $exception; + } } @@ -473,6 +665,8 @@ class TypeParser /** @phpstan-impure */ private function tryParseArrayOrOffsetAccess(TokenIterator $tokens, Ast\Type\TypeNode $type): Ast\Type\TypeNode { + $startLine = $type->getAttribute(Ast\Attribute::START_LINE); + $startIndex = $type->getAttribute(Ast\Attribute::START_INDEX); try { while ($tokens->isCurrentTokenType(Lexer::TOKEN_OPEN_SQUARE_BRACKET)) { $tokens->pushSavePoint(); @@ -485,10 +679,28 @@ class TypeParser $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET); $tokens->dropSavePoint(); $type = new Ast\Type\OffsetAccessTypeNode($type, $offset); + + if ($startLine !== null && $startIndex !== null) { + $type = $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + ); + } } else { $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_SQUARE_BRACKET); $tokens->dropSavePoint(); $type = new Ast\Type\ArrayTypeNode($type); + + if ($startLine !== null && $startIndex !== null) { + $type = $this->enrichWithAttributes( + $tokens, + $type, + $startLine, + $startIndex + ); + } } } @@ -539,6 +751,8 @@ class TypeParser /** @phpstan-impure */ private function parseArrayShapeItem(TokenIterator $tokens): Ast\Type\ArrayShapeItemNode { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); try { $tokens->pushSavePoint(); $key = $this->parseArrayShapeKey($tokens); @@ -547,12 +761,22 @@ class TypeParser $value = $this->parse($tokens); $tokens->dropSavePoint(); - return new Ast\Type\ArrayShapeItemNode($key, $optional, $value); + return $this->enrichWithAttributes( + $tokens, + new Ast\Type\ArrayShapeItemNode($key, $optional, $value), + $startLine, + $startIndex + ); } catch (ParserException $e) { $tokens->rollback(); $value = $this->parse($tokens); - return new Ast\Type\ArrayShapeItemNode(null, false, $value); + return $this->enrichWithAttributes( + $tokens, + new Ast\Type\ArrayShapeItemNode(null, false, $value), + $startLine, + $startIndex + ); } } @@ -562,16 +786,28 @@ class TypeParser */ private function parseArrayShapeKey(TokenIterator $tokens) { + $startIndex = $tokens->currentTokenIndex(); + $startLine = $tokens->currentTokenLine(); + if ($tokens->isCurrentTokenType(Lexer::TOKEN_INTEGER)) { - $key = new Ast\ConstExpr\ConstExprIntegerNode($tokens->currentTokenValue()); + $key = new Ast\ConstExpr\ConstExprIntegerNode(str_replace('_', '', $tokens->currentTokenValue())); $tokens->next(); } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { - $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); + } $tokens->next(); } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { - $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); + } + $tokens->next(); } else { @@ -579,7 +815,86 @@ class TypeParser $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); } - return $key; + return $this->enrichWithAttributes( + $tokens, + $key, + $startLine, + $startIndex + ); + } + + /** + * @phpstan-impure + */ + private function parseObjectShape(TokenIterator $tokens): Ast\Type\ObjectShapeNode + { + $tokens->consumeTokenType(Lexer::TOKEN_OPEN_CURLY_BRACKET); + + $items = []; + + do { + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + + if ($tokens->tryConsumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET)) { + return new Ast\Type\ObjectShapeNode($items); + } + + $items[] = $this->parseObjectShapeItem($tokens); + + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + } while ($tokens->tryConsumeTokenType(Lexer::TOKEN_COMMA)); + + $tokens->tryConsumeTokenType(Lexer::TOKEN_PHPDOC_EOL); + $tokens->consumeTokenType(Lexer::TOKEN_CLOSE_CURLY_BRACKET); + + return new Ast\Type\ObjectShapeNode($items); + } + + /** @phpstan-impure */ + private function parseObjectShapeItem(TokenIterator $tokens): Ast\Type\ObjectShapeItemNode + { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + + $key = $this->parseObjectShapeKey($tokens); + $optional = $tokens->tryConsumeTokenType(Lexer::TOKEN_NULLABLE); + $tokens->consumeTokenType(Lexer::TOKEN_COLON); + $value = $this->parse($tokens); + + return $this->enrichWithAttributes($tokens, new Ast\Type\ObjectShapeItemNode($key, $optional, $value), $startLine, $startIndex); + } + + /** + * @phpstan-impure + * @return Ast\ConstExpr\ConstExprStringNode|Ast\Type\IdentifierTypeNode + */ + private function parseObjectShapeKey(TokenIterator $tokens) + { + $startLine = $tokens->currentTokenLine(); + $startIndex = $tokens->currentTokenIndex(); + + if ($tokens->isCurrentTokenType(Lexer::TOKEN_SINGLE_QUOTED_STRING)) { + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::SINGLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), "'")); + } + $tokens->next(); + + } elseif ($tokens->isCurrentTokenType(Lexer::TOKEN_DOUBLE_QUOTED_STRING)) { + if ($this->quoteAwareConstExprString) { + $key = new Ast\ConstExpr\QuoteAwareConstExprStringNode(StringUnescaper::unescapeString($tokens->currentTokenValue()), Ast\ConstExpr\QuoteAwareConstExprStringNode::DOUBLE_QUOTED); + } else { + $key = new Ast\ConstExpr\ConstExprStringNode(trim($tokens->currentTokenValue(), '"')); + } + $tokens->next(); + + } else { + $key = new Ast\Type\IdentifierTypeNode($tokens->currentTokenValue()); + $tokens->consumeTokenType(Lexer::TOKEN_IDENTIFIER); + } + + return $this->enrichWithAttributes($tokens, $key, $startLine, $startIndex); } } diff --git a/vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php b/vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php new file mode 100644 index 00000000..2684dfc7 --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Printer/DiffElem.php @@ -0,0 +1,44 @@ +type = $type; + $this->old = $old; + $this->new = $new; + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Printer/Differ.php b/vendor/phpstan/phpdoc-parser/src/Printer/Differ.php new file mode 100644 index 00000000..ab10be59 --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Printer/Differ.php @@ -0,0 +1,196 @@ +isEqual = $isEqual; + } + + /** + * Calculate diff (edit script) from $old to $new. + * + * @param T[] $old Original array + * @param T[] $new New array + * + * @return DiffElem[] Diff (edit script) + */ + public function diff(array $old, array $new): array + { + [$trace, $x, $y] = $this->calculateTrace($old, $new); + return $this->extractDiff($trace, $x, $y, $old, $new); + } + + /** + * Calculate diff, including "replace" operations. + * + * If a sequence of remove operations is followed by the same number of add operations, these + * will be coalesced into replace operations. + * + * @param T[] $old Original array + * @param T[] $new New array + * + * @return DiffElem[] Diff (edit script), including replace operations + */ + public function diffWithReplacements(array $old, array $new): array + { + return $this->coalesceReplacements($this->diff($old, $new)); + } + + /** + * @param T[] $old + * @param T[] $new + * @return array{array>, int, int} + */ + private function calculateTrace(array $old, array $new): array + { + $n = count($old); + $m = count($new); + $max = $n + $m; + $v = [1 => 0]; + $trace = []; + for ($d = 0; $d <= $max; $d++) { + $trace[] = $v; + for ($k = -$d; $k <= $d; $k += 2) { + if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) { + $x = $v[$k + 1]; + } else { + $x = $v[$k - 1] + 1; + } + + $y = $x - $k; + while ($x < $n && $y < $m && ($this->isEqual)($old[$x], $new[$y])) { + $x++; + $y++; + } + + $v[$k] = $x; + if ($x >= $n && $y >= $m) { + return [$trace, $x, $y]; + } + } + } + throw new Exception('Should not happen'); + } + + /** + * @param array> $trace + * @param T[] $old + * @param T[] $new + * @return DiffElem[] + */ + private function extractDiff(array $trace, int $x, int $y, array $old, array $new): array + { + $result = []; + for ($d = count($trace) - 1; $d >= 0; $d--) { + $v = $trace[$d]; + $k = $x - $y; + + if ($k === -$d || ($k !== $d && $v[$k - 1] < $v[$k + 1])) { + $prevK = $k + 1; + } else { + $prevK = $k - 1; + } + + $prevX = $v[$prevK]; + $prevY = $prevX - $prevK; + + while ($x > $prevX && $y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_KEEP, $old[$x - 1], $new[$y - 1]); + $x--; + $y--; + } + + if ($d === 0) { + break; + } + + while ($x > $prevX) { + $result[] = new DiffElem(DiffElem::TYPE_REMOVE, $old[$x - 1], null); + $x--; + } + + while ($y > $prevY) { + $result[] = new DiffElem(DiffElem::TYPE_ADD, null, $new[$y - 1]); + $y--; + } + } + return array_reverse($result); + } + + /** + * Coalesce equal-length sequences of remove+add into a replace operation. + * + * @param DiffElem[] $diff + * @return DiffElem[] + */ + private function coalesceReplacements(array $diff): array + { + $newDiff = []; + $c = count($diff); + for ($i = 0; $i < $c; $i++) { + $diffType = $diff[$i]->type; + if ($diffType !== DiffElem::TYPE_REMOVE) { + $newDiff[] = $diff[$i]; + continue; + } + + $j = $i; + while ($j < $c && $diff[$j]->type === DiffElem::TYPE_REMOVE) { + $j++; + } + + $k = $j; + while ($k < $c && $diff[$k]->type === DiffElem::TYPE_ADD) { + $k++; + } + + if ($j - $i === $k - $j) { + $len = $j - $i; + for ($n = 0; $n < $len; $n++) { + $newDiff[] = new DiffElem( + DiffElem::TYPE_REPLACE, + $diff[$i + $n]->old, + $diff[$j + $n]->new + ); + } + } else { + for (; $i < $k; $i++) { + $newDiff[] = $diff[$i]; + } + } + $i = $k - 1; + } + return $newDiff; + } + +} diff --git a/vendor/phpstan/phpdoc-parser/src/Printer/Printer.php b/vendor/phpstan/phpdoc-parser/src/Printer/Printer.php new file mode 100644 index 00000000..bc07d10c --- /dev/null +++ b/vendor/phpstan/phpdoc-parser/src/Printer/Printer.php @@ -0,0 +1,796 @@ + */ + private $differ; + + /** + * Map From "{$class}->{$subNode}" to string that should be inserted + * between elements of this list subnode + * + * @var array + */ + private $listInsertionMap = [ + PhpDocNode::class . '->children' => "\n * ", + UnionTypeNode::class . '->types' => '|', + IntersectionTypeNode::class . '->types' => '&', + ArrayShapeNode::class . '->items' => ', ', + ObjectShapeNode::class . '->items' => ', ', + CallableTypeNode::class . '->parameters' => ', ', + GenericTypeNode::class . '->genericTypes' => ', ', + ConstExprArrayNode::class . '->items' => ', ', + MethodTagValueNode::class . '->parameters' => ', ', + ]; + + /** + * [$find, $extraLeft, $extraRight] + * + * @var array + */ + private $emptyListInsertionMap = [ + CallableTypeNode::class . '->parameters' => ['(', '', ''], + ArrayShapeNode::class . '->items' => ['{', '', ''], + ObjectShapeNode::class . '->items' => ['{', '', ''], + ]; + + /** @var array>> */ + private $parenthesesMap = [ + CallableTypeNode::class . '->returnType' => [ + CallableTypeNode::class, + UnionTypeNode::class, + IntersectionTypeNode::class, + ], + ArrayTypeNode::class . '->type' => [ + CallableTypeNode::class, + UnionTypeNode::class, + IntersectionTypeNode::class, + ConstTypeNode::class, + NullableTypeNode::class, + ], + OffsetAccessTypeNode::class . '->type' => [ + CallableTypeNode::class, + UnionTypeNode::class, + IntersectionTypeNode::class, + ConstTypeNode::class, + NullableTypeNode::class, + ], + ]; + + /** @var array>> */ + private $parenthesesListMap = [ + IntersectionTypeNode::class . '->types' => [ + IntersectionTypeNode::class, + UnionTypeNode::class, + NullableTypeNode::class, + ], + UnionTypeNode::class . '->types' => [ + IntersectionTypeNode::class, + UnionTypeNode::class, + NullableTypeNode::class, + ], + ]; + + public function printFormatPreserving(PhpDocNode $node, PhpDocNode $originalNode, TokenIterator $originalTokens): string + { + $this->differ = new Differ(static function ($a, $b) { + if ($a instanceof Node && $b instanceof Node) { + return $a === $b->getAttribute(Attribute::ORIGINAL_NODE); + } + + return false; + }); + + $tokenIndex = 0; + $result = $this->printArrayFormatPreserving( + $node->children, + $originalNode->children, + $originalTokens, + $tokenIndex, + PhpDocNode::class, + 'children' + ); + if ($result !== null) { + return $result . $originalTokens->getContentBetween($tokenIndex, $originalTokens->getTokenCount()); + } + + return $this->print($node); + } + + public function print(Node $node): string + { + if ($node instanceof PhpDocNode) { + return "/**\n *" . implode("\n *", array_map( + function (PhpDocChildNode $child): string { + $s = $this->print($child); + return $s === '' ? '' : ' ' . $s; + }, + $node->children + )) . "\n */"; + } + if ($node instanceof PhpDocTextNode) { + return $node->text; + } + if ($node instanceof PhpDocTagNode) { + return trim(sprintf('%s %s', $node->name, $this->print($node->value))); + } + if ($node instanceof PhpDocTagValueNode) { + return $this->printTagValue($node); + } + if ($node instanceof TypeNode) { + return $this->printType($node); + } + if ($node instanceof ConstExprNode) { + return $this->printConstExpr($node); + } + if ($node instanceof MethodTagValueParameterNode) { + $type = $node->type !== null ? $this->print($node->type) . ' ' : ''; + $isReference = $node->isReference ? '&' : ''; + $isVariadic = $node->isVariadic ? '...' : ''; + $default = $node->defaultValue !== null ? ' = ' . $this->print($node->defaultValue) : ''; + return "{$type}{$isReference}{$isVariadic}{$node->parameterName}{$default}"; + } + if ($node instanceof CallableTypeParameterNode) { + $type = $this->print($node->type) . ' '; + $isReference = $node->isReference ? '&' : ''; + $isVariadic = $node->isVariadic ? '...' : ''; + $isOptional = $node->isOptional ? '=' : ''; + return trim("{$type}{$isReference}{$isVariadic}{$node->parameterName}") . $isOptional; + } + + throw new LogicException(sprintf('Unknown node type %s', get_class($node))); + } + + private function printTagValue(PhpDocTagValueNode $node): string + { + // only nodes that contain another node are handled here + // the rest falls back on (string) $node + + if ($node instanceof AssertTagMethodValueNode) { + $isNegated = $node->isNegated ? '!' : ''; + $isEquality = $node->isEquality ? '=' : ''; + $type = $this->printType($node->type); + return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->method}() {$node->description}"); + } + if ($node instanceof AssertTagPropertyValueNode) { + $isNegated = $node->isNegated ? '!' : ''; + $isEquality = $node->isEquality ? '=' : ''; + $type = $this->printType($node->type); + return trim("{$isNegated}{$isEquality}{$type} {$node->parameter}->{$node->property} {$node->description}"); + } + if ($node instanceof AssertTagValueNode) { + $isNegated = $node->isNegated ? '!' : ''; + $isEquality = $node->isEquality ? '=' : ''; + $type = $this->printType($node->type); + return trim("{$isNegated}{$isEquality}{$type} {$node->parameter} {$node->description}"); + } + if ($node instanceof ExtendsTagValueNode || $node instanceof ImplementsTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof MethodTagValueNode) { + $static = $node->isStatic ? 'static ' : ''; + $returnType = $node->returnType !== null ? $this->printType($node->returnType) . ' ' : ''; + $parameters = implode(', ', array_map(function (MethodTagValueParameterNode $parameter): string { + return $this->print($parameter); + }, $node->parameters)); + $description = $node->description !== '' ? " {$node->description}" : ''; + $templateTypes = count($node->templateTypes) > 0 ? '<' . implode(', ', array_map(function (TemplateTagValueNode $templateTag): string { + return $this->print($templateTag); + }, $node->templateTypes)) . '>' : ''; + return "{$static}{$returnType}{$node->methodName}{$templateTypes}({$parameters}){$description}"; + } + if ($node instanceof MixinTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof ParamOutTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->parameterName} {$node->description}"); + } + if ($node instanceof ParamTagValueNode) { + $reference = $node->isReference ? '&' : ''; + $variadic = $node->isVariadic ? '...' : ''; + $type = $this->printType($node->type); + return trim("{$type} {$reference}{$variadic}{$node->parameterName} {$node->description}"); + } + if ($node instanceof PropertyTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->propertyName} {$node->description}"); + } + if ($node instanceof ReturnTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof SelfOutTagValueNode) { + $type = $this->printType($node->type); + return trim($type . ' ' . $node->description); + } + if ($node instanceof TemplateTagValueNode) { + $bound = $node->bound !== null ? ' of ' . $this->printType($node->bound) : ''; + $default = $node->default !== null ? ' = ' . $this->printType($node->default) : ''; + return trim("{$node->name}{$bound}{$default} {$node->description}"); + } + if ($node instanceof ThrowsTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof TypeAliasImportTagValueNode) { + return trim( + "{$node->importedAlias} from " . $this->printType($node->importedFrom) + . ($node->importedAs !== null ? " as {$node->importedAs}" : '') + ); + } + if ($node instanceof TypeAliasTagValueNode) { + $type = $this->printType($node->type); + return trim("{$node->alias} {$type}"); + } + if ($node instanceof UsesTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} {$node->description}"); + } + if ($node instanceof VarTagValueNode) { + $type = $this->printType($node->type); + return trim("{$type} " . trim("{$node->variableName} {$node->description}")); + } + + return (string) $node; + } + + private function printType(TypeNode $node): string + { + if ($node instanceof ArrayShapeNode) { + $items = array_map(function (ArrayShapeItemNode $item): string { + return $this->printType($item); + }, $node->items); + + if (! $node->sealed) { + $items[] = '...'; + } + + return $node->kind . '{' . implode(', ', $items) . '}'; + } + if ($node instanceof ArrayShapeItemNode) { + if ($node->keyName !== null) { + return sprintf( + '%s%s: %s', + $this->print($node->keyName), + $node->optional ? '?' : '', + $this->printType($node->valueType) + ); + } + + return $this->printType($node->valueType); + } + if ($node instanceof ArrayTypeNode) { + return $this->printOffsetAccessType($node->type) . '[]'; + } + if ($node instanceof CallableTypeNode) { + if ($node->returnType instanceof CallableTypeNode || $node->returnType instanceof UnionTypeNode || $node->returnType instanceof IntersectionTypeNode) { + $returnType = $this->wrapInParentheses($node->returnType); + } else { + $returnType = $this->printType($node->returnType); + } + $parameters = implode(', ', array_map(function (CallableTypeParameterNode $parameterNode): string { + return $this->print($parameterNode); + }, $node->parameters)); + return "{$node->identifier}({$parameters}): {$returnType}"; + } + if ($node instanceof ConditionalTypeForParameterNode) { + return sprintf( + '(%s %s %s ? %s : %s)', + $node->parameterName, + $node->negated ? 'is not' : 'is', + $this->printType($node->targetType), + $this->printType($node->if), + $this->printType($node->else) + ); + } + if ($node instanceof ConditionalTypeNode) { + return sprintf( + '(%s %s %s ? %s : %s)', + $this->printType($node->subjectType), + $node->negated ? 'is not' : 'is', + $this->printType($node->targetType), + $this->printType($node->if), + $this->printType($node->else) + ); + } + if ($node instanceof ConstTypeNode) { + return $this->printConstExpr($node->constExpr); + } + if ($node instanceof GenericTypeNode) { + $genericTypes = []; + + foreach ($node->genericTypes as $index => $type) { + $variance = $node->variances[$index] ?? GenericTypeNode::VARIANCE_INVARIANT; + if ($variance === GenericTypeNode::VARIANCE_INVARIANT) { + $genericTypes[] = $this->printType($type); + } elseif ($variance === GenericTypeNode::VARIANCE_BIVARIANT) { + $genericTypes[] = '*'; + } else { + $genericTypes[] = sprintf('%s %s', $variance, $this->print($type)); + } + } + + return $node->type . '<' . implode(', ', $genericTypes) . '>'; + } + if ($node instanceof IdentifierTypeNode) { + return $node->name; + } + if ($node instanceof IntersectionTypeNode || $node instanceof UnionTypeNode) { + $items = []; + foreach ($node->types as $type) { + if ( + $type instanceof IntersectionTypeNode + || $type instanceof UnionTypeNode + || $type instanceof NullableTypeNode + ) { + $items[] = $this->wrapInParentheses($type); + continue; + } + + $items[] = $this->printType($type); + } + + return implode($node instanceof IntersectionTypeNode ? '&' : '|', $items); + } + if ($node instanceof InvalidTypeNode) { + return (string) $node; + } + if ($node instanceof NullableTypeNode) { + if ($node->type instanceof IntersectionTypeNode || $node->type instanceof UnionTypeNode) { + return '?(' . $this->printType($node->type) . ')'; + } + + return '?' . $this->printType($node->type); + } + if ($node instanceof ObjectShapeNode) { + $items = array_map(function (ObjectShapeItemNode $item): string { + return $this->printType($item); + }, $node->items); + + return 'object{' . implode(', ', $items) . '}'; + } + if ($node instanceof ObjectShapeItemNode) { + if ($node->keyName !== null) { + return sprintf( + '%s%s: %s', + $this->print($node->keyName), + $node->optional ? '?' : '', + $this->printType($node->valueType) + ); + } + + return $this->printType($node->valueType); + } + if ($node instanceof OffsetAccessTypeNode) { + return $this->printOffsetAccessType($node->type) . '[' . $this->printType($node->offset) . ']'; + } + if ($node instanceof ThisTypeNode) { + return (string) $node; + } + + throw new LogicException(sprintf('Unknown node type %s', get_class($node))); + } + + private function wrapInParentheses(TypeNode $node): string + { + return '(' . $this->printType($node) . ')'; + } + + private function printOffsetAccessType(TypeNode $type): string + { + if ( + $type instanceof CallableTypeNode + || $type instanceof UnionTypeNode + || $type instanceof IntersectionTypeNode + || $type instanceof ConstTypeNode + || $type instanceof NullableTypeNode + ) { + return $this->wrapInParentheses($type); + } + + return $this->printType($type); + } + + private function printConstExpr(ConstExprNode $node): string + { + // this is fine - ConstExprNode classes do not contain nodes that need smart printer logic + return (string) $node; + } + + /** + * @param Node[] $nodes + * @param Node[] $originalNodes + */ + private function printArrayFormatPreserving(array $nodes, array $originalNodes, TokenIterator $originalTokens, int &$tokenIndex, string $parentNodeClass, string $subNodeName): ?string + { + $diff = $this->differ->diffWithReplacements($originalNodes, $nodes); + $mapKey = $parentNodeClass . '->' . $subNodeName; + $insertStr = $this->listInsertionMap[$mapKey] ?? null; + $result = ''; + $beforeFirstKeepOrReplace = true; + $delayedAdd = []; + + $insertNewline = false; + [$isMultiline, $beforeAsteriskIndent, $afterAsteriskIndent] = $this->isMultiline($tokenIndex, $originalNodes, $originalTokens); + + if ($insertStr === "\n * ") { + $insertStr = sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent); + } + + foreach ($diff as $i => $diffElem) { + $diffType = $diffElem->type; + $newNode = $diffElem->new; + $originalNode = $diffElem->old; + if ($diffType === DiffElem::TYPE_KEEP || $diffType === DiffElem::TYPE_REPLACE) { + $beforeFirstKeepOrReplace = false; + if (!$newNode instanceof Node || !$originalNode instanceof Node) { + return null; + } + $itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX); + $itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX); + if ($itemStartPos < 0 || $itemEndPos < 0 || $itemStartPos < $tokenIndex) { + throw new LogicException(); + } + + $result .= $originalTokens->getContentBetween($tokenIndex, $itemStartPos); + + if (count($delayedAdd) > 0) { + foreach ($delayedAdd as $delayedAddNode) { + $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) + && in_array(get_class($delayedAddNode), $this->parenthesesListMap[$mapKey], true); + if ($parenthesesNeeded) { + $result .= '('; + } + $result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens); + if ($parenthesesNeeded) { + $result .= ')'; + } + + if ($insertNewline) { + $result .= $insertStr . sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent); + } else { + $result .= $insertStr; + } + } + + $delayedAdd = []; + } + + $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) + && in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true); + $addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($itemStartPos, $itemEndPos); + if ($addParentheses) { + $result .= '('; + } + + $result .= $this->printNodeFormatPreserving($newNode, $originalTokens); + if ($addParentheses) { + $result .= ')'; + } + $tokenIndex = $itemEndPos + 1; + + } elseif ($diffType === DiffElem::TYPE_ADD) { + if ($insertStr === null) { + return null; + } + if (!$newNode instanceof Node) { + return null; + } + + if ($insertStr === ', ' && $isMultiline) { + $insertStr = ','; + $insertNewline = true; + } + + if ($beforeFirstKeepOrReplace) { + // Will be inserted at the next "replace" or "keep" element + $delayedAdd[] = $newNode; + continue; + } + + $itemEndPos = $tokenIndex - 1; + if ($insertNewline) { + $result .= $insertStr . sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent); + } else { + $result .= $insertStr; + } + + $parenthesesNeeded = isset($this->parenthesesListMap[$mapKey]) + && in_array(get_class($newNode), $this->parenthesesListMap[$mapKey], true); + if ($parenthesesNeeded) { + $result .= '('; + } + + $result .= $this->printNodeFormatPreserving($newNode, $originalTokens); + if ($parenthesesNeeded) { + $result .= ')'; + } + + $tokenIndex = $itemEndPos + 1; + + } elseif ($diffType === DiffElem::TYPE_REMOVE) { + if (!$originalNode instanceof Node) { + return null; + } + + $itemStartPos = $originalNode->getAttribute(Attribute::START_INDEX); + $itemEndPos = $originalNode->getAttribute(Attribute::END_INDEX); + if ($itemStartPos < 0 || $itemEndPos < 0) { + throw new LogicException(); + } + + if ($i === 0) { + // If we're removing from the start, keep the tokens before the node and drop those after it, + // instead of the other way around. + $originalTokensArray = $originalTokens->getTokens(); + for ($j = $tokenIndex; $j < $itemStartPos; $j++) { + if ($originalTokensArray[$j][Lexer::TYPE_OFFSET] === Lexer::TOKEN_PHPDOC_EOL) { + break; + } + $result .= $originalTokensArray[$j][Lexer::VALUE_OFFSET]; + } + } + + $tokenIndex = $itemEndPos + 1; + } + } + + if (count($delayedAdd) > 0) { + if (!isset($this->emptyListInsertionMap[$mapKey])) { + return null; + } + + [$findToken, $extraLeft, $extraRight] = $this->emptyListInsertionMap[$mapKey]; + if ($findToken !== null) { + $originalTokensArray = $originalTokens->getTokens(); + for (; $tokenIndex < count($originalTokensArray); $tokenIndex++) { + $result .= $originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET]; + if ($originalTokensArray[$tokenIndex][Lexer::VALUE_OFFSET] !== $findToken) { + continue; + } + + $tokenIndex++; + break; + } + } + $first = true; + $result .= $extraLeft; + foreach ($delayedAdd as $delayedAddNode) { + if (!$first) { + $result .= $insertStr; + if ($insertNewline) { + $result .= sprintf("\n%s*%s", $beforeAsteriskIndent, $afterAsteriskIndent); + } + } + + $result .= $this->printNodeFormatPreserving($delayedAddNode, $originalTokens); + $first = false; + } + $result .= $extraRight; + } + + return $result; + } + + /** + * @param Node[] $nodes + * @return array{bool, string, string} + */ + private function isMultiline(int $initialIndex, array $nodes, TokenIterator $originalTokens): array + { + $isMultiline = count($nodes) > 1; + $pos = $initialIndex; + $allText = ''; + /** @var Node|null $node */ + foreach ($nodes as $node) { + if (!$node instanceof Node) { + continue; + } + + $endPos = $node->getAttribute(Attribute::END_INDEX) + 1; + $text = $originalTokens->getContentBetween($pos, $endPos); + $allText .= $text; + if (strpos($text, "\n") === false) { + // We require that a newline is present between *every* item. If the formatting + // is inconsistent, with only some items having newlines, we don't consider it + // as multiline + $isMultiline = false; + } + $pos = $endPos; + } + + $c = preg_match_all('~\n(?[\\x09\\x20]*)\*(?\\x20*)~', $allText, $matches, PREG_SET_ORDER); + if ($c === 0) { + return [$isMultiline, '', '']; + } + + $before = ''; + $after = ''; + foreach ($matches as $match) { + if (strlen($match['before']) > strlen($before)) { + $before = $match['before']; + } + if (strlen($match['after']) <= strlen($after)) { + continue; + } + + $after = $match['after']; + } + + return [$isMultiline, $before, $after]; + } + + private function printNodeFormatPreserving(Node $node, TokenIterator $originalTokens): string + { + /** @var Node|null $originalNode */ + $originalNode = $node->getAttribute(Attribute::ORIGINAL_NODE); + if ($originalNode === null) { + return $this->print($node); + } + + $class = get_class($node); + if ($class !== get_class($originalNode)) { + throw new LogicException(); + } + + $startPos = $originalNode->getAttribute(Attribute::START_INDEX); + $endPos = $originalNode->getAttribute(Attribute::END_INDEX); + if ($startPos < 0 || $endPos < 0) { + throw new LogicException(); + } + + $result = ''; + $pos = $startPos; + $subNodeNames = array_keys(get_object_vars($node)); + foreach ($subNodeNames as $subNodeName) { + $subNode = $node->$subNodeName; + $origSubNode = $originalNode->$subNodeName; + + if ( + (!$subNode instanceof Node && $subNode !== null) + || (!$origSubNode instanceof Node && $origSubNode !== null) + ) { + if ($subNode === $origSubNode) { + // Unchanged, can reuse old code + continue; + } + + if (is_array($subNode) && is_array($origSubNode)) { + // Array subnode changed, we might be able to reconstruct it + $listResult = $this->printArrayFormatPreserving( + $subNode, + $origSubNode, + $originalTokens, + $pos, + $class, + $subNodeName + ); + + if ($listResult === null) { + return $this->print($node); + } + + $result .= $listResult; + continue; + } + + return $this->print($node); + } + + if ($origSubNode === null) { + if ($subNode === null) { + // Both null, nothing to do + continue; + } + + return $this->print($node); + } + + $subStartPos = $origSubNode->getAttribute(Attribute::START_INDEX); + $subEndPos = $origSubNode->getAttribute(Attribute::END_INDEX); + if ($subStartPos < 0 || $subEndPos < 0) { + throw new LogicException(); + } + + if ($subNode === null) { + return $this->print($node); + } + + $result .= $originalTokens->getContentBetween($pos, $subStartPos); + $mapKey = get_class($node) . '->' . $subNodeName; + $parenthesesNeeded = isset($this->parenthesesMap[$mapKey]) + && in_array(get_class($subNode), $this->parenthesesMap[$mapKey], true); + $addParentheses = $parenthesesNeeded && !$originalTokens->hasParentheses($subStartPos, $subEndPos); + if ($addParentheses) { + $result .= '('; + } + + $result .= $this->printNodeFormatPreserving($subNode, $originalTokens); + if ($addParentheses) { + $result .= ')'; + } + + $pos = $subEndPos + 1; + } + + return $result . $originalTokens->getContentBetween($pos, $endPos + 1); + } + +} diff --git a/vendor/phpstan/phpstan/bootstrap.php b/vendor/phpstan/phpstan/bootstrap.php index 204221a3..1537f3f1 100644 --- a/vendor/phpstan/phpstan/bootstrap.php +++ b/vendor/phpstan/phpstan/bootstrap.php @@ -23,7 +23,6 @@ final class PharAutoloader self::$composerAutoloader = require 'phar://' . __DIR__ . '/phpstan.phar/vendor/autoload.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/jetbrains/phpstorm-stubs/PhpStormStubsMap.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/async/src/functions_include.php'; - require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-stream/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise-timer/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/react/promise/src/functions_include.php'; require_once 'phar://' . __DIR__ . '/phpstan.phar/vendor/ringcentral/psr7/src/functions_include.php'; diff --git a/vendor/phpstan/phpstan/phpstan.phar b/vendor/phpstan/phpstan/phpstan.phar index 947d5ab0..3f043c71 100755 Binary files a/vendor/phpstan/phpstan/phpstan.phar and b/vendor/phpstan/phpstan/phpstan.phar differ diff --git a/vendor/phpstan/phpstan/phpstan.phar.asc b/vendor/phpstan/phpstan/phpstan.phar.asc index 7d0dcea9..3c34229a 100644 --- a/vendor/phpstan/phpstan/phpstan.phar.asc +++ b/vendor/phpstan/phpstan/phpstan.phar.asc @@ -1,16 +1,16 @@ -----BEGIN PGP SIGNATURE----- -iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmQdeygACgkQUcZzBf/C -5cAe5Q//YDKPvVm4HlLT90M/Ov0IJ8brPP6LN0fbMV93TJlXFhh6Ph79YUwxRWve -FPYr93HDCSOdlg3q3xRNgPcUim5+7U5xf5ze5RWIBR07IDY5+i9Os66CLRALlO4u -ToXHCO2k0hw26sSskRXmF2IrNIibvPjVjR+ephFGFenWI0S7vq5cYGqOnzwdURyu -ZNIswHWQsJSGLn4AXfDnushBCy3w5IsSgnENIWD7L9a37A45kek+iHETcX1OLTOd -AlJOvQ0l2OAE4kMx8tailGYtJo9yLnjtSLw6xQbdw5mf47iapm1U09C52XvYsZg4 -oZNCJ8QFHR1YbbLpdMxPFcMQgbVLGKBwHYcpsi2VdMKdR+7altlF9govkvBLxxm7 -Polq9ya0fS7wAJP0vMESGeP6UJi68DMWH7hxJ7d9tyBieYJHSpm5q5QSYjj4RxM5 -LiTTv9ug8DFIsLJiw1CplE4pxtJ82arXBqggpqO15MRNxwzyJmY7XIHtEZI2dl+d -BImd5bWl6nCkhKEYPs+6SEt/caXIz/XERap5gO9Q8UBx5jaKPHEKRbuvTgbW1Ods -fFNDgIsQyg+56LzIxgp2a6IUVeeQSrL4kjeHYQDBMzG0P78ZXjXdUl3kYG3TJHmb -QNWgYcz3jJJd/F5YSMPpAPT/gpcD4FBnHZ+mlLnGvXvuuGh+MyU= -=vW1l +iQIzBAABCgAdFiEEynwsejDI6OEnSoR2UcZzBf/C5cAFAmRaZmcACgkQUcZzBf/C +5cAn/Q//fbWiR/qaSvlHpk73KH7iDfoHwNvRrHSQODZdMa4PGiEbL+SXsKnRxFDo +kEJZwgU5qi3WMflt7Ml3dYDlQDgoDerdaiySYFoBcv1NXDWKoF7+Egy1AHxpfNq+ +FMCkZNR2ulSaYUCofM4GkTNap4yVkPCy289ZU6yUmRnJxF+hh/CFfdVPAPbwh/a6 +UqV3R2ENJZSbtA1pzSTBpUPQGQ9qcsqngKyNyxk1hEd9opdMg2eSFvO1e1ZZm/Tk +Kgh5wCbsbSJuRPGO4vbiybTeO/qXPDlHV6oA5SHnjJ4H24phCsHdyJHHvLQmrUeR +BKHgnH1y/b5J9cgr9OgEQJK9TMHHd6dii9//Qp+0rUZIDZ4Ym2lDSA/Vn/D9GoV3 +zo4QYzW3TvE3QMdnLcX/ZtaLliPdDYIaYUXOiyaYwLFGVxSWZWOC5IN0G0bLJb39 +Ca/z839nkWdMqg68q/oHC2Nk/v/KZnKg1RlRjYhj53T6nr0JDEiaYMyETSOIFsVX +AcCQnLLwMndUAibJAyORDnTk+ipg0SecFoPvvhea1BtlTfhSDIlrT4OPKZ5nExzd +nR/zGbIH8lCvsBc+hq+Kgodtfs5nauwEOwlVUwet26xL1YKOd0jxz+Zp6tgk0wba +cMf5L9fm85j83DQYr7Ukaaj81kmMujRWDo/dRojKhUlJUrNnjXA= +=jTtX -----END PGP SIGNATURE----- diff --git a/vendor/sebastian/diff/ChangeLog.md b/vendor/sebastian/diff/ChangeLog.md index ab8640c8..9142e50f 100644 --- a/vendor/sebastian/diff/ChangeLog.md +++ b/vendor/sebastian/diff/ChangeLog.md @@ -2,6 +2,18 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. +## [5.0.3] - 2023-05-01 + +### Changed + +* [#119](https://github.com/sebastianbergmann/diff/pull/119): Improve performance of `TimeEfficientLongestCommonSubsequenceCalculator` + +## [5.0.2] - 2023-05-01 + +### Changed + +* [#118](https://github.com/sebastianbergmann/diff/pull/118): Improve performance of `MemoryEfficientLongestCommonSubsequenceCalculator` + ## [5.0.1] - 2023-03-23 ### Fixed @@ -92,6 +104,8 @@ 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.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 [5.0.1]: https://github.com/sebastianbergmann/diff/compare/5.0.0...5.0.1 [5.0.0]: https://github.com/sebastianbergmann/diff/compare/4.0.4...5.0.0 [4.0.4]: https://github.com/sebastianbergmann/diff/compare/4.0.3...4.0.4 diff --git a/vendor/sebastian/diff/README.md b/vendor/sebastian/diff/README.md index 3e84a35b..539dc59e 100644 --- a/vendor/sebastian/diff/README.md +++ b/vendor/sebastian/diff/README.md @@ -202,3 +202,5 @@ The code above yields the output below: ) ) ) + +Note: If the chunk size is 0 lines, i.e., `getStartRange()` or `getEndRange()` return 0, the number of line returned by `getStart()` or `getEnd()` is one lower than one would expect. It is the line number after which the chunk should be inserted or deleted; in all other cases, it gives the first line number of the replaced range of lines. diff --git a/vendor/sebastian/diff/SECURITY.md b/vendor/sebastian/diff/SECURITY.md index 778f018b..d88ff001 100644 --- a/vendor/sebastian/diff/SECURITY.md +++ b/vendor/sebastian/diff/SECURITY.md @@ -4,7 +4,7 @@ If you believe you have found a security vulnerability in the library that is de **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.** -Instead, please send an email to `sebastian@phpunit.de`. +Instead, please email `sebastian@phpunit.de`. Please include as much of the information listed below as you can to help us better understand and resolve the issue: @@ -22,9 +22,9 @@ This information will help us triage your report more quickly. The library that is developed in this repository was either extracted from [PHPUnit](https://github.com/sebastianbergmann/phpunit) or developed specifically as a dependency for PHPUnit. -The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in a HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. +The library is developed with a focus on development environments and the command-line. No specific testing or hardening with regard to using the library in an HTTP or web context or with untrusted input data is performed. The library might also contain functionality that intentionally exposes internal application data for debugging purposes. If the library is used in a web application, the application developer is responsible for filtering inputs or escaping outputs as necessary and for verifying that the used functionality is safe for use within the intended context. -Vulnerabilities specific to the use outside of a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. +Vulnerabilities specific to the use outside a development context will be fixed as applicable, provided that the fix does not have an averse effect on the primary use case for development purposes. diff --git a/vendor/sebastian/diff/src/Differ.php b/vendor/sebastian/diff/src/Differ.php index edf2dcc4..19ccf97c 100644 --- a/vendor/sebastian/diff/src/Differ.php +++ b/vendor/sebastian/diff/src/Differ.php @@ -30,14 +30,10 @@ use SebastianBergmann\Diff\Output\DiffOutputBuilderInterface; final class Differ { - public const OLD = 0; - - public const ADDED = 1; - - public const REMOVED = 2; - - public const DIFF_LINE_END_WARNING = 3; - + public const OLD = 0; + public const ADDED = 1; + public const REMOVED = 2; + public const DIFF_LINE_END_WARNING = 3; public const NO_LINE_END_EOF_WARNING = 4; private DiffOutputBuilderInterface $outputBuilder; diff --git a/vendor/sebastian/diff/src/Line.php b/vendor/sebastian/diff/src/Line.php index ecb2f794..a9106a25 100644 --- a/vendor/sebastian/diff/src/Line.php +++ b/vendor/sebastian/diff/src/Line.php @@ -11,10 +11,8 @@ namespace SebastianBergmann\Diff; final class Line { - public const ADDED = 1; - - public const REMOVED = 2; - + public const ADDED = 1; + public const REMOVED = 2; public const UNCHANGED = 3; private int $type; private string $content; diff --git a/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php b/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php index dde08027..a46de07d 100644 --- a/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php +++ b/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php @@ -78,7 +78,12 @@ final class MemoryEfficientLongestCommonSubsequenceCalculator implements Longest if ($from[$i] === $to[$j]) { $current[$j + 1] = $prev[$j] + 1; } else { - $current[$j + 1] = max($current[$j], $prev[$j + 1]); + // don't use max() to avoid function call overhead + if ($current[$j] > $prev[$j + 1]) { + $current[$j + 1] = $current[$j]; + } else { + $current[$j + 1] = $prev[$j + 1]; + } } } } diff --git a/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php b/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php index 51bac1f1..93b76288 100644 --- a/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php +++ b/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php @@ -37,12 +37,24 @@ final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCo for ($i = 1; $i <= $fromLength; $i++) { for ($j = 1; $j <= $toLength; $j++) { - $o = ($j * $width) + $i; - $matrix[$o] = max( - $matrix[$o - 1], - $matrix[$o - $width], - $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 - ); + $o = ($j * $width) + $i; + + // don't use max() to avoid function call overhead + $firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0; + + if ($matrix[$o - 1] > $matrix[$o - $width]) { + if ($firstOrLast > $matrix[$o - 1]) { + $matrix[$o] = $firstOrLast; + } else { + $matrix[$o] = $matrix[$o - 1]; + } + } else { + if ($firstOrLast > $matrix[$o - $width]) { + $matrix[$o] = $firstOrLast; + } else { + $matrix[$o] = $matrix[$o - $width]; + } + } } } diff --git a/vendor/spatie/array-to-xml/CHANGELOG.md b/vendor/spatie/array-to-xml/CHANGELOG.md index d43177a4..0a086699 100755 --- a/vendor/spatie/array-to-xml/CHANGELOG.md +++ b/vendor/spatie/array-to-xml/CHANGELOG.md @@ -2,6 +2,23 @@ All notable changes to `array-to-xml` will be documented in this file +## 3.1.5 - 2022-12-24 + +### What's Changed + +- Add Dependabot Automation by @patinthehat in https://github.com/spatie/array-to-xml/pull/196 +- Bump actions/checkout from 2 to 3 by @dependabot in https://github.com/spatie/array-to-xml/pull/197 +- Fix PHP version by @parallels999 in https://github.com/spatie/array-to-xml/pull/198 +- fix deprecated `passing null as string type` by @trin4ik in https://github.com/spatie/array-to-xml/pull/204 + +### New Contributors + +- @dependabot made their first contribution in https://github.com/spatie/array-to-xml/pull/197 +- @parallels999 made their first contribution in https://github.com/spatie/array-to-xml/pull/198 +- @trin4ik made their first contribution in https://github.com/spatie/array-to-xml/pull/204 + +**Full Changelog**: https://github.com/spatie/array-to-xml/compare/3.1.4...3.1.5 + ## 3.1.4 - 2022-11-24 ### What's Changed diff --git a/vendor/spatie/array-to-xml/README.md b/vendor/spatie/array-to-xml/README.md index 5d4769a3..ec6a6ad8 100755 --- a/vendor/spatie/array-to-xml/README.md +++ b/vendor/spatie/array-to-xml/README.md @@ -1,6 +1,3 @@ - -[](https://supportukrainenow.org) - # Convert an array to xml [![Latest Version](https://img.shields.io/github/release/spatie/array-to-xml.svg?style=flat-square)](https://github.com/spatie/array-to-xml/releases) diff --git a/vendor/spatie/array-to-xml/src/ArrayToXml.php b/vendor/spatie/array-to-xml/src/ArrayToXml.php index e08a150d..b84bdc86 100644 --- a/vendor/spatie/array-to-xml/src/ArrayToXml.php +++ b/vendor/spatie/array-to-xml/src/ArrayToXml.php @@ -24,7 +24,8 @@ class ArrayToXml string | null $xmlEncoding = null, string $xmlVersion = '1.0', array $domProperties = [], - bool | null $xmlStandalone = null + bool | null $xmlStandalone = null, + bool $addXmlDeclaration = true ) { $this->document = new DOMDocument($xmlVersion, $xmlEncoding ?? ''); @@ -36,6 +37,8 @@ class ArrayToXml $this->setDomProperties($domProperties); } + $this->addXmlDeclaration = $addXmlDeclaration; + $this->replaceSpacesByUnderScoresInKeyNames = $replaceSpacesByUnderScoresInKeyNames; if (! empty($array) && $this->isArrayAllKeySequential($array)) { @@ -61,7 +64,8 @@ class ArrayToXml string $xmlEncoding = null, string $xmlVersion = '1.0', array $domProperties = [], - bool $xmlStandalone = null + bool $xmlStandalone = null, + bool $addXmlDeclaration = true, ): string { $converter = new static( $array, @@ -70,7 +74,8 @@ class ArrayToXml $xmlEncoding, $xmlVersion, $domProperties, - $xmlStandalone + $xmlStandalone, + $addXmlDeclaration ); return $converter->toXml(); @@ -80,7 +85,7 @@ class ArrayToXml { return $this->addXmlDeclaration ? $this->document->saveXML() - : $this->document->saveXml($this->document->documentElement); + : $this->document->saveXML($this->document->documentElement); } public function toDom(): DOMDocument diff --git a/vendor/symfony/console/Command/CompleteCommand.php b/vendor/symfony/console/Command/CompleteCommand.php index e65b334c..dbf5d7dd 100644 --- a/vendor/symfony/console/Command/CompleteCommand.php +++ b/vendor/symfony/console/Command/CompleteCommand.php @@ -173,10 +173,10 @@ final class CompleteCommand extends Command throw $e; } - return self::FAILURE; + return 2; } - return self::SUCCESS; + return 0; } private function createCompletionInput(InputInterface $input): CompletionInput diff --git a/vendor/symfony/console/Command/DumpCompletionCommand.php b/vendor/symfony/console/Command/DumpCompletionCommand.php index 1ad1c0e7..cac944ec 100644 --- a/vendor/symfony/console/Command/DumpCompletionCommand.php +++ b/vendor/symfony/console/Command/DumpCompletionCommand.php @@ -48,14 +48,16 @@ final class DumpCompletionCommand extends Command $shell = $this->guessShell(); [$rcFile, $completionFile] = match ($shell) { 'fish' => ['~/.config/fish/config.fish', "/etc/fish/completions/$commandName.fish"], - 'zsh' => ['~/.zshrc', '$fpath[1]/'.$commandName], + 'zsh' => ['~/.zshrc', '$fpath[1]/_'.$commandName], default => ['~/.bashrc', "/etc/bash_completion.d/$commandName"], }; + $supportedShells = implode(', ', $this->getSupportedShells()); + $this ->setHelp(<<%command.name% command dumps the shell completion script required -to use shell autocompletion (currently, bash and fish completion is supported). +to use shell autocompletion (currently, {$supportedShells} completion are supported). Static installation ------------------- @@ -94,7 +96,7 @@ EOH if ($input->getOption('debug')) { $this->tailDebugLog($commandName, $output); - return self::SUCCESS; + return 0; } $shell = $input->getArgument('shell') ?? self::guessShell(); @@ -111,12 +113,12 @@ EOH $output->writeln(sprintf('Shell not detected, Symfony shell completion only supports "%s").', implode('", "', $supportedShells))); } - return self::INVALID; + return 2; } $output->write(str_replace(['{{ COMMAND_NAME }}', '{{ VERSION }}'], [$commandName, CompleteCommand::COMPLETION_API_VERSION], file_get_contents($completionFile))); - return self::SUCCESS; + return 0; } private static function guessShell(): string @@ -141,8 +143,19 @@ EOH */ private function getSupportedShells(): array { - return $this->supportedShells ??= array_map(function ($f) { - return pathinfo($f, \PATHINFO_EXTENSION); - }, glob(__DIR__.'/../Resources/completion.*')); + if (isset($this->supportedShells)) { + return $this->supportedShells; + } + + $shells = []; + + foreach (new \DirectoryIterator(__DIR__.'/../Resources/') as $file) { + if (str_starts_with($file->getBasename(), 'completion.') && $file->isFile()) { + $shells[] = $file->getExtension(); + } + } + sort($shells); + + return $this->supportedShells = $shells; } } diff --git a/vendor/symfony/console/Formatter/OutputFormatterStyle.php b/vendor/symfony/console/Formatter/OutputFormatterStyle.php index 1659986e..3394c9ad 100644 --- a/vendor/symfony/console/Formatter/OutputFormatterStyle.php +++ b/vendor/symfony/console/Formatter/OutputFormatterStyle.php @@ -83,7 +83,8 @@ class OutputFormatterStyle implements OutputFormatterStyleInterface public function apply(string $text): string { $this->handlesHrefGracefully ??= 'JetBrains-JediTerm' !== getenv('TERMINAL_EMULATOR') - && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100); + && (!getenv('KONSOLE_VERSION') || (int) getenv('KONSOLE_VERSION') > 201100) + && !isset($_SERVER['IDEA_INITIAL_DIRECTORY']); if (null !== $this->href && $this->handlesHrefGracefully) { $text = "\033]8;;$this->href\033\\$text\033]8;;\033\\"; diff --git a/vendor/symfony/console/Helper/QuestionHelper.php b/vendor/symfony/console/Helper/QuestionHelper.php index c345b4af..f26ca577 100644 --- a/vendor/symfony/console/Helper/QuestionHelper.php +++ b/vendor/symfony/console/Helper/QuestionHelper.php @@ -123,7 +123,18 @@ class QuestionHelper extends Helper } if (false === $ret) { + $isBlocked = stream_get_meta_data($inputStream)['blocked'] ?? true; + + if (!$isBlocked) { + stream_set_blocking($inputStream, true); + } + $ret = $this->readInput($inputStream, $question); + + if (!$isBlocked) { + stream_set_blocking($inputStream, false); + } + if (false === $ret) { throw new MissingInputException('Aborted.'); } @@ -496,13 +507,11 @@ class QuestionHelper extends Helper return self::$stdinIsInteractive = @posix_isatty(fopen('php://stdin', 'r')); } - if (!\function_exists('exec')) { + if (!\function_exists('shell_exec')) { return self::$stdinIsInteractive = true; } - exec('stty 2> /dev/null', $output, $status); - - return self::$stdinIsInteractive = 1 !== $status; + return self::$stdinIsInteractive = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); } /** diff --git a/vendor/symfony/console/Helper/Table.php b/vendor/symfony/console/Helper/Table.php index 893b3192..907c9f50 100644 --- a/vendor/symfony/console/Helper/Table.php +++ b/vendor/symfony/console/Helper/Table.php @@ -804,7 +804,7 @@ class Table $textContent = Helper::removeDecoration($this->output->getFormatter(), $cell); $textLength = Helper::width($textContent); if ($textLength > 0) { - $contentColumns = str_split($textContent, ceil($textLength / $cell->getColspan())); + $contentColumns = mb_str_split($textContent, ceil($textLength / $cell->getColspan())); foreach ($contentColumns as $position => $content) { $row[$i + $position] = $content; } diff --git a/vendor/symfony/console/Input/InputArgument.php b/vendor/symfony/console/Input/InputArgument.php index a130c412..0e86e916 100644 --- a/vendor/symfony/console/Input/InputArgument.php +++ b/vendor/symfony/console/Input/InputArgument.php @@ -37,7 +37,7 @@ class InputArgument /** * @param string $name The argument name - * @param int|null $mode The argument mode: self::REQUIRED or self::OPTIONAL + * @param int|null $mode The argument mode: a bit mask of self::REQUIRED, self::OPTIONAL and self::IS_ARRAY * @param string $description A description text * @param string|bool|int|float|array|null $default The default value (for self::OPTIONAL mode only) * @param array|\Closure(CompletionInput,CompletionSuggestions):list $suggestedValues The values used for input completion diff --git a/vendor/symfony/console/Resources/completion.zsh b/vendor/symfony/console/Resources/completion.zsh index 97a9e88c..ff76fe5f 100644 --- a/vendor/symfony/console/Resources/completion.zsh +++ b/vendor/symfony/console/Resources/completion.zsh @@ -1,3 +1,5 @@ +#compdef {{ COMMAND_NAME }} + # This file is part of the Symfony package. # # (c) Fabien Potencier diff --git a/vendor/symfony/console/Terminal.php b/vendor/symfony/console/Terminal.php index 216c609f..855f4114 100644 --- a/vendor/symfony/console/Terminal.php +++ b/vendor/symfony/console/Terminal.php @@ -123,20 +123,19 @@ class Terminal return self::$stty; } - // skip check if exec function is disabled - if (!\function_exists('exec')) { + // skip check if shell_exec function is disabled + if (!\function_exists('shell_exec')) { return false; } - exec('stty 2>&1', $output, $exitcode); - - return self::$stty = 0 === $exitcode; + return self::$stty = (bool) shell_exec('stty 2> '.('\\' === \DIRECTORY_SEPARATOR ? 'NUL' : '/dev/null')); } private static function initDimensions() { if ('\\' === \DIRECTORY_SEPARATOR) { - if (preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim(getenv('ANSICON')), $matches)) { + $ansicon = getenv('ANSICON'); + if (false !== $ansicon && preg_match('/^(\d+)x(\d+)(?: \((\d+)x(\d+)\))?$/', trim($ansicon), $matches)) { // extract [w, H] from "wxh (WxH)" // or [w, h] from "wxh" self::$width = (int) $matches[1]; @@ -216,6 +215,8 @@ class Terminal 2 => ['pipe', 'w'], ]; + $cp = \function_exists('sapi_windows_cp_set') ? sapi_windows_cp_get() : 0; + $process = proc_open($command, $descriptorspec, $pipes, null, null, ['suppress_errors' => true]); if (!\is_resource($process)) { return null; @@ -226,6 +227,10 @@ class Terminal fclose($pipes[2]); proc_close($process); + if ($cp) { + sapi_windows_cp_set($cp); + } + return $info; } } diff --git a/vendor/symfony/console/composer.json b/vendor/symfony/console/composer.json index bafe5d16..6cc6166d 100644 --- a/vendor/symfony/console/composer.json +++ b/vendor/symfony/console/composer.json @@ -2,7 +2,7 @@ "name": "symfony/console", "type": "library", "description": "Eases the creation of beautiful and testable command line interfaces", - "keywords": ["console", "cli", "command line", "terminal"], + "keywords": ["console", "cli", "command-line", "terminal"], "homepage": "https://symfony.com", "license": "MIT", "authors": [ diff --git a/vendor/symfony/filesystem/Filesystem.php b/vendor/symfony/filesystem/Filesystem.php index c7f3dd29..3b3cf7a1 100644 --- a/vendor/symfony/filesystem/Filesystem.php +++ b/vendor/symfony/filesystem/Filesystem.php @@ -161,7 +161,7 @@ class Filesystem } } elseif (is_dir($file)) { if (!$isRecursive) { - $tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-.')); + $tmpName = \dirname(realpath($file)).'/.'.strrev(strtr(base64_encode(random_bytes(2)), '/=', '-_')); if (file_exists($tmpName)) { try { diff --git a/vendor/symfony/string/Inflector/EnglishInflector.php b/vendor/symfony/string/Inflector/EnglishInflector.php index 4474736a..2871e4e5 100644 --- a/vendor/symfony/string/Inflector/EnglishInflector.php +++ b/vendor/symfony/string/Inflector/EnglishInflector.php @@ -55,6 +55,9 @@ final class EnglishInflector implements InflectorInterface // indices (index), appendices (appendix), prices (price) ['seci', 4, false, true, ['ex', 'ix', 'ice']], + // codes (code) + ['sedoc', 5, false, true, 'code'], + // selfies (selfie) ['seifles', 7, true, true, 'selfie'], @@ -64,6 +67,9 @@ final class EnglishInflector implements InflectorInterface // movies (movie) ['seivom', 6, true, true, 'movie'], + // names (name) + ['seman', 5, true, false, 'name'], + // conspectuses (conspectus), prospectuses (prospectus) ['sesutcep', 8, true, true, 'pectus'], diff --git a/vendor/vimeo/psalm/composer.json b/vendor/vimeo/psalm/composer.json index ed857f9a..cfca1191 100644 --- a/vendor/vimeo/psalm/composer.json +++ b/vendor/vimeo/psalm/composer.json @@ -44,6 +44,7 @@ }, "require-dev": { "ext-curl": "*", + "amphp/phpunit-util": "^2.0", "bamarni/composer-bin-plugin": "^1.4", "brianium/paratest": "^6.9", "mockery/mockery": "^1.5", diff --git a/vendor/vimeo/psalm/config.xsd b/vendor/vimeo/psalm/config.xsd index f4302e20..4d4f377f 100644 --- a/vendor/vimeo/psalm/config.xsd +++ b/vendor/vimeo/psalm/config.xsd @@ -62,6 +62,7 @@ + @@ -249,6 +250,7 @@ + @@ -480,6 +482,7 @@ + diff --git a/vendor/vimeo/psalm/dictionaries/CallMap.php b/vendor/vimeo/psalm/dictionaries/CallMap.php index 2e1191c8..bb61ce41 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap.php @@ -418,9 +418,7 @@ return [ 'array_uintersect_assoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable', '...rest='=>'array|callable(mixed,mixed):int'], 'array_uintersect_uassoc' => ['array', 'array'=>'array', 'rest'=>'array', 'data_compare_func'=>'callable(mixed,mixed):int', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_uintersect_uassoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', 'arg5'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], -'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'], -'array_unique\'1' => ['array', 'array'=>'array', 'flags='=>'1'], -'array_unique\'2' => ['array', 'array'=>'array', 'flags='=>'2|5'], +'array_unique' => ['array', 'array'=>'array', 'flags='=>'int'], 'array_unshift' => ['int', '&rw_array'=>'array', '...values='=>'mixed'], 'array_values' => ['list', 'array'=>'array'], 'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'], @@ -612,7 +610,7 @@ return [ 'CallbackFilterIterator::next' => ['void'], 'CallbackFilterIterator::rewind' => ['void'], 'CallbackFilterIterator::valid' => ['bool'], -'ceil' => ['float', 'num'=>'float'], +'ceil' => ['float', 'num'=>'float|int'], 'chdb::__construct' => ['void', 'pathname'=>'string'], 'chdb::get' => ['string', 'key'=>'string'], 'chdb_create' => ['bool', 'pathname'=>'string', 'data'=>'array'], @@ -1260,7 +1258,7 @@ return [ 'cubrid_unbuffered_query' => ['resource', 'query'=>'string', 'conn_identifier='=>''], 'cubrid_version' => ['string'], 'curl_close' => ['void', 'handle'=>'CurlHandle'], -'curl_copy_handle' => ['CurlHandle', 'handle'=>'CurlHandle'], +'curl_copy_handle' => ['CurlHandle|false', 'handle'=>'CurlHandle'], 'curl_errno' => ['int', 'handle'=>'CurlHandle'], 'curl_error' => ['string', 'handle'=>'CurlHandle'], 'curl_escape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'], @@ -1293,7 +1291,6 @@ return [ 'curl_unescape' => ['string|false', 'handle'=>'CurlHandle', 'string'=>'string'], 'curl_version' => ['array', 'version='=>'int'], 'CURLFile::__construct' => ['void', 'filename'=>'string', 'mime_type='=>'?string', 'posted_filename='=>'?string'], -'CURLFile::__wakeup' => ['void'], 'CURLFile::getFilename' => ['string'], 'CURLFile::getMimeType' => ['string'], 'CURLFile::getPostFilename' => ['string'], @@ -1321,7 +1318,7 @@ return [ 'date_get_last_errors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'], 'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'], -'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'], +'date_isodate_set' => ['DateTime', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int'], 'date_modify' => ['DateTime|false', 'object'=>'DateTime', 'modifier'=>'string'], 'date_offset_get' => ['int', 'object'=>'DateTimeInterface'], 'date_parse' => ['array', 'datetime'=>'string'], @@ -1350,7 +1347,7 @@ return [ 'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'], 'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'], 'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], -'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], +'datefmt_parse' => ['float|int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'IntlCalendar|int|null'], 'datefmt_set_lenient' => ['void', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], @@ -1463,20 +1460,20 @@ return [ 'db2_tableprivileges' => [''], 'db2_tables' => ['resource|false', 'connection'=>'resource', 'qualifier='=>'?string', 'schema='=>'?string', 'table_name='=>'?string', 'table_type='=>'?string'], 'dba_close' => ['void', 'dba'=>'resource'], -'dba_delete' => ['bool', 'key'=>'string', 'dba'=>'resource'], -'dba_exists' => ['bool', 'key'=>'string', 'dba'=>'resource'], -'dba_fetch' => ['string|false', 'key'=>'string', 'skip'=>'int', 'dba'=>'resource'], -'dba_fetch\'1' => ['string|false', 'key'=>'string', 'skip'=>'resource'], +'dba_delete' => ['bool', 'key'=>'array|string', 'dba'=>'resource'], +'dba_exists' => ['bool', 'key'=>'array|string', 'dba'=>'resource'], +'dba_fetch' => ['string|false', 'key'=>'array|string', 'skip'=>'int', 'dba'=>'resource'], +'dba_fetch\'1' => ['string|false', 'key'=>'array|string', 'skip'=>'resource'], 'dba_firstkey' => ['string', 'dba'=>'resource'], 'dba_handlers' => ['array', 'full_info='=>'bool'], -'dba_insert' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'], +'dba_insert' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'], 'dba_key_split' => ['array|false', 'key'=>'string|false|null'], 'dba_list' => ['array'], 'dba_nextkey' => ['string', 'dba'=>'resource'], 'dba_open' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'?string', 'permission='=>'int', 'map_size='=>'int', 'flags='=>'?int'], 'dba_optimize' => ['bool', 'dba'=>'resource'], 'dba_popen' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'?string', 'permission='=>'int', 'map_size='=>'int', 'flags='=>'?int'], -'dba_replace' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'], +'dba_replace' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'], 'dba_sync' => ['bool', 'dba'=>'resource'], 'dbase_add_record' => ['bool', 'dbase_identifier'=>'resource', 'record'=>'array'], 'dbase_close' => ['bool', 'dbase_identifier'=>'resource'], @@ -1990,7 +1987,7 @@ return [ 'Ds\Vector::sum' => ['int|float'], 'Ds\Vector::toArray' => ['array'], 'Ds\Vector::unshift' => ['void', '...values='=>'mixed'], -'easter_date' => ['int', 'year='=>'?int'], +'easter_date' => ['int', 'year='=>'?int', 'mode='=>'int'], 'easter_days' => ['int', 'year='=>'?int', 'mode='=>'int'], 'echo' => ['void', 'arg1'=>'string', '...args='=>'string'], 'eio_busy' => ['resource', 'delay'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'], @@ -2882,7 +2879,7 @@ return [ 'finfo_set_flags' => ['bool', 'finfo'=>'finfo', 'flags'=>'int'], 'floatval' => ['float', 'value'=>'mixed'], 'flock' => ['bool', 'stream'=>'resource', 'operation'=>'int', '&w_would_block='=>'int'], -'floor' => ['float', 'num'=>'float'], +'floor' => ['float', 'num'=>'float|int'], 'flush' => ['void'], 'fmod' => ['float', 'num1'=>'float', 'num2'=>'float'], 'fnmatch' => ['bool', 'pattern'=>'string', 'filename'=>'string', 'flags='=>'int'], @@ -3159,7 +3156,6 @@ return [ 'Gender\Gender::get' => ['int', 'name'=>'string', 'country='=>'int'], 'Gender\Gender::isNick' => ['array', 'name0'=>'string', 'name1'=>'string', 'country='=>'int'], 'Gender\Gender::similarNames' => ['array', 'name'=>'string', 'country='=>'int'], -'Generator::__wakeup' => ['void'], 'Generator::current' => ['mixed'], 'Generator::getReturn' => ['mixed'], 'Generator::key' => ['mixed'], @@ -3280,8 +3276,8 @@ return [ 'get_called_class' => ['class-string'], 'get_cfg_var' => ['string|false', 'option'=>'string'], 'get_class' => ['class-string', 'object='=>'object'], -'get_class_methods' => ['list', 'object_or_class'=>'object|class-string'], -'get_class_vars' => ['array', 'class'=>'string'], +'get_class_methods' => ['list', 'object_or_class'=>'object|class-string'], +'get_class_vars' => ['array', 'class'=>'string'], 'get_current_user' => ['string'], 'get_debug_type' => ['string', 'value'=>'mixed'], 'get_declared_classes' => ['list'], @@ -3333,7 +3329,7 @@ return [ 'gettimeofday' => ['array'], 'gettimeofday\'1' => ['float', 'as_float='=>'true'], 'gettype' => ['string', 'value'=>'mixed'], -'glob' => ['list|false', 'pattern'=>'string', 'flags='=>'int'], +'glob' => ['list|false', 'pattern'=>'non-empty-string', 'flags='=>'int<1, max>'], 'GlobIterator::__construct' => ['void', 'pattern'=>'string', 'flags='=>'int'], 'GlobIterator::count' => ['int'], 'GlobIterator::current' => ['FilesystemIterator|SplFileInfo|string'], @@ -3555,9 +3551,7 @@ return [ 'GmagickPixel::setcolorvalue' => ['GmagickPixel', 'color'=>'int', 'value'=>'float'], 'gmdate' => ['string', 'format'=>'string', 'timestamp='=>'int|null'], 'gmmktime' => ['int|false', 'hour'=>'int', 'minute='=>'int|null', 'second='=>'int|null', 'month='=>'int|null', 'day='=>'int|null', 'year='=>'int|null'], -'GMP::__construct' => ['void'], 'GMP::__serialize' => ['array'], -'GMP::__toString' => ['numeric-string'], 'GMP::__unserialize' => ['void', 'data'=>'array'], 'gmp_abs' => ['GMP', 'num'=>'GMP|string|int'], 'gmp_add' => ['GMP', 'num1'=>'GMP|string|int', 'num2'=>'GMP|string|int'], @@ -3742,19 +3736,19 @@ return [ 'gzdeflate' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'], 'gzencode' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'], 'gzeof' => ['bool', 'stream'=>'resource'], -'gzfile' => ['list', 'filename'=>'string', 'use_include_path='=>'int'], +'gzfile' => ['list|false', 'filename'=>'string', 'use_include_path='=>'int'], 'gzgetc' => ['string|false', 'stream'=>'resource'], 'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'?int'], 'gzinflate' => ['string|false', 'data'=>'string', 'max_length='=>'int'], 'gzopen' => ['resource|false', 'filename'=>'string', 'mode'=>'string', 'use_include_path='=>'int'], 'gzpassthru' => ['int', 'stream'=>'resource'], -'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], +'gzputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], 'gzread' => ['string|false', 'stream'=>'resource', 'length'=>'int'], 'gzrewind' => ['bool', 'stream'=>'resource'], 'gzseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'gztell' => ['int|false', 'stream'=>'resource'], 'gzuncompress' => ['string|false', 'data'=>'string', 'max_length='=>'int'], -'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], +'gzwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], 'HaruAnnotation::setBorderStyle' => ['bool', 'width'=>'float', 'dash_on'=>'int', 'dash_off'=>'int'], 'HaruAnnotation::setHighlightMode' => ['bool', 'mode'=>'int'], 'HaruAnnotation::setIcon' => ['bool', 'icon'=>'int'], @@ -6129,10 +6123,10 @@ return [ 'Iterator::rewind' => ['void'], 'Iterator::valid' => ['bool'], 'iterator_apply' => ['0|positive-int', 'iterator'=>'Traversable', 'callback'=>'callable(mixed):bool', 'args='=>'?array'], -'iterator_count' => ['0|positive-int', 'iterator'=>'Traversable'], -'iterator_to_array' => ['array', 'iterator'=>'Traversable', 'preserve_keys='=>'bool'], +'iterator_count' => ['0|positive-int', 'iterator'=>'Traversable|array'], +'iterator_to_array' => ['array', 'iterator'=>'Traversable|array', 'preserve_keys='=>'bool'], 'IteratorAggregate::getIterator' => ['Traversable'], -'IteratorIterator::__construct' => ['void', 'it'=>'Traversable'], +'IteratorIterator::__construct' => ['void', 'iterator'=>'Traversable', 'class='=>'?string'], 'IteratorIterator::current' => ['mixed'], 'IteratorIterator::getInnerIterator' => ['Iterator'], 'IteratorIterator::key' => ['mixed'], @@ -6164,7 +6158,7 @@ return [ 'json_last_error_msg' => ['string'], 'json_validate' => ['bool', 'json'=>'string', 'depth='=>'positive-int', 'flags='=>'int'], 'JsonException::__clone' => ['void'], -'JsonException::__construct' => ['void'], +'JsonException::__construct' => ['void', "message="=>"string", 'code='=>'int', 'previous='=>'?Throwable'], 'JsonException::__toString' => ['string'], 'JsonException::__wakeup' => ['void'], 'JsonException::getCode' => ['int'], @@ -6339,7 +6333,7 @@ return [ 'ldap_count_entries' => ['int', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result'], 'ldap_delete' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], 'ldap_delete_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'controls='=>'?array'], -'ldap_dn2ufn' => ['string', 'dn'=>'string'], +'ldap_dn2ufn' => ['string|false', 'dn'=>'string'], 'ldap_err2str' => ['string', 'errno'=>'int'], 'ldap_errno' => ['int', 'ldap'=>'LDAP\Connection'], 'ldap_error' => ['string', 'ldap'=>'LDAP\Connection'], @@ -6359,7 +6353,7 @@ return [ 'ldap_get_option' => ['bool', 'ldap'=>'LDAP\Connection', 'option'=>'int', '&w_value='=>'array|string|int'], 'ldap_get_values' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'], 'ldap_get_values_len' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'], -'ldap_list' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], +'ldap_list' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], 'ldap_mod_add' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], 'ldap_mod_add_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], 'ldap_mod_del' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], @@ -6374,11 +6368,11 @@ return [ 'ldap_parse_exop' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_response_data='=>'string', '&w_response_oid='=>'string'], 'ldap_parse_reference' => ['bool', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', '&w_referrals'=>'array'], 'ldap_parse_result' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'], -'ldap_read' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], +'ldap_read' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], 'ldap_rename' => ['bool', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], 'ldap_rename_ext' => ['LDAP\Result|false', 'ldap'=>'LDAP\Connection', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], 'ldap_sasl_bind' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'?string', 'password='=>'?string', 'mech='=>'?string', 'realm='=>'?string', 'authc_id='=>'?string', 'authz_id='=>'?string', 'props='=>'?string'], -'ldap_search' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], +'ldap_search' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], 'ldap_set_option' => ['bool', 'ldap'=>'LDAP\Connection|null', 'option'=>'int', 'value'=>'mixed'], 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'LDAP\Connection', 'callback'=>'?callable'], 'ldap_start_tls' => ['bool', 'ldap'=>'LDAP\Connection'], @@ -6442,14 +6436,14 @@ return [ 'libxml_set_external_entity_loader' => ['bool', 'resolver_function'=>'(callable(string,string,array{directory:?string,intSubName:?string,extSubURI:?string,extSubSystem:?string}):(resource|string|null))|null'], 'libxml_set_streams_context' => ['void', 'context'=>'resource'], 'libxml_use_internal_errors' => ['bool', 'use_errors='=>'?bool'], -'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'count='=>'int'], +'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'limit='=>'int'], 'LimitIterator::current' => ['mixed'], 'LimitIterator::getInnerIterator' => ['Iterator'], 'LimitIterator::getPosition' => ['int'], 'LimitIterator::key' => ['mixed'], 'LimitIterator::next' => ['void'], 'LimitIterator::rewind' => ['void'], -'LimitIterator::seek' => ['int', 'position'=>'int'], +'LimitIterator::seek' => ['int', 'offset'=>'int'], 'LimitIterator::valid' => ['bool'], 'lineObj::__construct' => ['void'], 'lineObj::add' => ['int', 'point'=>'pointObj'], @@ -6848,14 +6842,14 @@ return [ 'memory_reset_peak_usage' => ['void'], 'MessageFormatter::__construct' => ['void', 'locale'=>'string', 'pattern'=>'string'], 'MessageFormatter::create' => ['MessageFormatter', 'locale'=>'string', 'pattern'=>'string'], -'MessageFormatter::format' => ['false|string', 'args'=>'array'], -'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'args'=>'array'], +'MessageFormatter::format' => ['false|string', 'values'=>'array'], +'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'values'=>'array'], 'MessageFormatter::getErrorCode' => ['int'], 'MessageFormatter::getErrorMessage' => ['string'], 'MessageFormatter::getLocale' => ['string'], 'MessageFormatter::getPattern' => ['string'], -'MessageFormatter::parse' => ['array|false', 'value'=>'string'], -'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'source'=>'string'], +'MessageFormatter::parse' => ['array|false', 'string'=>'string'], +'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'message'=>'string'], 'MessageFormatter::setPattern' => ['bool', 'pattern'=>'string'], 'metaphone' => ['string', 'string'=>'string', 'max_phonemes='=>'int'], 'method_exists' => ['bool', 'object_or_class'=>'object|class-string|interface-string|enum-string', 'method'=>'string'], @@ -7065,7 +7059,7 @@ return [ 'MongoDB::setReadPreference' => ['bool', 'read_preference'=>'string', 'tags='=>'array'], 'MongoDB::setSlaveOkay' => ['bool', 'ok='=>'bool'], 'MongoDB::setWriteConcern' => ['bool', 'w'=>'mixed', 'wtimeout='=>'int'], -'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type' => 'int'], +'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type=' => 'int'], 'MongoDB\BSON\Binary::getData' => ['string'], 'MongoDB\BSON\Binary::getType' => ['int'], 'MongoDB\BSON\Binary::__toString' => ['string'], @@ -7623,7 +7617,7 @@ return [ 'mt_rand\'1' => ['int'], 'mt_srand' => ['void', 'seed='=>'int', 'mode='=>'int'], 'MultipleIterator::__construct' => ['void', 'flags='=>'int'], -'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'infos='=>'string'], +'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'info='=>'string|int|null'], 'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'], 'MultipleIterator::countIterators' => ['int'], 'MultipleIterator::current' => ['array|false'], @@ -7811,7 +7805,6 @@ return [ 'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'?string'], 'mysqli::connect' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null'], 'mysqli::debug' => ['bool', 'options'=>'string'], -'mysqli::disable_reads_from_master' => ['bool'], 'mysqli::dump_debug_info' => ['bool'], 'mysqli::escape_string' => ['string', 'string'=>'string'], 'mysqli::execute_query' => ['mysqli_result|bool', 'query'=>'non-empty-string', 'params='=>'list|null'], @@ -7826,23 +7819,19 @@ return [ 'mysqli::next_result' => ['bool'], 'mysqli::options' => ['bool', 'option'=>'int', 'value'=>'string|int'], 'mysqli::ping' => ['bool'], -'mysqli::poll' => ['int|false', '&w_read'=>'array', '&w_write'=>'array', '&w_error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], +'mysqli::poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], 'mysqli::prepare' => ['mysqli_stmt|false', 'query'=>'string'], 'mysqli::query' => ['bool|mysqli_result', 'query'=>'string', 'result_mode='=>'int'], -'mysqli::real_connect' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'], +'mysqli::real_connect' => ['bool', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'], 'mysqli::real_escape_string' => ['string', 'string'=>'string'], 'mysqli::real_query' => ['bool', 'query'=>'string'], 'mysqli::reap_async_query' => ['mysqli_result|false'], 'mysqli::refresh' => ['bool', 'flags'=>'int'], 'mysqli::release_savepoint' => ['bool', 'name'=>'string'], 'mysqli::rollback' => ['bool', 'flags='=>'int', 'name='=>'?string'], -'mysqli::rpl_query_type' => ['int', 'query'=>'string'], 'mysqli::savepoint' => ['bool', 'name'=>'string'], 'mysqli::select_db' => ['bool', 'database'=>'string'], -'mysqli::send_query' => ['bool', 'query'=>'string'], 'mysqli::set_charset' => ['bool', 'charset'=>'string'], -'mysqli::set_local_infile_default' => ['void'], -'mysqli::set_local_infile_handler' => ['bool', 'read_func='=>'callable'], 'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'], 'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli::stat' => ['string|false'], @@ -7864,8 +7853,6 @@ return [ 'mysqli_debug' => ['true', 'options'=>'string'], 'mysqli_disable_reads_from_master' => ['bool', 'link'=>'mysqli'], 'mysqli_disable_rpl_parse' => ['bool', 'link'=>'mysqli'], -'mysqli_driver::embedded_server_end' => ['void'], -'mysqli_driver::embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'], 'mysqli_dump_debug_info' => ['bool', 'mysql'=>'mysqli'], 'mysqli_embedded_server_end' => ['void'], 'mysqli_embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'], @@ -7885,9 +7872,9 @@ return [ 'mysqli_fetch_array\'2' => ['list|false|null', 'result'=>'mysqli_result', 'mode='=>'2'], 'mysqli_fetch_assoc' => ['array|false|null', 'result'=>'mysqli_result'], 'mysqli_fetch_column' => ['null|int|float|string|false', 'result'=>'mysqli_result', 'column='=>'int'], -'mysqli_fetch_field' => ['object|false', 'result'=>'mysqli_result'], -'mysqli_fetch_field_direct' => ['object|false', 'result'=>'mysqli_result', 'index'=>'int'], -'mysqli_fetch_fields' => ['stdClass[]', 'result'=>'mysqli_result'], +'mysqli_fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result'], +'mysqli_fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result', 'index'=>'int'], +'mysqli_fetch_fields' => ['list', 'result'=>'mysqli_result'], 'mysqli_fetch_lengths' => ['array|false', 'result'=>'mysqli_result'], 'mysqli_fetch_object' => ['object|false|null', 'result'=>'mysqli_result', 'class='=>'string', 'constructor_args='=>'array'], 'mysqli_fetch_row' => ['list|false|null', 'result'=>'mysqli_result'], @@ -7920,10 +7907,10 @@ return [ 'mysqli_num_rows' => ['int<0, max>|numeric-string', 'result'=>'mysqli_result'], 'mysqli_options' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'], 'mysqli_ping' => ['bool', 'mysql'=>'mysqli'], -'mysqli_poll' => ['int|false', 'read'=>'array', 'write'=>'array', 'error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], +'mysqli_poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], 'mysqli_prepare' => ['mysqli_stmt|false', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_query' => ['mysqli_result|bool', 'mysql'=>'mysqli', 'query'=>'string', 'result_mode='=>'int'], -'mysqli_real_connect' => ['bool', 'mysql='=>'mysqli', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'], +'mysqli_real_connect' => ['bool', 'mysql'=>'mysqli', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'], 'mysqli_real_escape_string' => ['string', 'mysql'=>'mysqli', 'string'=>'string'], 'mysqli_real_query' => ['bool', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_reap_async_query' => ['mysqli_result|false', 'mysql'=>'mysqli'], @@ -7941,9 +7928,9 @@ return [ 'mysqli_result::fetch_array\'2' => ['list|false|null', 'mode='=>'2'], 'mysqli_result::fetch_assoc' => ['array|false|null'], 'mysqli_result::fetch_column' => ['null|int|float|string|false', 'column='=>'int'], -'mysqli_result::fetch_field' => ['object|false'], -'mysqli_result::fetch_field_direct' => ['object|false', 'index'=>'int'], -'mysqli_result::fetch_fields' => ['stdClass[]'], +'mysqli_result::fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false'], +'mysqli_result::fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'index'=>'int'], +'mysqli_result::fetch_fields' => ['list'], 'mysqli_result::fetch_object' => ['object|false|null', 'class='=>'string', 'constructor_args='=>'array'], 'mysqli_result::fetch_row' => ['list|false|null'], 'mysqli_result::field_seek' => ['bool', 'index'=>'int'], @@ -7965,10 +7952,10 @@ return [ 'mysqli_sqlstate' => ['string', 'mysql'=>'mysqli'], 'mysqli_ssl_set' => ['true', 'mysql'=>'mysqli', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli_stat' => ['string|false', 'mysql'=>'mysqli'], -'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query'=>'string'], +'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query='=>'?string'], 'mysqli_stmt::attr_get' => ['int', 'attribute'=>'int'], 'mysqli_stmt::attr_set' => ['bool', 'attribute'=>'int', 'value'=>'int'], -'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'], +'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'], 'mysqli_stmt::bind_result' => ['bool', '&w_var1'=>'', '&...w_vars='=>''], 'mysqli_stmt::close' => ['bool'], 'mysqli_stmt::data_seek' => ['void', 'offset'=>'int'], @@ -7988,7 +7975,7 @@ return [ 'mysqli_stmt_affected_rows' => ['int<-1, max>|numeric-string', 'statement'=>'mysqli_stmt'], 'mysqli_stmt_attr_get' => ['int', 'statement'=>'mysqli_stmt', 'attribute'=>'int'], 'mysqli_stmt_attr_set' => ['bool', 'statement'=>'mysqli_stmt', 'attribute'=>'int', 'value'=>'int'], -'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'], +'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'], 'mysqli_stmt_bind_result' => ['bool', 'statement'=>'mysqli_stmt', '&w_var1'=>'', '&...w_vars='=>''], 'mysqli_stmt_close' => ['true', 'statement'=>'mysqli_stmt'], 'mysqli_stmt_data_seek' => ['void', 'statement'=>'mysqli_stmt', 'offset'=>'int'], @@ -8163,28 +8150,28 @@ return [ 'nsapi_response_headers' => ['array'], 'nsapi_virtual' => ['bool', 'uri'=>'string'], 'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'], -'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'], +'number_format' => ['string', 'num'=>'float', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'], 'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'], 'NumberFormatter::create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'], 'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'], -'NumberFormatter::formatCurrency' => ['string|false', 'num'=>'float', 'currency'=>'string'], -'NumberFormatter::getAttribute' => ['int|false', 'attr'=>'int'], +'NumberFormatter::formatCurrency' => ['string|false', 'amount'=>'float', 'currency'=>'string'], +'NumberFormatter::getAttribute' => ['int|float|false', 'attribute'=>'int'], 'NumberFormatter::getErrorCode' => ['int'], 'NumberFormatter::getErrorMessage' => ['string'], 'NumberFormatter::getLocale' => ['string', 'type='=>'int'], 'NumberFormatter::getPattern' => ['string|false'], -'NumberFormatter::getSymbol' => ['string|false', 'attr'=>'int'], -'NumberFormatter::getTextAttribute' => ['string|false', 'attr'=>'int'], -'NumberFormatter::parse' => ['float|false', 'string'=>'string', 'type='=>'int', '&rw_position='=>'int'], -'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_position='=>'int'], -'NumberFormatter::setAttribute' => ['bool', 'attr'=>'int', 'value'=>''], +'NumberFormatter::getSymbol' => ['string|false', 'symbol'=>'int'], +'NumberFormatter::getTextAttribute' => ['string|false', 'attribute'=>'int'], +'NumberFormatter::parse' => ['int|float|false', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'], +'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'], +'NumberFormatter::setAttribute' => ['bool', 'attribute'=>'int', 'value'=>'int|float'], 'NumberFormatter::setPattern' => ['bool', 'pattern'=>'string'], -'NumberFormatter::setSymbol' => ['bool', 'attr'=>'int', 'symbol'=>'string'], -'NumberFormatter::setTextAttribute' => ['bool', 'attr'=>'int', 'value'=>'string'], +'NumberFormatter::setSymbol' => ['bool', 'symbol'=>'int', 'value'=>'string'], +'NumberFormatter::setTextAttribute' => ['bool', 'attribute'=>'int', 'value'=>'string'], 'numfmt_create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'], 'numfmt_format' => ['string|false', 'formatter'=>'NumberFormatter', 'num'=>'int|float', 'type='=>'int'], 'numfmt_format_currency' => ['string|false', 'formatter'=>'NumberFormatter', 'amount'=>'float', 'currency'=>'string'], -'numfmt_get_attribute' => ['int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'], +'numfmt_get_attribute' => ['float|int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'], 'numfmt_get_error_code' => ['int', 'formatter'=>'NumberFormatter'], 'numfmt_get_error_message' => ['string', 'formatter'=>'NumberFormatter'], 'numfmt_get_locale' => ['string', 'formatter'=>'NumberFormatter', 'type='=>'int'], @@ -8193,7 +8180,7 @@ return [ 'numfmt_get_text_attribute' => ['string|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'], 'numfmt_parse' => ['float|int|false', 'formatter'=>'NumberFormatter', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'], 'numfmt_parse_currency' => ['float|false', 'formatter'=>'NumberFormatter', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'], -'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'int'], +'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'float|int'], 'numfmt_set_pattern' => ['bool', 'formatter'=>'NumberFormatter', 'pattern'=>'string'], 'numfmt_set_symbol' => ['bool', 'formatter'=>'NumberFormatter', 'symbol'=>'int', 'value'=>'string'], 'numfmt_set_text_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'string'], @@ -8408,9 +8395,9 @@ return [ 'odbc_pconnect' => ['resource|false', 'dsn'=>'string', 'user'=>'string', 'password'=>'string', 'cursor_option='=>'int'], 'odbc_prepare' => ['resource|false', 'odbc'=>'resource', 'query'=>'string'], 'odbc_primarykeys' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'?string', 'schema'=>'string', 'table'=>'string'], -'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string', 'column'=>'string'], -'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string'], -'odbc_result' => ['mixed|false', 'statement'=>'resource', 'field'=>'mixed'], +'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string', 'column='=>'?string'], +'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string'], +'odbc_result' => ['string|bool|null', 'statement'=>'resource', 'field'=>'string|int'], 'odbc_result_all' => ['int|false', 'statement'=>'resource', 'format='=>'string'], 'odbc_rollback' => ['bool', 'odbc'=>'resource'], 'odbc_setoption' => ['bool', 'odbc'=>'resource', 'which'=>'int', 'option'=>'int', 'value'=>'int'], @@ -8476,7 +8463,7 @@ return [ 'openssl_pkcs12_read' => ['bool', 'pkcs12'=>'string', '&w_certificates'=>'array', 'passphrase'=>'string'], 'openssl_pkcs7_decrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'OpenSSLCertificate|string', 'private_key='=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string|null'], 'openssl_pkcs7_encrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'OpenSSLCertificate|list|string', 'headers'=>'array|null', 'flags='=>'int', 'cipher_algo='=>'int'], -'openssl_pkcs7_read' => ['bool', 'input_filename'=>'string', '&w_certificates'=>'array'], +'openssl_pkcs7_read' => ['bool', 'data'=>'string', '&w_certificates'=>'array'], 'openssl_pkcs7_sign' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'OpenSSLCertificate|string', 'private_key'=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string', 'headers'=>'array|null', 'flags='=>'int', 'untrusted_certificates_filename='=>'string|null'], 'openssl_pkcs7_verify' => ['bool|int', 'input_filename'=>'string', 'flags'=>'int', 'signers_certificates_filename='=>'?string', 'ca_info='=>'array', 'untrusted_certificates_filename='=>'?string', 'content='=>'?string', 'output_filename='=>'?string'], 'openssl_pkey_derive' => ['string|false', 'public_key'=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string', 'private_key'=>'OpenSSLAsymmetricKey|OpenSSLCertificate|array{OpenSSLAsymmetricKey|OpenSSLCertificate|string, string}|string', 'key_length='=>'int'], @@ -8998,8 +8985,6 @@ return [ 'PDFlib::utf32_to_utf16' => ['string', 'utf32string'=>'string', 'ordering'=>'string'], 'PDFlib::utf8_to_utf16' => ['string', 'utf8string'=>'string', 'ordering'=>'string'], 'PDO::__construct' => ['void', 'dsn'=>'string', 'username='=>'?string', 'password='=>'?string', 'options='=>'?array'], -'PDO::__sleep' => ['list'], -'PDO::__wakeup' => ['void'], 'PDO::beginTransaction' => ['bool'], 'PDO::commit' => ['bool'], 'PDO::cubrid_schema' => ['array', 'schema_type'=>'int', 'table_name='=>'string', 'col_name='=>'string'], @@ -9038,8 +9023,6 @@ return [ 'PDOException::getPrevious' => ['?Throwable'], 'PDOException::getTrace' => ['list\',args?:array}>'], 'PDOException::getTraceAsString' => ['string'], -'PDOStatement::__sleep' => ['list'], -'PDOStatement::__wakeup' => ['void'], 'PDOStatement::bindColumn' => ['bool', 'column'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'], 'PDOStatement::bindParam' => ['bool', 'param'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'], 'PDOStatement::bindValue' => ['bool', 'param'=>'string|int', 'value'=>'mixed', 'type='=>'int'], @@ -9640,7 +9623,7 @@ return [ 'rand\'1' => ['int'], 'random_bytes' => ['non-empty-string', 'length'=>'positive-int'], 'random_int' => ['int', 'min'=>'int', 'max'=>'int'], -'range' => ['array', 'start'=>'mixed', 'end'=>'mixed', 'step='=>'int|float'], +'range' => ['non-empty-array', 'start'=>'string|int|float', 'end'=>'string|int|float', 'step='=>'int<1, max>|float'], 'RangeException::__clone' => ['void'], 'RangeException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'RangeException::__toString' => ['string'], @@ -10443,7 +10426,7 @@ return [ 'RedisCluster::zUnionStore' => ['int', 'Output'=>'string', 'ZSetKeys'=>'array', 'Weights='=>'?array', 'aggregateFunction='=>'string'], 'Reflection::getModifierNames' => ['list', 'modifiers'=>'int'], 'ReflectionClass::__clone' => ['void'], -'ReflectionClass::__construct' => ['void', 'argument'=>'object|class-string'], +'ReflectionClass::__construct' => ['void', 'objectOrClass'=>'object|class-string'], 'ReflectionClass::__toString' => ['string'], 'ReflectionClass::getAttributes' => ['list', 'name='=>'?string', 'flags='=>'int'], 'ReflectionClass::getConstant' => ['mixed', 'name'=>'string'], @@ -10477,7 +10460,7 @@ return [ 'ReflectionClass::hasConstant' => ['bool', 'name'=>'string'], 'ReflectionClass::hasMethod' => ['bool', 'name'=>'string'], 'ReflectionClass::hasProperty' => ['bool', 'name'=>'string'], -'ReflectionClass::implementsInterface' => ['bool', 'interface_name'=>'interface-string|ReflectionClass'], +'ReflectionClass::implementsInterface' => ['bool', 'interface'=>'interface-string|ReflectionClass'], 'ReflectionClass::inNamespace' => ['bool'], 'ReflectionClass::isAbstract' => ['bool'], 'ReflectionClass::isAnonymous' => ['bool'], @@ -10497,7 +10480,7 @@ return [ 'ReflectionClass::newInstanceArgs' => ['object', 'args='=>'list|array'], 'ReflectionClass::newInstanceWithoutConstructor' => ['object'], 'ReflectionClass::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'mixed'], -'ReflectionClassConstant::__construct' => ['void', 'class'=>'mixed', 'name'=>'string'], +'ReflectionClassConstant::__construct' => ['void', 'class'=>'object|class-string', 'constant'=>'string'], 'ReflectionClassConstant::__toString' => ['string'], 'ReflectionClassConstant::getAttributes' => ['list', 'name='=>'?string', 'flags='=>'int'], 'ReflectionClassConstant::getDeclaringClass' => ['ReflectionClass'], @@ -10530,7 +10513,7 @@ return [ 'ReflectionExtension::info' => ['void'], 'ReflectionExtension::isPersistent' => ['bool'], 'ReflectionExtension::isTemporary' => ['bool'], -'ReflectionFunction::__construct' => ['void', 'name'=>'callable-string|Closure'], +'ReflectionFunction::__construct' => ['void', 'function'=>'callable-string|Closure'], 'ReflectionFunction::__toString' => ['string'], 'ReflectionFunction::getClosure' => ['Closure'], 'ReflectionFunction::getClosureScopeClass' => ['ReflectionClass'], @@ -10592,7 +10575,7 @@ return [ 'ReflectionFunctionAbstract::isUserDefined' => ['bool'], 'ReflectionFunctionAbstract::isVariadic' => ['bool'], 'ReflectionFunctionAbstract::returnsReference' => ['bool'], -'ReflectionGenerator::__construct' => ['void', 'generator'=>'object'], +'ReflectionGenerator::__construct' => ['void', 'generator'=>'Generator'], 'ReflectionGenerator::getExecutingFile' => ['string'], 'ReflectionGenerator::getExecutingGenerator' => ['Generator'], 'ReflectionGenerator::getExecutingLine' => ['int'], @@ -10640,17 +10623,17 @@ return [ 'ReflectionMethod::isUserDefined' => ['bool'], 'ReflectionMethod::isVariadic' => ['bool'], 'ReflectionMethod::returnsReference' => ['bool'], -'ReflectionMethod::setAccessible' => ['void', 'visible'=>'bool'], +'ReflectionMethod::setAccessible' => ['void', 'accessible'=>'bool'], 'ReflectionNamedType::__clone' => ['void'], 'ReflectionNamedType::__toString' => ['string'], 'ReflectionNamedType::allowsNull' => ['bool'], 'ReflectionNamedType::getName' => ['string'], 'ReflectionNamedType::isBuiltin' => ['bool'], 'ReflectionObject::__clone' => ['void'], -'ReflectionObject::__construct' => ['void', 'argument'=>'object'], +'ReflectionObject::__construct' => ['void', 'object'=>'object'], 'ReflectionObject::__toString' => ['string'], 'ReflectionObject::getConstant' => ['mixed', 'name'=>'string'], -'ReflectionObject::getConstants' => ['array'], +'ReflectionObject::getConstants' => ['array', 'filter='=>'?int'], 'ReflectionObject::getConstructor' => ['?ReflectionMethod'], 'ReflectionObject::getDefaultProperties' => ['array'], 'ReflectionObject::getDocComment' => ['false|string'], @@ -10669,7 +10652,7 @@ return [ 'ReflectionObject::getProperties' => ['ReflectionProperty[]', 'filter='=>'?int'], 'ReflectionObject::getProperty' => ['ReflectionProperty', 'name'=>'string'], 'ReflectionObject::getReflectionConstant' => ['ReflectionClassConstant', 'name'=>'string'], -'ReflectionObject::getReflectionConstants' => ['list<\ReflectionClassConstant>'], +'ReflectionObject::getReflectionConstants' => ['list<\ReflectionClassConstant>', 'filter='=>'?int'], 'ReflectionObject::getShortName' => ['string'], 'ReflectionObject::getStartLine' => ['false|int'], 'ReflectionObject::getStaticProperties' => ['ReflectionProperty[]'], @@ -10680,7 +10663,7 @@ return [ 'ReflectionObject::hasConstant' => ['bool', 'name'=>'string'], 'ReflectionObject::hasMethod' => ['bool', 'name'=>'string'], 'ReflectionObject::hasProperty' => ['bool', 'name'=>'string'], -'ReflectionObject::implementsInterface' => ['bool', 'interface_name'=>'ReflectionClass|string'], +'ReflectionObject::implementsInterface' => ['bool', 'interface'=>'ReflectionClass|interface-string'], 'ReflectionObject::inNamespace' => ['bool'], 'ReflectionObject::isAbstract' => ['bool'], 'ReflectionObject::isAnonymous' => ['bool'], @@ -10701,7 +10684,7 @@ return [ 'ReflectionObject::newInstanceWithoutConstructor' => ['object'], 'ReflectionObject::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'string'], 'ReflectionParameter::__clone' => ['void'], -'ReflectionParameter::__construct' => ['void', 'function'=>'', 'parameter'=>''], +'ReflectionParameter::__construct' => ['void', 'function'=>'string|array|object', 'param'=>'int|string'], 'ReflectionParameter::__toString' => ['string'], 'ReflectionParameter::allowsNull' => ['bool'], 'ReflectionParameter::canBePassedByValue' => ['bool'], @@ -10723,7 +10706,7 @@ return [ 'ReflectionParameter::isPassedByReference' => ['bool'], 'ReflectionParameter::isVariadic' => ['bool'], 'ReflectionProperty::__clone' => ['void'], -'ReflectionProperty::__construct' => ['void', 'class'=>'', 'name'=>'string'], +'ReflectionProperty::__construct' => ['void', 'class'=>'object|class-string', 'property'=>'string'], 'ReflectionProperty::__toString' => ['string'], 'ReflectionProperty::getAttributes' => ['list', 'name='=>'?string', 'flags='=>'int'], 'ReflectionProperty::getDeclaringClass' => ['ReflectionClass'], @@ -10738,7 +10721,7 @@ return [ 'ReflectionProperty::isProtected' => ['bool'], 'ReflectionProperty::isPublic' => ['bool'], 'ReflectionProperty::isStatic' => ['bool'], -'ReflectionProperty::setAccessible' => ['void', 'visible'=>'bool'], +'ReflectionProperty::setAccessible' => ['void', 'accessible'=>'bool'], 'ReflectionProperty::setValue' => ['void', 'object'=>'null|object', 'value'=>''], 'ReflectionProperty::setValue\'1' => ['void', 'value'=>''], 'ReflectionType::__clone' => ['void'], @@ -10776,13 +10759,13 @@ return [ 'rename' => ['bool', 'from'=>'string', 'to'=>'string', 'context='=>'resource'], 'rename_function' => ['bool', 'original_name'=>'string', 'new_name'=>'string'], 'reset' => ['mixed|false', '&r_array'=>'array|object'], -'ResourceBundle::__construct' => ['void', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'], +'ResourceBundle::__construct' => ['void', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'], 'ResourceBundle::count' => ['int'], -'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'], +'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'], 'ResourceBundle::get' => ['mixed', 'index'=>'string|int', 'fallback='=>'bool'], 'ResourceBundle::getErrorCode' => ['int'], 'ResourceBundle::getErrorMessage' => ['string'], -'ResourceBundle::getLocales' => ['array', 'bundlename'=>'string'], +'ResourceBundle::getLocales' => ['array|false', 'bundle'=>'string'], 'resourcebundle_count' => ['int', 'bundle'=>'ResourceBundle'], 'resourcebundle_create' => ['?ResourceBundle', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'], 'resourcebundle_get' => ['mixed|null', 'bundle'=>'ResourceBundle', 'index'=>'string|int', 'fallback='=>'bool'], @@ -10795,7 +10778,7 @@ return [ 'rewind' => ['bool', 'stream'=>'resource'], 'rewinddir' => ['void', 'dir_handle='=>'resource'], 'rmdir' => ['bool', 'directory'=>'string', 'context='=>'resource'], -'round' => ['float', 'num'=>'float', 'precision='=>'int', 'mode='=>'0|positive-int'], +'round' => ['float', 'num'=>'float|int', 'precision='=>'int', 'mode='=>'0|positive-int'], 'rpm_close' => ['bool', 'rpmr'=>'resource'], 'rpm_get_tag' => ['mixed', 'rpmr'=>'resource', 'tagnum'=>'int'], 'rpm_is_valid' => ['bool', 'filename'=>'string'], @@ -11169,18 +11152,18 @@ return [ 'ServerResponse::setStatus' => ['void', 'status'=>'int'], 'ServerResponse::setVersion' => ['void', 'version'=>'string'], 'session_abort' => ['bool'], -'session_cache_expire' => ['int', 'value='=>'?int'], -'session_cache_limiter' => ['string', 'value='=>'?string'], +'session_cache_expire' => ['int|false', 'value='=>'?int'], +'session_cache_limiter' => ['string|false', 'value='=>'?string'], 'session_commit' => ['bool'], -'session_create_id' => ['string', 'prefix='=>'string'], +'session_create_id' => ['string|false', 'prefix='=>'string'], 'session_decode' => ['bool', 'data'=>'string'], 'session_destroy' => ['bool'], -'session_encode' => ['string'], +'session_encode' => ['string|false'], 'session_gc' => ['int|false'], 'session_get_cookie_params' => ['array'], 'session_id' => ['string|false', 'id='=>'?string'], 'session_is_registered' => ['bool', 'name'=>'string'], -'session_module_name' => ['string', 'module='=>'?string'], +'session_module_name' => ['string|false', 'module='=>'?string'], 'session_name' => ['string|false', 'name='=>'?string'], 'session_pgsql_add_error' => ['bool', 'error_level'=>'int', 'error_message='=>'string'], 'session_pgsql_get_error' => ['array', 'with_error_message='=>'bool'], @@ -11192,7 +11175,7 @@ return [ 'session_register' => ['bool', 'name'=>'mixed', '...args='=>'mixed'], 'session_register_shutdown' => ['void'], 'session_reset' => ['bool'], -'session_save_path' => ['string', 'path='=>'?string'], +'session_save_path' => ['string|false', 'path='=>'?string'], 'session_set_cookie_params' => ['bool', 'lifetime'=>'int', 'path='=>'?string', 'domain='=>'?string', 'secure='=>'?bool', 'httponly='=>'?bool'], 'session_set_cookie_params\'1' => ['bool', 'options'=>'array{lifetime?:?int,path?:?string,domain?:?string,secure?:?bool,httponly?:?bool,samesite?:?string}'], 'session_set_save_handler' => ['bool', 'open'=>'callable(string,string):bool', 'close'=>'callable():bool', 'read'=>'callable(string):string', 'write'=>'callable(string,string):bool', 'destroy'=>'callable(string):bool', 'gc'=>'callable(string):bool', 'create_sid='=>'callable():string', 'validate_sid='=>'callable(string):bool', 'update_timestamp='=>'callable(string):bool'], @@ -11205,9 +11188,9 @@ return [ 'SessionHandler::close' => ['bool'], 'SessionHandler::create_sid' => ['string'], 'SessionHandler::destroy' => ['bool', 'id'=>'string'], -'SessionHandler::gc' => ['bool', 'maxlifetime'=>'int'], -'SessionHandler::open' => ['bool', 'save_path'=>'string', 'session_name'=>'string'], -'SessionHandler::read' => ['string', 'id'=>'string'], +'SessionHandler::gc' => ['int|false', 'max_lifetime'=>'int'], +'SessionHandler::open' => ['bool', 'path'=>'string', 'name'=>'string'], +'SessionHandler::read' => ['string|false', 'id'=>'string'], 'SessionHandler::write' => ['bool', 'id'=>'string', 'data'=>'string'], 'SessionHandlerInterface::close' => ['bool'], 'SessionHandlerInterface::destroy' => ['bool', 'id'=>'string'], @@ -11305,40 +11288,40 @@ return [ 'simplexml_import_dom' => ['?SimpleXMLElement', 'node'=>'DOMNode', 'class_name='=>'?string'], 'simplexml_load_file' => ['SimpleXMLElement|false', 'filename'=>'string', 'class_name='=>'?string', 'options='=>'int', 'namespace_or_prefix='=>'string', 'is_prefix='=>'bool'], 'simplexml_load_string' => ['SimpleXMLElement|false', 'data'=>'string', 'class_name='=>'?string', 'options='=>'int', 'namespace_or_prefix='=>'string', 'is_prefix='=>'bool'], -'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'data_is_url='=>'bool', 'ns='=>'string', 'is_prefix='=>'bool'], +'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'dataIsURL='=>'bool', 'namespaceOrPrefix='=>'string', 'isPrefix='=>'bool'], 'SimpleXMLElement::__get' => ['SimpleXMLElement', 'name'=>'string'], 'SimpleXMLElement::__toString' => ['string'], -'SimpleXMLElement::addAttribute' => ['void', 'name'=>'string', 'value='=>'string', 'ns='=>'string'], -'SimpleXMLElement::addChild' => ['SimpleXMLElement', 'name'=>'string', 'value='=>'string', 'ns='=>'string'], +'SimpleXMLElement::addAttribute' => ['void', 'qualifiedName'=>'string', 'value'=>'string', 'namespace='=>'?string'], +'SimpleXMLElement::addChild' => ['?SimpleXMLElement', 'qualifiedName'=>'string', 'value='=>'?string', 'namespace='=>'?string'], 'SimpleXMLElement::asXML' => ['string|bool', 'filename='=>'?string'], 'SimpleXMLElement::asXML\'1' => ['string|false'], -'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'], -'SimpleXMLElement::children' => ['SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'], +'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'], +'SimpleXMLElement::children' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'], 'SimpleXMLElement::count' => ['int'], -'SimpleXMLElement::getDocNamespaces' => ['string[]', 'recursive='=>'bool', 'from_root='=>'bool'], +'SimpleXMLElement::getDocNamespaces' => ['array', 'recursive='=>'bool', 'fromRoot='=>'bool'], 'SimpleXMLElement::getName' => ['string'], -'SimpleXMLElement::getNamespaces' => ['string[]', 'recursive='=>'bool'], +'SimpleXMLElement::getNamespaces' => ['array', 'recursive='=>'bool'], 'SimpleXMLElement::offsetExists' => ['bool', 'offset'=>'int|string'], 'SimpleXMLElement::offsetGet' => ['SimpleXMLElement', 'offset'=>'int|string'], 'SimpleXMLElement::offsetSet' => ['void', 'offset'=>'int|string', 'value'=>'mixed'], 'SimpleXMLElement::offsetUnset' => ['void', 'offset'=>'int|string'], -'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'ns'=>'string'], +'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'namespace'=>'string'], 'SimpleXMLElement::saveXML' => ['string|bool', 'filename='=>'?string'], -'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false', 'path'=>'string'], +'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false|null', 'expression'=>'string'], 'sin' => ['float', 'num'=>'float'], 'sinh' => ['float', 'num'=>'float'], 'sizeof' => ['int<0, max>', 'value'=>'Countable|array', 'mode='=>'int'], 'sleep' => ['int', 'seconds'=>'0|positive-int'], -'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], +'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], 'SNMP::__construct' => ['void', 'version'=>'int', 'hostname'=>'string', 'community'=>'string', 'timeout='=>'int', 'retries='=>'int'], 'SNMP::close' => ['bool'], 'SNMP::get' => ['array|string|false', 'objectId'=>'string|array', 'preserveKeys='=>'bool'], @@ -11347,7 +11330,7 @@ return [ 'SNMP::getnext' => ['string|array|false', 'objectId'=>'string|array'], 'SNMP::set' => ['bool', 'objectId'=>'string|array', 'type'=>'string|array', 'value'=>'string|array'], 'SNMP::setSecurity' => ['bool', 'securityLevel'=>'string', 'authProtocol='=>'string', 'authPassphrase='=>'string', 'privacyProtocol='=>'string', 'privacyPassphrase='=>'string', 'contextName='=>'string', 'contextEngineId='=>'string'], -'SNMP::walk' => ['array|false', 'objectId'=>'string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'], +'SNMP::walk' => ['array|false', 'objectId'=>'array|string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'], 'snmp_get_quick_print' => ['bool'], 'snmp_get_valueretrieval' => ['int'], 'snmp_read_mib' => ['bool', 'filename'=>'string'], @@ -11356,12 +11339,12 @@ return [ 'snmp_set_oid_output_format' => ['true', 'format'=>'int'], 'snmp_set_quick_print' => ['bool', 'enable'=>'bool'], 'snmp_set_valueretrieval' => ['true', 'method'=>'int'], -'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'], -'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], -'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], +'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'], +'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], +'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], 'SoapClient::__call' => ['', 'function_name'=>'string', 'arguments'=>'array'], 'SoapClient::__construct' => ['void', 'wsdl'=>'mixed', 'options='=>'array|null'], 'SoapClient::__doRequest' => ['?string', 'request'=>'string', 'location'=>'string', 'action'=>'string', 'version'=>'int', 'one_way='=>'bool'], @@ -11506,7 +11489,7 @@ return [ 'sodium_crypto_secretstream_xchacha20poly1305_init_pull' => ['string', 'header'=>'string', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_init_push' => ['array', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_keygen' => ['non-empty-string'], -'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], +'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array|false', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&w_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'], 'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', '&w_state'=>'string'], 'sodium_crypto_shorthash' => ['string', 'message'=>'string', 'key'=>'string'], @@ -12281,10 +12264,10 @@ return [ 'SplDoublyLinkedList::isEmpty' => ['bool'], 'SplDoublyLinkedList::key' => ['int'], 'SplDoublyLinkedList::next' => ['void'], -'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'mixed'], -'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'mixed'], -'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'mixed', 'value'=>'mixed'], -'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'mixed'], +'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'int'], +'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'int'], +'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'?int', 'value'=>'mixed'], +'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'int'], 'SplDoublyLinkedList::pop' => ['mixed'], 'SplDoublyLinkedList::prev' => ['void'], 'SplDoublyLinkedList::push' => ['void', 'value'=>'mixed'], @@ -12300,7 +12283,6 @@ return [ 'SplEnum::getConstList' => ['array', 'include_default='=>'bool'], 'SplFileInfo::__construct' => ['void', 'filename'=>'string'], 'SplFileInfo::__toString' => ['string'], -'SplFileInfo::__wakeup' => ['void'], 'SplFileInfo::getATime' => ['int|false'], 'SplFileInfo::getBasename' => ['string', 'suffix='=>'string'], 'SplFileInfo::getCTime' => ['int|false'], @@ -12390,19 +12372,15 @@ return [ 'SplFixedArray::__construct' => ['void', 'size='=>'int'], 'SplFixedArray::__wakeup' => ['void'], 'SplFixedArray::count' => ['int'], -'SplFixedArray::current' => ['mixed'], 'SplFixedArray::fromArray' => ['SplFixedArray', 'array'=>'array', 'preserveKeys='=>'bool'], +'SplFixedArray::getIterator' => ['Iterator'], 'SplFixedArray::getSize' => ['int'], -'SplFixedArray::key' => ['int'], -'SplFixedArray::next' => ['void'], 'SplFixedArray::offsetExists' => ['bool', 'index'=>'int'], 'SplFixedArray::offsetGet' => ['mixed', 'index'=>'int'], 'SplFixedArray::offsetSet' => ['void', 'index'=>'int', 'value'=>'mixed'], 'SplFixedArray::offsetUnset' => ['void', 'index'=>'int'], -'SplFixedArray::rewind' => ['void'], 'SplFixedArray::setSize' => ['bool', 'size'=>'int'], 'SplFixedArray::toArray' => ['array'], -'SplFixedArray::valid' => ['bool'], 'SplHeap::__construct' => ['void'], 'SplHeap::compare' => ['int', 'value1'=>'mixed', 'value2'=>'mixed'], 'SplHeap::count' => ['int'], @@ -12586,7 +12564,7 @@ return [ 'Spoofchecker::setChecks' => ['void', 'checks'=>'int'], 'Spoofchecker::setRestrictionLevel' => ['void', 'level'=>'int'], 'sprintf' => ['string', 'format'=>'string', '...values='=>'string|int|float'], -'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'], +'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'], 'SQLite3::busyTimeout' => ['bool', 'milliseconds'=>'int'], 'SQLite3::changes' => ['int'], 'SQLite3::close' => ['bool'], @@ -12600,7 +12578,7 @@ return [ 'SQLite3::lastErrorMsg' => ['string'], 'SQLite3::lastInsertRowID' => ['int'], 'SQLite3::loadExtension' => ['bool', 'name'=>'string'], -'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'], +'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'], 'SQLite3::openBlob' => ['resource|false', 'table'=>'string', 'column'=>'string', 'rowid'=>'int', 'database='=>'string', 'flags='=>'int'], 'SQLite3::prepare' => ['SQLite3Stmt|false', 'query'=>'string'], 'SQLite3::query' => ['SQLite3Result|false', 'query'=>'string'], @@ -12723,7 +12701,7 @@ return [ 'sqlsrv_commit' => ['bool', 'conn'=>'resource'], 'sqlsrv_configure' => ['bool', 'setting'=>'string', 'value'=>'mixed'], 'sqlsrv_connect' => ['resource|false', 'serverName'=>'string', 'connectionInfo='=>'array'], -'sqlsrv_errors' => ['?array', 'errorsOrWarnings='=>'int'], +'sqlsrv_errors' => ['?array', 'errorsAndOrWarnings='=>'int'], 'sqlsrv_execute' => ['bool', 'stmt'=>'resource'], 'sqlsrv_fetch' => ['?bool', 'stmt'=>'resource', 'row='=>'int', 'offset='=>'int'], 'sqlsrv_fetch_array' => ['array|null|false', 'stmt'=>'resource', 'fetchType='=>'int', 'row='=>'int', 'offset='=>'int'], @@ -12898,7 +12876,7 @@ return [ 'str_replace' => ['string|string[]', 'search'=>'string|array', 'replace'=>'string|array', 'subject'=>'string|array', '&w_count='=>'int'], 'str_rot13' => ['string', 'string'=>'string'], 'str_shuffle' => ['string', 'string'=>'string'], -'str_split' => ['list', 'string'=>'string', 'length='=>'positive-int'], +'str_split' => ['list', 'string'=>'string', 'length='=>'positive-int'], 'str_starts_with' => ['bool', 'haystack'=>'string', 'needle'=>'string'], 'str_word_count' => ['array|int', 'string'=>'string', 'format='=>'int', 'characters='=>'?string'], 'strcasecmp' => ['int', 'string1'=>'string', 'string2'=>'string'], @@ -12913,7 +12891,7 @@ return [ 'stream_context_create' => ['resource', 'options='=>'?array', 'params='=>'?array'], 'stream_context_get_default' => ['resource', 'options='=>'?array'], 'stream_context_get_options' => ['array', 'stream_or_context'=>'resource'], -'stream_context_get_params' => ['array', 'context'=>'resource'], +'stream_context_get_params' => ['array{notification:string,options:array}', 'context'=>'resource'], 'stream_context_set_default' => ['resource', 'options'=>'array'], 'stream_context_set_option' => ['bool', 'context'=>'', 'wrapper_or_options'=>'string', 'option_name'=>'string', 'value'=>''], 'stream_context_set_option\'1' => ['bool', 'context'=>'', 'wrapper_or_options'=>'array'], @@ -12944,10 +12922,10 @@ return [ 'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'?float', '&w_peer_name='=>'string'], 'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'?float', 'flags='=>'int', 'context='=>'?resource'], 'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'?int', 'session_stream='=>'?resource'], -'stream_socket_get_name' => ['string', 'socket'=>'resource', 'remote'=>'bool'], +'stream_socket_get_name' => ['string|false', 'socket'=>'resource', 'remote'=>'bool'], 'stream_socket_pair' => ['resource[]|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'], -'stream_socket_recvfrom' => ['string', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'], -'stream_socket_sendto' => ['int', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'], +'stream_socket_recvfrom' => ['string|false', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'], +'stream_socket_sendto' => ['int|false', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'], 'stream_socket_server' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'flags='=>'int', 'context='=>'resource'], 'stream_socket_shutdown' => ['bool', 'stream'=>'resource', 'mode'=>'int'], 'stream_supports_lock' => ['bool', 'stream'=>'resource'], @@ -13603,7 +13581,7 @@ return [ 'SyncSharedMemory::size' => ['int'], 'SyncSharedMemory::write' => ['int', 'string='=>'string', 'start='=>'int'], 'sys_get_temp_dir' => ['string'], -'sys_getloadavg' => ['array'], +'sys_getloadavg' => ['array|false'], 'syslog' => ['true', 'priority'=>'int', 'message'=>'string'], 'system' => ['string|false', 'command'=>'string', '&w_result_code='=>'int'], 'taint' => ['bool', '&rw_string'=>'string', '&...w_other_strings='=>'string'], @@ -13985,7 +13963,7 @@ return [ 'Transliterator::getErrorCode' => ['int'], 'Transliterator::getErrorMessage' => ['string'], 'Transliterator::listIDs' => ['array'], -'Transliterator::transliterate' => ['string|false', 'subject'=>'string', 'start='=>'int', 'end='=>'int'], +'Transliterator::transliterate' => ['string|false', 'string'=>'string', 'start='=>'int', 'end='=>'int'], 'transliterator_create' => ['?Transliterator', 'id'=>'string', 'direction='=>'int'], 'transliterator_create_from_rules' => ['?Transliterator', 'rules'=>'string', 'direction='=>'int'], 'transliterator_create_inverse' => ['?Transliterator', 'transliterator'=>'Transliterator'], @@ -14008,8 +13986,8 @@ return [ 'uasort' => ['true', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'], 'ucfirst' => ['string', 'string'=>'string'], 'UConverter::__construct' => ['void', 'destination_encoding='=>'?string', 'source_encoding='=>'?string'], -'UConverter::convert' => ['string', 'string'=>'string', 'reverse='=>'bool'], -'UConverter::fromUCallback' => ['mixed', 'reason'=>'int', 'source'=>'string', 'codePoint'=>'string', '&w_error'=>'int'], +'UConverter::convert' => ['string', 'str'=>'string', 'reverse='=>'bool'], +'UConverter::fromUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'array', 'codePoint'=>'int', '&w_error'=>'int'], 'UConverter::getAliases' => ['array|false|null', 'name'=>'string'], 'UConverter::getAvailable' => ['array'], 'UConverter::getDestinationEncoding' => ['string|false|null'], @@ -14020,12 +13998,12 @@ return [ 'UConverter::getSourceType' => ['int|false|null'], 'UConverter::getStandards' => ['?array'], 'UConverter::getSubstChars' => ['string|false|null'], -'UConverter::reasonText' => ['string', 'reason='=>'int'], +'UConverter::reasonText' => ['string', 'reason'=>'int'], 'UConverter::setDestinationEncoding' => ['bool', 'encoding'=>'string'], 'UConverter::setSourceEncoding' => ['bool', 'encoding'=>'string'], 'UConverter::setSubstChars' => ['bool', 'chars'=>'string'], 'UConverter::toUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'string', 'codeUnits'=>'string', '&w_error'=>'int'], -'UConverter::transcode' => ['string', 'string'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'], +'UConverter::transcode' => ['string', 'str'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'], 'ucwords' => ['string', 'string'=>'string', 'separators='=>'string'], 'udm_add_search_limit' => ['bool', 'agent'=>'resource', 'var'=>'int', 'value'=>'string'], 'udm_alloc_agent' => ['resource', 'dbaddr'=>'string', 'dbmode='=>'string'], @@ -14239,7 +14217,7 @@ return [ 'UnexpectedValueException::getTrace' => ['list\',args?:array}>'], 'UnexpectedValueException::getTraceAsString' => ['string'], 'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'], -'unixtojd' => ['int', 'timestamp='=>'?int'], +'unixtojd' => ['int|false', 'timestamp='=>'?int'], 'unlink' => ['bool', 'filename'=>'string', 'context='=>'resource'], 'unpack' => ['array|false', 'format'=>'string', 'string'=>'string', 'offset='=>'int'], 'unregister_tick_function' => ['void', 'callback'=>'callable'], @@ -14502,17 +14480,12 @@ return [ 'wddx_packet_start' => ['resource|false', 'comment='=>'string'], 'wddx_serialize_value' => ['string|false', 'value'=>'mixed', 'comment='=>'string'], 'wddx_serialize_vars' => ['string|false', 'var_name'=>'mixed', '...vars='=>'mixed'], -'WeakMap::__construct' => ['void'], 'WeakMap::count' => ['int'], -'WeakMap::current' => ['mixed'], -'WeakMap::key' => ['object'], -'WeakMap::next' => ['void'], +'WeakMap::getIterator' => ['Iterator'], 'WeakMap::offsetExists' => ['bool', 'object'=>'object'], 'WeakMap::offsetGet' => ['mixed', 'object'=>'object'], 'WeakMap::offsetSet' => ['void', 'object'=>'object', 'value'=>'mixed'], 'WeakMap::offsetUnset' => ['void', 'object'=>'object'], -'WeakMap::rewind' => ['void'], -'WeakMap::valid' => ['bool'], 'Weakref::acquire' => ['bool'], 'Weakref::get' => ['object'], 'Weakref::release' => ['bool'], @@ -14728,7 +14701,7 @@ return [ 'xml_parser_create' => ['XMLParser', 'encoding='=>'?string'], 'xml_parser_create_ns' => ['XMLParser', 'encoding='=>'?string', 'separator='=>'string'], 'xml_parser_free' => ['bool', 'parser'=>'XMLParser'], -'xml_parser_get_option' => ['string', 'parser'=>'XMLParser', 'option'=>'int'], +'xml_parser_get_option' => ['string|int', 'parser'=>'XMLParser', 'option'=>'int'], 'xml_parser_set_option' => ['bool', 'parser'=>'XMLParser', 'option'=>'int', 'value'=>'mixed'], 'xml_set_character_data_handler' => ['true', 'parser'=>'XMLParser', 'handler'=>'callable'], 'xml_set_default_handler' => ['true', 'parser'=>'XMLParser', 'handler'=>'callable'], @@ -14753,18 +14726,18 @@ return [ 'XMLReader::expand' => ['DOMNode|false', 'baseNode='=>'?DOMNode'], 'XMLReader::getAttribute' => ['?string', 'name'=>'string'], 'XMLReader::getAttributeNo' => ['?string', 'index'=>'int'], -'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespaceuri'=>'string'], +'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespace'=>'string'], 'XMLReader::getParserProperty' => ['bool', 'property'=>'int'], 'XMLReader::isValid' => ['bool'], 'XMLReader::lookupNamespace' => ['?string', 'prefix'=>'string'], 'XMLReader::moveToAttribute' => ['bool', 'name'=>'string'], 'XMLReader::moveToAttributeNo' => ['bool', 'index'=>'int'], -'XMLReader::moveToAttributeNs' => ['bool', 'localname'=>'string', 'namespaceuri'=>'string'], +'XMLReader::moveToAttributeNs' => ['bool', 'name'=>'string', 'namespace'=>'string'], 'XMLReader::moveToElement' => ['bool'], 'XMLReader::moveToFirstAttribute' => ['bool'], 'XMLReader::moveToNextAttribute' => ['bool'], -'XMLReader::next' => ['bool', 'localname='=>'string'], -'XMLReader::open' => ['bool', 'uri'=>'string', 'encoding='=>'?string', 'options='=>'int'], +'XMLReader::next' => ['bool', 'name='=>'?string'], +'XMLReader::open' => ['bool|XmlReader', 'uri'=>'string', 'encoding='=>'?string', 'flags='=>'int'], 'XMLReader::read' => ['bool'], 'XMLReader::readInnerXML' => ['string'], 'XMLReader::readOuterXML' => ['string'], @@ -14773,7 +14746,7 @@ return [ 'XMLReader::setRelaxNGSchema' => ['bool', 'filename'=>'?string'], 'XMLReader::setRelaxNGSchemaSource' => ['bool', 'source'=>'?string'], 'XMLReader::setSchema' => ['bool', 'filename'=>'?string'], -'XMLReader::XML' => ['bool', 'source'=>'string', 'encoding='=>'?string', 'options='=>'int'], +'XMLReader::XML' => ['bool|XMLReader', 'source'=>'string', 'encoding='=>'?string', 'flags='=>'int'], 'XMLWriter::endAttribute' => ['bool'], 'XMLWriter::endCdata' => ['bool'], 'XMLWriter::endComment' => ['bool'], @@ -14866,13 +14839,13 @@ return [ 'XsltProcessor::getSecurityPrefs' => ['int'], 'XSLTProcessor::hasExsltSupport' => ['bool'], 'XSLTProcessor::importStylesheet' => ['bool', 'stylesheet'=>'object'], -'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'mixed'], +'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'array|string|null'], 'XSLTProcessor::removeParameter' => ['bool', 'namespace'=>'string', 'name'=>'string'], 'XSLTProcessor::setParameter' => ['bool', 'namespace'=>'string', 'name'=>'string', 'value'=>'string'], 'XSLTProcessor::setParameter\'1' => ['bool', 'namespace'=>'string', 'options'=>'array'], 'XSLTProcessor::setProfiling' => ['bool', 'filename'=>'?string'], 'XsltProcessor::setSecurityPrefs' => ['int', 'preferences'=>'int'], -'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode'], +'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode', 'returnClass='=>'?string'], 'XSLTProcessor::transformToURI' => ['int', 'document'=>'DOMDocument', 'uri'=>'string'], 'XSLTProcessor::transformToXML' => ['string|false', 'document'=>'DOMDocument'], 'yac::__construct' => ['void', 'prefix='=>'string'], @@ -15778,7 +15751,7 @@ return [ 'zip_entry_compressedsize' => ['int', 'zip_entry'=>'resource'], 'zip_entry_compressionmethod' => ['string', 'zip_entry'=>'resource'], 'zip_entry_filesize' => ['int', 'zip_entry'=>'resource'], -'zip_entry_name' => ['string', 'zip_entry'=>'resource'], +'zip_entry_name' => ['string|false', 'zip_entry'=>'resource'], 'zip_entry_open' => ['bool', 'zip_dp'=>'resource', 'zip_entry'=>'resource', 'mode='=>'string'], 'zip_entry_read' => ['string|false', 'zip_entry'=>'resource', 'len='=>'int'], 'zip_open' => ['resource|int|false', 'filename'=>'string'], @@ -15806,8 +15779,8 @@ return [ 'ZipArchive::getStream' => ['resource|false', 'name'=>'string'], 'ZipArchive::getStreamIndex' => ['resource|false', 'index'=>'int', 'flags='=>'int'], 'ZipArchive::getStreamName' => ['resource|false', 'name'=>'string', 'flags='=>'int'], -'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'], -'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'], +'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'], +'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'], 'ZipArchive::locateName' => ['int|false', 'name'=>'string', 'flags='=>'int'], 'ZipArchive::open' => ['int|bool', 'filename'=>'string', 'flags='=>'int'], 'ZipArchive::registerCancelCallback' => ['bool', 'callback'=>'callable'], @@ -15818,9 +15791,9 @@ return [ 'ZipArchive::setArchiveComment' => ['bool', 'comment'=>'string'], 'ZipArchive::setCommentIndex' => ['bool', 'index'=>'int', 'comment'=>'string'], 'ZipArchive::setCommentName' => ['bool', 'name'=>'string', 'comment'=>'string'], -'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'comp_method'=>'int', 'comp_flags='=>'int'], -'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'comp_method'=>'int', 'comp_flags='=>'int'], -'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'?string'], +'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'compflags='=>'int'], +'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'method'=>'int', 'compflags='=>'int'], +'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'?string'], 'ZipArchive::setEncryptionName' => ['bool', 'name'=>'string', 'method'=>'int', 'password='=>'?string'], 'ZipArchive::setExternalAttributesIndex' => ['bool', 'index'=>'int', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'], 'ZipArchive::setExternalAttributesName' => ['bool', 'name'=>'string', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'], diff --git a/vendor/vimeo/psalm/dictionaries/CallMap_71_delta.php b/vendor/vimeo/psalm/dictionaries/CallMap_71_delta.php index 93bb8cae..8f11f599 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap_71_delta.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap_71_delta.php @@ -30,7 +30,7 @@ return [ 'sapi_windows_cp_get' => ['int', 'kind='=>'string'], 'sapi_windows_cp_is_utf8' => ['bool'], 'sapi_windows_cp_set' => ['bool', 'codepage'=>'int'], - 'session_create_id' => ['string', 'prefix='=>'string'], + 'session_create_id' => ['string|false', 'prefix='=>'string'], 'session_gc' => ['int|false'], ], 'changed' => [ @@ -42,6 +42,10 @@ return [ 'old' => ['string|false', 'value'=>'IntlCalendar|DateTime|array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int}|array{tm_sec: int, tm_min: int, tm_hour: int, tm_mday: int, tm_mon: int, tm_year: int, tm_wday: int, tm_yday: int, tm_isdst: int}|string|int|float'], 'new' => ['string|false', 'value'=>'IntlCalendar|DateTimeInterface|array{0: int, 1: int, 2: int, 3: int, 4: int, 5: int, 6: int, 7: int, 8: int}|array{tm_sec: int, tm_min: int, tm_hour: int, tm_mday: int, tm_mon: int, tm_year: int, tm_wday: int, tm_yday: int, tm_isdst: int}|string|int|float'], ], + 'SessionHandler::gc' => [ + 'old' => ['bool', 'max_lifetime'=>'int'], + 'new' => ['int|false', 'max_lifetime'=>'int'], + ], 'SQLite3::createFunction' => [ 'old' => ['bool', 'name'=>'string', 'callback'=>'callable', 'argCount='=>'int'], 'new' => ['bool', 'name'=>'string', 'callback'=>'callable', 'argCount='=>'int', 'flags='=>'int'], diff --git a/vendor/vimeo/psalm/dictionaries/CallMap_72_delta.php b/vendor/vimeo/psalm/dictionaries/CallMap_72_delta.php index 1956ee7c..aedc76cd 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap_72_delta.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap_72_delta.php @@ -19,7 +19,7 @@ return [ 'DOMNodeList::count' => ['int'], 'ReflectionClass::isIterable' => ['bool'], 'ZipArchive::count' => ['int'], - 'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'string'], + 'ZipArchive::setEncryptionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'string'], 'ZipArchive::setEncryptionName' => ['bool', 'name'=>'string', 'method'=>'int', 'password='=>'string'], 'ftp_append' => ['bool', 'ftp'=>'resource', 'remote_filename'=>'string', 'local_filename'=>'string', 'mode='=>'int'], 'hash_hmac_algos' => ['list'], @@ -103,7 +103,7 @@ return [ 'sodium_crypto_secretstream_xchacha20poly1305_init_pull' => ['string', 'header'=>'string', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_init_push' => ['array', 'key'=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_keygen' => ['non-empty-string'], - 'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], + 'sodium_crypto_secretstream_xchacha20poly1305_pull' => ['array|false', '&r_state'=>'string', 'ciphertext'=>'string', 'additional_data='=>'string'], 'sodium_crypto_secretstream_xchacha20poly1305_push' => ['string', '&w_state'=>'string', 'message'=>'string', 'additional_data='=>'string', 'tag='=>'int'], 'sodium_crypto_secretstream_xchacha20poly1305_rekey' => ['void', '&w_state'=>'string'], 'sodium_crypto_shorthash' => ['string', 'message'=>'string', 'key'=>'string'], diff --git a/vendor/vimeo/psalm/dictionaries/CallMap_73_delta.php b/vendor/vimeo/psalm/dictionaries/CallMap_73_delta.php index 568603b6..d79026c3 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap_73_delta.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap_73_delta.php @@ -18,7 +18,7 @@ return [ 'added' => [ 'DateTime::createFromImmutable' => ['static', 'object'=>'DateTimeImmutable'], 'JsonException::__clone' => ['void'], - 'JsonException::__construct' => ['void'], + 'JsonException::__construct' => ['void', "message="=>"string", 'code='=>'int', 'previous='=>'?Throwable'], 'JsonException::__toString' => ['string'], 'JsonException::__wakeup' => ['void'], 'JsonException::getCode' => ['int'], @@ -77,8 +77,8 @@ return [ 'new' => ['bool|string', 'ldap'=>'resource', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array'], ], 'ldap_list' => [ - 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], - 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], + 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], ], 'ldap_mod_add' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'], @@ -101,16 +101,16 @@ return [ 'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'modifications_info'=>'array', 'controls='=>'array'], ], 'ldap_read' => [ - 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], - 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], + 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], ], 'ldap_rename' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], 'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], ], 'ldap_search' => [ - 'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], - 'new' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], + 'old' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'new' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], ], ], 'removed' => [ diff --git a/vendor/vimeo/psalm/dictionaries/CallMap_80_delta.php b/vendor/vimeo/psalm/dictionaries/CallMap_80_delta.php index 89b4bdfc..134b79a0 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap_80_delta.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap_80_delta.php @@ -28,6 +28,13 @@ return [ 'ReflectionParameter::getAttributes' => ['list', 'name='=>'?string', 'flags='=>'int'], 'ReflectionProperty::getAttributes' => ['list', 'name='=>'?string', 'flags='=>'int'], 'ReflectionUnionType::getTypes' => ['list'], + 'SplFixedArray::getIterator' => ['Iterator'], + 'WeakMap::count' => ['int'], + 'WeakMap::getIterator' => ['Iterator'], + 'WeakMap::offsetExists' => ['bool', 'object'=>'object'], + 'WeakMap::offsetGet' => ['mixed', 'object'=>'object'], + 'WeakMap::offsetSet' => ['void', 'object'=>'object', 'value'=>'mixed'], + 'WeakMap::offsetUnset' => ['void', 'object'=>'object'], 'fdiv' => ['float', 'num1'=>'float', 'num2'=>'float'], 'get_debug_type' => ['string', 'value'=>'mixed'], 'get_resource_id' => ['int', 'resource'=>'resource'], @@ -217,6 +224,10 @@ return [ 'old' => ['string', 'locale'=>'string', 'displayLocale='=>'string'], 'new' => ['string', 'locale'=>'string', 'displayLocale='=>'?string'], ], + 'mysqli_stmt::__construct' => [ + 'old' => ['void', 'mysql'=>'mysqli', 'query='=>'string'], + 'new' => ['void', 'mysql'=>'mysqli', 'query='=>'?string'], + ], 'NumberFormatter::__construct' => [ 'old' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'new' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'?string'], @@ -373,6 +384,14 @@ return [ 'old' => ['?Closure', 'object='=>'object'], 'new' => ['Closure', 'object='=>'?object'], ], + 'ReflectionObject::getConstants' => [ + 'old' => ['array'], + 'new' => ['array', 'filter='=>'?int'], + ], + 'ReflectionObject::getReflectionConstants' => [ + 'old' => ['list<\ReflectionClassConstant>'], + 'new' => ['list<\ReflectionClassConstant>', 'filter='=>'?int'], + ], 'ReflectionObject::newInstanceArgs' => [ 'old' => ['object', 'args='=>'list'], 'new' => ['object', 'args='=>'list|array'], @@ -461,6 +480,10 @@ return [ 'old' => ['string|false'], 'new' => ['string'], ], + 'XMLReader::next' => [ + 'old' => ['bool', 'name='=>'string'], + 'new' => ['bool', 'name='=>'?string'], + ], 'XMLWriter::startAttributeNs' => [ 'old' => ['bool', 'prefix'=>'string', 'name'=>'string', 'namespace'=>'?string'], 'new' => ['bool', 'prefix'=>'?string', 'name'=>'string', 'namespace'=>'?string'], @@ -478,8 +501,8 @@ return [ 'new' => ['string'], ], 'ZipArchive::setEncryptionIndex' => [ - 'old' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'string'], - 'new' => ['bool', 'index'=>'int', 'method'=>'string', 'password='=>'?string'], + 'old' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'string'], + 'new' => ['bool', 'index'=>'int', 'method'=>'int', 'password='=>'?string'], ], 'ZipArchive::setEncryptionName' => [ 'old' => ['bool', 'name'=>'string', 'method'=>'int', 'password='=>'string'], @@ -618,8 +641,8 @@ return [ 'new' => ['void', 'handle'=>'CurlHandle'], ], 'curl_copy_handle' => [ - 'old' => ['resource', 'ch'=>'resource'], - 'new' => ['CurlHandle', 'handle'=>'CurlHandle'], + 'old' => ['resource|false', 'ch'=>'resource'], + 'new' => ['CurlHandle|false', 'handle'=>'CurlHandle'], ], 'curl_errno' => [ 'old' => ['int', 'ch'=>'resource'], @@ -798,8 +821,8 @@ return [ 'new' => ['DOMElement', 'node'=>'SimpleXMLElement'], ], 'easter_date' => [ - 'old' => ['int', 'year='=>'int'], - 'new' => ['int', 'year='=>'?int'], + 'old' => ['int', 'year='=>'int', 'mode='=>'int'], + 'new' => ['int', 'year='=>'?int', 'mode='=>'int'], ], 'easter_days' => [ 'old' => ['int', 'year='=>'int', 'mode='=>'int'], @@ -934,8 +957,8 @@ return [ 'new' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], ], 'get_class_methods' => [ - 'old' => ['list|null', 'object_or_class'=>'mixed'], - 'new' => ['list', 'object_or_class'=>'object|class-string'], + 'old' => ['list|null', 'object_or_class'=>'mixed'], + 'new' => ['list', 'object_or_class'=>'object|class-string'], ], 'get_headers' => [ 'old' => ['array|false', 'url'=>'string', 'associative='=>'int', 'context='=>'?resource'], @@ -982,12 +1005,12 @@ return [ 'new' => ['string|false', 'stream'=>'resource', 'length='=>'?int'], ], 'gzputs' => [ - 'old' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], - 'new' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], + 'old' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], + 'new' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], ], 'gzwrite' => [ - 'old' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], - 'new' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], + 'old' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], + 'new' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'?int'], ], 'hash' => [ 'old' => ['string|false', 'algo'=>'string', 'data'=>'string', 'binary='=>'bool'], @@ -1506,8 +1529,8 @@ return [ 'new' => ['bool|string', 'ldap'=>'resource', 'user='=>'string', 'old_password='=>'string', 'new_password='=>'string', '&w_controls='=>'array|null'], ], 'ldap_list' => [ - 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], - 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], + 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_rename_ext' => [ 'old' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], @@ -1546,16 +1569,16 @@ return [ 'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'modifications_info'=>'array', 'controls='=>'?array'], ], 'ldap_read' => [ - 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], - 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], + 'new' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_rename' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], 'new' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], ], 'ldap_search' => [ - 'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], - 'new' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'old' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'array'], + 'new' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_set_rebind_proc' => [ 'old' => ['bool', 'ldap'=>'resource', 'callback'=>'callable'], @@ -1866,8 +1889,8 @@ return [ 'new' => ['bool', 'mysql'=>'mysqli', 'flags='=>'int', 'name='=>'?string'], ], 'number_format' => [ - 'old' => ['string', 'num'=>'float|int', 'decimals='=>'int'], - 'new' => ['string', 'num'=>'float|int', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'], + 'old' => ['string', 'num'=>'float', 'decimals='=>'int'], + 'new' => ['string', 'num'=>'float', 'decimals='=>'int', 'decimal_separator='=>'?string', 'thousands_separator='=>'?string'], ], 'numfmt_create' => [ 'old' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], @@ -2186,28 +2209,28 @@ return [ 'new' => ['bool', 'semaphore'=>'SysvSemaphore'], ], 'session_cache_expire' => [ - 'old' => ['int', 'value='=>'int'], - 'new' => ['int', 'value='=>'?int'], + 'old' => ['int|false', 'value='=>'int'], + 'new' => ['int|false', 'value='=>'?int'], ], 'session_cache_limiter' => [ - 'old' => ['string', 'value='=>'string'], - 'new' => ['string', 'value='=>'?string'], + 'old' => ['string|false', 'value='=>'string'], + 'new' => ['string|false', 'value='=>'?string'], ], 'session_id' => [ 'old' => ['string|false', 'id='=>'string'], 'new' => ['string|false', 'id='=>'?string'], ], 'session_module_name' => [ - 'old' => ['string', 'module='=>'string'], - 'new' => ['string', 'module='=>'?string'], + 'old' => ['string|false', 'module='=>'string'], + 'new' => ['string|false', 'module='=>'?string'], ], 'session_name' => [ 'old' => ['string|false', 'name='=>'string'], 'new' => ['string|false', 'name='=>'?string'], ], 'session_save_path' => [ - 'old' => ['string', 'path='=>'string'], - 'new' => ['string', 'path='=>'?string'], + 'old' => ['string|false', 'path='=>'string'], + 'new' => ['string|false', 'path='=>'?string'], ], 'session_set_cookie_params' => [ 'old' => ['bool', 'lifetime'=>'int', 'path='=>'string', 'domain='=>'string', 'secure='=>'bool', 'httponly='=>'bool'], @@ -2578,8 +2601,8 @@ return [ 'new' => ['int', 'mask='=>'?int'], ], 'unixtojd' => [ - 'old' => ['int', 'timestamp='=>'int'], - 'new' => ['int', 'timestamp='=>'?int'], + 'old' => ['int|false', 'timestamp='=>'int'], + 'new' => ['int|false', 'timestamp='=>'?int'], ], 'xml_get_current_byte_index' => [ 'old' => ['int|false', 'parser'=>'resource'], @@ -2618,8 +2641,8 @@ return [ 'new' => ['bool', 'parser'=>'XMLParser'], ], 'xml_parser_get_option' => [ - 'old' => ['string|false', 'parser'=>'resource', 'option'=>'int'], - 'new' => ['string', 'parser'=>'XMLParser', 'option'=>'int'], + 'old' => ['string|int', 'parser'=>'resource', 'option'=>'int'], + 'new' => ['string|int', 'parser'=>'XMLParser', 'option'=>'int'], ], 'xml_parser_set_option' => [ 'old' => ['bool', 'parser'=>'resource', 'option'=>'int', 'value'=>'mixed'], @@ -2862,7 +2885,7 @@ return [ 'ldap_control_paged_result' => ['bool', 'link_identifier'=>'resource', 'pagesize'=>'int', 'iscritical='=>'bool', 'cookie='=>'string'], 'ldap_control_paged_result_response' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', '&w_cookie'=>'string', '&w_estimated'=>'int'], 'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'], - 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'], + 'number_format\'1' => ['string', 'num'=>'float', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'], 'png2wbmp' => ['bool', 'pngname'=>'string', 'wbmpname'=>'string', 'dest_height'=>'int', 'dest_width'=>'int', 'threshold'=>'int'], 'read_exif_data' => ['array', 'filename'=>'string', 'sections_needed='=>'string', 'sub_arrays='=>'bool', 'read_thumbnail='=>'bool'], 'Reflection::export' => ['?string', 'r'=>'reflector', 'return='=>'bool'], @@ -2883,6 +2906,11 @@ return [ 'SimpleXMLIterator::next' => ['void'], 'SimpleXMLIterator::hasChildren' => ['bool'], 'SimpleXMLIterator::getChildren' => ['?SimpleXMLIterator'], + 'SplFixedArray::current' => ['mixed'], + 'SplFixedArray::key' => ['int'], + 'SplFixedArray::next' => ['void'], + 'SplFixedArray::rewind' => ['void'], + 'SplFixedArray::valid' => ['bool'], 'SplTempFileObject::fgetss' => ['string', 'allowable_tags='=>'string'], 'xmlrpc_decode' => ['mixed', 'xml'=>'string', 'encoding='=>'string'], 'xmlrpc_decode_request' => ['?array', 'xml'=>'string', '&w_method'=>'string', 'encoding='=>'string'], diff --git a/vendor/vimeo/psalm/dictionaries/CallMap_81_delta.php b/vendor/vimeo/psalm/dictionaries/CallMap_81_delta.php index ded1f5e4..3bd304ea 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap_81_delta.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap_81_delta.php @@ -619,8 +619,8 @@ return [ 'new' => ['array|false', 'ldap'=>'LDAP\Connection', 'entry'=>'LDAP\ResultEntry', 'attribute'=>'string'], ], 'ldap_list' => [ - 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], - 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_mod_add' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'?array'], @@ -679,8 +679,8 @@ return [ 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'result'=>'LDAP\Result', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'], ], 'ldap_read' => [ - 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], - 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'old' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_rename' => [ 'old' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'?array'], @@ -695,8 +695,8 @@ return [ 'new' => ['bool', 'ldap'=>'LDAP\Connection', 'dn='=>'?string', 'password='=>'?string', 'mech='=>'?string', 'realm='=>'?string', 'authc_id='=>'?string', 'authz_id='=>'?string', 'props='=>'?string'], ], 'ldap_search' => [ - 'old' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], - 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'old' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], + 'new' => ['LDAP\Result|LDAP\Result[]|false', 'ldap'=>'LDAP\Connection|LDAP\Connection[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int', 'controls='=>'?array'], ], 'ldap_set_option' => [ 'old' => ['bool', 'ldap'=>'resource|null', 'option'=>'int', 'value'=>'mixed'], diff --git a/vendor/vimeo/psalm/dictionaries/CallMap_82_delta.php b/vendor/vimeo/psalm/dictionaries/CallMap_82_delta.php index 555ea008..47ded4fb 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap_82_delta.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap_82_delta.php @@ -41,9 +41,17 @@ return [ 'old' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'string', '...handler_params='=>'string'], 'new' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'?string', 'permission='=>'int', 'map_size='=>'int', 'flags='=>'?int'], ], + 'iterator_count' => [ + 'old' => ['0|positive-int', 'iterator'=>'Traversable'], + 'new' => ['0|positive-int', 'iterator'=>'Traversable|array'], + ], + 'iterator_to_array' => [ + 'old' => ['array', 'iterator'=>'Traversable', 'preserve_keys='=>'bool'], + 'new' => ['array', 'iterator'=>'Traversable|array', 'preserve_keys='=>'bool'], + ], 'str_split' => [ 'old' => ['non-empty-list', 'string'=>'string', 'length='=>'positive-int'], - 'new' => ['list', 'string'=>'string', 'length='=>'positive-int'], + 'new' => ['list', 'string'=>'string', 'length='=>'positive-int'], ], ], diff --git a/vendor/vimeo/psalm/dictionaries/CallMap_historical.php b/vendor/vimeo/psalm/dictionaries/CallMap_historical.php index 2b14a8a5..a88bab36 100644 --- a/vendor/vimeo/psalm/dictionaries/CallMap_historical.php +++ b/vendor/vimeo/psalm/dictionaries/CallMap_historical.php @@ -293,7 +293,6 @@ return [ 'COMPersistHelper::SaveToStream' => ['int', 'stream'=>''], 'COMPersistHelper::__construct' => ['void', 'variant'=>'object'], 'CURLFile::__construct' => ['void', 'filename'=>'string', 'mime_type='=>'string', 'posted_filename='=>'string'], - 'CURLFile::__wakeup' => ['void'], 'CURLFile::getFilename' => ['string'], 'CURLFile::getMimeType' => ['string'], 'CURLFile::getPostFilename' => ['string'], @@ -1623,8 +1622,6 @@ return [ 'GEOSWKTWriter::setRoundingPrecision' => ['void', 'prec'=>'int'], 'GEOSWKTWriter::setTrim' => ['void', 'trim'=>'bool'], 'GEOSWKTWriter::write' => ['string', 'geom'=>'GEOSGeometry'], - 'GMP::__construct' => ['void'], - 'GMP::__toString' => ['numeric-string'], 'GearmanClient::__construct' => ['void'], 'GearmanClient::addOptions' => ['bool', 'options'=>'int'], 'GearmanClient::addServer' => ['bool', 'host='=>'string', 'port='=>'int'], @@ -1738,7 +1735,6 @@ return [ 'Gender\Gender::get' => ['int', 'name'=>'string', 'country='=>'int'], 'Gender\Gender::isNick' => ['array', 'name0'=>'string', 'name1'=>'string', 'country='=>'int'], 'Gender\Gender::similarNames' => ['array', 'name'=>'string', 'country='=>'int'], - 'Generator::__wakeup' => ['void'], 'Generator::current' => ['mixed'], 'Generator::getReturn' => ['mixed'], 'Generator::key' => ['mixed'], @@ -3265,7 +3261,7 @@ return [ 'Iterator::rewind' => ['void'], 'Iterator::valid' => ['bool'], 'IteratorAggregate::getIterator' => ['Traversable'], - 'IteratorIterator::__construct' => ['void', 'it'=>'Traversable'], + 'IteratorIterator::__construct' => ['void', 'iterator'=>'Traversable', 'class='=>'?string'], 'IteratorIterator::current' => ['mixed'], 'IteratorIterator::getInnerIterator' => ['Iterator'], 'IteratorIterator::key' => ['mixed'], @@ -3387,14 +3383,14 @@ return [ 'LevelDBWriteBatch::delete' => ['', 'key'=>'', 'write_options='=>'array'], 'LevelDBWriteBatch::put' => ['', 'key'=>'', 'value'=>'', 'write_options='=>'array'], 'LevelDBWriteBatch::set' => ['', 'key'=>'', 'value'=>'', 'write_options='=>'array'], - 'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'count='=>'int'], + 'LimitIterator::__construct' => ['void', 'iterator'=>'Iterator', 'offset='=>'int', 'limit='=>'int'], 'LimitIterator::current' => ['mixed'], 'LimitIterator::getInnerIterator' => ['Iterator'], 'LimitIterator::getPosition' => ['int'], 'LimitIterator::key' => ['mixed'], 'LimitIterator::next' => ['void'], 'LimitIterator::rewind' => ['void'], - 'LimitIterator::seek' => ['int', 'position'=>'int'], + 'LimitIterator::seek' => ['int', 'offset'=>'int'], 'LimitIterator::valid' => ['bool'], 'Locale::acceptFromHttp' => ['string|false', 'header'=>'string'], 'Locale::canonicalize' => ['string', 'locale'=>'string'], @@ -3539,14 +3535,14 @@ return [ 'Memcached::touchByKey' => ['bool', 'server_key'=>'string', 'key'=>'string', 'expiration='=>'int'], 'MessageFormatter::__construct' => ['void', 'locale'=>'string', 'pattern'=>'string'], 'MessageFormatter::create' => ['MessageFormatter', 'locale'=>'string', 'pattern'=>'string'], - 'MessageFormatter::format' => ['false|string', 'args'=>'array'], - 'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'args'=>'array'], + 'MessageFormatter::format' => ['false|string', 'values'=>'array'], + 'MessageFormatter::formatMessage' => ['false|string', 'locale'=>'string', 'pattern'=>'string', 'values'=>'array'], 'MessageFormatter::getErrorCode' => ['int'], 'MessageFormatter::getErrorMessage' => ['string'], 'MessageFormatter::getLocale' => ['string'], 'MessageFormatter::getPattern' => ['string'], - 'MessageFormatter::parse' => ['array|false', 'value'=>'string'], - 'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'source'=>'string'], + 'MessageFormatter::parse' => ['array|false', 'string'=>'string'], + 'MessageFormatter::parseMessage' => ['array|false', 'locale'=>'string', 'pattern'=>'string', 'message'=>'string'], 'MessageFormatter::setPattern' => ['bool', 'pattern'=>'string'], 'Mongo::__construct' => ['void', 'server='=>'string', 'options='=>'array', 'driver_options='=>'array'], 'Mongo::__get' => ['MongoDB', 'dbname'=>'string'], @@ -3735,7 +3731,7 @@ return [ 'MongoDBRef::create' => ['array', 'collection'=>'string', 'id'=>'mixed', 'database='=>'string'], 'MongoDBRef::get' => ['?array', 'db'=>'MongoDB', 'ref'=>'array'], 'MongoDBRef::isRef' => ['bool', 'ref'=>'mixed'], - 'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type' => 'int'], + 'MongoDB\BSON\Binary::__construct' => ['void', 'data' => 'string', 'type=' => 'int'], 'MongoDB\BSON\Binary::getData' => ['string'], 'MongoDB\BSON\Binary::getType' => ['int'], 'MongoDB\BSON\Binary::__toString' => ['string'], @@ -4191,7 +4187,7 @@ return [ 'MongoWriteConcernException::getTrace' => ['list\',args?:array}>'], 'MongoWriteConcernException::getTraceAsString' => ['string'], 'MultipleIterator::__construct' => ['void', 'flags='=>'int'], - 'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'infos='=>'string'], + 'MultipleIterator::attachIterator' => ['void', 'iterator'=>'Iterator', 'info='=>'string|int|null'], 'MultipleIterator::containsIterator' => ['bool', 'iterator'=>'Iterator'], 'MultipleIterator::countIterators' => ['int'], 'MultipleIterator::current' => ['array|false'], @@ -4273,20 +4269,20 @@ return [ 'NumberFormatter::__construct' => ['void', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'NumberFormatter::format' => ['string|false', 'num'=>'', 'type='=>'int'], - 'NumberFormatter::formatCurrency' => ['string|false', 'num'=>'float', 'currency'=>'string'], - 'NumberFormatter::getAttribute' => ['int|false', 'attr'=>'int'], + 'NumberFormatter::formatCurrency' => ['string|false', 'amount'=>'float', 'currency'=>'string'], + 'NumberFormatter::getAttribute' => ['int|float|false', 'attribute'=>'int'], 'NumberFormatter::getErrorCode' => ['int'], 'NumberFormatter::getErrorMessage' => ['string'], 'NumberFormatter::getLocale' => ['string', 'type='=>'int'], 'NumberFormatter::getPattern' => ['string|false'], - 'NumberFormatter::getSymbol' => ['string|false', 'attr'=>'int'], - 'NumberFormatter::getTextAttribute' => ['string|false', 'attr'=>'int'], - 'NumberFormatter::parse' => ['float|false', 'string'=>'string', 'type='=>'int', '&rw_position='=>'int'], - 'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_position='=>'int'], - 'NumberFormatter::setAttribute' => ['bool', 'attr'=>'int', 'value'=>''], + 'NumberFormatter::getSymbol' => ['string|false', 'symbol'=>'int'], + 'NumberFormatter::getTextAttribute' => ['string|false', 'attribute'=>'int'], + 'NumberFormatter::parse' => ['int|float|false', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'], + 'NumberFormatter::parseCurrency' => ['float|false', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'], + 'NumberFormatter::setAttribute' => ['bool', 'attribute'=>'int', 'value'=>'int|float'], 'NumberFormatter::setPattern' => ['bool', 'pattern'=>'string'], - 'NumberFormatter::setSymbol' => ['bool', 'attr'=>'int', 'symbol'=>'string'], - 'NumberFormatter::setTextAttribute' => ['bool', 'attr'=>'int', 'value'=>'string'], + 'NumberFormatter::setSymbol' => ['bool', 'symbol'=>'int', 'value'=>'string'], + 'NumberFormatter::setTextAttribute' => ['bool', 'attribute'=>'int', 'value'=>'string'], 'OAuth::__construct' => ['void', 'consumer_key'=>'string', 'consumer_secret'=>'string', 'signature_method='=>'string', 'auth_type='=>'int'], 'OAuth::disableDebug' => ['bool'], 'OAuth::disableRedirects' => ['bool'], @@ -4710,8 +4706,6 @@ return [ 'PDFlib::utf32_to_utf16' => ['string', 'utf32string'=>'string', 'ordering'=>'string'], 'PDFlib::utf8_to_utf16' => ['string', 'utf8string'=>'string', 'ordering'=>'string'], 'PDO::__construct' => ['void', 'dsn'=>'string', 'username='=>'?string', 'password='=>'?string', 'options='=>'?array'], - 'PDO::__sleep' => ['list'], - 'PDO::__wakeup' => ['void'], 'PDO::beginTransaction' => ['bool'], 'PDO::commit' => ['bool'], 'PDO::cubrid_schema' => ['array', 'schema_type'=>'int', 'table_name='=>'string', 'col_name='=>'string'], @@ -4749,8 +4743,6 @@ return [ 'PDOException::getPrevious' => ['?Throwable'], 'PDOException::getTrace' => ['list\',args?:array}>'], 'PDOException::getTraceAsString' => ['string'], - 'PDOStatement::__sleep' => ['list'], - 'PDOStatement::__wakeup' => ['void'], 'PDOStatement::bindColumn' => ['bool', 'column'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'], 'PDOStatement::bindParam' => ['bool', 'param'=>'string|int', '&rw_var'=>'mixed', 'type='=>'int', 'maxLength='=>'int', 'driverOptions='=>'mixed'], 'PDOStatement::bindValue' => ['bool', 'param'=>'string|int', 'value'=>'mixed', 'type='=>'int'], @@ -5758,7 +5750,7 @@ return [ 'Reflection::export' => ['?string', 'r'=>'reflector', 'return='=>'bool'], 'Reflection::getModifierNames' => ['list', 'modifiers'=>'int'], 'ReflectionClass::__clone' => ['void'], - 'ReflectionClass::__construct' => ['void', 'argument'=>'object|class-string'], + 'ReflectionClass::__construct' => ['void', 'objectOrClass'=>'object|class-string'], 'ReflectionClass::__toString' => ['string'], 'ReflectionClass::export' => ['?string', 'argument'=>'string|object', 'return='=>'bool'], 'ReflectionClass::getConstant' => ['mixed', 'name'=>'string'], @@ -5792,7 +5784,7 @@ return [ 'ReflectionClass::hasConstant' => ['bool', 'name'=>'string'], 'ReflectionClass::hasMethod' => ['bool', 'name'=>'string'], 'ReflectionClass::hasProperty' => ['bool', 'name'=>'string'], - 'ReflectionClass::implementsInterface' => ['bool', 'interface_name'=>'interface-string|ReflectionClass'], + 'ReflectionClass::implementsInterface' => ['bool', 'interface'=>'interface-string|ReflectionClass'], 'ReflectionClass::inNamespace' => ['bool'], 'ReflectionClass::isAbstract' => ['bool'], 'ReflectionClass::isAnonymous' => ['bool'], @@ -5810,7 +5802,7 @@ return [ 'ReflectionClass::newInstanceArgs' => ['object', 'args='=>'list'], 'ReflectionClass::newInstanceWithoutConstructor' => ['object'], 'ReflectionClass::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'mixed'], - 'ReflectionClassConstant::__construct' => ['void', 'class'=>'mixed', 'name'=>'string'], + 'ReflectionClassConstant::__construct' => ['void', 'class'=>'object|class-string', 'constant'=>'string'], 'ReflectionClassConstant::__toString' => ['string'], 'ReflectionClassConstant::export' => ['string', 'class'=>'mixed', 'name'=>'string', 'return='=>'bool'], 'ReflectionClassConstant::getDeclaringClass' => ['ReflectionClass'], @@ -5836,7 +5828,7 @@ return [ 'ReflectionExtension::info' => ['void'], 'ReflectionExtension::isPersistent' => ['bool'], 'ReflectionExtension::isTemporary' => ['bool'], - 'ReflectionFunction::__construct' => ['void', 'name'=>'callable-string|Closure'], + 'ReflectionFunction::__construct' => ['void', 'function'=>'callable-string|Closure'], 'ReflectionFunction::__toString' => ['string'], 'ReflectionFunction::export' => ['?string', 'name'=>'string', 'return='=>'bool'], 'ReflectionFunction::getClosure' => ['Closure'], @@ -5896,7 +5888,7 @@ return [ 'ReflectionFunctionAbstract::isUserDefined' => ['bool'], 'ReflectionFunctionAbstract::isVariadic' => ['bool'], 'ReflectionFunctionAbstract::returnsReference' => ['bool'], - 'ReflectionGenerator::__construct' => ['void', 'generator'=>'object'], + 'ReflectionGenerator::__construct' => ['void', 'generator'=>'Generator'], 'ReflectionGenerator::getExecutingFile' => ['string'], 'ReflectionGenerator::getExecutingGenerator' => ['Generator'], 'ReflectionGenerator::getExecutingLine' => ['int'], @@ -5946,14 +5938,14 @@ return [ 'ReflectionMethod::isUserDefined' => ['bool'], 'ReflectionMethod::isVariadic' => ['bool'], 'ReflectionMethod::returnsReference' => ['bool'], - 'ReflectionMethod::setAccessible' => ['void', 'visible'=>'bool'], + 'ReflectionMethod::setAccessible' => ['void', 'accessible'=>'bool'], 'ReflectionNamedType::__clone' => ['void'], 'ReflectionNamedType::__toString' => ['string'], 'ReflectionNamedType::allowsNull' => ['bool'], 'ReflectionNamedType::getName' => ['string'], 'ReflectionNamedType::isBuiltin' => ['bool'], 'ReflectionObject::__clone' => ['void'], - 'ReflectionObject::__construct' => ['void', 'argument'=>'object'], + 'ReflectionObject::__construct' => ['void', 'object'=>'object'], 'ReflectionObject::__toString' => ['string'], 'ReflectionObject::export' => ['?string', 'argument'=>'object', 'return='=>'bool'], 'ReflectionObject::getConstant' => ['mixed', 'name'=>'string'], @@ -5987,7 +5979,7 @@ return [ 'ReflectionObject::hasConstant' => ['bool', 'name'=>'string'], 'ReflectionObject::hasMethod' => ['bool', 'name'=>'string'], 'ReflectionObject::hasProperty' => ['bool', 'name'=>'string'], - 'ReflectionObject::implementsInterface' => ['bool', 'interface_name'=>'ReflectionClass|string'], + 'ReflectionObject::implementsInterface' => ['bool', 'interface'=>'ReflectionClass|interface-string'], 'ReflectionObject::inNamespace' => ['bool'], 'ReflectionObject::isAbstract' => ['bool'], 'ReflectionObject::isAnonymous' => ['bool'], @@ -6007,7 +5999,7 @@ return [ 'ReflectionObject::newInstanceWithoutConstructor' => ['object'], 'ReflectionObject::setStaticPropertyValue' => ['void', 'name'=>'string', 'value'=>'string'], 'ReflectionParameter::__clone' => ['void'], - 'ReflectionParameter::__construct' => ['void', 'function'=>'', 'parameter'=>''], + 'ReflectionParameter::__construct' => ['void', 'function'=>'string|array|object', 'param'=>'int|string'], 'ReflectionParameter::__toString' => ['string'], 'ReflectionParameter::allowsNull' => ['bool'], 'ReflectionParameter::canBePassedByValue' => ['bool'], @@ -6029,7 +6021,7 @@ return [ 'ReflectionParameter::isPassedByReference' => ['bool'], 'ReflectionParameter::isVariadic' => ['bool'], 'ReflectionProperty::__clone' => ['void'], - 'ReflectionProperty::__construct' => ['void', 'class'=>'', 'name'=>'string'], + 'ReflectionProperty::__construct' => ['void', 'class'=>'object|class-string', 'property'=>'string'], 'ReflectionProperty::__toString' => ['string'], 'ReflectionProperty::export' => ['?string', 'class'=>'mixed', 'name'=>'string', 'return='=>'bool'], 'ReflectionProperty::getDeclaringClass' => ['ReflectionClass'], @@ -6043,7 +6035,7 @@ return [ 'ReflectionProperty::isProtected' => ['bool'], 'ReflectionProperty::isPublic' => ['bool'], 'ReflectionProperty::isStatic' => ['bool'], - 'ReflectionProperty::setAccessible' => ['void', 'visible'=>'bool'], + 'ReflectionProperty::setAccessible' => ['void', 'accessible'=>'bool'], 'ReflectionProperty::setValue' => ['void', 'object'=>'null|object', 'value'=>''], 'ReflectionProperty::setValue\'1' => ['void', 'value'=>''], 'ReflectionType::__clone' => ['void'], @@ -6076,13 +6068,13 @@ return [ 'RegexIterator::setMode' => ['void', 'mode'=>'int'], 'RegexIterator::setPregFlags' => ['void', 'pregFlags'=>'int'], 'RegexIterator::valid' => ['bool'], - 'ResourceBundle::__construct' => ['void', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'], + 'ResourceBundle::__construct' => ['void', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'], 'ResourceBundle::count' => ['int'], - 'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'string', 'bundlename'=>'string', 'fallback='=>'bool'], + 'ResourceBundle::create' => ['?ResourceBundle', 'locale'=>'?string', 'bundle'=>'?string', 'fallback='=>'bool'], 'ResourceBundle::get' => ['mixed', 'index'=>'string|int', 'fallback='=>'bool'], 'ResourceBundle::getErrorCode' => ['int'], 'ResourceBundle::getErrorMessage' => ['string'], - 'ResourceBundle::getLocales' => ['array', 'bundlename'=>'string'], + 'ResourceBundle::getLocales' => ['array|false', 'bundle'=>'string'], 'Runkit_Sandbox::__construct' => ['void', 'options='=>'array'], 'Runkit_Sandbox_Parent' => [''], 'Runkit_Sandbox_Parent::__construct' => ['void'], @@ -6193,8 +6185,8 @@ return [ 'SNMP::getnext' => ['string|array|false', 'objectId'=>'string|array'], 'SNMP::set' => ['bool', 'objectId'=>'string|array', 'type'=>'string|array', 'value'=>'string|array'], 'SNMP::setSecurity' => ['bool', 'securityLevel'=>'string', 'authProtocol='=>'string', 'authPassphrase='=>'string', 'privacyProtocol='=>'string', 'privacyPassphrase='=>'string', 'contextName='=>'string', 'contextEngineId='=>'string'], - 'SNMP::walk' => ['array|false', 'objectId'=>'string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'], - 'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'], + 'SNMP::walk' => ['array|false', 'objectId'=>'array|string', 'suffixAsKey='=>'bool', 'maxRepetitions='=>'int', 'nonRepeaters='=>'int'], + 'SQLite3::__construct' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'], 'SQLite3::busyTimeout' => ['bool', 'milliseconds'=>'int'], 'SQLite3::changes' => ['int'], 'SQLite3::close' => ['bool'], @@ -6208,7 +6200,7 @@ return [ 'SQLite3::lastErrorMsg' => ['string'], 'SQLite3::lastInsertRowID' => ['int'], 'SQLite3::loadExtension' => ['bool', 'name'=>'string'], - 'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'?string'], + 'SQLite3::open' => ['void', 'filename'=>'string', 'flags='=>'int', 'encryptionKey='=>'string'], 'SQLite3::openBlob' => ['resource|false', 'table'=>'string', 'column'=>'string', 'rowid'=>'int', 'dbname='=>'string'], 'SQLite3::prepare' => ['SQLite3Stmt|false', 'query'=>'string'], 'SQLite3::query' => ['SQLite3Result|false', 'query'=>'string'], @@ -6619,9 +6611,9 @@ return [ 'SessionHandler::close' => ['bool'], 'SessionHandler::create_sid' => ['string'], 'SessionHandler::destroy' => ['bool', 'id'=>'string'], - 'SessionHandler::gc' => ['bool', 'maxlifetime'=>'int'], - 'SessionHandler::open' => ['bool', 'save_path'=>'string', 'session_name'=>'string'], - 'SessionHandler::read' => ['string', 'id'=>'string'], + 'SessionHandler::gc' => ['bool', 'max_lifetime'=>'int'], + 'SessionHandler::open' => ['bool', 'path'=>'string', 'name'=>'string'], + 'SessionHandler::read' => ['string|false', 'id'=>'string'], 'SessionHandler::write' => ['bool', 'id'=>'string', 'data'=>'string'], 'SessionHandlerInterface::close' => ['bool'], 'SessionHandlerInterface::destroy' => ['bool', 'id'=>'string'], @@ -6634,26 +6626,26 @@ return [ 'SessionUpdateTimestampHandler::validateId' => ['char', 'id'=>'string'], 'SessionUpdateTimestampHandlerInterface::updateTimestamp' => ['bool', 'key'=>'string', 'value'=>'string'], 'SessionUpdateTimestampHandlerInterface::validateId' => ['bool', 'key'=>'string'], - 'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'data_is_url='=>'bool', 'ns='=>'string', 'is_prefix='=>'bool'], + 'SimpleXMLElement::__construct' => ['void', 'data'=>'string', 'options='=>'int', 'dataIsURL='=>'bool', 'namespaceOrPrefix='=>'string', 'isPrefix='=>'bool'], 'SimpleXMLElement::__get' => ['SimpleXMLElement', 'name'=>'string'], 'SimpleXMLElement::__toString' => ['string'], - 'SimpleXMLElement::addAttribute' => ['void', 'name'=>'string', 'value='=>'string', 'ns='=>'string'], - 'SimpleXMLElement::addChild' => ['SimpleXMLElement', 'name'=>'string', 'value='=>'string', 'ns='=>'string'], + 'SimpleXMLElement::addAttribute' => ['void', 'qualifiedName'=>'string', 'value'=>'string', 'namespace='=>'?string'], + 'SimpleXMLElement::addChild' => ['?SimpleXMLElement', 'qualifiedName'=>'string', 'value='=>'?string', 'namespace='=>'?string'], 'SimpleXMLElement::asXML' => ['string|bool', 'filename'=>'string'], 'SimpleXMLElement::asXML\'1' => ['string|false'], - 'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'], - 'SimpleXMLElement::children' => ['SimpleXMLElement', 'ns='=>'string', 'is_prefix='=>'bool'], + 'SimpleXMLElement::attributes' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'], + 'SimpleXMLElement::children' => ['?SimpleXMLElement', 'namespaceOrPrefix='=>'?string', 'isPrefix='=>'bool'], 'SimpleXMLElement::count' => ['int'], - 'SimpleXMLElement::getDocNamespaces' => ['string[]', 'recursive='=>'bool', 'from_root='=>'bool'], + 'SimpleXMLElement::getDocNamespaces' => ['array', 'recursive='=>'bool', 'fromRoot='=>'bool'], 'SimpleXMLElement::getName' => ['string'], - 'SimpleXMLElement::getNamespaces' => ['string[]', 'recursive='=>'bool'], + 'SimpleXMLElement::getNamespaces' => ['array', 'recursive='=>'bool'], 'SimpleXMLElement::offsetExists' => ['bool', 'offset'=>'int|string'], 'SimpleXMLElement::offsetGet' => ['SimpleXMLElement', 'offset'=>'int|string'], 'SimpleXMLElement::offsetSet' => ['void', 'offset'=>'int|string', 'value'=>'mixed'], 'SimpleXMLElement::offsetUnset' => ['void', 'offset'=>'int|string'], - 'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'ns'=>'string'], + 'SimpleXMLElement::registerXPathNamespace' => ['bool', 'prefix'=>'string', 'namespace'=>'string'], 'SimpleXMLElement::saveXML' => ['string|bool', 'filename='=>'string'], - 'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false', 'path'=>'string'], + 'SimpleXMLElement::xpath' => ['SimpleXMLElement[]|false|null', 'expression'=>'string'], 'SimpleXMLIterator::current' => ['?SimpleXMLIterator'], 'SimpleXMLIterator::getChildren' => ['?SimpleXMLIterator'], 'SimpleXMLIterator::hasChildren' => ['bool'], @@ -7496,10 +7488,10 @@ return [ 'SplDoublyLinkedList::isEmpty' => ['bool'], 'SplDoublyLinkedList::key' => ['int'], 'SplDoublyLinkedList::next' => ['void'], - 'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'mixed'], - 'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'mixed'], - 'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'mixed', 'value'=>'mixed'], - 'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'mixed'], + 'SplDoublyLinkedList::offsetExists' => ['bool', 'index'=>'int'], + 'SplDoublyLinkedList::offsetGet' => ['mixed', 'index'=>'int'], + 'SplDoublyLinkedList::offsetSet' => ['void', 'index'=>'?int', 'value'=>'mixed'], + 'SplDoublyLinkedList::offsetUnset' => ['void', 'index'=>'int'], 'SplDoublyLinkedList::pop' => ['mixed'], 'SplDoublyLinkedList::prev' => ['void'], 'SplDoublyLinkedList::push' => ['void', 'value'=>'mixed'], @@ -7515,7 +7507,6 @@ return [ 'SplEnum::getConstList' => ['array', 'include_default='=>'bool'], 'SplFileInfo::__construct' => ['void', 'filename'=>'string'], 'SplFileInfo::__toString' => ['string'], - 'SplFileInfo::__wakeup' => ['void'], 'SplFileInfo::getATime' => ['int|false'], 'SplFileInfo::getBasename' => ['string', 'suffix='=>'string'], 'SplFileInfo::getCTime' => ['int|false'], @@ -7989,7 +7980,7 @@ return [ 'Transliterator::getErrorCode' => ['int'], 'Transliterator::getErrorMessage' => ['string'], 'Transliterator::listIDs' => ['array'], - 'Transliterator::transliterate' => ['string|false', 'subject'=>'string', 'start='=>'int', 'end='=>'int'], + 'Transliterator::transliterate' => ['string|false', 'string'=>'string', 'start='=>'int', 'end='=>'int'], 'TypeError::__clone' => ['void'], 'TypeError::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'TypeError::__toString' => ['string'], @@ -8001,8 +7992,8 @@ return [ 'TypeError::getTrace' => ['list\',args?:array}>'], 'TypeError::getTraceAsString' => ['string'], 'UConverter::__construct' => ['void', 'destination_encoding='=>'?string', 'source_encoding='=>'?string'], - 'UConverter::convert' => ['string', 'string'=>'string', 'reverse='=>'bool'], - 'UConverter::fromUCallback' => ['mixed', 'reason'=>'int', 'source'=>'string', 'codePoint'=>'string', '&w_error'=>'int'], + 'UConverter::convert' => ['string', 'str'=>'string', 'reverse='=>'bool'], + 'UConverter::fromUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'array', 'codePoint'=>'int', '&w_error'=>'int'], 'UConverter::getAliases' => ['array|false|null', 'name'=>'string'], 'UConverter::getAvailable' => ['array'], 'UConverter::getDestinationEncoding' => ['string|false|null'], @@ -8013,12 +8004,12 @@ return [ 'UConverter::getSourceType' => ['int|false|null'], 'UConverter::getStandards' => ['?array'], 'UConverter::getSubstChars' => ['string|false|null'], - 'UConverter::reasonText' => ['string', 'reason='=>'int'], + 'UConverter::reasonText' => ['string', 'reason'=>'int'], 'UConverter::setDestinationEncoding' => ['bool', 'encoding'=>'string'], 'UConverter::setSourceEncoding' => ['bool', 'encoding'=>'string'], 'UConverter::setSubstChars' => ['bool', 'chars'=>'string'], 'UConverter::toUCallback' => ['string|int|array|null', 'reason'=>'int', 'source'=>'string', 'codeUnits'=>'string', '&w_error'=>'int'], - 'UConverter::transcode' => ['string', 'string'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'], + 'UConverter::transcode' => ['string', 'str'=>'string', 'toEncoding'=>'string', 'fromEncoding'=>'string', 'options='=>'?array'], 'UnderflowException::__clone' => ['void'], 'UnderflowException::__construct' => ['void', 'message='=>'string', 'code='=>'int', 'previous='=>'?Throwable'], 'UnderflowException::__toString' => ['string'], @@ -8183,17 +8174,6 @@ return [ 'Vtiful\Kernel\Validation::validationType' => ['?Vtiful\Kernel\Validation', 'type'=>'int'], 'Vtiful\Kernel\Validation::valueList' => ['?Vtiful\Kernel\Validation', 'value_list'=>'array'], 'Vtiful\Kernel\Validation::valueNumber' => ['?Vtiful\Kernel\Validation', 'value_number'=>'int'], - 'WeakMap::__construct' => ['void'], - 'WeakMap::count' => ['int'], - 'WeakMap::current' => ['mixed'], - 'WeakMap::key' => ['object'], - 'WeakMap::next' => ['void'], - 'WeakMap::offsetExists' => ['bool', 'object'=>'object'], - 'WeakMap::offsetGet' => ['mixed', 'object'=>'object'], - 'WeakMap::offsetSet' => ['void', 'object'=>'object', 'value'=>'mixed'], - 'WeakMap::offsetUnset' => ['void', 'object'=>'object'], - 'WeakMap::rewind' => ['void'], - 'WeakMap::valid' => ['bool'], 'Weakref::acquire' => ['bool'], 'Weakref::get' => ['object'], 'Weakref::release' => ['bool'], @@ -8252,23 +8232,23 @@ return [ 'XMLDiff\File::merge' => ['string', 'src'=>'string', 'diff'=>'string'], 'XMLDiff\Memory::diff' => ['string', 'from'=>'string', 'to'=>'string'], 'XMLDiff\Memory::merge' => ['string', 'src'=>'string', 'diff'=>'string'], - 'XMLReader::XML' => ['bool', 'source'=>'string', 'encoding='=>'?string', 'options='=>'int'], + 'XMLReader::XML' => ['bool|XMLReader', 'source'=>'string', 'encoding='=>'?string', 'flags='=>'int'], 'XMLReader::close' => ['bool'], 'XMLReader::expand' => ['DOMNode|false', 'baseNode='=>'?DOMNode'], 'XMLReader::getAttribute' => ['?string', 'name'=>'string'], 'XMLReader::getAttributeNo' => ['?string', 'index'=>'int'], - 'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespaceuri'=>'string'], + 'XMLReader::getAttributeNs' => ['?string', 'name'=>'string', 'namespace'=>'string'], 'XMLReader::getParserProperty' => ['bool', 'property'=>'int'], 'XMLReader::isValid' => ['bool'], 'XMLReader::lookupNamespace' => ['?string', 'prefix'=>'string'], 'XMLReader::moveToAttribute' => ['bool', 'name'=>'string'], 'XMLReader::moveToAttributeNo' => ['bool', 'index'=>'int'], - 'XMLReader::moveToAttributeNs' => ['bool', 'localname'=>'string', 'namespaceuri'=>'string'], + 'XMLReader::moveToAttributeNs' => ['bool', 'name'=>'string', 'namespace'=>'string'], 'XMLReader::moveToElement' => ['bool'], 'XMLReader::moveToFirstAttribute' => ['bool'], 'XMLReader::moveToNextAttribute' => ['bool'], - 'XMLReader::next' => ['bool', 'localname='=>'string'], - 'XMLReader::open' => ['bool', 'uri'=>'string', 'encoding='=>'?string', 'options='=>'int'], + 'XMLReader::next' => ['bool', 'name='=>'string'], + 'XMLReader::open' => ['bool|XmlReader', 'uri'=>'string', 'encoding='=>'?string', 'flags='=>'int'], 'XMLReader::read' => ['bool'], 'XMLReader::readInnerXML' => ['string'], 'XMLReader::readOuterXML' => ['string'], @@ -8322,12 +8302,12 @@ return [ 'XSLTProcessor::getParameter' => ['string|false', 'namespace'=>'string', 'name'=>'string'], 'XSLTProcessor::hasExsltSupport' => ['bool'], 'XSLTProcessor::importStylesheet' => ['bool', 'stylesheet'=>'object'], - 'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'mixed'], + 'XSLTProcessor::registerPHPFunctions' => ['void', 'functions='=>'array|string|null'], 'XSLTProcessor::removeParameter' => ['bool', 'namespace'=>'string', 'name'=>'string'], 'XSLTProcessor::setParameter' => ['bool', 'namespace'=>'string', 'name'=>'string', 'value'=>'string'], 'XSLTProcessor::setParameter\'1' => ['bool', 'namespace'=>'string', 'options'=>'array'], 'XSLTProcessor::setProfiling' => ['bool', 'filename'=>'?string'], - 'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode'], + 'XSLTProcessor::transformToDoc' => ['DOMDocument|false', 'document'=>'DOMNode', 'returnClass='=>'?string'], 'XSLTProcessor::transformToURI' => ['int', 'document'=>'DOMDocument', 'uri'=>'string'], 'XSLTProcessor::transformToXML' => ['string|false', 'document'=>'DOMDocument'], 'Xcom::__construct' => ['void', 'fabric_url='=>'string', 'fabric_token='=>'string', 'capability_token='=>'string'], @@ -9216,8 +9196,8 @@ return [ 'ZipArchive::getNameIndex' => ['string|false', 'index'=>'int', 'flags='=>'int'], 'ZipArchive::getStatusString' => ['string|false'], 'ZipArchive::getStream' => ['resource|false', 'name'=>'string'], - 'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'], - 'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'encode='=>'bool'], + 'ZipArchive::isCompressionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'], + 'ZipArchive::isEncryptionMethodSupported' => ['bool', 'method'=>'int', 'enc='=>'bool'], 'ZipArchive::locateName' => ['int|false', 'name'=>'string', 'flags='=>'int'], 'ZipArchive::open' => ['int|bool', 'filename'=>'string', 'flags='=>'int'], 'ZipArchive::registerCancelCallback' => ['bool', 'callback'=>'callable'], @@ -9228,8 +9208,8 @@ return [ 'ZipArchive::setArchiveComment' => ['bool', 'comment'=>'string'], 'ZipArchive::setCommentIndex' => ['bool', 'index'=>'int', 'comment'=>'string'], 'ZipArchive::setCommentName' => ['bool', 'name'=>'string', 'comment'=>'string'], - 'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'comp_method'=>'int', 'comp_flags='=>'int'], - 'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'comp_method'=>'int', 'comp_flags='=>'int'], + 'ZipArchive::setCompressionIndex' => ['bool', 'index'=>'int', 'method'=>'int', 'compflags='=>'int'], + 'ZipArchive::setCompressionName' => ['bool', 'name'=>'string', 'method'=>'int', 'compflags='=>'int'], 'ZipArchive::setExternalAttributesIndex' => ['bool', 'index'=>'int', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'], 'ZipArchive::setExternalAttributesName' => ['bool', 'name'=>'string', 'opsys'=>'int', 'attr'=>'int', 'flags='=>'int'], 'ZipArchive::setMtimeIndex' => ['bool', 'index'=>'int', 'timestamp'=>'int', 'flags='=>'int'], @@ -9401,9 +9381,7 @@ return [ 'array_uintersect_assoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable', '...rest='=>'array|callable(mixed,mixed):int'], 'array_uintersect_uassoc' => ['array', 'array'=>'array', 'rest'=>'array', 'data_compare_func'=>'callable(mixed,mixed):int', 'key_compare_func'=>'callable(mixed,mixed):int'], 'array_uintersect_uassoc\'1' => ['array', 'array'=>'array', 'rest'=>'array', 'arr3'=>'array', 'arg4'=>'array|callable(mixed,mixed):int', 'arg5'=>'array|callable(mixed,mixed):int', '...rest='=>'array|callable(mixed,mixed):int'], - 'array_unique' => ['array', 'array'=>'array', 'flags='=>'0'], - 'array_unique\'1' => ['array', 'array'=>'array', 'flags='=>'1'], - 'array_unique\'2' => ['array', 'array'=>'array', 'flags='=>'2|5'], + 'array_unique' => ['array', 'array'=>'array', 'flags='=>'int'], 'array_unshift' => ['int', '&rw_array'=>'array', '...values'=>'mixed'], 'array_values' => ['list', 'array'=>'array'], 'array_walk' => ['bool', '&rw_array'=>'array', 'callback'=>'callable', 'arg='=>'mixed'], @@ -9500,7 +9478,7 @@ return [ 'call_user_func_array' => ['mixed|false', 'callback'=>'callable', 'args'=>'list'], 'call_user_method' => ['mixed', 'method_name'=>'string', 'object'=>'object', 'parameter='=>'mixed', '...args='=>'mixed'], 'call_user_method_array' => ['mixed', 'method_name'=>'string', 'object'=>'object', 'params'=>'list'], - 'ceil' => ['float', 'num'=>'float'], + 'ceil' => ['float', 'num'=>'float|int'], 'chdb::__construct' => ['void', 'pathname'=>'string'], 'chdb::get' => ['string', 'key'=>'string'], 'chdb_create' => ['bool', 'pathname'=>'string', 'data'=>'array'], @@ -9763,7 +9741,7 @@ return [ 'cubrid_unbuffered_query' => ['resource', 'query'=>'string', 'conn_identifier='=>''], 'cubrid_version' => ['string'], 'curl_close' => ['void', 'ch'=>'resource'], - 'curl_copy_handle' => ['resource', 'ch'=>'resource'], + 'curl_copy_handle' => ['resource|false', 'ch'=>'resource'], 'curl_errno' => ['int', 'ch'=>'resource'], 'curl_error' => ['string', 'ch'=>'resource'], 'curl_escape' => ['string|false', 'ch'=>'resource', 'string'=>'string'], @@ -9812,7 +9790,7 @@ return [ 'date_get_last_errors' => ['array{warning_count:int,warnings:array,error_count:int,errors:array}|false'], 'date_interval_create_from_date_string' => ['DateInterval', 'datetime'=>'string'], 'date_interval_format' => ['string', 'object'=>'DateInterval', 'format'=>'string'], - 'date_isodate_set' => ['DateTime|false', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int|mixed'], + 'date_isodate_set' => ['DateTime', 'object'=>'DateTime', 'year'=>'int', 'week'=>'int', 'dayOfWeek='=>'int'], 'date_modify' => ['DateTime|false', 'object'=>'DateTime', 'modifier'=>'string'], 'date_offset_get' => ['int|false', 'object'=>'DateTimeInterface'], 'date_parse' => ['array|false', 'datetime'=>'string'], @@ -9841,7 +9819,7 @@ return [ 'datefmt_get_timezone_id' => ['string|false', 'formatter'=>'IntlDateFormatter'], 'datefmt_is_lenient' => ['bool', 'formatter'=>'IntlDateFormatter'], 'datefmt_localtime' => ['array|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], - 'datefmt_parse' => ['int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], + 'datefmt_parse' => ['float|int|false', 'formatter'=>'IntlDateFormatter', 'string'=>'string', '&rw_offset='=>'int'], 'datefmt_set_calendar' => ['bool', 'formatter'=>'IntlDateFormatter', 'calendar'=>'IntlCalendar|int|null'], 'datefmt_set_lenient' => ['void', 'formatter'=>'IntlDateFormatter', 'lenient'=>'bool'], 'datefmt_set_pattern' => ['bool', 'formatter'=>'IntlDateFormatter', 'pattern'=>'string'], @@ -9903,20 +9881,20 @@ return [ 'db2_tableprivileges' => [''], 'db2_tables' => ['resource|false', 'connection'=>'resource', 'qualifier='=>'?string', 'schema='=>'?string', 'table_name='=>'?string', 'table_type='=>'?string'], 'dba_close' => ['void', 'dba'=>'resource'], - 'dba_delete' => ['bool', 'key'=>'string', 'dba'=>'resource'], - 'dba_exists' => ['bool', 'key'=>'string', 'dba'=>'resource'], - 'dba_fetch' => ['string|false', 'key'=>'string', 'skip'=>'int', 'dba'=>'resource'], - 'dba_fetch\'1' => ['string|false', 'key'=>'string', 'skip'=>'resource'], + 'dba_delete' => ['bool', 'key'=>'array|string', 'dba'=>'resource'], + 'dba_exists' => ['bool', 'key'=>'array|string', 'dba'=>'resource'], + 'dba_fetch' => ['string|false', 'key'=>'array|string', 'skip'=>'int', 'dba'=>'resource'], + 'dba_fetch\'1' => ['string|false', 'key'=>'array|string', 'skip'=>'resource'], 'dba_firstkey' => ['string', 'dba'=>'resource'], 'dba_handlers' => ['array', 'full_info='=>'bool'], - 'dba_insert' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'], + 'dba_insert' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'], 'dba_key_split' => ['array|false', 'key'=>'string|false|null'], 'dba_list' => ['array'], 'dba_nextkey' => ['string', 'dba'=>'resource'], 'dba_open' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'string', '...handler_params='=>'string'], 'dba_optimize' => ['bool', 'dba'=>'resource'], 'dba_popen' => ['resource', 'path'=>'string', 'mode'=>'string', 'handler='=>'string', '...handler_params='=>'string'], - 'dba_replace' => ['bool', 'key'=>'string', 'value'=>'string', 'dba'=>'resource'], + 'dba_replace' => ['bool', 'key'=>'array|string', 'value'=>'string', 'dba'=>'resource'], 'dba_sync' => ['bool', 'dba'=>'resource'], 'dbase_add_record' => ['bool', 'dbase_identifier'=>'resource', 'record'=>'array'], 'dbase_close' => ['bool', 'dbase_identifier'=>'resource'], @@ -10048,7 +10026,7 @@ return [ 'dotnet_load' => ['int', 'assembly_name'=>'string', 'datatype_name='=>'string', 'codepage='=>'int'], 'doubleval' => ['float', 'value'=>'mixed'], 'each' => ['array{0:int|string,key:int|string,1:mixed,value:mixed}', '&r_arr'=>'array'], - 'easter_date' => ['int', 'year='=>'int'], + 'easter_date' => ['int', 'year='=>'int', 'mode='=>'int'], 'easter_days' => ['int', 'year='=>'int', 'mode='=>'int'], 'echo' => ['void', 'arg1'=>'string', '...args='=>'string'], 'eio_busy' => ['resource', 'delay'=>'int', 'pri='=>'int', 'callback='=>'callable', 'data='=>'mixed'], @@ -10514,7 +10492,7 @@ return [ 'finfo_set_flags' => ['bool', 'finfo'=>'resource', 'flags'=>'int'], 'floatval' => ['float', 'value'=>'mixed'], 'flock' => ['bool', 'stream'=>'resource', 'operation'=>'int', '&w_would_block='=>'int'], - 'floor' => ['float', 'num'=>'float'], + 'floor' => ['float', 'num'=>'float|int'], 'flush' => ['void'], 'fmod' => ['float', 'num1'=>'float', 'num2'=>'float'], 'fnmatch' => ['bool', 'pattern'=>'string', 'filename'=>'string', 'flags='=>'int'], @@ -10699,8 +10677,8 @@ return [ 'get_called_class' => ['class-string'], 'get_cfg_var' => ['string|false', 'option'=>'string'], 'get_class' => ['class-string', 'object='=>'object'], - 'get_class_methods' => ['list|null', 'object_or_class'=>'mixed'], - 'get_class_vars' => ['array', 'class'=>'string'], + 'get_class_methods' => ['list|null', 'object_or_class'=>'mixed'], + 'get_class_vars' => ['array', 'class'=>'string'], 'get_current_user' => ['string'], 'get_declared_classes' => ['list'], 'get_declared_interfaces' => ['list'], @@ -10749,7 +10727,7 @@ return [ 'gettimeofday' => ['array'], 'gettimeofday\'1' => ['float', 'as_float='=>'true'], 'gettype' => ['string', 'value'=>'mixed'], - 'glob' => ['list|false', 'pattern'=>'string', 'flags='=>'int'], + 'glob' => ['list|false', 'pattern'=>'non-empty-string', 'flags='=>'int<1, max>'], 'gmdate' => ['string', 'format'=>'string', 'timestamp='=>'int'], 'gmmktime' => ['int|false', 'hour='=>'int', 'minute='=>'int', 'second='=>'int', 'month='=>'int', 'day='=>'int', 'year='=>'int'], 'gmp_abs' => ['GMP', 'num'=>'GMP|string|int'], @@ -10899,20 +10877,20 @@ return [ 'gzdeflate' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'], 'gzencode' => ['string|false', 'data'=>'string', 'level='=>'int', 'encoding='=>'int'], 'gzeof' => ['bool', 'stream'=>'resource'], - 'gzfile' => ['list', 'filename'=>'string', 'use_include_path='=>'int'], + 'gzfile' => ['list|false', 'filename'=>'string', 'use_include_path='=>'int'], 'gzgetc' => ['string|false', 'stream'=>'resource'], 'gzgets' => ['string|false', 'stream'=>'resource', 'length='=>'int'], 'gzgetss' => ['string|false', 'zp'=>'resource', 'length'=>'int', 'allowable_tags='=>'string'], 'gzinflate' => ['string|false', 'data'=>'string', 'max_length='=>'int'], 'gzopen' => ['resource|false', 'filename'=>'string', 'mode'=>'string', 'use_include_path='=>'int'], 'gzpassthru' => ['int', 'stream'=>'resource'], - 'gzputs' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], + 'gzputs' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], 'gzread' => ['string|0', 'stream'=>'resource', 'length'=>'int'], 'gzrewind' => ['bool', 'stream'=>'resource'], 'gzseek' => ['int', 'stream'=>'resource', 'offset'=>'int', 'whence='=>'int'], 'gztell' => ['int|false', 'stream'=>'resource'], 'gzuncompress' => ['string|false', 'data'=>'string', 'max_length='=>'int'], - 'gzwrite' => ['int', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], + 'gzwrite' => ['int|false', 'stream'=>'resource', 'data'=>'string', 'length='=>'int'], 'hash' => ['string|false', 'algo'=>'string', 'data'=>'string', 'binary='=>'bool'], 'hashTableObj::clear' => ['void'], 'hashTableObj::get' => ['string', 'key'=>'string'], @@ -12127,7 +12105,7 @@ return [ 'ldap_count_entries' => ['int', 'ldap'=>'resource', 'result'=>'resource'], 'ldap_delete' => ['bool', 'ldap'=>'resource', 'dn'=>'string'], 'ldap_delete_ext' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'controls='=>'array'], - 'ldap_dn2ufn' => ['string', 'dn'=>'string'], + 'ldap_dn2ufn' => ['string|false', 'dn'=>'string'], 'ldap_err2str' => ['string', 'errno'=>'int'], 'ldap_errno' => ['int', 'ldap'=>'resource'], 'ldap_error' => ['string', 'ldap'=>'resource'], @@ -12143,7 +12121,7 @@ return [ 'ldap_get_option' => ['bool', 'ldap'=>'resource', 'option'=>'int', '&w_value='=>'array|string|int'], 'ldap_get_values' => ['array|false', 'ldap'=>'resource', 'entry'=>'resource', 'attribute'=>'string'], 'ldap_get_values_len' => ['array|false', 'ldap'=>'resource', 'entry'=>'resource', 'attribute'=>'string'], - 'ldap_list' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'ldap_list' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_mod_add' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'], 'ldap_mod_add_ext' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array', 'controls='=>'array'], 'ldap_mod_del' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'entry'=>'array'], @@ -12157,11 +12135,11 @@ return [ 'ldap_next_reference' => ['resource|false', 'ldap'=>'resource', 'entry'=>'resource'], 'ldap_parse_reference' => ['bool', 'ldap'=>'resource', 'entry'=>'resource', '&w_referrals'=>'array'], 'ldap_parse_result' => ['bool', 'ldap'=>'resource', 'result'=>'resource', '&w_error_code'=>'int', '&w_matched_dn='=>'string', '&w_error_message='=>'string', '&w_referrals='=>'array', '&w_controls='=>'array'], - 'ldap_read' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'ldap_read' => ['resource|false', 'ldap'=>'resource|array', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_rename' => ['bool', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool'], 'ldap_rename_ext' => ['resource|false', 'ldap'=>'resource', 'dn'=>'string', 'new_rdn'=>'string', 'new_parent'=>'string', 'delete_old_rdn'=>'bool', 'controls='=>'array'], 'ldap_sasl_bind' => ['bool', 'ldap'=>'resource', 'dn='=>'string', 'password='=>'string', 'mech='=>'string', 'realm='=>'string', 'authc_id='=>'string', 'authz_id='=>'string', 'props='=>'string'], - 'ldap_search' => ['resource|false', 'ldap'=>'resource|resource[]', 'base'=>'string', 'filter'=>'string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], + 'ldap_search' => ['resource[]|resource|false', 'ldap'=>'resource|resource[]', 'base'=>'array|string', 'filter'=>'array|string', 'attributes='=>'array', 'attributes_only='=>'int', 'sizelimit='=>'int', 'timelimit='=>'int', 'deref='=>'int'], 'ldap_set_option' => ['bool', 'ldap'=>'resource|null', 'option'=>'int', 'value'=>'mixed'], 'ldap_set_rebind_proc' => ['bool', 'ldap'=>'resource', 'callback'=>'callable'], 'ldap_sort' => ['bool', 'link_identifier'=>'resource', 'result_identifier'=>'resource', 'sortfilter'=>'string'], @@ -12726,7 +12704,6 @@ return [ 'mysqli::commit' => ['bool', 'flags='=>'int', 'name='=>'string'], 'mysqli::connect' => ['null|false', 'hostname='=>'string', 'username='=>'string', 'password='=>'string', 'database='=>'string', 'port='=>'int', 'socket='=>'string'], 'mysqli::debug' => ['bool', 'options'=>'string'], - 'mysqli::disable_reads_from_master' => ['bool'], 'mysqli::dump_debug_info' => ['bool'], 'mysqli::escape_string' => ['string', 'string'=>'string'], 'mysqli::get_charset' => ['object'], @@ -12740,23 +12717,19 @@ return [ 'mysqli::next_result' => ['bool'], 'mysqli::options' => ['bool', 'option'=>'int', 'value'=>'string|int'], 'mysqli::ping' => ['bool'], - 'mysqli::poll' => ['int|false', '&w_read'=>'array', '&w_write'=>'array', '&w_error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], + 'mysqli::poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], 'mysqli::prepare' => ['mysqli_stmt|false', 'query'=>'string'], 'mysqli::query' => ['bool|mysqli_result', 'query'=>'string', 'result_mode='=>'int'], - 'mysqli::real_connect' => ['bool', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'], + 'mysqli::real_connect' => ['bool', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'], 'mysqli::real_escape_string' => ['string', 'string'=>'string'], 'mysqli::real_query' => ['bool', 'query'=>'string'], 'mysqli::reap_async_query' => ['mysqli_result|false'], 'mysqli::refresh' => ['bool', 'flags'=>'int'], 'mysqli::release_savepoint' => ['bool', 'name'=>'string'], 'mysqli::rollback' => ['bool', 'flags='=>'int', 'name='=>'string'], - 'mysqli::rpl_query_type' => ['int', 'query'=>'string'], 'mysqli::savepoint' => ['bool', 'name'=>'string'], 'mysqli::select_db' => ['bool', 'database'=>'string'], - 'mysqli::send_query' => ['bool', 'query'=>'string'], 'mysqli::set_charset' => ['bool', 'charset'=>'string'], - 'mysqli::set_local_infile_default' => ['void'], - 'mysqli::set_local_infile_handler' => ['bool', 'read_func='=>'callable'], 'mysqli::set_opt' => ['bool', 'option'=>'int', 'value'=>'string|int'], 'mysqli::ssl_set' => ['bool', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli::stat' => ['string|false'], @@ -12778,8 +12751,6 @@ return [ 'mysqli_debug' => ['true', 'options'=>'string'], 'mysqli_disable_reads_from_master' => ['bool', 'link'=>'mysqli'], 'mysqli_disable_rpl_parse' => ['bool', 'link'=>'mysqli'], - 'mysqli_driver::embedded_server_end' => ['void'], - 'mysqli_driver::embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'], 'mysqli_dump_debug_info' => ['bool', 'mysql'=>'mysqli'], 'mysqli_embedded_server_end' => ['void'], 'mysqli_embedded_server_start' => ['bool', 'start'=>'int', 'arguments'=>'array', 'groups'=>'array'], @@ -12797,9 +12768,9 @@ return [ 'mysqli_fetch_array\'1' => ['array|false|null', 'result'=>'mysqli_result', 'mode='=>'1'], 'mysqli_fetch_array\'2' => ['list|false|null', 'result'=>'mysqli_result', 'mode='=>'2'], 'mysqli_fetch_assoc' => ['array|false|null', 'result'=>'mysqli_result'], - 'mysqli_fetch_field' => ['object|false', 'result'=>'mysqli_result'], - 'mysqli_fetch_field_direct' => ['object|false', 'result'=>'mysqli_result', 'index'=>'int'], - 'mysqli_fetch_fields' => ['stdClass[]', 'result'=>'mysqli_result'], + 'mysqli_fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result'], + 'mysqli_fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'result'=>'mysqli_result', 'index'=>'int'], + 'mysqli_fetch_fields' => ['list', 'result'=>'mysqli_result'], 'mysqli_fetch_lengths' => ['array|false', 'result'=>'mysqli_result'], 'mysqli_fetch_object' => ['object|false|null', 'result'=>'mysqli_result', 'class='=>'string', 'constructor_args='=>'array'], 'mysqli_fetch_row' => ['list|false|null', 'result'=>'mysqli_result'], @@ -12832,10 +12803,10 @@ return [ 'mysqli_num_rows' => ['int<0, max>|numeric-string', 'result'=>'mysqli_result'], 'mysqli_options' => ['bool', 'mysql'=>'mysqli', 'option'=>'int', 'value'=>'string|int'], 'mysqli_ping' => ['bool', 'mysql'=>'mysqli'], - 'mysqli_poll' => ['int|false', 'read'=>'array', 'write'=>'array', 'error'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], + 'mysqli_poll' => ['int|false', '&w_read'=>'?array', '&w_error'=>'?array', '&w_reject'=>'array', 'seconds'=>'int', 'microseconds='=>'int'], 'mysqli_prepare' => ['mysqli_stmt|false', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_query' => ['mysqli_result|bool', 'mysql'=>'mysqli', 'query'=>'string', 'result_mode='=>'int'], - 'mysqli_real_connect' => ['bool', 'mysql='=>'mysqli', 'hostname='=>'string|null', 'username='=>'string|null', 'password='=>'string|null', 'database='=>'string|null', 'port='=>'int|null', 'socket='=>'string|null', 'flags='=>'int'], + 'mysqli_real_connect' => ['bool', 'mysql'=>'mysqli', 'hostname='=>'?string', 'username='=>'?string', 'password='=>'?string', 'database='=>'?string', 'port='=>'?int', 'socket='=>'?string', 'flags='=>'int'], 'mysqli_real_escape_string' => ['string', 'mysql'=>'mysqli', 'string'=>'string'], 'mysqli_real_query' => ['bool', 'mysql'=>'mysqli', 'query'=>'string'], 'mysqli_reap_async_query' => ['mysqli_result|false', 'mysql'=>'mysqli'], @@ -12852,9 +12823,9 @@ return [ 'mysqli_result::fetch_array\'1' => ['array|false|null', 'mode='=>'1'], 'mysqli_result::fetch_array\'2' => ['list|false|null', 'mode='=>'2'], 'mysqli_result::fetch_assoc' => ['array|false|null'], - 'mysqli_result::fetch_field' => ['object|false'], - 'mysqli_result::fetch_field_direct' => ['object|false', 'index'=>'int'], - 'mysqli_result::fetch_fields' => ['stdClass[]'], + 'mysqli_result::fetch_field' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false'], + 'mysqli_result::fetch_field_direct' => ['object{name:non-empty-string,orgname:string,table:string,orgtable:string,max_length:int,length:int,charsetnr:int,flags:int,type:int,decimals:int,db:string,def:string,catalog:string}|false', 'index'=>'int'], + 'mysqli_result::fetch_fields' => ['list'], 'mysqli_result::fetch_object' => ['object|false|null', 'class='=>'string', 'constructor_args='=>'array'], 'mysqli_result::fetch_row' => ['list|false|null'], 'mysqli_result::field_seek' => ['bool', 'index'=>'int'], @@ -12876,10 +12847,10 @@ return [ 'mysqli_sqlstate' => ['string', 'mysql'=>'mysqli'], 'mysqli_ssl_set' => ['true', 'mysql'=>'mysqli', 'key'=>'?string', 'certificate'=>'?string', 'ca_certificate'=>'?string', 'ca_path'=>'?string', 'cipher_algos'=>'?string'], 'mysqli_stat' => ['string|false', 'mysql'=>'mysqli'], - 'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query'=>'string'], + 'mysqli_stmt::__construct' => ['void', 'mysql'=>'mysqli', 'query='=>'string'], 'mysqli_stmt::attr_get' => ['int', 'attribute'=>'int'], 'mysqli_stmt::attr_set' => ['bool', 'attribute'=>'int', 'value'=>'int'], - 'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'], + 'mysqli_stmt::bind_param' => ['bool', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'], 'mysqli_stmt::bind_result' => ['bool', '&w_var1'=>'', '&...w_vars='=>''], 'mysqli_stmt::close' => ['bool'], 'mysqli_stmt::data_seek' => ['void', 'offset'=>'int'], @@ -12899,7 +12870,7 @@ return [ 'mysqli_stmt_affected_rows' => ['int<-1, max>|numeric-string', 'statement'=>'mysqli_stmt'], 'mysqli_stmt_attr_get' => ['int', 'statement'=>'mysqli_stmt', 'attribute'=>'int'], 'mysqli_stmt_attr_set' => ['bool', 'statement'=>'mysqli_stmt', 'attribute'=>'int', 'value'=>'int'], - 'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&vars'=>'mixed', '&...args='=>'mixed'], + 'mysqli_stmt_bind_param' => ['bool', 'statement'=>'mysqli_stmt', 'types'=>'string', '&var'=>'mixed', '&...vars='=>'mixed'], 'mysqli_stmt_bind_result' => ['bool', 'statement'=>'mysqli_stmt', '&w_var1'=>'', '&...w_vars='=>''], 'mysqli_stmt_close' => ['true', 'statement'=>'mysqli_stmt'], 'mysqli_stmt_data_seek' => ['void', 'statement'=>'mysqli_stmt', 'offset'=>'int'], @@ -13008,12 +12979,12 @@ return [ 'nsapi_response_headers' => ['array'], 'nsapi_virtual' => ['bool', 'uri'=>'string'], 'nthmac' => ['string', 'clent'=>'string', 'data'=>'string'], - 'number_format' => ['string', 'num'=>'float|int', 'decimals='=>'int'], - 'number_format\'1' => ['string', 'num'=>'float|int', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'], + 'number_format' => ['string', 'num'=>'float', 'decimals='=>'int'], + 'number_format\'1' => ['string', 'num'=>'float', 'decimals'=>'int', 'decimal_separator'=>'?string', 'thousands_separator'=>'?string'], 'numfmt_create' => ['NumberFormatter|null', 'locale'=>'string', 'style'=>'int', 'pattern='=>'string'], 'numfmt_format' => ['string|false', 'formatter'=>'NumberFormatter', 'num'=>'int|float', 'type='=>'int'], 'numfmt_format_currency' => ['string|false', 'formatter'=>'NumberFormatter', 'amount'=>'float', 'currency'=>'string'], - 'numfmt_get_attribute' => ['int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'], + 'numfmt_get_attribute' => ['float|int|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'], 'numfmt_get_error_code' => ['int', 'formatter'=>'NumberFormatter'], 'numfmt_get_error_message' => ['string', 'formatter'=>'NumberFormatter'], 'numfmt_get_locale' => ['string', 'formatter'=>'NumberFormatter', 'type='=>'int'], @@ -13022,7 +12993,7 @@ return [ 'numfmt_get_text_attribute' => ['string|false', 'formatter'=>'NumberFormatter', 'attribute'=>'int'], 'numfmt_parse' => ['float|int|false', 'formatter'=>'NumberFormatter', 'string'=>'string', 'type='=>'int', '&rw_offset='=>'int'], 'numfmt_parse_currency' => ['float|false', 'formatter'=>'NumberFormatter', 'string'=>'string', '&w_currency'=>'string', '&rw_offset='=>'int'], - 'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'int'], + 'numfmt_set_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'float|int'], 'numfmt_set_pattern' => ['bool', 'formatter'=>'NumberFormatter', 'pattern'=>'string'], 'numfmt_set_symbol' => ['bool', 'formatter'=>'NumberFormatter', 'symbol'=>'int', 'value'=>'string'], 'numfmt_set_text_attribute' => ['bool', 'formatter'=>'NumberFormatter', 'attribute'=>'int', 'value'=>'string'], @@ -13163,9 +13134,9 @@ return [ 'odbc_pconnect' => ['resource|false', 'dsn'=>'string', 'user'=>'string', 'password'=>'string', 'cursor_option='=>'int'], 'odbc_prepare' => ['resource|false', 'odbc'=>'resource', 'query'=>'string'], 'odbc_primarykeys' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'?string', 'schema'=>'string', 'table'=>'string'], - 'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string', 'column'=>'string'], - 'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog'=>'string', 'schema'=>'string', 'procedure'=>'string'], - 'odbc_result' => ['mixed|false', 'statement'=>'resource', 'field'=>'mixed'], + 'odbc_procedurecolumns' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string', 'column='=>'?string'], + 'odbc_procedures' => ['resource|false', 'odbc'=>'resource', 'catalog='=>'?string', 'schema='=>'?string', 'procedure='=>'?string'], + 'odbc_result' => ['string|bool|null', 'statement'=>'resource', 'field'=>'string|int'], 'odbc_result_all' => ['int|false', 'statement'=>'resource', 'format='=>'string'], 'odbc_rollback' => ['bool', 'odbc'=>'resource'], 'odbc_setoption' => ['bool', 'odbc'=>'resource', 'which'=>'int', 'option'=>'int', 'value'=>'int'], @@ -13229,7 +13200,7 @@ return [ 'openssl_pkcs12_read' => ['bool', 'pkcs12'=>'string', '&w_certificates'=>'array', 'passphrase'=>'string'], 'openssl_pkcs7_decrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'string|resource', 'private_key='=>'string|resource|array'], 'openssl_pkcs7_encrypt' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'string|resource|array', 'headers'=>'array', 'flags='=>'int', 'cipher_algo='=>'int'], - 'openssl_pkcs7_read' => ['bool', 'input_filename'=>'string', '&w_certificates'=>'array'], + 'openssl_pkcs7_read' => ['bool', 'data'=>'string', '&w_certificates'=>'array'], 'openssl_pkcs7_sign' => ['bool', 'input_filename'=>'string', 'output_filename'=>'string', 'certificate'=>'string|resource', 'private_key'=>'string|resource|array', 'headers'=>'array', 'flags='=>'int', 'untrusted_certificates_filename='=>'string'], 'openssl_pkcs7_verify' => ['bool|int', 'input_filename'=>'string', 'flags'=>'int', 'signers_certificates_filename='=>'string', 'ca_info='=>'array', 'untrusted_certificates_filename='=>'string', 'content='=>'string', 'output_filename='=>'string'], 'openssl_pkey_export' => ['bool', 'key'=>'resource', '&w_output'=>'string', 'passphrase='=>'string|null', 'options='=>'array'], @@ -13758,7 +13729,7 @@ return [ 'rand\'1' => ['int'], 'random_bytes' => ['non-empty-string', 'length'=>'positive-int'], 'random_int' => ['int', 'min'=>'int', 'max'=>'int'], - 'range' => ['array', 'start'=>'mixed', 'end'=>'mixed', 'step='=>'int|float'], + 'range' => ['non-empty-array', 'start'=>'string|int|float', 'end'=>'string|int|float', 'step='=>'int<1, max>|float'], 'rar_allow_broken_set' => ['bool', 'rarfile'=>'RarArchive', 'allow_broken'=>'bool'], 'rar_broken_is' => ['bool', 'rarfile'=>'rararchive'], 'rar_close' => ['bool', 'rarfile'=>'rararchive'], @@ -13823,7 +13794,7 @@ return [ 'rewind' => ['bool', 'stream'=>'resource'], 'rewinddir' => ['void', 'dir_handle='=>'resource'], 'rmdir' => ['bool', 'directory'=>'string', 'context='=>'resource'], - 'round' => ['float', 'num'=>'float', 'precision='=>'int', 'mode='=>'0|positive-int'], + 'round' => ['float', 'num'=>'float|int', 'precision='=>'int', 'mode='=>'0|positive-int'], 'rpm_close' => ['bool', 'rpmr'=>'resource'], 'rpm_get_tag' => ['mixed', 'rpmr'=>'resource', 'tagnum'=>'int'], 'rpm_is_valid' => ['bool', 'filename'=>'string'], @@ -13907,16 +13878,16 @@ return [ 'sem_remove' => ['bool', 'semaphore'=>'resource'], 'serialize' => ['string', 'value'=>'mixed'], 'session_abort' => ['bool'], - 'session_cache_expire' => ['int', 'value='=>'int'], - 'session_cache_limiter' => ['string', 'value='=>'string'], + 'session_cache_expire' => ['int|false', 'value='=>'int'], + 'session_cache_limiter' => ['string|false', 'value='=>'string'], 'session_commit' => ['bool'], 'session_decode' => ['bool', 'data'=>'string'], 'session_destroy' => ['bool'], - 'session_encode' => ['string'], + 'session_encode' => ['string|false'], 'session_get_cookie_params' => ['array'], 'session_id' => ['string|false', 'id='=>'string'], 'session_is_registered' => ['bool', 'name'=>'string'], - 'session_module_name' => ['string', 'module='=>'string'], + 'session_module_name' => ['string|false', 'module='=>'string'], 'session_name' => ['string|false', 'name='=>'string'], 'session_pgsql_add_error' => ['bool', 'error_level'=>'int', 'error_message='=>'string'], 'session_pgsql_get_error' => ['array', 'with_error_message='=>'bool'], @@ -13928,7 +13899,7 @@ return [ 'session_register' => ['bool', 'name'=>'mixed', '...args='=>'mixed'], 'session_register_shutdown' => ['void'], 'session_reset' => ['bool'], - 'session_save_path' => ['string', 'path='=>'string'], + 'session_save_path' => ['string|false', 'path='=>'string'], 'session_set_cookie_params' => ['bool', 'lifetime'=>'int', 'path='=>'string', 'domain='=>'string', 'secure='=>'bool', 'httponly='=>'bool'], 'session_set_save_handler' => ['bool', 'open'=>'callable(string,string):bool', 'close'=>'callable():bool', 'read'=>'callable(string):string', 'write'=>'callable(string,string):bool', 'destroy'=>'callable(string):bool', 'gc'=>'callable(string):bool', 'create_sid='=>'callable():string', 'validate_sid='=>'callable(string):bool', 'update_timestamp='=>'callable(string):bool'], 'session_set_save_handler\'1' => ['bool', 'open'=>'SessionHandlerInterface', 'close='=>'bool'], @@ -14024,16 +13995,16 @@ return [ 'sinh' => ['float', 'num'=>'float'], 'sizeof' => ['int<0, max>', 'value'=>'Countable|array|SimpleXMLElement', 'mode='=>'int'], 'sleep' => ['int|false', 'seconds'=>'0|positive-int'], - 'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'type'=>'string', 'value'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp2_get' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp2_getnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp2_real_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp2_set' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp2_walk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp3_get' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp3_getnext' => ['string|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp3_real_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp3_set' => ['bool', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'type'=>'array|string', 'value'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmp3_walk' => ['array|false', 'hostname'=>'string', 'security_name'=>'string', 'security_level'=>'string', 'auth_protocol'=>'string', 'auth_passphrase'=>'string', 'privacy_protocol'=>'string', 'privacy_passphrase'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], 'snmp_get_quick_print' => ['bool'], 'snmp_get_valueretrieval' => ['int'], 'snmp_read_mib' => ['bool', 'filename'=>'string'], @@ -14042,12 +14013,12 @@ return [ 'snmp_set_oid_output_format' => ['true', 'format'=>'int'], 'snmp_set_quick_print' => ['bool', 'enable'=>'bool'], 'snmp_set_valueretrieval' => ['true', 'method'=>'int'], - 'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'], - 'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], - 'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'string', 'timeout='=>'int', 'retries='=>'int'], + 'snmpget' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmpgetnext' => ['string|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmprealwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmpset' => ['bool', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'type'=>'string|string[]', 'value'=>'string|string[]', 'timeout='=>'int', 'retries='=>'int'], + 'snmpwalk' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], + 'snmpwalkoid' => ['array|false', 'hostname'=>'string', 'community'=>'string', 'object_id'=>'array|string', 'timeout='=>'int', 'retries='=>'int'], 'socket_accept' => ['resource|false', 'socket'=>'resource'], 'socket_bind' => ['bool', 'socket'=>'resource', 'address'=>'string', 'port='=>'int'], 'socket_clear_error' => ['void', 'socket='=>'resource'], @@ -14144,7 +14115,7 @@ return [ 'sqlsrv_commit' => ['bool', 'conn'=>'resource'], 'sqlsrv_configure' => ['bool', 'setting'=>'string', 'value'=>'mixed'], 'sqlsrv_connect' => ['resource|false', 'serverName'=>'string', 'connectionInfo='=>'array'], - 'sqlsrv_errors' => ['?array', 'errorsOrWarnings='=>'int'], + 'sqlsrv_errors' => ['?array', 'errorsAndOrWarnings='=>'int'], 'sqlsrv_execute' => ['bool', 'stmt'=>'resource'], 'sqlsrv_fetch' => ['?bool', 'stmt'=>'resource', 'row='=>'int', 'offset='=>'int'], 'sqlsrv_fetch_array' => ['array|null|false', 'stmt'=>'resource', 'fetchType='=>'int', 'row='=>'int', 'offset='=>'int'], @@ -14340,7 +14311,7 @@ return [ 'stream_context_create' => ['resource', 'options='=>'array', 'params='=>'array'], 'stream_context_get_default' => ['resource', 'options='=>'array'], 'stream_context_get_options' => ['array', 'stream_or_context'=>'resource'], - 'stream_context_get_params' => ['array', 'context'=>'resource'], + 'stream_context_get_params' => ['array{notification:string,options:array}', 'context'=>'resource'], 'stream_context_set_default' => ['resource', 'options'=>'array'], 'stream_context_set_option' => ['bool', 'context'=>'', 'wrapper_or_options'=>'string', 'option_name'=>'string', 'value'=>''], 'stream_context_set_option\'1' => ['bool', 'context'=>'', 'wrapper_or_options'=>'array'], @@ -14370,10 +14341,10 @@ return [ 'stream_socket_accept' => ['resource|false', 'socket'=>'resource', 'timeout='=>'float', '&w_peer_name='=>'string'], 'stream_socket_client' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'timeout='=>'float', 'flags='=>'int', 'context='=>'resource'], 'stream_socket_enable_crypto' => ['int|bool', 'stream'=>'resource', 'enable'=>'bool', 'crypto_method='=>'?int', 'session_stream='=>'resource'], - 'stream_socket_get_name' => ['string', 'socket'=>'resource', 'remote'=>'bool'], + 'stream_socket_get_name' => ['string|false', 'socket'=>'resource', 'remote'=>'bool'], 'stream_socket_pair' => ['resource[]|false', 'domain'=>'int', 'type'=>'int', 'protocol'=>'int'], - 'stream_socket_recvfrom' => ['string', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'], - 'stream_socket_sendto' => ['int', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'], + 'stream_socket_recvfrom' => ['string|false', 'socket'=>'resource', 'length'=>'int', 'flags='=>'int', '&w_address='=>'string'], + 'stream_socket_sendto' => ['int|false', 'socket'=>'resource', 'data'=>'string', 'flags='=>'int', 'address='=>'string'], 'stream_socket_server' => ['resource|false', 'address'=>'string', '&w_error_code='=>'int', '&w_error_message='=>'string', 'flags='=>'int', 'context='=>'resource'], 'stream_socket_shutdown' => ['bool', 'stream'=>'resource', 'mode'=>'int'], 'stream_supports_lock' => ['bool', 'stream'=>'resource'], @@ -14808,7 +14779,7 @@ return [ 'symbolObj::setPoints' => ['int', 'double'=>'array'], 'symlink' => ['bool', 'target'=>'string', 'link'=>'string'], 'sys_get_temp_dir' => ['string'], - 'sys_getloadavg' => ['array'], + 'sys_getloadavg' => ['array|false'], 'syslog' => ['true', 'priority'=>'int', 'message'=>'string'], 'system' => ['string|false', 'command'=>'string', '&w_result_code='=>'int'], 'taint' => ['bool', '&rw_string'=>'string', '&...w_other_strings='=>'string'], @@ -15259,7 +15230,7 @@ return [ 'uksort' => ['true', '&rw_array'=>'array', 'callback'=>'callable(mixed,mixed):int'], 'umask' => ['int', 'mask='=>'int'], 'uniqid' => ['non-empty-string', 'prefix='=>'string', 'more_entropy='=>'bool'], - 'unixtojd' => ['int', 'timestamp='=>'int'], + 'unixtojd' => ['int|false', 'timestamp='=>'int'], 'unlink' => ['bool', 'filename'=>'string', 'context='=>'resource'], 'unpack' => ['array', 'format'=>'string', 'string'=>'string'], 'unregister_tick_function' => ['void', 'callback'=>'callable'], @@ -15534,7 +15505,7 @@ return [ 'xml_parser_create' => ['resource', 'encoding='=>'string'], 'xml_parser_create_ns' => ['resource', 'encoding='=>'string', 'separator='=>'string'], 'xml_parser_free' => ['bool', 'parser'=>'resource'], - 'xml_parser_get_option' => ['string|false', 'parser'=>'resource', 'option'=>'int'], + 'xml_parser_get_option' => ['string|int', 'parser'=>'resource', 'option'=>'int'], 'xml_parser_set_option' => ['bool', 'parser'=>'resource', 'option'=>'int', 'value'=>'mixed'], 'xml_set_character_data_handler' => ['true', 'parser'=>'resource', 'handler'=>'callable'], 'xml_set_default_handler' => ['true', 'parser'=>'resource', 'handler'=>'callable'], @@ -15689,7 +15660,7 @@ return [ 'zip_entry_compressedsize' => ['int', 'zip_entry'=>'resource'], 'zip_entry_compressionmethod' => ['string', 'zip_entry'=>'resource'], 'zip_entry_filesize' => ['int', 'zip_entry'=>'resource'], - 'zip_entry_name' => ['string', 'zip_entry'=>'resource'], + 'zip_entry_name' => ['string|false', 'zip_entry'=>'resource'], 'zip_entry_open' => ['bool', 'zip_dp'=>'resource', 'zip_entry'=>'resource', 'mode='=>'string'], 'zip_entry_read' => ['string|false', 'zip_entry'=>'resource', 'len='=>'int'], 'zip_open' => ['resource|int|false', 'filename'=>'string'], diff --git a/vendor/vimeo/psalm/docs/annotating_code/supported_annotations.md b/vendor/vimeo/psalm/docs/annotating_code/supported_annotations.md index 922f5baa..346b525f 100644 --- a/vendor/vimeo/psalm/docs/annotating_code/supported_annotations.md +++ b/vendor/vimeo/psalm/docs/annotating_code/supported_annotations.md @@ -7,23 +7,25 @@ Psalm supports a wide range of docblock annotations. Psalm uses the following PHPDoc tags to understand your code: - [`@var`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/var.html) - Used for specifying the types of properties and variables@ + Used for specifying the types of properties and variables - [`@return`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/return.html) Used for specifying the return types of functions, methods and closures - [`@param`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/param.html) Used for specifying types of parameters passed to functions, methods and closures - [`@property`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html) Used to specify what properties can be accessed on an object that uses `__get` and `__set` -- [`@property-read`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property-read.html) +- [`@property-read`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html) Used to specify what properties can be read on object that uses `__get` -- [`@property-write`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property-write.html) +- [`@property-write`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/property.html) Used to specify what properties can be written on object that uses `__set` - [`@method`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/method.html) Used to specify which magic methods are available on object that uses `__call`. - [`@deprecated`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/deprecated.html) Used to mark functions, methods, classes and interfaces as being deprecated - [`@internal`](https://docs.phpdoc.org/latest/guide/references/phpdoc/tags/internal.html) - used to mark classes, functions and properties that are internal to an application or library. + Used to mark classes, functions and properties that are internal to an application or library. +- [`@mixin`](#mixins) + Used to tell Psalm that the current class proxies the methods and properties of the referenced class. ### Off-label usage of the `@var` tag @@ -46,6 +48,49 @@ function bat(): string { return $_GET['bat']; } ``` +### @mixins + +Adding `@mixin` to a classes docblock tells Psalm that the class proxies will proxy the methods and properties of the referenced class. + +```php +class A +{ + public string $a = 'A'; + + public function doA(): void + { + } +} + +/** + * @mixin A + */ +class B +{ + public string $b = 'B'; + + public function doB(): void + { + } + + public function __call($name, $arguments) + { + (new A())->$name(...$arguments); + } + + public function __get($name) + { + (new A())->$name; + } +} + +$b = new B(); +$b->doB(); +$b->doA(); // works +echo $b->b; +echo $b->a; // works +``` + ## Psalm-specific tags @@ -157,9 +202,10 @@ takesFoo(getFoo()); This provides the same, but for `false`. Psalm uses this internally for functions like `preg_replace`, which can return false if the given input has encoding errors, but where 99.9% of the time the function operates as expected. -### `@psalm-seal-properties` +### `@psalm-seal-properties`, `@psalm-no-seal-properties` If you have a magic property getter/setter, you can use `@psalm-seal-properties` to instruct Psalm to disallow getting and setting any properties not contained in a list of `@property` (or `@property-read`/`@property-write`) annotations. +This is automatically enabled with the configuration option `sealAllProperties` and can be disabled for a class with `@psalm-no-seal-properties` ```php bar = 5; // this call fails ``` +### `@psalm-seal-methods`, `@psalm-no-seal-methods` + +If you have a magic method caller, you can use `@psalm-seal-methods` to instruct Psalm to disallow calling any methods not contained in a list of `@method` annotations. +This is automatically enabled with the configuration option `sealAllMethods` and can be disabled for a class with `@psalm-no-seal-methods` + +```php +bar(); // this call fails +``` + ### `@psalm-internal` Used to mark a class, property or function as internal to a given namespace. Psalm treats this slightly differently to @@ -446,7 +515,18 @@ $username = $_GET['username']; // prints something like "test.php:4 $username: m ``` -*Note*: it throws [special low-level issue](../running_psalm/issues/Trace.md), so you have to set errorLevel to 1, override it in config or invoke Psalm with `--show-info=true`. +*Note*: it throws [special low-level issue](../running_psalm/issues/Trace.md). +To see it, you can set the global `errorLevel` to 1, or invoke Psalm with +`--show-info=true`, but both these solutions will probably result in a lot of +output. Another solution is to selectively bump the error level of the issue, +so that you only get one more error: + +```xml + + + + +``` ### `@psalm-check-type` @@ -596,7 +676,7 @@ class Foo } ``` When Psalm encounters variable property, it treats all properties in given class as potentially referenced. -With `@psalm-ignore-variable-property` annotation, this reference is ignored. +With `@psalm-ignore-variable-property` annotation, this reference is ignored. While `PossiblyUnusedProperty` would be emitted in both cases, using `@psalm-ignore-variable-property` would allow [Psalter](../manipulating_code/fixing.md) to delete `Foo::$bar`. @@ -642,9 +722,9 @@ function (): Generator { ``` This annotation supports only generic types, meaning that e.g. `@psalm-yield string` would be ignored. -### `@psalm-api` +### `@api`, `@psalm-api` -Used to tell Psalm that a class is used, even if no references to it can be +Used to tell Psalm that a class or method is used, even if no references to it can be found. Unused issues will be suppressed. For example, in frameworks, controllers are often invoked "magically" without @@ -657,6 +737,22 @@ any explicit references to them in your code. You should mark these classes with class UnreferencedClass {} ``` +### `@psalm-inheritors` + +Used to tell Psalm that a class can only be extended by a certain subset of classes. + +For example, +```php +` you can use other array generic types to provide more information about the open shape. + +```php +// This is an open array +/** @param array{someKey: string, ...} */ +// Which is the same as +/** @param array{someKey: string, ...} */ +// But it can be further locked down with a shape ... +/** @return array{someKey: string, ...} */ +``` + ## Callable arrays An array holding a callable, like PHP's native `call_user_func()` and friends supports it: diff --git a/vendor/vimeo/psalm/docs/annotating_code/type_syntax/atomic_types.md b/vendor/vimeo/psalm/docs/annotating_code/type_syntax/atomic_types.md index 7e3f0ea9..612cf9f7 100644 --- a/vendor/vimeo/psalm/docs/annotating_code/type_syntax/atomic_types.md +++ b/vendor/vimeo/psalm/docs/annotating_code/type_syntax/atomic_types.md @@ -46,7 +46,7 @@ Atomic types are the basic building block of all type information used in Psalm. * [`key-of`](utility_types.md#key-oft) * [`value-of`](utility_types.md#value-oft) * [`properties-of`](utility_types.md#properties-oft) - * [`class-string-map`](utility_types.md#class-string-mapt-as-foo-t) + * [`class-string-map`](utility_types.md#class-string-mapt-as-foo-t) * [`T[K]`](utility_types.md#tk) * [Type aliases](utility_types.md#type-aliases) * [Variable templates](utility_types.md#variable-templates) diff --git a/vendor/vimeo/psalm/docs/annotating_code/type_syntax/utility_types.md b/vendor/vimeo/psalm/docs/annotating_code/type_syntax/utility_types.md index d2de471f..4e690cbd 100644 --- a/vendor/vimeo/psalm/docs/annotating_code/type_syntax/utility_types.md +++ b/vendor/vimeo/psalm/docs/annotating_code/type_syntax/utility_types.md @@ -148,7 +148,7 @@ $b = asArray(new B); /** @psalm-trace $b */; // array{foo: string, bar: int, baz: float} ``` -## class-string-map<T as Foo, T> +## class-string-map<T of Foo, T> Used to indicate an array where each value is equal an instance of the class string contained in the key: @@ -166,11 +166,11 @@ class Foo {} class Bar extends Foo {} class A { - /** @var class-string-map */ + /** @var class-string-map */ private static array $map = []; /** - * @template U as Foo + * @template U of Foo * @param class-string $class * @return U */ @@ -191,7 +191,7 @@ $bar = A::get(Bar::class); /** @psalm-trace $bar */; // Bar ``` -If we had used an `array, Foo>` instead of a `class-string-map` in the above example, we would've gotten some false positive `InvalidReturnStatement` issues, caused by the lack of a type assertion inside the `isset`. +If we had used an `array, Foo>` instead of a `class-string-map` in the above example, we would've gotten some false positive `InvalidReturnStatement` issues, caused by the lack of a type assertion inside the `isset`. On the other hand, when using `class-string-map`, Psalm assumes that the value obtained by using a key `class-string` is always equal to `T`. Unbounded templates can also be used for unrelated classes: @@ -250,8 +250,8 @@ Used to get the value corresponding to the specified key: +``` +Whether using @property in class docblocks should imply @psalm-seal-properties. Defaults to `true`. + #### usePhpDocMethodsWithoutMagicCall ```xml diff --git a/vendor/vimeo/psalm/docs/running_psalm/issues.md b/vendor/vimeo/psalm/docs/running_psalm/issues.md index 9eb35fce..d9b3b4f1 100644 --- a/vendor/vimeo/psalm/docs/running_psalm/issues.md +++ b/vendor/vimeo/psalm/docs/running_psalm/issues.md @@ -52,6 +52,7 @@ - [InaccessibleClassConstant](issues/InaccessibleClassConstant.md) - [InaccessibleMethod](issues/InaccessibleMethod.md) - [InaccessibleProperty](issues/InaccessibleProperty.md) + - [InheritorViolation](issues/InheritorViolation.md) - [InterfaceInstantiation](issues/InterfaceInstantiation.md) - [InternalClass](issues/InternalClass.md) - [InternalMethod](issues/InternalMethod.md) @@ -284,6 +285,7 @@ - [UnresolvableInclude](issues/UnresolvableInclude.md) - [UnsafeGenericInstantiation](issues/UnsafeGenericInstantiation.md) - [UnsafeInstantiation](issues/UnsafeInstantiation.md) + - [UnsupportedPropertyReferenceUsage](issues/UnsupportedPropertyReferenceUsage.md) - [UnsupportedReferenceUsage](issues/UnsupportedReferenceUsage.md) - [UnusedBaselineEntry](issues/UnusedBaselineEntry.md) - [UnusedClass](issues/UnusedClass.md) diff --git a/vendor/vimeo/psalm/docs/running_psalm/issues/InheritorViolation.md b/vendor/vimeo/psalm/docs/running_psalm/issues/InheritorViolation.md new file mode 100644 index 00000000..33b9ffef --- /dev/null +++ b/vendor/vimeo/psalm/docs/running_psalm/issues/InheritorViolation.md @@ -0,0 +1,17 @@ +# InheritorViolation + +Emitted when a class/interface using `@psalm-inheritors` is extended/implemented +by a class that does not fulfil it's requirements. + +```php +b; +$b = ''; // Fatal error +``` + +* Static property assigned wrong type: +```php +b; +$b = 1; // Fatal error +``` diff --git a/vendor/vimeo/psalm/docs/security_analysis/avoiding_false_positives.md b/vendor/vimeo/psalm/docs/security_analysis/avoiding_false_positives.md index 13023660..583d8f33 100644 --- a/vendor/vimeo/psalm/docs/security_analysis/avoiding_false_positives.md +++ b/vendor/vimeo/psalm/docs/security_analysis/avoiding_false_positives.md @@ -26,7 +26,7 @@ function echoVar(string $str) : void { echoVar($_GET["text"]); ``` -## Conditional escaping tainted input +## Conditionally escaping tainted input A slightly modified version of the previous example is using a condition to determine whether the return value is considered secure. Only in case function argument `$escape` is true, the corresponding annotation @@ -50,6 +50,23 @@ echo processVar($_GET['text'], false); // detects tainted HTML echo processVar($_GET['text'], true); // considered secure ``` +## Sanitizing HTML user input + +Whenever possible, applications should be designed to accept & store user input as discrete text fields, rather than blocks of HTML. This allows user input to be fully escaped via `htmlspecialchars` or `htmlentities`. In cases where HTML user input is required (e.g. rich text editors like [TinyMCE](https://www.tiny.cloud/)), a library designed specifically to filter out risky HTML is highly recommended. For example, [HTML Purifier](http://htmlpurifier.org/docs) could be used as follows: + +```php +purify($html); +} +``` + ## Specializing taints in functions For functions, methods and classes you can use the `@psalm-taint-specialize` annotation. diff --git a/vendor/vimeo/psalm/src/Psalm/CodeLocation.php b/vendor/vimeo/psalm/src/Psalm/CodeLocation.php index 04790dfc..0bdfd64a 100644 --- a/vendor/vimeo/psalm/src/Psalm/CodeLocation.php +++ b/vendor/vimeo/psalm/src/Psalm/CodeLocation.php @@ -169,6 +169,7 @@ class CodeLocation $codebase = $project_analyzer->getCodebase(); + /** @psalm-suppress ImpureMethodCall */ $file_contents = $codebase->getFileContents($this->file_path); $file_length = strlen($file_contents); diff --git a/vendor/vimeo/psalm/src/Psalm/Codebase.php b/vendor/vimeo/psalm/src/Psalm/Codebase.php index 2e51cd49..d6caa46d 100644 --- a/vendor/vimeo/psalm/src/Psalm/Codebase.php +++ b/vendor/vimeo/psalm/src/Psalm/Codebase.php @@ -37,6 +37,8 @@ use Psalm\Internal\Codebase\Scanner; use Psalm\Internal\Codebase\TaintFlowGraph; use Psalm\Internal\DataFlow\TaintSink; use Psalm\Internal\DataFlow\TaintSource; +use Psalm\Internal\LanguageServer\PHPMarkdownContent; +use Psalm\Internal\LanguageServer\Reference; use Psalm\Internal\MethodIdentifier; use Psalm\Internal\Provider\ClassLikeStorageProvider; use Psalm\Internal\Provider\FileProvider; @@ -67,8 +69,10 @@ use ReflectionType; use UnexpectedValueException; use function array_combine; +use function array_merge; use function array_pop; use function array_reverse; +use function array_values; use function count; use function dirname; use function error_log; @@ -82,6 +86,7 @@ use function krsort; use function ksort; use function preg_match; use function preg_replace; +use function str_replace; use function strlen; use function strpos; use function strrpos; @@ -390,15 +395,19 @@ final class Codebase /** * @param array $candidate_files */ - public function reloadFiles(ProjectAnalyzer $project_analyzer, array $candidate_files): void + public function reloadFiles(ProjectAnalyzer $project_analyzer, array $candidate_files, bool $force = false): void { $this->loadAnalyzer(); + if ($force) { + FileReferenceProvider::clearCache(); + } + $this->file_reference_provider->loadReferenceCache(false); FunctionLikeAnalyzer::clearCache(); - if (!$this->statements_provider->parser_cache_provider) { + if ($force || !$this->statements_provider->parser_cache_provider) { $diff_files = $candidate_files; } else { $diff_files = []; @@ -500,7 +509,6 @@ final class Codebase } } - /** @psalm-mutation-free */ public function getFileContents(string $file_path): string { return $this->file_provider->getContents($file_path); @@ -593,6 +601,7 @@ final class Codebase */ public function findReferencesToProperty(string $property_id): array { + /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ [$fq_class_name, $property_name] = explode('::', $property_id); return $this->file_reference_provider->getClassPropertyLocations( @@ -970,7 +979,225 @@ final class Codebase } /** - * @return array{ type: string, description?: string|null}|null + * Get Markup content from Reference + */ + public function getMarkupContentForSymbolByReference( + Reference $reference + ): ?PHPMarkdownContent { + //Direct Assignment + if (is_numeric($reference->symbol[0])) { + return new PHPMarkdownContent( + preg_replace( + '/^[^:]*:/', + '', + $reference->symbol, + ), + ); + } + + //Class + if (strpos($reference->symbol, '::')) { + //Class Method + if (strpos($reference->symbol, '()')) { + $symbol = substr($reference->symbol, 0, -2); + + /** @psalm-suppress ArgumentTypeCoercion */ + $method_id = new MethodIdentifier(...explode('::', $symbol)); + + $declaring_method_id = $this->methods->getDeclaringMethodId( + $method_id, + ); + + if (!$declaring_method_id) { + return null; + } + + $storage = $this->methods->getStorage($declaring_method_id); + + return new PHPMarkdownContent( + $storage->getHoverMarkdown(), + "{$storage->defining_fqcln}::{$storage->cased_name}", + $storage->description, + ); + } + + /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ + [, $symbol_name] = explode('::', $reference->symbol); + + //Class Property + if (strpos($reference->symbol, '$') !== false) { + $property_id = preg_replace('/^\\\\/', '', $reference->symbol); + /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ + [$fq_class_name, $property_name] = explode('::$', $property_id); + $class_storage = $this->classlikes->getStorageFor($fq_class_name); + + //Get Real Properties + if (isset($class_storage->declaring_property_ids[$property_name])) { + $declaring_property_class = $class_storage->declaring_property_ids[$property_name]; + $declaring_class_storage = $this->classlike_storage_provider->get($declaring_property_class); + + if (isset($declaring_class_storage->properties[$property_name])) { + $storage = $declaring_class_storage->properties[$property_name]; + return new PHPMarkdownContent( + "{$storage->getInfo()} {$symbol_name}", + $reference->symbol, + $storage->description, + ); + } + } + + //Get Docblock properties + if (isset($class_storage->pseudo_property_set_types['$'.$property_name])) { + return new PHPMarkdownContent( + 'public '. + (string) $class_storage->pseudo_property_set_types['$'.$property_name].' $'.$property_name, + $reference->symbol, + ); + } + + //Get Docblock properties + if (isset($class_storage->pseudo_property_get_types['$'.$property_name])) { + return new PHPMarkdownContent( + 'public '. + (string) $class_storage->pseudo_property_get_types['$'.$property_name].' $'.$property_name, + $reference->symbol, + ); + } + + return null; + } + + /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ + [$fq_classlike_name, $const_name] = explode( + '::', + $reference->symbol, + ); + + $class_constants = $this->classlikes->getConstantsForClass( + $fq_classlike_name, + ReflectionProperty::IS_PRIVATE, + ); + + if (!isset($class_constants[$const_name])) { + return null; + } + + //Class Constant + return new PHPMarkdownContent( + $class_constants[$const_name]->getHoverMarkdown($const_name), + $fq_classlike_name . '::' . $const_name, + $class_constants[$const_name]->description, + ); + } + + //Procedural Function + if (strpos($reference->symbol, '()')) { + $function_id = strtolower(substr($reference->symbol, 0, -2)); + $file_storage = $this->file_storage_provider->get( + $reference->file_path, + ); + + if (isset($file_storage->functions[$function_id])) { + $function_storage = $file_storage->functions[$function_id]; + + return new PHPMarkdownContent( + $function_storage->getHoverMarkdown(), + $function_id, + $function_storage->description, + ); + } + + if (!$function_id) { + return null; + } + + $function = $this->functions->getStorage(null, $function_id); + + return new PHPMarkdownContent( + $function->getHoverMarkdown(), + $function_id, + $function->description, + ); + } + + //Procedural Variable + if (strpos($reference->symbol, '$') === 0) { + $type = VariableFetchAnalyzer::getGlobalType($reference->symbol, $this->analysis_php_version_id); + if (!$type->isMixed()) { + return new PHPMarkdownContent( + (string) $type, + $reference->symbol, + ); + } + } + + try { + $storage = $this->classlike_storage_provider->get( + $reference->symbol, + ); + return new PHPMarkdownContent( + ($storage->abstract ? 'abstract ' : '') . + 'class ' . + $storage->name, + $storage->name, + $storage->description, + ); + } catch (InvalidArgumentException $e) { + //continue on as normal + } + + if (strpos($reference->symbol, '\\')) { + $const_name_parts = explode('\\', $reference->symbol); + $const_name = array_pop($const_name_parts); + $namespace_name = implode('\\', $const_name_parts); + + $namespace_constants = NamespaceAnalyzer::getConstantsForNamespace( + $namespace_name, + ReflectionProperty::IS_PUBLIC, + ); + //Namespace Constant + if (isset($namespace_constants[$const_name])) { + $type = $namespace_constants[$const_name]; + return new PHPMarkdownContent( + $reference->symbol . ' ' . $type, + $reference->symbol, + ); + } + } else { + $file_storage = $this->file_storage_provider->get( + $reference->file_path, + ); + // ? + if (isset($file_storage->constants[$reference->symbol])) { + return new PHPMarkdownContent( + 'const ' . + $reference->symbol . + ' ' . + $file_storage->constants[$reference->symbol], + $reference->symbol, + ); + } + $type = ConstFetchAnalyzer::getGlobalConstType( + $this, + $reference->symbol, + $reference->symbol, + ); + + //Global Constant + if ($type) { + return new PHPMarkdownContent( + 'const ' . $reference->symbol . ' ' . $type, + $reference->symbol, + ); + } + } + + return new PHPMarkdownContent($reference->symbol); + } + + /** + * @psalm-suppress PossiblyUnusedMethod + * @deprecated will be removed in Psalm 6. use {@see Codebase::getSymbolLocationByReference()} instead */ public function getSymbolInformation(string $file_path, string $symbol): ?array { @@ -995,7 +1222,7 @@ final class Codebase $storage = $this->methods->getStorage($declaring_method_id); return [ - 'type' => 'getSignature(true), + 'type' => 'getCompletionSignature(), 'description' => $storage->description, ]; } @@ -1036,7 +1263,7 @@ final class Codebase $function_storage = $file_storage->functions[$function_id]; return [ - 'type' => 'getSignature(true), + 'type' => 'getCompletionSignature(), 'description' => $function_storage->description, ]; } @@ -1047,7 +1274,7 @@ final class Codebase $function = $this->functions->getStorage(null, $function_id); return [ - 'type' => 'getSignature(true), + 'type' => 'getCompletionSignature(), 'description' => $function->description, ]; } @@ -1100,6 +1327,10 @@ final class Codebase } } + /** + * @psalm-suppress PossiblyUnusedMethod + * @deprecated will be removed in Psalm 6. use {@see Codebase::getSymbolLocationByReference()} instead + */ public function getSymbolLocation(string $file_path, string $symbol): ?CodeLocation { if (is_numeric($symbol[0])) { @@ -1182,11 +1413,127 @@ final class Codebase } } + public function getSymbolLocationByReference(Reference $reference): ?CodeLocation + { + if (is_numeric($reference->symbol[0])) { + $symbol = preg_replace('/:.*/', '', $reference->symbol); + $symbol_parts = explode('-', $symbol); + + if (!isset($symbol_parts[0]) || !isset($symbol_parts[1])) { + return null; + } + + $file_contents = $this->getFileContents($reference->file_path); + + return new Raw( + $file_contents, + $reference->file_path, + $this->config->shortenFileName($reference->file_path), + (int) $symbol_parts[0], + (int) $symbol_parts[1], + ); + } + + try { + if (strpos($reference->symbol, '::')) { + if (strpos($reference->symbol, '()')) { + $symbol = substr($reference->symbol, 0, -2); + + /** @psalm-suppress ArgumentTypeCoercion */ + $method_id = new MethodIdentifier( + ...explode('::', $symbol), + ); + + $declaring_method_id = $this->methods->getDeclaringMethodId( + $method_id, + ); + + if (!$declaring_method_id) { + return null; + } + + $storage = $this->methods->getStorage($declaring_method_id); + + return $storage->location; + } + + if (strpos($reference->symbol, '$') !== false) { + $storage = $this->properties->getStorage( + $reference->symbol, + ); + + return $storage->location; + } + + /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ + [$fq_classlike_name, $const_name] = explode( + '::', + $reference->symbol, + ); + + $class_constants = $this->classlikes->getConstantsForClass( + $fq_classlike_name, + ReflectionProperty::IS_PRIVATE, + ); + + if (!isset($class_constants[$const_name])) { + return null; + } + + return $class_constants[$const_name]->location; + } + + if (strpos($reference->symbol, '()')) { + $file_storage = $this->file_storage_provider->get( + $reference->file_path, + ); + + $function_id = strtolower(substr($reference->symbol, 0, -2)); + + if (isset($file_storage->functions[$function_id])) { + return $file_storage->functions[$function_id]->location; + } + + if (!$function_id) { + return null; + } + + return $this->functions->getStorage(null, $function_id) + ->location; + } + + return $this->classlike_storage_provider->get( + $reference->symbol, + )->location; + } catch (UnexpectedValueException $e) { + error_log($e->getMessage()); + + return null; + } catch (InvalidArgumentException $e) { + return null; + } + } + /** + * @psalm-suppress PossiblyUnusedMethod * @return array{0: string, 1: Range}|null */ public function getReferenceAtPosition(string $file_path, Position $position): ?array { + $ref = $this->getReferenceAtPositionAsReference($file_path, $position); + if ($ref === null) { + return null; + } + return [$ref->symbol, $ref->range]; + } + + /** + * Get Reference from Position + */ + public function getReferenceAtPositionAsReference( + string $file_path, + Position $position + ): ?Reference { $is_open = $this->file_provider->isOpen($file_path); if (!$is_open) { @@ -1197,33 +1544,37 @@ final class Codebase $offset = $position->toOffset($file_contents); - [$reference_map, $type_map] = $this->analyzer->getMapsForFile($file_path); - - $reference = null; - - if (!$reference_map && !$type_map) { - return null; - } + $reference_maps = $this->analyzer->getMapsForFile($file_path); $reference_start_pos = null; $reference_end_pos = null; + $symbol = null; - ksort($reference_map); + foreach ($reference_maps as $reference_map) { + ksort($reference_map); - foreach ($reference_map as $start_pos => [$end_pos, $possible_reference]) { - if ($offset < $start_pos) { + foreach ($reference_map as $start_pos => [$end_pos, $possible_reference]) { + if ($offset < $start_pos) { + break; + } + + if ($offset > $end_pos) { + continue; + } + $reference_start_pos = $start_pos; + $reference_end_pos = $end_pos; + $symbol = $possible_reference; + } + + if ($symbol !== null && + $reference_start_pos !== null && + $reference_end_pos !== null + ) { break; } - - if ($offset > $end_pos) { - continue; - } - $reference_start_pos = $start_pos; - $reference_end_pos = $end_pos; - $reference = $possible_reference; } - if ($reference === null || $reference_start_pos === null || $reference_end_pos === null) { + if ($symbol === null || $reference_start_pos === null || $reference_end_pos === null) { return null; } @@ -1232,7 +1583,7 @@ final class Codebase self::getPositionFromOffset($reference_end_pos, $file_contents), ); - return [$reference, $range]; + return new Reference($file_path, $symbol, $range); } /** @@ -1399,6 +1750,7 @@ final class Codebase continue; } + /** @psalm-suppress PossiblyUndefinedIntArrayOffset */ $num_whitespace_bytes = preg_match('/\G\s+/', $file_contents, $matches, 0, $end_pos_excluding_whitespace) ? strlen($matches[0]) : 0; @@ -1487,8 +1839,11 @@ final class Codebase /** * @return list */ - public function getCompletionItemsForClassishThing(string $type_string, string $gap): array - { + public function getCompletionItemsForClassishThing( + string $type_string, + string $gap, + bool $snippets_supported = false + ): array { $completion_items = []; $type = Type::parseString($type_string); @@ -1505,11 +1860,11 @@ final class Codebase $completion_item = new CompletionItem( $method_storage->cased_name, CompletionItemKind::METHOD, - (string)$method_storage, + $method_storage->getCompletionSignature(), $method_storage->description, (string)$method_storage->visibility, $method_storage->cased_name, - $method_storage->cased_name . (count($method_storage->params) !== 0 ? '($0)' : '()'), + $method_storage->cased_name, null, null, new Command('Trigger parameter hints', 'editor.action.triggerParameterHints'), @@ -1517,12 +1872,47 @@ final class Codebase 2, ); - $completion_item->insertTextFormat = InsertTextFormat::SNIPPET; + if ($snippets_supported && count($method_storage->params) > 0) { + $completion_item->insertText .= '($0)'; + $completion_item->insertTextFormat = + InsertTextFormat::SNIPPET; + } else { + $completion_item->insertText .= '()'; + } $completion_items[] = $completion_item; } } + $pseudo_property_types = []; + foreach ($class_storage->pseudo_property_get_types as $property_name => $type) { + $pseudo_property_types[$property_name] = new CompletionItem( + str_replace('$', '', $property_name), + CompletionItemKind::PROPERTY, + $type->__toString(), + null, + '1', //sort text + str_replace('$', '', $property_name), + ($gap === '::' ? '$' : '') . + str_replace('$', '', $property_name), + ); + } + + foreach ($class_storage->pseudo_property_set_types as $property_name => $type) { + $pseudo_property_types[$property_name] = new CompletionItem( + str_replace('$', '', $property_name), + CompletionItemKind::PROPERTY, + $type->__toString(), + null, + '1', + str_replace('$', '', $property_name), + ($gap === '::' ? '$' : '') . + str_replace('$', '', $property_name), + ); + } + + $completion_items = array_merge($completion_items, array_values($pseudo_property_types)); + foreach ($class_storage->declaring_property_ids as $property_name => $declaring_class) { $property_storage = $this->properties->getStorage( $declaring_class . '::$' . $property_name, @@ -1715,7 +2105,7 @@ final class Codebase $completion_items[] = new CompletionItem( $function_name, CompletionItemKind::FUNCTION, - $function->getSignature(false), + $function->getCompletionSignature(), $function->description, null, $function_name, @@ -1832,9 +2222,9 @@ final class Codebase ); } - public function addTemporaryFileChanges(string $file_path, string $new_content): void + public function addTemporaryFileChanges(string $file_path, string $new_content, ?int $version = null): void { - $this->file_provider->addTemporaryFileChanges($file_path, $new_content); + $this->file_provider->addTemporaryFileChanges($file_path, $new_content, $version); } public function removeTemporaryFileChanges(string $file_path): void diff --git a/vendor/vimeo/psalm/src/Psalm/Config.php b/vendor/vimeo/psalm/src/Psalm/Config.php index de71b0d1..fcc1fbea 100644 --- a/vendor/vimeo/psalm/src/Psalm/Config.php +++ b/vendor/vimeo/psalm/src/Psalm/Config.php @@ -194,6 +194,13 @@ class Config */ public $use_docblock_property_types = false; + /** + * Whether using property annotations in docblocks should implicitly seal properties + * + * @var bool + */ + public $docblock_property_types_seal_properties = true; + /** * Whether or not to throw an exception on first error * @@ -1049,6 +1056,7 @@ class Config $booleanAttributes = [ 'useDocblockTypes' => 'use_docblock_types', 'useDocblockPropertyTypes' => 'use_docblock_property_types', + 'docblockPropertyTypesSealProperties' => 'docblock_property_types_seal_properties', 'throwExceptionOnError' => 'throw_exception', 'hideExternalErrors' => 'hide_external_errors', 'hideAllErrorsExceptPassedFiles' => 'hide_all_errors_except_passed_files', @@ -1727,7 +1735,8 @@ class Config public function reportIssueInFile(string $issue_type, string $file_path): bool { - if (($this->show_mixed_issues === false || $this->level > 2) + if ((($this->level < 3 && $this->show_mixed_issues === false) + || ($this->level > 2 && $this->show_mixed_issues !== true)) && in_array($issue_type, self::MIXED_ISSUES, true) ) { return false; diff --git a/vendor/vimeo/psalm/src/Psalm/Config/FileFilter.php b/vendor/vimeo/psalm/src/Psalm/Config/FileFilter.php index 01b1656e..cefa55b0 100644 --- a/vendor/vimeo/psalm/src/Psalm/Config/FileFilter.php +++ b/vendor/vimeo/psalm/src/Psalm/Config/FileFilter.php @@ -122,6 +122,7 @@ class FileFilter $declare_strict_types = (bool) ($directory['useStrictTypes'] ?? false); if ($directory_path[0] === '/' && DIRECTORY_SEPARATOR === '/') { + /** @var non-empty-string */ $prospective_directory_path = $directory_path; } else { $prospective_directory_path = $base_dir . DIRECTORY_SEPARATOR . $directory_path; @@ -238,6 +239,7 @@ class FileFilter $file_path = (string) ($file['name'] ?? ''); if ($file_path[0] === '/' && DIRECTORY_SEPARATOR === '/') { + /** @var non-empty-string */ $prospective_file_path = $file_path; } else { $prospective_file_path = $base_dir . DIRECTORY_SEPARATOR . $file_path; diff --git a/vendor/vimeo/psalm/src/Psalm/DocComment.php b/vendor/vimeo/psalm/src/Psalm/DocComment.php index 3c4d09ad..2a04279f 100644 --- a/vendor/vimeo/psalm/src/Psalm/DocComment.php +++ b/vendor/vimeo/psalm/src/Psalm/DocComment.php @@ -24,6 +24,7 @@ final class DocComment 'assert', 'assert-if-true', 'assert-if-false', 'suppress', 'ignore-nullable-return', 'override-property-visibility', 'override-method-visibility', 'seal-properties', 'seal-methods', + 'no-seal-properties', 'no-seal-methods', 'ignore-falsable-return', 'variadic', 'pure', 'ignore-variable-method', 'ignore-variable-property', 'internal', 'taint-sink', 'taint-source', 'assert-untainted', 'scope-this', @@ -33,7 +34,7 @@ final class DocComment 'taint-unescape', 'self-out', 'consistent-constructor', 'stub-override', 'require-extends', 'require-implements', 'param-out', 'ignore-var', 'consistent-templates', 'if-this-is', 'this-out', 'check-type', 'check-type-exact', - 'api', + 'api', 'inheritors', ]; /** diff --git a/vendor/vimeo/psalm/src/Psalm/ErrorBaseline.php b/vendor/vimeo/psalm/src/Psalm/ErrorBaseline.php index 9a22ec82..9a83b0a2 100644 --- a/vendor/vimeo/psalm/src/Psalm/ErrorBaseline.php +++ b/vendor/vimeo/psalm/src/Psalm/ErrorBaseline.php @@ -118,7 +118,7 @@ final class ErrorBaseline foreach ($codeSamples as $codeSample) { $files[$fileName][$issueType]['o'] += 1; - $files[$fileName][$issueType]['s'][] = trim($codeSample->textContent); + $files[$fileName][$issueType]['s'][] = str_replace("\r\n", "\n", trim($codeSample->textContent)); } // TODO: Remove in Psalm 6 diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php index 137b4755..3d120c89 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassAnalyzer.php @@ -362,16 +362,6 @@ class ClassAnalyzer extends ClassLikeAnalyzer return; } - if ($this->leftover_stmts) { - (new StatementsAnalyzer( - $this, - new NodeDataProvider(), - ))->analyze( - $this->leftover_stmts, - $class_context, - ); - } - if (!$storage->abstract) { foreach ($storage->declaring_method_ids as $declaring_method_id) { $method_storage = $codebase->methods->getStorage($declaring_method_id); @@ -712,7 +702,7 @@ class ClassAnalyzer extends ClassLikeAnalyzer new OverriddenPropertyAccess( 'Property ' . $fq_class_name . '::$' . $property_name . ' has different access level than ' - . $storage->name . '::$' . $property_name, + . $guide_class_name . '::$' . $property_name, $property_storage->location, ), ); @@ -1781,8 +1771,6 @@ class ClassAnalyzer extends ClassLikeAnalyzer $method_context = clone $class_context; foreach ($method_context->vars_in_scope as $context_var_id => $context_type) { - $method_context->vars_in_scope[$context_var_id] = $context_type; - if ($context_type->from_property && $stmt->name->name !== '__construct') { $method_context->vars_in_scope[$context_var_id] = $method_context->vars_in_scope[$context_var_id]->setProperties(['initialized' => true]); diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php index 60491a78..92e86998 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ClassLikeAnalyzer.php @@ -14,6 +14,7 @@ use Psalm\Internal\Type\Comparator\UnionTypeComparator; use Psalm\Internal\Type\TemplateResult; use Psalm\Internal\Type\TemplateStandinTypeReplacer; use Psalm\Issue\InaccessibleProperty; +use Psalm\Issue\InheritorViolation; use Psalm\Issue\InvalidClass; use Psalm\Issue\InvalidTemplateParam; use Psalm\Issue\MissingDependency; @@ -28,6 +29,7 @@ use Psalm\Plugin\EventHandler\Event\AfterClassLikeExistenceCheckEvent; use Psalm\StatementsSource; use Psalm\Storage\ClassLikeStorage; use Psalm\Type; +use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; use UnexpectedValueException; @@ -92,11 +94,6 @@ abstract class ClassLikeAnalyzer extends SourceAnalyzer */ protected ?string $parent_fq_class_name = null; - /** - * @var PhpParser\Node\Stmt[] - */ - protected array $leftover_stmts = []; - protected ClassLikeStorage $storage; public function __construct(PhpParser\Node\Stmt\ClassLike $class, SourceAnalyzer $source, string $fq_class_name) @@ -336,6 +333,23 @@ abstract class ClassLikeAnalyzer extends SourceAnalyzer return null; } + + $classUnion = new Union([new TNamedObject($fq_class_name)]); + foreach ($class_storage->parent_classes + $class_storage->direct_class_interfaces as $parent_class) { + $parent_storage = $codebase->classlikes->getStorageFor($parent_class); + if ($parent_storage && $parent_storage->inheritors) { + if (!UnionTypeComparator::isContainedBy($codebase, $classUnion, $parent_storage->inheritors)) { + IssueBuffer::maybeAdd( + new InheritorViolation( + 'Class ' . $fq_class_name . ' is not an allowed inheritor of parent class ' . $parent_class, + $code_location, + ), + $suppressed_issues, + ); + } + } + } + foreach ($class_storage->invalid_dependencies as $dependency_class_name => $_) { // if the implemented/extended class is stubbed, it may not yet have // been hydrated diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php index 152565c2..3c0b6f2b 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/FunctionLikeAnalyzer.php @@ -72,6 +72,7 @@ use function count; use function end; use function in_array; use function is_string; +use function krsort; use function mb_strpos; use function md5; use function microtime; @@ -80,6 +81,8 @@ use function strpos; use function strtolower; use function substr; +use const SORT_NUMERIC; + /** * @internal * @template-covariant TFunction as Closure|Function_|ClassMethod|ArrowFunction @@ -721,7 +724,10 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer if ($expected_exception === $possibly_thrown_exception || ( $codebase->classOrInterfaceExists($possibly_thrown_exception) - && $codebase->classExtendsOrImplements($possibly_thrown_exception, $expected_exception) + && ( + $codebase->interfaceExtends($possibly_thrown_exception, $expected_exception) + || $codebase->classExtendsOrImplements($possibly_thrown_exception, $expected_exception) + ) ) ) { $is_expected = true; @@ -870,96 +876,55 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer ): void { $codebase = $statements_analyzer->getCodebase(); - $unused_params = []; + $unused_params = $this->detectUnusedParameters($statements_analyzer, $storage, $context); - foreach ($statements_analyzer->getUnusedVarLocations() as [$var_name, $original_location]) { - if (!array_key_exists(substr($var_name, 1), $storage->param_lookup)) { - continue; - } + if (!$storage instanceof MethodStorage + || !$storage->cased_name + || $storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE + ) { + $last_unused_argument_position = $this->detectPreviousUnusedArgumentPosition( + $storage, + count($storage->params) - 1, + ); - if (strpos($var_name, '$_') === 0 || (strpos($var_name, '$unused') === 0 && $var_name !== '$unused')) { - continue; - } + // Sort parameters in reverse order so that we can start from the end of parameters + krsort($unused_params, SORT_NUMERIC); - $position = array_search(substr($var_name, 1), array_keys($storage->param_lookup), true); + foreach ($unused_params as $unused_param_position => $unused_param_code_location) { + $unused_param_var_name = $storage->params[$unused_param_position]->name; + $unused_param_message = 'Param ' . $unused_param_var_name . ' is never referenced in this method'; - if ($position === false) { - throw new UnexpectedValueException('$position should not be false here'); - } + // Remove the key as we already report the issue + unset($unused_params[$unused_param_position]); - if ($storage->params[$position]->promoted_property) { - continue; - } - - $did_match_param = false; - - foreach ($this->function->params as $param) { - if ($param->var->getAttribute('endFilePos') === $original_location->raw_file_end) { - $did_match_param = true; + // Do not report unused required parameters + if ($unused_param_position !== $last_unused_argument_position) { break; } - } - if (!$did_match_param) { - continue; - } + $last_unused_argument_position = $this->detectPreviousUnusedArgumentPosition( + $storage, + $unused_param_position - 1, + ); - $assignment_node = DataFlowNode::getForAssignment($var_name, $original_location); - - if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph - && $statements_analyzer->data_flow_graph->isVariableUsed($assignment_node) - ) { - continue; - } - - if (!$storage instanceof MethodStorage - || !$storage->cased_name - || $storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE - ) { if ($this instanceof ClosureAnalyzer) { IssueBuffer::maybeAdd( new UnusedClosureParam( - 'Param ' . $var_name . ' is never referenced in this method', - $original_location, + $unused_param_message, + $unused_param_code_location, ), $this->getSuppressedIssues(), ); - } else { - IssueBuffer::maybeAdd( - new UnusedParam( - 'Param ' . $var_name . ' is never referenced in this method', - $original_location, - ), - $this->getSuppressedIssues(), - ); - } - } else { - $fq_class_name = (string)$context->self; - - $class_storage = $codebase->classlike_storage_provider->get($fq_class_name); - - $method_name_lc = strtolower($storage->cased_name); - - if ($storage->abstract) { continue; } - if (isset($class_storage->overridden_method_ids[$method_name_lc])) { - $parent_method_id = end($class_storage->overridden_method_ids[$method_name_lc]); - - if ($parent_method_id) { - $parent_method_storage = $codebase->methods->getStorage($parent_method_id); - - // if the parent method has a param at that position and isn't abstract - if (!$parent_method_storage->abstract - && isset($parent_method_storage->params[$position]) - ) { - continue; - } - } - } - - $unused_params[$position] = $original_location; + IssueBuffer::maybeAdd( + new UnusedParam( + $unused_param_message, + $unused_param_code_location, + ), + $this->getSuppressedIssues(), + ); } } @@ -2076,4 +2041,120 @@ abstract class FunctionLikeAnalyzer extends SourceAnalyzer $overridden_method_ids, ]; } + + /** + * @return array + */ + private function detectUnusedParameters( + StatementsAnalyzer $statements_analyzer, + FunctionLikeStorage $storage, + Context $context + ): array { + $codebase = $statements_analyzer->getCodebase(); + + $unused_params = []; + + foreach ($statements_analyzer->getUnusedVarLocations() as [$var_name, $original_location]) { + if (!array_key_exists(substr($var_name, 1), $storage->param_lookup)) { + continue; + } + + if ($this->isIgnoredForUnusedParam($var_name)) { + continue; + } + + $position = array_search(substr($var_name, 1), array_keys($storage->param_lookup), true); + + if ($position === false) { + throw new UnexpectedValueException('$position should not be false here'); + } + + if ($storage->params[$position]->promoted_property) { + continue; + } + + $did_match_param = false; + + foreach ($this->function->params as $param) { + if ($param->var->getAttribute('endFilePos') === $original_location->raw_file_end) { + $did_match_param = true; + break; + } + } + + if (!$did_match_param) { + continue; + } + + $assignment_node = DataFlowNode::getForAssignment($var_name, $original_location); + + if ($statements_analyzer->data_flow_graph instanceof VariableUseGraph + && $statements_analyzer->data_flow_graph->isVariableUsed($assignment_node) + ) { + continue; + } + + if (!$storage instanceof MethodStorage + || !$storage->cased_name + || $storage->visibility === ClassLikeAnalyzer::VISIBILITY_PRIVATE + ) { + $unused_params[$position] = $original_location; + continue; + } + + $fq_class_name = (string)$context->self; + + $class_storage = $codebase->classlike_storage_provider->get($fq_class_name); + + $method_name_lc = strtolower($storage->cased_name); + + if ($storage->abstract) { + continue; + } + + if (isset($class_storage->overridden_method_ids[$method_name_lc])) { + $parent_method_id = end($class_storage->overridden_method_ids[$method_name_lc]); + + if ($parent_method_id) { + $parent_method_storage = $codebase->methods->getStorage($parent_method_id); + + // if the parent method has a param at that position and isn't abstract + if (!$parent_method_storage->abstract + && isset($parent_method_storage->params[$position]) + ) { + continue; + } + } + } + + $unused_params[$position] = $original_location; + } + + return $unused_params; + } + + private function detectPreviousUnusedArgumentPosition(FunctionLikeStorage $function, int $position): int + { + $params = $function->params; + krsort($params, SORT_NUMERIC); + + foreach ($params as $index => $param) { + if ($index > $position) { + continue; + } + + if ($this->isIgnoredForUnusedParam($param->name)) { + continue; + } + + return $index; + } + + return 0; + } + + private function isIgnoredForUnusedParam(string $var_name): bool + { + return strpos($var_name, '$_') === 0 || (strpos($var_name, '$unused') === 0 && $var_name !== '$unused'); + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php index 9187b76c..6b5d2809 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/NamespaceAnalyzer.php @@ -15,6 +15,7 @@ use UnexpectedValueException; use function assert; use function count; use function implode; +use function is_string; use function preg_replace; use function strpos; use function strtolower; @@ -244,7 +245,7 @@ class NamespaceAnalyzer extends SourceAnalyzer while (($pos = strpos($identifier, "\\")) !== false) { if ($pos > 0) { $part = substr($identifier, 0, $pos); - assert($part !== ""); + assert(is_string($part) && $part !== ""); $parts[] = $part; } $parts[] = "\\"; @@ -253,13 +254,13 @@ class NamespaceAnalyzer extends SourceAnalyzer if (($pos = strpos($identifier, "::")) !== false) { if ($pos > 0) { $part = substr($identifier, 0, $pos); - assert($part !== ""); + assert(is_string($part) && $part !== ""); $parts[] = $part; } $parts[] = "::"; $identifier = substr($identifier, $pos + 2); } - if ($identifier !== "") { + if ($identifier !== "" && $identifier !== false) { $parts[] = $identifier; } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php index b874537f..6104e067 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/ProjectAnalyzer.php @@ -2,7 +2,6 @@ namespace Psalm\Internal\Analyzer; -use Amp\Loop; use Fidry\CpuCoreCounter\CpuCoreCounter; use Fidry\CpuCoreCounter\NumberOfCpuCoreNotFound; use InvalidArgumentException; @@ -15,8 +14,6 @@ use Psalm\FileManipulation; use Psalm\Internal\Codebase\TaintFlowGraph; use Psalm\Internal\FileManipulation\FileManipulationBuffer; use Psalm\Internal\LanguageServer\LanguageServer; -use Psalm\Internal\LanguageServer\ProtocolStreamReader; -use Psalm\Internal\LanguageServer\ProtocolStreamWriter; use Psalm\Internal\MethodIdentifier; use Psalm\Internal\Provider\ClassLikeStorageProvider; use Psalm\Internal\Provider\FileProvider; @@ -65,7 +62,6 @@ use function array_map; use function array_merge; use function array_shift; use function clearstatcache; -use function cli_set_process_title; use function count; use function defined; use function dirname; @@ -82,14 +78,9 @@ use function is_file; use function microtime; use function mkdir; use function number_format; -use function pcntl_fork; use function preg_match; use function rename; use function sprintf; -use function stream_set_blocking; -use function stream_socket_accept; -use function stream_socket_client; -use function stream_socket_server; use function strlen; use function strpos; use function strtolower; @@ -102,8 +93,6 @@ use const PHP_OS; use const PHP_VERSION; use const PSALM_VERSION; use const STDERR; -use const STDIN; -use const STDOUT; /** * @internal @@ -211,16 +200,6 @@ class ProjectAnalyzer UnnecessaryVarAnnotation::class, ]; - /** - * When this is true, the language server will send the diagnostic code with a help link. - */ - public bool $language_server_use_extended_diagnostic_codes = false; - - /** - * If this is true then the language server will send log messages to the client with additional information. - */ - public bool $language_server_verbose = false; - /** * @param array $generated_report_options */ @@ -230,12 +209,21 @@ class ProjectAnalyzer ?ReportOptions $stdout_report_options = null, array $generated_report_options = [], int $threads = 1, - ?Progress $progress = null + ?Progress $progress = null, + ?Codebase $codebase = null ) { if ($progress === null) { $progress = new VoidProgress(); } + if ($codebase === null) { + $codebase = new Codebase( + $config, + $providers, + $progress, + ); + } + $this->parser_cache_provider = $providers->parser_cache_provider; $this->project_cache_provider = $providers->project_cache_provider; $this->file_provider = $providers->file_provider; @@ -248,11 +236,7 @@ class ProjectAnalyzer $this->clearCacheDirectoryIfConfigOrComposerLockfileChanged(); - $this->codebase = new Codebase( - $config, - $providers, - $progress, - ); + $this->codebase = $codebase; $this->stdout_report_options = $stdout_report_options; $this->generated_report_options = $generated_report_options; @@ -394,10 +378,12 @@ class ProjectAnalyzer ); } - public function server(?string $address = '127.0.0.1:12345', bool $socket_server_mode = false): void + public function serverMode(LanguageServer $server): void { + $server->logInfo("Initializing: Visiting Autoload Files..."); $this->visitAutoloadFiles(); $this->codebase->diff_methods = true; + $server->logInfo("Initializing: Loading Reference Cache..."); $this->file_reference_provider->loadReferenceCache(); $this->codebase->enterServerMode(); @@ -418,103 +404,12 @@ class ProjectAnalyzer } } + $server->logInfo("Initializing: Initialize Plugins..."); $this->config->initializePlugins($this); foreach ($this->config->getProjectDirectories() as $dir_name) { $this->checkDirWithConfig($dir_name, $this->config); } - - @cli_set_process_title('Psalm ' . PSALM_VERSION . ' - PHP Language Server'); - - if (!$socket_server_mode && $address) { - // Connect to a TCP server - $socket = stream_socket_client('tcp://' . $address, $errno, $errstr); - if ($socket === false) { - fwrite(STDERR, "Could not connect to language client. Error $errno\n$errstr"); - exit(1); - } - stream_set_blocking($socket, false); - new LanguageServer( - new ProtocolStreamReader($socket), - new ProtocolStreamWriter($socket), - $this, - ); - Loop::run(); - } elseif ($socket_server_mode && $address) { - // Run a TCP Server - $tcpServer = stream_socket_server('tcp://' . $address, $errno, $errstr); - if ($tcpServer === false) { - fwrite(STDERR, "Could not listen on $address. Error $errno\n$errstr"); - exit(1); - } - fwrite(STDOUT, "Server listening on $address\n"); - - $fork_available = true; - if (!extension_loaded('pcntl')) { - fwrite(STDERR, "PCNTL is not available. Only a single connection will be accepted\n"); - $fork_available = false; - } - - $disabled_functions = array_map('trim', explode(',', ini_get('disable_functions'))); - if (in_array('pcntl_fork', $disabled_functions)) { - fwrite( - STDERR, - "pcntl_fork() is disabled by php configuration (disable_functions directive)." - . " Only a single connection will be accepted\n", - ); - $fork_available = false; - } - - while ($socket = stream_socket_accept($tcpServer, -1)) { - fwrite(STDOUT, "Connection accepted\n"); - stream_set_blocking($socket, false); - if ($fork_available) { - // If PCNTL is available, fork a child process for the connection - // An exit notification will only terminate the child process - $pid = pcntl_fork(); - if ($pid === -1) { - fwrite(STDERR, "Could not fork\n"); - exit(1); - } - - if ($pid === 0) { - // Child process - $reader = new ProtocolStreamReader($socket); - $reader->on( - 'close', - static function (): void { - fwrite(STDOUT, "Connection closed\n"); - }, - ); - new LanguageServer( - $reader, - new ProtocolStreamWriter($socket), - $this, - ); - // Just for safety - exit(0); - } - } else { - // If PCNTL is not available, we only accept one connection. - // An exit notification will terminate the server - new LanguageServer( - new ProtocolStreamReader($socket), - new ProtocolStreamWriter($socket), - $this, - ); - Loop::run(); - } - } - } else { - // Use STDIO - stream_set_blocking(STDIN, false); - new LanguageServer( - new ProtocolStreamReader(STDIN), - new ProtocolStreamWriter(STDOUT), - $this, - ); - Loop::run(); - } } /** @psalm-mutation-free */ @@ -1244,6 +1139,11 @@ class ProjectAnalyzer return $this->file_provider->fileExists($file_path); } + public function isDirectory(string $file_path): bool + { + return $this->file_provider->isDirectory($file_path); + } + public function alterCodeAfterCompletion( bool $dry_run = false, bool $safe_types = false diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php index 7baed128..bb55c91f 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/ForeachAnalyzer.php @@ -42,6 +42,7 @@ use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; +use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TFalse; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TIterable; @@ -190,7 +191,7 @@ class ForeachAnalyzer && $type_location && isset($context->vars_in_scope[$var_comment->var_id]) && $context->vars_in_scope[$var_comment->var_id]->getId() === $comment_type->getId() - && !$comment_type->isMixed() + && !$comment_type->isMixed(true) ) { $project_analyzer = $statements_analyzer->getProjectAnalyzer(); @@ -266,10 +267,6 @@ class ForeachAnalyzer $foreach_context = clone $context; - foreach ($foreach_context->vars_in_scope as $context_var_id => $context_type) { - $foreach_context->vars_in_scope[$context_var_id] = $context_type; - } - if ($var_id && $foreach_context->hasVariable($var_id)) { // refine the type of the array variable we iterate over // if we entered loop body, the array cannot be empty @@ -750,6 +747,7 @@ class ForeachAnalyzer foreach ($iterator_atomic_types as $iterator_atomic_type) { if ($iterator_atomic_type instanceof TTemplateParam || $iterator_atomic_type instanceof TObjectWithProperties + || $iterator_atomic_type instanceof TCallableObject ) { throw new UnexpectedValueException('Shouldn’t get a generic param here'); } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php index 094b3fca..31fca6dc 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/IfElse/ElseAnalyzer.php @@ -169,7 +169,7 @@ class ElseAnalyzer $original_context, $new_assigned_var_ids, $new_possibly_assigned_var_ids, - [], + $if_scope->if_cond_changed_var_ids, true, ); diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php index cd6be47e..f1f5ad61 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Block/LoopAnalyzer.php @@ -108,11 +108,6 @@ class LoopAnalyzer if ($assignment_depth === 0 || $does_always_break) { $continue_context = clone $loop_context; - - foreach ($continue_context->vars_in_scope as $context_var_id => $context_type) { - $continue_context->vars_in_scope[$context_var_id] = $context_type; - } - $continue_context->loop_scope = $loop_scope; foreach ($pre_conditions as $condition_offset => $pre_condition) { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php index 0c0e30fc..fa1fb7f1 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayAnalyzer.php @@ -333,19 +333,32 @@ class ArrayAnalyzer } elseif ($key_type->isSingleIntLiteral()) { $item_key_value = $key_type->getSingleIntLiteral()->value; - if ($item_key_value >= $array_creation_info->int_offset) { - if ($item_key_value === $array_creation_info->int_offset) { + if ($item_key_value <= PHP_INT_MAX + && $item_key_value > $array_creation_info->int_offset + ) { + if ($item_key_value - 1 === $array_creation_info->int_offset) { $item_is_list_item = true; } - $array_creation_info->int_offset = $item_key_value + 1; + $array_creation_info->int_offset = $item_key_value; } } } else { $key_type = Type::getArrayKey(); } } else { + if ($array_creation_info->int_offset === PHP_INT_MAX) { + IssueBuffer::maybeAdd( + new InvalidArrayOffset( + 'Cannot add an item with an offset beyond PHP_INT_MAX', + new CodeLocation($statements_analyzer->getSource(), $item), + ), + ); + return; + } + $item_is_list_item = true; - $item_key_value = $array_creation_info->int_offset++; + $item_key_value = ++$array_creation_info->int_offset; + $key_atomic_type = new TLiteralInt($item_key_value); $array_creation_info->item_key_atomic_types[] = $key_atomic_type; $key_type = new Union([$key_atomic_type]); @@ -538,7 +551,17 @@ class ArrayAnalyzer $array_creation_info->item_key_atomic_types[] = Type::getAtomicStringFromLiteral($new_offset); $array_creation_info->all_list = false; } else { - $new_offset = $array_creation_info->int_offset++; + if ($array_creation_info->int_offset === PHP_INT_MAX) { + IssueBuffer::maybeAdd( + new InvalidArrayOffset( + 'Cannot add an item with an offset beyond PHP_INT_MAX', + new CodeLocation($statements_analyzer->getSource(), $item->value), + ), + $statements_analyzer->getSuppressedIssues(), + ); + continue 2; + } + $new_offset = ++$array_creation_info->int_offset; $array_creation_info->item_key_atomic_types[] = new TLiteralInt($new_offset); } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php index f122b73f..43161d22 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ArrayCreationInfo.php @@ -38,7 +38,12 @@ class ArrayCreationInfo */ public array $array_keys = []; - public int $int_offset = 0; + /** + * Holds the integer offset of the *last* element added + * + * -1 may mean no elements have been added yet, but can also mean there's an element with offset -1 + */ + public int $int_offset = -1; public bool $all_list = true; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php index 9435ea9e..127bab85 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Assignment/InstancePropertyAssignmentAnalyzer.php @@ -1087,7 +1087,7 @@ class InstancePropertyAssignmentAnalyzer * If we have an explicit list of all allowed magic properties on the class, and we're * not in that list, fall through */ - if (!$var_id || !$class_storage->sealed_properties) { + if (!$var_id || !$class_storage->hasSealedProperties($codebase->config)) { if (!$context->collect_initializations && !$context->collect_mutations) { self::taintProperty( $statements_analyzer, diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php index 95b2adf3..509acb41 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/AssignmentAnalyzer.php @@ -56,6 +56,7 @@ use Psalm\Issue\PossiblyUndefinedIntArrayOffset; use Psalm\Issue\ReferenceConstraintViolation; use Psalm\Issue\ReferenceReusedFromConfusingScope; use Psalm\Issue\UnnecessaryVarAnnotation; +use Psalm\Issue\UnsupportedPropertyReferenceUsage; use Psalm\IssueBuffer; use Psalm\Node\Expr\BinaryOp\VirtualBitwiseAnd; use Psalm\Node\Expr\BinaryOp\VirtualBitwiseOr; @@ -270,7 +271,7 @@ class AssignmentAnalyzer && $extended_var_id && (!$not_ignored_docblock_var_ids || isset($not_ignored_docblock_var_ids[$extended_var_id])) && $temp_assign_value_type->getId() === $comment_type->getId() - && !$comment_type->isMixed() + && !$comment_type->isMixed(true) ) { if ($codebase->alter_code && isset($statements_analyzer->getProjectAnalyzer()->getIssuesToFix()['UnnecessaryVarAnnotation']) @@ -980,10 +981,18 @@ class AssignmentAnalyzer $context->references_to_external_scope[$lhs_var_id] = true; } if (strpos($rhs_var_id, '->') !== false) { + IssueBuffer::maybeAdd(new UnsupportedPropertyReferenceUsage( + new CodeLocation($statements_analyzer->getSource(), $stmt), + )); // Reference to object property, we always consider object properties to be an external scope for references // TODO handle differently so it's detected as unused if the object is unused? $context->references_to_external_scope[$lhs_var_id] = true; } + if (strpos($rhs_var_id, '::') !== false) { + IssueBuffer::maybeAdd(new UnsupportedPropertyReferenceUsage( + new CodeLocation($statements_analyzer->getSource(), $stmt), + )); + } $lhs_location = new CodeLocation($statements_analyzer->getSource(), $stmt->var); if (!$stmt->var instanceof ArrayDimFetch && !$stmt->var instanceof PropertyFetch) { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php index d47b07ad..43da070b 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentAnalyzer.php @@ -71,7 +71,9 @@ use function reset; use function strpos; use function strtolower; use function substr; +use function substr_count; +use const DIRECTORY_SEPARATOR; use const PREG_SPLIT_NO_EMPTY; /** @@ -174,7 +176,9 @@ class ArgumentAnalyzer $prev_ord = $ord; } - if (count($values) < 12 || ($gt_count / count($values)) < 0.8) { + if (substr_count($arg_value_type->getSingleStringLiteral()->value, DIRECTORY_SEPARATOR) <= 2 + && (count($values) < 12 || ($gt_count / count($values)) < 0.8) + ) { IssueBuffer::maybeAdd( new InvalidLiteralArgument( 'Argument ' . ($argument_offset + 1) . ' of ' . $cased_method_id diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php index 33fbdbca..c9fdc523 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArgumentsAnalyzer.php @@ -20,7 +20,6 @@ use Psalm\Internal\Codebase\TaintFlowGraph; use Psalm\Internal\DataFlow\TaintSink; use Psalm\Internal\MethodIdentifier; use Psalm\Internal\Stubs\Generator\StubsGenerator; -use Psalm\Internal\Type\Comparator\CallableTypeComparator; use Psalm\Internal\Type\Comparator\UnionTypeComparator; use Psalm\Internal\Type\TemplateInferredTypeReplacer; use Psalm\Internal\Type\TemplateResult; @@ -197,18 +196,16 @@ class ArgumentsAnalyzer } $high_order_template_result = null; + $high_order_callable_info = $param + ? HighOrderFunctionArgHandler::getCallableArgInfo($context, $arg->value, $statements_analyzer, $param) + : null; - if (($arg->value instanceof PhpParser\Node\Expr\FuncCall - || $arg->value instanceof PhpParser\Node\Expr\MethodCall - || $arg->value instanceof PhpParser\Node\Expr\StaticCall) - && $param - && $function_storage = self::getHighOrderFuncStorage($context, $statements_analyzer, $arg->value) - ) { - $high_order_template_result = self::handleHighOrderFuncCallArg( + if ($param && $high_order_callable_info) { + $high_order_template_result = HighOrderFunctionArgHandler::remapLowerBounds( $statements_analyzer, $template_result ?? new TemplateResult([], []), - $function_storage, - $param, + $high_order_callable_info, + $param->type ?? Type::getMixed(), ); } elseif (($arg->value instanceof PhpParser\Node\Expr\Closure || $arg->value instanceof PhpParser\Node\Expr\ArrowFunction) @@ -228,7 +225,6 @@ class ArgumentsAnalyzer } $was_inside_call = $context->inside_call; - $context->inside_call = true; if (ExpressionAnalyzer::analyze( @@ -247,6 +243,16 @@ class ArgumentsAnalyzer $context->inside_call = $was_inside_call; + if ($high_order_callable_info && $high_order_template_result) { + HighOrderFunctionArgHandler::enhanceCallableArgType( + $context, + $arg->value, + $statements_analyzer, + $high_order_callable_info, + $high_order_template_result, + ); + } + if (($argument_offset === 0 && $method_id === 'array_filter' && count($args) === 2) || ($argument_offset > 0 && $method_id === 'array_map' && count($args) >= 2) ) { @@ -345,184 +351,6 @@ class ArgumentsAnalyzer } } - private static function getHighOrderFuncStorage( - Context $context, - StatementsAnalyzer $statements_analyzer, - PhpParser\Node\Expr\CallLike $function_like_call - ): ?FunctionLikeStorage { - $codebase = $statements_analyzer->getCodebase(); - - try { - if ($function_like_call instanceof PhpParser\Node\Expr\FuncCall && - !$function_like_call->isFirstClassCallable() - ) { - $function_id = strtolower((string) $function_like_call->name->getAttribute('resolvedName')); - - if (empty($function_id)) { - return null; - } - - if ($codebase->functions->dynamic_storage_provider->has($function_id)) { - return $codebase->functions->dynamic_storage_provider->getFunctionStorage( - $function_like_call, - $statements_analyzer, - $function_id, - $context, - new CodeLocation($statements_analyzer, $function_like_call), - ); - } - - return $codebase->functions->getStorage($statements_analyzer, $function_id); - } - - if ($function_like_call instanceof PhpParser\Node\Expr\MethodCall && - $function_like_call->var instanceof PhpParser\Node\Expr\Variable && - $function_like_call->name instanceof PhpParser\Node\Identifier && - is_string($function_like_call->var->name) && - isset($context->vars_in_scope['$' . $function_like_call->var->name]) - ) { - $lhs_type = $context->vars_in_scope['$' . $function_like_call->var->name]->getSingleAtomic(); - - if (!$lhs_type instanceof Type\Atomic\TNamedObject) { - return null; - } - - $method_id = new MethodIdentifier( - $lhs_type->value, - strtolower((string)$function_like_call->name), - ); - - return $codebase->methods->getStorage($method_id); - } - - if ($function_like_call instanceof PhpParser\Node\Expr\StaticCall && - $function_like_call->name instanceof PhpParser\Node\Identifier - ) { - $method_id = new MethodIdentifier( - (string)$function_like_call->class->getAttribute('resolvedName'), - strtolower($function_like_call->name->name), - ); - - return $codebase->methods->getStorage($method_id); - } - } catch (UnexpectedValueException $e) { - return null; - } - - return null; - } - - /** - * Compiles TemplateResult for high-order functions ($func_call) - * by previous template args ($inferred_template_result). - * - * It's need for proper template replacement: - * - * ``` - * * template T - * * return Closure(T): T - * function id(): Closure { ... } - * - * * template A - * * template B - * * - * * param list $_items - * * param callable(A): B $_ab - * * return list - * function map(array $items, callable $ab): array { ... } - * - * // list - * $numbers = [1, 2, 3]; - * - * $result = map($numbers, id()); - * // $result is list because template T of id() was inferred by previous arg. - * ``` - */ - private static function handleHighOrderFuncCallArg( - StatementsAnalyzer $statements_analyzer, - TemplateResult $inferred_template_result, - FunctionLikeStorage $storage, - FunctionLikeParameter $actual_func_param - ): ?TemplateResult { - $codebase = $statements_analyzer->getCodebase(); - - $input_hof_atomic = $storage->return_type && $storage->return_type->isSingle() - ? $storage->return_type->getSingleAtomic() - : null; - - // Try upcast invokable to callable type. - if ($input_hof_atomic instanceof Type\Atomic\TNamedObject && - $input_hof_atomic->value !== 'Closure' && - $codebase->classExists($input_hof_atomic->value) - ) { - $callable_from_invokable = CallableTypeComparator::getCallableFromAtomic( - $codebase, - $input_hof_atomic, - ); - - if ($callable_from_invokable) { - $invoke_id = new MethodIdentifier($input_hof_atomic->value, '__invoke'); - $declaring_invoke_id = $codebase->methods->getDeclaringMethodId($invoke_id); - - $storage = $codebase->methods->getStorage($declaring_invoke_id ?? $invoke_id); - $input_hof_atomic = $callable_from_invokable; - } - } - - if (!$input_hof_atomic instanceof TClosure && !$input_hof_atomic instanceof TCallable) { - return null; - } - - $container_hof_atomic = $actual_func_param->type && $actual_func_param->type->isSingle() - ? $actual_func_param->type->getSingleAtomic() - : null; - - if (!$container_hof_atomic instanceof TClosure && !$container_hof_atomic instanceof TCallable) { - return null; - } - - $replaced_container_hof_atomic = new Union([$container_hof_atomic]); - - // Replaces all input args in container function. - // - // For example: - // The map function expects callable(A):B as second param - // We know that previous arg type is list where the int is the A template. - // Then we can replace callable(A): B to callable(int):B using $inferred_template_result. - $replaced_container_hof_atomic = TemplateInferredTypeReplacer::replace( - $replaced_container_hof_atomic, - $inferred_template_result, - $codebase, - ); - - /** @var TClosure|TCallable $container_hof_atomic */ - $container_hof_atomic = $replaced_container_hof_atomic->getSingleAtomic(); - $high_order_template_result = new TemplateResult($storage->template_types ?: [], []); - - // We can replace each templated param for the input function. - // Example: - // map($numbers, id()); - // We know that map expects callable(int):B because the $numbers is list. - // We know that id() returns callable(T):T. - // Then we can replace templated params sequentially using the expected callable(int):B. - foreach ($input_hof_atomic->params ?? [] as $offset => $actual_func_param) { - if ($actual_func_param->type && - $actual_func_param->type->getTemplateTypes() && - isset($container_hof_atomic->params[$offset]) - ) { - TemplateStandinTypeReplacer::fillTemplateResult( - $actual_func_param->type, - $high_order_template_result, - $codebase, - null, - $container_hof_atomic->params[$offset]->type, - ); - } - } - - return $high_order_template_result; - } - /** * @param array $args */ @@ -930,7 +758,7 @@ class ArgumentsAnalyzer IssueBuffer::maybeAdd( new InvalidNamedArgument( 'Parameter $' . $key_type->value . ' does not exist on function ' - . ($cased_method_id ?: $method_id), + . ($cased_method_id ?: $method_id), new CodeLocation($statements_analyzer, $arg), (string)$method_id, ), @@ -970,7 +798,7 @@ class ArgumentsAnalyzer IssueBuffer::maybeAdd( new InvalidNamedArgument( 'Parameter $' . $arg->name->name . ' does not exist on function ' - . ($cased_method_id ?: $method_id), + . ($cased_method_id ?: $method_id), new CodeLocation($statements_analyzer, $arg->name), (string) $method_id, ), diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php index 8b55808b..c1aa8540 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/ArrayFunctionArgumentsAnalyzer.php @@ -14,6 +14,7 @@ use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Internal\Codebase\InternalCallMapHandler; use Psalm\Internal\MethodIdentifier; +use Psalm\Internal\Type\ArrayType; use Psalm\Internal\Type\Comparator\TypeComparisonResult; use Psalm\Internal\Type\Comparator\UnionTypeComparator; use Psalm\Internal\Type\TemplateResult; @@ -41,12 +42,14 @@ use Psalm\Type\Union; use UnexpectedValueException; use function array_filter; +use function array_merge; use function array_pop; use function array_shift; use function array_unshift; use function assert; use function count; use function explode; +use function is_numeric; use function strpos; use function strtolower; use function substr; @@ -346,6 +349,40 @@ class ArrayFunctionArgumentsAnalyzer return false; } + $array_type = null; + $array_size = null; + + if (($array_arg_type = $statements_analyzer->node_data->getType($array_arg)) + && $array_arg_type->hasArray() + ) { + /** + * @var TArray|TKeyedArray + */ + $array_type = $array_arg_type->getArray(); + if ($generic_array_type = ArrayType::infer($array_type)) { + $array_size = $generic_array_type->count; + } + + if ($array_type instanceof TKeyedArray) { + if ($array_type->is_list && isset($args[3])) { + $array_type = Type::getNonEmptyListAtomic($array_type->getGenericValueType()); + } else { + $array_type = $array_type->getGenericArrayType(); + } + } + + if ($array_type instanceof TArray + && $array_type->type_params[0]->hasInt() + && !$array_type->type_params[0]->hasString() + ) { + if ($array_type instanceof TNonEmptyArray && isset($args[3])) { + $array_type = Type::getNonEmptyListAtomic($array_type->type_params[1]); + } else { + $array_type = Type::getListAtomic($array_type->type_params[1]); + } + } + } + $offset_arg = $args[1]->value; if (ExpressionAnalyzer::analyze( @@ -356,7 +393,47 @@ class ArrayFunctionArgumentsAnalyzer return false; } + $offset_arg_is_zero = false; + + if (($offset_arg_type = $statements_analyzer->node_data->getType($offset_arg)) + && $offset_arg_type->hasLiteralValue() && $offset_arg_type->isSingleLiteral() + ) { + $offset_literal_value = $offset_arg_type->getSingleLiteral()->value; + $offset_arg_is_zero = is_numeric($offset_literal_value) && ((int) $offset_literal_value)===0; + } + if (!isset($args[2])) { + if ($offset_arg_is_zero) { + $array_type = Type::getEmptyArray(); + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + $array_type, + $array_type, + $context, + false, + ); + } elseif ($array_type) { + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + new Union([$array_type]), + new Union([$array_type]), + $context, + false, + ); + } else { + $default_array_type = Type::getArray(); + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + $default_array_type, + $default_array_type, + $context, + false, + ); + } + return null; } @@ -370,7 +447,69 @@ class ArrayFunctionArgumentsAnalyzer return false; } + $cover_whole_arr = false; + if ($offset_arg_is_zero && is_numeric($array_size)) { + if (($length_arg_type = $statements_analyzer->node_data->getType($length_arg)) + && $length_arg_type->hasLiteralValue() + ) { + $length_min = null; + if ($length_arg_type->isSingleLiteral()) { + $length_literal = $length_arg_type->getSingleLiteral(); + if ($length_literal->isNumericType()) { + $length_min = (int) $length_literal->value; + } + } else { + $literals = array_merge( + $length_arg_type->getLiteralStrings(), + $length_arg_type->getLiteralInts(), + $length_arg_type->getLiteralFloats(), + ); + foreach ($literals as $literal) { + if ($literal->isNumericType() + && ($literal_val = (int) $literal->value) + && ((isset($length_min) && $length_min> $literal_val) || !isset($length_min))) { + $length_min = $literal_val; + } + } + } + $cover_whole_arr = isset($length_min) && $length_min>= $array_size; + } elseif ($length_arg_type&& $length_arg_type->isNull()) { + $cover_whole_arr = true; + } + } + if (!isset($args[3])) { + if ($cover_whole_arr) { + $array_type = Type::getEmptyArray(); + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + $array_type, + $array_type, + $context, + false, + ); + } elseif ($array_type) { + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + new Union([$array_type]), + new Union([$array_type]), + $context, + false, + ); + } else { + $default_array_type = Type::getArray(); + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + $default_array_type, + $default_array_type, + $context, + false, + ); + } + return null; } @@ -400,40 +539,31 @@ class ArrayFunctionArgumentsAnalyzer $statements_analyzer->node_data->setType($replacement_arg, $replacement_arg_type); } - if (($array_arg_type = $statements_analyzer->node_data->getType($array_arg)) - && $array_arg_type->hasArray() + if ($array_type && $replacement_arg_type && $replacement_arg_type->hasArray() ) { - /** - * @var TArray|TKeyedArray - */ - $array_type = $array_arg_type->getArray(); - - if ($array_type instanceof TKeyedArray) { - if ($array_type->is_list) { - $array_type = Type::getNonEmptyListAtomic($array_type->getGenericValueType()); - } else { - $array_type = $array_type->getGenericArrayType(); - } - } - - if ($array_type instanceof TArray - && $array_type->type_params[0]->hasInt() - && !$array_type->type_params[0]->hasString() - ) { - if ($array_type instanceof TNonEmptyArray) { - $array_type = Type::getNonEmptyListAtomic($array_type->type_params[1]); - } else { - $array_type = Type::getListAtomic($array_type->type_params[1]); - } - } - /** * @var TArray|TKeyedArray */ $replacement_array_type = $replacement_arg_type->getArray(); + if (($replacement_array_type_generic = ArrayType::infer($replacement_array_type)) + && $replacement_array_type_generic->count === 0 + && $cover_whole_arr) { + $empty_array_type = Type::getEmptyArray(); + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + $empty_array_type, + $empty_array_type, + $context, + false, + ); + + return null; + } + if ($replacement_array_type instanceof TKeyedArray) { $was_list = $replacement_array_type->is_list; @@ -462,16 +592,26 @@ class ArrayFunctionArgumentsAnalyzer return null; } - $array_type = Type::getArray(); - - AssignmentAnalyzer::assignByRefParam( - $statements_analyzer, - $array_arg, - $array_type, - $array_type, - $context, - false, - ); + if ($array_type) { + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + new Union([$array_type]), + new Union([$array_type]), + $context, + false, + ); + } else { + $default_array_type = Type::getArray(); + AssignmentAnalyzer::assignByRefParam( + $statements_analyzer, + $array_arg, + $default_array_type, + $default_array_type, + $context, + false, + ); + } return null; } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php index 0ad6a6a8..7ecfff03 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallAnalyzer.php @@ -723,8 +723,17 @@ class FunctionCallAnalyzer extends CallAnalyzer ), $statements_analyzer->getSuppressedIssues(), ); - } elseif ($var_type_part instanceof TCallableObject - || $var_type_part instanceof TCallableString + } elseif ($var_type_part instanceof TCallableObject) { + $has_valid_function_call_type = true; + self::analyzeInvokeCall( + $statements_analyzer, + $stmt, + $real_stmt, + $function_name, + $context, + $var_type_part, + ); + } elseif ($var_type_part instanceof TCallableString || ($var_type_part instanceof TNamedObject && $var_type_part->value === 'Closure') || ($var_type_part instanceof TObjectWithProperties && isset($var_type_part->methods['__invoke'])) ) { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php index 123437eb..7941941c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/FunctionCallReturnTypeFetcher.php @@ -482,15 +482,25 @@ class FunctionCallReturnTypeFetcher return $call_map_return_type; case 'mb_strtolower': + $string_arg_type = $statements_analyzer->node_data->getType($call_args[0]->value); + if ($string_arg_type !== null && $string_arg_type->isNonEmptyString()) { + $returnType = Type::getNonEmptyLowercaseString(); + } else { + $returnType = Type::getLowercaseString(); + } if (count($call_args) < 2) { - return Type::getLowercaseString(); + return $returnType; } else { $second_arg_type = $statements_analyzer->node_data->getType($call_args[1]->value); if ($second_arg_type && $second_arg_type->isNull()) { - return Type::getLowercaseString(); + return $returnType; } } - return Type::getString(); + if ($string_arg_type !== null && $string_arg_type->isNonEmptyString()) { + return Type::getNonEmptyString(); + } else { + return Type::getString(); + } } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgHandler.php new file mode 100644 index 00000000..3d1a51c4 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgHandler.php @@ -0,0 +1,325 @@ + $_items + * * param callable(A): B $_ab + * * return list + * function map(array $items, callable $ab): array { ... } + * + * // list + * $numbers = [1, 2, 3]; + * + * $result = map($numbers, id()); + * // $result is list because template T of id() was inferred by previous arg. + * ``` + */ + public static function remapLowerBounds( + StatementsAnalyzer $statements_analyzer, + TemplateResult $inferred_template_result, + HighOrderFunctionArgInfo $input_function, + Union $container_function_type + ): TemplateResult { + // Try to infer container callable by $inferred_template_result + $container_type = TemplateInferredTypeReplacer::replace( + $container_function_type, + $inferred_template_result, + $statements_analyzer->getCodebase(), + ); + + $input_function_type = $input_function->getFunctionType(); + $input_function_template_result = $input_function->getTemplates(); + + // Traverse side by side 'container' params and 'input' params. + // This maps 'input' templates to 'container' templates. + // + // Example: + // 'input' => Closure(C:Bar, D:Bar): array{C:Bar, D:Bar} + // 'container' => Closure(int, string): array{int, string} + // + // $remapped_lower_bounds will be: [ + // 'C' => ['Bar' => [int]], + // 'D' => ['Bar' => [string]] + // ]. + foreach ($input_function_type->getAtomicTypes() as $input_atomic) { + if (!$input_atomic instanceof TClosure && !$input_atomic instanceof TCallable) { + continue; + } + + foreach ($container_type->getAtomicTypes() as $container_atomic) { + if (!$container_atomic instanceof TClosure && !$container_atomic instanceof TCallable) { + continue; + } + + foreach ($input_atomic->params ?? [] as $offset => $input_param) { + if (!isset($container_atomic->params[$offset])) { + continue; + } + + TemplateStandinTypeReplacer::fillTemplateResult( + $input_param->type ?? Type::getMixed(), + $input_function_template_result, + $statements_analyzer->getCodebase(), + $statements_analyzer, + $container_atomic->params[$offset]->type, + ); + } + } + } + + return $input_function_template_result; + } + + public static function enhanceCallableArgType( + Context $context, + PhpParser\Node\Expr $arg_expr, + StatementsAnalyzer $statements_analyzer, + HighOrderFunctionArgInfo $high_order_callable_info, + TemplateResult $high_order_template_result + ): void { + // Psalm can infer simple callable/closure. + // But can't infer first-class-callable or high-order function. + if ($high_order_callable_info->getType() === HighOrderFunctionArgInfo::TYPE_CALLABLE) { + return; + } + + $fully_inferred_callable_type = TemplateInferredTypeReplacer::replace( + $high_order_callable_info->getFunctionType(), + $high_order_template_result, + $statements_analyzer->getCodebase(), + ); + + // Some templates may not have been replaced. + // They expansion makes error message better. + $expanded = TypeExpander::expandUnion( + $statements_analyzer->getCodebase(), + $fully_inferred_callable_type, + $context->self, + $context->self, + $context->parent, + true, + true, + false, + false, + true, + ); + + $statements_analyzer->node_data->setType($arg_expr, $expanded); + } + + public static function getCallableArgInfo( + Context $context, + PhpParser\Node\Expr $input_arg_expr, + StatementsAnalyzer $statements_analyzer, + FunctionLikeParameter $container_param + ): ?HighOrderFunctionArgInfo { + if (!self::isSupported($container_param)) { + return null; + } + + $codebase = $statements_analyzer->getCodebase(); + + try { + if ($input_arg_expr instanceof PhpParser\Node\Expr\FuncCall) { + $function_id = strtolower((string) $input_arg_expr->name->getAttribute('resolvedName')); + + if (empty($function_id)) { + return null; + } + + $dynamic_storage = !$input_arg_expr->isFirstClassCallable() + ? $codebase->functions->dynamic_storage_provider->getFunctionStorage( + $input_arg_expr, + $statements_analyzer, + $function_id, + $context, + new CodeLocation($statements_analyzer, $input_arg_expr), + ) + : null; + + return new HighOrderFunctionArgInfo( + $input_arg_expr->isFirstClassCallable() + ? HighOrderFunctionArgInfo::TYPE_FIRST_CLASS_CALLABLE + : HighOrderFunctionArgInfo::TYPE_CALLABLE, + $dynamic_storage ?? $codebase->functions->getStorage($statements_analyzer, $function_id), + ); + } + + if ($input_arg_expr instanceof PhpParser\Node\Expr\MethodCall && + $input_arg_expr->var instanceof PhpParser\Node\Expr\Variable && + $input_arg_expr->name instanceof PhpParser\Node\Identifier && + is_string($input_arg_expr->var->name) && + isset($context->vars_in_scope['$' . $input_arg_expr->var->name]) + ) { + $lhs_type = $context->vars_in_scope['$' . $input_arg_expr->var->name]->getSingleAtomic(); + + if (!$lhs_type instanceof Type\Atomic\TNamedObject) { + return null; + } + + $method_id = new MethodIdentifier( + $lhs_type->value, + strtolower((string)$input_arg_expr->name), + ); + + return new HighOrderFunctionArgInfo( + $input_arg_expr->isFirstClassCallable() + ? HighOrderFunctionArgInfo::TYPE_FIRST_CLASS_CALLABLE + : HighOrderFunctionArgInfo::TYPE_CALLABLE, + $codebase->methods->getStorage($method_id), + ); + } + + if ($input_arg_expr instanceof PhpParser\Node\Expr\StaticCall && + $input_arg_expr->name instanceof PhpParser\Node\Identifier + ) { + $method_id = new MethodIdentifier( + (string)$input_arg_expr->class->getAttribute('resolvedName'), + strtolower($input_arg_expr->name->toString()), + ); + + return new HighOrderFunctionArgInfo( + $input_arg_expr->isFirstClassCallable() + ? HighOrderFunctionArgInfo::TYPE_FIRST_CLASS_CALLABLE + : HighOrderFunctionArgInfo::TYPE_CALLABLE, + $codebase->methods->getStorage($method_id), + ); + } + + if ($input_arg_expr instanceof PhpParser\Node\Scalar\String_) { + return self::fromLiteralString(Type::getString($input_arg_expr->value), $statements_analyzer); + } + + if ($input_arg_expr instanceof PhpParser\Node\Expr\ConstFetch) { + $constant = $context->constants[$input_arg_expr->name->toString()] ?? null; + + return null !== $constant + ? self::fromLiteralString($constant, $statements_analyzer) + : null; + } + + if ($input_arg_expr instanceof PhpParser\Node\Expr\ClassConstFetch && + $input_arg_expr->name instanceof PhpParser\Node\Identifier + ) { + $storage = $codebase->classlikes + ->getStorageFor((string)$input_arg_expr->class->getAttribute('resolvedName')); + + $constant = null !== $storage + ? $storage->constants[$input_arg_expr->name->toString()] ?? null + : null; + + return null !== $constant && null !== $constant->type + ? self::fromLiteralString($constant->type, $statements_analyzer) + : null; + } + + if ($input_arg_expr instanceof PhpParser\Node\Expr\New_ && + $input_arg_expr->class instanceof PhpParser\Node\Name + ) { + $class_storage = $codebase->classlikes + ->getStorageFor((string) $input_arg_expr->class->getAttribute('resolvedName')); + + $invoke_storage = $class_storage && isset($class_storage->methods['__invoke']) + ? $class_storage->methods['__invoke'] + : null; + + if (!$invoke_storage) { + return null; + } + + return new HighOrderFunctionArgInfo( + HighOrderFunctionArgInfo::TYPE_CLASS_CALLABLE, + $invoke_storage, + $class_storage, + ); + } + } catch (UnexpectedValueException $e) { + return null; + } + + return null; + } + + private static function isSupported(FunctionLikeParameter $container_param): bool + { + if (!$container_param->type || !$container_param->type->hasCallableType()) { + return false; + } + + foreach ($container_param->type->getAtomicTypes() as $a) { + if (($a instanceof TClosure || $a instanceof TCallable) && !$a->params) { + return false; + } + + if ($a instanceof Type\Atomic\TCallableArray || + $a instanceof Type\Atomic\TCallableString || + $a instanceof Type\Atomic\TCallableKeyedArray + ) { + return false; + } + } + + return true; + } + + private static function fromLiteralString( + Union $constant, + StatementsAnalyzer $statements_analyzer + ): ?HighOrderFunctionArgInfo { + $literal = $constant->isSingle() ? $constant->getSingleAtomic() : null; + + if (!$literal instanceof Type\Atomic\TLiteralString || empty($literal->value)) { + return null; + } + + $codebase = $statements_analyzer->getCodebase(); + + return new HighOrderFunctionArgInfo( + HighOrderFunctionArgInfo::TYPE_STRING_CALLABLE, + strpos($literal->value, '::') !== false + ? $codebase->methods->getStorage(MethodIdentifier::wrap($literal->value)) + : $codebase->functions->getStorage($statements_analyzer, strtolower($literal->value)), + ); + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgInfo.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgInfo.php new file mode 100644 index 00000000..526e6ee1 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/HighOrderFunctionArgInfo.php @@ -0,0 +1,90 @@ +type = $type; + $this->function_storage = $function_storage; + $this->class_storage = $class_storage; + } + + public function getTemplates(): TemplateResult + { + $templates = $this->class_storage + ? array_merge( + $this->function_storage->template_types ?? [], + $this->class_storage->template_types ?? [], + ) + : $this->function_storage->template_types ?? []; + + return new TemplateResult($templates, []); + } + + public function getType(): string + { + return $this->type; + } + + public function getFunctionType(): Union + { + switch ($this->type) { + case self::TYPE_FIRST_CLASS_CALLABLE: + return new Union([ + new TClosure( + 'Closure', + $this->function_storage->params, + $this->function_storage->return_type, + $this->function_storage->pure, + ), + ]); + + case self::TYPE_STRING_CALLABLE: + case self::TYPE_CLASS_CALLABLE: + return new Union([ + new TCallable( + 'callable', + $this->function_storage->params, + $this->function_storage->return_type, + $this->function_storage->pure, + ), + ]); + + default: + return $this->function_storage->return_type ?? Type::getMixed(); + } + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php index 74109d03..4e6d3188 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/AtomicMethodCallAnalyzer.php @@ -27,6 +27,8 @@ use Psalm\StatementsSource; use Psalm\Storage\ClassLikeStorage; use Psalm\Type; use Psalm\Type\Atomic; +use Psalm\Type\Atomic\TCallable; +use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TFalse; @@ -104,6 +106,18 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer $source = $statements_analyzer->getSource(); + if ($lhs_type_part instanceof TCallableObject) { + self::handleCallableObject( + $statements_analyzer, + $stmt, + $context, + $lhs_type_part->callable, + $result, + $inferred_template_result, + ); + return; + } + if (!$lhs_type_part instanceof TNamedObject) { self::handleInvalidClass( $statements_analyzer, @@ -891,4 +905,55 @@ class AtomicMethodCallAnalyzer extends CallAnalyzer $fq_class_name, ]; } + + private static function handleCallableObject( + StatementsAnalyzer $statements_analyzer, + PhpParser\Node\Expr\MethodCall $stmt, + Context $context, + ?TCallable $lhs_type_part_callable, + AtomicMethodCallAnalysisResult $result, + ?TemplateResult $inferred_template_result = null + ): void { + $method_id = 'object::__invoke'; + $result->existent_method_ids[] = $method_id; + $result->has_valid_method_call_type = true; + + if ($lhs_type_part_callable !== null) { + $result->return_type = $lhs_type_part_callable->return_type ?? Type::getMixed(); + $callableArgumentCount = count($lhs_type_part_callable->params ?? []); + $providedArgumentsCount = count($stmt->getArgs()); + + if ($callableArgumentCount > $providedArgumentsCount) { + $result->too_few_arguments = true; + $result->too_few_arguments_method_ids[] = new MethodIdentifier('callable-object', '__invoke'); + } elseif ($providedArgumentsCount > $callableArgumentCount) { + $result->too_many_arguments = true; + $result->too_many_arguments_method_ids[] = new MethodIdentifier('callable-object', '__invoke'); + } + + $template_result = $inferred_template_result ?? new TemplateResult([], []); + + ArgumentsAnalyzer::analyze( + $statements_analyzer, + $stmt->getArgs(), + $lhs_type_part_callable->params, + $method_id, + false, + $context, + $template_result, + ); + + ArgumentsAnalyzer::checkArgumentsMatch( + $statements_analyzer, + $stmt->getArgs(), + $method_id, + $lhs_type_part_callable->params ?? [], + null, + null, + $template_result, + new CodeLocation($statements_analyzer->getSource(), $stmt), + $context, + ); + } + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php index c3f4717e..980043f1 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/ExistingAtomicMethodCallAnalyzer.php @@ -42,10 +42,13 @@ use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Union; use UnexpectedValueException; +use function array_filter; use function array_map; use function count; use function explode; use function in_array; +use function is_string; +use function strpos; use function strtolower; /** @@ -225,6 +228,9 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer if ($inferred_template_result) { $template_result->lower_bounds += $inferred_template_result->lower_bounds; } + if ($method_storage && $method_storage->template_types) { + $template_result->template_types += $method_storage->template_types; + } if ($codebase->store_node_types && !$stmt->isFirstClassCallable() @@ -422,30 +428,48 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer } if ($method_storage->if_true_assertions) { + $possibilities = array_map( + static fn(Possibilities $assertion): Possibilities => $assertion->getUntemplatedCopy( + $template_result, + $lhs_var_id, + $codebase, + ), + $method_storage->if_true_assertions, + ); + if ($lhs_var_id === null) { + $possibilities = array_filter( + $possibilities, + static fn(Possibilities $assertion): bool => !(is_string($assertion->var_id) + && strpos($assertion->var_id, '$this->') === 0 + ) + ); + } $statements_analyzer->node_data->setIfTrueAssertions( $stmt, - array_map( - static fn(Possibilities $assertion): Possibilities => $assertion->getUntemplatedCopy( - $template_result, - $lhs_var_id, - $codebase, - ), - $method_storage->if_true_assertions, - ), + $possibilities, ); } if ($method_storage->if_false_assertions) { + $possibilities = array_map( + static fn(Possibilities $assertion): Possibilities => $assertion->getUntemplatedCopy( + $template_result, + $lhs_var_id, + $codebase, + ), + $method_storage->if_false_assertions, + ); + if ($lhs_var_id === null) { + $possibilities = array_filter( + $possibilities, + static fn(Possibilities $assertion): bool => !(is_string($assertion->var_id) + && strpos($assertion->var_id, '$this->') === 0 + ) + ); + } $statements_analyzer->node_data->setIfFalseAssertions( $stmt, - array_map( - static fn(Possibilities $assertion): Possibilities => $assertion->getUntemplatedCopy( - $template_result, - $lhs_var_id, - $codebase, - ), - $method_storage->if_false_assertions, - ), + $possibilities, ); } } @@ -546,7 +570,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer case '__set': // If `@psalm-seal-properties` is set, the property must be defined with // a `@property` annotation - if (($class_storage->sealed_properties || $codebase->config->seal_all_properties) + if (($class_storage->hasSealedProperties($codebase->config)) && !isset($class_storage->pseudo_property_set_types['$' . $prop_name]) ) { IssueBuffer::maybeAdd( @@ -644,7 +668,7 @@ class ExistingAtomicMethodCallAnalyzer extends CallAnalyzer case '__get': // If `@psalm-seal-properties` is set, the property must be defined with // a `@property` annotation - if (($class_storage->sealed_properties || $codebase->config->seal_all_properties) + if (($class_storage->hasSealedProperties($codebase->config)) && !isset($class_storage->pseudo_property_get_types['$' . $prop_name]) ) { IssueBuffer::maybeAdd( diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php index 2f16c392..8e9346d8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/Method/MissingMethodCallHandler.php @@ -190,7 +190,7 @@ class MissingMethodCallHandler $context, ); - if ($class_storage->sealed_methods || $config->seal_all_methods) { + if ($class_storage->hasSealedMethods($config)) { $result->non_existent_magic_method_ids[] = $method_id->__toString(); return null; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php index 0087a585..b521f8bc 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/NewAnalyzer.php @@ -52,12 +52,11 @@ use Psalm\Type\Atomic\TObject; use Psalm\Type\Atomic\TString; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Atomic\TTemplateParamClass; +use Psalm\Type\Atomic\TUnknownClassString; use Psalm\Type\TaintKind; use Psalm\Type\Union; use function array_map; -use function array_merge; -use function array_shift; use function array_values; use function implode; use function in_array; @@ -74,7 +73,8 @@ class NewAnalyzer extends CallAnalyzer public static function analyze( StatementsAnalyzer $statements_analyzer, PhpParser\Node\Expr\New_ $stmt, - Context $context + Context $context, + TemplateResult $template_result = null ): bool { $fq_class_name = null; @@ -256,6 +256,7 @@ class NewAnalyzer extends CallAnalyzer $fq_class_name, $from_static, $can_extend, + $template_result, ); } else { ArgumentsAnalyzer::analyze( @@ -291,7 +292,8 @@ class NewAnalyzer extends CallAnalyzer Context $context, string $fq_class_name, bool $from_static, - bool $can_extend + bool $can_extend, + TemplateResult $template_result = null ): void { $storage = $codebase->classlike_storage_provider->get($fq_class_name); @@ -392,7 +394,7 @@ class NewAnalyzer extends CallAnalyzer ); } - $template_result = new TemplateResult([], []); + $template_result ??= new TemplateResult([], []); if (self::checkMethodArgs( $method_id, @@ -699,15 +701,58 @@ class NewAnalyzer extends CallAnalyzer } } - $new_type = null; + $new_type = self::getNewType( + $statements_analyzer, + $codebase, + $context, + $stmt, + $stmt_class_type, + $config, + $can_extend, + ); - $stmt_class_types = $stmt_class_type->getAtomicTypes(); + if (!$has_single_class) { + if ($new_type) { + $statements_analyzer->node_data->setType($stmt, $new_type); + } - while ($stmt_class_types) { - $lhs_type_part = array_shift($stmt_class_types); + ArgumentsAnalyzer::analyze( + $statements_analyzer, + $stmt->getArgs(), + null, + null, + true, + $context, + ); + return; + } + } + private static function getNewType( + StatementsAnalyzer $statements_analyzer, + Codebase $codebase, + Context $context, + PhpParser\Node\Expr\New_ $stmt, + Union $stmt_class_type, + Config $config, + bool &$can_extend + ): ?Union { + $new_types = []; + + foreach ($stmt_class_type->getAtomicTypes() as $lhs_type_part) { if ($lhs_type_part instanceof TTemplateParam) { - $stmt_class_types = array_merge($stmt_class_types, $lhs_type_part->as->getAtomicTypes()); + $as = self::getNewType( + $statements_analyzer, + $codebase, + $context, + $stmt, + $lhs_type_part->as, + $config, + $can_extend, + ); + if ($as) { + $new_types []= new Union([$lhs_type_part->replaceAs($as)]); + } continue; } @@ -731,7 +776,7 @@ class NewAnalyzer extends CallAnalyzer ); } - $new_type = Type::combineUnionTypes($new_type, new Union([$new_type_part])); + $new_types []= new Union([$new_type_part]); if ($lhs_type_part->as_type && $codebase->classlikes->classExists($lhs_type_part->as_type->value) @@ -777,9 +822,10 @@ class NewAnalyzer extends CallAnalyzer ) { if (!$statements_analyzer->node_data->getType($stmt)) { if ($lhs_type_part instanceof TClassString) { - $generated_type = $lhs_type_part->as_type - ? $lhs_type_part->as_type - : new TObject(); + $generated_type = $lhs_type_part->as_type ?? new TObject(); + if ($lhs_type_part instanceof TUnknownClassString) { + $generated_type = $lhs_type_part->as_unknown_type ?? $generated_type; + } if ($lhs_type_part->as_type && $codebase->classlikes->classExists($lhs_type_part->as_type->value) @@ -834,7 +880,7 @@ class NewAnalyzer extends CallAnalyzer ); } - $new_type = Type::combineUnionTypes($new_type, new Union([$generated_type])); + $new_types []= new Union([$generated_type]); } continue; @@ -871,7 +917,7 @@ class NewAnalyzer extends CallAnalyzer ) { // do nothing } elseif ($lhs_type_part instanceof TNamedObject) { - $new_type = Type::combineUnionTypes($new_type, new Union([$lhs_type_part])); + $new_types []= new Union([$lhs_type_part]); continue; } else { IssueBuffer::maybeAdd( @@ -884,24 +930,12 @@ class NewAnalyzer extends CallAnalyzer ); } - $new_type = Type::combineUnionTypes($new_type, Type::getObject()); + $new_types []= Type::getObject(); } - if (!$has_single_class) { - if ($new_type) { - $statements_analyzer->node_data->setType($stmt, $new_type); - } - - ArgumentsAnalyzer::analyze( - $statements_analyzer, - $stmt->getArgs(), - null, - null, - true, - $context, - ); - - return; + if ($new_types) { + return Type::combineUnionTypeArray($new_types, $codebase); } + return null; } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php index ccad64bb..0a604e08 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Call/StaticMethod/ExistingAtomicStaticCallAnalyzer.php @@ -3,6 +3,7 @@ namespace Psalm\Internal\Analyzer\Statements\Expression\Call\StaticMethod; use PhpParser; +use PhpParser\Node\Expr\StaticCall; use Psalm\CodeLocation; use Psalm\Codebase; use Psalm\Config; @@ -465,7 +466,7 @@ class ExistingAtomicStaticCallAnalyzer private static function getMethodReturnType( StatementsAnalyzer $statements_analyzer, Codebase $codebase, - PhpParser\Node\Expr\StaticCall $stmt, + StaticCall $stmt, MethodIdentifier $method_id, array $args, TemplateResult $template_result, @@ -493,40 +494,14 @@ class ExistingAtomicStaticCallAnalyzer [$template_type->param_name] [$template_type->defining_class], )) { - if ($template_type->param_name === 'TFunctionArgCount') { - $template_result->lower_bounds[$template_type->param_name] = [ - 'fn-' . strtolower((string)$method_id) => [ - new TemplateBound( - Type::getInt(false, count($stmt->getArgs())), - ), - ], - ]; - } elseif ($template_type->param_name === 'TPhpMajorVersion') { - $template_result->lower_bounds[$template_type->param_name] = [ - 'fn-' . strtolower((string)$method_id) => [ - new TemplateBound( - Type::getInt(false, $codebase->getMajorAnalysisPhpVersion()), - ), - ], - ]; - } elseif ($template_type->param_name === 'TPhpVersionId') { - $template_result->lower_bounds[$template_type->param_name] = [ - 'fn-' . strtolower((string) $method_id) => [ - new TemplateBound( - Type::getInt( - false, - $codebase->analysis_php_version_id, - ), - ), - ], - ]; - } else { - $template_result->lower_bounds[$template_type->param_name] = [ - ($template_type->defining_class) => [ - new TemplateBound(Type::getNever()), - ], - ]; - } + $template_result->lower_bounds[$template_type->param_name] + = self::resolveTemplateResultLowerBound( + $codebase, + $stmt, + $class_storage, + $method_id, + $template_type, + ); } } } @@ -632,4 +607,68 @@ class ExistingAtomicStaticCallAnalyzer $visitor->traverse($type); return $visitor->matches(); } + + /** + * @return non-empty-array> + */ + private static function resolveTemplateResultLowerBound( + Codebase $codebase, + StaticCall $stmt, + ClassLikeStorage $class_storage, + MethodIdentifier $method_id, + TTemplateParam $template_type + ): array { + if ($template_type->param_name === 'TFunctionArgCount') { + return [ + 'fn-' . strtolower((string)$method_id) => [ + new TemplateBound( + Type::getInt(false, count($stmt->getArgs())), + ), + ], + ]; + } + + if ($template_type->param_name === 'TPhpMajorVersion') { + return [ + 'fn-' . strtolower((string)$method_id) => [ + new TemplateBound( + Type::getInt(false, $codebase->getMajorAnalysisPhpVersion()), + ), + ], + ]; + } + + if ($template_type->param_name === 'TPhpVersionId') { + return [ + 'fn-' . strtolower((string) $method_id) => [ + new TemplateBound( + Type::getInt( + false, + $codebase->analysis_php_version_id, + ), + ), + ], + ]; + } + + if (isset( + $class_storage->template_extended_params[$template_type->defining_class][$template_type->param_name], + )) { + $extended_param_type = $class_storage->template_extended_params[ + $template_type->defining_class + ][$template_type->param_name]; + + return [ + ($template_type->defining_class) => [ + new TemplateBound($extended_param_type), + ], + ]; + } + + return [ + ($template_type->defining_class) => [ + new TemplateBound(Type::getNever()), + ], + ]; + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php index 5d072adb..348f528c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/CallAnalyzer.php @@ -42,6 +42,7 @@ use Psalm\Storage\Assertion\Truthy; use Psalm\Storage\ClassLikeStorage; use Psalm\Storage\Possibilities; use Psalm\Type; +use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TObjectWithProperties; use Psalm\Type\Atomic\TTemplateParam; @@ -579,6 +580,7 @@ class CallAnalyzer foreach ($type_part->extra_types as $extra_type) { if ($extra_type instanceof TTemplateParam || $extra_type instanceof TObjectWithProperties + || $extra_type instanceof TCallableObject ) { throw new UnexpectedValueException('Shouldn’t get a generic param here'); } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php index d271ce94..0af910bd 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/ClassConstAnalyzer.php @@ -844,6 +844,7 @@ class ClassConstAnalyzer assert($parent_classlike_storage !== null); if (!isset($parent_classlike_storage->parent_interfaces[strtolower($interface)]) && !isset($interface_storage->parent_interfaces[strtolower($parent_classlike_storage->name)]) + && $interface_const_storage !== $parent_const_storage ) { IssueBuffer::maybeAdd( new AmbiguousConstantInheritance( diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php index e02ad2c6..34160e58 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/EvalAnalyzer.php @@ -26,7 +26,10 @@ class EvalAnalyzer PhpParser\Node\Expr\Eval_ $stmt, Context $context ): void { + $was_inside_call = $context->inside_call; + $context->inside_call = true; ExpressionAnalyzer::analyze($statements_analyzer, $stmt->expr, $context); + $context->inside_call = $was_inside_call; $codebase = $statements_analyzer->getCodebase(); diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php index 585d7ca1..66367938 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/Fetch/AtomicPropertyFetchAnalyzer.php @@ -115,17 +115,31 @@ class AtomicPropertyFetchAnalyzer return; } - $has_valid_fetch_type = true; + if ($lhs_type_part instanceof TObjectWithProperties) { + if (!isset($lhs_type_part->properties[$prop_name])) { + return; + } + + $has_valid_fetch_type = true; - if ($lhs_type_part instanceof TObjectWithProperties - && isset($lhs_type_part->properties[$prop_name]) - ) { $stmt_type = $statements_analyzer->node_data->getType($stmt); $statements_analyzer->node_data->setType( $stmt, Type::combineUnionTypes( - $lhs_type_part->properties[$prop_name], + TypeExpander::expandUnion( + $statements_analyzer->getCodebase(), + $lhs_type_part->properties[$prop_name], + null, + null, + null, + true, + true, + false, + true, + false, + true, + ), $stmt_type, ), ); @@ -133,12 +147,22 @@ class AtomicPropertyFetchAnalyzer return; } + $intersection_types = []; + if (!$lhs_type_part instanceof TObject) { + $intersection_types = $lhs_type_part->getIntersectionTypes(); + } + // stdClass and SimpleXMLElement are special cases where we cannot infer the return types // but we don't want to throw an error // Hack has a similar issue: https://github.com/facebook/hhvm/issues/5164 if ($lhs_type_part instanceof TObject - || in_array(strtolower($lhs_type_part->value), Config::getInstance()->getUniversalObjectCrates(), true) + || ( + in_array(strtolower($lhs_type_part->value), Config::getInstance()->getUniversalObjectCrates(), true) + && $intersection_types === [] + ) ) { + $has_valid_fetch_type = true; + $statements_analyzer->node_data->setType($stmt, Type::getMixed()); return; @@ -149,8 +173,6 @@ class AtomicPropertyFetchAnalyzer return; } - $intersection_types = $lhs_type_part->getIntersectionTypes() ?: []; - $fq_class_name = $lhs_type_part->value; $override_property_visibility = false; @@ -193,6 +215,7 @@ class AtomicPropertyFetchAnalyzer if ($class_storage->is_enum || in_array('UnitEnum', $codebase->getParentInterfaces($fq_class_name))) { if ($prop_name === 'value' && !$class_storage->is_enum) { + $has_valid_fetch_type = true; $statements_analyzer->node_data->setType( $stmt, new Union([ @@ -201,8 +224,10 @@ class AtomicPropertyFetchAnalyzer ]), ); } elseif ($prop_name === 'value' && $class_storage->enum_type !== null && $class_storage->enum_cases) { + $has_valid_fetch_type = true; self::handleEnumValue($statements_analyzer, $stmt, $stmt_var_type, $class_storage); } elseif ($prop_name === 'name') { + $has_valid_fetch_type = true; self::handleEnumName($statements_analyzer, $stmt, $lhs_type_part); } else { self::handleNonExistentProperty( @@ -220,6 +245,7 @@ class AtomicPropertyFetchAnalyzer $stmt_var_id, $has_magic_getter, $var_id, + $has_valid_fetch_type, ); } @@ -237,39 +263,60 @@ class AtomicPropertyFetchAnalyzer // add method before changing fq_class_name $get_method_id = new MethodIdentifier($fq_class_name, '__get'); - if (!$naive_property_exists - && $class_storage->namedMixins - ) { - foreach ($class_storage->namedMixins as $mixin) { - $new_property_id = $mixin->value . '::$' . $prop_name; + if (!$naive_property_exists) { + if ($class_storage->namedMixins) { + foreach ($class_storage->namedMixins as $mixin) { + $new_property_id = $mixin->value . '::$' . $prop_name; - try { - $new_class_storage = $codebase->classlike_storage_provider->get($mixin->value); - } catch (InvalidArgumentException $e) { - $new_class_storage = null; - } - - if ($new_class_storage - && ($codebase->properties->propertyExists( - $new_property_id, - !$in_assignment, - $statements_analyzer, - $context, - $codebase->collect_locations - ? new CodeLocation($statements_analyzer->getSource(), $stmt) - : null, - ) - || isset($new_class_storage->pseudo_property_get_types['$' . $prop_name])) - ) { - $fq_class_name = $mixin->value; - $lhs_type_part = $mixin; - $class_storage = $new_class_storage; - - if (!isset($new_class_storage->pseudo_property_get_types['$' . $prop_name])) { - $naive_property_exists = true; + try { + $new_class_storage = $codebase->classlike_storage_provider->get($mixin->value); + } catch (InvalidArgumentException $e) { + $new_class_storage = null; } - $property_id = $new_property_id; + if ($new_class_storage + && ($codebase->properties->propertyExists( + $new_property_id, + !$in_assignment, + $statements_analyzer, + $context, + $codebase->collect_locations + ? new CodeLocation($statements_analyzer->getSource(), $stmt) + : null, + ) + || isset($new_class_storage->pseudo_property_get_types['$' . $prop_name])) + ) { + $fq_class_name = $mixin->value; + $lhs_type_part = $mixin; + $class_storage = $new_class_storage; + + if (!isset($new_class_storage->pseudo_property_get_types['$' . $prop_name])) { + $naive_property_exists = true; + } + + $property_id = $new_property_id; + } + } + } elseif ($intersection_types !== [] && !$class_storage->final) { + foreach ($intersection_types as $intersection_type) { + self::analyze( + $statements_analyzer, + $stmt, + $context, + $in_assignment, + $var_id, + $stmt_var_id, + $stmt_var_type, + $intersection_type, + $prop_name, + $has_valid_fetch_type, + $invalid_fetch_types, + $is_static_access, + ); + + if ($has_valid_fetch_type) { + return; + } } } } @@ -350,6 +397,7 @@ class AtomicPropertyFetchAnalyzer $stmt_var_id, $has_magic_getter, $var_id, + $has_valid_fetch_type, ); return; @@ -485,6 +533,8 @@ class AtomicPropertyFetchAnalyzer } $stmt_type = $statements_analyzer->node_data->getType($stmt); + + $has_valid_fetch_type = true; $statements_analyzer->node_data->setType( $stmt, Type::combineUnionTypes($class_property_type, $stmt_type), @@ -663,7 +713,7 @@ class AtomicPropertyFetchAnalyzer * If we have an explicit list of all allowed magic properties on the class, and we're * not in that list, fall through */ - if (!($class_storage->sealed_properties || $codebase->config->seal_all_properties) + if (!($class_storage->hasSealedProperties($codebase->config)) && !$override_property_visibility ) { return false; @@ -1144,9 +1194,11 @@ class AtomicPropertyFetchAnalyzer bool $in_assignment, ?string $stmt_var_id, bool $has_magic_getter, - ?string $var_id + ?string $var_id, + bool &$has_valid_fetch_type ): void { - if ($config->use_phpdoc_property_without_magic_or_parent + if (($config->use_phpdoc_property_without_magic_or_parent + || $class_storage->hasAttributeIncludingParents('AllowDynamicProperties', $codebase)) && isset($class_storage->pseudo_property_get_types['$' . $prop_name]) ) { $stmt_type = $class_storage->pseudo_property_get_types['$' . $prop_name]; @@ -1178,6 +1230,7 @@ class AtomicPropertyFetchAnalyzer $context, ); + $has_valid_fetch_type = true; $statements_analyzer->node_data->setType($stmt, $stmt_type); return; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php index 1b6ad40f..cfa4326c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/IncludeAnalyzer.php @@ -158,7 +158,8 @@ class IncludeAnalyzer $current_file_analyzer = $statements_analyzer->getFileAnalyzer(); - if ($current_file_analyzer->project_analyzer->fileExists($path_to_file)) { + if ($current_file_analyzer->project_analyzer->fileExists($path_to_file) + && !$current_file_analyzer->project_analyzer->isDirectory($path_to_file)) { if ($statements_analyzer->hasParentFilePath($path_to_file) || !$codebase->file_storage_provider->has($path_to_file) || ($statements_analyzer->hasAlreadyRequiredFilePath($path_to_file) @@ -395,6 +396,18 @@ class IncludeAnalyzer return $file_name; } + if ((substr($file_name, 0, 2) === '.' . DIRECTORY_SEPARATOR) + || (substr($file_name, 0, 3) === '..' . DIRECTORY_SEPARATOR) + ) { + $file = $current_directory . DIRECTORY_SEPARATOR . $file_name; + + if (file_exists($file)) { + return $file; + } + + return null; + } + $paths = PATH_SEPARATOR === ':' ? preg_split('#(?cond->getAttributes(), ); } - } elseif ($stmt->cond instanceof PhpParser\Node\Expr\FuncCall - || $stmt->cond instanceof PhpParser\Node\Expr\MethodCall - || $stmt->cond instanceof PhpParser\Node\Expr\StaticCall + } elseif ($stmt->cond instanceof PhpParser\Node\Expr\ClassConstFetch + && $stmt->cond->name instanceof PhpParser\Node\Identifier + && $stmt->cond->name->toString() === 'class' ) { + // do nothing + } elseif ($stmt->cond instanceof PhpParser\Node\Expr\ConstFetch + && $stmt->cond->name->toString() === 'true' + ) { + // do nothing + } else { $switch_var_id = '$__tmp_switch__' . (int) $stmt->cond->getAttribute('startFilePos'); $condition_type = $statements_analyzer->node_data->getType($stmt->cond) ?? Type::getMixed(); @@ -128,18 +134,27 @@ class MatchAnalyzer } $arms = $stmt->arms; + $flattened_arms = []; + $last_arm = null; - foreach ($arms as $i => $arm) { - // move default to the end + foreach ($arms as $arm) { if ($arm->conds === null) { - unset($arms[$i]); - $arms[] = $arm; + $last_arm = $arm; + continue; + } + + foreach ($arm->conds as $cond) { + $flattened_arms[] = new PhpParser\Node\MatchArm( + [$cond], + $arm->body, + $arm->getAttributes(), + ); } } + $arms = $flattened_arms; $arms = array_reverse($arms); - - $last_arm = array_shift($arms); + $last_arm ??= array_shift($arms); if (!$last_arm) { IssueBuffer::maybeAdd( diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php index aa229281..3ba14ca6 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/Expression/SimpleTypeInferer.php @@ -666,17 +666,22 @@ class SimpleTypeInferer } elseif ($key_type->isSingleIntLiteral()) { $item_key_value = $key_type->getSingleIntLiteral()->value; - if ($item_key_value >= $array_creation_info->int_offset) { - if ($item_key_value === $array_creation_info->int_offset) { + if ($item_key_value <= PHP_INT_MAX + && $item_key_value > $array_creation_info->int_offset + ) { + if ($item_key_value - 1 === $array_creation_info->int_offset) { $item_is_list_item = true; } - $array_creation_info->int_offset = $item_key_value + 1; + $array_creation_info->int_offset = $item_key_value; } } } } else { + if ($array_creation_info->int_offset === PHP_INT_MAX) { + return false; + } $item_is_list_item = true; - $item_key_value = $array_creation_info->int_offset++; + $item_key_value = ++$array_creation_info->int_offset; $array_creation_info->item_key_atomic_types[] = new TLiteralInt($item_key_value); } @@ -760,7 +765,10 @@ class SimpleTypeInferer $new_offset = $key; $array_creation_info->item_key_atomic_types[] = Type::getAtomicStringFromLiteral($new_offset); } else { - $new_offset = $array_creation_info->int_offset++; + if ($array_creation_info->int_offset === PHP_INT_MAX) { + return false; + } + $new_offset = ++$array_creation_info->int_offset; $array_creation_info->item_key_atomic_types[] = new TLiteralInt($new_offset); } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php index 0a946a36..7cdb1b54 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/Statements/ExpressionAnalyzer.php @@ -53,6 +53,7 @@ use Psalm\Issue\UnrecognizedExpression; use Psalm\Issue\UnsupportedReferenceUsage; use Psalm\IssueBuffer; use Psalm\Plugin\EventHandler\Event\AfterExpressionAnalysisEvent; +use Psalm\Plugin\EventHandler\Event\BeforeExpressionAnalysisEvent; use Psalm\Storage\FunctionLikeParameter; use Psalm\Type; use Psalm\Type\TaintKind; @@ -80,6 +81,10 @@ class ExpressionAnalyzer ?TemplateResult $template_result = null, bool $assigned_to_reference = false ): bool { + if (self::dispatchBeforeExpressionAnalysis($stmt, $context, $statements_analyzer) === false) { + return false; + } + $codebase = $statements_analyzer->getCodebase(); if (self::handleExpression( @@ -126,24 +131,10 @@ class ExpressionAnalyzer } } - $event = new AfterExpressionAnalysisEvent( - $stmt, - $context, - $statements_analyzer, - $codebase, - [], - ); - - if ($codebase->config->eventDispatcher->dispatchAfterExpressionAnalysis($event) === false) { + if (self::dispatchAfterExpressionAnalysis($stmt, $context, $statements_analyzer) === false) { return false; } - $file_manipulations = $event->getFileReplacements(); - - if ($file_manipulations) { - FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations); - } - return true; } @@ -280,7 +271,7 @@ class ExpressionAnalyzer } if ($stmt instanceof PhpParser\Node\Expr\New_) { - return NewAnalyzer::analyze($statements_analyzer, $stmt, $context); + return NewAnalyzer::analyze($statements_analyzer, $stmt, $context, $template_result); } if ($stmt instanceof PhpParser\Node\Expr\Array_) { @@ -554,4 +545,60 @@ class ExpressionAnalyzer return true; } + + private static function dispatchBeforeExpressionAnalysis( + PhpParser\Node\Expr $expr, + Context $context, + StatementsAnalyzer $statements_analyzer + ): ?bool { + $codebase = $statements_analyzer->getCodebase(); + + $event = new BeforeExpressionAnalysisEvent( + $expr, + $context, + $statements_analyzer, + $codebase, + [], + ); + + if ($codebase->config->eventDispatcher->dispatchBeforeExpressionAnalysis($event) === false) { + return false; + } + + $file_manipulations = $event->getFileReplacements(); + + if ($file_manipulations !== []) { + FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations); + } + + return null; + } + + private static function dispatchAfterExpressionAnalysis( + PhpParser\Node\Expr $expr, + Context $context, + StatementsAnalyzer $statements_analyzer + ): ?bool { + $codebase = $statements_analyzer->getCodebase(); + + $event = new AfterExpressionAnalysisEvent( + $expr, + $context, + $statements_analyzer, + $codebase, + [], + ); + + if ($codebase->config->eventDispatcher->dispatchAfterExpressionAnalysis($event) === false) { + return false; + } + + $file_manipulations = $event->getFileReplacements(); + + if ($file_manipulations !== []) { + FileManipulationBuffer::add($statements_analyzer->getFilePath(), $file_manipulations); + } + + return null; + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php index 896f5ac3..2b43d085 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Analyzer/StatementsAnalyzer.php @@ -496,6 +496,7 @@ class StatementsAnalyzer extends SourceAnalyzer && !($stmt instanceof PhpParser\Node\Stmt\Interface_) && !($stmt instanceof PhpParser\Node\Stmt\Trait_) && !($stmt instanceof PhpParser\Node\Stmt\HaltCompiler) + && !($stmt instanceof PhpParser\Node\Stmt\Declare_) ) { if ($codebase->find_unused_variables) { IssueBuffer::maybeAdd( diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php index d9df6548..429144b6 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/LanguageServer.php @@ -2,20 +2,14 @@ namespace Psalm\Internal\Cli; +use LanguageServerProtocol\MessageType; use Psalm\Config; -use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\CliUtils; -use Psalm\Internal\Composer; use Psalm\Internal\ErrorHandler; use Psalm\Internal\Fork\PsalmRestarter; use Psalm\Internal\IncludeCollector; -use Psalm\Internal\Provider\ClassLikeStorageCacheProvider; -use Psalm\Internal\Provider\FileProvider; -use Psalm\Internal\Provider\FileReferenceCacheProvider; -use Psalm\Internal\Provider\FileStorageCacheProvider; -use Psalm\Internal\Provider\ParserCacheProvider; -use Psalm\Internal\Provider\ProjectCacheProvider; -use Psalm\Internal\Provider\Providers; +use Psalm\Internal\LanguageServer\ClientConfiguration; +use Psalm\Internal\LanguageServer\LanguageServer as LanguageServerLanguageServer; use Psalm\Report; use function array_key_exists; @@ -52,16 +46,21 @@ require_once __DIR__ . '/../ErrorHandler.php'; require_once __DIR__ . '/../CliUtils.php'; require_once __DIR__ . '/../Composer.php'; require_once __DIR__ . '/../IncludeCollector.php'; +require_once __DIR__ . '/../LanguageServer/ClientConfiguration.php'; /** * @internal */ final class LanguageServer { - /** @param array $argv */ + /** + * @param array $argv + * @psalm-suppress ComplexMethod + */ public static function run(array $argv): void { CliUtils::checkRuntimeRequirements(); + $clientConfiguration = new ClientConfiguration(); gc_disable(); ErrorHandler::install($argv); $valid_short_options = [ @@ -72,7 +71,6 @@ final class LanguageServer ]; $valid_long_options = [ - 'clear-cache', 'config:', 'find-dead-code', 'help', @@ -82,7 +80,17 @@ final class LanguageServer 'tcp:', 'tcp-server', 'disable-on-change::', + 'use-baseline:', 'enable-autocomplete::', + 'enable-code-actions::', + 'enable-provide-diagnostics::', + 'enable-provide-hover::', + 'enable-provide-signature-help::', + 'enable-provide-definition::', + 'show-diagnostic-warnings::', + 'in-memory::', + 'disable-xdebug::', + 'on-change-debounce-ms::', 'use-extended-diagnostic-codes', 'verbose', ]; @@ -164,12 +172,12 @@ final class LanguageServer --find-dead-code Look for dead code - --clear-cache - Clears all cache files that the language server uses for this specific project - --use-ini-defaults Use PHP-provided ini defaults for memory and error display + --use-baseline=PATH + Allows you to use a baseline other than the default baseline provided in your config + --tcp=url Use TCP mode (by default Psalm uses STDIO) @@ -180,12 +188,39 @@ final class LanguageServer If added, the language server will not respond to onChange events. You can also specify a line count over which Psalm will not run on-change events. + --enable-code-actions[=BOOL] + Enables or disables code actions. Default is true. + + --enable-provide-diagnostics[=BOOL] + Enables or disables providing diagnostics. Default is true. + --enable-autocomplete[=BOOL] Enables or disables autocomplete on methods and properties. Default is true. - --use-extended-diagnostic-codes + --enable-provide-hover[=BOOL] + Enables or disables providing hover. Default is true. + + --enable-provide-signature-help[=BOOL] + Enables or disables providing signature help. Default is true. + + --enable-provide-definition[=BOOL] + Enables or disables providing definition. Default is true. + + --show-diagnostic-warnings[=BOOL] + Enables or disables showing diagnostic warnings. Default is true. + + --use-extended-diagnostic-codes (DEPRECATED) Enables sending help uri links with the code in diagnostic messages. + --on-change-debounce-ms=[INT] + The number of milliseconds to debounce onChange events. + + --disable-xdebug[=BOOL] + Disable xdebug for performance reasons. Enable for debugging + + --in-memory[=BOOL] + Use in-memory mode. Default is false. Experimental. + --verbose Will send log messages to the client with information. @@ -245,8 +280,14 @@ final class LanguageServer 'blackfire', ]); - // If Xdebug is enabled, restart without it - $ini_handler->check(); + $disableXdebug = !isset($options['disable-xdebug']) + || !is_string($options['disable-xdebug']) + || strtolower($options['disable-xdebug']) !== 'false'; + + // If Xdebug is enabled, restart without it based on cli + if ($disableXdebug) { + $ini_handler->check(); + } setlocale(LC_CTYPE, 'C'); @@ -259,8 +300,6 @@ final class LanguageServer } } - $find_unused_code = isset($options['find-dead-code']) ? 'auto' : null; - $config = CliUtils::initializeConfig( $path_to_config, $current_dir, @@ -276,58 +315,85 @@ final class LanguageServer $config->setServerMode(); - if (isset($options['clear-cache'])) { + $inMemory = isset($options['in-memory']) && + is_string($options['in-memory']) && + strtolower($options['in-memory']) === 'true'; + + if ($inMemory) { + $config->cache_directory = null; + } else { $cache_directory = $config->getCacheDirectory(); if ($cache_directory !== null) { Config::removeCacheDirectory($cache_directory); } - echo 'Cache directory deleted' . PHP_EOL; - exit; } - $providers = new Providers( - new FileProvider, - new ParserCacheProvider($config), - new FileStorageCacheProvider($config), - new ClassLikeStorageCacheProvider($config), - new FileReferenceCacheProvider($config), - new ProjectCacheProvider(Composer::getLockFilePath($current_dir)), - ); - - $project_analyzer = new ProjectAnalyzer( - $config, - $providers, - ); - - if ($config->find_unused_variables) { - $project_analyzer->getCodebase()->reportUnusedVariables(); - } - - if ($config->find_unused_code) { - $find_unused_code = 'auto'; + if (isset($options['use-baseline']) && is_string($options['use-baseline'])) { + $clientConfiguration->baseline = $options['use-baseline']; } if (isset($options['disable-on-change']) && is_numeric($options['disable-on-change'])) { - $project_analyzer->onchange_line_limit = (int) $options['disable-on-change']; + $clientConfiguration->onchangeLineLimit = (int) $options['disable-on-change']; } - $project_analyzer->provide_completion = !isset($options['enable-autocomplete']) + if (isset($options['on-change-debounce-ms']) && is_numeric($options['on-change-debounce-ms'])) { + $clientConfiguration->onChangeDebounceMs = (int) $options['on-change-debounce-ms']; + } + + $clientConfiguration->provideDefinition = !isset($options['enable-provide-definition']) + || !is_string($options['enable-provide-definition']) + || strtolower($options['enable-provide-definition']) !== 'false'; + + $clientConfiguration->provideSignatureHelp = !isset($options['enable-provide-signature-help']) + || !is_string($options['enable-provide-signature-help']) + || strtolower($options['enable-provide-signature-help']) !== 'false'; + + $clientConfiguration->provideHover = !isset($options['enable-provide-hover']) + || !is_string($options['enable-provide-hover']) + || strtolower($options['enable-provide-hover']) !== 'false'; + + $clientConfiguration->provideDiagnostics = !isset($options['enable-provide-diagnostics']) + || !is_string($options['enable-provide-diagnostics']) + || strtolower($options['enable-provide-diagnostics']) !== 'false'; + + $clientConfiguration->provideCodeActions = !isset($options['enable-code-actions']) + || !is_string($options['enable-code-actions']) + || strtolower($options['enable-code-actions']) !== 'false'; + + $clientConfiguration->provideCompletion = !isset($options['enable-autocomplete']) || !is_string($options['enable-autocomplete']) || strtolower($options['enable-autocomplete']) !== 'false'; - if ($find_unused_code) { - $project_analyzer->getCodebase()->reportUnusedCode($find_unused_code); - } + $clientConfiguration->hideWarnings = !( + !isset($options['show-diagnostic-warnings']) + || !is_string($options['show-diagnostic-warnings']) + || strtolower($options['show-diagnostic-warnings']) !== 'false' + ); - if (isset($options['use-extended-diagnostic-codes'])) { - $project_analyzer->language_server_use_extended_diagnostic_codes = true; + /** + * if ($config->find_unused_variables) { + * $project_analyzer->getCodebase()->reportUnusedVariables(); + * } + */ + + $find_unused_code = isset($options['find-dead-code']) ? 'auto' : null; + if ($config->find_unused_code) { + $find_unused_code = 'auto'; + } + if ($find_unused_code) { + $clientConfiguration->findUnusedCode = $find_unused_code; } if (isset($options['verbose'])) { - $project_analyzer->language_server_verbose = true; + $clientConfiguration->logLevel = $options['verbose'] ? MessageType::LOG : MessageType::INFO; + } else { + $clientConfiguration->logLevel = MessageType::INFO; } - $project_analyzer->server($options['tcp'] ?? null, isset($options['tcp-server'])); + $clientConfiguration->TCPServerAddress = $options['tcp'] ?? null; + $clientConfiguration->TCPServerMode = isset($options['tcp-server']); + + LanguageServerLanguageServer::run($config, $clientConfiguration, $current_dir, $inMemory); } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php index 58ce6d78..7401cd1e 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalm.php @@ -57,15 +57,12 @@ use function getopt; use function implode; use function in_array; use function ini_get; -use function ini_set; use function is_array; use function is_numeric; -use function is_scalar; use function is_string; use function json_encode; use function max; use function microtime; -use function opcache_get_status; use function parse_url; use function preg_match; use function preg_replace; @@ -190,7 +187,7 @@ final class Psalm self::validateCliArguments($args); - self::setMemoryLimit($options); + CliUtils::setMemoryLimit($options); self::syncShortOptions($options); @@ -463,29 +460,6 @@ final class Psalm ); } - /** - * @param array> $options - */ - private static function setMemoryLimit(array $options): void - { - if (!array_key_exists('use-ini-defaults', $options)) { - ini_set('display_errors', 'stderr'); - ini_set('display_startup_errors', '1'); - - $memoryLimit = (8 * 1_024 * 1_024 * 1_024); - - if (array_key_exists('memory-limit', $options)) { - $memoryLimit = $options['memory-limit']; - - if (!is_scalar($memoryLimit)) { - throw new ConfigException('Invalid memory limit specified.'); - } - } - - ini_set('memory_limit', (string) $memoryLimit); - } - } - /** * @param array $args */ @@ -923,11 +897,7 @@ final class Psalm // If Xdebug is enabled, restart without it $ini_handler->check(); - if (!function_exists('opcache_get_status') - || !($opcache_status = opcache_get_status(false)) - || !isset($opcache_status['opcache_enabled']) - || !$opcache_status['opcache_enabled'] - ) { + if (!function_exists('opcache_get_status')) { $progress->write(PHP_EOL . 'Install the opcache extension to make use of JIT on PHP 8.0+ for a 20%+ performance boost!' . PHP_EOL . PHP_EOL); @@ -1273,6 +1243,9 @@ final class Psalm --php-version=PHP_VERSION Explicitly set PHP version to analyse code against. + --error-level=ERROR_LEVEL + Set the error reporting level + Surfacing issues: --show-info[=BOOLEAN] Show non-exception parser findings (defaults to false). diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php index 6d5dfa0c..9dd8eaf4 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Cli/Psalter.php @@ -43,7 +43,6 @@ use function getcwd; use function getopt; use function implode; use function in_array; -use function ini_set; use function is_array; use function is_dir; use function is_numeric; @@ -89,6 +88,7 @@ final class Psalter 'add-newline-between-docblock-annotations:', 'no-cache', 'no-progress', + 'memory-limit:', ]; /** @param array $argv */ @@ -100,8 +100,6 @@ final class Psalter ErrorHandler::install($argv); - self::setMemoryLimit(); - $args = array_slice($argv, 1); // get options from command line @@ -109,6 +107,8 @@ final class Psalter self::validateCliArguments($args); + CliUtils::setMemoryLimit($options); + self::syncShortOptions($options); if (isset($options['c']) && is_array($options['c'])) { @@ -442,15 +442,6 @@ final class Psalter IssueBuffer::finish($project_analyzer, false, $start_time); } - private static function setMemoryLimit(): void - { - $memLimit = CliUtils::getMemoryLimitInBytes(); - // Magic number is 4096M in bytes - if ($memLimit > 0 && $memLimit < 8 * 1_024 * 1_024 * 1_024) { - ini_set('memory_limit', (string) (8 * 1_024 * 1_024 * 1_024)); - } - } - /** @param array $args */ private static function validateCliArguments(array $args): void { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php b/vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php index fc1f979f..1e5a1abd 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/CliUtils.php @@ -14,8 +14,8 @@ use Psalm\Report; use RuntimeException; use function array_filter; +use function array_key_exists; use function array_slice; -use function assert; use function count; use function define; use function dirname; @@ -27,13 +27,13 @@ use function file_put_contents; use function fwrite; use function implode; use function in_array; -use function ini_get; +use function ini_set; use function is_array; use function is_dir; +use function is_scalar; use function is_string; use function json_decode; use function preg_last_error_msg; -use function preg_match; use function preg_replace; use function preg_split; use function realpath; @@ -41,7 +41,6 @@ use function stream_get_meta_data; use function stream_set_blocking; use function strlen; use function strpos; -use function strtoupper; use function substr; use function substr_replace; use function trim; @@ -446,38 +445,27 @@ final class CliUtils } /** - * @psalm-pure + * @param array> $options + * @throws ConfigException */ - public static function getMemoryLimitInBytes(): int + public static function setMemoryLimit(array $options): void { - return self::convertMemoryLimitToBytes(ini_get('memory_limit')); - } + if (!array_key_exists('use-ini-defaults', $options)) { + ini_set('display_errors', 'stderr'); + ini_set('display_startup_errors', '1'); - /** @psalm-pure */ - public static function convertMemoryLimitToBytes(string $limit): int - { - // for unlimited = -1 - if ($limit < 0) { - return -1; - } + $memoryLimit = (8 * 1_024 * 1_024 * 1_024); - if (preg_match('/^(\d+)(\D?)$/', $limit, $matches)) { - assert(isset($matches[1])); - $limit = (int)$matches[1]; - switch (strtoupper($matches[2] ?? '')) { - case 'G': - $limit *= 1_024 * 1_024 * 1_024; - break; - case 'M': - $limit *= 1_024 * 1_024; - break; - case 'K': - $limit *= 1_024; - break; + if (array_key_exists('memory-limit', $options)) { + $memoryLimit = $options['memory-limit']; + + if (!is_scalar($memoryLimit)) { + throw new ConfigException('Invalid memory limit specified.'); + } } - } - return (int)$limit; + ini_set('memory_limit', (string) $memoryLimit); + } } public static function initPhpVersion(array $options, Config $config, ProjectAnalyzer $project_analyzer): void diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php index 97d3931b..0e6ef6f8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassConstantByWildcardResolver.php @@ -9,25 +9,23 @@ use Psalm\Type\Atomic; use Psalm\Type\Atomic\TMixed; use function array_merge; -use function array_values; -use function preg_match; -use function sprintf; -use function str_replace; /** * @internal */ final class ClassConstantByWildcardResolver { + private StorageByPatternResolver $resolver; private Codebase $codebase; public function __construct(Codebase $codebase) { + $this->resolver = new StorageByPatternResolver(); $this->codebase = $codebase; } /** - * @return list|null + * @return non-empty-array|null */ public function resolve(string $class_name, string $constant_pattern): ?array { @@ -35,24 +33,27 @@ final class ClassConstantByWildcardResolver return null; } - $constant_regex_pattern = sprintf('#^%s$#', str_replace('*', '.*', $constant_pattern)); + $classlike_storage = $this->codebase->classlike_storage_provider->get($class_name); - $class_like_storage = $this->codebase->classlike_storage_provider->get($class_name); - $matched_class_constant_types = []; - - foreach ($class_like_storage->constants as $constant => $class_constant_storage) { - if (preg_match($constant_regex_pattern, $constant) === 0) { - continue; - } + $constants = $this->resolver->resolveConstants( + $classlike_storage, + $constant_pattern, + ); + $types = []; + foreach ($constants as $class_constant_storage) { if (! $class_constant_storage->type) { - $matched_class_constant_types[] = [new TMixed()]; + $types[] = [new TMixed()]; continue; } - $matched_class_constant_types[] = $class_constant_storage->type->getAtomicTypes(); + $types[] = $class_constant_storage->type->getAtomicTypes(); } - return array_values(array_merge([], ...$matched_class_constant_types)); + if ($types === []) { + return null; + } + + return array_merge([], ...$types); } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php index 618ba707..696b0413 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ClassLikes.php @@ -47,6 +47,7 @@ use ReflectionProperty; use UnexpectedValueException; use function array_filter; +use function array_keys; use function array_merge; use function array_pop; use function count; @@ -1603,8 +1604,7 @@ class ClassLikes } /** - * @param ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PRIVATE - * $visibility + * @param ReflectionProperty::IS_PUBLIC|ReflectionProperty::IS_PROTECTED|ReflectionProperty::IS_PRIVATE $visibility */ public function getClassConstantType( string $class_name, @@ -1612,7 +1612,8 @@ class ClassLikes int $visibility, ?StatementsAnalyzer $statements_analyzer = null, array $visited_constant_ids = [], - bool $late_static_binding = false + bool $late_static_binding = false, + bool $in_value_of_context = false ): ?Union { $class_name = strtolower($class_name); @@ -1622,41 +1623,42 @@ class ClassLikes $storage = $this->classlike_storage_provider->get($class_name); - if (isset($storage->constants[$constant_name])) { - $constant_storage = $storage->constants[$constant_name]; + $enum_types = null; - if ($visibility === ReflectionProperty::IS_PUBLIC - && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PUBLIC - ) { - return null; + if ($storage->is_enum) { + $enum_types = $this->getEnumType( + $storage, + $constant_name, + ); + + if ($in_value_of_context) { + return $enum_types; } - - if ($visibility === ReflectionProperty::IS_PROTECTED - && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PUBLIC - && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PROTECTED - ) { - return null; - } - - if ($constant_storage->unresolved_node) { - /** @psalm-suppress InaccessibleProperty Lazy resolution */ - $constant_storage->inferred_type = new Union([ConstantTypeResolver::resolve( - $this, - $constant_storage->unresolved_node, - $statements_analyzer, - $visited_constant_ids, - )]); - if ($constant_storage->type === null || !$constant_storage->type->from_docblock) { - /** @psalm-suppress InaccessibleProperty Lazy resolution */ - $constant_storage->type = $constant_storage->inferred_type; - } - } - - return $late_static_binding ? $constant_storage->type : ($constant_storage->inferred_type ?? null); - } elseif (isset($storage->enum_cases[$constant_name])) { - return new Union([new TEnumCase($storage->name, $constant_name)]); } - return null; + + $constant_types = $this->getConstantType( + $storage, + $constant_name, + $visibility, + $statements_analyzer, + $visited_constant_ids, + $late_static_binding, + ); + + $types = []; + if ($enum_types !== null) { + $types = array_merge($types, $enum_types->getAtomicTypes()); + } + + if ($constant_types !== null) { + $types = array_merge($types, $constant_types->getAtomicTypes()); + } + + if ($types === []) { + return null; + } + + return new Union($types); } private function checkMethodReferences(ClassLikeStorage $classlike_storage, Methods $methods): void @@ -2366,4 +2368,113 @@ class ClassLikes return null; } } + + private function getConstantType( + ClassLikeStorage $class_like_storage, + string $constant_name, + int $visibility, + ?StatementsAnalyzer $statements_analyzer, + array $visited_constant_ids, + bool $late_static_binding + ): ?Union { + $constant_resolver = new StorageByPatternResolver(); + $resolved_constants = $constant_resolver->resolveConstants( + $class_like_storage, + $constant_name, + ); + + $filtered_constants_by_visibility = array_filter( + $resolved_constants, + fn(ClassConstantStorage $resolved_constant) => $this->filterConstantNameByVisibility( + $resolved_constant, + $visibility, + ) + ); + + if ($filtered_constants_by_visibility === []) { + return null; + } + + $new_atomic_types = []; + + foreach ($filtered_constants_by_visibility as $filtered_constant_name => $constant_storage) { + if (!isset($class_like_storage->constants[$filtered_constant_name])) { + continue; + } + + if ($constant_storage->unresolved_node) { + /** @psalm-suppress InaccessibleProperty Lazy resolution */ + $constant_storage->inferred_type = new Union([ConstantTypeResolver::resolve( + $this, + $constant_storage->unresolved_node, + $statements_analyzer, + $visited_constant_ids, + )]); + + if ($constant_storage->type === null || !$constant_storage->type->from_docblock) { + /** @psalm-suppress InaccessibleProperty Lazy resolution */ + $constant_storage->type = $constant_storage->inferred_type; + } + } + + $constant_type = $late_static_binding + ? $constant_storage->type + : ($constant_storage->inferred_type ?? null); + + if ($constant_type === null) { + continue; + } + + $new_atomic_types[] = $constant_type->getAtomicTypes(); + } + + if ($new_atomic_types === []) { + return null; + } + + return new Union(array_merge([], ...$new_atomic_types)); + } + + private function getEnumType( + ClassLikeStorage $class_like_storage, + string $constant_name + ): ?Union { + $constant_resolver = new StorageByPatternResolver(); + $resolved_enums = $constant_resolver->resolveEnums( + $class_like_storage, + $constant_name, + ); + + if ($resolved_enums === []) { + return null; + } + + $types = []; + foreach (array_keys($resolved_enums) as $enum_case_name) { + $types[$enum_case_name] = new TEnumCase($class_like_storage->name, $enum_case_name); + } + + return new Union($types); + } + + private function filterConstantNameByVisibility( + ClassConstantStorage $constant_storage, + int $visibility + ): bool { + + if ($visibility === ReflectionProperty::IS_PUBLIC + && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PUBLIC + ) { + return false; + } + + if ($visibility === ReflectionProperty::IS_PROTECTED + && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PUBLIC + && $constant_storage->visibility !== ClassLikeAnalyzer::VISIBILITY_PROTECTED + ) { + return false; + } + + return true; + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php index d341bbc9..9bf8caba 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/ConstantTypeResolver.php @@ -11,6 +11,9 @@ use Psalm\Internal\Scanner\UnresolvedConstant\ArraySpread; use Psalm\Internal\Scanner\UnresolvedConstant\ArrayValue; use Psalm\Internal\Scanner\UnresolvedConstant\ClassConstant; use Psalm\Internal\Scanner\UnresolvedConstant\Constant; +use Psalm\Internal\Scanner\UnresolvedConstant\EnumNameFetch; +use Psalm\Internal\Scanner\UnresolvedConstant\EnumPropertyFetch; +use Psalm\Internal\Scanner\UnresolvedConstant\EnumValueFetch; use Psalm\Internal\Scanner\UnresolvedConstant\ScalarValue; use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedAdditionOp; use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedBinaryOp; @@ -331,6 +334,24 @@ class ConstantTypeResolver } } + if ($c instanceof EnumPropertyFetch) { + if ($classlikes->enumExists($c->fqcln)) { + $enum_storage = $classlikes->getStorageFor($c->fqcln); + if (isset($enum_storage->enum_cases[$c->case])) { + if ($c instanceof EnumValueFetch) { + $value = $enum_storage->enum_cases[$c->case]->value; + if (is_string($value)) { + return Type::getString($value)->getSingleAtomic(); + } elseif (is_int($value)) { + return Type::getInt(false, $value)->getSingleAtomic(); + } + } elseif ($c instanceof EnumNameFetch) { + return Type::getString($c->case)->getSingleAtomic(); + } + } + } + } + return new TMixed; } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php index 802d41dc..a67a1d38 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Methods.php @@ -825,8 +825,6 @@ class Methods return null; } - $candidate_type = null; - foreach ($class_storage->overridden_method_ids[$appearing_method_name] as $overridden_method_id) { $overridden_storage = $this->getStorage($overridden_method_id); diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php index a033401e..022cc684 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Populator.php @@ -920,8 +920,8 @@ class Populator $fq_class_name = $storage->name; $fq_class_name_lc = strtolower($fq_class_name); - if ($parent_storage->sealed_methods) { - $storage->sealed_methods = true; + if ($parent_storage->sealed_methods !== null) { + $storage->sealed_methods = $parent_storage->sealed_methods; } // register where they appear (can never be in a trait) @@ -1032,8 +1032,8 @@ class Populator ClassLikeStorage $storage, ClassLikeStorage $parent_storage ): void { - if ($parent_storage->sealed_properties) { - $storage->sealed_properties = true; + if ($parent_storage->sealed_properties !== null) { + $storage->sealed_properties = $parent_storage->sealed_properties; } // register where they appear (can never be in a trait) diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php index 0e215c32..ab2c586e 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/Scanner.php @@ -291,6 +291,7 @@ class Scanner private function shouldScan(string $file_path): bool { return $this->file_provider->fileExists($file_path) + && !$this->file_provider->isDirectory($file_path) && (!isset($this->scanned_files[$file_path]) || (isset($this->files_to_deep_scan[$file_path]) && !$this->scanned_files[$file_path])); } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/StorageByPatternResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/StorageByPatternResolver.php new file mode 100644 index 00000000..ed5baed6 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Codebase/StorageByPatternResolver.php @@ -0,0 +1,87 @@ + + */ + public function resolveConstants( + ClassLikeStorage $class_like_storage, + string $pattern + ): array { + $constants = $class_like_storage->constants; + + if (strpos($pattern, '*') === false) { + if (isset($constants[$pattern])) { + return [$pattern => $constants[$pattern]]; + } + + return []; + } elseif ($pattern === '*') { + return $constants; + } + + $regex_pattern = sprintf('#^%s$#', str_replace('*', '.*?', $pattern)); + $matched_constants = []; + + foreach ($constants as $constant => $class_constant_storage) { + if (preg_match($regex_pattern, $constant) === 0) { + continue; + } + + $matched_constants[$constant] = $class_constant_storage; + } + + return $matched_constants; + } + + /** + * @return array + */ + public function resolveEnums( + ClassLikeStorage $class_like_storage, + string $pattern + ): array { + $enum_cases = $class_like_storage->enum_cases; + if (strpos($pattern, '*') === false) { + if (isset($enum_cases[$pattern])) { + return [$pattern => $enum_cases[$pattern]]; + } + + return []; + } elseif ($pattern === '*') { + return $enum_cases; + } + + $regex_pattern = sprintf('#^%s$#', str_replace('*', '.*?', $pattern)); + $matched_enums = []; + foreach ($enum_cases as $enum_case_name => $enum_case_storage) { + if (preg_match($regex_pattern, $enum_case_name) === 0) { + continue; + } + + $matched_enums[$enum_case_name] = $enum_case_storage; + } + + return $matched_enums; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php b/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php index b42c7b3d..fb574949 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/EventDispatcher.php @@ -16,6 +16,7 @@ use Psalm\Plugin\EventHandler\AfterFunctionLikeAnalysisInterface; use Psalm\Plugin\EventHandler\AfterMethodCallAnalysisInterface; use Psalm\Plugin\EventHandler\AfterStatementAnalysisInterface; use Psalm\Plugin\EventHandler\BeforeAddIssueInterface; +use Psalm\Plugin\EventHandler\BeforeExpressionAnalysisInterface; use Psalm\Plugin\EventHandler\BeforeFileAnalysisInterface; use Psalm\Plugin\EventHandler\BeforeStatementAnalysisInterface; use Psalm\Plugin\EventHandler\Event\AddRemoveTaintsEvent; @@ -32,6 +33,7 @@ use Psalm\Plugin\EventHandler\Event\AfterFunctionLikeAnalysisEvent; use Psalm\Plugin\EventHandler\Event\AfterMethodCallAnalysisEvent; use Psalm\Plugin\EventHandler\Event\AfterStatementAnalysisEvent; use Psalm\Plugin\EventHandler\Event\BeforeAddIssueEvent; +use Psalm\Plugin\EventHandler\Event\BeforeExpressionAnalysisEvent; use Psalm\Plugin\EventHandler\Event\BeforeFileAnalysisEvent; use Psalm\Plugin\EventHandler\Event\BeforeStatementAnalysisEvent; use Psalm\Plugin\EventHandler\Event\StringInterpreterEvent; @@ -77,6 +79,13 @@ class EventDispatcher */ public array $after_every_function_checks = []; + /** + * Static methods to be called before expression checks are completed + * + * @var list> + */ + public array $before_expression_checks = []; + /** * Static methods to be called after expression checks have completed * @@ -197,6 +206,10 @@ class EventDispatcher $this->after_every_function_checks[] = $class; } + if (is_subclass_of($class, BeforeExpressionAnalysisInterface::class)) { + $this->before_expression_checks[] = $class; + } + if (is_subclass_of($class, AfterExpressionAnalysisInterface::class)) { $this->after_expression_checks[] = $class; } @@ -284,6 +297,17 @@ class EventDispatcher } } + public function dispatchBeforeExpressionAnalysis(BeforeExpressionAnalysisEvent $event): ?bool + { + foreach ($this->before_expression_checks as $handler) { + if ($handler::beforeExpressionAnalysis($event) === false) { + return false; + } + } + + return null; + } + public function dispatchAfterExpressionAnalysis(AfterExpressionAnalysisEvent $event): ?bool { foreach ($this->after_expression_checks as $handler) { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php b/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php index 9b1762db..b04952c0 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/FileManipulation/FunctionDocblockManipulator.php @@ -137,7 +137,7 @@ class FunctionDocblockManipulator if ($param->type) { $this->param_typehint_offsets[$param->var->name] = [ (int) $param->type->getAttribute('startFilePos'), - (int) $param->type->getAttribute('endFilePos'), + (int) $param->type->getAttribute('endFilePos') + 1, ]; } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php index 32c255ca..27840944 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Fork/PsalmRestarter.php @@ -30,6 +30,7 @@ class PsalmRestarter extends XdebugHandler 'jit_buffer_size' => 512 * 1024 * 1024, 'optimization_level' => '0x7FFEBFFF', 'preload' => '', + 'log_verbosity_level' => 0, ]; private bool $required = false; @@ -70,6 +71,7 @@ class PsalmRestarter extends XdebugHandler $opcache_settings = [ 'enable_cli' => in_array(ini_get('opcache.enable_cli'), ['1', 'true', true, 1]), 'jit' => (int) ini_get('opcache.jit'), + 'log_verbosity_level' => (int) ini_get('opcache.log_verbosity_level'), 'optimization_level' => (string) ini_get('opcache.optimization_level'), 'preload' => (string) ini_get('opcache.preload'), 'jit_buffer_size' => self::toBytes(ini_get('opcache.jit_buffer_size')), @@ -146,6 +148,7 @@ class PsalmRestarter extends XdebugHandler '-dopcache.jit=1205', '-dopcache.optimization_level=0x7FFEBFFF', '-dopcache.preload=', + '-dopcache.log_verbosity_level=0', ]; } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php index 2338465a..c7f4e378 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/TextDocument.php @@ -4,15 +4,9 @@ declare(strict_types=1); namespace Psalm\Internal\LanguageServer\Client; -use Amp\Promise; -use Generator; -use JsonMapper; use LanguageServerProtocol\Diagnostic; -use LanguageServerProtocol\TextDocumentIdentifier; -use LanguageServerProtocol\TextDocumentItem; use Psalm\Internal\LanguageServer\ClientHandler; - -use function Amp\call; +use Psalm\Internal\LanguageServer\LanguageServer; /** * Provides method handlers for all textDocument/* methods @@ -23,12 +17,12 @@ class TextDocument { private ClientHandler $handler; - private JsonMapper $mapper; + private LanguageServer $server; - public function __construct(ClientHandler $handler, JsonMapper $mapper) + public function __construct(ClientHandler $handler, LanguageServer $server) { $this->handler = $handler; - $this->mapper = $mapper; + $this->server = $server; } /** @@ -36,40 +30,18 @@ class TextDocument * * @param Diagnostic[] $diagnostics */ - public function publishDiagnostics(string $uri, array $diagnostics): void + public function publishDiagnostics(string $uri, array $diagnostics, ?int $version = null): void { + if (!$this->server->client->clientConfiguration->provideDiagnostics) { + return; + } + + $this->server->logDebug("textDocument/publishDiagnostics"); + $this->handler->notify('textDocument/publishDiagnostics', [ 'uri' => $uri, 'diagnostics' => $diagnostics, + 'version' => $version, ]); } - - /** - * The content request is sent from a server to a client - * to request the current content of a text document identified by the URI - * - * @param TextDocumentIdentifier $textDocument The document to get the content for - * @return Promise The document's current content - * @psalm-suppress MixedReturnTypeCoercion due to Psalm bug - */ - public function xcontent(TextDocumentIdentifier $textDocument): Promise - { - return call( - /** - * @return Generator, object, TextDocumentItem> - */ - function () use ($textDocument) { - /** @var Promise */ - $promise = $this->handler->request( - 'textDocument/xcontent', - ['textDocument' => $textDocument], - ); - - $result = yield $promise; - - /** @var TextDocumentItem */ - return $this->mapper->map($result, new TextDocumentItem); - }, - ); - } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Workspace.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Workspace.php new file mode 100644 index 00000000..f9d9cf39 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Client/Workspace.php @@ -0,0 +1,59 @@ +handler = $handler; + $this->mapper = $mapper; + $this->server = $server; + } + + /** + * The workspace/configuration request is sent from the server to the client to + * fetch configuration settings from the client. The request can fetch several + * configuration settings in one roundtrip. The order of the returned configuration + * settings correspond to the order of the passed ConfigurationItems (e.g. the first + * item in the response is the result for the first configuration item in the params). + * + * @param string $section The configuration section asked for. + * @param string|null $scopeUri The scope to get the configuration section for. + */ + public function requestConfiguration(string $section, ?string $scopeUri = null): Promise + { + $this->server->logDebug("workspace/configuration"); + + /** @var Promise */ + return $this->handler->request('workspace/configuration', [ + 'items' => [ + [ + 'section' => $section, + 'scopeUri' => $scopeUri, + ], + ], + ]); + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientConfiguration.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientConfiguration.php new file mode 100644 index 00000000..115a38f6 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientConfiguration.php @@ -0,0 +1,129 @@ +hideWarnings = $hideWarnings; + $this->provideCompletion = $provideCompletion; + $this->provideDefinition = $provideDefinition; + $this->provideHover = $provideHover; + $this->provideSignatureHelp = $provideSignatureHelp; + $this->provideCodeActions = $provideCodeActions; + $this->provideDiagnostics = $provideDiagnostics; + $this->findUnusedVariables = $findUnusedVariables; + $this->findUnusedCode = $findUnusedCode; + $this->logLevel = $logLevel; + $this->onchangeLineLimit = $onchangeLineLimit; + $this->baseline = $baseline; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php index 444e916b..06e48e3d 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ClientHandler.php @@ -13,7 +13,6 @@ use Amp\Promise; use Generator; use function Amp\call; -use function error_log; /** * @internal @@ -59,7 +58,6 @@ class ClientHandler $listener = function (Message $msg) use ($id, $deferred, &$listener): void { - error_log('request handler'); /** * @psalm-suppress UndefinedPropertyFetch * @psalm-suppress MixedArgument diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php index 9a766dce..096177d9 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageClient.php @@ -5,7 +5,14 @@ declare(strict_types=1); namespace Psalm\Internal\LanguageServer; use JsonMapper; +use LanguageServerProtocol\LogMessage; +use LanguageServerProtocol\LogTrace; use Psalm\Internal\LanguageServer\Client\TextDocument as ClientTextDocument; +use Psalm\Internal\LanguageServer\Client\Workspace as ClientWorkspace; + +use function is_null; +use function json_decode; +use function json_encode; /** * @internal @@ -17,44 +24,159 @@ class LanguageClient */ public ClientTextDocument $textDocument; + /** + * Handles workspace/* methods + */ + public ClientWorkspace $workspace; + /** * The client handler */ private ClientHandler $handler; - public function __construct(ProtocolReader $reader, ProtocolWriter $writer) - { - $this->handler = new ClientHandler($reader, $writer); - $mapper = new JsonMapper; + /** + * The Language Server + */ + private LanguageServer $server; - $this->textDocument = new ClientTextDocument($this->handler, $mapper); + /** + * The Client Configuration + */ + public ClientConfiguration $clientConfiguration; + + public function __construct( + ProtocolReader $reader, + ProtocolWriter $writer, + LanguageServer $server, + ClientConfiguration $clientConfiguration + ) { + $this->handler = new ClientHandler($reader, $writer); + $this->server = $server; + + $this->textDocument = new ClientTextDocument($this->handler, $this->server); + $this->workspace = new ClientWorkspace($this->handler, new JsonMapper, $this->server); + $this->clientConfiguration = $clientConfiguration; + } + + /** + * Request Configuration from Client and save it + */ + public function refreshConfiguration(): void + { + $capabilities = $this->server->clientCapabilities; + if ($capabilities && $capabilities->workspace && $capabilities->workspace->configuration) { + $this->workspace->requestConfiguration('psalm')->onResolve(function ($error, $value): void { + if ($error) { + $this->server->logError('There was an error getting configuration'); + } else { + /** @var array $value */ + [$config] = $value; + $this->configurationRefreshed((array) $config); + } + }); + } + } + + /** + * A notification to log the trace of the server’s execution. + * The amount and content of these notifications depends on the current trace configuration. + * + * @psalm-suppress PossiblyUnusedMethod + */ + public function logTrace(LogTrace $logTrace): void + { + //If trace is 'off', the server should not send any logTrace notification. + if (is_null($this->server->trace) || $this->server->trace === 'off') { + return; + } + + //If trace is 'messages', the server should not add the 'verbose' field in the LogTraceParams. + if ($this->server->trace === 'messages') { + $logTrace->verbose = null; + } + + $this->handler->notify( + '$/logTrace', + $logTrace, + ); } /** * Send a log message to the client. - * - * @param string $message The message to send to the client. - * @psalm-param 1|2|3|4 $type - * @param int $type The log type: - * - 1 = Error - * - 2 = Warning - * - 3 = Info - * - 4 = Log */ - public function logMessage(string $message, int $type = 4, string $method = 'window/logMessage'): void + public function logMessage(LogMessage $logMessage): void { - // https://microsoft.github.io/language-server-protocol/specifications/specification-current/#window_logMessage - - if ($type < 1 || $type > 4) { - $type = 4; - } - $this->handler->notify( - $method, - [ - 'type' => $type, - 'message' => $message, - ], + 'window/logMessage', + $logMessage, ); } + + /** + * The telemetry notification is sent from the + * server to the client to ask the client to log + * a telemetry event. + * + * The protocol doesn’t specify the payload since no + * interpretation of the data happens in the protocol. + * Most clients even don’t handle the event directly + * but forward them to the extensions owing the corresponding + * server issuing the event. + */ + public function event(LogMessage $logMessage): void + { + $this->handler->notify( + 'telemetry/event', + $logMessage, + ); + } + + /** + * Configuration Refreshed from Client + * + * @param array $config + */ + private function configurationRefreshed(array $config): void + { + //do things when the config is refreshed + + if (empty($config)) { + return; + } + + /** @var array */ + $array = json_decode(json_encode($config), true); + + if (isset($array['hideWarnings'])) { + $this->clientConfiguration->hideWarnings = (bool) $array['hideWarnings']; + } + + if (isset($array['provideCompletion'])) { + $this->clientConfiguration->provideCompletion = (bool) $array['provideCompletion']; + } + + if (isset($array['provideDefinition'])) { + $this->clientConfiguration->provideDefinition = (bool) $array['provideDefinition']; + } + + if (isset($array['provideHover'])) { + $this->clientConfiguration->provideHover = (bool) $array['provideHover']; + } + + if (isset($array['provideSignatureHelp'])) { + $this->clientConfiguration->provideSignatureHelp = (bool) $array['provideSignatureHelp']; + } + + if (isset($array['provideCodeActions'])) { + $this->clientConfiguration->provideCodeActions = (bool) $array['provideCodeActions']; + } + + if (isset($array['provideDiagnostics'])) { + $this->clientConfiguration->provideDiagnostics = (bool) $array['provideDiagnostics']; + } + + if (isset($array['findUnusedVariables'])) { + $this->clientConfiguration->findUnusedVariables = (bool) $array['findUnusedVariables']; + } + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php index 9784cd40..daec34e8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/LanguageServer.php @@ -11,16 +11,23 @@ use AdvancedJsonRpc\ErrorResponse; use AdvancedJsonRpc\Request; use AdvancedJsonRpc\Response; use AdvancedJsonRpc\SuccessResponse; +use Amp\Loop; use Amp\Promise; use Amp\Success; use Generator; use InvalidArgumentException; use JsonMapper; use LanguageServerProtocol\ClientCapabilities; +use LanguageServerProtocol\ClientInfo; +use LanguageServerProtocol\CodeDescription; use LanguageServerProtocol\CompletionOptions; use LanguageServerProtocol\Diagnostic; use LanguageServerProtocol\DiagnosticSeverity; +use LanguageServerProtocol\ExecuteCommandOptions; use LanguageServerProtocol\InitializeResult; +use LanguageServerProtocol\InitializeResultServerInfo; +use LanguageServerProtocol\LogMessage; +use LanguageServerProtocol\MessageType; use LanguageServerProtocol\Position; use LanguageServerProtocol\Range; use LanguageServerProtocol\SaveOptions; @@ -28,34 +35,68 @@ use LanguageServerProtocol\ServerCapabilities; use LanguageServerProtocol\SignatureHelpOptions; use LanguageServerProtocol\TextDocumentSyncKind; use LanguageServerProtocol\TextDocumentSyncOptions; +use Psalm\Codebase; use Psalm\Config; +use Psalm\ErrorBaseline; use Psalm\Internal\Analyzer\IssueData; use Psalm\Internal\Analyzer\ProjectAnalyzer; +use Psalm\Internal\Composer; +use Psalm\Internal\LanguageServer\Provider\ClassLikeStorageCacheProvider as InMemoryClassLikeStorageCacheProvider; +use Psalm\Internal\LanguageServer\Provider\FileReferenceCacheProvider as InMemoryFileReferenceCacheProvider; +use Psalm\Internal\LanguageServer\Provider\FileStorageCacheProvider as InMemoryFileStorageCacheProvider; +use Psalm\Internal\LanguageServer\Provider\ParserCacheProvider as InMemoryParserCacheProvider; +use Psalm\Internal\LanguageServer\Provider\ProjectCacheProvider as InMemoryProjectCacheProvider; use Psalm\Internal\LanguageServer\Server\TextDocument as ServerTextDocument; use Psalm\Internal\LanguageServer\Server\Workspace as ServerWorkspace; +use Psalm\Internal\Provider\ClassLikeStorageCacheProvider; +use Psalm\Internal\Provider\FileProvider; +use Psalm\Internal\Provider\FileReferenceCacheProvider; +use Psalm\Internal\Provider\FileStorageCacheProvider; +use Psalm\Internal\Provider\ParserCacheProvider; +use Psalm\Internal\Provider\ProjectCacheProvider; +use Psalm\Internal\Provider\Providers; use Psalm\IssueBuffer; use Throwable; use function Amp\asyncCoroutine; use function Amp\call; use function array_combine; +use function array_filter; use function array_keys; use function array_map; +use function array_reduce; +use function array_search; use function array_shift; +use function array_splice; use function array_unshift; +use function array_values; +use function cli_set_process_title; +use function count; use function explode; +use function fwrite; use function implode; +use function json_encode; use function max; use function parse_url; use function rawurlencode; use function realpath; use function str_replace; +use function stream_set_blocking; +use function stream_socket_accept; +use function stream_socket_client; +use function stream_socket_server; use function strpos; use function substr; use function trim; use function urldecode; +use const JSON_PRETTY_PRINT; +use const STDERR; +use const STDIN; +use const STDOUT; + /** + * @psalm-api * @internal */ class LanguageServer extends Dispatcher @@ -70,28 +111,29 @@ class LanguageServer extends Dispatcher */ public ?ServerWorkspace $workspace = null; + public ?ClientInfo $clientInfo = null; + protected ProtocolReader $protocolReader; protected ProtocolWriter $protocolWriter; public LanguageClient $client; + public ?ClientCapabilities $clientCapabilities = null; + + public ?string $trace = null; + protected ProjectAnalyzer $project_analyzer; - /** - * @var array - */ - protected array $onsave_paths_to_analyze = []; + protected Codebase $codebase; /** - * @var array + * The AMP Delay token */ - protected array $onchange_paths_to_analyze = []; + protected string $versionedAnalysisDelayToken = ''; - /** - * @var array> - */ - protected array $current_issues = []; + /** @var array}>> */ + protected array $issue_baseline = []; /** * This should actually be a private property on `parent` @@ -103,11 +145,19 @@ class LanguageServer extends Dispatcher public function __construct( ProtocolReader $reader, ProtocolWriter $writer, - ProjectAnalyzer $project_analyzer + ProjectAnalyzer $project_analyzer, + Codebase $codebase, + ClientConfiguration $clientConfiguration, + Progress $progress ) { parent::__construct($this, '/'); + + $progress->setServer($this); + $this->project_analyzer = $project_analyzer; + $this->codebase = $codebase; + $this->protocolWriter = $writer; $this->protocolReader = $reader; @@ -139,10 +189,14 @@ class LanguageServer extends Dispatcher try { // Invoke the method handler to get a result /** - * @var Promise + * @var Promise|null */ $dispatched = $this->dispatch($msg->body); - $result = yield $dispatched; + if ($dispatched !== null) { + $result = yield $dispatched; + } else { + $result = null; + } } catch (Error $e) { // If a ResponseError is thrown, send it back in the Response $error = $e; @@ -155,6 +209,9 @@ class LanguageServer extends Dispatcher $e, ); } + if ($error !== null) { + $this->logError($error->message); + } // Only send a Response for a Request // Notifications do not send Responses /** @@ -176,35 +233,171 @@ class LanguageServer extends Dispatcher $this->protocolReader->on( 'readMessageGroup', function (): void { - $this->doAnalysis(); + //$this->verboseLog('Received message group'); + //$this->doAnalysis(); }, ); - $this->client = new LanguageClient($reader, $writer); + $this->client = new LanguageClient($reader, $writer, $this, $clientConfiguration); - $this->verboseLog("Language server has started."); + $this->logInfo("Psalm Language Server ".PSALM_VERSION." has started."); + } + + /** + * Start the Server + */ + public static function run( + Config $config, + ClientConfiguration $clientConfiguration, + string $base_dir, + bool $inMemory = false + ): void { + $progress = new Progress(); + + if ($inMemory) { + $providers = new Providers( + new FileProvider, + new InMemoryParserCacheProvider, + new InMemoryFileStorageCacheProvider, + new InMemoryClassLikeStorageCacheProvider, + new InMemoryFileReferenceCacheProvider($config), + new InMemoryProjectCacheProvider, + ); + } else { + $providers = new Providers( + new FileProvider, + new ParserCacheProvider($config), + new FileStorageCacheProvider($config), + new ClassLikeStorageCacheProvider($config), + new FileReferenceCacheProvider($config), + new ProjectCacheProvider(Composer::getLockFilePath($base_dir)), + ); + } + + $codebase = new Codebase( + $config, + $providers, + $progress, + ); + + if ($config->find_unused_variables) { + $codebase->reportUnusedVariables(); + } + + if ($clientConfiguration->findUnusedCode) { + $codebase->reportUnusedCode($clientConfiguration->findUnusedCode); + } + + $project_analyzer = new ProjectAnalyzer( + $config, + $providers, + null, + [], + 1, + $progress, + $codebase, + ); + + if ($clientConfiguration->onchangeLineLimit) { + $project_analyzer->onchange_line_limit = $clientConfiguration->onchangeLineLimit; + } + + //Setup Project Analyzer + $project_analyzer->provide_completion = (bool) $clientConfiguration->provideCompletion; + + @cli_set_process_title('Psalm ' . PSALM_VERSION . ' - PHP Language Server'); + + if (!$clientConfiguration->TCPServerMode && $clientConfiguration->TCPServerAddress) { + // Connect to a TCP server + $socket = stream_socket_client('tcp://' . $clientConfiguration->TCPServerAddress, $errno, $errstr); + if ($socket === false) { + fwrite(STDERR, "Could not connect to language client. Error $errno\n$errstr"); + exit(1); + } + stream_set_blocking($socket, false); + new self( + new ProtocolStreamReader($socket), + new ProtocolStreamWriter($socket), + $project_analyzer, + $codebase, + $clientConfiguration, + $progress, + ); + Loop::run(); + } elseif ($clientConfiguration->TCPServerMode && $clientConfiguration->TCPServerAddress) { + // Run a TCP Server + $tcpServer = stream_socket_server('tcp://' . $clientConfiguration->TCPServerAddress, $errno, $errstr); + if ($tcpServer === false) { + fwrite(STDERR, "Could not listen on {$clientConfiguration->TCPServerAddress}. Error $errno\n$errstr"); + exit(1); + } + fwrite(STDOUT, "Server listening on {$clientConfiguration->TCPServerAddress}\n"); + + while ($socket = stream_socket_accept($tcpServer, -1)) { + fwrite(STDOUT, "Connection accepted\n"); + stream_set_blocking($socket, false); + //we only accept one connection. + //An exit notification will terminate the server + new LanguageServer( + new ProtocolStreamReader($socket), + new ProtocolStreamWriter($socket), + $project_analyzer, + $codebase, + $clientConfiguration, + $progress, + ); + Loop::run(); + } + } else { + // Use STDIO + stream_set_blocking(STDIN, false); + new LanguageServer( + new ProtocolStreamReader(STDIN), + new ProtocolStreamWriter(STDOUT), + $project_analyzer, + $codebase, + $clientConfiguration, + $progress, + ); + Loop::run(); + } } /** * The initialize request is sent as the first request from the client to the server. * * @param ClientCapabilities $capabilities The capabilities provided by the client (editor) - * @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open. * @param int|null $processId The process Id of the parent process that started the server. * Is null if the process has not been started by another process. If the parent process is * not alive then the server should exit (see exit notification) its process. + * @param ClientInfo|null $clientInfo Information about the client + * @param string|null $locale The locale the client is currently showing the user interface + * in. This must not necessarily be the locale of the operating + * system. + * @param string|null $rootPath The rootPath of the workspace. Is null if no folder is open. + * @param mixed $initializationOptions + * @param string|null $trace The initial trace setting. If omitted trace is disabled ('off'). * @psalm-return Promise - * @psalm-suppress PossiblyUnusedMethod + * @psalm-suppress PossiblyUnusedParam */ public function initialize( ClientCapabilities $capabilities, + ?int $processId = null, + ?ClientInfo $clientInfo = null, + ?string $locale = null, ?string $rootPath = null, - ?int $processId = null + ?string $rootUri = null, + $initializationOptions = null, + ?string $trace = null + //?array $workspaceFolders = null //error in json-dispatcher ): Promise { + $this->clientInfo = $clientInfo; + $this->clientCapabilities = $capabilities; + $this->trace = $trace; return call( /** @return Generator */ - function (): Generator { - $this->verboseLog("Initializing..."); + function () { + $this->logInfo("Initializing..."); $this->clientStatus('initializing'); // Eventually, this might block on something. Leave it as a generator. @@ -213,22 +406,23 @@ class LanguageServer extends Dispatcher yield true; } - $this->verboseLog("Initializing: Getting code base..."); + $this->project_analyzer->serverMode($this); + + $this->logInfo("Initializing: Getting code base..."); $this->clientStatus('initializing', 'getting code base'); - $codebase = $this->project_analyzer->getCodebase(); - $this->verboseLog("Initializing: Scanning files..."); + $this->logInfo("Initializing: Scanning files ({$this->project_analyzer->threads} Threads)..."); $this->clientStatus('initializing', 'scanning files'); - $codebase->scanFiles($this->project_analyzer->threads); + $this->codebase->scanFiles($this->project_analyzer->threads); - $this->verboseLog("Initializing: Registering stub files..."); + $this->logInfo("Initializing: Registering stub files..."); $this->clientStatus('initializing', 'registering stub files'); - $codebase->config->visitStubFiles($codebase); + $this->codebase->config->visitStubFiles($this->codebase, $this->project_analyzer->progress); if ($this->textDocument === null) { $this->textDocument = new ServerTextDocument( $this, - $codebase, + $this->codebase, $this->project_analyzer, ); } @@ -236,199 +430,408 @@ class LanguageServer extends Dispatcher if ($this->workspace === null) { $this->workspace = new ServerWorkspace( $this, - $codebase, + $this->codebase, $this->project_analyzer, ); } $serverCapabilities = new ServerCapabilities(); + //The server provides execute command support. + $serverCapabilities->executeCommandProvider = new ExecuteCommandOptions(['test']); + $textDocumentSyncOptions = new TextDocumentSyncOptions(); + //Open and close notifications are sent to the server. $textDocumentSyncOptions->openClose = true; $saveOptions = new SaveOptions(); + //The client is supposed to include the content on save. $saveOptions->includeText = true; $textDocumentSyncOptions->save = $saveOptions; + /** + * Change notifications are sent to the server. See + * TextDocumentSyncKind.None, TextDocumentSyncKind.Full and + * TextDocumentSyncKind.Incremental. If omitted it defaults to + * TextDocumentSyncKind.None. + */ if ($this->project_analyzer->onchange_line_limit === 0) { + /** + * Documents should not be synced at all. + */ $textDocumentSyncOptions->change = TextDocumentSyncKind::NONE; } else { + /** + * Documents are synced by always sending the full content + * of the document. + */ $textDocumentSyncOptions->change = TextDocumentSyncKind::FULL; } + /** + * Defines how text documents are synced. Is either a detailed structure + * defining each notification or for backwards compatibility the + * TextDocumentSyncKind number. If omitted it defaults to + * `TextDocumentSyncKind.None`. + */ $serverCapabilities->textDocumentSync = $textDocumentSyncOptions; - // Support "Find all symbols" + /** + * The server provides document symbol support. + * Support "Find all symbols" + */ $serverCapabilities->documentSymbolProvider = false; - // Support "Find all symbols in workspace" + /** + * The server provides workspace symbol support. + * Support "Find all symbols in workspace" + */ $serverCapabilities->workspaceSymbolProvider = false; - // Support "Go to definition" + /** + * The server provides goto definition support. + * Support "Go to definition" + */ $serverCapabilities->definitionProvider = true; - // Support "Find all references" + /** + * The server provides find references support. + * Support "Find all references" + */ $serverCapabilities->referencesProvider = false; - // Support "Hover" + /** + * The server provides hover support. + * Support "Hover" + */ $serverCapabilities->hoverProvider = true; - // Support "Completion" - $serverCapabilities->codeActionProvider = true; - // Support "Code Actions" + /** + * The server provides completion support. + * Support "Completion" + */ if ($this->project_analyzer->provide_completion) { $serverCapabilities->completionProvider = new CompletionOptions(); + /** + * The server provides support to resolve additional + * information for a completion item. + */ $serverCapabilities->completionProvider->resolveProvider = false; + /** + * Most tools trigger completion request automatically without explicitly + * requesting it using a keyboard shortcut (e.g. Ctrl+Space). Typically they + * do so when the user starts to type an identifier. For example if the user + * types `c` in a JavaScript file code complete will automatically pop up + * present `console` besides others as a completion item. Characters that + * make up identifiers don't need to be listed here. + * + * If code complete should automatically be trigger on characters not being + * valid inside an identifier (for example `.` in JavaScript) list them in + * `triggerCharacters`. + */ $serverCapabilities->completionProvider->triggerCharacters = ['$', '>', ':',"[", "(", ",", " "]; } + /** + * Whether code action supports the `data` property which is + * preserved between a `textDocument/codeAction` and a + * `codeAction/resolve` request. + * + * Support "Code Actions" if we support data + * + * @since LSP 3.16.0 + */ + if ($this->clientCapabilities && + $this->clientCapabilities->textDocument && + $this->clientCapabilities->textDocument->publishDiagnostics && + $this->clientCapabilities->textDocument->publishDiagnostics->dataSupport + ) { + $serverCapabilities->codeActionProvider = true; + } + + /** + * The server provides signature help support. + */ $serverCapabilities->signatureHelpProvider = new SignatureHelpOptions(['(', ',']); - // Support global references - $serverCapabilities->xworkspaceReferencesProvider = false; - $serverCapabilities->xdefinitionProvider = false; - $serverCapabilities->dependenciesProvider = false; + if ($this->client->clientConfiguration->baseline !== null) { + $this->logInfo('Utilizing Baseline: '.$this->client->clientConfiguration->baseline); + $this->issue_baseline= ErrorBaseline::read( + new FileProvider, + $this->client->clientConfiguration->baseline, + ); + } - $this->verboseLog("Initializing: Complete."); + $this->logInfo("Initializing: Complete."); $this->clientStatus('initialized'); - return new InitializeResult($serverCapabilities); + + /** + * Information about the server. + * + * @since LSP 3.15.0 + */ + $initializeResultServerInfo = new InitializeResultServerInfo('Psalm Language Server', PSALM_VERSION); + + return new InitializeResult($serverCapabilities, $initializeResultServerInfo); }, ); } /** - * @psalm-suppress PossiblyUnusedMethod + * The initialized notification is sent from the client to the server after the client received the result of the + * initialize request but before the client is sending any other request or notification to the server. + * The server can use the initialized notification for example to dynamically register capabilities. + * The initialized notification may only be sent once. */ public function initialized(): void { + try { + $this->client->refreshConfiguration(); + } catch (Throwable $e) { + $this->logError((string) $e); + } $this->clientStatus('running'); } - public function queueTemporaryFileAnalysis(string $file_path, string $uri): void - { - $this->onchange_paths_to_analyze[$file_path] = $uri; - } - - public function queueFileAnalysis(string $file_path, string $uri): void - { - $this->onsave_paths_to_analyze[$file_path] = $uri; - } - - public function doAnalysis(): void - { - $this->clientStatus('analyzing'); - - try { - $codebase = $this->project_analyzer->getCodebase(); - - $all_files_to_analyze = $this->onchange_paths_to_analyze + $this->onsave_paths_to_analyze; - - if (!$all_files_to_analyze) { - return; - } - - if ($this->onsave_paths_to_analyze) { - $codebase->reloadFiles($this->project_analyzer, array_keys($this->onsave_paths_to_analyze)); - } - - if ($this->onchange_paths_to_analyze) { - $codebase->reloadFiles($this->project_analyzer, array_keys($this->onchange_paths_to_analyze)); - } - - $all_file_paths_to_analyze = array_keys($all_files_to_analyze); - $codebase->analyzer->addFilesToAnalyze( - array_combine($all_file_paths_to_analyze, $all_file_paths_to_analyze), - ); - $codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false); - - $this->emitIssues($all_files_to_analyze); - - $this->onchange_paths_to_analyze = []; - $this->onsave_paths_to_analyze = []; - } finally { - // we are done, so set the status back to running - $this->clientStatus('running'); - } - } - /** - * @param array $uris + * Queue Change File Analysis */ - public function emitIssues(array $uris): void + public function queueChangeFileAnalysis(string $file_path, string $uri, ?int $version = null): void { - $data = IssueBuffer::clear(); - $this->current_issues = $data; + $this->doVersionedAnalysisDebounce([$file_path => $uri], $version); + } - foreach ($uris as $file_path => $uri) { - $diagnostics = []; + /** + * Queue Open File Analysis + */ + public function queueOpenFileAnalysis(string $file_path, string $uri, ?int $version = null): void + { + $this->doVersionedAnalysis([$file_path => $uri], $version); + } - foreach (($data[$file_path] ?? []) as $issue_data) { - //$check_name = $issue->check_name; - $description = $issue_data->message; - $severity = $issue_data->severity; + /** + * Queue Closed File Analysis + */ + public function queueClosedFileAnalysis(string $file_path, string $uri): void + { + $this->doVersionedAnalysis([$file_path => $uri]); + } - $start_line = max($issue_data->line_from, 1); - $end_line = $issue_data->line_to; - $start_column = $issue_data->column_from; - $end_column = $issue_data->column_to; - // Language server has 0 based lines and columns, phan has 1-based lines and columns. - $range = new Range( - new Position($start_line - 1, $start_column - 1), - new Position($end_line - 1, $end_column - 1), - ); - switch ($severity) { - case Config::REPORT_INFO: - $diagnostic_severity = DiagnosticSeverity::WARNING; - break; - case Config::REPORT_ERROR: - default: - $diagnostic_severity = DiagnosticSeverity::ERROR; - break; - } - $diagnostic = new Diagnostic( - $description, - $range, - null, - $diagnostic_severity, - 'Psalm', - ); + /** + * Queue Saved File Analysis + */ + public function queueSaveFileAnalysis(string $file_path, string $uri): void + { + $this->queueFileAnalysisWithOpenedFiles([$file_path => $uri]); + } - //$code = 'PS' . \str_pad((string) $issue_data->shortcode, 3, "0", \STR_PAD_LEFT); - $code = $issue_data->link; + /** + * Queue File Analysis appending any opened files + * + * This allows for reanalysis of files that have been opened + * + * @param array $files + */ + public function queueFileAnalysisWithOpenedFiles(array $files = []): void + { + /** @var array $opened */ + $opened = array_reduce( + $this->project_analyzer->getCodebase()->file_provider->getOpenFilesPath(), + function (array $opened, string $file_path) { + $opened[$file_path] = $this->pathToUri($file_path); + return $opened; + }, + $files, + ); - if ($this->project_analyzer->language_server_use_extended_diagnostic_codes) { - // Added in VSCode 1.43.0 and will be part of the LSP 3.16.0 standard. - // Since this new functionality is not backwards compatible, we use a - // configuration option so the end user must opt in to it using the cli argument. - // https://github.com/microsoft/vscode/blob/1.43.0/src/vs/vscode.d.ts#L4688-L4699 + $this->doVersionedAnalysis($opened); + } - /** @psalm-suppress InvalidPropertyAssignmentValue */ - $diagnostic->code = [ - "value" => $code, - "target" => $issue_data->link, - ]; - } else { - // the Diagnostic constructor only takes `int` for the code, but the property can be - // `int` or `string`, so we set the property directly because we want to use a `string` - /** @psalm-suppress InvalidPropertyAssignmentValue */ - $diagnostic->code = $code; - } - - $diagnostics[] = $diagnostic; - } - - $this->client->textDocument->publishDiagnostics($uri, $diagnostics); + /** + * Debounced Queue File Analysis with optional version + * + * @param array $files + */ + public function doVersionedAnalysisDebounce(array $files, ?int $version = null): void + { + Loop::cancel($this->versionedAnalysisDelayToken); + if ($this->client->clientConfiguration->onChangeDebounceMs === null) { + $this->doVersionedAnalysis($files, $version); + } else { + /** @psalm-suppress MixedAssignment,UnusedPsalmSuppress */ + $this->versionedAnalysisDelayToken = Loop::delay( + $this->client->clientConfiguration->onChangeDebounceMs, + fn() => $this->doVersionedAnalysis($files, $version), + ); } } /** - * The shutdown request is sent from the client to the server. It asks the server to shut down, - * but to not exit (otherwise the response might not be delivered correctly to the client). - * There is a separate exit notification that asks the server to exit. + * Queue File Analysis with optional version * - * @psalm-suppress PossiblyUnusedReturnValue + * @param array $files + */ + public function doVersionedAnalysis(array $files, ?int $version = null): void + { + Loop::cancel($this->versionedAnalysisDelayToken); + try { + $this->logDebug("Doing Analysis from version: $version"); + $this->codebase->reloadFiles( + $this->project_analyzer, + array_keys($files), + ); + + $this->codebase->analyzer->addFilesToAnalyze( + array_combine(array_keys($files), array_keys($files)), + ); + + $this->logDebug("Reloading Files"); + $this->codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false); + + $this->emitVersionedIssues($files, $version); + } catch (Throwable $e) { + $this->logError((string) $e); + } + } + + /** + * Emit Publish Diagnostics + * + * @param array $files + */ + public function emitVersionedIssues(array $files, ?int $version = null): void + { + $this->logDebug("Perform Analysis", [ + 'files' => array_keys($files), + 'version' => $version, + ]); + + //Copy variable here to be able to process it + $issue_baseline = $this->issue_baseline; + + $data = IssueBuffer::clear(); + foreach ($files as $file_path => $uri) { + //Dont report errors in files we are not watching + if (!$this->project_analyzer->getCodebase()->config->isInProjectDirs($file_path)) { + continue; + } + $diagnostics = array_map( + function (IssueData $issue_data): Diagnostic { + //$check_name = $issue->check_name; + $description = $issue_data->message; + $severity = $issue_data->severity; + + $start_line = max($issue_data->line_from, 1); + $end_line = $issue_data->line_to; + $start_column = $issue_data->column_from; + $end_column = $issue_data->column_to; + // Language server has 0 based lines and columns, phan has 1-based lines and columns. + $range = new Range( + new Position($start_line - 1, $start_column - 1), + new Position($end_line - 1, $end_column - 1), + ); + switch ($severity) { + case Config::REPORT_INFO: + $diagnostic_severity = DiagnosticSeverity::WARNING; + break; + case Config::REPORT_ERROR: + default: + $diagnostic_severity = DiagnosticSeverity::ERROR; + break; + } + $diagnostic = new Diagnostic( + $description, + $range, + null, + $diagnostic_severity, + 'psalm', + ); + + $diagnostic->data = [ + 'type' => $issue_data->type, + 'snippet' => $issue_data->snippet, + 'line_from' => $issue_data->line_from, + 'line_to' => $issue_data->line_to, + ]; + + $diagnostic->code = $issue_data->shortcode; + + /** + * Client supports a codeDescription property + * + * @since LSP 3.16.0 + */ + if ($this->clientCapabilities !== null && + $this->clientCapabilities->textDocument && + $this->clientCapabilities->textDocument->publishDiagnostics && + $this->clientCapabilities->textDocument->publishDiagnostics->codeDescriptionSupport + ) { + $diagnostic->codeDescription = new CodeDescription($issue_data->link); + } + + return $diagnostic; + }, + array_filter( + array_map(function (IssueData $issue_data) use (&$issue_baseline) { + if (empty($issue_baseline)) { + return $issue_data; + } + //Process Baseline + $file = $issue_data->file_name; + $type = $issue_data->type; + /** @psalm-suppress MixedArrayAccess */ + if (isset($issue_baseline[$file][$type]) && $issue_baseline[$file][$type]['o'] > 0) { + /** @psalm-suppress MixedArrayAccess, MixedArgument */ + if ($issue_baseline[$file][$type]['o'] === count($issue_baseline[$file][$type]['s'])) { + /** @psalm-suppress MixedArrayAccess, MixedAssignment */ + $position = array_search( + str_replace("\r\n", "\n", trim($issue_data->selected_text)), + $issue_baseline[$file][$type]['s'], + true, + ); + + if ($position !== false) { + $issue_data->severity = Config::REPORT_INFO; + /** @psalm-suppress MixedArgument */ + array_splice($issue_baseline[$file][$type]['s'], $position, 1); + /** @psalm-suppress MixedArrayAssignment, MixedOperand, MixedAssignment */ + $issue_baseline[$file][$type]['o']--; + } + } else { + /** @psalm-suppress MixedArrayAssignment */ + $issue_baseline[$file][$type]['s'] = []; + $issue_data->severity = Config::REPORT_INFO; + /** @psalm-suppress MixedArrayAssignment, MixedOperand, MixedAssignment */ + $issue_baseline[$file][$type]['o']--; + } + } + return $issue_data; + }, $data[$file_path] ?? []), + function (IssueData $issue_data) { + //Hide Warnings + if ($issue_data->severity === Config::REPORT_INFO && + $this->client->clientConfiguration->hideWarnings + ) { + return false; + } + + return true; + }, + ), + ); + + $this->client->textDocument->publishDiagnostics($uri, array_values($diagnostics), $version); + } + } + + /** + * The shutdown request is sent from the client to the server. It asks the server to shut down, but to not exit + * (otherwise the response might not be delivered correctly to the client). There is a separate exit notification + * that asks the server to exit. Clients must not send any notifications other than exit or requests to a server to + * which they have sent a shutdown request. Clients should also wait with sending the exit notification until they + * have received a response from the shutdown request. */ public function shutdown(): Promise { $this->clientStatus('closing'); - $this->verboseLog("Shutting down..."); + $this->logInfo("Shutting down..."); $codebase = $this->project_analyzer->getCodebase(); $scanned_files = $codebase->scanner->getScannedFiles(); $codebase->file_reference_provider->updateReferenceCache( @@ -441,6 +844,8 @@ class LanguageServer extends Dispatcher /** * A notification to ask the server to exit its process. + * The server should exit with success code 0 if the shutdown request has been received before; + * otherwise with error code 1. */ public function exit(): void { @@ -451,31 +856,85 @@ class LanguageServer extends Dispatcher /** * Send log message to the client * - * @param string $message The log message to send to the client. * @psalm-param 1|2|3|4 $type * @param int $type The log type: * - 1 = Error * - 2 = Warning * - 3 = Info * - 4 = Log + * @see MessageType + * @param string $message The log message to send to the client. + * @param mixed[] $context The log context */ - public function verboseLog(string $message, int $type = 4): void + public function log(int $type, string $message, array $context = []): void { - if ($this->project_analyzer->language_server_verbose) { - try { - $this->client->logMessage( - '[Psalm ' .PSALM_VERSION. ' - PHP Language Server] ' . $message, - $type, - ); - } catch (Throwable $err) { - // do nothing - } + $logLevel = $this->client->clientConfiguration->logLevel; + if ($logLevel === null) { + return; + } + + if ($type > $logLevel) { + return; + } + + if (!empty($context)) { + $message .= "\n" . json_encode($context, JSON_PRETTY_PRINT); + } + try { + $this->client->logMessage( + new LogMessage( + $type, + $message, + ), + ); + } catch (Throwable $err) { + // do nothing as we could potentially go into a loop here is not careful + //TODO: Investigate if we can use error_log instead } - new Success(null); } /** - * Send status message to client. This is the same as sending a log message, + * Log Throwable Error + */ + public function logThrowable(Throwable $throwable): void + { + $this->log(MessageType::ERROR, (string) $throwable); + } + + /** + * Log Error message to the client + */ + public function logError(string $message, array $context = []): void + { + $this->log(MessageType::ERROR, $message, $context); + } + + /** + * Log Warning message to the client + */ + public function logWarning(string $message, array $context = []): void + { + $this->log(MessageType::WARNING, $message, $context); + } + + /** + * Log Info message to the client + */ + public function logInfo(string $message, array $context = []): void + { + $this->log(MessageType::INFO, $message, $context); + } + + /** + * Log Debug message to the client + */ + public function logDebug(string $message, array $context = []): void + { + $this->log(MessageType::LOG, $message, $context); + } + + /** + * Send status message to client. This is the same as sending a log message, * except this is meant for parsing by the client to present status updates in a UI. * * @param string $status The log message to send to the client. Should not contain colons `:`. @@ -485,16 +944,15 @@ class LanguageServer extends Dispatcher private function clientStatus(string $status, ?string $additional_info = null): void { try { - // here we send a notification to the client using the telemetry notification method - $this->client->logMessage( - $status . (!empty($additional_info) ? ': ' . $additional_info : ''), - 3, - 'telemetry/event', + $this->client->event( + new LogMessage( + MessageType::INFO, + $status . (!empty($additional_info) ? ': ' . $additional_info : ''), + ), ); } catch (Throwable $err) { // do nothing } - new Success(null); } /** @@ -548,14 +1006,4 @@ class LanguageServer extends Dispatcher return $filepath; } - - /** - * Get the value of current_issues - * - * @return array> - */ - public function getCurrentIssues(): array - { - return $this->current_issues; - } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php index ba1e73f9..56b46c23 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Message.php @@ -24,8 +24,6 @@ class Message /** * Parses a message - * - * @psalm-suppress UnusedMethod */ public static function parse(string $msg): Message { @@ -35,7 +33,9 @@ class Message foreach ($parts as $line) { if ($line) { $pair = explode(': ', $line); - $obj->headers[$pair[0]] = $pair[1]; + if (isset($pair[1])) { + $obj->headers[$pair[0]] = $pair[1]; + } } } @@ -56,6 +56,7 @@ class Message public function __toString(): string { + $body = (string)$this->body; $contentLength = strlen($body); $this->headers['Content-Length'] = (string) $contentLength; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/PHPMarkdownContent.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/PHPMarkdownContent.php new file mode 100644 index 00000000..3290ea5c --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/PHPMarkdownContent.php @@ -0,0 +1,58 @@ +code = $code; + $this->title = $title; + $this->description = $description; + + $markdown = ''; + if ($title !== null) { + $markdown = "**$title**\n\n"; + } + if ($description !== null) { + $markdown = "$markdown$description\n\n"; + } + parent::__construct( + MarkupKind::MARKDOWN, + "$markdown```php\nserver = $server; + } + + public function debug(string $message): void + { + if ($this->server) { + $this->server->logDebug(str_replace("\n", "", $message)); + } + } + + public function write(string $message): void + { + if ($this->server) { + $this->server->logInfo(str_replace("\n", "", $message)); + } + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php index 66ef9af9..35031d85 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/ProtocolStreamReader.php @@ -91,7 +91,9 @@ class ProtocolStreamReader implements ProtocolReader $this->buffer = ''; } elseif (substr($this->buffer, -2) === "\r\n") { $parts = explode(':', $this->buffer); - $this->headers[$parts[0]] = trim($parts[1]); + if (isset($parts[1])) { + $this->headers[$parts[0]] = trim($parts[1]); + } $this->buffer = ''; } break; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ClassLikeStorageCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ClassLikeStorageCacheProvider.php new file mode 100644 index 00000000..f1925fe7 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ClassLikeStorageCacheProvider.php @@ -0,0 +1,47 @@ + */ + private array $cache = []; + + public function __construct() + { + } + + public function writeToCache(ClassLikeStorage $storage, ?string $file_path, ?string $file_contents): void + { + $fq_classlike_name_lc = strtolower($storage->name); + $this->cache[$fq_classlike_name_lc] = $storage; + } + + public function getLatestFromCache( + string $fq_classlike_name_lc, + ?string $file_path, + ?string $file_contents + ): ClassLikeStorage { + $cached_value = $this->loadFromCache($fq_classlike_name_lc); + + if (!$cached_value) { + throw new UnexpectedValueException('Should be in cache'); + } + + return $cached_value; + } + + private function loadFromCache(string $fq_classlike_name_lc): ?ClassLikeStorage + { + return $this->cache[$fq_classlike_name_lc] ?? null; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/FileReferenceCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/FileReferenceCacheProvider.php new file mode 100644 index 00000000..79d54534 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/FileReferenceCacheProvider.php @@ -0,0 +1,280 @@ +> */ + private array $cached_correct_methods = []; + + /** + * @var array< + * string, + * array{ + * 0: array, + * 1: array, + * 2: array + * } + * > + */ + private array $cached_file_maps = []; + + public function __construct(Config $config) + { + $this->config = $config; + } + + public function getCachedFileReferences(): ?array + { + return $this->cached_file_references; + } + + public function getCachedClassLikeFiles(): ?array + { + return $this->cached_classlike_files; + } + + public function getCachedMethodClassReferences(): ?array + { + return $this->cached_method_class_references; + } + + public function getCachedNonMethodClassReferences(): ?array + { + return $this->cached_nonmethod_class_references; + } + + public function getCachedFileMemberReferences(): ?array + { + return $this->cached_file_member_references; + } + + public function getCachedFilePropertyReferences(): ?array + { + return $this->cached_file_property_references; + } + + public function getCachedFileMethodReturnReferences(): ?array + { + return $this->cached_file_method_return_references; + } + + public function getCachedMethodMemberReferences(): ?array + { + return $this->cached_method_member_references; + } + + public function getCachedMethodDependencies(): ?array + { + return $this->cached_method_dependencies; + } + + public function getCachedMethodPropertyReferences(): ?array + { + return $this->cached_method_property_references; + } + + public function getCachedMethodMethodReturnReferences(): ?array + { + return $this->cached_method_method_return_references; + } + + public function getCachedFileMissingMemberReferences(): ?array + { + return $this->cached_file_missing_member_references; + } + + public function getCachedMixedMemberNameReferences(): ?array + { + return $this->cached_unknown_member_references; + } + + public function getCachedMethodMissingMemberReferences(): ?array + { + return $this->cached_method_missing_member_references; + } + + public function getCachedMethodParamUses(): ?array + { + return $this->cached_method_param_uses; + } + + public function getCachedIssues(): ?array + { + return $this->cached_issues; + } + + public function setCachedFileReferences(array $file_references): void + { + $this->cached_file_references = $file_references; + } + + public function setCachedClassLikeFiles(array $file_references): void + { + $this->cached_classlike_files = $file_references; + } + + public function setCachedMethodClassReferences(array $method_class_references): void + { + $this->cached_method_class_references = $method_class_references; + } + + public function setCachedNonMethodClassReferences(array $file_class_references): void + { + $this->cached_nonmethod_class_references = $file_class_references; + } + + public function setCachedMethodMemberReferences(array $member_references): void + { + $this->cached_method_member_references = $member_references; + } + + public function setCachedMethodDependencies(array $member_references): void + { + $this->cached_method_dependencies = $member_references; + } + + public function setCachedMethodPropertyReferences(array $property_references): void + { + $this->cached_method_property_references = $property_references; + } + + public function setCachedMethodMethodReturnReferences(array $method_return_references): void + { + $this->cached_method_method_return_references = $method_return_references; + } + + public function setCachedMethodMissingMemberReferences(array $member_references): void + { + $this->cached_method_missing_member_references = $member_references; + } + + public function setCachedFileMemberReferences(array $member_references): void + { + $this->cached_file_member_references = $member_references; + } + + public function setCachedFilePropertyReferences(array $property_references): void + { + $this->cached_file_property_references = $property_references; + } + + public function setCachedFileMethodReturnReferences(array $method_return_references): void + { + $this->cached_file_method_return_references = $method_return_references; + } + + public function setCachedFileMissingMemberReferences(array $member_references): void + { + $this->cached_file_missing_member_references = $member_references; + } + + public function setCachedMixedMemberNameReferences(array $references): void + { + $this->cached_unknown_member_references = $references; + } + + public function setCachedMethodParamUses(array $uses): void + { + $this->cached_method_param_uses = $uses; + } + + public function setCachedIssues(array $issues): void + { + $this->cached_issues = $issues; + } + + /** + * @return array> + */ + public function getAnalyzedMethodCache(): array + { + return $this->cached_correct_methods; + } + + /** + * @param array> $analyzed_methods + */ + public function setAnalyzedMethodCache(array $analyzed_methods): void + { + $this->cached_correct_methods = $analyzed_methods; + } + + /** + * @return array< + * string, + * array{ + * 0: array, + * 1: array, + * 2: array + * } + * > + */ + public function getFileMapCache(): array + { + return $this->cached_file_maps; + } + + /** + * @param array< + * string, + * array{ + * 0: array, + * 1: array, + * 2: array + * } + * > $file_maps + */ + public function setFileMapCache(array $file_maps): void + { + $this->cached_file_maps = $file_maps; + } + + /** + * @param array $mixed_counts + */ + public function setTypeCoverage(array $mixed_counts): void + { + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/FileStorageCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/FileStorageCacheProvider.php new file mode 100644 index 00000000..c91ffcc1 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/FileStorageCacheProvider.php @@ -0,0 +1,48 @@ + */ + private array $cache = []; + + public function __construct() + { + } + + public function writeToCache(FileStorage $storage, string $file_contents): void + { + $file_path = strtolower($storage->file_path); + $this->cache[$file_path] = $storage; + } + + public function getLatestFromCache(string $file_path, string $file_contents): ?FileStorage + { + $cached_value = $this->loadFromCache(strtolower($file_path)); + + if (!$cached_value) { + return null; + } + + return $cached_value; + } + + public function removeCacheForFile(string $file_path): void + { + unset($this->cache[strtolower($file_path)]); + } + + private function loadFromCache(string $file_path): ?FileStorage + { + return $this->cache[strtolower($file_path)] ?? null; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ParserCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ParserCacheProvider.php new file mode 100644 index 00000000..0b30a2dd --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ParserCacheProvider.php @@ -0,0 +1,109 @@ + + */ + private array $file_contents_cache = []; + + /** + * @var array + */ + private array $file_content_hash = []; + + /** + * @var array> + */ + private array $statements_cache = []; + + /** + * @var array + */ + private array $statements_cache_time = []; + + public function __construct() + { + } + + public function loadStatementsFromCache( + string $file_path, + int $file_modified_time, + string $file_content_hash + ): ?array { + if (isset($this->statements_cache[$file_path]) + && $this->statements_cache_time[$file_path] >= $file_modified_time + && $this->file_content_hash[$file_path] === $file_content_hash + ) { + return $this->statements_cache[$file_path]; + } + + return null; + } + + /** + * @return list|null + */ + public function loadExistingStatementsFromCache(string $file_path): ?array + { + if (isset($this->statements_cache[$file_path])) { + return $this->statements_cache[$file_path]; + } + + return null; + } + + /** + * @param list $stmts + */ + public function saveStatementsToCache( + string $file_path, + string $file_content_hash, + array $stmts, + bool $touch_only + ): void { + $this->statements_cache[$file_path] = $stmts; + $this->statements_cache_time[$file_path] = microtime(true); + $this->file_content_hash[$file_path] = $file_content_hash; + } + + public function loadExistingFileContentsFromCache(string $file_path): ?string + { + if (isset($this->file_contents_cache[$file_path])) { + return $this->file_contents_cache[$file_path]; + } + + return null; + } + + public function cacheFileContents(string $file_path, string $file_contents): void + { + $this->file_contents_cache[$file_path] = $file_contents; + } + + public function deleteOldParserCaches(float $time_before): int + { + $this->existing_file_content_hashes = null; + $this->new_file_content_hashes = []; + + $this->file_contents_cache = []; + $this->file_content_hash = []; + $this->statements_cache = []; + $this->statements_cache_time = []; + return 0; + } + + public function saveFileContentHashes(): void + { + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ProjectCacheProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ProjectCacheProvider.php new file mode 100644 index 00000000..ed210fa0 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Provider/ProjectCacheProvider.php @@ -0,0 +1,41 @@ +last_run; + } + + public function processSuccessfulRun(float $start_time, string $psalm_version): void + { + $this->last_run = (int) $start_time; + } + + public function canDiffFiles(): bool + { + return $this->last_run > 0; + } + + public function hasLockfileChanged(): bool + { + return false; + } + + public function updateComposerLockHash(): void + { + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Reference.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Reference.php new file mode 100644 index 00000000..32694c7c --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Reference.php @@ -0,0 +1,22 @@ +file_path = $file_path; + $this->symbol = $symbol; + $this->range = $range; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php index 87d4c650..b9c4bcca 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/TextDocument.php @@ -6,11 +6,12 @@ namespace Psalm\Internal\LanguageServer\Server; use Amp\Promise; use Amp\Success; +use LanguageServerProtocol\CodeAction; +use LanguageServerProtocol\CodeActionContext; +use LanguageServerProtocol\CodeActionKind; use LanguageServerProtocol\CompletionList; use LanguageServerProtocol\Hover; use LanguageServerProtocol\Location; -use LanguageServerProtocol\MarkupContent; -use LanguageServerProtocol\MarkupKind; use LanguageServerProtocol\Position; use LanguageServerProtocol\Range; use LanguageServerProtocol\SignatureHelp; @@ -21,6 +22,7 @@ use LanguageServerProtocol\TextEdit; use LanguageServerProtocol\VersionedTextDocumentIdentifier; use LanguageServerProtocol\WorkspaceEdit; use Psalm\Codebase; +use Psalm\Exception\TypeParseTreeException; use Psalm\Exception\UnanalyzedFileException; use Psalm\Internal\Analyzer\ProjectAnalyzer; use Psalm\Internal\LanguageServer\LanguageServer; @@ -28,7 +30,6 @@ use UnexpectedValueException; use function array_values; use function count; -use function error_log; use function preg_match; use function substr_count; @@ -68,36 +69,41 @@ class TextDocument */ public function didOpen(TextDocumentItem $textDocument): void { + $this->server->logDebug( + 'textDocument/didOpen', + ['version' => $textDocument->version, 'uri' => $textDocument->uri], + ); + $file_path = LanguageServer::uriToPath($textDocument->uri); - if (!$this->codebase->config->isInProjectDirs($file_path)) { - return; - } - + $this->codebase->removeTemporaryFileChanges($file_path); $this->codebase->file_provider->openFile($file_path); + $this->codebase->file_provider->setOpenContents($file_path, $textDocument->text); - $this->server->queueFileAnalysis($file_path, $textDocument->uri); + $this->server->queueOpenFileAnalysis($file_path, $textDocument->uri, $textDocument->version); } /** * The document save notification is sent from the client to the server when the document was saved in the client * - * @param TextDocumentItem $textDocument the document that was opened - * @param ?string $text the content when saved + * @param TextDocumentIdentifier $textDocument the document that was opened + * @param string|null $text Optional the content when saved. Depends on the includeText value + * when the save notification was requested. */ - public function didSave(TextDocumentItem $textDocument, ?string $text): void + public function didSave(TextDocumentIdentifier $textDocument, ?string $text = null): void { - $file_path = LanguageServer::uriToPath($textDocument->uri); + $this->server->logDebug( + 'textDocument/didSave', + ['uri' => (array) $textDocument], + ); - if (!$this->codebase->config->isInProjectDirs($file_path)) { - return; - } + $file_path = LanguageServer::uriToPath($textDocument->uri); // reopen file $this->codebase->removeTemporaryFileChanges($file_path); - $this->codebase->file_provider->setOpenContents($file_path, (string) $text); + $this->codebase->file_provider->setOpenContents($file_path, $text); - $this->server->queueFileAnalysis($file_path, $textDocument->uri); + $this->server->queueSaveFileAnalysis($file_path, $textDocument->uri); } /** @@ -108,13 +114,14 @@ class TextDocument */ public function didChange(VersionedTextDocumentIdentifier $textDocument, array $contentChanges): void { + $this->server->logDebug( + 'textDocument/didChange', + ['version' => $textDocument->version, 'uri' => $textDocument->uri], + ); + $file_path = LanguageServer::uriToPath($textDocument->uri); - if (!$this->codebase->config->isInProjectDirs($file_path)) { - return; - } - - if (count($contentChanges) === 1 && $contentChanges[0]->range === null) { + if (count($contentChanges) === 1 && isset($contentChanges[0]) && $contentChanges[0]->range === null) { $new_content = $contentChanges[0]->text; } else { throw new UnexpectedValueException('Not expecting partial diff'); @@ -126,8 +133,8 @@ class TextDocument } } - $this->codebase->addTemporaryFileChanges($file_path, $new_content); - $this->server->queueTemporaryFileAnalysis($file_path, $textDocument->uri); + $this->codebase->addTemporaryFileChanges($file_path, $new_content, $textDocument->version); + $this->server->queueChangeFileAnalysis($file_path, $textDocument->uri, $textDocument->version); } /** @@ -142,6 +149,11 @@ class TextDocument */ public function didClose(TextDocumentIdentifier $textDocument): void { + $this->server->logDebug( + 'textDocument/didClose', + ['uri' => $textDocument->uri], + ); + $file_path = LanguageServer::uriToPath($textDocument->uri); $this->codebase->file_provider->closeFile($file_path); @@ -158,24 +170,34 @@ class TextDocument */ public function definition(TextDocumentIdentifier $textDocument, Position $position): Promise { + if (!$this->server->client->clientConfiguration->provideDefinition) { + return new Success(null); + } + + $this->server->logDebug( + 'textDocument/definition', + ); + $file_path = LanguageServer::uriToPath($textDocument->uri); + //This currently doesnt work right with out of project files + if (!$this->codebase->config->isInProjectDirs($file_path)) { + return new Success(null); + } + try { - $reference_location = $this->codebase->getReferenceAtPosition($file_path, $position); + $reference = $this->codebase->getReferenceAtPositionAsReference($file_path, $position); } catch (UnanalyzedFileException $e) { - $this->codebase->file_provider->openFile($file_path); - $this->server->queueFileAnalysis($file_path, $textDocument->uri); - + $this->server->logThrowable($e); return new Success(null); } - if ($reference_location === null) { + if ($reference === null) { return new Success(null); } - [$reference] = $reference_location; - $code_location = $this->codebase->getSymbolLocation($file_path, $reference); + $code_location = $this->codebase->getSymbolLocationByReference($reference); if (!$code_location) { return new Success(null); @@ -202,39 +224,44 @@ class TextDocument */ public function hover(TextDocumentIdentifier $textDocument, Position $position): Promise { - $file_path = LanguageServer::uriToPath($textDocument->uri); - - try { - $reference_location = $this->codebase->getReferenceAtPosition($file_path, $position); - } catch (UnanalyzedFileException $e) { - $this->codebase->file_provider->openFile($file_path); - $this->server->queueFileAnalysis($file_path, $textDocument->uri); - + if (!$this->server->client->clientConfiguration->provideHover) { return new Success(null); } - if ($reference_location === null) { - return new Success(null); - } - - [$reference, $range] = $reference_location; - - $symbol_information = $this->codebase->getSymbolInformation($file_path, $reference); - - if ($symbol_information === null) { - return new Success(null); - } - - $content = "```php\n" . $symbol_information['type'] . "\n```"; - if (isset($symbol_information['description'])) { - $content .= "\n---\n" . $symbol_information['description']; - } - $contents = new MarkupContent( - MarkupKind::MARKDOWN, - $content, + $this->server->logDebug( + 'textDocument/hover', ); - return new Success(new Hover($contents, $range)); + $file_path = LanguageServer::uriToPath($textDocument->uri); + + //This currently doesnt work right with out of project files + if (!$this->codebase->config->isInProjectDirs($file_path)) { + return new Success(null); + } + + try { + $reference = $this->codebase->getReferenceAtPositionAsReference($file_path, $position); + } catch (UnanalyzedFileException $e) { + $this->server->logThrowable($e); + return new Success(null); + } + + if ($reference === null) { + return new Success(null); + } + + try { + $markup = $this->codebase->getMarkupContentForSymbolByReference($reference); + } catch (UnexpectedValueException $e) { + $this->server->logThrowable($e); + return new Success(null); + } + + if ($markup === null) { + return new Success(null); + } + + return new Success(new Hover($markup, $reference->range)); } /** @@ -249,56 +276,74 @@ class TextDocument * * @param TextDocumentIdentifier $textDocument The text document * @param Position $position The position - * @psalm-return Promise>|Promise + * @psalm-return Promise>|Promise|Promise */ public function completion(TextDocumentIdentifier $textDocument, Position $position): Promise { + if (!$this->server->client->clientConfiguration->provideCompletion) { + return new Success(null); + } + + $this->server->logDebug( + 'textDocument/completion', + ); + $file_path = LanguageServer::uriToPath($textDocument->uri); + + //This currently doesnt work right with out of project files if (!$this->codebase->config->isInProjectDirs($file_path)) { - return new Success([]); + return new Success(null); } try { $completion_data = $this->codebase->getCompletionDataAtPosition($file_path, $position); - } catch (UnanalyzedFileException $e) { - $this->codebase->file_provider->openFile($file_path); - $this->server->queueFileAnalysis($file_path, $textDocument->uri); + if ($completion_data) { + [$recent_type, $gap, $offset] = $completion_data; - return new Success([]); + if ($gap === '->' || $gap === '::') { + $snippetSupport = ($this->server->clientCapabilities && + $this->server->clientCapabilities->textDocument && + $this->server->clientCapabilities->textDocument->completion && + $this->server->clientCapabilities->textDocument->completion->completionItem && + $this->server->clientCapabilities->textDocument->completion->completionItem->snippetSupport) + ? true : false; + $completion_items = + $this->codebase->getCompletionItemsForClassishThing($recent_type, $gap, $snippetSupport); + } elseif ($gap === '[') { + $completion_items = $this->codebase->getCompletionItemsForArrayKeys($recent_type); + } else { + $completion_items = $this->codebase->getCompletionItemsForPartialSymbol( + $recent_type, + $offset, + $file_path, + ); + } + return new Success(new CompletionList($completion_items, false)); + } + } catch (UnanalyzedFileException $e) { + $this->server->logThrowable($e); + return new Success(null); + } catch (TypeParseTreeException $e) { + $this->server->logThrowable($e); + return new Success(null); } try { $type_context = $this->codebase->getTypeContextAtPosition($file_path, $position); - } catch (UnexpectedValueException $e) { - error_log('completion errored at ' . $position->line . ':' . $position->character. - ', Reason: '.$e->getMessage()); - return new Success([]); - } - - if (!$completion_data && !$type_context) { - error_log('completion not found at ' . $position->line . ':' . $position->character); - return new Success([]); - } - - if ($completion_data) { - [$recent_type, $gap, $offset] = $completion_data; - - if ($gap === '->' || $gap === '::') { - $completion_items = $this->codebase->getCompletionItemsForClassishThing($recent_type, $gap); - } elseif ($gap === '[') { - $completion_items = $this->codebase->getCompletionItemsForArrayKeys($recent_type); - } else { - $completion_items = $this->codebase->getCompletionItemsForPartialSymbol( - $recent_type, - $offset, - $file_path, - ); + if ($type_context) { + $completion_items = $this->codebase->getCompletionItemsForType($type_context); + return new Success(new CompletionList($completion_items, false)); } - } else { - $completion_items = $this->codebase->getCompletionItemsForType($type_context); + } catch (UnexpectedValueException $e) { + $this->server->logThrowable($e); + return new Success(null); + } catch (TypeParseTreeException $e) { + $this->server->logThrowable($e); + return new Success(null); } - return new Success(new CompletionList($completion_items, false)); + $this->server->logError('completion not found at ' . $position->line . ':' . $position->character); + return new Success(null); } /** @@ -307,96 +352,134 @@ class TextDocument */ public function signatureHelp(TextDocumentIdentifier $textDocument, Position $position): Promise { + if (!$this->server->client->clientConfiguration->provideSignatureHelp) { + return new Success(null); + } + + $this->server->logDebug( + 'textDocument/signatureHelp', + ); + $file_path = LanguageServer::uriToPath($textDocument->uri); + //This currently doesnt work right with out of project files + if (!$this->codebase->config->isInProjectDirs($file_path)) { + return new Success(null); + } + try { $argument_location = $this->codebase->getFunctionArgumentAtPosition($file_path, $position); } catch (UnanalyzedFileException $e) { - $this->codebase->file_provider->openFile($file_path); - $this->server->queueFileAnalysis($file_path, $textDocument->uri); - - return new Success(new SignatureHelp()); + $this->server->logThrowable($e); + return new Success(null); } if ($argument_location === null) { - return new Success(new SignatureHelp()); + return new Success(null); } - $signature_information = $this->codebase->getSignatureInformation($argument_location[0], $file_path); + try { + $signature_information = $this->codebase->getSignatureInformation($argument_location[0], $file_path); + } catch (UnexpectedValueException $e) { + $this->server->logThrowable($e); + return new Success(null); + } if (!$signature_information) { - return new Success(new SignatureHelp()); + return new Success(null); } - return new Success(new SignatureHelp([ - $signature_information, - ], 0, $argument_location[1])); + return new Success( + new SignatureHelp( + [$signature_information], + 0, + $argument_location[1], + ), + ); } /** * The code action request is sent from the client to the server to compute commands * for a given text document and range. These commands are typically code fixes to * either fix problems or to beautify/refactor code. + * + * @psalm-suppress PossiblyUnusedParam */ - public function codeAction(TextDocumentIdentifier $textDocument, Range $range): Promise + public function codeAction(TextDocumentIdentifier $textDocument, Range $range, CodeActionContext $context): Promise { + if (!$this->server->client->clientConfiguration->provideCodeActions) { + return new Success(null); + } + + $this->server->logDebug( + 'textDocument/codeAction', + ); + $file_path = LanguageServer::uriToPath($textDocument->uri); - if (!$this->codebase->file_provider->isOpen($file_path)) { + + //Don't report code actions for files we arent watching + if (!$this->codebase->config->isInProjectDirs($file_path)) { return new Success(null); } - $issues = $this->server->getCurrentIssues(); - - if (empty($issues[$file_path])) { - return new Success(null); - } - - $file_contents = $this->codebase->getFileContents($file_path); - - $offsetStart = $range->start->toOffset($file_contents); - $offsetEnd = $range->end->toOffset($file_contents); - $fixers = []; - foreach ($issues[$file_path] as $issue) { - if ($offsetStart === $issue->from && $offsetEnd === $issue->to) { - $snippetRange = new Range( - new Position($issue->line_from-1), - new Position($issue->line_to), - ); + foreach ($context->diagnostics as $diagnostic) { + if ($diagnostic->source !== 'psalm') { + continue; + } - $indentation = ''; - if (preg_match('/^(\s*)/', $issue->snippet, $matches)) { - $indentation = $matches[1] ?? ''; - } + /** @var array{type: string, snippet: string, line_from: int, line_to: int} */ + $data = (array)$diagnostic->data; - /** - * Suppress Psalm because ther are bugs in how - * LanguageServer's signature of WorkspaceEdit is declared: - * - * See: - * https://github.com/felixfbecker/php-language-server-protocol - * See: - * https://microsoft.github.io/language-server-protocol/specifications/specification-3-17/#workspaceEdit - */ - $edit = new WorkspaceEdit([ + //$file_path = LanguageServer::uriToPath($textDocument->uri); + //$contents = $this->codebase->file_provider->getContents($file_path); + + $snippetRange = new Range( + new Position($data['line_from']-1), + new Position($data['line_to']), + ); + + $indentation = ''; + if (preg_match('/^(\s*)/', $data['snippet'], $matches)) { + $indentation = $matches[1] ?? ''; + } + + //Suppress Ability + $fixers["suppress.{$data['type']}"] = new CodeAction( + "Suppress {$data['type']} for this line", + CodeActionKind::QUICK_FIX, + null, + null, + null, + new WorkspaceEdit([ $textDocument->uri => [ new TextEdit( $snippetRange, - "{$indentation}/**\n". - "{$indentation} * @psalm-suppress {$issue->type}\n". - "{$indentation} */\n". - "{$issue->snippet}\n", + "{$indentation}/** @psalm-suppress {$data['type']} */\n". + "{$data['snippet']}\n", ), ], - ]); + ]), + ); - //Suppress Ability - $fixers["suppress.{$issue->type}"] = [ - 'title' => "Suppress {$issue->type} for this line", - 'kind' => 'quickfix', - 'edit' => $edit, - ]; - } + /* + $fixers["fixAll.{$diagnostic->data->type}"] = new CodeAction( + "FixAll {$diagnostic->data->type} for this file", + CodeActionKind::QUICK_FIX, + null, + null, + null, + null, + new Command( + "Fix All", + "psalm.fixall", + [ + 'uri' => $textDocument->uri, + 'type' => $diagnostic->data->type + ] + ) + ); + */ } if (empty($fixers)) { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php index 333c1bb6..af49619c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/LanguageServer/Server/Workspace.php @@ -4,11 +4,21 @@ declare(strict_types=1); namespace Psalm\Internal\LanguageServer\Server; +use Amp\Promise; +use Amp\Success; +use InvalidArgumentException; use LanguageServerProtocol\FileChangeType; use LanguageServerProtocol\FileEvent; use Psalm\Codebase; use Psalm\Internal\Analyzer\ProjectAnalyzer; +use Psalm\Internal\Composer; use Psalm\Internal\LanguageServer\LanguageServer; +use Psalm\Internal\Provider\FileReferenceProvider; + +use function array_filter; +use function array_map; +use function in_array; +use function realpath; /** * Provides method handlers for all workspace/* methods @@ -46,9 +56,35 @@ class Workspace */ public function didChangeWatchedFiles(array $changes): void { + $this->server->logDebug( + 'workspace/didChangeWatchedFiles', + ); + + $realFiles = array_filter( + array_map(function (FileEvent $change) { + try { + return LanguageServer::uriToPath($change->uri); + } catch (InvalidArgumentException $e) { + return null; + } + }, $changes), + ); + + $composerLockFile = realpath(Composer::getLockFilePath($this->codebase->config->base_dir)); + if (in_array($composerLockFile, $realFiles)) { + $this->server->logInfo('Composer.lock file changed. Reloading codebase'); + FileReferenceProvider::clearCache(); + $this->server->queueFileAnalysisWithOpenedFiles(); + return; + } + foreach ($changes as $change) { $file_path = LanguageServer::uriToPath($change->uri); + if ($composerLockFile === $file_path) { + continue; + } + if ($change->type === FileChangeType::DELETED) { $this->codebase->invalidateInformationForFile($file_path); continue; @@ -62,10 +98,64 @@ class Workspace continue; } - //If the file is currently open then dont analyse it because its tracked by the client + //If the file is currently open then dont analize it because its tracked in didChange if (!$this->codebase->file_provider->isOpen($file_path)) { - $this->server->queueFileAnalysis($file_path, $change->uri); + $this->server->queueClosedFileAnalysis($file_path, $change->uri); } } } + + /** + * A notification sent from the client to the server to signal the change of configuration settings. + * + * @param mixed $settings + * @psalm-suppress PossiblyUnusedMethod, PossiblyUnusedParam + */ + public function didChangeConfiguration($settings): void + { + $this->server->logDebug( + 'workspace/didChangeConfiguration', + ); + $this->server->client->refreshConfiguration(); + } + + /** + * The workspace/executeCommand request is sent from the client to the server to + * trigger command execution on the server. + * + * @param mixed $arguments + * @psalm-suppress PossiblyUnusedMethod + */ + public function executeCommand(string $command, $arguments): Promise + { + $this->server->logDebug( + 'workspace/executeCommand', + [ + 'command' => $command, + 'arguments' => $arguments, + ], + ); + + switch ($command) { + case 'psalm.analyze.uri': + /** @var array{uri: string} */ + $arguments = (array) $arguments; + $file = LanguageServer::uriToPath($arguments['uri']); + $this->codebase->reloadFiles( + $this->project_analyzer, + [$file], + true, + ); + + $this->codebase->analyzer->addFilesToAnalyze( + [$file => $file], + ); + $this->codebase->analyzer->analyzeFiles($this->project_analyzer, 1, false); + + $this->server->emitVersionedIssues([$file => $arguments['uri']]); + break; + } + + return new Success(null); + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php index 38f524b6..2f1c5467 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeDocblockParser.php @@ -241,10 +241,24 @@ class ClassLikeDocblockParser if (isset($parsed_docblock->tags['psalm-seal-properties'])) { $info->sealed_properties = true; } + if (isset($parsed_docblock->tags['psalm-no-seal-properties'])) { + $info->sealed_properties = false; + } if (isset($parsed_docblock->tags['psalm-seal-methods'])) { $info->sealed_methods = true; } + if (isset($parsed_docblock->tags['psalm-no-seal-methods'])) { + $info->sealed_methods = false; + } + + if (isset($parsed_docblock->tags['psalm-inheritors'])) { + foreach ($parsed_docblock->tags['psalm-inheritors'] as $template_line) { + $doc_line_parts = CommentAnalyzer::splitDocLine($template_line); + $doc_line_parts[0] = CommentAnalyzer::sanitizeDocblockType($doc_line_parts[0]); + $info->inheritors = $doc_line_parts[0]; + } + } if (isset($parsed_docblock->tags['psalm-immutable']) || isset($parsed_docblock->tags['psalm-mutation-free']) @@ -296,6 +310,9 @@ class ClassLikeDocblockParser } if (isset($parsed_docblock->combined_tags['method'])) { + if ($info->sealed_methods === null) { + $info->sealed_methods = true; + } foreach ($parsed_docblock->combined_tags['method'] as $offset => $method_entry) { $method_entry = preg_replace('/[ \t]+/', ' ', trim($method_entry)); @@ -481,6 +498,13 @@ class ClassLikeDocblockParser $info->public_api = isset($parsed_docblock->tags['psalm-api']) || isset($parsed_docblock->tags['api']); + if (isset($parsed_docblock->tags['property']) + && $codebase->config->docblock_property_types_seal_properties + && $info->sealed_properties === null + ) { + $info->sealed_properties = true; + } + self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'property'); self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'psalm-property'); self::addMagicPropertyToInfo($comment, $info, $parsed_docblock->tags, 'property-read'); diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php index 6331b7ec..2bc9d718 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ClassLikeNodeScanner.php @@ -143,6 +143,7 @@ class ClassLikeNodeScanner /** * @return false|null + * @psalm-suppress ComplexMethod */ public function start(PhpParser\Node\Stmt\ClassLike $node): ?bool { @@ -420,10 +421,19 @@ class ClassLikeNodeScanner if ($template_map[1] !== null && $template_map[2] !== null) { if (trim($template_map[2])) { + $type_string = $template_map[2]; + try { + $type_string = CommentAnalyzer::splitDocLine($type_string)[0]; + } catch (DocblockParseException $e) { + throw new DocblockParseException( + $type_string . ' is not a valid type: ' . $e->getMessage(), + ); + } + $type_string = CommentAnalyzer::sanitizeDocblockType($type_string); try { $template_type = TypeParser::parseTokens( TypeTokenizer::getFullyQualifiedTokens( - $template_map[2], + $type_string, $this->aliases, $storage->template_types, $this->type_aliases, @@ -529,6 +539,31 @@ class ClassLikeNodeScanner $storage->sealed_properties = $docblock_info->sealed_properties; $storage->sealed_methods = $docblock_info->sealed_methods; + + if ($docblock_info->inheritors) { + try { + $storage->inheritors = TypeParser::parseTokens( + TypeTokenizer::getFullyQualifiedTokens( + $docblock_info->inheritors, + $storage->aliases, + $storage->template_types ?? [], + $storage->type_aliases, + $fq_classlike_name, + ), + null, + $storage->template_types ?? [], + $storage->type_aliases, + true, + ); + } catch (TypeParseTreeException $e) { + $storage->docblock_issues[] = new InvalidDocblock( + '@psalm-inheritors contains invalid reference:' . $e->getMessage(), + $name_location ?? $class_location, + ); + } + } + + if ($docblock_info->properties) { foreach ($docblock_info->properties as $property) { $pseudo_property_type_tokens = TypeTokenizer::getFullyQualifiedTokens( @@ -567,8 +602,6 @@ class ClassLikeNodeScanner ); } } - - $storage->sealed_properties = true; } foreach ($docblock_info->methods as $method) { @@ -595,8 +628,6 @@ class ClassLikeNodeScanner $lc_method_name, ); } - - $storage->sealed_methods = true; } @@ -1173,7 +1204,7 @@ class ClassLikeNodeScanner $storage->template_type_uses_count[$generic_class_lc] = count($atomic_type->type_params); foreach ($atomic_type->type_params as $type_param) { - $used_type_parameters[] = $type_param; + $used_type_parameters[] = $type_param->replaceClassLike('self', $storage->name); } $storage->template_extended_offsets[$atomic_type->value] = $used_type_parameters; @@ -1905,9 +1936,12 @@ class ClassLikeNodeScanner } $type_string = str_replace("\n", '', implode('', $var_line_parts)); - - // Strip any remaining characters after the last grouping character >, } or ) - $type_string = preg_replace('/(?<=[>})])[^>})]*$/', '', $type_string, 1); + try { + $type_string = CommentAnalyzer::splitDocLine($type_string)[0]; + } catch (DocblockParseException $e) { + throw new DocblockParseException($type_string . ' is not a valid type: '.$e->getMessage()); + } + $type_string = CommentAnalyzer::sanitizeDocblockType($type_string); try { $type_tokens = TypeTokenizer::getFullyQualifiedTokens( diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php index 021b8930..20c75007 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/ExpressionResolver.php @@ -15,6 +15,8 @@ use Psalm\Internal\Scanner\UnresolvedConstant\ArraySpread; use Psalm\Internal\Scanner\UnresolvedConstant\ArrayValue; use Psalm\Internal\Scanner\UnresolvedConstant\ClassConstant; use Psalm\Internal\Scanner\UnresolvedConstant\Constant; +use Psalm\Internal\Scanner\UnresolvedConstant\EnumNameFetch; +use Psalm\Internal\Scanner\UnresolvedConstant\EnumValueFetch; use Psalm\Internal\Scanner\UnresolvedConstant\KeyValuePair; use Psalm\Internal\Scanner\UnresolvedConstant\ScalarValue; use Psalm\Internal\Scanner\UnresolvedConstant\UnresolvedAdditionOp; @@ -34,6 +36,7 @@ use function assert; use function class_exists; use function function_exists; use function implode; +use function in_array; use function interface_exists; use function strtolower; @@ -297,6 +300,24 @@ class ExpressionResolver return new ArrayValue($items); } + if ($stmt instanceof PhpParser\Node\Expr\PropertyFetch + && $stmt->var instanceof PhpParser\Node\Expr\ClassConstFetch + && $stmt->var->class instanceof PhpParser\Node\Name + && $stmt->var->name instanceof PhpParser\Node\Identifier + && $stmt->name instanceof PhpParser\Node\Identifier + && in_array($stmt->name->name, ['name', 'value', true]) + ) { + $enum_fq_class_name = ClassLikeAnalyzer::getFQCLNFromNameObject( + $stmt->var->class, + $aliases, + ); + if ($stmt->name->name === 'value') { + return new EnumValueFetch($enum_fq_class_name, $stmt->var->name->name); + } elseif ($stmt->name->name === 'name') { + return new EnumNameFetch($enum_fq_class_name, $stmt->var->name->name); + } + } + return null; } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php index f0cc1dc5..9f9a477c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/Reflector/FunctionLikeDocblockScanner.php @@ -9,8 +9,10 @@ use Psalm\CodeLocation; use Psalm\CodeLocation\DocblockTypeLocation; use Psalm\Codebase; use Psalm\Config; +use Psalm\Exception\DocblockParseException; use Psalm\Exception\InvalidMethodOverrideException; use Psalm\Exception\TypeParseTreeException; +use Psalm\Internal\Analyzer\CommentAnalyzer; use Psalm\Internal\Analyzer\NamespaceAnalyzer; use Psalm\Internal\Scanner\FileScanner; use Psalm\Internal\Scanner\FunctionDocblockComment; @@ -61,6 +63,7 @@ use function strlen; use function strpos; use function strtolower; use function substr; +use function substr_replace; use function trim; /** @@ -1252,7 +1255,7 @@ class FunctionLikeDocblockScanner if (strpos($assertion['param_name'], $param->name.'->') === 0) { $storage->assertions[] = new Possibilities( - str_replace($param->name, (string) $i, $assertion['param_name']), + substr_replace($assertion['param_name'], (string) $i, 0, strlen($param->name)), $assertion_type_parts, ); continue 2; @@ -1439,10 +1442,17 @@ class FunctionLikeDocblockScanner if ($template_map[1] !== null && $template_map[2] !== null) { if (trim($template_map[2])) { + $type_string = $template_map[2]; + try { + $type_string = CommentAnalyzer::splitDocLine($type_string)[0]; + } catch (DocblockParseException $e) { + throw new DocblockParseException($type_string . ' is not a valid type: '.$e->getMessage()); + } + $type_string = CommentAnalyzer::sanitizeDocblockType($type_string); try { $template_type = TypeParser::parseTokens( TypeTokenizer::getFullyQualifiedTokens( - $template_map[2], + $type_string, $aliases, $storage->template_types + ($template_types ?: []), $type_aliases, diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php index 4a4779d3..989bae6e 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/PhpVisitor/ReflectorVisitor.php @@ -238,6 +238,12 @@ class ReflectorVisitor extends PhpParser\NodeVisitorAbstract implements FileSour $var_id = '$' . $var->name; $functionlike_node_scanner->storage->global_variables[$var_id] = true; + + if (isset($this->codebase->config->globals[$var_id])) { + $var_type = Type::parseString($this->codebase->config->globals[$var_id]); + /** @psalm-suppress UnusedMethodCall */ + $var_type->queueClassLikesForScanning($this->codebase, $this->file_storage); + } } } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php index 5e9d9e5a..9b8c8261 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ClassLikeStorageProvider.php @@ -49,10 +49,14 @@ class ClassLikeStorageProvider return self::$storage[$fq_classlike_name_lc]; } + /** + * @psalm-mutation-free + */ public function has(string $fq_classlike_name): bool { $fq_classlike_name_lc = strtolower($fq_classlike_name); + /** @psalm-suppress ImpureStaticProperty Used only for caching */ return isset(self::$storage[$fq_classlike_name_lc]); } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php index 47dcb7c5..64251084 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FakeFileProvider.php @@ -20,16 +20,26 @@ class FakeFileProvider extends FileProvider */ public array $fake_file_times = []; + /** + * @var array + */ + public array $fake_directories = []; + public function fileExists(string $file_path): bool { return isset($this->fake_files[$file_path]) || parent::fileExists($file_path); } + public function isDirectory(string $file_path): bool + { + return isset($this->fake_directories[$file_path]) || parent::isDirectory($file_path); + } + /** @psalm-external-mutation-free */ public function getContents(string $file_path, bool $go_to_source = false): string { if (!$go_to_source && isset($this->temp_files[$file_path])) { - return $this->temp_files[$file_path]; + return $this->temp_files[$file_path]['content']; } return $this->fake_files[$file_path] ?? parent::getContents($file_path); @@ -40,10 +50,10 @@ class FakeFileProvider extends FileProvider $this->fake_files[$file_path] = $file_contents; } - public function setOpenContents(string $file_path, string $file_contents): void + public function setOpenContents(string $file_path, ?string $file_contents = null): void { if (isset($this->fake_files[$file_path])) { - $this->fake_files[$file_path] = $file_contents; + $this->fake_files[$file_path] = $file_contents ?? $this->getContents($file_path, true); } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php index 13fcf572..aa4202d0 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FileProvider.php @@ -24,7 +24,7 @@ use const DIRECTORY_SEPARATOR; class FileProvider { /** - * @var array + * @var array */ protected array $temp_files = []; @@ -33,32 +33,31 @@ class FileProvider */ protected static array $open_files = []; - /** @psalm-mutation-free */ + /** + * @var array + */ + protected array $open_files_paths = []; + public function getContents(string $file_path, bool $go_to_source = false): string { if (!$go_to_source && isset($this->temp_files[$file_path])) { - return $this->temp_files[$file_path]; + return $this->temp_files[$file_path]['content']; } - /** @psalm-suppress ImpureStaticProperty Used only for caching */ if (isset(self::$open_files[$file_path])) { return self::$open_files[$file_path]; } - /** @psalm-suppress ImpureFunctionCall For our purposes, this should not mutate external state */ if (!file_exists($file_path)) { throw new UnexpectedValueException('File ' . $file_path . ' should exist to get contents'); } - /** @psalm-suppress ImpureFunctionCall For our purposes, this should not mutate external state */ if (is_dir($file_path)) { throw new UnexpectedValueException('File ' . $file_path . ' is a directory'); } - /** @psalm-suppress ImpureFunctionCall For our purposes, this should not mutate external state */ $file_contents = (string) file_get_contents($file_path); - /** @psalm-suppress ImpureStaticProperty Used only for caching */ self::$open_files[$file_path] = $file_contents; return $file_contents; @@ -71,16 +70,19 @@ class FileProvider } if (isset($this->temp_files[$file_path])) { - $this->temp_files[$file_path] = $file_contents; + $this->temp_files[$file_path] = [ + 'version'=> null, + 'content' => $file_contents, + ]; } file_put_contents($file_path, $file_contents); } - public function setOpenContents(string $file_path, string $file_contents): void + public function setOpenContents(string $file_path, ?string $file_contents = null): void { if (isset(self::$open_files[$file_path])) { - self::$open_files[$file_path] = $file_contents; + self::$open_files[$file_path] = $file_contents ?? $this->getContents($file_path, true); } } @@ -93,9 +95,19 @@ class FileProvider return (int) filemtime($file_path); } - public function addTemporaryFileChanges(string $file_path, string $new_content): void + public function addTemporaryFileChanges(string $file_path, string $new_content, ?int $version = null): void { - $this->temp_files[$file_path] = $new_content; + if (isset($this->temp_files[$file_path]) && + $version !== null && + $this->temp_files[$file_path]['version'] !== null && + $version < $this->temp_files[$file_path]['version'] + ) { + return; + } + $this->temp_files[$file_path] = [ + 'version' => $version, + 'content' => $new_content, + ]; } public function removeTemporaryFileChanges(string $file_path): void @@ -103,9 +115,15 @@ class FileProvider unset($this->temp_files[$file_path]); } + public function getOpenFilesPath(): array + { + return $this->open_files_paths; + } + public function openFile(string $file_path): void { self::$open_files[$file_path] = $this->getContents($file_path, true); + $this->open_files_paths[$file_path] = $file_path; } public function isOpen(string $file_path): bool @@ -115,7 +133,11 @@ class FileProvider public function closeFile(string $file_path): void { - unset($this->temp_files[$file_path], self::$open_files[$file_path]); + unset( + $this->temp_files[$file_path], + self::$open_files[$file_path], + $this->open_files_paths[$file_path], + ); } public function fileExists(string $file_path): bool @@ -123,6 +145,11 @@ class FileProvider return file_exists($file_path); } + public function isDirectory(string $file_path): bool + { + return is_dir($file_path); + } + /** * @param array $file_extensions * @param null|callable(string):bool $filter diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php index bdd59a0a..de1e27d7 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/FunctionReturnTypeProvider.php @@ -22,8 +22,8 @@ use Psalm\Internal\Provider\ReturnTypeProvider\ArrayReduceReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ArrayReverseReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ArraySliceReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ArraySpliceReturnTypeProvider; -use Psalm\Internal\Provider\ReturnTypeProvider\ArrayUniqueReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\BasenameReturnTypeProvider; +use Psalm\Internal\Provider\ReturnTypeProvider\DateReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\DirnameReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\FilterVarReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\FirstArgStringReturnTypeProvider; @@ -36,6 +36,7 @@ use Psalm\Internal\Provider\ReturnTypeProvider\MbInternalEncodingReturnTypeProvi use Psalm\Internal\Provider\ReturnTypeProvider\MinMaxReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\MktimeReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\ParseUrlReturnTypeProvider; +use Psalm\Internal\Provider\ReturnTypeProvider\PowReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\RandReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\RoundReturnTypeProvider; use Psalm\Internal\Provider\ReturnTypeProvider\StrReplaceReturnTypeProvider; @@ -81,7 +82,6 @@ class FunctionReturnTypeProvider $this->registerClass(ArraySliceReturnTypeProvider::class); $this->registerClass(ArraySpliceReturnTypeProvider::class); $this->registerClass(ArrayReverseReturnTypeProvider::class); - $this->registerClass(ArrayUniqueReturnTypeProvider::class); $this->registerClass(ArrayFillReturnTypeProvider::class); $this->registerClass(ArrayFillKeysReturnTypeProvider::class); $this->registerClass(FilterVarReturnTypeProvider::class); @@ -103,6 +103,8 @@ class FunctionReturnTypeProvider $this->registerClass(InArrayReturnTypeProvider::class); $this->registerClass(RoundReturnTypeProvider::class); $this->registerClass(MbInternalEncodingReturnTypeProvider::class); + $this->registerClass(DateReturnTypeProvider::class); + $this->registerClass(PowReturnTypeProvider::class); } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php deleted file mode 100644 index 1b46a3fb..00000000 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/ArrayUniqueReturnTypeProvider.php +++ /dev/null @@ -1,60 +0,0 @@ - - */ - public static function getFunctionIds(): array - { - return ['array_unique']; - } - - public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): Union - { - $statements_source = $event->getStatementsSource(); - $call_args = $event->getCallArgs(); - if (!$statements_source instanceof StatementsAnalyzer) { - return Type::getMixed(); - } - - $first_arg = $call_args[0]->value ?? null; - - $first_arg_array = $first_arg - && ($first_arg_type = $statements_source->node_data->getType($first_arg)) - && $first_arg_type->hasType('array') - && ($array_atomic_type = $first_arg_type->getArray()) - && ($array_atomic_type instanceof TArray - || $array_atomic_type instanceof TKeyedArray) - ? $array_atomic_type - : null; - - if (!$first_arg_array) { - return Type::getArray(); - } - - if ($first_arg_array instanceof TArray) { - if ($first_arg_array instanceof TNonEmptyArray) { - $first_arg_array = $first_arg_array->setCount(null); - } - - return new Union([$first_arg_array]); - } - - return new Union([$first_arg_array->getGenericArrayType()]); - } -} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php new file mode 100644 index 00000000..13bc381d --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/DateReturnTypeProvider.php @@ -0,0 +1,69 @@ + + */ + public static function getFunctionIds(): array + { + return ['date', 'gmdate']; + } + + public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union + { + $source = $event->getStatementsSource(); + if (!$source instanceof StatementsAnalyzer) { + return null; + } + + $call_args = $event->getCallArgs(); + + $format_type = Type::getString(); + if (isset($call_args[0])) { + $type = $source->node_data->getType($call_args[0]->value); + if ($type !== null + && $type->isSingleStringLiteral() + && is_numeric(date($type->getSingleStringLiteral()->value)) + ) { + $format_type = Type::getNumericString(); + } + } + + if (!isset($call_args[1])) { + return $format_type; + } + + $type = $source->node_data->getType($call_args[1]->value); + if ($type !== null && $type->isSingle()) { + $atomic_type = array_values($type->getAtomicTypes())[0]; + if ($atomic_type instanceof Type\Atomic\TNumeric + || $atomic_type instanceof Type\Atomic\TInt + || $atomic_type instanceof TLiteralInt + || ($atomic_type instanceof TLiteralString && is_numeric($atomic_type->value)) + ) { + return $format_type; + } + } + return $format_type; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PowReturnTypeProvider.php b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PowReturnTypeProvider.php new file mode 100644 index 00000000..bffd07fc --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Provider/ReturnTypeProvider/PowReturnTypeProvider.php @@ -0,0 +1,92 @@ + + */ + public static function getFunctionIds(): array + { + return ['pow']; + } + + public static function getFunctionReturnType(FunctionReturnTypeProviderEvent $event): ?Union + { + $call_args = $event->getCallArgs(); + + if (count($call_args) !== 2) { + return null; + } + + $first_arg = $event->getStatementsSource()->getNodeTypeProvider()->getType($call_args[0]->value); + $second_arg = $event->getStatementsSource()->getNodeTypeProvider()->getType($call_args[1]->value); + + $first_arg_literal = null; + $first_arg_is_int = false; + $first_arg_is_float = false; + if ($first_arg !== null && $first_arg->isSingle()) { + $first_atomic_type = $first_arg->getSingleAtomic(); + if ($first_atomic_type instanceof TInt) { + $first_arg_is_int = true; + } elseif ($first_atomic_type instanceof TFloat) { + $first_arg_is_float = true; + } + if ($first_atomic_type instanceof TLiteralInt + || $first_atomic_type instanceof TLiteralFloat + ) { + $first_arg_literal = $first_atomic_type->value; + } + } + + $second_arg_literal = null; + $second_arg_is_int = false; + $second_arg_is_float = false; + if ($second_arg !== null && $second_arg->isSingle()) { + $second_atomic_type = $second_arg->getSingleAtomic(); + if ($second_atomic_type instanceof TInt) { + $second_arg_is_int = true; + } elseif ($second_atomic_type instanceof TFloat) { + $second_arg_is_float = true; + } + if ($second_atomic_type instanceof TLiteralInt + || $second_atomic_type instanceof TLiteralFloat + ) { + $second_arg_literal = $second_atomic_type->value; + } + } + + if ($first_arg_literal === 0) { + return Type::getInt(true, 0); + } + if ($second_arg_literal === 0) { + return Type::getInt(true, 1); + } + if ($first_arg_literal !== null && $second_arg_literal !== null) { + return Type::getFloat($first_arg_literal ** $second_arg_literal); + } + if ($first_arg_is_int && $second_arg_is_int) { + return Type::getInt(); + } + if ($first_arg_is_float || $second_arg_is_float) { + return Type::getFloat(); + } + + return new Union([new TInt(), new TFloat()]); + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php index fc0a83c9..0d68ece0 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/ClassLikeDocblockComment.php @@ -63,9 +63,9 @@ class ClassLikeDocblockComment */ public array $methods = []; - public bool $sealed_properties = false; + public ?bool $sealed_properties = null; - public bool $sealed_methods = false; + public ?bool $sealed_methods = null; public bool $override_property_visibility = false; @@ -87,6 +87,8 @@ class ClassLikeDocblockComment */ public array $imported_types = []; + public ?string $inheritors = null; + public bool $consistent_constructor = false; public bool $consistent_templates = false; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/EnumNameFetch.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/EnumNameFetch.php new file mode 100644 index 00000000..29639fb3 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/EnumNameFetch.php @@ -0,0 +1,11 @@ +fqcln = $fqcln; + $this->case = $case; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/EnumValueFetch.php b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/EnumValueFetch.php new file mode 100644 index 00000000..1ee9c003 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Scanner/UnresolvedConstant/EnumValueFetch.php @@ -0,0 +1,11 @@ +key = $key; $this->value = $value; $this->is_list = $is_list; + $this->count = $count; } /** @@ -37,18 +43,35 @@ class ArrayType public static function infer(Atomic $type): ?self { if ($type instanceof TKeyedArray) { + $count = null; + if ($type->isSealed()) { + $count = count($type->properties); + } + return new self( $type->getGenericKeyType(), $type->getGenericValueType(), $type->is_list, + $count, ); } - if ($type instanceof TArray) { + if ($type instanceof TNonEmptyArray) { return new self( $type->type_params[0], $type->type_params[1], false, + $type->count, + ); + } + + if ($type instanceof TArray) { + $empty = $type->isEmptyArray(); + return new self( + $type->type_params[0], + $type->type_params[1], + false, + $empty?0:null, ); } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php index b0f461cc..e01ee2d2 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/AtomicTypeComparator.php @@ -4,6 +4,7 @@ namespace Psalm\Internal\Type\Comparator; use Psalm\Codebase; use Psalm\Internal\MethodIdentifier; +use Psalm\Type; use Psalm\Type\Atomic; use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; @@ -17,15 +18,18 @@ use Psalm\Type\Atomic\TConditional; use Psalm\Type\Atomic\TEmptyMixed; use Psalm\Type\Atomic\TEnumCase; use Psalm\Type\Atomic\TGenericObject; +use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TKeyOf; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TList; +use Psalm\Type\Atomic\TLiteralInt; use Psalm\Type\Atomic\TLiteralString; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyArray; +use Psalm\Type\Atomic\TNonEmptyMixed; use Psalm\Type\Atomic\TNull; use Psalm\Type\Atomic\TObject; use Psalm\Type\Atomic\TObjectWithProperties; @@ -35,12 +39,14 @@ use Psalm\Type\Atomic\TTemplateKeyOf; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Atomic\TTemplateValueOf; use Psalm\Type\Atomic\TValueOf; +use Psalm\Type\Union; use function array_merge; use function array_values; use function assert; use function count; use function get_class; +use function is_int; use function strtolower; /** @@ -81,14 +87,43 @@ class AtomicTypeComparator ); } + if ($input_type_part instanceof TValueOf) { + if ($container_type_part instanceof TValueOf) { + return UnionTypeComparator::isContainedBy( + $codebase, + $input_type_part->type, + $container_type_part->type, + false, + false, + null, + false, + false, + ); + } elseif ($container_type_part instanceof Scalar) { + return UnionTypeComparator::isContainedBy( + $codebase, + TValueOf::getValueType($input_type_part->type, $codebase) ?? $input_type_part->type, + new Union([$container_type_part]), + false, + false, + null, + false, + false, + ); + } + } + if ($container_type_part instanceof TMixed || ($container_type_part instanceof TTemplateParam && $container_type_part->as->isMixed() && !$container_type_part->extra_types && $input_type_part instanceof TMixed) ) { - if (get_class($container_type_part) === TEmptyMixed::class - && get_class($input_type_part) === TMixed::class + if (get_class($input_type_part) === TMixed::class + && ( + get_class($container_type_part) === TEmptyMixed::class + || get_class($container_type_part) === TNonEmptyMixed::class + ) ) { if ($atomic_comparison_result) { $atomic_comparison_result->type_coerced = true; @@ -298,7 +333,7 @@ class AtomicTypeComparator $atomic_comparison_result->type_coerced = true; } - return false; + return true; } if ($container_type_part instanceof TEnumCase @@ -600,6 +635,40 @@ class AtomicTypeComparator } } + if ($input_type_part instanceof TEnumCase + && $codebase->classlike_storage_provider->has($input_type_part->value) + ) { + if ($container_type_part instanceof TString || $container_type_part instanceof TInt) { + $input_type_classlike_storage = $codebase->classlike_storage_provider->get($input_type_part->value); + if ($input_type_classlike_storage->enum_type === null + || !isset($input_type_classlike_storage->enum_cases[$input_type_part->case_name]) + ) { + // Not a backed enum or non-existent enum case + return false; + } + + $input_type_enum_case_storage = $input_type_classlike_storage->enum_cases[$input_type_part->case_name]; + assert( + $input_type_enum_case_storage->value !== null, + 'Backed enums cannot have values without a value.', + ); + + if (is_int($input_type_enum_case_storage->value)) { + return self::isContainedBy( + $codebase, + new TLiteralInt($input_type_enum_case_storage->value), + $container_type_part, + ); + } + + return self::isContainedBy( + $codebase, + Type::getAtomicStringFromLiteral($input_type_enum_case_storage->value), + $container_type_part, + ); + } + } + if ($container_type_part instanceof TString || $container_type_part instanceof TScalar) { if ($input_type_part instanceof TNamedObject) { // check whether the object has a __toString method diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php index c074efd8..a3830ea8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ClassLikeStringComparator.php @@ -80,7 +80,7 @@ class ClassLikeStringComparator : $input_type_part->value, ); - return AtomicTypeComparator::isContainedBy( + $isContainedBy = AtomicTypeComparator::isContainedBy( $codebase, $fake_input_object, $fake_container_object, @@ -88,5 +88,16 @@ class ClassLikeStringComparator false, $atomic_comparison_result, ); + + if ($atomic_comparison_result + && $atomic_comparison_result->replacement_atomic_type instanceof TNamedObject + ) { + $atomic_comparison_result->replacement_atomic_type = new TClassString( + 'object', + $atomic_comparison_result->replacement_atomic_type, + ); + } + + return $isContainedBy; } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php index 9eba8781..c9d7aad5 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/KeyedArrayComparator.php @@ -3,11 +3,15 @@ namespace Psalm\Internal\Type\Comparator; use Psalm\Codebase; +use Psalm\Internal\Type\TemplateInferredTypeReplacer; +use Psalm\Internal\Type\TemplateResult; use Psalm\Type; use Psalm\Type\Atomic; +use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TKeyedArray; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TObjectWithProperties; +use Psalm\Type\Union; use function array_keys; use function is_string; @@ -84,24 +88,47 @@ class KeyedArrayComparator $property_type_comparison, $allow_interface_equality, ); - if (!$is_input_containedby_container && !$property_type_comparison->type_coerced_from_scalar) { - $inverse_property_type_comparison = new TypeComparisonResult(); - + if (!$is_input_containedby_container) { if ($atomic_comparison_result) { - if (UnionTypeComparator::isContainedBy( - $codebase, - $container_property_type, - $input_property_type, - false, - false, - $inverse_property_type_comparison, - $allow_interface_equality, - ) - || $inverse_property_type_comparison->type_coerced_from_scalar - ) { - $atomic_comparison_result->type_coerced = true; + $atomic_comparison_result->type_coerced + = $property_type_comparison->type_coerced === true + && $atomic_comparison_result->type_coerced !== false; + + if (!$property_type_comparison->type_coerced_from_scalar + && !$atomic_comparison_result->type_coerced) { + //if we didn't detect a coercion, we try to compare the other way around + $inverse_property_type_comparison = new TypeComparisonResult(); + if (UnionTypeComparator::isContainedBy( + $codebase, + $container_property_type, + $input_property_type, + false, + false, + $inverse_property_type_comparison, + $allow_interface_equality, + ) + || $inverse_property_type_comparison->type_coerced_from_scalar + ) { + $atomic_comparison_result->type_coerced = true; + } } + $atomic_comparison_result->type_coerced_from_mixed + = $property_type_comparison->type_coerced_from_mixed === true + && $atomic_comparison_result->type_coerced_from_mixed !== false; + + $atomic_comparison_result->type_coerced_from_as_mixed + = $property_type_comparison->type_coerced_from_as_mixed === true + && $atomic_comparison_result->type_coerced_from_as_mixed !== false; + + $atomic_comparison_result->type_coerced_from_scalar + = $property_type_comparison->type_coerced_from_scalar === true + && $atomic_comparison_result->type_coerced_from_scalar !== false; + + $atomic_comparison_result->scalar_type_match_found + = $property_type_comparison->scalar_type_match_found === true + && $atomic_comparison_result->scalar_type_match_found !== false; + if ($property_type_comparison->missing_shape_fields) { $atomic_comparison_result->missing_shape_fields = $property_type_comparison->missing_shape_fields; @@ -110,13 +137,10 @@ class KeyedArrayComparator $all_types_contain = false; } else { - if (!$is_input_containedby_container) { - $all_types_contain = false; - } if ($atomic_comparison_result) { $atomic_comparison_result->to_string_cast = $atomic_comparison_result->to_string_cast === true - || $property_type_comparison->to_string_cast === true; + || $property_type_comparison->to_string_cast === true; } } } @@ -127,6 +151,132 @@ class KeyedArrayComparator } return false; } + + // check remaining $input_properties against container's fallback_params + if ($container_type_part instanceof TKeyedArray + && $container_type_part->fallback_params !== null + ) { + [$key_type, $value_type] = $container_type_part->fallback_params; + // treat fallback params as possibly undefined + // otherwise comparison below would fail for list{0?:int} <=> list{..., int>} + // as the latter `int` is not marked as possibly_undefined + $value_type = $value_type->setPossiblyUndefined(true); + + foreach ($input_properties as $key => $input_property_type) { + $key_type_comparison = new TypeComparisonResult(); + if (!UnionTypeComparator::isContainedBy( + $codebase, + is_string($key) ? Type::getString($key) : Type::getInt(false, $key), + $key_type, + false, + false, + $key_type_comparison, + $allow_interface_equality, + )) { + if ($atomic_comparison_result) { + $atomic_comparison_result->type_coerced + = $key_type_comparison->type_coerced === true + && $atomic_comparison_result->type_coerced !== false; + + $atomic_comparison_result->type_coerced_from_mixed + = $key_type_comparison->type_coerced_from_mixed === true + && $atomic_comparison_result->type_coerced_from_mixed !== false; + + $atomic_comparison_result->type_coerced_from_as_mixed + = $key_type_comparison->type_coerced_from_as_mixed === true + && $atomic_comparison_result->type_coerced_from_as_mixed !== false; + + $atomic_comparison_result->type_coerced_from_scalar + = $key_type_comparison->type_coerced_from_scalar === true + && $atomic_comparison_result->type_coerced_from_scalar !== false; + + $atomic_comparison_result->scalar_type_match_found + = $key_type_comparison->scalar_type_match_found === true + && $atomic_comparison_result->scalar_type_match_found !== false; + } + $all_types_contain = false; + } + + $property_type_comparison = new TypeComparisonResult(); + if (!UnionTypeComparator::isContainedBy( + $codebase, + $input_property_type, + $value_type, + false, + false, + $property_type_comparison, + $allow_interface_equality, + )) { + if ($atomic_comparison_result) { + $atomic_comparison_result->type_coerced + = $property_type_comparison->type_coerced === true + && $atomic_comparison_result->type_coerced !== false; + + $atomic_comparison_result->type_coerced_from_mixed + = $property_type_comparison->type_coerced_from_mixed === true + && $atomic_comparison_result->type_coerced_from_mixed !== false; + + $atomic_comparison_result->type_coerced_from_as_mixed + = $property_type_comparison->type_coerced_from_as_mixed === true + && $atomic_comparison_result->type_coerced_from_as_mixed !== false; + + $atomic_comparison_result->type_coerced_from_scalar + = $property_type_comparison->type_coerced_from_scalar === true + && $atomic_comparison_result->type_coerced_from_scalar !== false; + + $atomic_comparison_result->scalar_type_match_found + = $property_type_comparison->scalar_type_match_found === true + && $atomic_comparison_result->scalar_type_match_found !== false; + } + $all_types_contain = false; + } + } + } + + // finally, check input type fallback params against container type fallback params + if ($input_type_part instanceof TKeyedArray + && $container_type_part instanceof TKeyedArray + && $input_type_part->fallback_params !== null + && $container_type_part->fallback_params !== null + ) { + foreach ($input_type_part->fallback_params as $i => $input_param) { + $container_param = $container_type_part->fallback_params[$i]; + $param_comparison = new TypeComparisonResult(); + if (!UnionTypeComparator::isContainedBy( + $codebase, + $input_param, + $container_param, + false, + false, + $param_comparison, + $allow_interface_equality, + )) { + if ($atomic_comparison_result) { + $atomic_comparison_result->type_coerced + = $param_comparison->type_coerced === true + && $atomic_comparison_result->type_coerced !== false; + + $atomic_comparison_result->type_coerced_from_mixed + = $param_comparison->type_coerced_from_mixed === true + && $atomic_comparison_result->type_coerced_from_mixed !== false; + + $atomic_comparison_result->type_coerced_from_as_mixed + = $param_comparison->type_coerced_from_as_mixed === true + && $atomic_comparison_result->type_coerced_from_as_mixed !== false; + + $atomic_comparison_result->type_coerced_from_scalar + = $param_comparison->type_coerced_from_scalar === true + && $atomic_comparison_result->type_coerced_from_scalar !== false; + + $atomic_comparison_result->scalar_type_match_found + = $param_comparison->scalar_type_match_found === true + && $atomic_comparison_result->scalar_type_match_found !== false; + } + $all_types_contain = false; + } + } + } + return $all_types_contain; } @@ -139,36 +289,20 @@ class KeyedArrayComparator ): bool { $all_types_contain = true; + $input_object_with_keys = self::coerceToObjectWithProperties( + $codebase, + $input_type_part, + $container_type_part, + ); + foreach ($container_type_part->properties as $property_name => $container_property_type) { - if (!is_string($property_name)) { - continue; - } - - if (!$codebase->classlikes->classOrInterfaceExists($input_type_part->value)) { + if (!$input_object_with_keys || !isset($input_object_with_keys->properties[$property_name])) { $all_types_contain = false; continue; } - if (!$codebase->properties->propertyExists( - $input_type_part->value . '::$' . $property_name, - true, - )) { - $all_types_contain = false; - - continue; - } - - $property_declaring_class = (string) $codebase->properties->getDeclaringClassForProperty( - $input_type_part . '::$' . $property_name, - true, - ); - - $class_storage = $codebase->classlike_storage_provider->get($property_declaring_class); - - $input_property_storage = $class_storage->properties[$property_name]; - - $input_property_type = $input_property_storage->type ?: Type::getMixed(); + $input_property_type = $input_object_with_keys->properties[$property_name]; $property_type_comparison = new TypeComparisonResult(); @@ -208,4 +342,61 @@ class KeyedArrayComparator return $all_types_contain; } + + public static function coerceToObjectWithProperties( + Codebase $codebase, + TNamedObject $input_type_part, + TObjectWithProperties $container_type_part + ): ?TObjectWithProperties { + $storage = $codebase->classlikes->getStorageFor($input_type_part->value); + + if (!$storage) { + return null; + } + + $inferred_lower_bounds = []; + + if ($input_type_part instanceof TGenericObject) { + foreach ($storage->template_types ?? [] as $template_name => $templates) { + foreach (array_keys($templates) as $offset => $defining_at) { + $inferred_lower_bounds[$template_name][$defining_at] = + $input_type_part->type_params[$offset]; + } + } + } + + foreach ($storage->template_extended_params ?? [] as $defining_at => $templates) { + foreach ($templates as $template_name => $template_atomic) { + $inferred_lower_bounds[$template_name][$defining_at] = $template_atomic; + } + } + + $properties = []; + + foreach ($storage->appearing_property_ids as $property_name => $property_id) { + if (!isset($container_type_part->properties[$property_name])) { + continue; + } + + $property_type = $codebase->properties->hasStorage($property_id) + ? $codebase->properties->getStorage($property_id)->type + : null; + + $properties[$property_name] = $property_type ?? Type::getMixed(); + } + + $replaced_object = TemplateInferredTypeReplacer::replace( + new Union([ + new TObjectWithProperties($properties), + ]), + new TemplateResult( + $storage->template_types ?? [], + $inferred_lower_bounds, + ), + $codebase, + ); + + /** @var TObjectWithProperties */ + return $replaced_object->getSingleAtomic(); + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php index 2e7b7c76..1d264658 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/ObjectComparator.php @@ -5,6 +5,7 @@ namespace Psalm\Internal\Type\Comparator; use Psalm\Codebase; use Psalm\Internal\Analyzer\Statements\ExpressionAnalyzer; use Psalm\Type\Atomic; +use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TIterable; use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; @@ -90,6 +91,8 @@ class ObjectComparator $intersection_container_type_lower = 'object'; } elseif ($intersection_container_type instanceof TTemplateParam) { $intersection_container_type_lower = null; + } elseif ($intersection_container_type instanceof TCallableObject) { + $intersection_container_type_lower = 'callable-object'; } else { $container_was_static = $intersection_container_type->is_static; @@ -134,7 +137,7 @@ class ObjectComparator /** * @param TNamedObject|TTemplateParam|TIterable $type_part - * @return array + * @return array */ private static function getIntersectionTypes(Atomic $type_part): array { @@ -166,8 +169,8 @@ class ObjectComparator } /** - * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties $intersection_input_type - * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties $intersection_container_type + * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $intersection_input_type + * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $intersection_container_type */ private static function isIntersectionShallowlyContainedBy( Codebase $codebase, @@ -268,6 +271,8 @@ class ObjectComparator $intersection_input_type_lower = 'iterable'; } elseif ($intersection_input_type instanceof TObjectWithProperties) { $intersection_input_type_lower = 'object'; + } elseif ($intersection_input_type instanceof TCallableObject) { + $intersection_input_type_lower = 'callable-object'; } else { $input_was_static = $intersection_input_type->is_static; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php index c4465006..d3c1b457 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/Comparator/UnionTypeComparator.php @@ -44,7 +44,7 @@ class UnionTypeComparator bool $allow_interface_equality = false, bool $allow_float_int_equality = true ): bool { - if ($container_type->isMixed()) { + if ($container_type->isVanillaMixed()) { return true; } @@ -63,9 +63,6 @@ class UnionTypeComparator return false; } - if ($container_type->hasMixed() && !$container_type->isEmptyMixed()) { - return true; - } $container_has_template = $container_type->hasTemplateOrStatic(); @@ -178,7 +175,7 @@ class UnionTypeComparator if ($container_required_param_count > $input_all_param_count || $container_all_param_count < $input_required_param_count ) { - return false; + continue; } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php index 5e1d994e..3ce8aedd 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/NegatedAssertionReconciler.php @@ -182,6 +182,10 @@ class NegatedAssertionReconciler extends Reconciler ) { $existing_var_type->removeType('array-key'); $existing_var_type->addType(new TString); + } elseif ($assertion_type instanceof TNonEmptyString + && $existing_var_type->hasString() + ) { + // do nothing } elseif ($assertion instanceof IsClassNotEqual) { // do nothing } elseif ($assertion_type instanceof TClassString && $assertion_type->is_loaded) { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php index 34502e11..9fdd3fcc 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/ParseTreeCreator.php @@ -64,11 +64,14 @@ class ParseTreeCreator $type_token = $this->type_tokens[$this->t]; switch ($type_token[0]) { - case '<': case '{': case ']': throw new TypeParseTreeException('Unexpected token ' . $type_token[0]); + case '<': + $this->handleLessThan(); + break; + case '[': $this->handleOpenSquareBracket(); break; @@ -232,6 +235,29 @@ class ParseTreeCreator $this->current_leaf = $new_parent_leaf; } + private function handleLessThan(): void + { + if (!$this->current_leaf instanceof FieldEllipsis) { + throw new TypeParseTreeException('Unexpected token <'); + } + + $current_parent = $this->current_leaf->parent; + + if (!$current_parent instanceof KeyedArrayTree) { + throw new TypeParseTreeException('Unexpected token <'); + } + + array_pop($current_parent->children); + + $generic_leaf = new GenericTree( + '', + $current_parent, + ); + $current_parent->children []= $generic_leaf; + + $this->current_leaf = $generic_leaf; + } + private function handleOpenSquareBracket(): void { if ($this->current_leaf instanceof Root) { diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php index 44adb58d..57dd5c2b 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/SimpleAssertionReconciler.php @@ -28,6 +28,7 @@ use Psalm\Storage\Assertion\NonEmpty; use Psalm\Storage\Assertion\NonEmptyCountable; use Psalm\Storage\Assertion\Truthy; use Psalm\Type; +use Psalm\Type\Atomic; use Psalm\Type\Atomic\Scalar; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; @@ -71,11 +72,13 @@ use Psalm\Type\Atomic\TScalar; use Psalm\Type\Atomic\TString; use Psalm\Type\Atomic\TTemplateParam; use Psalm\Type\Atomic\TTrue; +use Psalm\Type\Atomic\TValueOf; use Psalm\Type\Reconciler; use Psalm\Type\Union; use function array_map; use function array_merge; +use function array_values; use function assert; use function count; use function explode; @@ -290,7 +293,9 @@ class SimpleAssertionReconciler extends Reconciler if ($assertion_type instanceof TObject) { return self::reconcileObject( + $codebase, $assertion, + $assertion_type, $existing_var_type, $key, $negated, @@ -527,6 +532,10 @@ class SimpleAssertionReconciler extends Reconciler } } + if ($assertion_type instanceof TValueOf) { + return $assertion_type->type; + } + return null; } @@ -1574,7 +1583,9 @@ class SimpleAssertionReconciler extends Reconciler * @param Reconciler::RECONCILIATION_* $failed_reconciliation */ private static function reconcileObject( + Codebase $codebase, Assertion $assertion, + TObject $assertion_type, Union $existing_var_type, ?string $key, bool $negated, @@ -1593,11 +1604,25 @@ class SimpleAssertionReconciler extends Reconciler $object_types = []; $redundant = true; + $assertion_type_is_intersectable_type = Type::isIntersectionType($assertion_type); foreach ($existing_var_atomic_types as $type) { - if ($type->isObjectType()) { - $object_types[] = $type; + if ($assertion_type_is_intersectable_type + && self::areIntersectionTypesAllowed($codebase, $type) + ) { + /** @var TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $assertion_type */ + $object_types[] = $type->addIntersectionType($assertion_type); + $redundant = false; + } elseif ($type->isObjectType()) { + if ($assertion_type_is_intersectable_type + && !self::areIntersectionTypesAllowed($codebase, $type) + ) { + $redundant = false; + } else { + $object_types[] = $type; + } } elseif ($type instanceof TCallable) { - $object_types[] = new TCallableObject(); + $callable_object = new TCallableObject($type->from_docblock, $type); + $object_types[] = $callable_object; $redundant = false; } elseif ($type instanceof TTemplateParam && $type->as->isMixed() @@ -1607,8 +1632,17 @@ class SimpleAssertionReconciler extends Reconciler $redundant = false; } elseif ($type instanceof TTemplateParam) { if ($type->as->hasObject() || $type->as->hasMixed()) { - $type = $type->replaceAs(self::reconcileObject( + /** + * @psalm-suppress PossiblyInvalidArgument This looks wrong, psalm assumes that $assertion_type + * can contain TNamedObject due to the reconciliation above + * regarding {@see Type::isIntersectionType}. Due to the + * native argument type `TObject`, the variable object will + * never be `TNamedObject`. + */ + $reconciled_type = self::reconcileObject( + $codebase, $assertion, + $assertion_type, $type->as, null, false, @@ -1616,7 +1650,8 @@ class SimpleAssertionReconciler extends Reconciler $suppressed_issues, $failed_reconciliation, $is_equality, - )); + ); + $type = $type->replaceAs($reconciled_type); $object_types[] = $type; } @@ -2894,19 +2929,41 @@ class SimpleAssertionReconciler extends Reconciler int &$failed_reconciliation ): Union { $class_name = $class_constant_expression->fq_classlike_name; - $constant_pattern = $class_constant_expression->const_name; - - $resolver = new ClassConstantByWildcardResolver($codebase); - $matched_class_constant_types = $resolver->resolve($class_name, $constant_pattern); - if ($matched_class_constant_types === null) { + if (!$codebase->classlike_storage_provider->has($class_name)) { return $existing_type; } - if ($matched_class_constant_types === []) { + $constant_pattern = $class_constant_expression->const_name; + + $resolver = new ClassConstantByWildcardResolver($codebase); + $matched_class_constant_types = $resolver->resolve( + $class_name, + $constant_pattern, + ); + + if ($matched_class_constant_types === null) { $failed_reconciliation = Reconciler::RECONCILIATION_EMPTY; return Type::getNever(); } - return TypeCombiner::combine($matched_class_constant_types, $codebase); + return TypeCombiner::combine(array_values($matched_class_constant_types), $codebase); + } + + /** + * @psalm-assert-if-true TCallableObject|TObjectWithProperties|TNamedObject $type + */ + private static function areIntersectionTypesAllowed(Codebase $codebase, Atomic $type): bool + { + if ($type instanceof TObjectWithProperties || $type instanceof TCallableObject) { + return true; + } + + if (!$type instanceof TNamedObject || !$codebase->classlike_storage_provider->has($type->value)) { + return false; + } + + $class_storage = $codebase->classlike_storage_provider->get($type->value); + + return !$class_storage->final; } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php index e5a85ddd..e824e76f 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TemplateStandinTypeReplacer.php @@ -7,6 +7,7 @@ use Psalm\Codebase; use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Internal\Codebase\Methods; use Psalm\Internal\Type\Comparator\CallableTypeComparator; +use Psalm\Internal\Type\Comparator\KeyedArrayComparator; use Psalm\Internal\Type\Comparator\UnionTypeComparator; use Psalm\Type; use Psalm\Type\Atomic; @@ -33,7 +34,6 @@ use Psalm\Type\Atomic\TTemplateParamClass; use Psalm\Type\Atomic\TTemplatePropertiesOf; use Psalm\Type\Atomic\TTemplateValueOf; use Psalm\Type\Union; -use Throwable; use function array_fill; use function array_keys; @@ -231,6 +231,17 @@ class TemplateStandinTypeReplacer ); } + if ($atomic_type instanceof TTemplateParam + && isset($template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class]) + ) { + $most_specific_type = self::getMostSpecificTypeFromBounds( + $template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class], + $codebase, + ); + + return array_values($most_specific_type->getAtomicTypes()); + } + if ($atomic_type instanceof TTemplateParamClass && isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class]) ) { @@ -259,11 +270,14 @@ class TemplateStandinTypeReplacer $include_first = true; - if (isset($template_result->template_types[$atomic_type->array_param_name][$atomic_type->defining_class]) + if (isset($template_result->lower_bounds[$atomic_type->array_param_name][$atomic_type->defining_class]) && !empty($template_result->lower_bounds[$atomic_type->offset_param_name]) ) { $array_template_type - = $template_result->template_types[$atomic_type->array_param_name][$atomic_type->defining_class]; + = self::getMostSpecificTypeFromBounds( + $template_result->lower_bounds[$atomic_type->array_param_name][$atomic_type->defining_class], + $codebase, + ); $offset_template_type = self::getMostSpecificTypeFromBounds( array_values($template_result->lower_bounds[$atomic_type->offset_param_name])[0], @@ -317,10 +331,18 @@ class TemplateStandinTypeReplacer $atomic_types = []; $include_first = true; + $template_type = null; - if (isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class])) { + if (isset($template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class])) { + $template_type = self::getMostSpecificTypeFromBounds( + $template_result->lower_bounds[$atomic_type->param_name][$atomic_type->defining_class], + $codebase, + ); + } elseif (isset($template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class])) { $template_type = $template_result->template_types[$atomic_type->param_name][$atomic_type->defining_class]; + } + if ($template_type) { foreach ($template_type->getAtomicTypes() as $template_atomic) { if ($template_atomic instanceof TList) { $template_atomic = $template_atomic->getKeyedArray(); @@ -563,7 +585,7 @@ class TemplateStandinTypeReplacer if (!empty($classlike_storage->template_extended_params[$base_type->value])) { $atomic_input_type = new TGenericObject( - $atomic_input_type->value, + $base_type->value, array_values($classlike_storage->template_extended_params[$base_type->value]), ); @@ -582,6 +604,22 @@ class TemplateStandinTypeReplacer } } + if ($atomic_input_type instanceof TNamedObject + && $base_type instanceof TObjectWithProperties + ) { + $object_with_keys = KeyedArrayComparator::coerceToObjectWithProperties( + $codebase, + $atomic_input_type, + $base_type, + ); + + if ($object_with_keys) { + $matching_atomic_types[$object_with_keys->getId()] = $object_with_keys; + } + + continue; + } + if ($atomic_input_type instanceof TTemplateParam) { $matching_atomic_types = array_merge( $matching_atomic_types, @@ -1233,13 +1271,13 @@ class TemplateStandinTypeReplacer $input_type_params = []; } - try { - $input_class_storage = $codebase->classlike_storage_provider->get($input_type_part->value); - $container_class_storage = $codebase->classlike_storage_provider->get($container_type_part->value); - $container_type_params_covariant = $container_class_storage->template_covariants; - } catch (Throwable $e) { - $input_class_storage = null; - } + $input_class_storage = $codebase->classlike_storage_provider->has($input_type_part->value) + ? $codebase->classlike_storage_provider->get($input_type_part->value) + : null; + + $container_type_params_covariant = $codebase->classlike_storage_provider->has($container_type_part->value) + ? $codebase->classlike_storage_provider->get($container_type_part->value)->template_covariants + : null; if ($input_type_part->value !== $container_type_part->value && $input_class_storage @@ -1266,8 +1304,12 @@ class TemplateStandinTypeReplacer $template_extends = $input_class_storage->template_extended_params; - if (isset($template_extends[$container_type_part->value])) { - $params = $template_extends[$container_type_part->value]; + $container_type_part_value = $container_type_part->value === 'iterable' + ? 'Traversable' + : $container_type_part->value; + + if (isset($template_extends[$container_type_part_value])) { + $params = $template_extends[$container_type_part_value]; $new_input_params = []; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php index 85aad52b..0daa9d29 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeCombiner.php @@ -551,6 +551,8 @@ class TypeCombiner } foreach ($type->type_params as $i => $type_param) { + // See https://github.com/vimeo/psalm/pull/9439#issuecomment-1464563015 + /** @psalm-suppress PropertyTypeCoercion */ $combination->array_type_params[$i] = Type::combineUnionTypes( $combination->array_type_params[$i] ?? null, $type_param, @@ -599,6 +601,8 @@ class TypeCombiner if ($type instanceof TClassStringMap) { foreach ([$type->getStandinKeyParam(), $type->value_param] as $i => $type_param) { + // See https://github.com/vimeo/psalm/pull/9439#issuecomment-1464563015 + /** @psalm-suppress PropertyTypeCoercion */ $combination->array_type_params[$i] = Type::combineUnionTypes( $combination->array_type_params[$i] ?? null, $type_param, @@ -1047,19 +1051,25 @@ class TypeCombiner if (!isset($combination->value_types['string'])) { if ($combination->strings) { if ($type instanceof TNumericString) { - $has_non_numeric_string = false; + $has_only_numeric_strings = true; + $has_only_non_empty_strings = true; foreach ($combination->strings as $string_type) { if (!is_numeric($string_type->value)) { - $has_non_numeric_string = true; - break; + $has_only_numeric_strings = false; + } + + if ($string_type->value === '') { + $has_only_non_empty_strings = false; } } - if ($has_non_numeric_string) { - $combination->value_types['string'] = new TString(); - } else { + if ($has_only_numeric_strings) { $combination->value_types['string'] = $type; + } elseif ($has_only_non_empty_strings) { + $combination->value_types['string'] = new TNonEmptyString(); + } else { + $combination->value_types['string'] = new TString(); } } elseif ($type instanceof TLowercaseString) { $has_non_lowercase_string = false; diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php index 6a521522..e329f92b 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeExpander.php @@ -6,9 +6,6 @@ use Psalm\Codebase; use Psalm\Exception\CircularReferenceException; use Psalm\Exception\UnresolvableConstantException; use Psalm\Internal\Analyzer\Statements\Expression\Fetch\AtomicPropertyFetchAnalyzer; -use Psalm\Internal\Type\SimpleAssertionReconciler; -use Psalm\Internal\Type\SimpleNegatedAssertionReconciler; -use Psalm\Internal\Type\TypeParser; use Psalm\Storage\Assertion\IsType; use Psalm\Type; use Psalm\Type\Atomic; @@ -18,6 +15,7 @@ use Psalm\Type\Atomic\TClassConstant; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TConditional; +use Psalm\Type\Atomic\TEnumCase; use Psalm\Type\Atomic\TGenericObject; use Psalm\Type\Atomic\TInt; use Psalm\Type\Atomic\TIntMask; @@ -41,7 +39,6 @@ use Psalm\Type\Union; use ReflectionProperty; use function array_filter; -use function array_keys; use function array_map; use function array_merge; use function array_values; @@ -49,9 +46,7 @@ use function count; use function get_class; use function is_string; use function reset; -use function strpos; use function strtolower; -use function substr; /** * @internal @@ -77,8 +72,6 @@ class TypeExpander ): Union { $new_return_type_parts = []; - $had_split_values = false; - foreach ($return_type->getAtomicTypes() as $return_type_part) { $parts = self::expandAtomic( $codebase, @@ -94,21 +87,13 @@ class TypeExpander $throw_on_unresolvable_constant, ); - if ($return_type_part instanceof TTypeAlias || count($parts) > 1) { - $had_split_values = true; - } - $new_return_type_parts = [...$new_return_type_parts, ...$parts]; } - if ($had_split_values) { - $fleshed_out_type = TypeCombiner::combine( - $new_return_type_parts, - $codebase, - ); - } else { - $fleshed_out_type = new Union($new_return_type_parts); - } + $fleshed_out_type = TypeCombiner::combine( + $new_return_type_parts, + $codebase, + ); $fleshed_out_type->from_docblock = $return_type->from_docblock; $fleshed_out_type->ignore_nullable_issues = $return_type->ignore_nullable_issues; @@ -146,6 +131,10 @@ class TypeExpander bool $expand_templates = false, bool $throw_on_unresolvable_constant = false ): array { + if ($return_type instanceof TEnumCase) { + return [$return_type]; + } + if ($return_type instanceof TNamedObject || $return_type instanceof TTemplateParam ) { @@ -260,52 +249,18 @@ class TypeExpander return [new TLiteralClassString($return_type->fq_classlike_name)]; } - $class_storage = $codebase->classlike_storage_provider->get($return_type->fq_classlike_name); - - if (strpos($return_type->const_name, '*') !== false) { - $matching_constants = [ - ...array_keys($class_storage->constants), - ...array_keys($class_storage->enum_cases), - ]; - - $const_name_part = substr($return_type->const_name, 0, -1); - - if ($const_name_part) { - $matching_constants = array_filter( - $matching_constants, - static fn($constant_name): bool => $constant_name !== $const_name_part - && strpos($constant_name, $const_name_part) === 0 - ); - } - } else { - $matching_constants = [$return_type->const_name]; + try { + $class_constant = $codebase->classlikes->getClassConstantType( + $return_type->fq_classlike_name, + $return_type->const_name, + ReflectionProperty::IS_PRIVATE, + ); + } catch (CircularReferenceException $e) { + $class_constant = null; } - $matching_constant_types = []; - - foreach ($matching_constants as $matching_constant) { - try { - $class_constant = $codebase->classlikes->getClassConstantType( - $return_type->fq_classlike_name, - $matching_constant, - ReflectionProperty::IS_PRIVATE, - ); - } catch (CircularReferenceException $e) { - $class_constant = null; - } - - if ($class_constant) { - if ($class_constant->isSingle()) { - $matching_constant_types = array_merge( - array_values($class_constant->getAtomicTypes()), - $matching_constant_types, - ); - } - } - } - - if ($matching_constant_types) { - return $matching_constant_types; + if ($class_constant) { + return array_values($class_constant->getAtomicTypes()); } } @@ -369,6 +324,7 @@ class TypeExpander ]; } + /** @psalm-suppress DeprecatedProperty For backwards compatibility, we have to keep this here. */ foreach ($return_type->extra_types ?? [] as $alias) { $more_recursively_fleshed_out_types = self::expandAtomic( $codebase, @@ -1102,7 +1058,7 @@ class TypeExpander } if ($throw_on_unresolvable_constant - && !$codebase->classOrInterfaceExists($type_param->fq_classlike_name) + && !$codebase->classOrInterfaceOrEnumExists($type_param->fq_classlike_name) ) { throw new UnresolvableConstantException($type_param->fq_classlike_name, $type_param->const_name); } @@ -1112,6 +1068,10 @@ class TypeExpander $type_param->fq_classlike_name, $type_param->const_name, ReflectionProperty::IS_PRIVATE, + null, + [], + false, + $return_type instanceof TValueOf, ); } catch (CircularReferenceException $e) { return [$return_type]; @@ -1148,9 +1108,11 @@ class TypeExpander } else { $new_return_types = TValueOf::getValueType(new Union($type_atomics), $codebase); } + if ($new_return_types === null) { return [$return_type]; } + return array_values($new_return_types->getAtomicTypes()); } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php index fc0016f7..35ca41aa 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeParser.php @@ -31,6 +31,7 @@ use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; use Psalm\Type\Atomic\TCallable; use Psalm\Type\Atomic\TCallableKeyedArray; +use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TClassConstant; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClassStringMap; @@ -61,6 +62,7 @@ use Psalm\Type\Atomic\TTemplateParamClass; use Psalm\Type\Atomic\TTemplatePropertiesOf; use Psalm\Type\Atomic\TTemplateValueOf; use Psalm\Type\Atomic\TTypeAlias; +use Psalm\Type\Atomic\TUnknownClassString; use Psalm\Type\Atomic\TValueOf; use Psalm\Type\TypeNode; use Psalm\Type\Union; @@ -69,6 +71,7 @@ use function array_key_exists; use function array_key_first; use function array_keys; use function array_map; +use function array_merge; use function array_pop; use function array_shift; use function array_unique; @@ -91,6 +94,7 @@ use function stripslashes; use function strlen; use function strpos; use function strtolower; +use function strtr; use function substr; /** @@ -420,8 +424,8 @@ class TypeParser return new TLiteralFloat((float) $parse_tree->value, $from_docblock); } - if (preg_match('/^\-?(0|[1-9][0-9]*)$/', $parse_tree->value)) { - return new TLiteralInt((int) $parse_tree->value, $from_docblock); + if (preg_match('/^\-?(0|[1-9]([0-9_]*[0-9])?)$/', $parse_tree->value)) { + return new TLiteralInt((int) strtr($parse_tree->value, ['_' => '']), $from_docblock); } if (!preg_match('@^(\$this|\\\\?[a-zA-Z_\x7f-\xff][\\\\\-0-9a-zA-Z_\x7f-\xff]*)$@', $parse_tree->value)) { @@ -684,6 +688,9 @@ class TypeParser } if ($generic_type_value === 'list') { + if (count($generic_params) > 1) { + throw new TypeParseTreeException('Too many template parameters for list'); + } return Type::getListAtomic($generic_params[0], $from_docblock); } @@ -710,13 +717,25 @@ class TypeParser $types = []; foreach ($generic_params[0]->getAtomicTypes() as $type) { - if (!$type instanceof TNamedObject) { - throw new TypeParseTreeException('Class string param should be a named object'); + if ($type instanceof TNamedObject) { + $types[] = new TClassString($type->value, $type, false, false, false, $from_docblock); + continue; } - $types[] = new TClassString($type->value, $type, false, false, false, $from_docblock); + if ($type instanceof TCallableObject) { + $types[] = new TUnknownClassString($type, false, $from_docblock); + continue; + } + + throw new TypeParseTreeException('class-string param can only target to named or callable objects'); } + assert( + $types !== [], + 'Since `Union` cannot be empty and all non-supported atomics lead to thrown exception,' + .' we can safely assert that the types array is non-empty.', + ); + return new Union($types); } @@ -778,7 +797,7 @@ class TypeParser if ($template_param->getIntersectionTypes()) { throw new TypeParseTreeException( $generic_type_value . '<' . $param_name . '> must be a TTemplateParam' - . ' with no intersection types.', + . ' with no intersection types.', ); } @@ -1093,6 +1112,10 @@ class TypeParser $intersection_types[$name] = $atomic_type; } + if ($intersection_types === []) { + return new TMixed(); + } + $first_type = reset($intersection_types); $last_type = end($intersection_types); @@ -1112,129 +1135,64 @@ class TypeParser } if ($onlyTKeyedArray) { - /** @var non-empty-array */ - $properties = []; - - if ($first_type instanceof TArray) { - array_shift($intersection_types); - } elseif ($last_type instanceof TArray) { - array_pop($intersection_types); - } - - $all_sealed = true; - - /** @var TKeyedArray $intersection_type */ - foreach ($intersection_types as $intersection_type) { - foreach ($intersection_type->properties as $property => $property_type) { - if ($intersection_type->fallback_params !== null) { - $all_sealed = false; - } - - if (!array_key_exists($property, $properties)) { - $properties[$property] = $property_type; - continue; - } - - $new_type = Type::intersectUnionTypes( - $properties[$property], - $property_type, - $codebase, - ); - - if ($new_type === null) { - throw new TypeParseTreeException( - 'Incompatible intersection types for "' . $property . '", ' - . $properties[$property] . ' and ' . $property_type - . ' provided', - ); - } - $properties[$property] = $new_type; - } - } - - $first_or_last_type = $first_type instanceof TArray - ? $first_type - : ($last_type instanceof TArray ? $last_type : null); - - $fallback_params = null; - - if ($first_or_last_type !== null) { - $fallback_params = [ - $first_or_last_type->type_params[0], - $first_or_last_type->type_params[1], - ]; - } elseif (!$all_sealed) { - $fallback_params = [Type::getArrayKey(), Type::getMixed()]; - } - - return new TKeyedArray( - $properties, - null, - $fallback_params, - false, + /** + * @var array $intersection_types + * @var TKeyedArray $first_type + * @var TKeyedArray $last_type + */ + return self::getTypeFromKeyedArrays( + $codebase, + $intersection_types, + $first_type, + $last_type, $from_docblock, ); } - $keyed_intersection_types = []; + $keyed_intersection_types = self::extractKeyedIntersectionTypes( + $codebase, + $intersection_types, + ); - if ($intersection_types[0] instanceof TTypeAlias) { - foreach ($intersection_types as $intersection_type) { - if (!$intersection_type instanceof TTypeAlias) { - throw new TypeParseTreeException( - 'Intersection types with a type alias can only be comprised of other type aliases, ' - . get_class($intersection_type) . ' provided', - ); - } + $intersect_static = false; - $keyed_intersection_types[$intersection_type->getKey()] = $intersection_type; - } + if (isset($keyed_intersection_types['static'])) { + unset($keyed_intersection_types['static']); + $intersect_static = true; + } - $first_type = array_shift($keyed_intersection_types); + if ($keyed_intersection_types === [] && $intersect_static) { + return new TNamedObject('static', false, false, [], $from_docblock); + } - if ($keyed_intersection_types) { - return $first_type->setIntersectionTypes($keyed_intersection_types); - } - } else { - foreach ($intersection_types as $intersection_type) { - if (!$intersection_type instanceof TIterable - && !$intersection_type instanceof TNamedObject - && !$intersection_type instanceof TTemplateParam - && !$intersection_type instanceof TObjectWithProperties - ) { - throw new TypeParseTreeException( - 'Intersection types must be all objects, ' - . get_class($intersection_type) . ' provided', - ); - } + $first_type = array_shift($keyed_intersection_types); - $keyed_intersection_types[$intersection_type instanceof TIterable - ? $intersection_type->getId() - : $intersection_type->getKey()] = $intersection_type; - } + // Keyed array intersection are merged together and are not combinable with object-types + if ($first_type instanceof TKeyedArray) { + // assume all types are keyed arrays + array_unshift($keyed_intersection_types, $first_type); + /** @var TKeyedArray $last_type */ + $last_type = end($keyed_intersection_types); - $intersect_static = false; + /** @var array $keyed_intersection_types */ + return self::getTypeFromKeyedArrays( + $codebase, + $keyed_intersection_types, + $first_type, + $last_type, + $from_docblock, + ); + } - if (isset($keyed_intersection_types['static'])) { - unset($keyed_intersection_types['static']); - $intersect_static = true; - } + if ($intersect_static + && $first_type instanceof TNamedObject + ) { + $first_type->is_static = true; + } - if (!$keyed_intersection_types && $intersect_static) { - return new TNamedObject('static', false, false, [], $from_docblock); - } - - $first_type = array_shift($keyed_intersection_types); - - if ($intersect_static - && $first_type instanceof TNamedObject - ) { - $first_type->is_static = true; - } - - if ($keyed_intersection_types) { - return $first_type->setIntersectionTypes($keyed_intersection_types); - } + if ($keyed_intersection_types) { + /** @var non-empty-array $keyed_intersection_types */ + return $first_type->setIntersectionTypes($keyed_intersection_types); } return $first_type; @@ -1397,6 +1355,16 @@ class TypeParser $sealed = true; + $extra_params = null; + + $last_property_branch = end($parse_tree->children); + if ($last_property_branch instanceof GenericTree + && $last_property_branch->value === '' + ) { + $extra_params = $last_property_branch->children; + array_pop($parse_tree->children); + } + foreach ($parse_tree->children as $i => $property_branch) { $class_string = false; @@ -1517,14 +1485,249 @@ class TypeParser return new TArray([Type::getNever($from_docblock), Type::getNever($from_docblock)], $from_docblock); } + if ($extra_params) { + if ($is_list && count($extra_params) !== 1) { + throw new TypeParseTreeException('Must have exactly one extra field!'); + } + if (!$is_list && count($extra_params) !== 2) { + throw new TypeParseTreeException('Must have exactly two extra fields!'); + } + $final_extra_params = $is_list ? [Type::getListKey(true)] : []; + foreach ($extra_params as $child_tree) { + $child_type = self::getTypeFromTree( + $child_tree, + $codebase, + null, + $template_type_map, + $type_aliases, + $from_docblock, + ); + if ($child_type instanceof Atomic) { + $child_type = new Union([$child_type]); + } + $final_extra_params []= $child_type; + } + $extra_params = $final_extra_params; + } return new $class( $properties, $class_strings, - $sealed + $extra_params ?? ($sealed ? null - : [$is_list ? Type::getListKey() : Type::getArrayKey(), Type::getMixed()], + : [$is_list ? Type::getListKey() : Type::getArrayKey(), Type::getMixed()] + ), $is_list, $from_docblock, ); } + + /** + * @param TNamedObject|TObjectWithProperties|TCallableObject|TIterable|TTemplateParam|TKeyedArray $intersection_type + */ + private static function extractIntersectionKey(Atomic $intersection_type): string + { + return $intersection_type instanceof TIterable || $intersection_type instanceof TKeyedArray + ? $intersection_type->getId() + : $intersection_type->getKey(); + } + + /** + * @param non-empty-array $intersection_types + * @return non-empty-array + */ + private static function extractKeyedIntersectionTypes( + Codebase $codebase, + array $intersection_types + ): array { + $keyed_intersection_types = []; + $callable_intersection = null; + $any_object_type_found = $any_array_found = false; + + $normalized_intersection_types = self::resolveTypeAliases( + $codebase, + $intersection_types, + ); + + foreach ($normalized_intersection_types as $intersection_type) { + if ($intersection_type instanceof TKeyedArray + && !$intersection_type instanceof TCallableKeyedArray + ) { + $any_array_found = true; + + if ($any_object_type_found) { + throw new TypeParseTreeException( + 'The intersection type must not mix array and object types!', + ); + } + + $keyed_intersection_types[self::extractIntersectionKey($intersection_type)] = $intersection_type; + continue; + } + + $any_object_type_found = true; + + if ($intersection_type instanceof TIterable + || $intersection_type instanceof TNamedObject + || $intersection_type instanceof TTemplateParam + || $intersection_type instanceof TObjectWithProperties + ) { + $keyed_intersection_types[self::extractIntersectionKey($intersection_type)] = $intersection_type; + continue; + } + + if (get_class($intersection_type) === TObject::class) { + continue; + } + + if ($intersection_type instanceof TCallable) { + if ($callable_intersection !== null) { + throw new TypeParseTreeException( + 'The intersection type must not contain more than one callable type!', + ); + } + $callable_intersection = $intersection_type; + continue; + } + + throw new TypeParseTreeException( + 'Intersection types must be all objects, ' + . get_class($intersection_type) . ' provided', + ); + } + + if ($callable_intersection !== null) { + $callable_object_type = new TCallableObject( + $callable_intersection->from_docblock, + $callable_intersection, + ); + + $keyed_intersection_types[self::extractIntersectionKey($callable_object_type)] = $callable_object_type; + } + + if ($any_object_type_found && $any_array_found) { + throw new TypeParseTreeException( + 'Intersection types must be all objects or all keyed array.', + ); + } + + assert($keyed_intersection_types !== []); + + return $keyed_intersection_types; + } + + /** + * @param array $intersection_types + * @return array + */ + private static function resolveTypeAliases(Codebase $codebase, array $intersection_types): array + { + $normalized_intersection_types = []; + $modified = false; + foreach ($intersection_types as $intersection_type) { + if (!$intersection_type instanceof TTypeAlias) { + $normalized_intersection_types[] = [$intersection_type]; + continue; + } + + $modified = true; + + $normalized_intersection_types[] = TypeExpander::expandAtomic( + $codebase, + $intersection_type, + null, + null, + null, + true, + false, + false, + true, + true, + true, + ); + } + + if ($modified === false) { + return $intersection_types; + } + + return self::resolveTypeAliases( + $codebase, + array_merge(...$normalized_intersection_types), + ); + } + + /** + * @param array $intersection_types + * @param TKeyedArray|TArray $first_type + * @param TKeyedArray|TArray $last_type + */ + private static function getTypeFromKeyedArrays( + Codebase $codebase, + array $intersection_types, + Atomic $first_type, + Atomic $last_type, + bool $from_docblock + ): Atomic { + /** @var non-empty-array */ + $properties = []; + + if ($first_type instanceof TArray) { + array_shift($intersection_types); + } elseif ($last_type instanceof TArray) { + array_pop($intersection_types); + } + + $all_sealed = true; + + foreach ($intersection_types as $intersection_type) { + if ($intersection_type->fallback_params !== null) { + $all_sealed = false; + } + + foreach ($intersection_type->properties as $property => $property_type) { + if (!array_key_exists($property, $properties)) { + $properties[$property] = $property_type; + continue; + } + + $new_type = Type::intersectUnionTypes( + $properties[$property], + $property_type, + $codebase, + ); + + if ($new_type === null) { + throw new TypeParseTreeException( + 'Incompatible intersection types for "' . $property . '", ' + . $properties[$property] . ' and ' . $property_type + . ' provided', + ); + } + $properties[$property] = $new_type; + } + } + + $first_or_last_type = $first_type instanceof TArray + ? $first_type + : ($last_type instanceof TArray ? $last_type : null); + + $fallback_params = null; + + if ($first_or_last_type !== null) { + $fallback_params = [ + $first_or_last_type->type_params[0], + $first_or_last_type->type_params[1], + ]; + } elseif (!$all_sealed) { + $fallback_params = [Type::getArrayKey(), Type::getMixed()]; + } + + return new TKeyedArray( + $properties, + null, + $fallback_params, + false, + $from_docblock, + ); + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php index ca7f1034..66e463e4 100644 --- a/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php +++ b/vendor/vimeo/psalm/src/Psalm/Internal/Type/TypeTokenizer.php @@ -41,6 +41,7 @@ class TypeTokenizer 'non-empty-array' => true, 'non-empty-string' => true, 'non-falsy-string' => true, + 'truthy-string' => true, 'iterable' => true, 'null' => true, 'mixed' => true, @@ -107,7 +108,6 @@ class TypeTokenizer * contains the string token and the second element contains its offset, * * @return list - * @psalm-suppress PossiblyUndefinedIntArrayOffset */ public static function tokenize(string $string_type, bool $ignore_space = true): array { diff --git a/vendor/vimeo/psalm/src/Psalm/Issue/InheritorViolation.php b/vendor/vimeo/psalm/src/Psalm/Issue/InheritorViolation.php new file mode 100644 index 00000000..8e0a7a08 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Issue/InheritorViolation.php @@ -0,0 +1,9 @@ + 0) { if ($issue_baseline[$file][$type]['o'] === count($issue_baseline[$file][$type]['s'])) { $position = array_search( - trim($issue_data->selected_text), + str_replace("\r\n", "\n", trim($issue_data->selected_text)), $issue_baseline[$file][$type]['s'], true, ); diff --git a/vendor/vimeo/psalm/src/Psalm/Plugin/EventHandler/BeforeExpressionAnalysisInterface.php b/vendor/vimeo/psalm/src/Psalm/Plugin/EventHandler/BeforeExpressionAnalysisInterface.php new file mode 100644 index 00000000..60d9f47a --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Plugin/EventHandler/BeforeExpressionAnalysisInterface.php @@ -0,0 +1,15 @@ + + */ + private array $file_replacements; + + /** + * Called before an expression is checked + * + * @param list $file_replacements + * @internal + */ + public function __construct( + Expr $expr, + Context $context, + StatementsSource $statements_source, + Codebase $codebase, + array $file_replacements = [] + ) { + $this->expr = $expr; + $this->context = $context; + $this->statements_source = $statements_source; + $this->codebase = $codebase; + $this->file_replacements = $file_replacements; + } + + public function getExpr(): Expr + { + return $this->expr; + } + + public function getContext(): Context + { + return $this->context; + } + + public function getStatementsSource(): StatementsSource + { + return $this->statements_source; + } + + public function getCodebase(): Codebase + { + return $this->codebase; + } + + /** + * @return list + */ + public function getFileReplacements(): array + { + return $this->file_replacements; + } + + /** + * @param list $file_replacements + */ + public function setFileReplacements(array $file_replacements): void + { + $this->file_replacements = $file_replacements; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Storage/ClassConstantStorage.php b/vendor/vimeo/psalm/src/Psalm/Storage/ClassConstantStorage.php index 66917f81..9c6d7cfc 100644 --- a/vendor/vimeo/psalm/src/Psalm/Storage/ClassConstantStorage.php +++ b/vendor/vimeo/psalm/src/Psalm/Storage/ClassConstantStorage.php @@ -7,6 +7,9 @@ use Psalm\Internal\Analyzer\ClassLikeAnalyzer; use Psalm\Internal\Scanner\UnresolvedConstantComponent; use Psalm\Type\Union; +use function array_values; +use function property_exists; + /** * @psalm-suppress PossiblyUnusedProperty * @psalm-immutable @@ -88,4 +91,36 @@ final class ClassConstantStorage $this->suppressed_issues = $suppressed_issues; $this->description = $description; } + + /** + * Used in the Language Server + */ + public function getHoverMarkdown(string $const): string + { + switch ($this->visibility) { + case ClassLikeAnalyzer::VISIBILITY_PRIVATE: + $visibility_text = 'private'; + break; + + case ClassLikeAnalyzer::VISIBILITY_PROTECTED: + $visibility_text = 'protected'; + break; + + default: + $visibility_text = 'public'; + } + + $value = ''; + if ($this->type) { + $types = $this->type->getAtomicTypes(); + $type = array_values($types)[0]; + if (property_exists($type, 'value')) { + /** @psalm-suppress UndefinedPropertyFetch */ + $value = " = {$type->value};"; + } + } + + + return "$visibility_text const $const$value"; + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Storage/ClassLikeStorage.php b/vendor/vimeo/psalm/src/Psalm/Storage/ClassLikeStorage.php index 45fa635d..5be0d522 100644 --- a/vendor/vimeo/psalm/src/Psalm/Storage/ClassLikeStorage.php +++ b/vendor/vimeo/psalm/src/Psalm/Storage/ClassLikeStorage.php @@ -4,6 +4,8 @@ namespace Psalm\Storage; use Psalm\Aliases; use Psalm\CodeLocation; +use Psalm\Codebase; +use Psalm\Config; use Psalm\Internal\Analyzer\ClassLikeAnalyzer; use Psalm\Internal\MethodIdentifier; use Psalm\Internal\Type\TypeAlias\ClassTypeAlias; @@ -66,14 +68,14 @@ final class ClassLikeStorage implements HasAttributesInterface public $mixin_declaring_fqcln; /** - * @var bool + * @var ?bool */ - public $sealed_properties = false; + public $sealed_properties = null; /** - * @var bool + * @var ?bool */ - public $sealed_methods = false; + public $sealed_methods = null; /** * @var bool @@ -313,6 +315,11 @@ final class ClassLikeStorage implements HasAttributesInterface */ public $appearing_property_ids = []; + /** + * @var ?Union + */ + public $inheritors = null; + /** * @var array */ @@ -485,6 +492,24 @@ final class ClassLikeStorage implements HasAttributesInterface return $this->attributes; } + public function hasAttributeIncludingParents( + string $fq_class_name, + Codebase $codebase + ): bool { + if ($this->hasAttribute($fq_class_name)) { + return true; + } + + foreach ($this->parent_classes as $parent_class) { + $parent_class_storage = $codebase->classlike_storage_provider->get($parent_class); + if ($parent_class_storage->hasAttribute($fq_class_name)) { + return true; + } + } + + return false; + } + /** * Get the template constraint types for the class. * @@ -500,4 +525,25 @@ final class ClassLikeStorage implements HasAttributesInterface return $type_params; } + + public function hasSealedProperties(Config $config): bool + { + return $this->sealed_properties ?? $config->seal_all_properties; + } + + public function hasSealedMethods(Config $config): bool + { + return $this->sealed_methods ?? $config->seal_all_methods; + } + + private function hasAttribute(string $fq_class_name): bool + { + foreach ($this->attributes as $attribute) { + if ($fq_class_name === $attribute->fq_class_name) { + return true; + } + } + + return false; + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Storage/FunctionLikeStorage.php b/vendor/vimeo/psalm/src/Psalm/Storage/FunctionLikeStorage.php index 63f3c895..929e2b84 100644 --- a/vendor/vimeo/psalm/src/Psalm/Storage/FunctionLikeStorage.php +++ b/vendor/vimeo/psalm/src/Psalm/Storage/FunctionLikeStorage.php @@ -10,6 +10,7 @@ use Psalm\Type\Union; use function array_column; use function array_fill_keys; use function array_map; +use function count; use function implode; abstract class FunctionLikeStorage implements HasAttributesInterface @@ -245,30 +246,53 @@ abstract class FunctionLikeStorage implements HasAttributesInterface public bool $public_api = false; - public function __toString(): string + /** + * Used in the Language Server + */ + public function getHoverMarkdown(): string { - return $this->getSignature(false); + $params = count($this->params) > 0 ? "\n" . implode( + ",\n", + array_map( + function (FunctionLikeParameter $param): string { + $realType = $param->type ?: 'mixed'; + return " {$realType} \${$param->name}"; + }, + $this->params, + ), + ) . "\n" : ''; + $return_type = $this->return_type ?: 'mixed'; + $symbol_text = "function {$this->cased_name}({$params}): {$return_type}"; + + if (!$this instanceof MethodStorage) { + return $symbol_text; + } + + switch ($this->visibility) { + case ClassLikeAnalyzer::VISIBILITY_PRIVATE: + $visibility_text = 'private'; + break; + + case ClassLikeAnalyzer::VISIBILITY_PROTECTED: + $visibility_text = 'protected'; + break; + + default: + $visibility_text = 'public'; + } + + return $visibility_text . ' ' . $symbol_text; } - public function getSignature(bool $allow_newlines): string + public function getCompletionSignature(): string { - $newlines = $allow_newlines && !empty($this->params); - - $symbol_text = 'function ' . $this->cased_name . '(' - . ($newlines ? "\n" : '') - . implode( - ',' . ($newlines ? "\n" : ' '), - array_map( - static fn(FunctionLikeParameter $param): string => - ($newlines ? ' ' : '') - . ($param->type ? $param->type->getId(false) : 'mixed') - . ' $' . $param->name, - $this->params, - ), - ) - . ($newlines ? "\n" : '') - . ') : ' - . ($this->return_type ?: 'mixed'); + $symbol_text = 'function ' . $this->cased_name . '(' . implode( + ',', + array_map( + fn(FunctionLikeParameter $param): string => ($param->type ?: 'mixed') . ' $' . $param->name, + $this->params, + ), + ) . ') : ' . ($this->return_type ?: 'mixed'); if (!$this instanceof MethodStorage) { return $symbol_text; @@ -317,4 +341,18 @@ abstract class FunctionLikeStorage implements HasAttributesInterface { return $this->attributes; } + + public function __toString(): string + { + return $this->getCompletionSignature(); + } + + /** + * @deprecated will be removed in Psalm 6. use {@see FunctionLikeStorage::getCompletionSignature()} instead + * @psalm-suppress PossiblyUnusedParam, PossiblyUnusedMethod + */ + public function getSignature(bool $allow_newlines): string + { + return $this->getCompletionSignature(); + } } diff --git a/vendor/vimeo/psalm/src/Psalm/Type.php b/vendor/vimeo/psalm/src/Psalm/Type.php index 4a066707..3b98f05c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type.php +++ b/vendor/vimeo/psalm/src/Psalm/Type.php @@ -15,6 +15,7 @@ use Psalm\Type\Atomic; use Psalm\Type\Atomic\TArray; use Psalm\Type\Atomic\TArrayKey; use Psalm\Type\Atomic\TBool; +use Psalm\Type\Atomic\TCallableObject; use Psalm\Type\Atomic\TClassString; use Psalm\Type\Atomic\TClosure; use Psalm\Type\Atomic\TFalse; @@ -483,12 +484,17 @@ abstract class Type } private static ?Union $listKey = null; + private static ?Union $listKeyFromDocblock = null; /** * @psalm-pure + * @psalm-suppress ImpureStaticProperty Used for caching */ - public static function getListKey(): Union + public static function getListKey(bool $from_docblock = false): Union { + if ($from_docblock) { + return self::$listKeyFromDocblock ??= new Union([new TIntRange(0, null, true)]); + } return self::$listKey ??= new Union([new TIntRange(0, null)]); } @@ -962,10 +968,18 @@ abstract class Type private static function hasIntersection(Atomic $type): bool { - return ($type instanceof TIterable - || $type instanceof TNamedObject - || $type instanceof TTemplateParam - || $type instanceof TObjectWithProperties - ) && $type->extra_types; + return self::isIntersectionType($type) && $type->extra_types; + } + + /** + * @psalm-assert-if-true TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $type + */ + public static function isIntersectionType(Atomic $type): bool + { + return $type instanceof TNamedObject + || $type instanceof TTemplateParam + || $type instanceof TIterable + || $type instanceof TObjectWithProperties + || $type instanceof TCallableObject; } } diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/CallableTrait.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/CallableTrait.php index 90f70aad..4ca12639 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/CallableTrait.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/CallableTrait.php @@ -38,6 +38,7 @@ trait CallableTrait * Constructs a new instance of a generic type * * @param list $params + * @deprecated */ public function __construct( string $value = 'callable', @@ -77,11 +78,10 @@ trait CallableTrait $cloned->is_pure = $is_pure; return $cloned; } - public function getKey(bool $include_extra = true): string + + public function getParamString(): string { $param_string = ''; - $return_type_string = ''; - if ($this->params !== null) { $param_string .= '('; foreach ($this->params as $i => $param) { @@ -95,12 +95,27 @@ trait CallableTrait $param_string .= ')'; } + return $param_string; + } + + public function getReturnTypeString(): string + { + $return_type_string = ''; + if ($this->return_type !== null) { $return_type_multiple = count($this->return_type->getAtomicTypes()) > 1; $return_type_string = ':' . ($return_type_multiple ? '(' : '') . $this->return_type->getId() . ($return_type_multiple ? ')' : ''); } + return $return_type_string; + } + + public function getKey(bool $include_extra = true): string + { + $param_string = $this->getParamString(); + $return_type_string = $this->getReturnTypeString(); + return ($this->is_pure ? 'pure-' : ($this->is_pure === null ? '' : 'impure-')) . $this->value . $param_string . $return_type_string; } diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/HasIntersectionTrait.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/HasIntersectionTrait.php index c5a88d55..9a8bc286 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/HasIntersectionTrait.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/HasIntersectionTrait.php @@ -18,7 +18,7 @@ use function implode; trait HasIntersectionTrait { /** - * @var array + * @var array */ public array $extra_types = []; @@ -39,7 +39,7 @@ trait HasIntersectionTrait '&', array_map( /** - * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties $extra_type + * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $extra_type */ static fn(Atomic $extra_type): string => $extra_type->toNamespacedString( $namespace, @@ -53,7 +53,7 @@ trait HasIntersectionTrait } /** - * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties $type + * @param TNamedObject|TTemplateParam|TIterable|TObjectWithProperties|TCallableObject $type * @return static */ public function addIntersectionType(Atomic $type): self @@ -65,7 +65,7 @@ trait HasIntersectionTrait } /** - * @param array $types + * @param array $types * @return static */ public function setIntersectionTypes(array $types): self @@ -79,7 +79,7 @@ trait HasIntersectionTrait } /** - * @return array + * @return array */ public function getIntersectionTypes(): array { @@ -87,7 +87,7 @@ trait HasIntersectionTrait } /** - * @return array|null + * @return array|null */ protected function replaceIntersectionTemplateTypesWithArgTypes( TemplateResult $template_result, @@ -125,7 +125,7 @@ trait HasIntersectionTrait } /** - * @return array|null + * @return array|null */ protected function replaceIntersectionTemplateTypesWithStandins( TemplateResult $template_result, diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TArray.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TArray.php index b3941efb..06477607 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TArray.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TArray.php @@ -41,7 +41,7 @@ class TArray extends Atomic public function __construct(array $type_params, bool $from_docblock = false) { $this->type_params = $type_params; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallable.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallable.php index 7266f17e..7ef847d4 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallable.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallable.php @@ -5,7 +5,9 @@ namespace Psalm\Type\Atomic; use Psalm\Codebase; use Psalm\Internal\Analyzer\StatementsAnalyzer; use Psalm\Internal\Type\TemplateResult; +use Psalm\Storage\FunctionLikeParameter; use Psalm\Type\Atomic; +use Psalm\Type\Union; /** * Denotes the `callable` type. Can result from an `is_callable` check. @@ -21,6 +23,25 @@ final class TCallable extends Atomic */ public $value; + /** + * Constructs a new instance of a generic type + * + * @param list $params + */ + public function __construct( + string $value = 'callable', + ?array $params = null, + ?Union $return_type = null, + ?bool $is_pure = null, + bool $from_docblock = false + ) { + $this->value = $value; + $this->params = $params; + $this->return_type = $return_type; + $this->is_pure = $is_pure; + parent::__construct($from_docblock); + } + /** * @param array $aliased_classes */ diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallableObject.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallableObject.php index db998562..3287617e 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallableObject.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TCallableObject.php @@ -9,9 +9,24 @@ namespace Psalm\Type\Atomic; */ final class TCallableObject extends TObject { + use HasIntersectionTrait; + + public ?TCallable $callable; + + public function __construct(bool $from_docblock = false, ?TCallable $callable = null) + { + parent::__construct($from_docblock); + $this->callable = $callable; + } + public function getKey(bool $include_extra = true): string { - return 'callable-object'; + $key = 'callable-object'; + if ($this->callable !== null) { + $key .= $this->callable->getParamString() . $this->callable->getReturnTypeString(); + } + + return $key; } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassConstant.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassConstant.php index db10f48b..d0e14c11 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassConstant.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassConstant.php @@ -22,7 +22,7 @@ final class TClassConstant extends Atomic { $this->fq_classlike_name = $fq_classlike_name; $this->const_name = $const_name; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassString.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassString.php index ff611d08..723f8f40 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassString.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassString.php @@ -54,7 +54,7 @@ class TClassString extends TString $this->is_loaded = $is_loaded; $this->is_interface = $is_interface; $this->is_enum = $is_enum; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } /** * @return static diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassStringMap.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassStringMap.php index 9cd25109..d15d297f 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassStringMap.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClassStringMap.php @@ -46,7 +46,7 @@ final class TClassStringMap extends Atomic $this->param_name = $param_name; $this->as_type = $as_type; $this->value_param = $value_param; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getId(bool $exact = true, bool $nested = false): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClosure.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClosure.php index 3ccabeeb..97949adc 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClosure.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TClosure.php @@ -26,7 +26,7 @@ final class TClosure extends TNamedObject /** * @param list $params * @param array $byref_uses - * @param array $extra_types + * @param array $extra_types */ public function __construct( string $value = 'callable', @@ -37,13 +37,17 @@ final class TClosure extends TNamedObject array $extra_types = [], bool $from_docblock = false ) { - $this->value = $value; $this->params = $params; $this->return_type = $return_type; $this->is_pure = $is_pure; $this->byref_uses = $byref_uses; - $this->extra_types = $extra_types; - $this->from_docblock = $from_docblock; + parent::__construct( + $value, + false, + false, + $extra_types, + $from_docblock, + ); } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TConditional.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TConditional.php index 84e0747b..9d3bbc92 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TConditional.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TConditional.php @@ -60,7 +60,7 @@ final class TConditional extends Atomic $this->conditional_type = $conditional_type; $this->if_type = $if_type; $this->else_type = $else_type; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function setTypes( diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetClass.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetClass.php index 61ebc89a..168355da 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetClass.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetClass.php @@ -30,6 +30,7 @@ final class TDependentGetClass extends TString implements DependentType { $this->typeof = $typeof; $this->as_type = $as_type; + parent::__construct(false); } public function getId(bool $exact = true, bool $nested = false): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetDebugType.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetDebugType.php index 8f33a19c..c71b9b71 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetDebugType.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetDebugType.php @@ -22,6 +22,7 @@ final class TDependentGetDebugType extends TString implements DependentType public function __construct(string $typeof) { $this->typeof = $typeof; + parent::__construct(false); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetType.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetType.php index abec9425..ca1c4f08 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetType.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentGetType.php @@ -22,6 +22,7 @@ final class TDependentGetType extends TString public function __construct(string $typeof) { $this->typeof = $typeof; + parent::__construct(false); } public function canBeFullyExpressedInPhp(int $analysis_php_version_id): bool diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentListKey.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentListKey.php index 62cf48d2..042c95d4 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentListKey.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TDependentListKey.php @@ -23,6 +23,7 @@ final class TDependentListKey extends TInt implements DependentType public function __construct(string $var_id) { $this->var_id = $var_id; + parent::__construct(false); } public function getId(bool $exact = true, bool $nested = false): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TGenericObject.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TGenericObject.php index 388ed91a..45aa50b8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TGenericObject.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TGenericObject.php @@ -37,7 +37,7 @@ final class TGenericObject extends TNamedObject /** * @param string $value the name of the object * @param non-empty-list $type_params - * @param array $extra_types + * @param array $extra_types */ public function __construct( string $value, @@ -51,12 +51,15 @@ final class TGenericObject extends TNamedObject $value = substr($value, 1); } - $this->value = $value; $this->type_params = $type_params; $this->remapped_params = $remapped_params; - $this->is_static = $is_static; - $this->extra_types = $extra_types; - $this->from_docblock = $from_docblock; + parent::__construct( + $value, + $is_static, + false, + $extra_types, + $from_docblock, + ); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMask.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMask.php index 09a2e83c..e6e57539 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMask.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMask.php @@ -19,7 +19,7 @@ final class TIntMask extends TInt public function __construct(array $values, bool $from_docblock = false) { $this->values = $values; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMaskOf.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMaskOf.php index 0048ee6f..d5a4ae79 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMaskOf.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntMaskOf.php @@ -22,7 +22,7 @@ final class TIntMaskOf extends TInt public function __construct(Atomic $value, bool $from_docblock = false) { $this->value = $value; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntRange.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntRange.php index ad255472..dd0c9fa8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntRange.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIntRange.php @@ -35,8 +35,8 @@ final class TIntRange extends TInt ) { $this->min_bound = $min_bound; $this->max_bound = $max_bound; - $this->from_docblock = $from_docblock; $this->dependent_list_key = $dependent_list_key; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIterable.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIterable.php index da9fce58..6b6c9ea3 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIterable.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TIterable.php @@ -43,7 +43,7 @@ final class TIterable extends Atomic /** * @param array{Union, Union}|array $type_params - * @param array $extra_types + * @param array $extra_types */ public function __construct(array $type_params = [], array $extra_types = [], bool $from_docblock = false) { @@ -54,7 +54,7 @@ final class TIterable extends Atomic $this->type_params = [Type::getMixed(), Type::getMixed()]; } $this->extra_types = $extra_types; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyOf.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyOf.php index 56fdf8f9..09762e73 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyOf.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyOf.php @@ -21,7 +21,7 @@ final class TKeyOf extends TArrayKey public function __construct(Union $type, bool $from_docblock = false) { $this->type = $type; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyedArray.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyedArray.php index 45dfb6b2..8510b83d 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyedArray.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TKeyedArray.php @@ -10,10 +10,6 @@ use Psalm\Internal\Type\TemplateStandinTypeReplacer; use Psalm\Internal\Type\TypeCombiner; use Psalm\Type; use Psalm\Type\Atomic; -use Psalm\Type\Atomic\TArray; -use Psalm\Type\Atomic\TLiteralClassString; -use Psalm\Type\Atomic\TLiteralInt; -use Psalm\Type\Atomic\TNonEmptyArray; use Psalm\Type\Union; use UnexpectedValueException; @@ -49,7 +45,7 @@ class TKeyedArray extends Atomic /** * If the shape has fallback params then they are here * - * @var ?list{Union, Union} + * @var array{Union, Union}|null */ public $fallback_params; @@ -67,7 +63,7 @@ class TKeyedArray extends Atomic * Constructs a new instance of a generic type * * @param non-empty-array $properties - * @param ?list{Union, Union} $fallback_params + * @param array{Union, Union}|null $fallback_params * @param array $class_strings */ public function __construct( @@ -84,7 +80,6 @@ class TKeyedArray extends Atomic $this->class_strings = $class_strings; $this->fallback_params = $fallback_params; $this->is_list = $is_list; - $this->from_docblock = $from_docblock; if ($this->is_list) { $last_k = -1; $had_possibly_undefined = false; @@ -100,6 +95,7 @@ class TKeyedArray extends Atomic $last_k = $k; } } + parent::__construct($from_docblock); } /** @@ -144,15 +140,29 @@ class TKeyedArray extends Atomic return $cloned; } + public function isSealed(): bool + { + return $this->fallback_params === null; + } + + /** + * @psalm-assert-if-true list{Union} $this->properties + * @psalm-assert-if-true list{Union, Union} $this->fallback_params + */ + public function isGenericList(): bool + { + return $this->is_list + && count($this->properties) === 1 + && $this->fallback_params + && $this->properties[0]->equals($this->fallback_params[1], true, true, false); + } + public function getId(bool $exact = true, bool $nested = false): string { $property_strings = []; if ($this->is_list) { - if (count($this->properties) === 1 - && $this->fallback_params - && $this->properties[0]->equals($this->fallback_params[1], true, true, false) - ) { + if ($this->isGenericList()) { $t = $this->properties[0]->possibly_undefined ? 'list' : 'non-empty-list'; return "$t<".$this->fallback_params[1]->getId($exact).'>'; } @@ -192,8 +202,11 @@ class TKeyedArray extends Atomic } $params_part = $this->fallback_params !== null - ? ', ...<' . $this->fallback_params[0]->getId($exact) . ', ' - . $this->fallback_params[1]->getId($exact) . '>' + ? ', ...<' . ($this->is_list + ? $this->fallback_params[1]->getId($exact) + : $this->fallback_params[0]->getId($exact) . ', ' + . $this->fallback_params[1]->getId($exact) + ) . '>' : ''; return $key . '{' . implode(', ', $property_strings) . $params_part . '}'; @@ -410,7 +423,7 @@ class TKeyedArray extends Atomic public function isNonEmpty(): bool { - if ($this->is_list) { + if ($this->isGenericList()) { return !$this->properties[0]->possibly_undefined; } foreach ($this->properties as $property) { @@ -498,6 +511,35 @@ class TKeyedArray extends Atomic bool $add_lower_bound = false, int $depth = 0 ): self { + if ($input_type instanceof TKeyedArray + && $input_type->is_list + && $input_type->isSealed() + && $this->isGenericList() + ) { + $replaced_list_type = $this + ->getGenericArrayType() + ->replaceTemplateTypesWithStandins( + $template_result, + $codebase, + $statements_analyzer, + $input_type->getGenericArrayType(), + $input_arg_offset, + $calling_class, + $calling_function, + $replace, + $add_lower_bound, + $depth, + ) + ->type_params[1] + ->setPossiblyUndefined(!$this->isNonEmpty()); + + $cloned = clone $this; + $cloned->properties = [$replaced_list_type]; + $cloned->fallback_params = [$this->fallback_params[1], $replaced_list_type]; + + return $cloned; + } + $properties = $this->properties; foreach ($properties as $offset => $property) { @@ -663,8 +705,22 @@ class TKeyedArray extends Atomic */ private function escapeAndQuote($name) { - if (is_string($name) && ($name === '' || preg_match('/[^a-zA-Z0-9_]/', $name))) { - $name = '\'' . str_replace("\n", '\n', addslashes($name)) . '\''; + if (is_string($name)) { + $quote = false; + + if ($name === '' || preg_match('/[^a-zA-Z0-9_]/', $name)) { + $quote = true; + } + + if (preg_match('/^-?[1-9][0-9]*$/', $name) + && (string)(int) $name !== $name // overflow occured + ) { + $quote = true; + } + + if ($quote) { + $name = '\'' . str_replace("\n", '\n', addslashes($name)) . '\''; + } } return $name; diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TList.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TList.php index e21ca39c..9e2c611c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TList.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TList.php @@ -42,7 +42,7 @@ class TList extends Atomic public function __construct(Union $type_param, bool $from_docblock = false) { $this->type_param = $type_param; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralFloat.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralFloat.php index 886c9152..c4e1a7fe 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralFloat.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralFloat.php @@ -15,7 +15,7 @@ final class TLiteralFloat extends TFloat public function __construct(float $value, bool $from_docblock = false) { $this->value = $value; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralInt.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralInt.php index b86495c6..8055974d 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralInt.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralInt.php @@ -15,7 +15,7 @@ final class TLiteralInt extends TInt public function __construct(int $value, bool $from_docblock = false) { $this->value = $value; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralString.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralString.php index 7346e5a0..6a622887 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralString.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TLiteralString.php @@ -42,7 +42,7 @@ class TLiteralString extends TString ); } $this->value = $value; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TMixed.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TMixed.php index d122a432..02e3cc5b 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TMixed.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TMixed.php @@ -17,7 +17,7 @@ class TMixed extends Atomic public function __construct(bool $from_loop_isset = false, bool $from_docblock = false) { $this->from_loop_isset = $from_loop_isset; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNamedObject.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNamedObject.php index e26036ca..ee7c5d38 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNamedObject.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNamedObject.php @@ -46,7 +46,7 @@ class TNamedObject extends Atomic /** * @param string $value the name of the object - * @param array $extra_types + * @param array $extra_types */ public function __construct( string $value, @@ -63,7 +63,7 @@ class TNamedObject extends Atomic $this->is_static = $is_static; $this->definite_class = $definite_class; $this->extra_types = $extra_types; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyArray.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyArray.php index f1031de0..4f967775 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyArray.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyArray.php @@ -39,11 +39,10 @@ class TNonEmptyArray extends TArray string $value = 'non-empty-array', bool $from_docblock = false ) { - $this->type_params = $type_params; $this->count = $count; $this->min_count = $min_count; $this->value = $value; - $this->from_docblock = $from_docblock; + parent::__construct($type_params, $from_docblock); } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyList.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyList.php index 832363a6..e6c02940 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyList.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TNonEmptyList.php @@ -44,10 +44,10 @@ class TNonEmptyList extends TList ?int $min_count = null, bool $from_docblock = false ) { - $this->type_param = $type_param; $this->count = $count; $this->min_count = $min_count; - $this->from_docblock = $from_docblock; + /** @psalm-suppress DeprecatedClass */ + parent::__construct($type_param, $from_docblock); } public function getKeyedArray(): TKeyedArray diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TObjectWithProperties.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TObjectWithProperties.php index 1755d10b..cae5be7e 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TObjectWithProperties.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TObjectWithProperties.php @@ -42,7 +42,7 @@ final class TObjectWithProperties extends TObject * * @param array $properties * @param array $methods - * @param array $extra_types + * @param array $extra_types */ public function __construct( array $properties, @@ -53,10 +53,11 @@ final class TObjectWithProperties extends TObject $this->properties = $properties; $this->methods = $methods; $this->extra_types = $extra_types; - $this->from_docblock = $from_docblock; $this->is_stringable_object_only = $this->properties === [] && $this->methods === ['__tostring' => 'string']; + + parent::__construct($from_docblock); } /** @@ -234,7 +235,7 @@ final class TObjectWithProperties extends TObject foreach ($this->properties as $offset => $property) { $input_type_param = null; - if ($input_type instanceof TKeyedArray + if ($input_type instanceof TObjectWithProperties && isset($input_type->properties[$offset]) ) { $input_type_param = $input_type->properties[$offset]; diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TPropertiesOf.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TPropertiesOf.php index 425a477a..49da46df 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TPropertiesOf.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TPropertiesOf.php @@ -49,7 +49,7 @@ final class TPropertiesOf extends Atomic ) { $this->classlike_type = $classlike_type; $this->visibility_filter = $visibility_filter; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TSingleLetter.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TSingleLetter.php index c12ad3d2..604e1ecb 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TSingleLetter.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TSingleLetter.php @@ -7,6 +7,6 @@ namespace Psalm\Type\Atomic; * * @psalm-immutable */ -final class TSingleLetter extends TString +final class TSingleLetter extends TNonEmptyString { } diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php index 0ef28969..80c7db23 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateIndexedAccess.php @@ -33,7 +33,7 @@ final class TTemplateIndexedAccess extends Atomic $this->array_param_name = $array_param_name; $this->offset_param_name = $offset_param_name; $this->defining_class = $defining_class; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateKeyOf.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateKeyOf.php index 34bc1001..debb1fc8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateKeyOf.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateKeyOf.php @@ -39,7 +39,7 @@ final class TTemplateKeyOf extends Atomic $this->param_name = $param_name; $this->defining_class = $defining_class; $this->as = $as; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParam.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParam.php index 33af6d19..f1bb94b8 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParam.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParam.php @@ -48,7 +48,7 @@ final class TTemplateParam extends Atomic $this->as = $extends; $this->defining_class = $defining_class; $this->extra_types = $extra_types; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } /** diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParamClass.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParamClass.php index 900451eb..ccf40a27 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParamClass.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateParamClass.php @@ -27,10 +27,15 @@ final class TTemplateParamClass extends TClassString bool $from_docblock = false ) { $this->param_name = $param_name; - $this->as = $as; - $this->as_type = $as_type; $this->defining_class = $defining_class; - $this->from_docblock = $from_docblock; + parent::__construct( + $as, + $as_type, + false, + false, + false, + $from_docblock, + ); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplatePropertiesOf.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplatePropertiesOf.php index e29e3710..9dbc710c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplatePropertiesOf.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplatePropertiesOf.php @@ -46,7 +46,7 @@ final class TTemplatePropertiesOf extends Atomic $this->defining_class = $defining_class; $this->as = $as; $this->visibility_filter = $visibility_filter; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateValueOf.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateValueOf.php index f8c29e58..6d00b32e 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateValueOf.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTemplateValueOf.php @@ -39,7 +39,7 @@ final class TTemplateValueOf extends Atomic $this->param_name = $param_name; $this->defining_class = $defining_class; $this->as = $as; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } public function getKey(bool $include_extra = true): string diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTypeAlias.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTypeAlias.php index 06ac86cf..e15c442c 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTypeAlias.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TTypeAlias.php @@ -14,6 +14,9 @@ final class TTypeAlias extends Atomic { /** * @var array|null + * @deprecated type aliases are resolved within {@see TypeParser::resolveTypeAliases()} and therefore the + * referencing type(s) are part of other intersection types. The intersection types are not set anymore + * and with v6 this property along with its related methods will get removed. */ public $extra_types; @@ -30,13 +33,19 @@ final class TTypeAlias extends Atomic { $this->declaring_fq_classlike_name = $declaring_fq_classlike_name; $this->alias_name = $alias_name; + /** @psalm-suppress DeprecatedProperty For backwards compatibility, we have to keep this here. */ $this->extra_types = $extra_types; + parent::__construct(true); } /** * @param array|null $extra_types + * @deprecated type aliases are resolved within {@see TypeParser::resolveTypeAliases()} and therefore the + * referencing type(s) are part of other intersection types. This method will get removed with v6. + * @psalm-suppress PossiblyUnusedMethod For backwards compatibility, we have to keep this here. */ public function setIntersectionTypes(?array $extra_types): self { + /** @psalm-suppress DeprecatedProperty For backwards compatibility, we have to keep this here. */ if ($extra_types === $this->extra_types) { return $this; } @@ -54,6 +63,7 @@ final class TTypeAlias extends Atomic public function getId(bool $exact = true, bool $nested = false): string { + /** @psalm-suppress DeprecatedProperty For backwards compatibility, we have to keep this here. */ if ($this->extra_types) { return $this->getKey() . '&' . implode( '&', diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TUnknownClassString.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TUnknownClassString.php new file mode 100644 index 00000000..765b15e9 --- /dev/null +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TUnknownClassString.php @@ -0,0 +1,30 @@ +as_unknown_type = $as_unknown_type; + } +} diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TValueOf.php b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TValueOf.php index 34d28c92..9220c079 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TValueOf.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Atomic/TValueOf.php @@ -12,7 +12,6 @@ use Psalm\Type\Union; use function array_map; use function array_values; use function assert; -use function count; /** * Represents a value of an array or enum. @@ -27,9 +26,36 @@ final class TValueOf extends Atomic public function __construct(Union $type, bool $from_docblock = false) { $this->type = $type; - $this->from_docblock = $from_docblock; + parent::__construct($from_docblock); } + /** + * @param non-empty-array $cases + */ + private static function getValueTypeForNamedObject(array $cases, TNamedObject $atomic_type): Union + { + if ($atomic_type instanceof TEnumCase) { + assert(isset($cases[$atomic_type->case_name]), 'Should\'ve been verified in TValueOf#getValueType'); + $value = $cases[$atomic_type->case_name]->value; + assert($value !== null, 'Backed enum must have a value.'); + return new Union([ConstantTypeResolver::getLiteralTypeFromScalarValue($value)]); + } + + return new Union(array_map( + function (EnumCaseStorage $case): Atomic { + assert($case->value !== null); // Backed enum must have a value + return ConstantTypeResolver::getLiteralTypeFromScalarValue($case->value); + }, + array_values($cases), + )); + } + + protected function getChildNodeKeys(): array + { + return ['type']; + } + + public function getKey(bool $include_extra = true): string { return 'value-of<' . $this->type . '>'; @@ -107,19 +133,14 @@ final class TValueOf extends Atomic $cases = $class_storage->enum_cases; if (!$class_storage->is_enum || $class_storage->enum_type === null - || count($cases) === 0 + || $cases === [] + || ($atomic_type instanceof TEnumCase && !isset($cases[$atomic_type->case_name])) ) { // Invalid value-of, skip continue; } - $value_atomics = new Union(array_map( - function (EnumCaseStorage $case): Atomic { - assert($case->value !== null); // Backed enum must have a value - return ConstantTypeResolver::getLiteralTypeFromScalarValue($case->value); - }, - array_values($cases), - )); + $value_atomics = self::getValueTypeForNamedObject($cases, $atomic_type); } else { continue; } diff --git a/vendor/vimeo/psalm/src/Psalm/Type/Reconciler.php b/vendor/vimeo/psalm/src/Psalm/Type/Reconciler.php index 9bf4d261..074ef233 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/Reconciler.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/Reconciler.php @@ -761,7 +761,7 @@ class Reconciler return null; } - $new_base_type_candidate = $existing_key_type_part->getGenericValueType(); + $new_base_type_candidate = $existing_key_type_part->getGenericValueType(true); } else { $array_properties = $existing_key_type_part->properties; diff --git a/vendor/vimeo/psalm/src/Psalm/Type/UnionTrait.php b/vendor/vimeo/psalm/src/Psalm/Type/UnionTrait.php index bd7a1bdb..3361c2ba 100644 --- a/vendor/vimeo/psalm/src/Psalm/Type/UnionTrait.php +++ b/vendor/vimeo/psalm/src/Psalm/Type/UnionTrait.php @@ -34,6 +34,7 @@ use Psalm\Type\Atomic\TMixed; use Psalm\Type\Atomic\TNamedObject; use Psalm\Type\Atomic\TNever; use Psalm\Type\Atomic\TNonEmptyLowercaseString; +use Psalm\Type\Atomic\TNonEmptyString; use Psalm\Type\Atomic\TNonspecificLiteralInt; use Psalm\Type\Atomic\TNonspecificLiteralString; use Psalm\Type\Atomic\TString; @@ -52,6 +53,8 @@ use function reset; use function sort; use function strpos; +use const ARRAY_FILTER_USE_BOTH; + /** * @psalm-immutable * @psalm-import-type TProperties from Union @@ -795,9 +798,20 @@ trait UnionTrait /** * @psalm-mutation-free */ - public function isMixed(): bool + public function isMixed(bool $check_templates = false): bool { - return isset($this->types['mixed']) && count($this->types) === 1; + return count( + array_filter( + $this->types, + static fn($type, $key): bool => $key === 'mixed' + || $type instanceof TMixed + || ($check_templates + && $type instanceof TTemplateParam + && $type->as->isMixed() + ), + ARRAY_FILTER_USE_BOTH, + ), + ) === count($this->types); } /** @@ -1016,6 +1030,25 @@ trait UnionTrait ) === count($this->types); } + /** + * @psalm-mutation-free + * @return bool true if this is a string + */ + public function isNonEmptyString(bool $check_templates = false): bool + { + return count( + array_filter( + $this->types, + static fn($type): bool => $type instanceof TNonEmptyString + || ($type instanceof TLiteralString && $type->value !== '') + || ($check_templates + && $type instanceof TTemplateParam + && $type->as->isNonEmptyString() + ) + ), + ) === count($this->types); + } + /** * @psalm-mutation-free * @return bool true if this is a boolean diff --git a/vendor/vimeo/psalm/stubs/CoreGenericClasses.phpstub b/vendor/vimeo/psalm/stubs/CoreGenericClasses.phpstub index 528f59ab..3fd64fb3 100644 --- a/vendor/vimeo/psalm/stubs/CoreGenericClasses.phpstub +++ b/vendor/vimeo/psalm/stubs/CoreGenericClasses.phpstub @@ -531,3 +531,93 @@ final class ReturnTypeWillChange public function __construct() {} } +class DateInterval +{ + /** + * Number of years + * @var int + * @readonly + */ + public $y; + + /** + * Number of months + * @var int + * @readonly + */ + public $m; + + /** + * Number of days + * @var int + * @readonly + */ + public $d; + + /** + * Number of hours + * @var int + * @readonly + */ + public $h; + + /** + * Number of minutes + * @var int + * @readonly + */ + public $i; + + /** + * Number of seconds + * @var int + * @readonly + */ + public $s; + + /** + * Number of microseconds + * @since 7.1.0 + * @var float + * @readonly + */ + public $f; + + /** + * Is 1 if the interval is inverted and 0 otherwise + * @var int + * @readonly + */ + public $invert; + + /** + * Total number of days the interval spans. If this is unknown, days will be FALSE. + * @var int|false + * @readonly + */ + public $days; + + /** + * @throws Exception when the $duration cannot be parsed as an interval. + * @link https://php.net/manual/en/dateinterval.construct.php + */ + public function __construct(string $duration = '') {} + + /** + * Formats the interval + * @return string + * @link https://php.net/manual/en/dateinterval.format.php + * @psalm-pure + */ + public function format(string $format = ''): string {} + + /** + * Sets up a DateInterval from the relative parts of the string + * @return DateInterval|false Returns a new {@link https://www.php.net/manual/en/class.dateinterval.php DateInterval} + * instance on success, or FALSE on failure. + * @link https://php.net/manual/en/dateinterval.createfromdatestring.php + * @psalm-ignore-falsable-return + */ + public static function createFromDateString(string $datetime = ''): DateInterval|false {} +} + diff --git a/vendor/vimeo/psalm/stubs/CoreGenericFunctions.phpstub b/vendor/vimeo/psalm/stubs/CoreGenericFunctions.phpstub index f9640b06..5e2640e1 100644 --- a/vendor/vimeo/psalm/stubs/CoreGenericFunctions.phpstub +++ b/vendor/vimeo/psalm/stubs/CoreGenericFunctions.phpstub @@ -122,13 +122,27 @@ function array_diff_assoc(array $array, array ...$arrays) * * @param array $array * - * @return array + * @return ($array is non-empty-array ? non-empty-array : array) * @psalm-pure */ function array_flip(array $array) { } +/** + * @psalm-template TKey as array-key + * @psalm-template TValue + * @psalm-template TArray as array + * + * @param TArray $array + * + * @return (TArray is non-empty-array ? non-empty-array : array) + * @psalm-pure + */ +function array_unique(array $array, int $flags = 0) +{ +} + /** * @psalm-template TKey as array-key * @psalm-template TArray as array @@ -545,17 +559,6 @@ function abs($num) {} */ function range($start, $end, $step = 1) {} -/** - * @psalm-pure - * - * @return ( - * $format is 'd'|'j'|'N'|'w'|'z'|'W'|'m'|'n'|'t'|'L'|'o'|'Y'|'y'|'B'|'g'|'G'|'h'|'H'|'i'|'s'|'u'|'v'|'Z'|'U'|'I' - * ? numeric-string - * : ($timestamp is numeric ? string : string|false) - * ) - */ -function date(string $format, int $timestamp = 0) {} - /** * @psalm-pure * @@ -624,12 +627,23 @@ function substr_replace($string, $replace, $offset, $length = null) {} /** * @psalm-pure - * * @param string $haystack - * + * @param string $needle + * @param int $offset + * @psalm-assert-if-true =non-empty-string $haystack * @psalm-return positive-int|0|false */ -function strpos($haystack, $needle, int $offset = 0) : int|false {} +function strpos(string $haystack, string $needle, int $offset = 0) {} + +/** + * @psalm-pure + * @param string $haystack + * @param string $needle + * @param int $offset + * @psalm-assert-if-true =non-empty-string $haystack + * @psalm-return positive-int|0|false + */ +function stripos(string $haystack, string $needle, int $offset = 0) {} /** * @psalm-pure @@ -1050,12 +1064,40 @@ function str_shuffle(string $string): string {} /** * @psalm-pure - * @return ($length is positive-int ? list : false) + * @param positive-int $length + * @return non-empty-list * * @psalm-flow ($string) -> return */ function str_split(string $string, int $length = 1) {} +/** + * @psalm-pure + * @template T as string + * @param T $needle + * @psalm-assert-if-true =(T is '' ? string : non-empty-string) $haystack + * @return ($needle is '' ? true : ($haystack is '' ? false : bool)) + */ +function str_starts_with(string $haystack, string $needle): bool {} + +/** + * @psalm-pure + * @template T as string + * @param T $needle + * @psalm-assert-if-true =(T is '' ? string : non-empty-string) $haystack + * @return ($needle is '' ? true : ($haystack is '' ? false : bool)) + */ +function str_ends_with(string $haystack, string $needle): bool {} + +/** + * @psalm-pure + * @template T as string + * @param T $needle + * @psalm-assert-if-true =(T is '' ? string : non-empty-string) $haystack + * @return ($needle is '' ? true : ($haystack is '' ? false : bool)) + */ +function str_contains(string $haystack, string $needle): bool {} + /** * @psalm-pure * @return string|false @@ -1459,15 +1501,19 @@ function ldap_escape(string $value, string $ignore = "", int $flags = 0) : strin /** * @psalm-pure * + * @param int<1, 2147483647> $depth * @return mixed * @psalm-flow ($json) -> return */ function json_decode(string $json, ?bool $associative = null, int $depth = 512, int $flags = 0) {} /** + * The conditional return type below relies on the fact that JSON_THROW_ON_ERROR is + * the highest-valued of JSON constants * @psalm-pure * - * @return ($flags is 4194304 ? non-empty-string : non-empty-string|false) + * @param int<1, 2147483647> $depth + * @return ($flags is int<4194304, 8388607> ? non-empty-string : non-empty-string|false) * * @psalm-flow ($value) -> return * @psalm-ignore-falsable-return diff --git a/vendor/vimeo/psalm/stubs/CoreGenericIterators.phpstub b/vendor/vimeo/psalm/stubs/CoreGenericIterators.phpstub index c69583d9..43a7bb1f 100644 --- a/vendor/vimeo/psalm/stubs/CoreGenericIterators.phpstub +++ b/vendor/vimeo/psalm/stubs/CoreGenericIterators.phpstub @@ -114,7 +114,7 @@ class IteratorIterator implements OuterIterator { /** * @param TIterator $iterator */ - public function __construct(Traversable $iterator) {} + public function __construct(Traversable $iterator, ?string $class = null) {} /** * @return TValue|null current value or null when iterator is drained diff --git a/vendor/vimeo/psalm/stubs/Php82.phpstub b/vendor/vimeo/psalm/stubs/Php82.phpstub index 40d60e3e..8696bd08 100644 --- a/vendor/vimeo/psalm/stubs/Php82.phpstub +++ b/vendor/vimeo/psalm/stubs/Php82.phpstub @@ -45,4 +45,13 @@ namespace { { public function __construct() {} } + + /** + * @psalm-pure + * @param positive-int $length + * @return list + * + * @psalm-flow ($string) -> return + */ + function str_split(string $string, int $length = 1) {} } diff --git a/vendor/vimeo/psalm/stubs/extensions/mysqli.phpstub b/vendor/vimeo/psalm/stubs/extensions/mysqli.phpstub index a5ec2f54..1569a524 100644 --- a/vendor/vimeo/psalm/stubs/extensions/mysqli.phpstub +++ b/vendor/vimeo/psalm/stubs/extensions/mysqli.phpstub @@ -1,5 +1,13 @@ |numeric-string + */ + public int|string $affected_rows; +} + /** * @template TValue * @@ -7,6 +15,11 @@ */ class mysqli_result implements Traversable { + /** + * @var int<0, max>|numeric-string + */ + public int|string $num_rows; + /** * @psalm-taint-sink callable $class * @@ -18,6 +31,44 @@ class mysqli_result implements Traversable function fetch_object(string $class = stdClass::class, array $constructor_args = []): object|false|null {} } +class mysqli_stmt +{ + /** + * @var int<-1, max>|numeric-string + */ + public int|string $affected_rows; + + public int $errno; + + /** + * @var list + */ + public $error_list; + + public string $error; + + /** + * @var 0|positive-int + */ + public int $field_count; + + public int|string $insert_id; + + /** + * @var int<0,max>|numeric-string + */ + public int|string $num_rows; + + /** + * @var 0|positive-int + */ + public int $param_count; + + /** + * @var non-empty-string + */ + public string $sqlstate; +} /** * @psalm-taint-sink callable $class diff --git a/vendor/vimeo/psalm/stubs/extensions/simplexml.phpstub b/vendor/vimeo/psalm/stubs/extensions/simplexml.phpstub index 7ff39239..5c2fb2f5 100644 --- a/vendor/vimeo/psalm/stubs/extensions/simplexml.phpstub +++ b/vendor/vimeo/psalm/stubs/extensions/simplexml.phpstub @@ -53,6 +53,7 @@ class SimpleXMLElement implements Traversable, Countable final public function __construct(string $data, int $options = 0, bool $dataIsURL = false, string $namespaceOrPrefix = '', bool $isPrefix = false) {} + /** @psalm-ignore-nullable-return */ public function addChild(string $qualifiedName, ?string $value = null, ?string $namespace = null): ?SimpleXMLElement {} public function addAttribute(string $qualifiedName, string $value, ?string $namespace = null): void {} diff --git a/www/composer.json b/www/composer.json index 5c05400e..e9c111e2 100644 --- a/www/composer.json +++ b/www/composer.json @@ -26,6 +26,7 @@ "require": { "egrajp/smarty-extended": "^4.3", "php": ">=8.1", - "gullevek/dotenv": "^2.0" + "gullevek/dotenv": "^2.0", + "psr/log": "^2.0 || ^3.0" } } diff --git a/www/composer.lock b/www/composer.lock index 3af0eac2..d3fa2255 100644 --- a/www/composer.lock +++ b/www/composer.lock @@ -4,7 +4,7 @@ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies", "This file is @generated automatically" ], - "content-hash": "88bf0aa9b348a55a525b96fcaed7b5a9", + "content-hash": "3613982c10e8a26141e775ac6bd16656", "packages": [ { "name": "egrajp/smarty-extended", @@ -89,6 +89,56 @@ "source": "https://github.com/gullevek/dotEnv/tree/v2.0.8" }, "time": "2023-03-03T00:32:02+00:00" + }, + { + "name": "psr/log", + "version": "3.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "time": "2021-07-14T16:46:02+00:00" } ], "packages-dev": [ @@ -164,16 +214,16 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.0", + "version": "1.11.1", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -211,7 +261,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { @@ -219,20 +269,20 @@ "type": "tidelift" } ], - "time": "2022-03-03T13:19:32+00:00" + "time": "2023-03-08T13:26:56+00:00" }, { "name": "nikic/php-parser", - "version": "v4.15.3", + "version": "v4.15.5", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -273,9 +323,9 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, - "time": "2023-01-16T22:05:37+00:00" + "time": "2023-05-19T20:20:00+00:00" }, { "name": "phar-io/manifest", @@ -390,16 +440,16 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.25", + "version": "9.2.26", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954" + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954", - "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", "shasum": "" }, "require": { @@ -421,8 +471,8 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "type": "library", "extra": { @@ -455,7 +505,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" }, "funding": [ { @@ -463,7 +513,7 @@ "type": "github" } ], - "time": "2023-02-25T05:32:00+00:00" + "time": "2023-03-06T12:58:08+00:00" }, { "name": "phpunit/php-file-iterator", @@ -708,16 +758,16 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.4", + "version": "9.6.8", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9125ee085b6d95e78277dc07aa1f46f9e0607b8d", - "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "shasum": "" }, "require": { @@ -750,8 +800,8 @@ "sebastian/version": "^3.0.2" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" @@ -790,7 +840,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.4" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8" }, "funding": [ { @@ -806,7 +857,7 @@ "type": "tidelift" } ], - "time": "2023-02-27T13:06:37+00:00" + "time": "2023-05-11T05:14:45+00:00" }, { "name": "sebastian/cli-parser", @@ -1108,16 +1159,16 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", + "version": "4.0.5", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1162,7 +1213,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { @@ -1170,7 +1221,7 @@ "type": "github" } ], - "time": "2020-10-26T13:10:38+00:00" + "time": "2023-05-07T05:35:17+00:00" }, { "name": "sebastian/environment", diff --git a/www/vendor/composer/autoload_classmap.php b/www/vendor/composer/autoload_classmap.php index 7a6e31d7..04c4c89c 100644 --- a/www/vendor/composer/autoload_classmap.php +++ b/www/vendor/composer/autoload_classmap.php @@ -43,6 +43,7 @@ return array( 'CoreLibs\\DB\\SQL\\PgSQL' => $baseDir . '/lib/CoreLibs/DB/SQL/PgSQL.php', 'CoreLibs\\Debug\\FileWriter' => $baseDir . '/lib/CoreLibs/Debug/FileWriter.php', 'CoreLibs\\Debug\\Logging' => $baseDir . '/lib/CoreLibs/Debug/Logging.php', + 'CoreLibs\\Debug\\LoggingLegacy' => $baseDir . '/lib/CoreLibs/Debug/LoggingLegacy.php', 'CoreLibs\\Debug\\MemoryUsage' => $baseDir . '/lib/CoreLibs/Debug/MemoryUsage.php', 'CoreLibs\\Debug\\RunningTime' => $baseDir . '/lib/CoreLibs/Debug/RunningTime.php', 'CoreLibs\\Debug\\Support' => $baseDir . '/lib/CoreLibs/Debug/Support.php', @@ -55,6 +56,9 @@ return array( 'CoreLibs\\Language\\Core\\StringReader' => $baseDir . '/lib/CoreLibs/Language/Core/StringReader.php', 'CoreLibs\\Language\\GetLocale' => $baseDir . '/lib/CoreLibs/Language/GetLocale.php', 'CoreLibs\\Language\\L10n' => $baseDir . '/lib/CoreLibs/Language/L10n.php', + 'CoreLibs\\Logging\\Logger\\Flag' => $baseDir . '/lib/CoreLibs/Logging/Logger/Flag.php', + 'CoreLibs\\Logging\\Logger\\Level' => $baseDir . '/lib/CoreLibs/Logging/Logger/Level.php', + 'CoreLibs\\Logging\\Logging' => $baseDir . '/lib/CoreLibs/Logging/Logging.php', 'CoreLibs\\Output\\Form\\Elements' => $baseDir . '/lib/CoreLibs/Output/Form/Elements.php', 'CoreLibs\\Output\\Form\\Generate' => $baseDir . '/lib/CoreLibs/Output/Form/Generate.php', 'CoreLibs\\Output\\Form\\TableArrays\\EditAccess' => $baseDir . '/lib/CoreLibs/Output/Form/TableArrays/EditAccess.php', diff --git a/www/vendor/composer/autoload_psr4.php b/www/vendor/composer/autoload_psr4.php index def4fddf..81c4c26f 100644 --- a/www/vendor/composer/autoload_psr4.php +++ b/www/vendor/composer/autoload_psr4.php @@ -8,6 +8,7 @@ $baseDir = dirname($vendorDir); return array( 'gullevek\\dotenv\\' => array($vendorDir . '/gullevek/dotenv/src'), 'gullevek\\dotEnv\\' => array($vendorDir . '/gullevek/dotenv/src'), + 'Psr\\Log\\' => array($vendorDir . '/psr/log/src'), 'PhpParser\\' => array($vendorDir . '/nikic/php-parser/lib/PhpParser'), 'Doctrine\\Instantiator\\' => array($vendorDir . '/doctrine/instantiator/src/Doctrine/Instantiator'), 'DeepCopy\\' => array($vendorDir . '/myclabs/deep-copy/src/DeepCopy'), diff --git a/www/vendor/composer/autoload_static.php b/www/vendor/composer/autoload_static.php index 7ff5acf8..095f108a 100644 --- a/www/vendor/composer/autoload_static.php +++ b/www/vendor/composer/autoload_static.php @@ -19,6 +19,7 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9 ), 'P' => array ( + 'Psr\\Log\\' => 8, 'PhpParser\\' => 10, ), 'D' => @@ -37,6 +38,10 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9 array ( 0 => __DIR__ . '/..' . '/gullevek/dotenv/src', ), + 'Psr\\Log\\' => + array ( + 0 => __DIR__ . '/..' . '/psr/log/src', + ), 'PhpParser\\' => array ( 0 => __DIR__ . '/..' . '/nikic/php-parser/lib/PhpParser', @@ -89,6 +94,7 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9 'CoreLibs\\DB\\SQL\\PgSQL' => __DIR__ . '/../..' . '/lib/CoreLibs/DB/SQL/PgSQL.php', 'CoreLibs\\Debug\\FileWriter' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/FileWriter.php', 'CoreLibs\\Debug\\Logging' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/Logging.php', + 'CoreLibs\\Debug\\LoggingLegacy' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/LoggingLegacy.php', 'CoreLibs\\Debug\\MemoryUsage' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/MemoryUsage.php', 'CoreLibs\\Debug\\RunningTime' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/RunningTime.php', 'CoreLibs\\Debug\\Support' => __DIR__ . '/../..' . '/lib/CoreLibs/Debug/Support.php', @@ -101,6 +107,9 @@ class ComposerStaticInit10fe8fe2ec4017b8644d2b64bcf398b9 'CoreLibs\\Language\\Core\\StringReader' => __DIR__ . '/../..' . '/lib/CoreLibs/Language/Core/StringReader.php', 'CoreLibs\\Language\\GetLocale' => __DIR__ . '/../..' . '/lib/CoreLibs/Language/GetLocale.php', 'CoreLibs\\Language\\L10n' => __DIR__ . '/../..' . '/lib/CoreLibs/Language/L10n.php', + 'CoreLibs\\Logging\\Logger\\Flag' => __DIR__ . '/../..' . '/lib/CoreLibs/Logging/Logger/Flag.php', + 'CoreLibs\\Logging\\Logger\\Level' => __DIR__ . '/../..' . '/lib/CoreLibs/Logging/Logger/Level.php', + 'CoreLibs\\Logging\\Logging' => __DIR__ . '/../..' . '/lib/CoreLibs/Logging/Logging.php', 'CoreLibs\\Output\\Form\\Elements' => __DIR__ . '/../..' . '/lib/CoreLibs/Output/Form/Elements.php', 'CoreLibs\\Output\\Form\\Generate' => __DIR__ . '/../..' . '/lib/CoreLibs/Output/Form/Generate.php', 'CoreLibs\\Output\\Form\\TableArrays\\EditAccess' => __DIR__ . '/../..' . '/lib/CoreLibs/Output/Form/TableArrays/EditAccess.php', diff --git a/www/vendor/composer/installed.json b/www/vendor/composer/installed.json index 976dd13f..916dedc0 100644 --- a/www/vendor/composer/installed.json +++ b/www/vendor/composer/installed.json @@ -165,17 +165,17 @@ }, { "name": "myclabs/deep-copy", - "version": "1.11.0", - "version_normalized": "1.11.0.0", + "version": "1.11.1", + "version_normalized": "1.11.1.0", "source": { "type": "git", "url": "https://github.com/myclabs/DeepCopy.git", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614" + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/14daed4296fae74d9e3201d2c4925d1acb7aa614", - "reference": "14daed4296fae74d9e3201d2c4925d1acb7aa614", + "url": "https://api.github.com/repos/myclabs/DeepCopy/zipball/7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", + "reference": "7284c22080590fb39f2ffa3e9057f10a4ddd0e0c", "shasum": "" }, "require": { @@ -190,7 +190,7 @@ "doctrine/common": "^2.13.3 || ^3.2.2", "phpunit/phpunit": "^7.5.20 || ^8.5.23 || ^9.5.13" }, - "time": "2022-03-03T13:19:32+00:00", + "time": "2023-03-08T13:26:56+00:00", "type": "library", "installation-source": "dist", "autoload": { @@ -215,7 +215,7 @@ ], "support": { "issues": "https://github.com/myclabs/DeepCopy/issues", - "source": "https://github.com/myclabs/DeepCopy/tree/1.11.0" + "source": "https://github.com/myclabs/DeepCopy/tree/1.11.1" }, "funding": [ { @@ -227,17 +227,17 @@ }, { "name": "nikic/php-parser", - "version": "v4.15.3", - "version_normalized": "4.15.3.0", + "version": "v4.15.5", + "version_normalized": "4.15.5.0", "source": { "type": "git", "url": "https://github.com/nikic/PHP-Parser.git", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039" + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/570e980a201d8ed0236b0a62ddf2c9cbb2034039", - "reference": "570e980a201d8ed0236b0a62ddf2c9cbb2034039", + "url": "https://api.github.com/repos/nikic/PHP-Parser/zipball/11e2663a5bc9db5d714eedb4277ee300403b4a9e", + "reference": "11e2663a5bc9db5d714eedb4277ee300403b4a9e", "shasum": "" }, "require": { @@ -248,7 +248,7 @@ "ircmaxell/php-yacc": "^0.0.7", "phpunit/phpunit": "^6.5 || ^7.0 || ^8.0 || ^9.0" }, - "time": "2023-01-16T22:05:37+00:00", + "time": "2023-05-19T20:20:00+00:00", "bin": [ "bin/php-parse" ], @@ -280,7 +280,7 @@ ], "support": { "issues": "https://github.com/nikic/PHP-Parser/issues", - "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.3" + "source": "https://github.com/nikic/PHP-Parser/tree/v4.15.5" }, "install-path": "../nikic/php-parser" }, @@ -403,17 +403,17 @@ }, { "name": "phpunit/php-code-coverage", - "version": "9.2.25", - "version_normalized": "9.2.25.0", + "version": "9.2.26", + "version_normalized": "9.2.26.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/php-code-coverage.git", - "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954" + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/0e2b40518197a8c0d4b08bc34dfff1c99c508954", - "reference": "0e2b40518197a8c0d4b08bc34dfff1c99c508954", + "url": "https://api.github.com/repos/sebastianbergmann/php-code-coverage/zipball/443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", + "reference": "443bc6912c9bd5b409254a40f4b0f4ced7c80ea1", "shasum": "" }, "require": { @@ -435,10 +435,10 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, - "time": "2023-02-25T05:32:00+00:00", + "time": "2023-03-06T12:58:08+00:00", "type": "library", "extra": { "branch-alias": { @@ -471,7 +471,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/php-code-coverage/issues", - "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.25" + "source": "https://github.com/sebastianbergmann/php-code-coverage/tree/9.2.26" }, "funding": [ { @@ -736,17 +736,17 @@ }, { "name": "phpunit/phpunit", - "version": "9.6.4", - "version_normalized": "9.6.4.0", + "version": "9.6.8", + "version_normalized": "9.6.8.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/phpunit.git", - "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d" + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/9125ee085b6d95e78277dc07aa1f46f9e0607b8d", - "reference": "9125ee085b6d95e78277dc07aa1f46f9e0607b8d", + "url": "https://api.github.com/repos/sebastianbergmann/phpunit/zipball/17d621b3aff84d0c8b62539e269e87d8d5baa76e", + "reference": "17d621b3aff84d0c8b62539e269e87d8d5baa76e", "shasum": "" }, "require": { @@ -779,10 +779,10 @@ "sebastian/version": "^3.0.2" }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, - "time": "2023-02-27T13:06:37+00:00", + "time": "2023-05-11T05:14:45+00:00", "bin": [ "phpunit" ], @@ -821,7 +821,8 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/phpunit/issues", - "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.4" + "security": "https://github.com/sebastianbergmann/phpunit/security/policy", + "source": "https://github.com/sebastianbergmann/phpunit/tree/9.6.8" }, "funding": [ { @@ -839,6 +840,59 @@ ], "install-path": "../phpunit/phpunit" }, + { + "name": "psr/log", + "version": "3.0.0", + "version_normalized": "3.0.0.0", + "source": { + "type": "git", + "url": "https://github.com/php-fig/log.git", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001" + }, + "dist": { + "type": "zip", + "url": "https://api.github.com/repos/php-fig/log/zipball/fe5ea303b0887d5caefd3d431c3e61ad47037001", + "reference": "fe5ea303b0887d5caefd3d431c3e61ad47037001", + "shasum": "" + }, + "require": { + "php": ">=8.0.0" + }, + "time": "2021-07-14T16:46:02+00:00", + "type": "library", + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + }, + "installation-source": "dist", + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "notification-url": "https://packagist.org/downloads/", + "license": [ + "MIT" + ], + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "description": "Common interface for logging libraries", + "homepage": "https://github.com/php-fig/log", + "keywords": [ + "log", + "psr", + "psr-3" + ], + "support": { + "source": "https://github.com/php-fig/log/tree/3.0.0" + }, + "install-path": "../psr/log" + }, { "name": "sebastian/cli-parser", "version": "1.0.1", @@ -1154,17 +1208,17 @@ }, { "name": "sebastian/diff", - "version": "4.0.4", - "version_normalized": "4.0.4.0", + "version": "4.0.5", + "version_normalized": "4.0.5.0", "source": { "type": "git", "url": "https://github.com/sebastianbergmann/diff.git", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d" + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131" }, "dist": { "type": "zip", - "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/3461e3fccc7cfdfc2720be910d3bd73c69be590d", - "reference": "3461e3fccc7cfdfc2720be910d3bd73c69be590d", + "url": "https://api.github.com/repos/sebastianbergmann/diff/zipball/74be17022044ebaaecfdf0c5cd504fc9cd5a7131", + "reference": "74be17022044ebaaecfdf0c5cd504fc9cd5a7131", "shasum": "" }, "require": { @@ -1174,7 +1228,7 @@ "phpunit/phpunit": "^9.3", "symfony/process": "^4.2 || ^5" }, - "time": "2020-10-26T13:10:38+00:00", + "time": "2023-05-07T05:35:17+00:00", "type": "library", "extra": { "branch-alias": { @@ -1211,7 +1265,7 @@ ], "support": { "issues": "https://github.com/sebastianbergmann/diff/issues", - "source": "https://github.com/sebastianbergmann/diff/tree/4.0.4" + "source": "https://github.com/sebastianbergmann/diff/tree/4.0.5" }, "funding": [ { diff --git a/www/vendor/composer/installed.php b/www/vendor/composer/installed.php index ab21956b..4d05e1ca 100644 --- a/www/vendor/composer/installed.php +++ b/www/vendor/composer/installed.php @@ -47,18 +47,18 @@ 'dev_requirement' => false, ), 'myclabs/deep-copy' => array( - 'pretty_version' => '1.11.0', - 'version' => '1.11.0.0', - 'reference' => '14daed4296fae74d9e3201d2c4925d1acb7aa614', + 'pretty_version' => '1.11.1', + 'version' => '1.11.1.0', + 'reference' => '7284c22080590fb39f2ffa3e9057f10a4ddd0e0c', 'type' => 'library', 'install_path' => __DIR__ . '/../myclabs/deep-copy', 'aliases' => array(), 'dev_requirement' => true, ), 'nikic/php-parser' => array( - 'pretty_version' => 'v4.15.3', - 'version' => '4.15.3.0', - 'reference' => '570e980a201d8ed0236b0a62ddf2c9cbb2034039', + 'pretty_version' => 'v4.15.5', + 'version' => '4.15.5.0', + 'reference' => '11e2663a5bc9db5d714eedb4277ee300403b4a9e', 'type' => 'library', 'install_path' => __DIR__ . '/../nikic/php-parser', 'aliases' => array(), @@ -83,9 +83,9 @@ 'dev_requirement' => true, ), 'phpunit/php-code-coverage' => array( - 'pretty_version' => '9.2.25', - 'version' => '9.2.25.0', - 'reference' => '0e2b40518197a8c0d4b08bc34dfff1c99c508954', + 'pretty_version' => '9.2.26', + 'version' => '9.2.26.0', + 'reference' => '443bc6912c9bd5b409254a40f4b0f4ced7c80ea1', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/php-code-coverage', 'aliases' => array(), @@ -128,14 +128,23 @@ 'dev_requirement' => true, ), 'phpunit/phpunit' => array( - 'pretty_version' => '9.6.4', - 'version' => '9.6.4.0', - 'reference' => '9125ee085b6d95e78277dc07aa1f46f9e0607b8d', + 'pretty_version' => '9.6.8', + 'version' => '9.6.8.0', + 'reference' => '17d621b3aff84d0c8b62539e269e87d8d5baa76e', 'type' => 'library', 'install_path' => __DIR__ . '/../phpunit/phpunit', 'aliases' => array(), 'dev_requirement' => true, ), + 'psr/log' => array( + 'pretty_version' => '3.0.0', + 'version' => '3.0.0.0', + 'reference' => 'fe5ea303b0887d5caefd3d431c3e61ad47037001', + 'type' => 'library', + 'install_path' => __DIR__ . '/../psr/log', + 'aliases' => array(), + 'dev_requirement' => false, + ), 'sebastian/cli-parser' => array( 'pretty_version' => '1.0.1', 'version' => '1.0.1.0', @@ -182,9 +191,9 @@ 'dev_requirement' => true, ), 'sebastian/diff' => array( - 'pretty_version' => '4.0.4', - 'version' => '4.0.4.0', - 'reference' => '3461e3fccc7cfdfc2720be910d3bd73c69be590d', + 'pretty_version' => '4.0.5', + 'version' => '4.0.5.0', + 'reference' => '74be17022044ebaaecfdf0c5cd504fc9cd5a7131', 'type' => 'library', 'install_path' => __DIR__ . '/../sebastian/diff', 'aliases' => array(), diff --git a/www/vendor/myclabs/deep-copy/README.md b/www/vendor/myclabs/deep-copy/README.md index 503e93df..94aaa06d 100644 --- a/www/vendor/myclabs/deep-copy/README.md +++ b/www/vendor/myclabs/deep-copy/README.md @@ -186,6 +186,9 @@ $matcher = new TypeMatcher('Doctrine\Common\Collections\Collection'); - `DeepCopy\Filter` applies a transformation to the object attribute matched by `DeepCopy\Matcher` - `DeepCopy\TypeFilter` applies a transformation to any element matched by `DeepCopy\TypeMatcher` +By design, matching a filter will stop the chain of filters (i.e. the next ones will not be applied). +Using the ([`ChainableFilter`](#chainablefilter-filter)) won't stop the chain of filters. + #### `SetNullFilter` (filter) @@ -226,6 +229,34 @@ $copy = $copier->copy($object); ``` +#### `ChainableFilter` (filter) + +If you use cloning on proxy classes, you might want to apply two filters for: +1. loading the data +2. applying a transformation + +You can use the `ChainableFilter` as a decorator of the proxy loader filter, which won't stop the chain of filters (i.e. +the next ones may be applied). + + +```php +use DeepCopy\DeepCopy; +use DeepCopy\Filter\ChainableFilter; +use DeepCopy\Filter\Doctrine\DoctrineProxyFilter; +use DeepCopy\Filter\SetNullFilter; +use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher; +use DeepCopy\Matcher\PropertyNameMatcher; + +$copier = new DeepCopy(); +$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher()); +$copier->addFilter(new SetNullFilter(), new PropertyNameMatcher('id')); + +$copy = $copier->copy($object); + +echo $copy->id; // null +``` + + #### `DoctrineCollectionFilter` (filter) If you use Doctrine and want to copy an entity, you will need to use the `DoctrineCollectionFilter`: @@ -268,6 +299,8 @@ Doctrine proxy class (...\\\_\_CG\_\_\Proxy). You can use the `DoctrineProxyFilter` to load the actual entity behind the Doctrine proxy class. **Make sure, though, to put this as one of your very first filters in the filter chain so that the entity is loaded before other filters are applied!** +We recommend to decorate the `DoctrineProxyFilter` with the `ChainableFilter` to allow applying other filters to the +cloned lazy loaded entities. ```php use DeepCopy\DeepCopy; @@ -275,7 +308,7 @@ use DeepCopy\Filter\Doctrine\DoctrineProxyFilter; use DeepCopy\Matcher\Doctrine\DoctrineProxyMatcher; $copier = new DeepCopy(); -$copier->addFilter(new DoctrineProxyFilter(), new DoctrineProxyMatcher()); +$copier->addFilter(new ChainableFilter(new DoctrineProxyFilter()), new DoctrineProxyMatcher()); $copy = $copier->copy($object); diff --git a/www/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php b/www/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php index 5e68c64e..6e766d80 100644 --- a/www/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php +++ b/www/vendor/myclabs/deep-copy/src/DeepCopy/DeepCopy.php @@ -7,6 +7,7 @@ use DateInterval; use DateTimeInterface; use DateTimeZone; use DeepCopy\Exception\CloneException; +use DeepCopy\Filter\ChainableFilter; use DeepCopy\Filter\Filter; use DeepCopy\Matcher\Matcher; use DeepCopy\Reflection\ReflectionHelper; @@ -239,6 +240,10 @@ class DeepCopy } ); + if ($filter instanceof ChainableFilter) { + continue; + } + // If a filter matches, we stop processing this property return; } diff --git a/www/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php b/www/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php new file mode 100644 index 00000000..4e3f7bbc --- /dev/null +++ b/www/vendor/myclabs/deep-copy/src/DeepCopy/Filter/ChainableFilter.php @@ -0,0 +1,24 @@ +filter = $filter; + } + + public function apply($object, $property, $objectCopier) + { + $this->filter->apply($object, $property, $objectCopier); + } +} diff --git a/www/vendor/nikic/php-parser/grammar/php7.y b/www/vendor/nikic/php-parser/grammar/php7.y index 1f9b4bdd..fc7862c3 100644 --- a/www/vendor/nikic/php-parser/grammar/php7.y +++ b/www/vendor/nikic/php-parser/grammar/php7.y @@ -518,7 +518,8 @@ new_elseif_list: ; new_elseif: - T_ELSEIF '(' expr ')' ':' inner_statement_list { $$ = Stmt\ElseIf_[$3, $6]; } + T_ELSEIF '(' expr ')' ':' inner_statement_list + { $$ = Stmt\ElseIf_[$3, $6]; $this->fixupAlternativeElse($$); } ; else_single: @@ -528,7 +529,8 @@ else_single: new_else_single: /* empty */ { $$ = null; } - | T_ELSE ':' inner_statement_list { $$ = Stmt\Else_[$3]; } + | T_ELSE ':' inner_statement_list + { $$ = Stmt\Else_[$3]; $this->fixupAlternativeElse($$); } ; foreach_variable: diff --git a/www/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php b/www/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php index de9aae7e..69f35332 100644 --- a/www/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php +++ b/www/vendor/nikic/php-parser/lib/PhpParser/Builder/Param.php @@ -19,6 +19,8 @@ class Param implements PhpParser\Builder protected $variadic = false; + protected $flags = 0; + /** @var Node\AttributeGroup[] */ protected $attributeGroups = []; @@ -95,6 +97,50 @@ class Param implements PhpParser\Builder return $this; } + /** + * Makes the (promoted) parameter public. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePublic() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PUBLIC); + + return $this; + } + + /** + * Makes the (promoted) parameter protected. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeProtected() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PROTECTED); + + return $this; + } + + /** + * Makes the (promoted) parameter private. + * + * @return $this The builder instance (for fluid interface) + */ + public function makePrivate() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_PRIVATE); + + return $this; + } + + /** + * Makes the (promoted) parameter readonly. + * + * @return $this The builder instance (for fluid interface) + */ + public function makeReadonly() { + $this->flags = BuilderHelpers::addModifier($this->flags, Node\Stmt\Class_::MODIFIER_READONLY); + + return $this; + } + /** * Adds an attribute group. * @@ -116,7 +162,7 @@ class Param implements PhpParser\Builder public function getNode() : Node { return new Node\Param( new Node\Expr\Variable($this->name), - $this->default, $this->type, $this->byRef, $this->variadic, [], 0, $this->attributeGroups + $this->default, $this->type, $this->byRef, $this->variadic, [], $this->flags, $this->attributeGroups ); } } diff --git a/www/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php b/www/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php index 48deff23..c6b9abd0 100644 --- a/www/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php +++ b/www/vendor/nikic/php-parser/lib/PhpParser/Parser/Php7.php @@ -1826,7 +1826,7 @@ class Php7 extends \PhpParser\ParserAbstract $this->semStack[$stackPos-(2-1)][] = $this->semStack[$stackPos-(2-2)]; $this->semValue = $this->semStack[$stackPos-(2-1)]; }, 266 => function ($stackPos) { - $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-6)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); + $this->semValue = new Stmt\ElseIf_($this->semStack[$stackPos-(6-3)], $this->semStack[$stackPos-(6-6)], $this->startAttributeStack[$stackPos-(6-1)] + $this->endAttributes); $this->fixupAlternativeElse($this->semValue); }, 267 => function ($stackPos) { $this->semValue = null; @@ -1838,7 +1838,7 @@ class Php7 extends \PhpParser\ParserAbstract $this->semValue = null; }, 270 => function ($stackPos) { - $this->semValue = new Stmt\Else_($this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); + $this->semValue = new Stmt\Else_($this->semStack[$stackPos-(3-3)], $this->startAttributeStack[$stackPos-(3-1)] + $this->endAttributes); $this->fixupAlternativeElse($this->semValue); }, 271 => function ($stackPos) { $this->semValue = array($this->semStack[$stackPos-(1-1)], false); diff --git a/www/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php b/www/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php index 301d3c6a..9f9d00c7 100644 --- a/www/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php +++ b/www/vendor/nikic/php-parser/lib/PhpParser/ParserAbstract.php @@ -16,9 +16,12 @@ use PhpParser\Node\Scalar\String_; use PhpParser\Node\Stmt\Class_; use PhpParser\Node\Stmt\ClassConst; use PhpParser\Node\Stmt\ClassMethod; +use PhpParser\Node\Stmt\Else_; +use PhpParser\Node\Stmt\ElseIf_; use PhpParser\Node\Stmt\Enum_; use PhpParser\Node\Stmt\Interface_; use PhpParser\Node\Stmt\Namespace_; +use PhpParser\Node\Stmt\Nop; use PhpParser\Node\Stmt\Property; use PhpParser\Node\Stmt\TryCatch; use PhpParser\Node\Stmt\UseUse; @@ -876,6 +879,24 @@ abstract class ParserAbstract implements Parser return $attributes; } + /** @param ElseIf_|Else_ $node */ + protected function fixupAlternativeElse($node) { + // Make sure a trailing nop statement carrying comments is part of the node. + $numStmts = \count($node->stmts); + if ($numStmts !== 0 && $node->stmts[$numStmts - 1] instanceof Nop) { + $nopAttrs = $node->stmts[$numStmts - 1]->getAttributes(); + if (isset($nopAttrs['endLine'])) { + $node->setAttribute('endLine', $nopAttrs['endLine']); + } + if (isset($nopAttrs['endFilePos'])) { + $node->setAttribute('endFilePos', $nopAttrs['endFilePos']); + } + if (isset($nopAttrs['endTokenPos'])) { + $node->setAttribute('endTokenPos', $nopAttrs['endTokenPos']); + } + } + } + protected function checkClassModifier($a, $b, $modifierPos) { try { Class_::verifyClassModifier($a, $b); diff --git a/www/vendor/phpunit/php-code-coverage/ChangeLog.md b/www/vendor/phpunit/php-code-coverage/ChangeLog-9.2.md similarity index 99% rename from www/vendor/phpunit/php-code-coverage/ChangeLog.md rename to www/vendor/phpunit/php-code-coverage/ChangeLog-9.2.md index 35ef8119..34587c05 100644 --- a/www/vendor/phpunit/php-code-coverage/ChangeLog.md +++ b/www/vendor/phpunit/php-code-coverage/ChangeLog-9.2.md @@ -2,6 +2,12 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. +## [9.2.26] - 2023-03-06 + +### Changed + +* Improved the legend on the file pages of the HTML code coverage report + ## [9.2.25] - 2023-02-25 ### Fixed @@ -470,6 +476,7 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](htt * This component is no longer supported on PHP 7.1 +[9.2.26]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.25...9.2.26 [9.2.25]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.24...9.2.25 [9.2.24]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.23...9.2.24 [9.2.23]: https://github.com/sebastianbergmann/php-code-coverage/compare/9.2.22...9.2.23 diff --git a/www/vendor/phpunit/php-code-coverage/composer.json b/www/vendor/phpunit/php-code-coverage/composer.json index 6db6936f..d6f10795 100644 --- a/www/vendor/phpunit/php-code-coverage/composer.json +++ b/www/vendor/phpunit/php-code-coverage/composer.json @@ -46,8 +46,8 @@ "phpunit/phpunit": "^9.3" }, "suggest": { - "ext-pcov": "*", - "ext-xdebug": "*" + "ext-pcov": "PHP extension that provides line coverage", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "autoload": { "classmap": [ diff --git a/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php b/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php index 101a9ada..b59dc89d 100644 --- a/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php +++ b/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/File.php @@ -134,7 +134,7 @@ final class File extends Renderer [ 'items' => $this->renderItems($node), 'lines' => $this->renderSourceWithLineCoverage($node), - 'legend' => '

ExecutedNot ExecutedDead Code

', + 'legend' => '

Covered by small (and larger) testsCovered by medium (and large) testsCovered by large tests (and tests of unknown size)Not coveredNot coverable

', 'structure' => '', ] ); diff --git a/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css b/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css index 5dc62ccf..526cac0d 100644 --- a/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css +++ b/www/vendor/phpunit/php-code-coverage/src/Report/Html/Renderer/Template/css/style.css @@ -128,3 +128,31 @@ table + .structure-heading { border-top: 1px solid lightgrey; padding-top: 0.5em; } + +.legend { + font-weight: bold; + margin-right: 2px; + padding-left: 10px; + padding-right: 10px; + text-align: center; +} + +.covered-by-small-tests { + background-color: #99cb84; +} + +.covered-by-medium-tests { + background-color: #c3e3b5; +} + +.covered-by-large-tests { + background-color: #dff0d8; +} + +.not-covered { + background-color: #f2dede; +} + +.not-coverable { + background-color: #fcf8e3; +} diff --git a/www/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php b/www/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php index 506f2752..794084ff 100644 --- a/www/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php +++ b/www/vendor/phpunit/php-code-coverage/src/StaticAnalysis/ExecutableLinesFindingVisitor.php @@ -88,12 +88,19 @@ final class ExecutableLinesFindingVisitor extends NodeVisitorAbstract return; } + if ($node instanceof Node\Stmt\Interface_) { + foreach (range($node->getStartLine(), $node->getEndLine()) as $line) { + $this->unsets[$line] = true; + } + + return; + } + if ($node instanceof Node\Stmt\Declare_ || $node instanceof Node\Stmt\DeclareDeclare || $node instanceof Node\Stmt\Else_ || $node instanceof Node\Stmt\EnumCase || $node instanceof Node\Stmt\Finally_ || - $node instanceof Node\Stmt\Interface_ || $node instanceof Node\Stmt\Label || $node instanceof Node\Stmt\Namespace_ || $node instanceof Node\Stmt\Nop || diff --git a/www/vendor/phpunit/php-code-coverage/src/Version.php b/www/vendor/phpunit/php-code-coverage/src/Version.php index 7feb0a9c..20e8e550 100644 --- a/www/vendor/phpunit/php-code-coverage/src/Version.php +++ b/www/vendor/phpunit/php-code-coverage/src/Version.php @@ -22,7 +22,7 @@ final class Version public static function id(): string { if (self::$version === null) { - self::$version = (new VersionId('9.2.25', dirname(__DIR__)))->getVersion(); + self::$version = (new VersionId('9.2.26', dirname(__DIR__)))->getVersion(); } return self::$version; diff --git a/www/vendor/phpunit/phpunit/ChangeLog-8.5.md b/www/vendor/phpunit/phpunit/ChangeLog-8.5.md deleted file mode 100644 index a65e6a61..00000000 --- a/www/vendor/phpunit/phpunit/ChangeLog-8.5.md +++ /dev/null @@ -1,304 +0,0 @@ -# Changes in PHPUnit 8.5 - -All notable changes of the PHPUnit 8.5 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. - -## [8.5.33] - 2023-02-27 - -### Fixed - -* [#5186](https://github.com/sebastianbergmann/phpunit/issues/5186): SBOM does not validate - -## [8.5.32] - 2023-01-26 - -### Fixed - -* [#5120](https://github.com/sebastianbergmann/phpunit/issues/5120): Test Runner incorrectly treats `--testsuite` and `--list-tests` as not combinable options - -## [8.5.31] - 2022-10-28 - -### Fixed - -* [#5076](https://github.com/sebastianbergmann/phpunit/issues/5076): Test Runner does not warn about conflicting options - -## [8.5.30] - 2022-09-25 - -### Changed - -* The configuration generator now asks for a cache directory - -### Fixed - -* [#4913](https://github.com/sebastianbergmann/phpunit/issues/4913): Failed `assert()` should show a backtrace -* [#4966](https://github.com/sebastianbergmann/phpunit/issues/4966): `TestCase::assertSame()` (and related exact comparisons) must compare `float` exactly - -## [8.5.29] - 2022-08-22 - -### Changed - -* [#5033](https://github.com/sebastianbergmann/phpunit/issues/5033): Do not depend on phpspec/prophecy - -## [8.5.28] - 2022-07-29 - -### Fixed - -* [#5015](https://github.com/sebastianbergmann/phpunit/pull/5015): Ukraine banner unreadable on black background -* [#5016](https://github.com/sebastianbergmann/phpunit/issues/5016): PHPUnit 8.5.27 does not work on PHP 7.2.0-7.2.18 and PHP 7.3.0-7.3.5 - -## [8.5.27] - 2022-06-19 - -### Fixed - -* [#4950](https://github.com/sebastianbergmann/phpunit/issues/4950): False error on `atMost()` invocation rule without call -* [#4962](https://github.com/sebastianbergmann/phpunit/issues/4962): Ukraine banner unreadable on white background - -## [8.5.26] - 2022-04-01 - -### Fixed - -* [#4938](https://github.com/sebastianbergmann/phpunit/issues/4938): Test Double code generator does not handle `void` return type declaration on `__clone()` methods - -## [8.5.25] - 2022-03-16 - -### Fixed - -* [#4934](https://github.com/sebastianbergmann/phpunit/issues/4934): Code Coverage does not work with PHPUnit 8.5.24 PHAR on PHP 7 - -## [8.5.24] - 2022-03-05 - #StandWithUkraine - -### Changed - -* [#4874](https://github.com/sebastianbergmann/phpunit/pull/4874): `PHP_FLOAT_EPSILON` is now used instead of hardcoded `0.0000000001` in `PHPUnit\Framework\Constraint\IsIdentical` - -### Fixed - -* When the HTML code coverage report's configured low upper bound is larger than the high lower bound then the default values are used instead - -## [8.5.23] - 2022-01-21 - -### Fixed - -* [#4799](https://github.com/sebastianbergmann/phpunit/pull/4799): Memory leaks in `PHPUnit\Framework\TestSuite` class -* [#4857](https://github.com/sebastianbergmann/phpunit/pull/4857): Result of `debug_backtrace()` is not used correctly - -## [8.5.22] - 2021-12-25 - -### Changed - -* [#4812](https://github.com/sebastianbergmann/phpunit/issues/4812): Do not enforce time limits when a debugging session through DBGp is active -* [#4835](https://github.com/sebastianbergmann/phpunit/issues/4835): Support for `$GLOBALS['_composer_autoload_path']` introduced in Composer 2.2 - -### Fixed - -* [#4840](https://github.com/sebastianbergmann/phpunit/pull/4840): TestDox prettifying for class names does not correctly handle diacritics -* [#4846](https://github.com/sebastianbergmann/phpunit/pull/4846): Composer proxy script is not ignored - -## [8.5.21] - 2021-09-25 - -### Changed - -* PHPUnit no longer converts PHP deprecations to exceptions by default (configure `convertDeprecationsToExceptions="true"` to enable this) -* The PHPUnit XML configuration file generator now configures `convertDeprecationsToExceptions="true"` - -### Fixed - -* [#4772](https://github.com/sebastianbergmann/phpunit/pull/4772): TestDox HTML report not displayed correctly when browser has custom colour settings - -## [8.5.20] - 2021-08-31 - -### Fixed - -* [#4751](https://github.com/sebastianbergmann/phpunit/issues/4751): Configuration validation fails when using brackets in glob pattern - -## [8.5.19] - 2021-07-31 - -### Fixed - -* [#4740](https://github.com/sebastianbergmann/phpunit/issues/4740): `phpunit.phar` does not work with PHP 8.1 - -## [8.5.18] - 2021-07-19 - -### Fixed - -* [#4720](https://github.com/sebastianbergmann/phpunit/issues/4720): PHPUnit does not verify its own PHP extension requirements - -## [8.5.17] - 2021-06-23 - -### Changed - -* PHPUnit now errors out on startup when `PHP_VERSION` contains a value that is not compatible with `version_compare()`, for instance `X.Y.Z-(to be removed in future macOS)` - -## [8.5.16] - 2021-06-05 - -### Changed - -* The test result cache (the storage for which is implemented in `PHPUnit\Runner\DefaultTestResultCache`) no longer uses PHP's `serialize()` and `unserialize()` functions for persistence. It now uses a versioned JSON format instead that is independent of PHP implementation details (see [#3581](https://github.com/sebastianbergmann/phpunit/issues/3581) and [#4662](https://github.com/sebastianbergmann/phpunit/pull/4662) for examples why this is a problem). When PHPUnit tries to load the test result cache from a file that does not exist, or from a file that does not contain data in JSON format, or from a file that contains data in a JSON format version other than the one used by the currently running PHPUnit version, then this is considered to be a "cache miss". An empty `DefaultTestResultCache` object is created in this case. This should also prevent PHPUnit from crashing when trying to load a test result cache file created by a different version of PHPUnit (see [#4580](https://github.com/sebastianbergmann/phpunit/issues/4580) for example). - -### Fixed - -* [#4663](https://github.com/sebastianbergmann/phpunit/issues/4663): `TestCase::expectError()` works on PHP 7.3, but not on PHP >= 7.4 -* [#4678](https://github.com/sebastianbergmann/phpunit/pull/4678): Stubbed methods with `iterable` return types should return empty array by default -* [#4692](https://github.com/sebastianbergmann/phpunit/issues/4692): Annotations in single-line doc-comments are not handled correctly -* [#4694](https://github.com/sebastianbergmann/phpunit/issues/4694): `TestCase::getMockFromWsdl()` does not work with PHP 8.1-dev - -## [8.5.15] - 2021-03-17 - -### Fixed - -* [#4591](https://github.com/sebastianbergmann/phpunit/issues/4591): TeamCity logger logs warnings as test failures - -## [8.5.14] - 2021-01-17 - -### Fixed - -* [#4535](https://github.com/sebastianbergmann/phpunit/issues/4535): `getMockFromWsdl()` does not handle methods that do not have parameters correctly -* [#4572](https://github.com/sebastianbergmann/phpunit/issues/4572): Schema validation does not work with `%xx` sequences in path to `phpunit.xsd` -* [#4575](https://github.com/sebastianbergmann/phpunit/issues/4575): PHPUnit 8.5 incompatibility with PHP 8.1 - -## [8.5.13] - 2020-12-01 - -### Fixed - -* Running tests in isolated processes did not work with PHP 8 on Windows - -## [8.5.12] - 2020-11-30 - -### Changed - -* Changed PHP version constraint in `composer.json` from `^7.2` to `>=7.2` to allow the installation of PHPUnit 8.5 on PHP 8. Please note that the code coverage functionality is not available for PHPUnit 8.5 on PHP 8. - -### Fixed - -* [#4529](https://github.com/sebastianbergmann/phpunit/issues/4529): Debug mode of Xdebug 2 is not disabled for PHPT tests - -## [8.5.11] - 2020-11-27 - -### Changed - -* Bumped required version of `phpunit/php-code-coverage` - -## [8.5.10] - 2020-11-27 - -### Added - -* Support for Xdebug 3 - -### Fixed - -* [#4516](https://github.com/sebastianbergmann/phpunit/issues/4516): `phpunit/phpunit-selenium` does not work with PHPUnit 8.5.9 - -## [8.5.9] - 2020-11-10 - -### Fixed - -* [#3965](https://github.com/sebastianbergmann/phpunit/issues/3965): Process Isolation throws exceptions when PHPDBG is used -* [#4470](https://github.com/sebastianbergmann/phpunit/pull/4470): Infinite recursion when `--static-backup --strict-global-state` is used - -## [8.5.8] - 2020-06-22 - -### Fixed - -* [#4312](https://github.com/sebastianbergmann/phpunit/issues/4312): Fix for [#4299](https://github.com/sebastianbergmann/phpunit/issues/4299) breaks backward compatibility - -## [8.5.7] - 2020-06-21 - -### Fixed - -* [#4299](https://github.com/sebastianbergmann/phpunit/issues/4299): "No tests executed" does not always result in exit code `1` -* [#4306](https://github.com/sebastianbergmann/phpunit/issues/4306): Exceptions during code coverage driver initialization are not handled correctly - -## [8.5.6] - 2020-06-15 - -### Fixed - -* [#4211](https://github.com/sebastianbergmann/phpunit/issues/4211): `phpdbg_*()` functions are scoped to `PHPUnit\phpdbg_*()` - -## [8.5.5] - 2020-05-22 - -### Fixed - -* [#4033](https://github.com/sebastianbergmann/phpunit/issues/4033): Unexpected behaviour when `$GLOBALS` is deleted - -## [8.5.4] - 2020-04-23 - -### Changed - -* Changed how `PHPUnit\TextUI\Command` passes warnings to `PHPUnit\TextUI\TestRunner` - -## [8.5.3] - 2020-03-31 - -### Fixed - -* [#4017](https://github.com/sebastianbergmann/phpunit/issues/4017): Do not suggest refactoring to something that is also deprecated -* [#4133](https://github.com/sebastianbergmann/phpunit/issues/4133): `expectExceptionMessageRegExp()` has been removed in PHPUnit 9 without a deprecation warning being given in PHPUnit 8 -* [#4139](https://github.com/sebastianbergmann/phpunit/issues/4139): Cannot double interfaces that declare a constructor with PHP 8 -* [#4144](https://github.com/sebastianbergmann/phpunit/issues/4144): Empty objects are converted to empty arrays in JSON comparison failure diff - -## [8.5.2] - 2020-01-08 - -### Removed - -* `eval-stdin.php` has been removed, it was not used anymore since PHPUnit 7.2.7 - -## [8.5.1] - 2019-12-25 - -### Changed - -* `eval-stdin.php` can now only be executed with `cli` and `phpdbg` - -### Fixed - -* [#3983](https://github.com/sebastianbergmann/phpunit/issues/3983): Deprecation warning given too eagerly - -## [8.5.0] - 2019-12-06 - -### Added - -* [#3911](https://github.com/sebastianbergmann/phpunit/issues/3911): Support combined use of `addMethods()` and `onlyMethods()` -* [#3949](https://github.com/sebastianbergmann/phpunit/issues/3949): Introduce specialized assertions `assertFileEqualsCanonicalizing()`, `assertFileEqualsIgnoringCase()`, `assertStringEqualsFileCanonicalizing()`, `assertStringEqualsFileIgnoringCase()`, `assertFileNotEqualsCanonicalizing()`, `assertFileNotEqualsIgnoringCase()`, `assertStringNotEqualsFileCanonicalizing()`, and `assertStringNotEqualsFileIgnoringCase()` as alternative to using `assertFileEquals()` etc. with optional parameters - -### Changed - -* [#3860](https://github.com/sebastianbergmann/phpunit/pull/3860): Deprecate invoking PHPUnit commandline test runner with just a class name -* [#3950](https://github.com/sebastianbergmann/phpunit/issues/3950): Deprecate optional parameters of `assertFileEquals()` etc. -* [#3955](https://github.com/sebastianbergmann/phpunit/issues/3955): Deprecate support for doubling multiple interfaces - -### Fixed - -* [#3953](https://github.com/sebastianbergmann/phpunit/issues/3953): Code Coverage for test executed in isolation does not work when the PHAR is used -* [#3967](https://github.com/sebastianbergmann/phpunit/issues/3967): Cannot double interface that extends interface that extends `\Throwable` -* [#3968](https://github.com/sebastianbergmann/phpunit/pull/3968): Test class run in a separate PHP process are passing when `exit` called inside - -[8.5.33]: https://github.com/sebastianbergmann/phpunit/compare/8.5.32...8.5.33 -[8.5.32]: https://github.com/sebastianbergmann/phpunit/compare/8.5.31...8.5.32 -[8.5.31]: https://github.com/sebastianbergmann/phpunit/compare/8.5.30...8.5.31 -[8.5.30]: https://github.com/sebastianbergmann/phpunit/compare/8.5.29...8.5.30 -[8.5.29]: https://github.com/sebastianbergmann/phpunit/compare/8.5.28...8.5.29 -[8.5.28]: https://github.com/sebastianbergmann/phpunit/compare/8.5.27...8.5.28 -[8.5.27]: https://github.com/sebastianbergmann/phpunit/compare/8.5.26...8.5.27 -[8.5.26]: https://github.com/sebastianbergmann/phpunit/compare/8.5.25...8.5.26 -[8.5.25]: https://github.com/sebastianbergmann/phpunit/compare/8.5.24...8.5.25 -[8.5.24]: https://github.com/sebastianbergmann/phpunit/compare/8.5.23...8.5.24 -[8.5.23]: https://github.com/sebastianbergmann/phpunit/compare/8.5.22...8.5.23 -[8.5.22]: https://github.com/sebastianbergmann/phpunit/compare/8.5.21...8.5.22 -[8.5.21]: https://github.com/sebastianbergmann/phpunit/compare/8.5.20...8.5.21 -[8.5.20]: https://github.com/sebastianbergmann/phpunit/compare/8.5.19...8.5.20 -[8.5.19]: https://github.com/sebastianbergmann/phpunit/compare/8.5.18...8.5.19 -[8.5.18]: https://github.com/sebastianbergmann/phpunit/compare/8.5.17...8.5.18 -[8.5.17]: https://github.com/sebastianbergmann/phpunit/compare/8.5.16...8.5.17 -[8.5.16]: https://github.com/sebastianbergmann/phpunit/compare/8.5.15...8.5.16 -[8.5.15]: https://github.com/sebastianbergmann/phpunit/compare/8.5.14...8.5.15 -[8.5.14]: https://github.com/sebastianbergmann/phpunit/compare/8.5.13...8.5.14 -[8.5.13]: https://github.com/sebastianbergmann/phpunit/compare/8.5.12...8.5.13 -[8.5.12]: https://github.com/sebastianbergmann/phpunit/compare/8.5.11...8.5.12 -[8.5.11]: https://github.com/sebastianbergmann/phpunit/compare/8.5.10...8.5.11 -[8.5.10]: https://github.com/sebastianbergmann/phpunit/compare/8.5.9...8.5.10 -[8.5.9]: https://github.com/sebastianbergmann/phpunit/compare/8.5.8...8.5.9 -[8.5.8]: https://github.com/sebastianbergmann/phpunit/compare/8.5.7...8.5.8 -[8.5.7]: https://github.com/sebastianbergmann/phpunit/compare/8.5.6...8.5.7 -[8.5.6]: https://github.com/sebastianbergmann/phpunit/compare/8.5.5...8.5.6 -[8.5.5]: https://github.com/sebastianbergmann/phpunit/compare/8.5.4...8.5.5 -[8.5.4]: https://github.com/sebastianbergmann/phpunit/compare/8.5.3...8.5.4 -[8.5.3]: https://github.com/sebastianbergmann/phpunit/compare/8.5.2...8.5.3 -[8.5.2]: https://github.com/sebastianbergmann/phpunit/compare/8.5.1...8.5.2 -[8.5.1]: https://github.com/sebastianbergmann/phpunit/compare/8.5.0...8.5.1 -[8.5.0]: https://github.com/sebastianbergmann/phpunit/compare/8.4.3...8.5.0 diff --git a/www/vendor/phpunit/phpunit/ChangeLog-9.6.md b/www/vendor/phpunit/phpunit/ChangeLog-9.6.md index df27bce7..5995d581 100644 --- a/www/vendor/phpunit/phpunit/ChangeLog-9.6.md +++ b/www/vendor/phpunit/phpunit/ChangeLog-9.6.md @@ -2,6 +2,34 @@ All notable changes of the PHPUnit 9.6 release series are documented in this file using the [Keep a CHANGELOG](https://keepachangelog.com/) principles. +## [9.6.8] - 2023-05-11 + +### Fixed + +* [#5345](https://github.com/sebastianbergmann/phpunit/issues/5345): No stack trace shown for previous exceptions during bootstrap + +## [9.6.7] - 2023-04-14 + +### Fixed + +* Tests that have `@doesNotPerformAssertions` do not contribute to code coverage + +## [9.6.6] - 2023-03-27 + +### Fixed + +* [#5270](https://github.com/sebastianbergmann/phpunit/issues/5270): `GlobalState::getIniSettingsAsString()` generates code that triggers warnings + +## [9.6.5] - 2023-03-09 + +### Changed + +* Backported the HTML and CSS improvements made to the `--testdox-html` from PHPUnit 10 + +### Fixed + +* [#5205](https://github.com/sebastianbergmann/phpunit/issues/5205): Wrong default value for optional parameter of `PHPUnit\Util\Test::parseTestMethodAnnotations()` causes `ReflectionException` + ## [9.6.4] - 2023-02-27 ### Fixed @@ -36,6 +64,10 @@ All notable changes of the PHPUnit 9.6 release series are documented in this fil * [#5064](https://github.com/sebastianbergmann/phpunit/issues/5064): Deprecate `PHPUnit\Framework\TestCase::getMockClass()` * [#5132](https://github.com/sebastianbergmann/phpunit/issues/5132): Deprecate `Test` suffix for abstract test case classes +[9.6.8]: https://github.com/sebastianbergmann/phpunit/compare/9.6.7...9.6.8 +[9.6.7]: https://github.com/sebastianbergmann/phpunit/compare/9.6.6...9.6.7 +[9.6.6]: https://github.com/sebastianbergmann/phpunit/compare/9.6.5...9.6.6 +[9.6.5]: https://github.com/sebastianbergmann/phpunit/compare/9.6.4...9.6.5 [9.6.4]: https://github.com/sebastianbergmann/phpunit/compare/9.6.3...9.6.4 [9.6.3]: https://github.com/sebastianbergmann/phpunit/compare/9.6.2...9.6.3 [9.6.2]: https://github.com/sebastianbergmann/phpunit/compare/9.6.1...9.6.2 diff --git a/www/vendor/phpunit/phpunit/composer.json b/www/vendor/phpunit/phpunit/composer.json index 38117ea1..28411d9b 100644 --- a/www/vendor/phpunit/phpunit/composer.json +++ b/www/vendor/phpunit/phpunit/composer.json @@ -17,7 +17,8 @@ } ], "support": { - "issues": "https://github.com/sebastianbergmann/phpunit/issues" + "issues": "https://github.com/sebastianbergmann/phpunit/issues", + "security": "https://github.com/sebastianbergmann/phpunit/security/policy" }, "prefer-stable": true, "require": { @@ -57,8 +58,8 @@ "sort-packages": true }, "suggest": { - "ext-soap": "*", - "ext-xdebug": "*" + "ext-soap": "To be able to generate mocks based on WSDL files", + "ext-xdebug": "PHP extension that provides line coverage as well as branch and path coverage" }, "bin": [ "phpunit" diff --git a/www/vendor/phpunit/phpunit/phpunit b/www/vendor/phpunit/phpunit/phpunit index c8029566..b9f5cf29 100755 --- a/www/vendor/phpunit/phpunit/phpunit +++ b/www/vendor/phpunit/phpunit/phpunit @@ -38,22 +38,31 @@ if (version_compare('7.3.0', PHP_VERSION, '>')) { die(1); } -foreach (['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter'] as $extension) { - if (extension_loaded($extension)) { - continue; - } +$requiredExtensions = ['dom', 'json', 'libxml', 'mbstring', 'tokenizer', 'xml', 'xmlwriter']; +$unavailableExtensions = array_filter( + $requiredExtensions, + static function ($extension) { + return !extension_loaded($extension); + } +); + +if ([] !== $unavailableExtensions) { fwrite( STDERR, sprintf( - 'PHPUnit requires the "%s" extension.' . PHP_EOL, - $extension + 'PHPUnit requires the "%s" extensions, but the "%s" %s not available.' . PHP_EOL, + implode('", "', $requiredExtensions), + implode('", "', $unavailableExtensions), + count($unavailableExtensions) === 1 ? 'extension is' : 'extensions are' ) ); die(1); } +unset($requiredExtensions, $unavailableExtensions); + if (!ini_get('date.timezone')) { ini_set('date.timezone', 'UTC'); } diff --git a/www/vendor/phpunit/phpunit/src/Framework/Assert.php b/www/vendor/phpunit/phpunit/src/Framework/Assert.php index 156220a5..9872b25c 100644 --- a/www/vendor/phpunit/phpunit/src/Framework/Assert.php +++ b/www/vendor/phpunit/phpunit/src/Framework/Assert.php @@ -1304,7 +1304,7 @@ abstract class Assert */ public static function assertObjectHasAttribute(string $attributeName, $object, string $message = ''): void { - self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10.'); + self::createWarning('assertObjectHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectHasProperty() (PHPUnit 10.1.0+) instead.'); if (!self::isValidObjectAttributeName($attributeName)) { throw InvalidArgumentException::create(1, 'valid attribute name'); @@ -1334,7 +1334,7 @@ abstract class Assert */ public static function assertObjectNotHasAttribute(string $attributeName, $object, string $message = ''): void { - self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10.'); + self::createWarning('assertObjectNotHasAttribute() is deprecated and will be removed in PHPUnit 10. Refactor your test to use assertObjectNotHasProperty() (PHPUnit 10.1.0+) instead.'); if (!self::isValidObjectAttributeName($attributeName)) { throw InvalidArgumentException::create(1, 'valid attribute name'); diff --git a/www/vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php b/www/vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php index 0370b511..4f106b1e 100644 --- a/www/vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php +++ b/www/vendor/phpunit/phpunit/src/Framework/Constraint/Equality/IsEqualWithDelta.php @@ -92,7 +92,7 @@ final class IsEqualWithDelta extends Constraint public function toString(): string { return sprintf( - 'is equal to %s with delta <%F>>', + 'is equal to %s with delta <%F>', $this->exporter()->export($this->value), $this->delta ); diff --git a/www/vendor/phpunit/phpunit/src/Framework/MockObject/Generator.php b/www/vendor/phpunit/phpunit/src/Framework/MockObject/Generator.php index 17e3312c..4810ecae 100644 --- a/www/vendor/phpunit/phpunit/src/Framework/MockObject/Generator.php +++ b/www/vendor/phpunit/phpunit/src/Framework/MockObject/Generator.php @@ -72,7 +72,6 @@ trait MockedCloneMethodWithVoidReturnType } } EOT; - private const MOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' namespace PHPUnit\Framework\MockObject; @@ -84,7 +83,6 @@ trait MockedCloneMethodWithoutReturnType } } EOT; - private const UNMOCKED_CLONE_METHOD_WITH_VOID_RETURN_TYPE_TRAIT = <<<'EOT' namespace PHPUnit\Framework\MockObject; @@ -98,7 +96,6 @@ trait UnmockedCloneMethodWithVoidReturnType } } EOT; - private const UNMOCKED_CLONE_METHOD_WITHOUT_RETURN_TYPE_TRAIT = <<<'EOT' namespace PHPUnit\Framework\MockObject; diff --git a/www/vendor/phpunit/phpunit/src/Framework/TestResult.php b/www/vendor/phpunit/phpunit/src/Framework/TestResult.php index 4fde29fa..22150a5a 100644 --- a/www/vendor/phpunit/phpunit/src/Framework/TestResult.php +++ b/www/vendor/phpunit/phpunit/src/Framework/TestResult.php @@ -804,6 +804,7 @@ final class TestResult implements Countable } if ($this->beStrictAboutTestsThatDoNotTestAnything && + !$test->doesNotPerformAssertions() && $test->getNumAssertions() === 0) { $risky = true; } diff --git a/www/vendor/phpunit/phpunit/src/Runner/Version.php b/www/vendor/phpunit/phpunit/src/Runner/Version.php index 7fda90ba..268045a4 100644 --- a/www/vendor/phpunit/phpunit/src/Runner/Version.php +++ b/www/vendor/phpunit/phpunit/src/Runner/Version.php @@ -41,7 +41,7 @@ final class Version } if (self::$version === '') { - self::$version = (new VersionId('9.6.4', dirname(__DIR__, 2)))->getVersion(); + self::$version = (new VersionId('9.6.8', dirname(__DIR__, 2)))->getVersion(); } return self::$version; diff --git a/www/vendor/phpunit/phpunit/src/TextUI/CliArguments/Builder.php b/www/vendor/phpunit/phpunit/src/TextUI/CliArguments/Builder.php index 9030b1db..da488082 100644 --- a/www/vendor/phpunit/phpunit/src/TextUI/CliArguments/Builder.php +++ b/www/vendor/phpunit/phpunit/src/TextUI/CliArguments/Builder.php @@ -121,7 +121,6 @@ final class Builder 'whitelist=', 'dump-xdebug-filter=', ]; - private const SHORT_OPTIONS = 'd:c:hv'; public function fromParameters(array $parameters, array $additionalLongOptions): Configuration diff --git a/www/vendor/phpunit/phpunit/src/TextUI/Command.php b/www/vendor/phpunit/phpunit/src/TextUI/Command.php index 48243caa..0bebad6a 100644 --- a/www/vendor/phpunit/phpunit/src/TextUI/Command.php +++ b/www/vendor/phpunit/phpunit/src/TextUI/Command.php @@ -568,16 +568,29 @@ class Command $this->exitWithErrorMessage($t->getMessage()); } - $this->exitWithErrorMessage( - sprintf( - 'Error in bootstrap script: %s:%s%s%s%s', + $message = sprintf( + 'Error in bootstrap script: %s:%s%s%s%s', + get_class($t), + PHP_EOL, + $t->getMessage(), + PHP_EOL, + $t->getTraceAsString() + ); + + while ($t = $t->getPrevious()) { + $message .= sprintf( + '%s%sPrevious error: %s:%s%s%s%s', + PHP_EOL, + PHP_EOL, get_class($t), PHP_EOL, $t->getMessage(), PHP_EOL, - $t->getTraceAsString() - ) - ); + $t->getTraceAsString(), + ); + } + + $this->exitWithErrorMessage($message); } } diff --git a/www/vendor/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php b/www/vendor/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php index 99f0fa9d..408fa133 100644 --- a/www/vendor/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php +++ b/www/vendor/phpunit/phpunit/src/TextUI/DefaultResultPrinter.php @@ -47,23 +47,15 @@ use Throwable; */ class DefaultResultPrinter extends Printer implements ResultPrinter { - public const EVENT_TEST_START = 0; - - public const EVENT_TEST_END = 1; - + public const EVENT_TEST_START = 0; + public const EVENT_TEST_END = 1; public const EVENT_TESTSUITE_START = 2; - - public const EVENT_TESTSUITE_END = 3; - - public const COLOR_NEVER = 'never'; - - public const COLOR_AUTO = 'auto'; - - public const COLOR_ALWAYS = 'always'; - - public const COLOR_DEFAULT = self::COLOR_NEVER; - - private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS]; + public const EVENT_TESTSUITE_END = 3; + public const COLOR_NEVER = 'never'; + public const COLOR_AUTO = 'auto'; + public const COLOR_ALWAYS = 'always'; + public const COLOR_DEFAULT = self::COLOR_NEVER; + private const AVAILABLE_COLORS = [self::COLOR_NEVER, self::COLOR_AUTO, self::COLOR_ALWAYS]; /** * @var int diff --git a/www/vendor/phpunit/phpunit/src/TextUI/Help.php b/www/vendor/phpunit/phpunit/src/TextUI/Help.php index 084f2a21..82f305dd 100644 --- a/www/vendor/phpunit/phpunit/src/TextUI/Help.php +++ b/www/vendor/phpunit/phpunit/src/TextUI/Help.php @@ -27,8 +27,7 @@ use SebastianBergmann\Environment\Console; final class Help { private const LEFT_MARGIN = ' '; - - private const HELP_TEXT = [ + private const HELP_TEXT = [ 'Usage' => [ ['text' => 'phpunit [options] UnitTest.php'], ['text' => 'phpunit [options] '], diff --git a/www/vendor/phpunit/phpunit/src/TextUI/TestRunner.php b/www/vendor/phpunit/phpunit/src/TextUI/TestRunner.php index c1362f3a..3ba298e8 100644 --- a/www/vendor/phpunit/phpunit/src/TextUI/TestRunner.php +++ b/www/vendor/phpunit/phpunit/src/TextUI/TestRunner.php @@ -88,10 +88,8 @@ use SebastianBergmann\Timer\Timer; */ final class TestRunner extends BaseTestRunner { - public const SUCCESS_EXIT = 0; - - public const FAILURE_EXIT = 1; - + public const SUCCESS_EXIT = 0; + public const FAILURE_EXIT = 1; public const EXCEPTION_EXIT = 2; /** diff --git a/www/vendor/phpunit/phpunit/src/Util/Annotation/DocBlock.php b/www/vendor/phpunit/phpunit/src/Util/Annotation/DocBlock.php index 648f9edc..5066c4bb 100644 --- a/www/vendor/phpunit/phpunit/src/Util/Annotation/DocBlock.php +++ b/www/vendor/phpunit/phpunit/src/Util/Annotation/DocBlock.php @@ -67,17 +67,12 @@ final class DocBlock */ public const REGEX_DATA_PROVIDER = '/@dataProvider\s+([a-zA-Z0-9._:-\\\\x7f-\xff]+)/'; - private const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; - + private const REGEX_REQUIRES_VERSION = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[<>=!]{0,2})\s*(?P[\d\.-]+(dev|(RC|alpha|beta)[\d\.])?)[ \t]*\r?$/m'; private const REGEX_REQUIRES_VERSION_CONSTRAINT = '/@requires\s+(?PPHP(?:Unit)?)\s+(?P[\d\t \-.|~^]+)[ \t]*\r?$/m'; - - private const REGEX_REQUIRES_OS = '/@requires\s+(?POS(?:FAMILY)?)\s+(?P.+?)[ \t]*\r?$/m'; - - private const REGEX_REQUIRES_SETTING = '/@requires\s+(?Psetting)\s+(?P([^ ]+?))\s*(?P[\w\.-]+[\w\.]?)?[ \t]*\r?$/m'; - - private const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^\s<>=!]+))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m'; - - private const REGEX_TEST_WITH = '/@testWith\s+/'; + private const REGEX_REQUIRES_OS = '/@requires\s+(?POS(?:FAMILY)?)\s+(?P.+?)[ \t]*\r?$/m'; + private const REGEX_REQUIRES_SETTING = '/@requires\s+(?Psetting)\s+(?P([^ ]+?))\s*(?P[\w\.-]+[\w\.]?)?[ \t]*\r?$/m'; + private const REGEX_REQUIRES = '/@requires\s+(?Pfunction|extension)\s+(?P([^\s<>=!]+))\s*(?P[<>=!]{0,2})\s*(?P[\d\.-]+[\d\.]?)?[ \t]*\r?$/m'; + private const REGEX_TEST_WITH = '/@testWith\s+/'; /** @var string */ private $docComment; diff --git a/www/vendor/phpunit/phpunit/src/Util/GlobalState.php b/www/vendor/phpunit/phpunit/src/Util/GlobalState.php index cc5c2228..5cf16bcd 100644 --- a/www/vendor/phpunit/phpunit/src/Util/GlobalState.php +++ b/www/vendor/phpunit/phpunit/src/Util/GlobalState.php @@ -9,6 +9,8 @@ */ namespace PHPUnit\Util; +use const PHP_MAJOR_VERSION; +use const PHP_MINOR_VERSION; use function array_keys; use function array_reverse; use function array_shift; @@ -47,6 +49,79 @@ final class GlobalState '_REQUEST', ]; + /** + * @psalm-var array> + */ + private const DEPRECATED_INI_SETTINGS = [ + '7.3' => [ + 'iconv.input_encoding' => true, + 'iconv.output_encoding' => true, + 'iconv.internal_encoding' => true, + 'mbstring.func_overload' => true, + 'mbstring.http_input' => true, + 'mbstring.http_output' => true, + 'mbstring.internal_encoding' => true, + 'string.strip_tags' => true, + ], + + '7.4' => [ + 'iconv.input_encoding' => true, + 'iconv.output_encoding' => true, + 'iconv.internal_encoding' => true, + 'mbstring.func_overload' => true, + 'mbstring.http_input' => true, + 'mbstring.http_output' => true, + 'mbstring.internal_encoding' => true, + 'pdo_odbc.db2_instance_name' => true, + 'string.strip_tags' => true, + ], + + '8.0' => [ + 'iconv.input_encoding' => true, + 'iconv.output_encoding' => true, + 'iconv.internal_encoding' => true, + 'mbstring.http_input' => true, + 'mbstring.http_output' => true, + 'mbstring.internal_encoding' => true, + ], + + '8.1' => [ + 'auto_detect_line_endings' => true, + 'filter.default' => true, + 'iconv.input_encoding' => true, + 'iconv.output_encoding' => true, + 'iconv.internal_encoding' => true, + 'mbstring.http_input' => true, + 'mbstring.http_output' => true, + 'mbstring.internal_encoding' => true, + 'oci8.old_oci_close_semantics' => true, + ], + + '8.2' => [ + 'auto_detect_line_endings' => true, + 'filter.default' => true, + 'iconv.input_encoding' => true, + 'iconv.output_encoding' => true, + 'iconv.internal_encoding' => true, + 'mbstring.http_input' => true, + 'mbstring.http_output' => true, + 'mbstring.internal_encoding' => true, + 'oci8.old_oci_close_semantics' => true, + ], + + '8.3' => [ + 'auto_detect_line_endings' => true, + 'filter.default' => true, + 'iconv.input_encoding' => true, + 'iconv.output_encoding' => true, + 'iconv.internal_encoding' => true, + 'mbstring.http_input' => true, + 'mbstring.http_output' => true, + 'mbstring.internal_encoding' => true, + 'oci8.old_oci_close_semantics' => true, + ], + ]; + /** * @throws Exception */ @@ -106,6 +181,10 @@ final class GlobalState $result = ''; foreach (ini_get_all(null, false) as $key => $value) { + if (self::isIniSettingDeprecated($key)) { + continue; + } + $result .= sprintf( '@ini_set(%s, %s);' . "\n", self::exportVariable($key), @@ -200,4 +279,9 @@ final class GlobalState return $result; } + + private static function isIniSettingDeprecated(string $iniSetting): bool + { + return isset(self::DEPRECATED_INI_SETTINGS[PHP_MAJOR_VERSION . '.' . PHP_MINOR_VERSION][$iniSetting]); + } } diff --git a/www/vendor/phpunit/phpunit/src/Util/Test.php b/www/vendor/phpunit/phpunit/src/Util/Test.php index f089f6d4..d04a7683 100644 --- a/www/vendor/phpunit/phpunit/src/Util/Test.php +++ b/www/vendor/phpunit/phpunit/src/Util/Test.php @@ -331,7 +331,7 @@ final class Test /** * @psalm-param class-string $className */ - public static function parseTestMethodAnnotations(string $className, ?string $methodName = ''): array + public static function parseTestMethodAnnotations(string $className, ?string $methodName = null): array { $registry = Registry::getInstance(); diff --git a/www/vendor/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php b/www/vendor/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php index def16c39..b66eb8b9 100644 --- a/www/vendor/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php +++ b/www/vendor/phpunit/phpunit/src/Util/TestDox/CliTestDoxPrinter.php @@ -66,7 +66,6 @@ class CliTestDoxPrinter extends TestDoxPrinter " \e[36m◑\e[0m running tests", " \e[36m◒\e[0m running tests", ]; - private const STATUS_STYLES = [ BaseTestRunner::STATUS_PASSED => [ 'symbol' => '✔', diff --git a/www/vendor/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php b/www/vendor/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php index 013d1de8..776a83bd 100644 --- a/www/vendor/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php +++ b/www/vendor/phpunit/phpunit/src/Util/TestDox/HtmlResultPrinter.php @@ -29,26 +29,47 @@ final class HtmlResultPrinter extends ResultPrinter @@ -60,7 +81,7 @@ EOT; */ private const CLASS_HEADER = <<<'EOT' -

%s

+

%s

    EOT; @@ -101,7 +122,6 @@ EOT; $this->write( sprintf( self::CLASS_HEADER, - $name, $this->currentTestClassPrettified ) ); @@ -114,9 +134,8 @@ EOT; { $this->write( sprintf( - "
  • %s %s
  • \n", - $success ? '#555753' : '#ef2929', - $success ? '✓' : '❌', + "
  • %s
  • \n", + $success ? 'success' : 'defect', $name ) ); diff --git a/www/vendor/psr/log/LICENSE b/www/vendor/psr/log/LICENSE new file mode 100644 index 00000000..474c952b --- /dev/null +++ b/www/vendor/psr/log/LICENSE @@ -0,0 +1,19 @@ +Copyright (c) 2012 PHP Framework Interoperability Group + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/www/vendor/psr/log/README.md b/www/vendor/psr/log/README.md new file mode 100644 index 00000000..a9f20c43 --- /dev/null +++ b/www/vendor/psr/log/README.md @@ -0,0 +1,58 @@ +PSR Log +======= + +This repository holds all interfaces/classes/traits related to +[PSR-3](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md). + +Note that this is not a logger of its own. It is merely an interface that +describes a logger. See the specification for more details. + +Installation +------------ + +```bash +composer require psr/log +``` + +Usage +----- + +If you need a logger, you can use the interface like this: + +```php +logger = $logger; + } + + public function doSomething() + { + if ($this->logger) { + $this->logger->info('Doing work'); + } + + try { + $this->doSomethingElse(); + } catch (Exception $exception) { + $this->logger->error('Oh no!', array('exception' => $exception)); + } + + // do something useful + } +} +``` + +You can then pick one of the implementations of the interface to get a logger. + +If you want to implement the interface, you can require this package and +implement `Psr\Log\LoggerInterface` in your code. Please read the +[specification text](https://github.com/php-fig/fig-standards/blob/master/accepted/PSR-3-logger-interface.md) +for details. diff --git a/www/vendor/psr/log/composer.json b/www/vendor/psr/log/composer.json new file mode 100644 index 00000000..879fc6f5 --- /dev/null +++ b/www/vendor/psr/log/composer.json @@ -0,0 +1,26 @@ +{ + "name": "psr/log", + "description": "Common interface for logging libraries", + "keywords": ["psr", "psr-3", "log"], + "homepage": "https://github.com/php-fig/log", + "license": "MIT", + "authors": [ + { + "name": "PHP-FIG", + "homepage": "https://www.php-fig.org/" + } + ], + "require": { + "php": ">=8.0.0" + }, + "autoload": { + "psr-4": { + "Psr\\Log\\": "src" + } + }, + "extra": { + "branch-alias": { + "dev-master": "3.x-dev" + } + } +} diff --git a/www/vendor/psr/log/src/AbstractLogger.php b/www/vendor/psr/log/src/AbstractLogger.php new file mode 100644 index 00000000..d60a091a --- /dev/null +++ b/www/vendor/psr/log/src/AbstractLogger.php @@ -0,0 +1,15 @@ +logger = $logger; + } +} diff --git a/www/vendor/psr/log/src/LoggerInterface.php b/www/vendor/psr/log/src/LoggerInterface.php new file mode 100644 index 00000000..b3a24b5f --- /dev/null +++ b/www/vendor/psr/log/src/LoggerInterface.php @@ -0,0 +1,125 @@ +log(LogLevel::EMERGENCY, $message, $context); + } + + /** + * Action must be taken immediately. + * + * Example: Entire website down, database unavailable, etc. This should + * trigger the SMS alerts and wake you up. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function alert(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::ALERT, $message, $context); + } + + /** + * Critical conditions. + * + * Example: Application component unavailable, unexpected exception. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function critical(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::CRITICAL, $message, $context); + } + + /** + * Runtime errors that do not require immediate action but should typically + * be logged and monitored. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function error(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::ERROR, $message, $context); + } + + /** + * Exceptional occurrences that are not errors. + * + * Example: Use of deprecated APIs, poor use of an API, undesirable things + * that are not necessarily wrong. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function warning(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::WARNING, $message, $context); + } + + /** + * Normal but significant events. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function notice(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::NOTICE, $message, $context); + } + + /** + * Interesting events. + * + * Example: User logs in, SQL logs. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function info(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::INFO, $message, $context); + } + + /** + * Detailed debug information. + * + * @param string|\Stringable $message + * @param array $context + * + * @return void + */ + public function debug(string|\Stringable $message, array $context = []): void + { + $this->log(LogLevel::DEBUG, $message, $context); + } + + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + abstract public function log($level, string|\Stringable $message, array $context = []): void; +} diff --git a/www/vendor/psr/log/src/NullLogger.php b/www/vendor/psr/log/src/NullLogger.php new file mode 100644 index 00000000..c1cc3c06 --- /dev/null +++ b/www/vendor/psr/log/src/NullLogger.php @@ -0,0 +1,30 @@ +logger) { }` + * blocks. + */ +class NullLogger extends AbstractLogger +{ + /** + * Logs with an arbitrary level. + * + * @param mixed $level + * @param string|\Stringable $message + * @param array $context + * + * @return void + * + * @throws \Psr\Log\InvalidArgumentException + */ + public function log($level, string|\Stringable $message, array $context = []): void + { + // noop + } +} diff --git a/www/vendor/sebastian/diff/ChangeLog.md b/www/vendor/sebastian/diff/ChangeLog.md index 9bdcc5b6..a6ccfad7 100644 --- a/www/vendor/sebastian/diff/ChangeLog.md +++ b/www/vendor/sebastian/diff/ChangeLog.md @@ -2,6 +2,13 @@ All notable changes are documented in this file using the [Keep a CHANGELOG](http://keepachangelog.com/) principles. +## [4.0.5] - 2023-05-07 + +### Changed + +* [#118](https://github.com/sebastianbergmann/diff/pull/118): Improve performance of `MemoryEfficientLongestCommonSubsequenceCalculator` +* [#119](https://github.com/sebastianbergmann/diff/pull/119): Improve performance of `TimeEfficientLongestCommonSubsequenceCalculator` + ## [4.0.4] - 2020-10-26 ### Fixed @@ -76,6 +83,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 +[4.0.5]: https://github.com/sebastianbergmann/diff/compare/4.0.4...4.0.5 [4.0.4]: https://github.com/sebastianbergmann/diff/compare/4.0.3...4.0.4 [4.0.3]: https://github.com/sebastianbergmann/diff/compare/4.0.2...4.0.3 [4.0.2]: https://github.com/sebastianbergmann/diff/compare/4.0.1...4.0.2 diff --git a/www/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php b/www/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php index 0b626eaf..489113b6 100644 --- a/www/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php +++ b/www/vendor/sebastian/diff/src/MemoryEfficientLongestCommonSubsequenceCalculator.php @@ -78,7 +78,12 @@ final class MemoryEfficientLongestCommonSubsequenceCalculator implements Longest if ($from[$i] === $to[$j]) { $current[$j + 1] = $prev[$j] + 1; } else { - $current[$j + 1] = max($current[$j], $prev[$j + 1]); + // don't use max() to avoid function call overhead + if ($current[$j] > $prev[$j + 1]) { + $current[$j + 1] = $current[$j]; + } else { + $current[$j + 1] = $prev[$j + 1]; + } } } } diff --git a/www/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php b/www/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php index fd19cac7..4e8d951d 100644 --- a/www/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php +++ b/www/vendor/sebastian/diff/src/TimeEfficientLongestCommonSubsequenceCalculator.php @@ -37,12 +37,24 @@ final class TimeEfficientLongestCommonSubsequenceCalculator implements LongestCo for ($i = 1; $i <= $fromLength; ++$i) { for ($j = 1; $j <= $toLength; ++$j) { - $o = ($j * $width) + $i; - $matrix[$o] = max( - $matrix[$o - 1], - $matrix[$o - $width], - $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0 - ); + $o = ($j * $width) + $i; + + // don't use max() to avoid function call overhead + $firstOrLast = $from[$i - 1] === $to[$j - 1] ? $matrix[$o - $width - 1] + 1 : 0; + + if ($matrix[$o - 1] > $matrix[$o - $width]) { + if ($firstOrLast > $matrix[$o - 1]) { + $matrix[$o] = $firstOrLast; + } else { + $matrix[$o] = $matrix[$o - 1]; + } + } else { + if ($firstOrLast > $matrix[$o - $width]) { + $matrix[$o] = $firstOrLast; + } else { + $matrix[$o] = $matrix[$o - $width]; + } + } } }